streaming_rules_abr_lolp_LoLpRule.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 Debug from '../../../../core/Debug';
import FactoryMaker from '../../../../core/FactoryMaker';
import LearningAbrController from './LearningAbrController';
import LoLpQoeEvaluator from './LoLpQoEEvaluator';
import SwitchRequest from '../../SwitchRequest';
import MetricsConstants from '../../../constants/MetricsConstants';
import LoLpWeightSelector from './LoLpWeightSelector';
import Constants from '../../../constants/Constants';
const DWS_TARGET_LATENCY = 1.5;
const DWS_BUFFER_MIN = 0.3;
function LoLPRule(config) {
config = config || {};
let dashMetrics = config.dashMetrics;
let context = this.context;
let logger,
instance,
learningController,
qoeEvaluator;
function _setup() {
logger = Debug(context).getInstance().getLogger(instance);
learningController = LearningAbrController(context).create();
qoeEvaluator = LoLpQoeEvaluator(context).create();
}
function getMaxIndex(rulesContext) {
try {
let switchRequest = SwitchRequest(context).create();
let mediaType = rulesContext.getMediaInfo().type;
let abrController = rulesContext.getAbrController();
const streamInfo = rulesContext.getStreamInfo();
let currentQuality = abrController.getQualityFor(mediaType, streamInfo.id);
const mediaInfo = rulesContext.getMediaInfo();
const bufferStateVO = dashMetrics.getCurrentBufferState(mediaType);
const scheduleController = rulesContext.getScheduleController();
const currentBufferLevel = dashMetrics.getCurrentBufferLevel(mediaType, true);
const isDynamic = streamInfo && streamInfo.manifestInfo ? streamInfo.manifestInfo.isDynamic : null;
const playbackController = scheduleController.getPlaybackController();
let latency = playbackController.getCurrentLiveLatency();
if (!rulesContext.useLoLPABR() || (mediaType === Constants.AUDIO)) {
return switchRequest;
}
if (!latency) {
latency = 0;
}
const playbackRate = playbackController.getPlaybackRate();
const throughputHistory = abrController.getThroughputHistory();
const throughput = throughputHistory.getSafeAverageThroughput(mediaType, isDynamic);
logger.debug(`Throughput ${Math.round(throughput)} kbps`);
if (isNaN(throughput) || !bufferStateVO) {
return switchRequest;
}
if (abrController.getAbandonmentStateFor(streamInfo.id, mediaType) === MetricsConstants.ABANDON_LOAD) {
return switchRequest;
}
// QoE parameters
let bitrateList = mediaInfo.bitrateList; // [{bandwidth: 200000, width: 640, height: 360}, ...]
let segmentDuration = rulesContext.getRepresentationInfo().fragmentDuration;
let minBitrateKbps = bitrateList[0].bandwidth / 1000.0; // min bitrate level
let maxBitrateKbps = bitrateList[bitrateList.length - 1].bandwidth / 1000.0; // max bitrate level
for (let i = 0; i < bitrateList.length; i++) { // in case bitrateList is not sorted as expected
let b = bitrateList[i].bandwidth / 1000.0;
if (b > maxBitrateKbps)
maxBitrateKbps = b;
else if (b < minBitrateKbps) {
minBitrateKbps = b;
}
}
// Learning rule pre-calculations
let currentBitrate = bitrateList[currentQuality].bandwidth;
let currentBitrateKbps = currentBitrate / 1000.0;
let httpRequest = dashMetrics.getCurrentHttpRequest(mediaType, true);
let lastFragmentDownloadTime = (httpRequest.tresponse.getTime() - httpRequest.trequest.getTime()) / 1000;
let segmentRebufferTime = lastFragmentDownloadTime > segmentDuration ? lastFragmentDownloadTime - segmentDuration : 0;
qoeEvaluator.setupPerSegmentQoe(segmentDuration, maxBitrateKbps, minBitrateKbps);
qoeEvaluator.logSegmentMetrics(currentBitrateKbps, segmentRebufferTime, latency, playbackRate);
/*
* Dynamic Weights Selector (step 1/2: initialization)
*/
let dynamicWeightsSelector = LoLpWeightSelector(context).create({
targetLatency: DWS_TARGET_LATENCY,
bufferMin: DWS_BUFFER_MIN,
segmentDuration,
qoeEvaluator
});
/*
* Select next quality
*/
switchRequest.quality = learningController.getNextQuality(mediaInfo, throughput * 1000, latency, currentBufferLevel, playbackRate, currentQuality, dynamicWeightsSelector);
switchRequest.reason = { throughput: throughput, latency: latency };
switchRequest.priority = SwitchRequest.PRIORITY.STRONG;
scheduleController.setTimeToLoadDelay(0);
if (switchRequest.quality !== currentQuality) {
logger.debug('[TgcLearningRule][' + mediaType + '] requesting switch to index: ', switchRequest.quality, 'Average throughput', Math.round(throughput), 'kbps');
}
return switchRequest;
} catch (e) {
throw e;
}
}
/**
* Reset objects to their initial state
* @private
*/
function _resetInitialSettings() {
learningController.reset();
qoeEvaluator.reset();
}
/**
* Reset the rule
*/
function reset() {
_resetInitialSettings();
}
instance = {
getMaxIndex,
reset
};
_setup();
return instance;
}
LoLPRule.__dashjs_factory_name = 'LoLPRule';
export default FactoryMaker.getClassFactory(LoLPRule);