streaming_rules_abr_lolp_LoLpWeightSelector.js
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of Dash Industry Forum nor the names of its
* contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Authors:
* Abdelhak Bentaleb | National University of Singapore | bentaleb@comp.nus.edu.sg
* Mehmet N. Akcay | Ozyegin University | necmettin.akcay@ozu.edu.tr
* May Lim | National University of Singapore | maylim@comp.nus.edu.sg
*/
import FactoryMaker from '../../../../core/FactoryMaker';
function LoLpWeightSelector(config) {
let targetLatency = config.targetLatency;
let bufferMin = config.bufferMin;
let segmentDuration = config.segmentDuration;
let qoeEvaluator = config.qoeEvaluator;
let instance,
valueList,
weightTypeCount,
weightOptions,
previousLatency;
/**
*
* @private
*/
function _setup() {
_resetInitialSettings();
}
/**
*
* @private
*/
function _resetInitialSettings() {
valueList = [0.2, 0.4, 0.6, 0.8, 1];
weightTypeCount = 4;
weightOptions = _getPermutations(valueList, weightTypeCount);
previousLatency = 0;
}
/**
* Next, at each segment boundary, ABR to input current neurons and target state (only used in Method II) to find the desired weight vector
* @param {array} neurons
* @param {number} currentLatency
* @param {number} currentBuffer
* @param {number} currentRebuffer
* @param {number} currentThroughput
* @param {number} playbackRate
* @return {number|null}
* @private
*/
function findWeightVector(neurons, currentLatency, currentBuffer, currentRebuffer, currentThroughput, playbackRate) {
let maxQoE = null;
let winnerWeights = null;
let winnerBitrate = null;
let deltaLatency = Math.abs(currentLatency - previousLatency);
// For each neuron, m
neurons.forEach((neuron) => {
// For each possible weight vector, z
// E.g. For [ throughput, latency, buffer, playbackRate, QoE ]
// Possible weightVector = [ 0.2, 0.4, 0.2, 0, 0.2 ]
weightOptions.forEach((weightVector) => {
// Apply weightVector to neuron, compute utility and determine winnerWeights
// Method I: Utility based on QoE given current state
let weightsObj = {
throughput: weightVector[0],
latency: weightVector[1],
buffer: weightVector[2],
switch: weightVector[3]
};
let downloadTime = (neuron.bitrate * segmentDuration) / currentThroughput;
let nextBuffer = getNextBuffer(currentBuffer, downloadTime);
let rebuffer = Math.max(0.00001, (downloadTime - nextBuffer));
let wt;
if (weightsObj.buffer === 0) {
wt = 10;
} else {
wt = (1 / weightsObj.buffer);
}
let weightedRebuffer = wt * rebuffer;
if (weightsObj.latency === 0) {
wt = 10;
} else {
wt = (1 / weightsObj.latency); // inverse the weight because wt and latency should have positive relationship, i.e., higher latency = higher wt
}
let weightedLatency = wt * neuron.state.latency;
let totalQoE = qoeEvaluator.calculateSingleUseQoe(neuron.bitrate, weightedRebuffer, weightedLatency, playbackRate);
if ((maxQoE === null || totalQoE > maxQoE) && _checkConstraints(currentLatency, nextBuffer, deltaLatency)) {
maxQoE = totalQoE;
winnerWeights = weightVector;
winnerBitrate = neuron.bitrate;
}
});
});
// winnerWeights was found, check if constraints are satisfied
if (winnerWeights === null && winnerBitrate === null) {
winnerWeights = -1;
}
previousLatency = currentLatency;
return winnerWeights;
}
/**
*
* @param {number} nextLatency
* @param {number} nextBuffer
* @param {number} deltaLatency
* @return {boolean}
* @private
*/
function _checkConstraints(nextLatency, nextBuffer, deltaLatency) {
// A1
// disabled till we find a better way of estimating latency
// fails for all with current value
if (nextLatency > targetLatency + deltaLatency) {
return false;
}
return nextBuffer >= bufferMin;
}
/**
*
* @param {array} list
* @param {number} length
* @return {*}
* @private
*/
function _getPermutations(list, length) {
// Copy initial values as arrays
let perm = list.map(function (val) {
return [val];
});
// Our permutation generator
let generate = function (perm, length, currLen) {
// Reached desired length
if (currLen === length) {
return perm;
}
// For each existing permutation
let len = perm.length;
for (let i = 0; i < len; i++) {
let currPerm = perm.shift();
// Create new permutation
for (let k = 0; k < list.length; k++) {
perm.push(currPerm.concat(list[k]));
}
}
// Recurse
return generate(perm, length, currLen + 1);
};
// Start with size 1 because of initial values
return generate(perm, length, 1);
}
/**
*
* @return {number}
*/
function getMinBuffer() {
return bufferMin;
}
/**
*
* @return {number}
*/
function getSegmentDuration() {
return segmentDuration;
}
/**
*
* @param {number} bitrateToDownload
* @param {number} currentBuffer
* @param {number} currentThroughput
* @return {number}
*/
function getNextBufferWithBitrate(bitrateToDownload, currentBuffer, currentThroughput) {
let downloadTime = (bitrateToDownload * segmentDuration) / currentThroughput;
return getNextBuffer(currentBuffer, downloadTime);
}
/**
*
* @param {number} currentBuffer
* @param {number} downloadTime
* @return {number}
*/
function getNextBuffer(currentBuffer, downloadTime) {
const segmentDuration = getSegmentDuration();
let nextBuffer;
if (downloadTime > segmentDuration) {
nextBuffer = currentBuffer - segmentDuration;
} else {
nextBuffer = currentBuffer + segmentDuration - downloadTime;
}
return nextBuffer;
}
instance = {
getMinBuffer,
getSegmentDuration,
getNextBufferWithBitrate,
getNextBuffer,
findWeightVector
};
_setup();
return instance;
}
LoLpWeightSelector.__dashjs_factory_name = 'LoLpWeightSelector';
export default FactoryMaker.getClassFactory(LoLpWeightSelector);