streaming_models_MediaPlayerModel.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.
*/
import Debug from '../../core/Debug';
import FactoryMaker from '../../core/FactoryMaker';
import Settings from '../../core/Settings';
const DEFAULT_MIN_BUFFER_TIME = 12;
const DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH = 20;
const LOW_LATENCY_REDUCTION_FACTOR = 10;
const LOW_LATENCY_MULTIPLY_FACTOR = 5;
const DEFAULT_CATCHUP_MAX_DRIFT = 12;
const DEFAULT_CATCHUP_PLAYBACK_RATE_MIN = -0.5;
const DEFAULT_CATCHUP_PLAYBACK_RATE_MAX = 0.5;
const CATCHUP_PLAYBACK_RATE_MIN_LIMIT = -0.5;
const CATCHUP_PLAYBACK_RATE_MAX_LIMIT = 1;
/**
* We use this model as a wrapper/proxy between Settings.js and classes that are using parameters from Settings.js.
* In some cases we require additional logic to be applied and the settings might need to be adjusted before being used.
* @class
* @constructor
*/
function MediaPlayerModel() {
let instance,
logger,
playbackController,
serviceDescriptionController;
const context = this.context;
const settings = Settings(context).getInstance();
function setup() {
logger = Debug(context).getInstance().getLogger(instance);
}
function setConfig(config) {
if (config.playbackController) {
playbackController = config.playbackController;
}
if (config.serviceDescriptionController) {
serviceDescriptionController = config.serviceDescriptionController;
}
}
/**
* Checks the supplied min playback rate is a valid vlaue and within supported limits
* @param {number} rate - Supplied min playback rate
* @param {boolean} log - wether to shown warning or not
* @returns {number} corrected min playback rate
*/
function _checkMinPlaybackRate (rate, log) {
if (isNaN(rate)) return 0;
if (rate > 0) {
if (log) {
logger.warn(`Supplied minimum playback rate is a positive value when it should be negative or 0. The supplied rate will not be applied and set to 0: 100% playback speed.`)
}
return 0;
}
if (rate < CATCHUP_PLAYBACK_RATE_MIN_LIMIT) {
if (log) {
logger.warn(`Supplied minimum playback rate is out of range and will be limited to ${CATCHUP_PLAYBACK_RATE_MIN_LIMIT}: ${CATCHUP_PLAYBACK_RATE_MIN_LIMIT * 100}% playback speed.`);
}
return CATCHUP_PLAYBACK_RATE_MIN_LIMIT;
}
return rate;
};
/**
* Checks the supplied max playback rate is a valid vlaue and within supported limits
* @param {number} rate - Supplied max playback rate
* @param {boolean} log - wether to shown warning or not
* @returns {number} corrected max playback rate
*/
function _checkMaxPlaybackRate (rate, log) {
if (isNaN(rate)) return 0;
if (rate < 0) {
if (log) {
logger.warn(`Supplied maximum playback rate is a negative value when it should be negative or 0. The supplied rate will not be applied and set to 0: 100% playback speed.`)
}
return 0;
}
if (rate > CATCHUP_PLAYBACK_RATE_MAX_LIMIT) {
if (log) {
logger.warn(`Supplied maximum playback rate is out of range and will be limited to ${CATCHUP_PLAYBACK_RATE_MAX_LIMIT}: ${(1 + CATCHUP_PLAYBACK_RATE_MAX_LIMIT) * 100}% playback speed.`);
}
return CATCHUP_PLAYBACK_RATE_MAX_LIMIT;
}
return rate;
};
/**
* Returns the maximum drift allowed before applying a seek back to the live edge when the catchup mode is enabled
* @return {number}
*/
function getCatchupMaxDrift() {
if (!isNaN(settings.get().streaming.liveCatchup.maxDrift) && settings.get().streaming.liveCatchup.maxDrift > 0) {
return settings.get().streaming.liveCatchup.maxDrift;
}
const serviceDescriptionSettings = serviceDescriptionController.getServiceDescriptionSettings();
if (serviceDescriptionSettings && serviceDescriptionSettings.liveCatchup && !isNaN(serviceDescriptionSettings.liveCatchup.maxDrift) && serviceDescriptionSettings.liveCatchup.maxDrift > 0) {
return serviceDescriptionSettings.liveCatchup.maxDrift;
}
return DEFAULT_CATCHUP_MAX_DRIFT;
}
/**
* Returns the minimum and maximum playback rates to be used when applying the catchup mechanism
* If only one of the min/max values has been set then the other will default to 0 (no playback rate change).
* @return {number}
*/
function getCatchupPlaybackRates(log) {
const settingsPlaybackRate = settings.get().streaming.liveCatchup.playbackRate;
if(!isNaN(settingsPlaybackRate.min) || !isNaN(settingsPlaybackRate.max)) {
return {
min: _checkMinPlaybackRate(settingsPlaybackRate.min, log),
max: _checkMaxPlaybackRate(settingsPlaybackRate.max, log),
}
}
const serviceDescriptionSettings = serviceDescriptionController.getServiceDescriptionSettings();
if (serviceDescriptionSettings && serviceDescriptionSettings.liveCatchup && (!isNaN(serviceDescriptionSettings.liveCatchup.playbackRate.min) || !isNaN(serviceDescriptionSettings.liveCatchup.playbackRate.max))) {
const sdPlaybackRate = serviceDescriptionSettings.liveCatchup.playbackRate;
return {
min: _checkMinPlaybackRate(sdPlaybackRate.min, log),
max: _checkMaxPlaybackRate(sdPlaybackRate.max, log),
}
}
return {
min: DEFAULT_CATCHUP_PLAYBACK_RATE_MIN,
max: DEFAULT_CATCHUP_PLAYBACK_RATE_MAX
}
}
/**
* Returns whether the catchup mode is activated via the settings or internally in the PlaybackController
* @return {boolean}
*/
function getCatchupModeEnabled() {
if (settings.get().streaming.liveCatchup.enabled !== null) {
return settings.get().streaming.liveCatchup.enabled;
}
return playbackController.getInitialCatchupModeActivated();
}
/**
* Returns the min,max or initial bitrate for a specific media type.
* @param {string} field
* @param {string} mediaType
*/
function getAbrBitrateParameter(field, mediaType) {
try {
const setting = settings.get().streaming.abr[field][mediaType];
if(!isNaN(setting) && setting !== -1) {
return setting;
}
const serviceDescriptionSettings = serviceDescriptionController.getServiceDescriptionSettings();
if(serviceDescriptionSettings && serviceDescriptionSettings[field] && !isNaN(serviceDescriptionSettings[field][mediaType])) {
return serviceDescriptionSettings[field][mediaType];
}
return -1;
}
catch(e) {
return -1;
}
}
/**
* Returns the initial buffer level taking the stable buffer time into account
* @return {number}
*/
function getInitialBufferLevel() {
const initialBufferLevel = settings.get().streaming.buffer.initialBufferLevel;
if (isNaN(initialBufferLevel) || initialBufferLevel < 0) {
return 0;
}
return Math.min(getStableBufferTime(), initialBufferLevel);
}
/**
* Returns the stable buffer time taking the live delay into account
* @return {number}
*/
function getStableBufferTime() {
let stableBufferTime = settings.get().streaming.buffer.stableBufferTime > 0 ? settings.get().streaming.buffer.stableBufferTime : settings.get().streaming.buffer.fastSwitchEnabled ? DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH : DEFAULT_MIN_BUFFER_TIME;
const liveDelay = playbackController.getLiveDelay();
return !isNaN(liveDelay) && liveDelay > 0 ? Math.min(stableBufferTime, liveDelay) : stableBufferTime;
}
/**
* Returns the number of retry attempts for a specific media type
* @param type
* @return {number}
*/
function getRetryAttemptsForType(type) {
const lowLatencyMultiplyFactor = !isNaN(settings.get().streaming.retryAttempts.lowLatencyMultiplyFactor) ? settings.get().streaming.retryAttempts.lowLatencyMultiplyFactor : LOW_LATENCY_MULTIPLY_FACTOR;
return playbackController.getLowLatencyModeEnabled() ? settings.get().streaming.retryAttempts[type] * lowLatencyMultiplyFactor : settings.get().streaming.retryAttempts[type];
}
/**
* Returns the retry interval for a specific media type
* @param type
* @return {number}
*/
function getRetryIntervalsForType(type) {
const lowLatencyReductionFactor = !isNaN(settings.get().streaming.retryIntervals.lowLatencyReductionFactor) ? settings.get().streaming.retryIntervals.lowLatencyReductionFactor : LOW_LATENCY_REDUCTION_FACTOR;
return playbackController.getLowLatencyModeEnabled() ? settings.get().streaming.retryIntervals[type] / lowLatencyReductionFactor : settings.get().streaming.retryIntervals[type];
}
function reset() {
}
instance = {
getCatchupMaxDrift,
getCatchupModeEnabled,
getStableBufferTime,
getInitialBufferLevel,
getRetryAttemptsForType,
getRetryIntervalsForType,
getCatchupPlaybackRates,
getAbrBitrateParameter,
setConfig,
reset
};
setup();
return instance;
}
MediaPlayerModel.__dashjs_factory_name = 'MediaPlayerModel';
export default FactoryMaker.getSingletonFactory(MediaPlayerModel);