UNPKG

dashjs

Version:

A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.

10 lines (9 loc) 1.62 MB
<html> <head> </head> <body style="background: transparent;"> <script src="scripts/docstrap.lib.js"></script> <script src="scripts/lunr.min.js"></script> <script src="scripts/fulltext-search.js"></script> <script type="text/x-docstrap-searchdb"> {"streaming_controllers_AbrController.js.html":{"id":"streaming_controllers_AbrController.js.html","title":"Source: streaming/controllers/AbrController.js","body":" dash.js Modules DashAdapterDashMetricsMediaPlayerOfflineControllerProtectionControllerSettings Classes ErrorsMediaPlayerEventsMediaPlayerModelMetricsReportingEventsMssErrorsOfflineErrorsOfflineEventsProtectionErrorsProtectionEvents Events MediaPlayerEvents#event:ADAPTATION_SET_REMOVED_NO_CAPABILITIESMediaPlayerEvents#event:AST_IN_FUTUREMediaPlayerEvents#event:BUFFER_EMPTYMediaPlayerEvents#event:BUFFER_LEVEL_STATE_CHANGEDMediaPlayerEvents#event:BUFFER_LEVEL_UPDATEDMediaPlayerEvents#event:BUFFER_LOADEDMediaPlayerEvents#event:CAN_PLAYMediaPlayerEvents#event:CAN_PLAY_THROUGHMediaPlayerEvents#event:CAPTION_CONTAINER_RESIZEMediaPlayerEvents#event:CAPTION_RENDEREDMediaPlayerEvents#event:CONFORMANCE_VIOLATIONMediaPlayerEvents#event:CONTENT_STEERING_REQUEST_COMPLETEDMediaPlayerEvents#event:DYNAMIC_TO_STATICMediaPlayerEvents#event:ERRORMediaPlayerEvents#event:EVENT_MODE_ON_RECEIVEMediaPlayerEvents#event:EVENT_MODE_ON_STARTMediaPlayerEvents#event:FRAGMENT_LOADING_ABANDONEDMediaPlayerEvents#event:FRAGMENT_LOADING_COMPLETEDMediaPlayerEvents#event:FRAGMENT_LOADING_PROGRESSMediaPlayerEvents#event:FRAGMENT_LOADING_STARTEDMediaPlayerEvents#event:LOGMediaPlayerEvents#event:MANIFEST_LOADEDMediaPlayerEvents#event:MANIFEST_VALIDITY_CHANGEDMediaPlayerEvents#event:METRIC_ADDEDMediaPlayerEvents#event:METRIC_CHANGEDMediaPlayerEvents#event:METRIC_UPDATEDMediaPlayerEvents#event:METRICS_CHANGEDMediaPlayerEvents#event:PERIOD_SWITCH_COMPLETEDMediaPlayerEvents#event:PERIOD_SWITCH_STARTEDMediaPlayerEvents#event:PLAYBACK_ENDEDMediaPlayerEvents#event:PLAYBACK_ERRORMediaPlayerEvents#event:PLAYBACK_LOADED_DATAMediaPlayerEvents#event:PLAYBACK_METADATA_LOADEDMediaPlayerEvents#event:PLAYBACK_NOT_ALLOWEDMediaPlayerEvents#event:PLAYBACK_PAUSEDMediaPlayerEvents#event:PLAYBACK_PLAYINGMediaPlayerEvents#event:PLAYBACK_PROGRESSMediaPlayerEvents#event:PLAYBACK_RATE_CHANGEDMediaPlayerEvents#event:PLAYBACK_SEEKEDMediaPlayerEvents#event:PLAYBACK_SEEKINGMediaPlayerEvents#event:PLAYBACK_STALLEDMediaPlayerEvents#event:PLAYBACK_STARTEDMediaPlayerEvents#event:PLAYBACK_TIME_UPDATEDMediaPlayerEvents#event:PLAYBACK_VOLUME_CHANGEDMediaPlayerEvents#event:PLAYBACK_WAITINGMediaPlayerEvents#event:QUALITY_CHANGE_RENDEREDMediaPlayerEvents#event:QUALITY_CHANGE_REQUESTEDMediaPlayerEvents#event:REPRESENTATION_SWITCHMediaPlayerEvents#event:STREAM_ACTIVATEDMediaPlayerEvents#event:STREAM_DEACTIVATEDMediaPlayerEvents#event:STREAM_INITIALIZEDMediaPlayerEvents#event:STREAM_INITIALIZINGMediaPlayerEvents#event:STREAM_TEARDOWN_COMPLETEMediaPlayerEvents#event:STREAM_UPDATEDMediaPlayerEvents#event:TEXT_TRACK_ADDEDMediaPlayerEvents#event:TEXT_TRACKS_ADDEDMediaPlayerEvents#event:TRACK_CHANGE_RENDEREDMediaPlayerEvents#event:TTML_PARSEDMediaPlayerEvents#event:TTML_TO_PARSEMetricsReportingEvents#event:CMCD_DATA_GENERATEDOfflineEvents#event:OFFLINE_RECORD_FINISHEDOfflineEvents#event:OFFLINE_RECORD_LOADEDMETADATAOfflineEvents#event:OFFLINE_RECORD_STARTEDOfflineEvents#event:OFFLINE_RECORD_STOPPEDProtectionEvents#event:KEY_ADDEDProtectionEvents#event:KEY_ERRORProtectionEvents#event:KEY_MESSAGEProtectionEvents#event:KEY_SESSION_CLOSEDProtectionEvents#event:KEY_SESSION_CREATEDProtectionEvents#event:KEY_SESSION_REMOVEDProtectionEvents#event:KEY_STATUSES_CHANGEDProtectionEvents#event:KEY_SYSTEM_SELECTEDProtectionEvents#event:LICENSE_REQUEST_COMPLETEProtectionEvents#event:LICENSE_REQUEST_SENDINGProtectionEvents#event:PROTECTION_CREATEDProtectionEvents#event:PROTECTION_DESTROYED Global LICENSE_SERVER_MANIFEST_CONFIGURATIONS Source: streaming/controllers/AbrController.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 ABRRulesCollection from '../rules/abr/ABRRulesCollection'; import Constants from '../constants/Constants'; import MetricsConstants from '../constants/MetricsConstants'; import BitrateInfo from '../vo/BitrateInfo'; import FragmentModel from '../models/FragmentModel'; import EventBus from '../../core/EventBus'; import Events from '../../core/events/Events'; import FactoryMaker from '../../core/FactoryMaker'; import RulesContext from '../rules/RulesContext'; import SwitchRequest from '../rules/SwitchRequest'; import SwitchRequestHistory from '../rules/SwitchRequestHistory'; import DroppedFramesHistory from '../rules/DroppedFramesHistory'; import ThroughputHistory from '../rules/ThroughputHistory'; import Debug from '../../core/Debug'; import {HTTPRequest} from '../vo/metrics/HTTPRequest'; import {checkInteger} from '../utils/SupervisorTools'; import MediaPlayerEvents from '../MediaPlayerEvents'; const DEFAULT_VIDEO_BITRATE = 1000; const DEFAULT_AUDIO_BITRATE = 100; const QUALITY_DEFAULT = 0; function AbrController() { const context = this.context; const debug = Debug(context).getInstance(); const eventBus = EventBus(context).getInstance(); let instance, logger, abrRulesCollection, streamController, topQualities, qualityDict, streamProcessorDict, abandonmentStateDict, abandonmentTimeout, windowResizeEventCalled, elementWidth, elementHeight, adapter, videoModel, mediaPlayerModel, customParametersModel, domStorage, playbackIndex, switchHistoryDict, droppedFramesHistory, throughputHistory, isUsingBufferOccupancyAbrDict, isUsingL2AAbrDict, isUsingLoLPAbrDict, dashMetrics, settings; function setup() { logger = debug.getLogger(instance); resetInitialSettings(); } /** * Initialize everything that is not Stream specific. We only have one instance of the ABR Controller for all periods. */ function initialize() { droppedFramesHistory = DroppedFramesHistory(context).create(); throughputHistory = ThroughputHistory(context).create({ settings }); abrRulesCollection = ABRRulesCollection(context).create({ dashMetrics, customParametersModel, mediaPlayerModel, settings }); abrRulesCollection.initialize(); eventBus.on(MediaPlayerEvents.QUALITY_CHANGE_RENDERED, _onQualityChangeRendered, instance); eventBus.on(MediaPlayerEvents.METRIC_ADDED, _onMetricAdded, instance); eventBus.on(Events.LOADING_PROGRESS, _onFragmentLoadProgress, instance); } /** * Whenever a StreamProcessor is created it is added to the list of streamProcessorDict * In addition, the corresponding objects for this object and its stream id are created * @param {object} type * @param {object} streamProcessor */ function registerStreamType(type, streamProcessor) { const streamId = streamProcessor.getStreamInfo().id; if (!streamProcessorDict[streamId]) { streamProcessorDict[streamId] = {}; } if (!switchHistoryDict[streamId]) { switchHistoryDict[streamId] = {}; } if (!abandonmentStateDict[streamId]) { abandonmentStateDict[streamId] = {}; } switchHistoryDict[streamId][type] = SwitchRequestHistory(context).create(); streamProcessorDict[streamId][type] = streamProcessor; abandonmentStateDict[streamId][type] = {}; abandonmentStateDict[streamId][type].state = MetricsConstants.ALLOW_LOAD; _initializeAbrStrategy(type); if (type === Constants.VIDEO) { setElementSize(); } } function _initializeAbrStrategy(type) { const strategy = settings.get().streaming.abr.ABRStrategy; if (strategy === Constants.ABR_STRATEGY_L2A) { isUsingBufferOccupancyAbrDict[type] = false; isUsingLoLPAbrDict[type] = false; isUsingL2AAbrDict[type] = true; } else if (strategy === Constants.ABR_STRATEGY_LoLP) { isUsingBufferOccupancyAbrDict[type] = false; isUsingLoLPAbrDict[type] = true; isUsingL2AAbrDict[type] = false; } else if (strategy === Constants.ABR_STRATEGY_BOLA) { isUsingBufferOccupancyAbrDict[type] = true; isUsingLoLPAbrDict[type] = false; isUsingL2AAbrDict[type] = false; } else if (strategy === Constants.ABR_STRATEGY_THROUGHPUT) { isUsingBufferOccupancyAbrDict[type] = false; isUsingLoLPAbrDict[type] = false; isUsingL2AAbrDict[type] = false; } else if (strategy === Constants.ABR_STRATEGY_DYNAMIC) { isUsingBufferOccupancyAbrDict[type] = isUsingBufferOccupancyAbrDict &amp;&amp; isUsingBufferOccupancyAbrDict[type] ? isUsingBufferOccupancyAbrDict[type] : false; isUsingLoLPAbrDict[type] = false; isUsingL2AAbrDict[type] = false; } } function unRegisterStreamType(streamId, type) { try { if (streamProcessorDict[streamId] &amp;&amp; streamProcessorDict[streamId][type]) { delete streamProcessorDict[streamId][type]; } if (switchHistoryDict[streamId] &amp;&amp; switchHistoryDict[streamId][type]) { delete switchHistoryDict[streamId][type]; } if (abandonmentStateDict[streamId] &amp;&amp; abandonmentStateDict[streamId][type]) { delete abandonmentStateDict[streamId][type]; } } catch (e) { } } function resetInitialSettings() { topQualities = {}; qualityDict = {}; abandonmentStateDict = {}; streamProcessorDict = {}; switchHistoryDict = {}; isUsingBufferOccupancyAbrDict = {}; isUsingL2AAbrDict = {}; isUsingLoLPAbrDict = {}; if (windowResizeEventCalled === undefined) { windowResizeEventCalled = false; } if (droppedFramesHistory) { droppedFramesHistory.reset(); } playbackIndex = undefined; droppedFramesHistory = undefined; throughputHistory = undefined; clearTimeout(abandonmentTimeout); abandonmentTimeout = null; } function reset() { resetInitialSettings(); eventBus.off(Events.LOADING_PROGRESS, _onFragmentLoadProgress, instance); eventBus.off(MediaPlayerEvents.QUALITY_CHANGE_RENDERED, _onQualityChangeRendered, instance); eventBus.off(MediaPlayerEvents.METRIC_ADDED, _onMetricAdded, instance); if (abrRulesCollection) { abrRulesCollection.reset(); } } function setConfig(config) { if (!config) return; if (config.streamController) { streamController = config.streamController; } if (config.domStorage) { domStorage = config.domStorage; } if (config.mediaPlayerModel) { mediaPlayerModel = config.mediaPlayerModel; } if (config.customParametersModel) { customParametersModel = config.customParametersModel; } if (config.dashMetrics) { dashMetrics = config.dashMetrics; } if (config.adapter) { adapter = config.adapter; } if (config.videoModel) { videoModel = config.videoModel; } if (config.settings) { settings = config.settings; } } function checkConfig() { if (!domStorage || !domStorage.hasOwnProperty('getSavedBitrateSettings')) { throw new Error(Constants.MISSING_CONFIG_ERROR); } } /** * While fragment loading is in progress we check if we might need to abort the request * @param {object} e * @private */ function _onFragmentLoadProgress(e) { const type = e.request.mediaType; const streamId = e.streamId; if (!type || !streamId || !streamProcessorDict[streamId] || !settings.get().streaming.abr.autoSwitchBitrate[type]) { return; } const streamProcessor = streamProcessorDict[streamId][type]; if (!streamProcessor) { return; } const rulesContext = RulesContext(context).create({ abrController: instance, streamProcessor: streamProcessor, currentRequest: e.request, useBufferOccupancyABR: isUsingBufferOccupancyAbrDict[type], useL2AABR: isUsingL2AAbrDict[type], useLoLPABR: isUsingLoLPAbrDict[type], videoModel }); const switchRequest = abrRulesCollection.shouldAbandonFragment(rulesContext, streamId); if (switchRequest.quality &gt; SwitchRequest.NO_CHANGE) { const fragmentModel = streamProcessor.getFragmentModel(); const request = fragmentModel.getRequests({ state: FragmentModel.FRAGMENT_MODEL_LOADING, index: e.request.index })[0]; if (request) { abandonmentStateDict[streamId][type].state = MetricsConstants.ABANDON_LOAD; switchHistoryDict[streamId][type].reset(); switchHistoryDict[streamId][type].push({ oldValue: getQualityFor(type, streamId), newValue: switchRequest.quality, confidence: 1, reason: switchRequest.reason }); setPlaybackQuality(type, streamController.getActiveStreamInfo(), switchRequest.quality, switchRequest.reason); clearTimeout(abandonmentTimeout); abandonmentTimeout = setTimeout( () =&gt; { abandonmentStateDict[streamId][type].state = MetricsConstants.ALLOW_LOAD; abandonmentTimeout = null; }, settings.get().streaming.abandonLoadTimeout ); } } } /** * Update dropped frames history when the quality was changed * @param {object} e * @private */ function _onQualityChangeRendered(e) { if (e.mediaType === Constants.VIDEO) { if (playbackIndex !== undefined) { droppedFramesHistory.push(e.streamId, playbackIndex, videoModel.getPlaybackQuality()); } playbackIndex = e.newQuality; } } /** * When the buffer level is updated we check if we need to change the ABR strategy * @param e * @private */ function _onMetricAdded(e) { if (e.metric === MetricsConstants.HTTP_REQUEST &amp;&amp; e.value &amp;&amp; e.value.type === HTTPRequest.MEDIA_SEGMENT_TYPE &amp;&amp; (e.mediaType === Constants.AUDIO || e.mediaType === Constants.VIDEO)) { throughputHistory.push(e.mediaType, e.value, settings.get().streaming.abr.useDeadTimeLatency); } if (e.metric === MetricsConstants.BUFFER_LEVEL &amp;&amp; (e.mediaType === Constants.AUDIO || e.mediaType === Constants.VIDEO)) { _updateAbrStrategy(e.mediaType, 0.001 * e.value.level); } } /** * Returns the highest possible index taking limitations like maxBitrate, representationRatio and portal size into account. * @param {string} type * @param {string} streamId * @return {number} */ function getMaxAllowedIndexFor(type, streamId) { try { let idx; topQualities[streamId] = topQualities[streamId] || {}; if (!topQualities[streamId].hasOwnProperty(type)) { topQualities[streamId][type] = 0; } idx = _checkMaxBitrate(type, streamId); idx = _checkMaxRepresentationRatio(idx, type, streamId); idx = _checkPortalSize(idx, type, streamId); return idx; } catch (e) { return undefined } } /** * Returns the minimum allowed index. We consider thresholds defined in the settings, i.e. minBitrate for the corresponding media type. * @param {string} type * @param {string} streamId * @return {undefined|number} */ function getMinAllowedIndexFor(type, streamId) { try { return _getMinIndexBasedOnBitrateFor(type, streamId); } catch (e) { return undefined } } /** * Returns the maximum allowed index. * @param {string} type * @param {string} streamId * @return {undefined|number} */ function _getMaxIndexBasedOnBitrateFor(type, streamId) { try { const maxBitrate = mediaPlayerModel.getAbrBitrateParameter('maxBitrate', type); if (maxBitrate &gt; -1) { return getQualityForBitrate(streamProcessorDict[streamId][type].getMediaInfo(), maxBitrate, streamId); } else { return undefined; } } catch (e) { return undefined } } /** * Returns the minimum allowed index. * @param {string} type * @param {string} streamId * @return {undefined|number} */ function _getMinIndexBasedOnBitrateFor(type, streamId) { try { const minBitrate = mediaPlayerModel.getAbrBitrateParameter('minBitrate', type); if (minBitrate &gt; -1) { const mediaInfo = streamProcessorDict[streamId][type].getMediaInfo(); const bitrateList = getBitrateList(mediaInfo); // This returns the quality index &lt;= for the given bitrate let minIdx = getQualityForBitrate(mediaInfo, minBitrate, streamId); if (bitrateList[minIdx] &amp;&amp; minIdx &lt; bitrateList.length - 1 &amp;&amp; bitrateList[minIdx].bitrate &lt; minBitrate * 1000) { minIdx++; // Go to the next bitrate } return minIdx; } else { return undefined; } } catch (e) { return undefined; } } /** * Returns the maximum possible index * @param type * @param streamId * @return {number|*} */ function _checkMaxBitrate(type, streamId) { let idx = topQualities[streamId][type]; let newIdx = idx; if (!streamProcessorDict[streamId] || !streamProcessorDict[streamId][type]) { return newIdx; } const minIdx = getMinAllowedIndexFor(type, streamId); if (minIdx !== undefined) { newIdx = Math.max(idx, minIdx); } const maxIdx = _getMaxIndexBasedOnBitrateFor(type, streamId); if (maxIdx !== undefined) { newIdx = Math.min(newIdx, maxIdx); } return newIdx; } /** * Returns the maximum index according to maximum representation ratio * @param idx * @param type * @param streamId * @return {number|*} * @private */ function _checkMaxRepresentationRatio(idx, type, streamId) { let maxIdx = topQualities[streamId][type] const maxRepresentationRatio = settings.get().streaming.abr.maxRepresentationRatio[type]; if (isNaN(maxRepresentationRatio) || maxRepresentationRatio &gt;= 1 || maxRepresentationRatio &lt; 0) { return idx; } return Math.min(idx, Math.round(maxIdx * maxRepresentationRatio)); } /** * Returns the maximum index according to the portal size * @param idx * @param type * @param streamId * @return {number|*} * @private */ function _checkPortalSize(idx, type, streamId) { if (type !== Constants.VIDEO || !settings.get().streaming.abr.limitBitrateByPortal || !streamProcessorDict[streamId] || !streamProcessorDict[streamId][type]) { return idx; } if (!windowResizeEventCalled) { setElementSize(); } const streamInfo = streamProcessorDict[streamId][type].getStreamInfo(); const representation = adapter.getAdaptationForType(streamInfo.index, type, streamInfo).Representation_asArray; let newIdx = idx; if (elementWidth &gt; 0 &amp;&amp; elementHeight &gt; 0) { while ( newIdx &gt; 0 &amp;&amp; representation[newIdx] &amp;&amp; elementWidth &lt; representation[newIdx].width &amp;&amp; elementWidth - representation[newIdx - 1].width &lt; representation[newIdx].width - elementWidth) { newIdx = newIdx - 1; } // Make sure that in case of multiple representation elements have same // resolution, every such element is included while (newIdx &lt; representation.length - 1 &amp;&amp; representation[newIdx].width === representation[newIdx + 1].width) { newIdx = newIdx + 1; } } return newIdx; } /** * Gets top BitrateInfo for the player * @param {string} type - 'video' or 'audio' are the type options. * @param {string} streamId - Id of the stream * @returns {BitrateInfo | null} */ function getTopBitrateInfoFor(type, streamId = null) { if (!streamId) { streamId = streamController.getActiveStreamInfo().id; } if (type &amp;&amp; streamProcessorDict &amp;&amp; streamProcessorDict[streamId] &amp;&amp; streamProcessorDict[streamId][type]) { const idx = getMaxAllowedIndexFor(type, streamId); const bitrates = getBitrateList(streamProcessorDict[streamId][type].getMediaInfo()); return bitrates[idx] ? bitrates[idx] : null; } return null; } /** * Returns the initial bitrate for a specific media type and stream id * @param {string} type * @param {string} streamId * @returns {number} A value of the initial bitrate, kbps * @memberof AbrController# */ function getInitialBitrateFor(type, streamId) { checkConfig(); if (type === Constants.TEXT) { return NaN; } const savedBitrate = domStorage.getSavedBitrateSettings(type); let configBitrate = mediaPlayerModel.getAbrBitrateParameter('initialBitrate', type); let configRatio = settings.get().streaming.abr.initialRepresentationRatio[type]; if (configBitrate === -1) { if (configRatio &gt; -1) { const streamInfo = streamProcessorDict[streamId][type].getStreamInfo(); const representation = adapter.getAdaptationForType(streamInfo.index, type, streamInfo).Representation_asArray; if (Array.isArray(representation)) { const repIdx = Math.max(Math.round(representation.length * configRatio) - 1, 0); configBitrate = representation[repIdx].bandwidth / 1000; } else { configBitrate = 0; } } else if (!isNaN(savedBitrate)) { configBitrate = savedBitrate; } else { configBitrate = (type === Constants.VIDEO) ? DEFAULT_VIDEO_BITRATE : DEFAULT_AUDIO_BITRATE; } } return configBitrate; } /** * This function is called by the scheduleControllers to check if the quality should be changed. * Consider this the main entry point for the ABR decision logic * @param {string} type * @param {string} streamId */ function checkPlaybackQuality(type, streamId) { try { if (!type || !streamProcessorDict || !streamProcessorDict[streamId] || !streamProcessorDict[streamId][type]) { return false; } if (droppedFramesHistory) { const playbackQuality = videoModel.getPlaybackQuality(); if (playbackQuality) { droppedFramesHistory.push(streamId, playbackIndex, playbackQuality); } } // ABR is turned off, do nothing if (!settings.get().streaming.abr.autoSwitchBitrate[type]) { return false; } const oldQuality = getQualityFor(type, streamId); const rulesContext = RulesContext(context).create({ abrController: instance, switchHistory: switchHistoryDict[streamId][type], droppedFramesHistory: droppedFramesHistory, streamProcessor: streamProcessorDict[streamId][type], currentValue: oldQuality, useBufferOccupancyABR: isUsingBufferOccupancyAbrDict[type], useL2AABR: isUsingL2AAbrDict[type], useLoLPABR: isUsingLoLPAbrDict[type], videoModel }); const minIdx = getMinAllowedIndexFor(type, streamId); const maxIdx = getMaxAllowedIndexFor(type, streamId); const switchRequest = abrRulesCollection.getMaxQuality(rulesContext); let newQuality = switchRequest.quality; if (minIdx !== undefined &amp;&amp; ((newQuality &gt; SwitchRequest.NO_CHANGE) ? newQuality : oldQuality) &lt; minIdx) { newQuality = minIdx; } if (newQuality &gt; maxIdx) { newQuality = maxIdx; } switchHistoryDict[streamId][type].push({ oldValue: oldQuality, newValue: newQuality }); if (newQuality &gt; SwitchRequest.NO_CHANGE &amp;&amp; newQuality !== oldQuality &amp;&amp; (abandonmentStateDict[streamId][type].state === MetricsConstants.ALLOW_LOAD || newQuality &lt; oldQuality)) { _changeQuality(type, oldQuality, newQuality, maxIdx, switchRequest.reason, streamId); return true; } return false; } catch (e) { return false; } } /** * Returns the current quality for a specific media type and a specific streamId * @param {string} type * @param {string} streamId * @return {number|*} */ function getQualityFor(type, streamId = null) { try { if (!streamId) { streamId = streamController.getActiveStreamInfo().id; } if (type &amp;&amp; streamProcessorDict[streamId] &amp;&amp; streamProcessorDict[streamId][type]) { let quality; if (streamId) { qualityDict[streamId] = qualityDict[streamId] || {}; if (!qualityDict[streamId].hasOwnProperty(type)) { qualityDict[streamId][type] = QUALITY_DEFAULT; } quality = qualityDict[streamId][type]; return quality; } } return QUALITY_DEFAULT; } catch (e) { return QUALITY_DEFAULT; } } /** * Sets the new playback quality. Starts from index 0. * If the index of the new quality is the same as the old one changeQuality will not be called. * @param {string} type * @param {object} streamInfo * @param {number} newQuality * @param {string} reason */ function setPlaybackQuality(type, streamInfo, newQuality, reason = null) { if (!streamInfo || !streamInfo.id || !type) { return; } const streamId = streamInfo.id; const oldQuality = getQualityFor(type, streamId); checkInteger(newQuality); const topQualityIdx = getMaxAllowedIndexFor(type, streamId); if (newQuality !== oldQuality &amp;&amp; newQuality &gt;= 0 &amp;&amp; newQuality &lt;= topQualityIdx) { _changeQuality(type, oldQuality, newQuality, topQualityIdx, reason, streamId); } } /** * * @param {string} streamId * @param {type} type * @return {*|null} */ function getAbandonmentStateFor(streamId, type) { return abandonmentStateDict[streamId] &amp;&amp; abandonmentStateDict[streamId][type] ? abandonmentStateDict[streamId][type].state : null; } /** * Changes the internal qualityDict values according to the new quality * @param {string} type * @param {number} oldQuality * @param {number} newQuality * @param {number} maxIdx * @param {string} reason * @param {object} streamId * @private */ function _changeQuality(type, oldQuality, newQuality, maxIdx, reason, streamId) { if (type &amp;&amp; streamProcessorDict[streamId] &amp;&amp; streamProcessorDict[streamId][type]) { const streamInfo = streamProcessorDict[streamId][type].getStreamInfo(); const isDynamic = streamInfo &amp;&amp; streamInfo.manifestInfo &amp;&amp; streamInfo.manifestInfo.isDynamic; const bufferLevel = dashMetrics.getCurrentBufferLevel(type); logger.info('Stream ID: ' + streamId + ' [' + type + '] switch from ' + oldQuality + ' to ' + newQuality + '/' + maxIdx + ' (buffer: ' + bufferLevel + ') ' + (reason ? JSON.stringify(reason) : '.')); qualityDict[streamId] = qualityDict[streamId] || {}; qualityDict[streamId][type] = newQuality; const bitrateInfo = _getBitrateInfoForQuality(streamId, type, newQuality); eventBus.trigger(Events.QUALITY_CHANGE_REQUESTED, { oldQuality, newQuality, reason, streamInfo, bitrateInfo, maxIdx, mediaType: type }, { streamId: streamInfo.id, mediaType: type } ); const bitrate = throughputHistory.getAverageThroughput(type, isDynamic); if (!isNaN(bitrate)) { domStorage.setSavedBitrateSettings(type, bitrate); } } } function _getBitrateInfoForQuality(streamId, type, idx) { if (type &amp;&amp; streamProcessorDict &amp;&amp; streamProcessorDict[streamId] &amp;&amp; streamProcessorDict[streamId][type]) { const bitrates = getBitrateList(streamProcessorDict[streamId][type].getMediaInfo()); return bitrates[idx] ? bitrates[idx] : null; } return null; } /** * @param {MediaInfo} mediaInfo * @param {number} bitrate A bitrate value, kbps * @param {String} streamId Period ID * @param {number|null} latency Expected latency of connection, ms * @returns {number} A quality index &lt;= for the given bitrate * @memberof AbrController# */ function getQualityForBitrate(mediaInfo, bitrate, streamId, latency = null) { const voRepresentation = mediaInfo &amp;&amp; mediaInfo.type ? streamProcessorDict[streamId][mediaInfo.type].getRepresentationInfo() : null; if (settings.get().streaming.abr.useDeadTimeLatency &amp;&amp; latency &amp;&amp; voRepresentation &amp;&amp; voRepresentation.fragmentDuration) { latency = latency / 1000; const fragmentDuration = voRepresentation.fragmentDuration; if (latency &gt; fragmentDuration) { return 0; } else { const deadTimeRatio = latency / fragmentDuration; bitrate = bitrate * (1 - deadTimeRatio); } } const bitrateList = getBitrateList(mediaInfo); for (let i = bitrateList.length - 1; i &gt;= 0; i--) { const bitrateInfo = bitrateList[i]; if (bitrate * 1000 &gt;= bitrateInfo.bitrate) { return i; } } return QUALITY_DEFAULT; } /** * @param {MediaInfo} mediaInfo * @returns {Array|null} A list of {@link BitrateInfo} objects * @memberof AbrController# */ function getBitrateList(mediaInfo) { const infoList = []; if (!mediaInfo || !mediaInfo.bitrateList) return infoList; const bitrateList = mediaInfo.bitrateList; const type = mediaInfo.type; let bitrateInfo; for (let i = 0, ln = bitrateList.length; i &lt; ln; i++) { bitrateInfo = new BitrateInfo(); bitrateInfo.mediaType = type; bitrateInfo.qualityIndex = i; bitrateInfo.bitrate = bitrateList[i].bandwidth; bitrateInfo.width = bitrateList[i].width; bitrateInfo.height = bitrateList[i].height; bitrateInfo.scanType = bitrateList[i].scanType; infoList.push(bitrateInfo); } return infoList; } function _updateAbrStrategy(mediaType, bufferLevel) { // else ABR_STRATEGY_DYNAMIC const strategy = settings.get().streaming.abr.ABRStrategy; if (strategy === Constants.ABR_STRATEGY_DYNAMIC) { _updateDynamicAbrStrategy(mediaType, bufferLevel); } } function _updateDynamicAbrStrategy(mediaType, bufferLevel) { try { const stableBufferTime = mediaPlayerModel.getStableBufferTime(); const switchOnThreshold = stableBufferTime; const switchOffThreshold = 0.5 * stableBufferTime; const useBufferABR = isUsingBufferOccupancyAbrDict[mediaType]; const newUseBufferABR = bufferLevel &gt; (useBufferABR ? switchOffThreshold : switchOnThreshold); // use hysteresis to avoid oscillating rules isUsingBufferOccupancyAbrDict[mediaType] = newUseBufferABR; if (newUseBufferABR !== useBufferABR) { if (newUseBufferABR) { logger.info('[' + mediaType + '] switching from throughput to buffer occupancy ABR rule (buffer: ' + bufferLevel.toFixed(3) + ').'); } else { logger.info('[' + mediaType + '] switching from buffer occupancy to throughput ABR rule (buffer: ' + bufferLevel.toFixed(3) + ').'); } } } catch (e) { logger.error(e); } } function getThroughputHistory() { return throughputHistory; } function updateTopQualityIndex(mediaInfo) { const type = mediaInfo.type; const streamId = mediaInfo.streamInfo.id; const max = mediaInfo.representationCount - 1; topQualities[streamId] = topQualities[streamId] || {}; topQualities[streamId][type] = max; return max; } function isPlayingAtTopQuality(streamInfo) { const streamId = streamInfo ? streamInfo.id : null; const audioQuality = getQualityFor(Constants.AUDIO, streamId); const videoQuality = getQualityFor(Constants.VIDEO, streamId); const isAtTop = (audioQuality === getMaxAllowedIndexFor(Constants.AUDIO, streamId)) &amp;&amp; (videoQuality === getMaxAllowedIndexFor(Constants.VIDEO, streamId)); return isAtTop; } function setWindowResizeEventCalled(value) { windowResizeEventCalled = value; } function setElementSize() { if (videoModel) { const hasPixelRatio = settings.get().streaming.abr.usePixelRatioInLimitBitrateByPortal &amp;&amp; window.hasOwnProperty('devicePixelRatio'); const pixelRatio = hasPixelRatio ? window.devicePixelRatio : 1; elementWidth = videoModel.getClientWidth() * pixelRatio; elementHeight = videoModel.getClientHeight() * pixelRatio; } } function clearDataForStream(streamId) { if (droppedFramesHistory) { droppedFramesHistory.clearForStream(streamId); } if (streamProcessorDict[streamId]) { delete streamProcessorDict[streamId]; } if (switchHistoryDict[streamId]) { delete switchHistoryDict[streamId]; } if (abandonmentStateDict[streamId]) { delete abandonmentStateDict[streamId]; } } instance = { initialize, isPlayingAtTopQuality, updateTopQualityIndex, clearDataForStream, getThroughputHistory, getBitrateList, getQualityForBitrate, getTopBitrateInfoFor, getMinAllowedIndexFor, getMaxAllowedIndexFor, getInitialBitrateFor, getQualityFor, getAbandonmentStateFor, setPlaybackQuality, checkPlaybackQuality, setElementSize, setWindowResizeEventCalled, registerStreamType, unRegisterStreamType, setConfig, reset }; setup(); return instance; } AbrController.__dashjs_factory_name = 'AbrController'; const factory = FactoryMaker.getSingletonFactory(AbrController); factory.QUALITY_DEFAULT = QUALITY_DEFAULT; FactoryMaker.updateSingletonFactory(AbrController.__dashjs_factory_name, factory); export default factory; × Search results Close "},"streaming_rules_abr_ABRRulesCollection.js.html":{"id":"streaming_rules_abr_ABRRulesCollection.js.html","title":"Source: streaming/rules/abr/ABRRulesCollection.js","body":" dash.js Modules DashAdapterDashMetricsMediaPlayerOfflineControllerProtectionControllerSettings Classes ErrorsMediaPlayerEventsMediaPlayerModelMetricsReportingEventsMssErrorsOfflineErrorsOfflineEventsProtectionErrorsProtectionEvents Events MediaPlayerEvents#event:ADAPTATION_SET_REMOVED_NO_CAPABILITIESMediaPlayerEvents#event:AST_IN_FUTUREMediaPlayerEvents#event:BUFFER_EMPTYMediaPlayerEvents#event:BUFFER_LEVEL_STATE_CHANGEDMediaPlayerEvents#event:BUFFER_LEVEL_UPDATEDMediaPlayerEvents#event:BUFFER_LOADEDMediaPlayerEvents#event:CAN_PLAYMediaPlayerEvents#event:CAN_PLAY_THROUGHMediaPlayerEvents#event:CAPTION_CONTAINER_RESIZEMediaPlayerEvents#event:CAPTION_RENDEREDMediaPlayerEvents#event:CONFORMANCE_VIOLATIONMediaPlayerEvents#event:CONTENT_STEERING_REQUEST_COMPLETEDMediaPlayerEvents#event:DYNAMIC_TO_STATICMediaPlayerEvents#event:ERRORMediaPlayerEvents#event:EVENT_MODE_ON_RECEIVEMediaPlayerEvents#event:EVENT_MODE_ON_STARTMediaPlayerEvents#event:FRAGMENT_LOADING_ABANDONEDMediaPlayerEvents#event:FRAGMENT_LOADING_COMPLETEDMediaPlayerEvents#event:FRAGMENT_LOADING_PROGRESSMediaPlayerEvents#event:FRAGMENT_LOADING_STARTEDMediaPlayerEvents#event:LOGMediaPlayerEvents#event:MANIFEST_LOADEDMediaPlayerEvents#event:MANIFEST_VALIDITY_CHANGEDMediaPlayerEvents#event:METRIC_ADDEDMediaPlayerEvents#event:METRIC_CHANGEDMediaPlayerEvents#event:METRIC_UPDATEDMediaPlayerEvents#event:METRICS_CHANGEDMediaPlayerEvents#event:PERIOD_SWITCH_COMPLETEDMediaPlayerEvents#event:PERIOD_SWITCH_STARTEDMediaPlayerEvents#event:PLAYBACK_ENDEDMediaPlayerEvents#event:PLAYBACK_ERRORMediaPlayerEvents#event:PLAYBACK_LOADED_DATAMediaPlayerEvents#event:PLAYBACK_METADATA_LOADEDMediaPlayerEvents#event:PLAYBACK_NOT_ALLOWEDMediaPlayerEvents#event:PLAYBACK_PAUSEDMediaPlayerEvents#event:PLAYBACK_PLAYINGMediaPlayerEvents#event:PLAYBACK_PROGRESSMediaPlayerEvents#event:PLAYBACK_RATE_CHANGEDMediaPlayerEvents#event:PLAYBACK_SEEKEDMediaPlayerEvents#event:PLAYBACK_SEEKINGMediaPlayerEvents#event:PLAYBACK_STALLEDMediaPlayerEvents#event:PLAYBACK_STARTEDMediaPlayerEvents#event:PLAYBACK_TIME_UPDATEDMediaPlayerEvents#event:PLAYBACK_VOLUME_CHANGEDMediaPlayerEvents#event:PLAYBACK_WAITINGMediaPlayerEvents#event:QUALITY_CHANGE_RENDEREDMediaPlayerEvents#event:QUALITY_CHANGE_REQUESTEDMediaPlayerEvents#event:REPRESENTATION_SWITCHMediaPlayerEvents#event:STREAM_ACTIVATEDMediaPlayerEvents#event:STREAM_DEACTIVATEDMediaPlayerEvents#event:STREAM_INITIALIZEDMediaPlayerEvents#event:STREAM_INITIALIZINGMediaPlayerEvents#event:STREAM_TEARDOWN_COMPLETEMediaPlayerEvents#event:STREAM_UPDATEDMediaPlayerEvents#event:TEXT_TRACK_ADDEDMediaPlayerEvents#event:TEXT_TRACKS_ADDEDMediaPlayerEvents#event:TRACK_CHANGE_RENDEREDMediaPlayerEvents#event:TTML_PARSEDMediaPlayerEvents#event:TTML_TO_PARSEMetricsReportingEvents#event:CMCD_DATA_GENERATEDOfflineEvents#event:OFFLINE_RECORD_FINISHEDOfflineEvents#event:OFFLINE_RECORD_LOADEDMETADATAOfflineEvents#event:OFFLINE_RECORD_STARTEDOfflineEvents#event:OFFLINE_RECORD_STOPPEDProtectionEvents#event:KEY_ADDEDProtectionEvents#event:KEY_ERRORProtectionEvents#event:KEY_MESSAGEProtectionEvents#event:KEY_SESSION_CLOSEDProtectionEvents#event:KEY_SESSION_CREATEDProtectionEvents#event:KEY_SESSION_REMOVEDProtectionEvents#event:KEY_STATUSES_CHANGEDProtectionEvents#event:KEY_SYSTEM_SELECTEDProtectionEvents#event:LICENSE_REQUEST_COMPLETEProtectionEvents#event:LICENSE_REQUEST_SENDINGProtectionEvents#event:PROTECTION_CREATEDProtectionEvents#event:PROTECTION_DESTROYED Global LICENSE_SERVER_MANIFEST_CONFIGURATIONS Source: streaming/rules/abr/ABRRulesCollection.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 ThroughputRule from './ThroughputRule'; import InsufficientBufferRule from './InsufficientBufferRule'; import AbandonRequestsRule from './AbandonRequestsRule'; import DroppedFramesRule from './DroppedFramesRule'; import SwitchHistoryRule from './SwitchHistoryRule'; import BolaRule from './BolaRule'; import L2ARule from './L2ARule.js'; import LoLPRule from './lolp/LoLpRule.js'; import FactoryMaker from '../../../core/FactoryMaker'; import SwitchRequest from '../SwitchRequest'; import Constants from '../../constants/Constants'; const QUALITY_SWITCH_RULES = 'qualitySwitchRules'; const ABANDON_FRAGMENT_RULES = 'abandonFragmentRules'; function ABRRulesCollection(config) { config = config || {}; const context = this.context; const mediaPlayerModel = config.mediaPlayerModel; const customParametersModel = config.customParametersModel; const dashMetrics = config.dashMetrics; const settings = config.settings; let instance, qualitySwitchRules, abandonFragmentRules; function initialize() { qualitySwitchRules = []; abandonFragmentRules = []; if (settings.get().streaming.abr.useDefaultABRRules) { // If L2A is used we only need this one rule if (settings.get().streaming.abr.ABRStrategy === Constants.ABR_STRATEGY_L2A) { qualitySwitchRules.push( L2ARule(context).create({ dashMetrics: dashMetrics, settings: settings }) ); } // If LoLP is used we only need this one rule else if (settings.get().streaming.abr.ABRStrategy === Constants.ABR_STRATEGY_LoLP) { qualitySwitchRules.push( LoLPRule(context).create({ dashMetrics: dashMetrics }) ); } else { // Only one of BolaRule and ThroughputRule will give a switchRequest.quality !== SwitchRequest.NO_CHANGE. // This is controlled by useBufferOccupancyABR mechanism in AbrController. qualitySwitchRules.push( BolaRule(context).create({ dashMetrics: dashMetrics, mediaPlayerModel: mediaPlayerModel, settings: settings }) ); qualitySwitchRules.push( ThroughputRule(context).create({ dashMetrics: dashMetrics }) ); if (settings.get().streaming.abr.additionalAbrRules.insufficientBufferRule) { qualitySwitchRules.push( InsufficientBufferRule(context).create({ dashMetrics: dashMetrics, settings }) ); } if (settings.get().streaming.abr.additionalAbrRules.switchHistoryRule) { qualitySwitchRules.push( SwitchHistoryRule(context).create() ); } if (settings.get().streaming.abr.additionalAbrRules.droppedFramesRule) { qualitySwitchRules.push( DroppedFramesRule(context).create() ); } if (settings.get().streaming.abr.additionalAbrRules.abandonRequestsRule) { abandonFragmentRules.push( AbandonRequestsRule(context).create({ dashMetrics: dashMetrics, mediaPlayerModel: mediaPlayerModel, settings: settings }) ); } } } // add custom ABR rules if any const customRules = customParametersModel.getAbrCustomRules(); customRules.forEach(function (rule) { if (rule.type === QUALITY_SWITCH_RULES) { qualitySwitchRules.push(rule.rule(context).create()); } if (rule.type === ABANDON_FRAGMENT_RULES) { abandonFragmentRules.push(rule.rule(context).create()); } }); } function _getRulesWithChange(srArray) { return srArray.filter(sr =&gt; sr.quality &gt; SwitchRequest.NO_CHANGE); } /** * * @param {array} srArray * @return {object} SwitchRequest */ function getMinSwitchRequest(srArray) { const values = {}; let newSwitchReq = null; let i, len, req, quality, reason; if (srArray.length === 0) { return; } values[SwitchRequest.PRIORITY.STRONG] = { quality: SwitchRequest.NO_CHANGE, reason: null }; values[SwitchRequest.PRIORITY.WEAK] = { quality: SwitchRequest.NO_CHANGE, reason: null }; values[SwitchRequest.PRIORITY.DEFAULT] = { quality: SwitchRequest.NO_CHANGE, reason: null }; for (i = 0, len = srArray.length; i &lt; len; i += 1) { req = srArray[i]; if (req.quality !== SwitchRequest.NO_CHANGE) { // We only use the new quality in case it is lower than the already saved one or if no new quality has been selected for the respective priority if (values[req.priority].quality === SwitchRequest.NO_CHANGE || values[req.priority].quality &gt; req.quality) { values[req.priority].quality = req.quality; values[req.priority].reason = req.reason || null; } } } if (values[SwitchRequest.PRIORITY.WEAK].quality !== SwitchRequest.NO_CHANGE) { newSwitchReq = values[SwitchRequest.PRIORITY.WEAK]; } if (values[SwitchRequest.PRIORITY.DEFAULT].quality !== SwitchRequest.NO_CHANGE) { newSwitchReq = values[SwitchRequest.PRIORITY.DEFAULT]; } if (values[SwitchRequest.PRIORITY.STRONG].quality !== SwitchRequest.NO_CHANGE) { newSwitchReq = values[SwitchRequest.PRIORITY.STRONG]; } if (newSwitchReq) { quality = newSwitchReq.quality; reason = newSwitchReq.reason; } return SwitchRequest(context).create(quality, reason); } function getMaxQuality(rulesContext) { const switchRequestArray = qualitySwitchRules.map(rule =&gt; rule.getMaxIndex(rulesContext)); const activeRules = _getRulesWithChange(switchRequestArray); const maxQuality = getMinSwitchRequest(activeRules); return maxQuality || SwitchRequest(context).create(); } function shouldAbandonFragment(rulesContext, streamId) { const abandonRequestArray = abandonFragmentRules.map(rule =&gt; rule.shouldAbandon(rulesContext, streamId)); const activeRules = _getRulesWithChange(abandonRequestArray); const shouldAbandon = getMinSwitchRequest(activeRules); return shouldAbandon || SwitchRequest(context).create(); } function reset() { [qualitySwitchRules, abandonFragmentRules].forEach(rules =&gt; { if (rules &amp;&amp; rules.length) { rules.forEach(rule =&gt; rule.reset &amp;&amp; rule.reset()); } }); qualitySwitchRules = []; abandonFragmentRules = []; } function getQualitySwitchRules() { return qualitySwitchRules; } instance = { initialize, reset, getMaxQuality, getMinSwitchRequest, shouldAbandonFragment, getQualitySwitchRules }; return instance; } ABRRulesCollection.__dashjs_factory_name = 'ABRRulesCollection'; const factory = FactoryMaker.getClassFactory(ABRRulesCollection); factory.QUALITY_SWITCH_RULES = QUALITY_SWITCH_RULES; factory.ABANDON_FRAGMENT_RULES = ABANDON_FRAGMENT_RULES; FactoryMaker.updateSingletonFactory(ABRRulesCollection.__dashjs_factory_name, factory); export default factory; × Search results Close "},"streaming_utils_BoxParser.js.html":{"id":"streaming_utils_BoxParser.js.html","title":"Source: streaming/utils/BoxParser.js","body":" dash.js Modules DashAdapterDashMetricsMediaPlayerOfflineControllerProtectionControllerSettings Classes ErrorsMediaPlayerEventsMediaPlayerModelMetricsReportingEventsMssErrorsOfflineErrorsOfflineEventsProtectionErrorsProtectionEvents Events MediaPlayerEvents#event:ADAPTATION_SET_REMOVED_NO_CAPABILITIESMediaPlayerEvents#event:AST_IN_FUTUREMediaPlayerEvents#event:BUFFER_EMPTYMediaPlayerEvents#event:BUFFER_LEVEL_STATE_CHANGEDMediaPlayerEvents#event:BUFFER_LEVEL_UPDATEDMediaPlayerEvents#event:BUFFER_LOADEDMediaPlayerEvents#event:CAN_PLAYMediaPlayerEvents#event:CAN_PLAY_THROUGHMediaPlayerEvents#event:CAPTION_CONTAINER_RESIZEMediaPlayerEvents#event:CAPTION_RENDEREDMediaPlayerEvents#event:CONFORMANCE_VIOLATIONMediaPlayerEvents#event:CONTENT_STEERING_REQUEST_COMPLETEDMediaPlayerEvents#event:DYNAMIC_TO_STATICMediaPlayerEvents#event:ERRORMediaPlayerEvents#event:EVENT_MODE_ON_RECEIVEMediaPlayerEvents#event:EVENT_MODE_ON_STARTMediaPlayerEvents#event:FRAGMENT_LOADING_ABANDONEDMediaPlayerEvents#event:FRAGMENT_LOADING_COMPLETEDMediaPlayerEvents#event:FRAGMENT_LOADING_PROGRESSMediaPlayerEvents#event:FRAGMENT_LOADING_STARTEDMediaPlayerEvents#event:LOGMediaPlayerEvents#event:MANIFEST_LOADEDMediaPlayerEvents#event:MANIFEST_VALIDITY_CHANGEDMediaPlayerEvents#event:METRIC_ADDEDMediaPlayerEvents#event:METRIC_CHANGEDMediaPlayerEvents#event:METRIC_UPDATEDMediaPlayerEvents#event:METRICS_CHANGEDMediaPlayerEvents#event:PERIOD_SWITCH_COMPLETEDMediaPlayerEvents#event:PERIOD_SWITCH_STARTEDMediaPlayerEvents#event:PLAYBACK_ENDEDMediaPlayerEvents#event:PLAYBACK_ERRORMediaPlayerEvents#event:PLAYBACK_LOADED_DATAMediaPlayerEvents#event:PLAYBACK_METADATA_LOADEDMediaPlayerEvents#event:PLAYBACK_NOT_ALLOWEDMediaPlayerEvents#event:PLAYBACK_PAUSEDMediaPlayerEvents#event:PLAYBACK_PLAYINGMediaPlayerEvents#event:PLAYBACK_PROGRESSMediaPlayerEvents#event:PLAYBACK_RATE_CHANGEDMediaPlayerEvents#event:PLAYBACK_SEEKEDMediaPlayerEvents#event:PLAYBACK_SEEKINGMediaPlayerEvents#event:PLAYBACK_STALLEDMediaPlayerEvents#event:PLAYBACK_STARTEDMediaPlayerEvents#event:PLAYBACK_TIME_UPDATEDMediaPlayerEvents#event:PLAYBACK_VOLUME_CHANGEDMediaPlayerEvents#event:PLAYBACK_WAITINGMediaPlayerEvents#event:QUALITY_CHANGE_RENDEREDMediaPlayerEvents#event:QUALITY_CHANGE_REQUESTEDMediaPlayerEvents#event:REPRESENTATION_SWITCHMediaPlayerEvents#event:STREAM_ACTIVATEDMediaPlayerEvents#event:STREAM_DEACTIVATEDMediaPlayerEvents#event:STREAM_INITIALIZEDMediaPlayerEvents#event:STREAM_INITIALIZINGMediaPlayerEvents#event:STREAM_TEARDOWN_COMPLETEMediaPlayerEvents#event:STREAM_UPDATEDMediaPlayerEvents#event:TEXT_TRACK_ADDEDMediaPlayerEvents#event:TEXT_TRACKS_ADDEDMediaPlayerEvents#event:TRACK_CHANGE_RENDEREDMediaPlayerEvents#event:TTML_PARSEDMediaPlayerEvents#event:TTML_TO_PARSEMetricsReportingEvents#event:CMCD_DATA_GENERATEDOfflineEvents#event:OFFLINE_RECORD_FINISHEDOfflineEvents#event:OFFLINE_RECORD_LOADEDMETADATAOfflineEvents#event:OFFLINE_RECORD_STARTEDOfflineEvents#event:OFFLINE_RECORD_STOPPEDProtectionEvents#event:KEY_ADDEDProtectionEvents#event:KEY_ERRORProtectionEvents#event:KEY_MESSAGEProtectionEvents#event:KEY_SESSION_CLOSEDProtectionEvents#event:KEY_SESSION_CREATEDProtectionEvents#event:KEY_SESSION_REMOVEDProtectionEvents#event:KEY_STATUSES_CHANGEDProtectionEvents#event:KEY_SYSTEM_SELECTEDProtectionEvents#event:LICENSE_REQUEST_COMPLETEProtectionEvents#event:LICENSE_REQUEST_SENDINGProtectionEvents#event:PROTECTION_CREATEDProtectionEvents#event:PROTECTION_DESTROYED Global LICENSE_SERVER_MANIFEST_CONFIGURATIONS Source: streaming/utils/BoxParser.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 IsoFile from './IsoFile'; import FactoryMaker from '../../core/FactoryMaker'; import ISOBoxer from 'codem-isoboxer'; import IsoBoxSearchInfo from '../vo/IsoBoxSearchInfo'; function BoxParser(/*config*/) { let logger, instance; let context = this.context; function setup() { logger = Debug(context).getInstance().getLogger(instance); } /** * @param {ArrayBuffer} data * @returns {IsoFile|null} * @memberof BoxParser# */ function parse(data) { if (!data) return null; if (data.fileStart === undefined) { data.fileStart = 0; } let parsedFile = ISOBoxer.parseBuffer(data); let dashIsoFile = IsoFile(context).create(); dashIsoFile.setData(parsedFile); return dashIsoFile; } /** * From the list of type boxes to look for, returns the latest one that is fully completed (header + payload). This * method only looks into the list of top boxes and doesn't analyze nested boxes. * @param {string[]} types * @param {ArrayBuffer|uint8Array} buffer * @param {number} offset * @returns {IsoBoxSearchInfo} * @memberof BoxParser# */ function findLastTopIsoBoxCompleted(types, buffer, offset) { if (o