dashjs
Version:
A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.
1,215 lines (1,072 loc) • 491 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["dashjs"] = factory();
else
root["dashjs"] = factory();
})(self, function() {
return /******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/core/Debug.js":
/*!***************************!*\
!*** ./src/core/Debug.js ***!
\***************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _EventBus__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./EventBus */ "./src/core/EventBus.js");
/* harmony import */ var _events_Events__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./events/Events */ "./src/core/events/Events.js");
/* harmony import */ var _FactoryMaker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FactoryMaker */ "./src/core/FactoryMaker.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.
*/
var LOG_LEVEL_NONE = 0;
var LOG_LEVEL_FATAL = 1;
var LOG_LEVEL_ERROR = 2;
var LOG_LEVEL_WARNING = 3;
var LOG_LEVEL_INFO = 4;
var LOG_LEVEL_DEBUG = 5;
/**
* @module Debug
* @param {object} config
* @ignore
*/
function Debug(config) {
config = config || {};
var context = this.context;
var eventBus = (0,_EventBus__WEBPACK_IMPORTED_MODULE_0__["default"])(context).getInstance();
var settings = config.settings;
var logFn = [];
var instance, showLogTimestamp, showCalleeName, startTime;
function setup() {
showLogTimestamp = true;
showCalleeName = true;
startTime = new Date().getTime();
if (typeof window !== 'undefined' && window.console) {
logFn[LOG_LEVEL_FATAL] = getLogFn(window.console.error);
logFn[LOG_LEVEL_ERROR] = getLogFn(window.console.error);
logFn[LOG_LEVEL_WARNING] = getLogFn(window.console.warn);
logFn[LOG_LEVEL_INFO] = getLogFn(window.console.info);
logFn[LOG_LEVEL_DEBUG] = getLogFn(window.console.debug);
}
}
function getLogFn(fn) {
if (fn && fn.bind) {
return fn.bind(window.console);
} // if not define, return the default function for reporting logs
return window.console.log.bind(window.console);
}
/**
* Retrieves a logger which can be used to write logging information in browser console.
* @param {object} instance Object for which the logger is created. It is used
* to include calle object information in log messages.
* @memberof module:Debug
* @returns {Logger}
* @instance
*/
function getLogger(instance) {
return {
fatal: fatal.bind(instance),
error: error.bind(instance),
warn: warn.bind(instance),
info: info.bind(instance),
debug: debug.bind(instance)
};
}
/**
* Prepends a timestamp in milliseconds to each log message.
* @param {boolean} value Set to true if you want to see a timestamp in each log message.
* @default LOG_LEVEL_WARNING
* @memberof module:Debug
* @instance
*/
function setLogTimestampVisible(value) {
showLogTimestamp = value;
}
/**
* Prepends the callee object name, and media type if available, to each log message.
* @param {boolean} value Set to true if you want to see the callee object name and media type in each log message.
* @default true
* @memberof module:Debug
* @instance
*/
function setCalleeNameVisible(value) {
showCalleeName = value;
}
function fatal() {
for (var _len = arguments.length, params = new Array(_len), _key = 0; _key < _len; _key++) {
params[_key] = arguments[_key];
}
doLog.apply(void 0, [LOG_LEVEL_FATAL, this].concat(params));
}
function error() {
for (var _len2 = arguments.length, params = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
params[_key2] = arguments[_key2];
}
doLog.apply(void 0, [LOG_LEVEL_ERROR, this].concat(params));
}
function warn() {
for (var _len3 = arguments.length, params = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
params[_key3] = arguments[_key3];
}
doLog.apply(void 0, [LOG_LEVEL_WARNING, this].concat(params));
}
function info() {
for (var _len4 = arguments.length, params = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
params[_key4] = arguments[_key4];
}
doLog.apply(void 0, [LOG_LEVEL_INFO, this].concat(params));
}
function debug() {
for (var _len5 = arguments.length, params = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
params[_key5] = arguments[_key5];
}
doLog.apply(void 0, [LOG_LEVEL_DEBUG, this].concat(params));
}
function doLog(level, _this) {
var message = '';
var logTime = null;
if (showLogTimestamp) {
logTime = new Date().getTime();
message += '[' + (logTime - startTime) + ']';
}
if (showCalleeName && _this && _this.getClassName) {
message += '[' + _this.getClassName() + ']';
if (_this.getType) {
message += '[' + _this.getType() + ']';
}
}
if (message.length > 0) {
message += ' ';
}
for (var _len6 = arguments.length, params = new Array(_len6 > 2 ? _len6 - 2 : 0), _key6 = 2; _key6 < _len6; _key6++) {
params[_key6 - 2] = arguments[_key6];
}
Array.apply(null, params).forEach(function (item) {
message += item + ' ';
}); // log to console if the log level is high enough
if (logFn[level] && settings && settings.get().debug.logLevel >= level) {
logFn[level](message);
} // send log event regardless of log level
if (settings && settings.get().debug.dispatchEvent) {
eventBus.trigger(_events_Events__WEBPACK_IMPORTED_MODULE_1__["default"].LOG, {
message: message,
level: level
});
}
}
instance = {
getLogger: getLogger,
setLogTimestampVisible: setLogTimestampVisible,
setCalleeNameVisible: setCalleeNameVisible
};
setup();
return instance;
}
Debug.__dashjs_factory_name = 'Debug';
var factory = _FactoryMaker__WEBPACK_IMPORTED_MODULE_2__["default"].getSingletonFactory(Debug);
factory.LOG_LEVEL_NONE = LOG_LEVEL_NONE;
factory.LOG_LEVEL_FATAL = LOG_LEVEL_FATAL;
factory.LOG_LEVEL_ERROR = LOG_LEVEL_ERROR;
factory.LOG_LEVEL_WARNING = LOG_LEVEL_WARNING;
factory.LOG_LEVEL_INFO = LOG_LEVEL_INFO;
factory.LOG_LEVEL_DEBUG = LOG_LEVEL_DEBUG;
_FactoryMaker__WEBPACK_IMPORTED_MODULE_2__["default"].updateSingletonFactory(Debug.__dashjs_factory_name, factory);
/* harmony default export */ __webpack_exports__["default"] = (factory);
/***/ }),
/***/ "./src/core/EventBus.js":
/*!******************************!*\
!*** ./src/core/EventBus.js ***!
\******************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _FactoryMaker__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FactoryMaker */ "./src/core/FactoryMaker.js");
/* harmony import */ var _streaming_MediaPlayerEvents__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../streaming/MediaPlayerEvents */ "./src/streaming/MediaPlayerEvents.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.
*/
var EVENT_PRIORITY_LOW = 0;
var EVENT_PRIORITY_HIGH = 5000;
function EventBus() {
var handlers = {};
function on(type, listener, scope) {
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
if (!type) {
throw new Error('event type cannot be null or undefined');
}
if (!listener || typeof listener !== 'function') {
throw new Error('listener must be a function: ' + listener);
}
var priority = options.priority || EVENT_PRIORITY_LOW;
if (getHandlerIdx(type, listener, scope) >= 0) return;
handlers[type] = handlers[type] || [];
var handler = {
callback: listener,
scope: scope,
priority: priority
};
if (scope && scope.getStreamId) {
handler.streamId = scope.getStreamId();
}
if (scope && scope.getType) {
handler.mediaType = scope.getType();
}
if (options && options.mode) {
handler.mode = options.mode;
}
var inserted = handlers[type].some(function (item, idx) {
if (item && priority > item.priority) {
handlers[type].splice(idx, 0, handler);
return true;
}
});
if (!inserted) {
handlers[type].push(handler);
}
}
function off(type, listener, scope) {
if (!type || !listener || !handlers[type]) return;
var idx = getHandlerIdx(type, listener, scope);
if (idx < 0) return;
handlers[type][idx] = null;
}
function trigger(type) {
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var filters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!type || !handlers[type]) return;
payload = payload || {};
if (payload.hasOwnProperty('type')) throw new Error('\'type\' is a reserved word for event dispatching');
payload.type = type;
if (filters.streamId) {
payload.streamId = filters.streamId;
}
if (filters.mediaType) {
payload.mediaType = filters.mediaType;
}
handlers[type].filter(function (handler) {
if (!handler) {
return false;
}
if (filters.streamId && handler.streamId && handler.streamId !== filters.streamId) {
return false;
}
if (filters.mediaType && handler.mediaType && handler.mediaType !== filters.mediaType) {
return false;
} // This is used for dispatching DASH events. By default we use the onStart mode. Consequently we filter everything that has a non matching mode and the onReceive events for handlers that did not specify a mode.
if (filters.mode && handler.mode && handler.mode !== filters.mode || !handler.mode && filters.mode && filters.mode === _streaming_MediaPlayerEvents__WEBPACK_IMPORTED_MODULE_1__["default"].EVENT_MODE_ON_RECEIVE) {
return false;
}
return true;
}).forEach(function (handler) {
return handler && handler.callback.call(handler.scope, payload);
});
}
function getHandlerIdx(type, listener, scope) {
var idx = -1;
if (!handlers[type]) return idx;
handlers[type].some(function (item, index) {
if (item && item.callback === listener && (!scope || scope === item.scope)) {
idx = index;
return true;
}
});
return idx;
}
function reset() {
handlers = {};
}
var instance = {
on: on,
off: off,
trigger: trigger,
reset: reset
};
return instance;
}
EventBus.__dashjs_factory_name = 'EventBus';
var factory = _FactoryMaker__WEBPACK_IMPORTED_MODULE_0__["default"].getSingletonFactory(EventBus);
factory.EVENT_PRIORITY_LOW = EVENT_PRIORITY_LOW;
factory.EVENT_PRIORITY_HIGH = EVENT_PRIORITY_HIGH;
_FactoryMaker__WEBPACK_IMPORTED_MODULE_0__["default"].updateSingletonFactory(EventBus.__dashjs_factory_name, factory);
/* harmony default export */ __webpack_exports__["default"] = (factory);
/***/ }),
/***/ "./src/core/FactoryMaker.js":
/*!**********************************!*\
!*** ./src/core/FactoryMaker.js ***!
\**********************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/**
* 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.
*/
/**
* @module FactoryMaker
* @ignore
*/
var FactoryMaker = function () {
var instance;
var singletonContexts = [];
var singletonFactories = {};
var classFactories = {};
function extend(name, childInstance, override, context) {
if (!context[name] && childInstance) {
context[name] = {
instance: childInstance,
override: override
};
}
}
/**
* Use this method from your extended object. this.factory is injected into your object.
* this.factory.getSingletonInstance(this.context, 'VideoModel')
* will return the video model for use in the extended object.
*
* @param {Object} context - injected into extended object as this.context
* @param {string} className - string name found in all dash.js objects
* with name __dashjs_factory_name Will be at the bottom. Will be the same as the object's name.
* @returns {*} Context aware instance of specified singleton name.
* @memberof module:FactoryMaker
* @instance
*/
function getSingletonInstance(context, className) {
for (var i in singletonContexts) {
var obj = singletonContexts[i];
if (obj.context === context && obj.name === className) {
return obj.instance;
}
}
return null;
}
/**
* Use this method to add an singleton instance to the system. Useful for unit testing to mock objects etc.
*
* @param {Object} context
* @param {string} className
* @param {Object} instance
* @memberof module:FactoryMaker
* @instance
*/
function setSingletonInstance(context, className, instance) {
for (var i in singletonContexts) {
var obj = singletonContexts[i];
if (obj.context === context && obj.name === className) {
singletonContexts[i].instance = instance;
return;
}
}
singletonContexts.push({
name: className,
context: context,
instance: instance
});
}
/**
* Use this method to remove all singleton instances associated with a particular context.
*
* @param {Object} context
* @memberof module:FactoryMaker
* @instance
*/
function deleteSingletonInstances(context) {
singletonContexts = singletonContexts.filter(function (x) {
return x.context !== context;
});
}
/*------------------------------------------------------------------------------------------*/
// Factories storage Management
/*------------------------------------------------------------------------------------------*/
function getFactoryByName(name, factoriesArray) {
return factoriesArray[name];
}
function updateFactory(name, factory, factoriesArray) {
if (name in factoriesArray) {
factoriesArray[name] = factory;
}
}
/*------------------------------------------------------------------------------------------*/
// Class Factories Management
/*------------------------------------------------------------------------------------------*/
function updateClassFactory(name, factory) {
updateFactory(name, factory, classFactories);
}
function getClassFactoryByName(name) {
return getFactoryByName(name, classFactories);
}
function getClassFactory(classConstructor) {
var factory = getFactoryByName(classConstructor.__dashjs_factory_name, classFactories);
if (!factory) {
factory = function factory(context) {
if (context === undefined) {
context = {};
}
return {
create: function create() {
return merge(classConstructor, context, arguments);
}
};
};
classFactories[classConstructor.__dashjs_factory_name] = factory; // store factory
}
return factory;
}
/*------------------------------------------------------------------------------------------*/
// Singleton Factory MAangement
/*------------------------------------------------------------------------------------------*/
function updateSingletonFactory(name, factory) {
updateFactory(name, factory, singletonFactories);
}
function getSingletonFactoryByName(name) {
return getFactoryByName(name, singletonFactories);
}
function getSingletonFactory(classConstructor) {
var factory = getFactoryByName(classConstructor.__dashjs_factory_name, singletonFactories);
if (!factory) {
factory = function factory(context) {
var instance;
if (context === undefined) {
context = {};
}
return {
getInstance: function getInstance() {
// If we don't have an instance yet check for one on the context
if (!instance) {
instance = getSingletonInstance(context, classConstructor.__dashjs_factory_name);
} // If there's no instance on the context then create one
if (!instance) {
instance = merge(classConstructor, context, arguments);
singletonContexts.push({
name: classConstructor.__dashjs_factory_name,
context: context,
instance: instance
});
}
return instance;
}
};
};
singletonFactories[classConstructor.__dashjs_factory_name] = factory; // store factory
}
return factory;
}
function merge(classConstructor, context, args) {
var classInstance;
var className = classConstructor.__dashjs_factory_name;
var extensionObject = context[className];
if (extensionObject) {
var extension = extensionObject.instance;
if (extensionObject.override) {
//Override public methods in parent but keep parent.
classInstance = classConstructor.apply({
context: context
}, args);
extension = extension.apply({
context: context,
factory: instance,
parent: classInstance
}, args);
for (var prop in extension) {
if (classInstance.hasOwnProperty(prop)) {
classInstance[prop] = extension[prop];
}
}
} else {
//replace parent object completely with new object. Same as dijon.
return extension.apply({
context: context,
factory: instance
}, args);
}
} else {
// Create new instance of the class
classInstance = classConstructor.apply({
context: context
}, args);
} // Add getClassName function to class instance prototype (used by Debug)
classInstance.getClassName = function () {
return className;
};
return classInstance;
}
instance = {
extend: extend,
getSingletonInstance: getSingletonInstance,
setSingletonInstance: setSingletonInstance,
deleteSingletonInstances: deleteSingletonInstances,
getSingletonFactory: getSingletonFactory,
getSingletonFactoryByName: getSingletonFactoryByName,
updateSingletonFactory: updateSingletonFactory,
getClassFactory: getClassFactory,
getClassFactoryByName: getClassFactoryByName,
updateClassFactory: updateClassFactory
};
return instance;
}();
/* harmony default export */ __webpack_exports__["default"] = (FactoryMaker);
/***/ }),
/***/ "./src/core/Settings.js":
/*!******************************!*\
!*** ./src/core/Settings.js ***!
\******************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _FactoryMaker__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FactoryMaker */ "./src/core/FactoryMaker.js");
/* harmony import */ var _Utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Utils.js */ "./src/core/Utils.js");
/* harmony import */ var _core_Debug__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../core/Debug */ "./src/core/Debug.js");
/* harmony import */ var _streaming_constants_Constants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../streaming/constants/Constants */ "./src/streaming/constants/Constants.js");
/* harmony import */ var _streaming_vo_metrics_HTTPRequest__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../streaming/vo/metrics/HTTPRequest */ "./src/streaming/vo/metrics/HTTPRequest.js");
/* harmony import */ var _EventBus__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./EventBus */ "./src/core/EventBus.js");
/* harmony import */ var _events_Events__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./events/Events */ "./src/core/events/Events.js");
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* 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.
*/
/** @module Settings
* @description Define the configuration parameters of Dash.js MediaPlayer.
* @see {@link module:Settings~PlayerSettings PlayerSettings} for further information about the supported configuration properties.
*/
/**
* @typedef {Object} PlayerSettings
* @property {module:Settings~DebugSettings} [debug]
* Debug related settings.
* @property {module:Settings~ErrorSettings} [errors]
* Error related settings
* @property {module:Settings~StreamingSettings} [streaming]
* Streaming related settings.
* @example
*
* // Full settings object
* settings = {
* debug: {
* logLevel: Debug.LOG_LEVEL_WARNING,
* dispatchEvent: false
* },
* streaming: {
* abandonLoadTimeout: 10000,
* wallclockTimeUpdateInterval: 100,
* manifestUpdateRetryInterval: 100,
* liveUpdateTimeThresholdInMilliseconds: 0,
* cacheInitSegments: false,
* applyServiceDescription: true,
* applyProducerReferenceTime: true,
* applyContentSteering: true,
* eventControllerRefreshDelay: 100,
* enableManifestDurationMismatchFix: true,
* enableManifestTimescaleMismatchFix: false,
* parseInbandPrft: false,
* capabilities: {
* filterUnsupportedEssentialProperties: true,
* useMediaCapabilitiesApi: false
* },
* timeShiftBuffer: {
* calcFromSegmentTimeline: false,
* fallbackToSegmentTimeline: true
* },
* metrics: {
* maxListDepth: 100
* },
* delay: {
* liveDelayFragmentCount: NaN,
* liveDelay: NaN,
* useSuggestedPresentationDelay: true
* },
* protection: {
* keepProtectionMediaKeys: false,
* ignoreEmeEncryptedEvent: false,
* detectPlayreadyMessageFormat: true,
* },
* buffer: {
* enableSeekDecorrelationFix: false,
* fastSwitchEnabled: true,
* flushBufferAtTrackSwitch: false,
* reuseExistingSourceBuffers: true,
* bufferPruningInterval: 10,
* bufferToKeep: 20,
* bufferTimeAtTopQuality: 30,
* bufferTimeAtTopQualityLongForm: 60,
* initialBufferLevel: NaN,
* stableBufferTime: 12,
* longFormContentDurationThreshold: 600,
* stallThreshold: 0.3,
* useAppendWindow: true,
* setStallState: true,
* avoidCurrentTimeRangePruning: false,
* useChangeTypeForTrackSwitch: true,
* mediaSourceDurationInfinity: true,
* resetSourceBuffersForTrackSwitch: false
* },
* gaps: {
* jumpGaps: true,
* jumpLargeGaps: true,
* smallGapLimit: 1.5,
* threshold: 0.3,
* enableSeekFix: true,
* enableStallFix: false,
* stallSeek: 0.1
* },
* utcSynchronization: {
* enabled: true,
* useManifestDateHeaderTimeSource: true,
* backgroundAttempts: 2,
* timeBetweenSyncAttempts: 30,
* maximumTimeBetweenSyncAttempts: 600,
* minimumTimeBetweenSyncAttempts: 2,
* timeBetweenSyncAttemptsAdjustmentFactor: 2,
* maximumAllowedDrift: 100,
* enableBackgroundSyncAfterSegmentDownloadError: true,
* defaultTimingSource: {
* scheme: 'urn:mpeg:dash:utc:http-xsdate:2014',
* value: 'http://time.akamai.com/?iso&ms'
* }
* },
* scheduling: {
* defaultTimeout: 500,
* lowLatencyTimeout: 0,
* scheduleWhilePaused: true
* },
* text: {
* defaultEnabled: true,
* dispatchForManualRendering: false,
* extendSegmentedCues: true,
* imsc: {
* displayForcedOnlyMode: false,
* enableRollUp: true
* },
* webvtt: {
* customRenderingEnabled: false
* }
* },
* liveCatchup: {
* maxDrift: NaN,
* playbackRate: {min: NaN, max: NaN},
* playbackBufferMin: 0.5,
* enabled: null,
* mode: Constants.LIVE_CATCHUP_MODE_DEFAULT
* },
* lastBitrateCachingInfo: { enabled: true, ttl: 360000 },
* lastMediaSettingsCachingInfo: { enabled: true, ttl: 360000 },
* saveLastMediaSettingsForCurrentStreamingSession: true,
* cacheLoadThresholds: { video: 50, audio: 5 },
* trackSwitchMode: {
* audio: Constants.TRACK_SWITCH_MODE_ALWAYS_REPLACE,
* video: Constants.TRACK_SWITCH_MODE_NEVER_REPLACE
* },
* selectionModeForInitialTrack: Constants.TRACK_SELECTION_MODE_HIGHEST_SELECTION_PRIORITY,
* fragmentRequestTimeout: 20000,
* fragmentRequestProgressTimeout: -1,
* manifestRequestTimeout: 10000,
* retryIntervals: {
* [HTTPRequest.MPD_TYPE]: 500,
* [HTTPRequest.XLINK_EXPANSION_TYPE]: 500,
* [HTTPRequest.MEDIA_SEGMENT_TYPE]: 1000,
* [HTTPRequest.INIT_SEGMENT_TYPE]: 1000,
* [HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE]: 1000,
* [HTTPRequest.INDEX_SEGMENT_TYPE]: 1000,
* [HTTPRequest.MSS_FRAGMENT_INFO_SEGMENT_TYPE]: 1000,
* [HTTPRequest.LICENSE]: 1000,
* [HTTPRequest.OTHER_TYPE]: 1000,
* lowLatencyReductionFactor: 10
* },
* retryAttempts: {
* [HTTPRequest.MPD_TYPE]: 3,
* [HTTPRequest.XLINK_EXPANSION_TYPE]: 1,
* [HTTPRequest.MEDIA_SEGMENT_TYPE]: 3,
* [HTTPRequest.INIT_SEGMENT_TYPE]: 3,
* [HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE]: 3,
* [HTTPRequest.INDEX_SEGMENT_TYPE]: 3,
* [HTTPRequest.MSS_FRAGMENT_INFO_SEGMENT_TYPE]: 3,
* [HTTPRequest.LICENSE]: 3,
* [HTTPRequest.OTHER_TYPE]: 3,
* lowLatencyMultiplyFactor: 5
* },
* abr: {
* movingAverageMethod: Constants.MOVING_AVERAGE_SLIDING_WINDOW,
* ABRStrategy: Constants.ABR_STRATEGY_DYNAMIC,
* additionalAbrRules: {
* insufficientBufferRule: true,
* switchHistoryRule: true,
* droppedFramesRule: true,
* abandonRequestsRule: true
* },
* abrRulesParameters: {
* abandonRequestsRule: {
* graceTimeThreshold: 500,
* abandonMultiplier: 1.8,
* minLengthToAverage: 5
* }
* },
* bandwidthSafetyFactor: 0.9,
* useDefaultABRRules: true,
* useDeadTimeLatency: true,
* limitBitrateByPortal: false,
* usePixelRatioInLimitBitrateByPortal: false,
* maxBitrate: { audio: -1, video: -1 },
* minBitrate: { audio: -1, video: -1 },
* maxRepresentationRatio: { audio: 1, video: 1 },
* initialBitrate: { audio: -1, video: -1 },
* initialRepresentationRatio: { audio: -1, video: -1 },
* autoSwitchBitrate: { audio: true, video: true },
* fetchThroughputCalculationMode: Constants.ABR_FETCH_THROUGHPUT_CALCULATION_DOWNLOADED_DATA
* },
* cmcd: {
* enabled: false,
* sid: null,
* cid: null,
* rtp: null,
* rtpSafetyFactor: 5,
* mode: Constants.CMCD_MODE_QUERY,
* enabledKeys: ['br', 'd', 'ot', 'tb' , 'bl', 'dl', 'mtp', 'nor', 'nrr', 'su' , 'bs', 'rtp' , 'cid', 'pr', 'sf', 'sid', 'st', 'v']
* },
* cmsd: {
* enabled: false,
* abr: {
* applyMb: false,
* etpWeightRatio: 0
* }
* }
* },
* errors: {
* recoverAttempts: {
* mediaErrorDecode: 5
* }
* }
* }
*/
/**
* @typedef {Object} TimeShiftBuffer
* @property {boolean} [calcFromSegmentTimeline=false]
* Enable calculation of the DVR window for SegmentTimeline manifests based on the entries in \<SegmentTimeline\>.
* * @property {boolean} [fallbackToSegmentTimeline=true]
* In case the MPD uses \<SegmentTimeline\ and no segment is found within the DVR window the DVR window is calculated based on the entries in \<SegmentTimeline\>.
*/
/**
* @typedef {Object} LiveDelay
* @property {number} [liveDelayFragmentCount=NaN]
* Changing this value will lower or increase live stream latency.
*
* The detected segment duration will be multiplied by this value to define a time in seconds to delay a live stream from the live edge.
*
* Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.
* @property {number} [liveDelay=NaN]
* Equivalent in seconds of setLiveDelayFragmentCount.
*
* Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.
*
* This value should be less than the manifest duration by a couple of segment durations to avoid playback issues.
*
* If set, this parameter will take precedence over setLiveDelayFragmentCount and manifest info.
* @property {boolean} [useSuggestedPresentationDelay=true]
* Set to true if you would like to overwrite the default live delay and honor the SuggestedPresentationDelay attribute in by the manifest.
*/
/**
* @typedef {Object} Buffer
* @property {boolean} [enableSeekDecorrelationFix=false]
* Enables a workaround for playback start on some devices, e.g. WebOS 4.9.
* It is necessary because some browsers do not support setting currentTime on video element to a value that is outside of current buffer.
*
* If you experience unexpected seeking triggered by BufferController, you can try setting this value to false.
* @property {boolean} [fastSwitchEnabled=true]
* When enabled, after an ABR up-switch in quality, instead of requesting and appending the next fragment at the end of the current buffer range it is requested and appended closer to the current time.
*
* When enabled, The maximum time to render a higher quality is current time + (1.5 * fragment duration).
*
* Note, When ABR down-switch is detected, we appended the lower quality at the end of the buffer range to preserve the
* higher quality media for as long as possible.
*
* If enabled, it should be noted there are a few cases when the client will not replace inside buffer range but rather just append at the end.
* 1. When the buffer level is less than one fragment duration.
* 2. The client is in an Abandonment State due to recent fragment abandonment event.
*
* Known issues:
* 1. In IE11 with auto switching off, if a user switches to a quality they can not download in time the fragment may be appended in the same range as the playhead or even in the past, in IE11 it may cause a stutter or stall in playback.
* @property {boolean} [flushBufferAtTrackSwitch=false]
* When enabled, after a track switch and in case buffer is being replaced, the video element is flushed (seek at current playback time) once a segment of the new track is appended in buffer in order to force video decoder to play new track.
*
* This can be required on some devices like GoogleCast devices to make track switching functional.
*
* Otherwise track switching will be effective only once after previous buffered track is fully consumed.
* @property {boolean} [reuseExistingSourceBuffers=true]
* Enable reuse of existing MediaSource Sourcebuffers during period transition.
* @property {number} [bufferPruningInterval=10]
* The interval of pruning buffer in seconds.
* @property {number} [bufferToKeep=20]
* This value influences the buffer pruning logic.
*
* Allows you to modify the buffer that is kept in source buffer in seconds.
* 0|-----------bufferToPrune-----------|-----bufferToKeep-----|currentTime|
* @property {number} [bufferTimeAtTopQuality=30]
* The time that the internal buffer target will be set to once playing the top quality.
*
* If there are multiple bitrates in your adaptation, and the media is playing at the highest bitrate, then we try to build a larger buffer at the top quality to increase stability and to maintain media quality.
* @property {number} [bufferTimeAtTopQualityLongForm=60]
* The time that the internal buffer target will be set to once playing the top quality for long form content.
* @property {number} [longFormContentDurationThreshold=600]
* The threshold which defines if the media is considered long form content.
*
* This will directly affect the buffer targets when playing back at the top quality.
* @property {number} [initialBufferLevel=NaN]
* Initial buffer level before playback starts
* @property {number} [stableBufferTime=12]
* The time that the internal buffer target will be set to post startup/seeks (NOT top quality).
*
* When the time is set higher than the default you will have to wait longer to see automatic bitrate switches but will have a larger buffer which will increase stability.
* @property {number} [stallThreshold=0.3]
* Stall threshold used in BufferController.js to determine whether a track should still be changed and which buffer range to prune.
* @property {boolean} [useAppendWindow=true]
* Specifies if the appendWindow attributes of the MSE SourceBuffers should be set according to content duration from manifest.
* @property {boolean} [setStallState=true]
* Specifies if we fire manual waiting events once the stall threshold is reached
* @property {boolean} [avoidCurrentTimeRangePruning=false]
* Avoids pruning of the buffered range that contains the current playback time.
*
* That buffered range is likely to have been enqueued for playback. Pruning it causes a flush and reenqueue in WPE and WebKitGTK based browsers. This stresses the video decoder and can cause stuttering on embedded platforms.
* @property {boolean} [useChangeTypeForTrackSwitch=true]
* If this flag is set to true then dash.js will use the MSE v.2 API call "changeType()" before switching to a different track.
* Note that some platforms might not implement the changeType functio. dash.js is checking for the availability before trying to call it.
* @property {boolean} [mediaSourceDurationInfinity=true]
* If this flag is set to true then dash.js will allow `Infinity` to be set as the MediaSource duration otherwise the duration will be set to `Math.pow(2,32)` instead of `Infinity` to allow appending segments indefinitely.
* Some platforms such as WebOS 4.x have issues with seeking when duration is set to `Infinity`, setting this flag to false resolve this.
* @property {boolean} [resetSourceBuffersForTrackSwitch=false]
* When switching to a track that is not compatible with the currently active MSE SourceBuffers, MSE will be reset. This happens when we switch codecs on a system
* that does not properly implement "changeType()", such as webOS 4.0 and before.
*/
/**
* @typedef {Object} module:Settings~AudioVideoSettings
* @property {number|boolean|string} [audio]
* Configuration for audio media type of tracks.
* @property {number|boolean|string} [video]
* Configuration for video media type of tracks.
*/
/**
* @typedef {Object} DebugSettings
* @property {number} [logLevel=dashjs.Debug.LOG_LEVEL_WARNING]
* Sets up the log level. The levels are cumulative.
*
* For example, if you set the log level to dashjs.Debug.LOG_LEVEL_WARNING all warnings, errors and fatals will be logged.
*
* Possible values.
*
* - dashjs.Debug.LOG_LEVEL_NONE
* No message is written in the browser console.
*
* - dashjs.Debug.LOG_LEVEL_FATAL
* Log fatal errors.
* An error is considered fatal when it causes playback to fail completely.
*
* - dashjs.Debug.LOG_LEVEL_ERROR
* Log error messages.
*
* - dashjs.Debug.LOG_LEVEL_WARNING
* Log warning messages.
*
* - dashjs.Debug.LOG_LEVEL_INFO
* Log info messages.
*
* - dashjs.Debug.LOG_LEVEL_DEBUG
* Log debug messages.
* @property {boolean} [dispatchEvent=false]
* Enable to trigger a Events.LOG event whenever log output is generated.
*
* Note this will be dispatched regardless of log level.
*/
/**
* @typedef {Object} module:Settings~ErrorSettings
* @property {object} [recoverAttempts={mediaErrorDecode: 5}]
* Defines the maximum number of recover attempts for specific media errors.
*
* For mediaErrorDecode the player will reset the MSE and skip the blacklisted segment that caused the decode error. The resulting gap will be handled by the GapController.
*/
/**
* @typedef {Object} CachingInfoSettings
* @property {boolean} [enable]
* Enable or disable the caching feature.
* @property {number} [ttl]
* Time to live.
*
* A value defined in milliseconds representing how log to cache the settings for.
*/
/**
* @typedef {Object} Gaps
* @property {boolean} [jumpGaps=true]
* Sets whether player should jump small gaps (discontinuities) in the buffer.
* @property {boolean} [jumpLargeGaps=true]
* Sets whether player should jump large gaps (discontinuities) in the buffer.
* @property {number} [smallGapLimit=1.5]
* Time in seconds for a gap to be considered small.
* @property {number} [threshold=0.3]
* Threshold at which the gap handling is executed. If currentRangeEnd - currentTime < threshold the gap jump will be triggered.
* For live stream the jump might be delayed to keep a consistent live edge.
* Note that the amount of buffer at which platforms automatically stall might differ.
* @property {boolean} [enableSeekFix=true]
* Enables the adjustment of the seek target once no valid segment request could be generated for a specific seek time. This can happen if the user seeks to a position for which there is a gap in the timeline.
* @property {boolean} [enableStallFix=false]
* If playback stalled in a buffered range this fix will perform a seek by the value defined in stallSeek to trigger playback again.
* @property {number} [stallSeek=0.1]
* Value to be used in case enableStallFix is set to true
*/
/**
* @typedef {Object} UtcSynchronizationSettings
* @property {boolean} [enabled=true]
* Enables or disables the UTC clock synchronization
* @property {boolean} [useManifestDateHeaderTimeSource=true]
* Allows you to enable the use of the Date Header, if exposed with CORS, as a timing source for live edge detection.
*
* The use of the date header will happen only after the other timing source that take precedence fail or are omitted as described.
* @property {number} [backgroundAttempts=2]
* Number of synchronization attempts to perform in the background after an initial synchronization request has been done. This is used to verify that the derived client-server offset is correct.
*
* The background requests are async and done in parallel to the start of the playback.
*
* This value is also used to perform a resync after 404 errors on segments.
* @property {number} [timeBetweenSyncAttempts=30]
* The time in seconds between two consecutive sync attempts.
*
* Note: This value is used as an initial starting value. The internal value of the TimeSyncController is adjusted during playback based on the drift between two consecutive synchronization attempts.
*
* Note: A sync is only performed after an MPD update. In case the @minimumUpdatePeriod is larger than this value the sync will be delayed until the next MPD update.
* @property {number} [maximumTimeBetweenSyncAttempts=600]
* The maximum time in seconds between two consecutive sync attempts.
*
* @property {number} [minimumTimeBetweenSyncAttempts=2]
* The minimum time in seconds between two consecutive sync attempts.
*
* @property {number} [timeBetweenSyncAttemptsAdjustmentFactor=2]
* The factor used to multiply or divide the timeBetweenSyncAttempts parameter after a sync. The maximumAllowedDrift defines whether this value is used as a factor or a dividend.
*
* @property {number} [maximumAllowedDrift=100]
* The maximum allowed drift specified in milliseconds between two consecutive synchronization attempts.
*
* @property {boolean} [enableBackgroundSyncAfterSegmentDownloadError=true]
* Enables or disables the background sync after the player ran into a segment download error.
*
* @property {object} [defaultTimingSource={scheme:'urn:mpeg:dash:utc:http-xsdate:2014',value: 'http://time.akamai.com/?iso&ms'}]
* The default timing source to be used. The timing sources in the MPD take precedence over this one.
*/
/**
* @typedef {Object} Scheduling
* @property {number} [defaultTimeout=500]
* Default timeout between two consecutive segment scheduling attempts
* @property {number} [lowLatencyTimeout=0]
* Default timeout between two consecutive low-latency segment scheduling attempts
* @property {boolean} [scheduleWhilePaused=true]
* Set to true if you would like dash.js to keep downloading fragments in the background when the video element is paused.
*/
/**
* @typedef {Object} Text
* @property {boolean} [defaultEnabled=true]
* Enable/disable subtitle rendering by default.
* @property {boolean} [dispatchForManualRend