UNPKG

dashjs

Version:

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

1,199 lines (989 loc) 52.2 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>dash.js Source: streaming/Stream.js</title> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/sunlight.default.css"> <link type="text/css" rel="stylesheet" href="styles/site.spacelab.css"> </head> <body> <div class="navbar navbar-default navbar-fixed-top navbar-inverse"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="index.html">dash.js</a> <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#topNavigation"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="navbar-collapse collapse" id="topNavigation"> <ul class="nav navbar-nav"> <li class="dropdown"> <a href="modules.list.html" class="dropdown-toggle" data-toggle="dropdown">Modules<b class="caret"></b></a> <ul class="dropdown-menu "> <li><a href="module-DashAdapter.html">DashAdapter</a></li><li><a href="module-DashMetrics.html">DashMetrics</a></li><li><a href="module-MediaPlayer.html">MediaPlayer</a></li><li><a href="module-OfflineController.html">OfflineController</a></li><li><a href="module-ProtectionController.html">ProtectionController</a></li><li><a href="module-Settings.html">Settings</a></li> </ul> </li> <li class="dropdown"> <a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b class="caret"></b></a> <ul class="dropdown-menu "> <li><a href="Errors.html">Errors</a></li><li><a href="MediaPlayerEvents.html">MediaPlayerEvents</a></li><li><a href="MediaPlayerModel.html">MediaPlayerModel</a></li><li><a href="MetricsReportingEvents.html">MetricsReportingEvents</a></li><li><a href="MssErrors.html">MssErrors</a></li><li><a href="OfflineErrors.html">OfflineErrors</a></li><li><a href="OfflineEvents.html">OfflineEvents</a></li><li><a href="ProtectionErrors.html">ProtectionErrors</a></li><li><a href="ProtectionEvents.html">ProtectionEvents</a></li> </ul> </li> <li class="dropdown"> <a href="events.list.html" class="dropdown-toggle" data-toggle="dropdown">Events<b class="caret"></b></a> <ul class="dropdown-menu "> <li><a href="MediaPlayerEvents.html#event:ADAPTATION_SET_REMOVED_NO_CAPABILITIES">MediaPlayerEvents#event:ADAPTATION_SET_REMOVED_NO_CAPABILITIES</a></li><li><a href="MediaPlayerEvents.html#event:AST_IN_FUTURE">MediaPlayerEvents#event:AST_IN_FUTURE</a></li><li><a href="MediaPlayerEvents.html#event:BUFFER_EMPTY">MediaPlayerEvents#event:BUFFER_EMPTY</a></li><li><a href="MediaPlayerEvents.html#event:BUFFER_LEVEL_STATE_CHANGED">MediaPlayerEvents#event:BUFFER_LEVEL_STATE_CHANGED</a></li><li><a href="MediaPlayerEvents.html#event:BUFFER_LEVEL_UPDATED">MediaPlayerEvents#event:BUFFER_LEVEL_UPDATED</a></li><li><a href="MediaPlayerEvents.html#event:BUFFER_LOADED">MediaPlayerEvents#event:BUFFER_LOADED</a></li><li><a href="MediaPlayerEvents.html#event:CAN_PLAY">MediaPlayerEvents#event:CAN_PLAY</a></li><li><a href="MediaPlayerEvents.html#event:CAN_PLAY_THROUGH">MediaPlayerEvents#event:CAN_PLAY_THROUGH</a></li><li><a href="MediaPlayerEvents.html#event:CAPTION_CONTAINER_RESIZE">MediaPlayerEvents#event:CAPTION_CONTAINER_RESIZE</a></li><li><a href="MediaPlayerEvents.html#event:CAPTION_RENDERED">MediaPlayerEvents#event:CAPTION_RENDERED</a></li><li><a href="MediaPlayerEvents.html#event:CONFORMANCE_VIOLATION">MediaPlayerEvents#event:CONFORMANCE_VIOLATION</a></li><li><a href="MediaPlayerEvents.html#event:CONTENT_STEERING_REQUEST_COMPLETED">MediaPlayerEvents#event:CONTENT_STEERING_REQUEST_COMPLETED</a></li><li><a href="MediaPlayerEvents.html#event:DYNAMIC_TO_STATIC">MediaPlayerEvents#event:DYNAMIC_TO_STATIC</a></li><li><a href="MediaPlayerEvents.html#event:ERROR">MediaPlayerEvents#event:ERROR</a></li><li><a href="MediaPlayerEvents.html#event:EVENT_MODE_ON_RECEIVE">MediaPlayerEvents#event:EVENT_MODE_ON_RECEIVE</a></li><li><a href="MediaPlayerEvents.html#event:EVENT_MODE_ON_START">MediaPlayerEvents#event:EVENT_MODE_ON_START</a></li><li><a href="MediaPlayerEvents.html#event:FRAGMENT_LOADING_ABANDONED">MediaPlayerEvents#event:FRAGMENT_LOADING_ABANDONED</a></li><li><a href="MediaPlayerEvents.html#event:FRAGMENT_LOADING_COMPLETED">MediaPlayerEvents#event:FRAGMENT_LOADING_COMPLETED</a></li><li><a href="MediaPlayerEvents.html#event:FRAGMENT_LOADING_PROGRESS">MediaPlayerEvents#event:FRAGMENT_LOADING_PROGRESS</a></li><li><a href="MediaPlayerEvents.html#event:FRAGMENT_LOADING_STARTED">MediaPlayerEvents#event:FRAGMENT_LOADING_STARTED</a></li><li><a href="MediaPlayerEvents.html#event:LOG">MediaPlayerEvents#event:LOG</a></li><li><a href="MediaPlayerEvents.html#event:MANIFEST_LOADED">MediaPlayerEvents#event:MANIFEST_LOADED</a></li><li><a href="MediaPlayerEvents.html#event:MANIFEST_VALIDITY_CHANGED">MediaPlayerEvents#event:MANIFEST_VALIDITY_CHANGED</a></li><li><a href="MediaPlayerEvents.html#event:METRIC_ADDED">MediaPlayerEvents#event:METRIC_ADDED</a></li><li><a href="MediaPlayerEvents.html#event:METRIC_CHANGED">MediaPlayerEvents#event:METRIC_CHANGED</a></li><li><a href="MediaPlayerEvents.html#event:METRIC_UPDATED">MediaPlayerEvents#event:METRIC_UPDATED</a></li><li><a href="MediaPlayerEvents.html#event:METRICS_CHANGED">MediaPlayerEvents#event:METRICS_CHANGED</a></li><li><a href="MediaPlayerEvents.html#event:PERIOD_SWITCH_COMPLETED">MediaPlayerEvents#event:PERIOD_SWITCH_COMPLETED</a></li><li><a href="MediaPlayerEvents.html#event:PERIOD_SWITCH_STARTED">MediaPlayerEvents#event:PERIOD_SWITCH_STARTED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_ENDED">MediaPlayerEvents#event:PLAYBACK_ENDED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_ERROR">MediaPlayerEvents#event:PLAYBACK_ERROR</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_LOADED_DATA">MediaPlayerEvents#event:PLAYBACK_LOADED_DATA</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_METADATA_LOADED">MediaPlayerEvents#event:PLAYBACK_METADATA_LOADED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_NOT_ALLOWED">MediaPlayerEvents#event:PLAYBACK_NOT_ALLOWED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_PAUSED">MediaPlayerEvents#event:PLAYBACK_PAUSED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_PLAYING">MediaPlayerEvents#event:PLAYBACK_PLAYING</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_PROGRESS">MediaPlayerEvents#event:PLAYBACK_PROGRESS</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_RATE_CHANGED">MediaPlayerEvents#event:PLAYBACK_RATE_CHANGED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_SEEKED">MediaPlayerEvents#event:PLAYBACK_SEEKED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_SEEKING">MediaPlayerEvents#event:PLAYBACK_SEEKING</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_STALLED">MediaPlayerEvents#event:PLAYBACK_STALLED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_STARTED">MediaPlayerEvents#event:PLAYBACK_STARTED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_TIME_UPDATED">MediaPlayerEvents#event:PLAYBACK_TIME_UPDATED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_VOLUME_CHANGED">MediaPlayerEvents#event:PLAYBACK_VOLUME_CHANGED</a></li><li><a href="MediaPlayerEvents.html#event:PLAYBACK_WAITING">MediaPlayerEvents#event:PLAYBACK_WAITING</a></li><li><a href="MediaPlayerEvents.html#event:QUALITY_CHANGE_RENDERED">MediaPlayerEvents#event:QUALITY_CHANGE_RENDERED</a></li><li><a href="MediaPlayerEvents.html#event:QUALITY_CHANGE_REQUESTED">MediaPlayerEvents#event:QUALITY_CHANGE_REQUESTED</a></li><li><a href="MediaPlayerEvents.html#event:REPRESENTATION_SWITCH">MediaPlayerEvents#event:REPRESENTATION_SWITCH</a></li><li><a href="MediaPlayerEvents.html#event:STREAM_ACTIVATED">MediaPlayerEvents#event:STREAM_ACTIVATED</a></li><li><a href="MediaPlayerEvents.html#event:STREAM_DEACTIVATED">MediaPlayerEvents#event:STREAM_DEACTIVATED</a></li><li><a href="MediaPlayerEvents.html#event:STREAM_INITIALIZED">MediaPlayerEvents#event:STREAM_INITIALIZED</a></li><li><a href="MediaPlayerEvents.html#event:STREAM_INITIALIZING">MediaPlayerEvents#event:STREAM_INITIALIZING</a></li><li><a href="MediaPlayerEvents.html#event:STREAM_TEARDOWN_COMPLETE">MediaPlayerEvents#event:STREAM_TEARDOWN_COMPLETE</a></li><li><a href="MediaPlayerEvents.html#event:STREAM_UPDATED">MediaPlayerEvents#event:STREAM_UPDATED</a></li><li><a href="MediaPlayerEvents.html#event:TEXT_TRACK_ADDED">MediaPlayerEvents#event:TEXT_TRACK_ADDED</a></li><li><a href="MediaPlayerEvents.html#event:TEXT_TRACKS_ADDED">MediaPlayerEvents#event:TEXT_TRACKS_ADDED</a></li><li><a href="MediaPlayerEvents.html#event:TRACK_CHANGE_RENDERED">MediaPlayerEvents#event:TRACK_CHANGE_RENDERED</a></li><li><a href="MediaPlayerEvents.html#event:TTML_PARSED">MediaPlayerEvents#event:TTML_PARSED</a></li><li><a href="MediaPlayerEvents.html#event:TTML_TO_PARSE">MediaPlayerEvents#event:TTML_TO_PARSE</a></li><li><a href="MetricsReportingEvents.html#event:CMCD_DATA_GENERATED">MetricsReportingEvents#event:CMCD_DATA_GENERATED</a></li><li><a href="OfflineEvents.html#event:OFFLINE_RECORD_FINISHED">OfflineEvents#event:OFFLINE_RECORD_FINISHED</a></li><li><a href="OfflineEvents.html#event:OFFLINE_RECORD_LOADEDMETADATA">OfflineEvents#event:OFFLINE_RECORD_LOADEDMETADATA</a></li><li><a href="OfflineEvents.html#event:OFFLINE_RECORD_STARTED">OfflineEvents#event:OFFLINE_RECORD_STARTED</a></li><li><a href="OfflineEvents.html#event:OFFLINE_RECORD_STOPPED">OfflineEvents#event:OFFLINE_RECORD_STOPPED</a></li><li><a href="ProtectionEvents.html#event:KEY_ADDED">ProtectionEvents#event:KEY_ADDED</a></li><li><a href="ProtectionEvents.html#event:KEY_ERROR">ProtectionEvents#event:KEY_ERROR</a></li><li><a href="ProtectionEvents.html#event:KEY_MESSAGE">ProtectionEvents#event:KEY_MESSAGE</a></li><li><a href="ProtectionEvents.html#event:KEY_SESSION_CLOSED">ProtectionEvents#event:KEY_SESSION_CLOSED</a></li><li><a href="ProtectionEvents.html#event:KEY_SESSION_CREATED">ProtectionEvents#event:KEY_SESSION_CREATED</a></li><li><a href="ProtectionEvents.html#event:KEY_SESSION_REMOVED">ProtectionEvents#event:KEY_SESSION_REMOVED</a></li><li><a href="ProtectionEvents.html#event:KEY_STATUSES_CHANGED">ProtectionEvents#event:KEY_STATUSES_CHANGED</a></li><li><a href="ProtectionEvents.html#event:KEY_SYSTEM_SELECTED">ProtectionEvents#event:KEY_SYSTEM_SELECTED</a></li><li><a href="ProtectionEvents.html#event:LICENSE_REQUEST_COMPLETE">ProtectionEvents#event:LICENSE_REQUEST_COMPLETE</a></li><li><a href="ProtectionEvents.html#event:LICENSE_REQUEST_SENDING">ProtectionEvents#event:LICENSE_REQUEST_SENDING</a></li><li><a href="ProtectionEvents.html#event:PROTECTION_CREATED">ProtectionEvents#event:PROTECTION_CREATED</a></li><li><a href="ProtectionEvents.html#event:PROTECTION_DESTROYED">ProtectionEvents#event:PROTECTION_DESTROYED</a></li> </ul> </li> <li class="dropdown"> <a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b class="caret"></b></a> <ul class="dropdown-menu "> <li><a href="global.html#LICENSE_SERVER_MANIFEST_CONFIGURATIONS">LICENSE_SERVER_MANIFEST_CONFIGURATIONS</a></li> </ul> </li> </ul> <div class="col-sm-3 col-md-3"> <form class="navbar-form" role="search"> <div class="input-group"> <input type="text" class="form-control" placeholder="Search" name="q" id="search-input"> <div class="input-group-btn"> <button class="btn btn-default" id="search-submit"><i class="glyphicon glyphicon-search"></i></button> </div> </div> </form> </div> </div> </div> </div> <div class="container" id="toc-content"> <div class="row"> <div class="col-md-12"> <div id="main"> <h1 class="page-title">Source: streaming/Stream.js</h1> <section> <article> <pre class="sunlight-highlight-javascript linenums">/** * 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 Constants from './constants/Constants'; import DashConstants from '../dash/constants/DashConstants'; import StreamProcessor from './StreamProcessor'; import FragmentController from './controllers/FragmentController'; import ThumbnailController from './thumbnail/ThumbnailController'; import EventBus from '../core/EventBus'; import Events from '../core/events/Events'; import Debug from '../core/Debug'; import Errors from '../core/errors/Errors'; import FactoryMaker from '../core/FactoryMaker'; import DashJSError from './vo/DashJSError'; import BoxParser from './utils/BoxParser'; import URLUtils from './utils/URLUtils'; import BlacklistController from './controllers/BlacklistController'; const MEDIA_TYPES = [Constants.VIDEO, Constants.AUDIO, Constants.TEXT, Constants.MUXED, Constants.IMAGE]; function Stream(config) { config = config || {}; const context = this.context; const eventBus = EventBus(context).getInstance(); const urlUtils = URLUtils(context).getInstance(); const manifestModel = config.manifestModel; const mediaPlayerModel = config.mediaPlayerModel; const dashMetrics = config.dashMetrics; const manifestUpdater = config.manifestUpdater; const adapter = config.adapter; const timelineConverter = config.timelineConverter; const capabilities = config.capabilities; const errHandler = config.errHandler; const abrController = config.abrController; const playbackController = config.playbackController; const eventController = config.eventController; const mediaController = config.mediaController; const protectionController = config.protectionController; const textController = config.textController; const videoModel = config.videoModel; let streamInfo = config.streamInfo; const settings = config.settings; let instance, logger, streamProcessors, isInitialized, isActive, hasFinishedBuffering, hasVideoTrack, hasAudioTrack, updateError, isUpdating, fragmentController, thumbnailController, segmentBlacklistController, preloaded, boxParser, debug, isEndedEventSignaled, trackChangedEvents; /** * Setup the stream */ function setup() { try { debug = Debug(context).getInstance(); logger = debug.getLogger(instance); resetInitialSettings(); boxParser = BoxParser(context).getInstance(); segmentBlacklistController = BlacklistController(context).create({ updateEventName: Events.SEGMENT_LOCATION_BLACKLIST_CHANGED, addBlacklistEventName: Events.SEGMENT_LOCATION_BLACKLIST_ADD }); fragmentController = FragmentController(context).create({ streamInfo: streamInfo, mediaPlayerModel: mediaPlayerModel, dashMetrics: dashMetrics, errHandler: errHandler, settings: settings, boxParser: boxParser, dashConstants: DashConstants, urlUtils: urlUtils }); } catch (e) { throw e; } } /** * Initialize the events */ function initialize() { registerEvents(); registerProtectionEvents(); textController.initializeForStream(streamInfo); eventBus.trigger(Events.STREAM_UPDATED, { streamInfo: streamInfo }); } /** * Register the streaming events */ function registerEvents() { eventBus.on(Events.BUFFERING_COMPLETED, onBufferingCompleted, instance); eventBus.on(Events.DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance); eventBus.on(Events.INBAND_EVENTS, onInbandEvents, instance); } /** * Unregister the streaming events */ function unRegisterEvents() { eventBus.off(Events.DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance); eventBus.off(Events.BUFFERING_COMPLETED, onBufferingCompleted, instance); eventBus.off(Events.INBAND_EVENTS, onInbandEvents, instance); } /** * Register the protection events */ function registerProtectionEvents() { if (protectionController) { eventBus.on(Events.KEY_ERROR, onProtectionError, instance); eventBus.on(Events.SERVER_CERTIFICATE_UPDATED, onProtectionError, instance); eventBus.on(Events.LICENSE_REQUEST_COMPLETE, onProtectionError, instance); eventBus.on(Events.KEY_SYSTEM_SELECTED, onProtectionError, instance); eventBus.on(Events.KEY_SESSION_CREATED, onProtectionError, instance); eventBus.on(Events.KEY_STATUSES_CHANGED, onProtectionError, instance); } } /** * Unregister the protection events */ function unRegisterProtectionEvents() { if (protectionController) { eventBus.off(Events.KEY_ERROR, onProtectionError, instance); eventBus.off(Events.SERVER_CERTIFICATE_UPDATED, onProtectionError, instance); eventBus.off(Events.LICENSE_REQUEST_COMPLETE, onProtectionError, instance); eventBus.off(Events.KEY_SYSTEM_SELECTED, onProtectionError, instance); eventBus.off(Events.KEY_SESSION_CREATED, onProtectionError, instance); eventBus.off(Events.KEY_STATUSES_CHANGED, onProtectionError, instance); } } /** * Returns the stream id * @return {*|null} */ function getStreamId() { return streamInfo ? streamInfo.id : null; } /** * Activates Stream by re-initializing some of its components * @param {MediaSource} mediaSource * @param {array} previousBufferSinks * @memberof Stream# */ function activate(mediaSource, previousBufferSinks) { return new Promise((resolve, reject) => { if (isActive) { resolve(previousBufferSinks); return; } if (getPreloaded()) { isActive = true; eventBus.trigger(Events.STREAM_ACTIVATED, { streamInfo }); resolve(previousBufferSinks); return; } _initializeMedia(mediaSource, previousBufferSinks) .then((bufferSinks) => { isActive = true; eventBus.trigger(Events.STREAM_ACTIVATED, { streamInfo }); resolve(bufferSinks); }) .catch((e) => { reject(e); }); }); } /** * * @param {object} mediaSource * @param {array} previousBufferSinks * @return {Promise&lt;Array>} * @private */ function _initializeMedia(mediaSource, previousBufferSinks) { return _commonMediaInitialization(mediaSource, previousBufferSinks); } function startPreloading(mediaSource, previousBuffers) { return new Promise((resolve, reject) => { if (getPreloaded()) { reject(); return; } logger.info(`[startPreloading] Preloading next stream with id ${getId()}`); setPreloaded(true); _commonMediaInitialization(mediaSource, previousBuffers) .then(() => { for (let i = 0; i &lt; streamProcessors.length &amp;&amp; streamProcessors[i]; i++) { streamProcessors[i].setExplicitBufferingTime(getStartTime()); streamProcessors[i].getScheduleController().startScheduleTimer(); } resolve(); }) .catch(() => { setPreloaded(false); reject(); }); }); } /** * * @param {object} mediaSource * @param {array} previousBufferSinks * @return {Promise&lt;array>} * @private */ function _commonMediaInitialization(mediaSource, previousBufferSinks) { return new Promise((resolve, reject) => { checkConfig(); isUpdating = true; _addInlineEvents(); let element = videoModel.getElement(); MEDIA_TYPES.forEach((mediaType) => { if (mediaType !== Constants.VIDEO || (!element || (element &amp;&amp; (/^VIDEO$/i).test(element.nodeName)))) { _initializeMediaForType(mediaType, mediaSource); } }); _createBufferSinks(previousBufferSinks) .then((bufferSinks) => { isUpdating = false; if (streamProcessors.length === 0) { const msg = 'No streams to play.'; errHandler.error(new DashJSError(Errors.MANIFEST_ERROR_ID_NOSTREAMS_CODE, msg, manifestModel.getValue())); logger.fatal(msg); } else { _checkIfInitializationCompleted(); } // All mediaInfos for texttracks are added to the TextSourceBuffer by now. We can start creating the tracks textController.createTracks(streamInfo); resolve(bufferSinks); }) .catch((e) => { reject(e); }); }); } /** * Initialize for a given media type. Creates a corresponding StreamProcessor * @param {string} type * @param {object} mediaSource * @private */ function _initializeMediaForType(type, mediaSource) { let allMediaForType = adapter.getAllMediaInfoForType(streamInfo, type); let embeddedMediaInfos = []; let mediaInfo = null; let initialMediaInfo; if (!allMediaForType || allMediaForType.length === 0) { logger.info('No ' + type + ' data.'); return; } if (type === Constants.VIDEO) { hasVideoTrack = true; } if (type === Constants.AUDIO) { hasAudioTrack = true; } for (let i = 0, ln = allMediaForType.length; i &lt; ln; i++) { mediaInfo = allMediaForType[i]; if (type === Constants.TEXT &amp;&amp; !!mediaInfo.isEmbedded) { textController.addEmbeddedTrack(streamInfo, mediaInfo); embeddedMediaInfos.push(mediaInfo); } if (_isMediaSupported(mediaInfo)) { mediaController.addTrack(mediaInfo); } } if (embeddedMediaInfos.length > 0) { mediaController.setInitialMediaSettingsForType(type, streamInfo); textController.setInitialSettings(mediaController.getInitialSettings(type)); textController.addMediaInfosToBuffer(streamInfo, type, embeddedMediaInfos); } // Filter out embedded text track before creating StreamProcessor allMediaForType = allMediaForType.filter(mediaInfo => { return !mediaInfo.isEmbedded; }); if (allMediaForType.length === 0) { return; } if (type === Constants.IMAGE) { thumbnailController = ThumbnailController(context).create({ streamInfo: streamInfo, adapter: adapter, baseURLController: config.baseURLController, timelineConverter: config.timelineConverter, debug: debug, eventBus: eventBus, events: Events, dashConstants: DashConstants, dashMetrics: config.dashMetrics, segmentBaseController: config.segmentBaseController }); thumbnailController.initialize(); return; } eventBus.trigger(Events.STREAM_INITIALIZING, { streamInfo: streamInfo, mediaInfo: mediaInfo }); mediaController.setInitialMediaSettingsForType(type, streamInfo); let streamProcessor = _createStreamProcessor(allMediaForType, mediaSource); initialMediaInfo = mediaController.getCurrentTrackFor(type, streamInfo.id); if (initialMediaInfo) { abrController.updateTopQualityIndex(initialMediaInfo); // In case of mixed fragmented and embedded text tracks, check if initial selected text track is not an embedded track streamProcessor.selectMediaInfo((type !== Constants.TEXT || !initialMediaInfo.isEmbedded) ? initialMediaInfo : allMediaForType[0]); } } function _isMediaSupported(mediaInfo) { const type = mediaInfo ? mediaInfo.type : null; let msg; if (type === Constants.MUXED) { msg = 'Multiplexed representations are intentionally not supported, as they are not compliant with the DASH-AVC/264 guidelines'; logger.fatal(msg); errHandler.error(new DashJSError(Errors.MANIFEST_ERROR_ID_MULTIPLEXED_CODE, msg, manifestModel.getValue())); return false; } if (type === Constants.TEXT || type === Constants.IMAGE) { return true; } if (!!mediaInfo.contentProtection &amp;&amp; !capabilities.supportsEncryptedMedia()) { errHandler.error(new DashJSError(Errors.CAPABILITY_MEDIAKEYS_ERROR_CODE, Errors.CAPABILITY_MEDIAKEYS_ERROR_MESSAGE)); return false; } return true; } /** * Creates the StreamProcessor for a given media type. * @param {object} initialMediaInfo * @param {array} allMediaForType * @param {object} mediaSource * @private */ function _createStreamProcessor(allMediaForType, mediaSource) { const mediaInfo = (allMediaForType &amp;&amp; allMediaForType.length > 0) ? allMediaForType[0] : null; let fragmentModel = fragmentController.getModel(mediaInfo ? mediaInfo.type : null); const type = mediaInfo ? mediaInfo.type : null; const mimeType = mediaInfo ? mediaInfo.mimeType : null; const isFragmented = mediaInfo ? mediaInfo.isFragmented : null; let streamProcessor = StreamProcessor(context).create({ streamInfo, type, mimeType, timelineConverter, adapter, manifestModel, mediaPlayerModel, fragmentModel, dashMetrics: config.dashMetrics, baseURLController: config.baseURLController, segmentBaseController: config.segmentBaseController, abrController, playbackController, mediaController, textController, errHandler, settings, boxParser, segmentBlacklistController }); streamProcessor.initialize(mediaSource, hasVideoTrack, isFragmented); streamProcessors.push(streamProcessor); for (let i = 0; i &lt; allMediaForType.length; i++) { streamProcessor.addMediaInfo(allMediaForType[i]); } if (type === Constants.TEXT) { textController.addMediaInfosToBuffer(streamInfo, type, allMediaForType, fragmentModel); } return streamProcessor; } /** * Creates the SourceBufferSink objects for all StreamProcessors * @param {array} previousBuffersSinks * @return {Promise&lt;object>} * @private */ function _createBufferSinks(previousBuffersSinks) { return new Promise((resolve) => { const buffers = {}; const promises = streamProcessors.map((sp) => { return sp.createBufferSinks(previousBuffersSinks); }); Promise.all(promises) .then((bufferSinks) => { bufferSinks.forEach((sink) => { if (sink) { buffers[sink.getType()] = sink; } }); resolve(buffers); }) .catch(() => { resolve(buffers); }); }); } /** * Partially resets some of the Stream elements. This function is called when preloading of streams is canceled or a stream switch occurs. * @memberof Stream# * @param {boolean} keepBuffers */ function deactivate(keepBuffers) { let ln = streamProcessors ? streamProcessors.length : 0; const errored = false; for (let i = 0; i &lt; ln; i++) { let fragmentModel = streamProcessors[i].getFragmentModel(); fragmentModel.abortRequests(); fragmentModel.resetInitialSettings(); streamProcessors[i].reset(errored, keepBuffers); } if (textController) { textController.deactivateStream(streamInfo); } streamProcessors = []; isActive = false; hasFinishedBuffering = false; setPreloaded(false); setIsEndedEventSignaled(false); eventBus.trigger(Events.STREAM_DEACTIVATED, { streamInfo }); } function getIsActive() { return isActive; } function setMediaSource(mediaSource) { for (let i = 0; i &lt; streamProcessors.length;) { if (_isMediaSupported(streamProcessors[i].getMediaInfo())) { streamProcessors[i].setMediaSource(mediaSource); i++; } else { streamProcessors[i].reset(); streamProcessors.splice(i, 1); } } if (streamProcessors.length === 0) { const msg = 'No streams to play.'; errHandler.error(new DashJSError(Errors.MANIFEST_ERROR_ID_NOSTREAMS_CODE, msg + 'nostreams', manifestModel.getValue())); logger.fatal(msg); } } function resetInitialSettings(keepBuffers) { deactivate(keepBuffers); isInitialized = false; hasVideoTrack = false; hasAudioTrack = false; updateError = {}; isUpdating = false; isEndedEventSignaled = false; trackChangedEvents = []; } function reset(keepBuffers) { if (fragmentController) { fragmentController.reset(); fragmentController = null; } if (abrController &amp;&amp; streamInfo) { abrController.clearDataForStream(streamInfo.id); } if (segmentBlacklistController) { segmentBlacklistController.reset(); segmentBlacklistController = null; } resetInitialSettings(keepBuffers); streamInfo = null; unRegisterEvents(); unRegisterProtectionEvents(); } function getDuration() { return streamInfo ? streamInfo.duration : NaN; } function getIsEndedEventSignaled() { return isEndedEventSignaled; } function setIsEndedEventSignaled(value) { isEndedEventSignaled = value; } function getStartTime() { return streamInfo ? streamInfo.start : NaN; } function getId() { return streamInfo ? streamInfo.id : null; } function getStreamInfo() { return streamInfo; } function getHasAudioTrack() { return hasAudioTrack; } function getHasVideoTrack() { return hasVideoTrack; } function getThumbnailController() { return thumbnailController; } function checkConfig() { if (!videoModel || !abrController) { throw new Error(Constants.MISSING_CONFIG_ERROR); } } /** * @param {string} type * @returns {Array} * @memberof Stream# */ function getBitrateListFor(type) { checkConfig(); if (type === Constants.IMAGE) { if (!thumbnailController) { return []; } return thumbnailController.getBitrateList(); } const mediaInfo = getMediaInfo(type); return abrController.getBitrateList(mediaInfo); } function onProtectionError(event) { if (event.error) { errHandler.error(event.error); logger.fatal(event.error.message); } } function prepareTrackChange(e) { if (!isActive || !streamInfo) { return; } hasFinishedBuffering = false; let mediaInfo = e.newMediaInfo; let manifest = manifestModel.getValue(); adapter.setCurrentMediaInfo(streamInfo.id, mediaInfo.type, mediaInfo); let processor = getProcessorForMediaInfo(mediaInfo); if (!processor) return; let currentTime = playbackController.getTime(); logger.info('Stream - Process track changed at current time ' + currentTime); // Applies only for MSS streams if (manifest.refreshManifestOnSwitchTrack) { trackChangedEvents.push(e); if (!manifestUpdater.getIsUpdating()) { logger.debug('Stream - Refreshing manifest for switch track'); manifestUpdater.refreshManifest(); } } else { processor.selectMediaInfo(mediaInfo) .then(() => { if (mediaInfo.type === Constants.VIDEO || mediaInfo.type === Constants.AUDIO) { abrController.updateTopQualityIndex(mediaInfo); } processor.prepareTrackSwitch(); }); } } function prepareQualityChange(e) { const processor = _getProcessorByType(e.mediaType); if (processor) { processor.prepareQualityChange(e); } } function _addInlineEvents() { if (eventController) { const events = adapter.getEventsFor(streamInfo); if (events &amp;&amp; events.length > 0) { eventController.addInlineEvents(events, streamInfo.id); } } } function _checkIfInitializationCompleted() { const ln = streamProcessors.length; const hasError = !!updateError.audio || !!updateError.video; let error = hasError ? new DashJSError(Errors.DATA_UPDATE_FAILED_ERROR_CODE, Errors.DATA_UPDATE_FAILED_ERROR_MESSAGE) : null; for (let i = 0; i &lt; ln; i++) { if (streamProcessors[i].isUpdating() || isUpdating) { return; } } if (protectionController) { // Need to check if streamProcessors exists because streamProcessors // could be cleared in case an error is detected while initializing DRM keysystem protectionController.clearMediaInfoArray(); for (let i = 0; i &lt; ln &amp;&amp; streamProcessors[i]; i++) { const type = streamProcessors[i].getType(); const mediaInfo = streamProcessors[i].getMediaInfo(); if (type === Constants.AUDIO || type === Constants.VIDEO || (type === Constants.TEXT &amp;&amp; mediaInfo.isFragmented)) { let mediaInfo = streamProcessors[i].getMediaInfo(); if (mediaInfo) { protectionController.initializeForMedia(mediaInfo); } } } protectionController.handleKeySystemFromManifest(); } if (error) { errHandler.error(error); } else if (!isInitialized) { isInitialized = true; videoModel.waitForReadyState(Constants.VIDEO_ELEMENT_READY_STATES.HAVE_METADATA, () => { eventBus.trigger(Events.STREAM_INITIALIZED, { streamInfo: streamInfo }); }) } } function getMediaInfo(type) { let streamProcessor = null; for (let i = 0; i &lt; streamProcessors.length; i++) { streamProcessor = streamProcessors[i]; if (streamProcessor.getType() === type) { return streamProcessor.getMediaInfo(); } } return null; } function onBufferingCompleted() { let processors = getProcessors(); const ln = processors.length; if (ln === 0) { logger.warn('onBufferingCompleted - can\'t trigger STREAM_BUFFERING_COMPLETED because no streamProcessor is defined'); return; } // if there is at least one buffer controller that has not completed buffering yet do nothing for (let i = 0; i &lt; ln; i++) { //if audio or video buffer is not buffering completed state, do not send STREAM_BUFFERING_COMPLETED if (!processors[i].isBufferingCompleted() &amp;&amp; (processors[i].getType() === Constants.AUDIO || processors[i].getType() === Constants.VIDEO)) { logger.debug('onBufferingCompleted - One streamProcessor has finished but', processors[i].getType(), 'one is not buffering completed'); return; } } logger.debug('onBufferingCompleted - trigger STREAM_BUFFERING_COMPLETED'); hasFinishedBuffering = true; eventBus.trigger(Events.STREAM_BUFFERING_COMPLETED, { streamInfo: streamInfo }, { streamInfo }); } function onDataUpdateCompleted(e) { updateError[e.mediaType] = e.error; _checkIfInitializationCompleted(); } function onInbandEvents(e) { if (eventController) { eventController.addInbandEvents(e.events, streamInfo.id); } } function getProcessorForMediaInfo(mediaInfo) { if (!mediaInfo || !mediaInfo.type) { return null; } return _getProcessorByType(mediaInfo.type); } function _getProcessorByType(type) { if (!type) { return null; } let processors = getProcessors(); return processors.filter(function (processor) { return (processor.getType() === type); })[0]; } function getProcessors() { let arr = []; let type, streamProcessor; for (let i = 0; i &lt; streamProcessors.length; i++) { streamProcessor = streamProcessors[i]; type = streamProcessor.getType(); if (type === Constants.AUDIO || type === Constants.VIDEO || type === Constants.TEXT) { arr.push(streamProcessor); } } return arr; } function startScheduleControllers() { const ln = streamProcessors.length; for (let i = 0; i &lt; ln &amp;&amp; streamProcessors[i]; i++) { streamProcessors[i].getScheduleController().startScheduleTimer(); } } function updateData(updatedStreamInfo) { return new Promise((resolve) => { isUpdating = true; streamInfo = updatedStreamInfo; if (eventController) { _addInlineEvents(); } let promises = []; for (let i = 0, ln = streamProcessors.length; i &lt; ln; i++) { let streamProcessor = streamProcessors[i]; const currentMediaInfo = streamProcessor.getMediaInfo(); promises.push(streamProcessor.updateStreamInfo(streamInfo)); let allMediaForType = adapter.getAllMediaInfoForType(streamInfo, streamProcessor.getType()); // Check if AdaptationSet has not been removed in MPD update if (allMediaForType) { // Remove the current mediaInfo objects before adding the updated ones streamProcessor.clearMediaInfoArray(); for (let j = 0; j &lt; allMediaForType.length; j++) { const mInfo = allMediaForType[j]; streamProcessor.addMediaInfo(allMediaForType[j]); if (adapter.areMediaInfosEqual(currentMediaInfo, mInfo)) { abrController.updateTopQualityIndex(mInfo); promises.push(streamProcessor.selectMediaInfo(mInfo)) } } } } Promise.all(promises) .then(() => { promises = []; while (trackChangedEvents.length > 0) { let trackChangedEvent = trackChangedEvents.pop(); let mediaInfo = trackChangedEvent.newMediaInfo; let processor = getProcessorForMediaInfo(trackChangedEvent.oldMediaInfo); if (!processor) return; promises.push(processor.prepareTrackSwitch()); processor.selectMediaInfo(mediaInfo); } return Promise.all(promises) }) .then(() => { isUpdating = false; _checkIfInitializationCompleted(); eventBus.trigger(Events.STREAM_UPDATED, { streamInfo: streamInfo }); resolve(); }) }) } function isMediaCodecCompatible(newStream, previousStream = null) { return compareCodecs(newStream, Constants.VIDEO, previousStream) &amp;&amp; compareCodecs(newStream, Constants.AUDIO, previousStream); } function isProtectionCompatible(newStream) { if (!newStream) { return true; } return _compareProtectionConfig(Constants.VIDEO, newStream) &amp;&amp; _compareProtectionConfig(Constants.AUDIO, newStream); } function _compareProtectionConfig(type, newStream) { const currentStreamInfo = getStreamInfo(); const newStreamInfo = newStream.getStreamInfo(); if (!newStreamInfo || !currentStreamInfo) { return true; } const newAdaptation = adapter.getAdaptationForType(newStreamInfo.index, type, newStreamInfo); const currentAdaptation = adapter.getAdaptationForType(currentStreamInfo.index, type, currentStreamInfo); if (!newAdaptation || !currentAdaptation) { // If there is no adaptation for neither the old or the new stream they're compatible return !newAdaptation &amp;&amp; !currentAdaptation; } // If the current period is unencrypted and the upcoming one is encrypted we need to reset sourcebuffers. return !(!_isAdaptationDrmProtected(currentAdaptation) &amp;&amp; _isAdaptationDrmProtected(newAdaptation)); } function _isAdaptationDrmProtected(adaptation) { if (!adaptation) { // If there is no adaptation for neither the old or the new stream they're compatible return false; } // If the current period is unencrypted and the upcoming one is encrypted we need to reset sourcebuffers. return !!(adaptation.ContentProtection || (adaptation.Representation &amp;&amp; adaptation.Representation.length > 0 &amp;&amp; adaptation.Representation[0].ContentProtection)); } function compareCodecs(newStream, type, previousStream = null) { if (!newStream || !newStream.hasOwnProperty('getStreamInfo')) { return false; } const newStreamInfo = newStream.getStreamInfo(); const currentStreamInfo = previousStream ? previousStream.getStreamInfo() : getStreamInfo(); if (!newStreamInfo || !currentStreamInfo) { return false; } const newAdaptation = adapter.getAdaptationForType(newStreamInfo.index, type, newStreamInfo); const currentAdaptation = adapter.getAdaptationForType(currentStreamInfo.index, type, currentStreamInfo); if (!newAdaptation || !currentAdaptation) { // If there is no adaptation for neither the old or the new stream they're compatible return !newAdaptation &amp;&amp; !currentAdaptation; } const sameMimeType = newAdaptation &amp;&amp; currentAdaptation &amp;&amp; newAdaptation.mimeType === currentAdaptation.mimeType; const oldCodecs = currentAdaptation.Representation_asArray.map((representation) => { return representation.codecs; }); const newCodecs = newAdaptation.Representation_asArray.map((representation) => { return representation.codecs; }); const codecMatch = newCodecs.some((newCodec) => { return oldCodecs.indexOf(newCodec) > -1; }); const partialCodecMatch = newCodecs.some((newCodec) => oldCodecs.some((oldCodec) => capabilities.codecRootCompatibleWithCodec(oldCodec, newCodec))); return codecMatch || (partialCodecMatch &amp;&amp; sameMimeType); } function setPreloaded(value) { preloaded = value; } function getPreloaded() { return preloaded; } function getHasFinishedBuffering() { return hasFinishedBuffering; } function getAdapter() { return adapter; } instance = { initialize, getStreamId, activate, deactivate, getIsActive, getDuration, getStartTime, getId, getStreamInfo, getHasAudioTrack, getHasVideoTrack, startPreloading, getThumbnailController, getBitrateListFor, updateData, reset, getProcessors, setMediaSource, isMediaCodecCompatible, isProtectionCompatible, getPreloaded, getIsEndedEventSignaled, setIsEndedEventSignaled, getAdapter, getHasFinishedBuffering, setPreloaded, startScheduleControllers, prepareTrackChange, prepareQualityChange }; setup(); return instance; } Stream.__dashjs_factory_name = 'Stream'; export default FactoryMaker.getClassFactory(Stream); </pre> </article> </section> </div> </div> <div class="clearfix"></div> </div> </div> <div class="modal fade" id="searchResults"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <h4 class="modal-title">Search results</h4> </div> <div class="modal-body"></div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div> <footer> <span class="copyright"> <h3>DASH Industry Forum</h3> </span> <span class="jsdoc-message"> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.7</a> on Wed Sep 28th 2022 using the <a href="https://github.com/docstrap/docstrap">DocStrap template</a>. </span> </footer> <script src="scripts/docstrap.lib.js"></script> <script src="scripts/toc.js"></script> <script type="text/javascript" src="scripts/fulltext-search-ui.js"></script> <script> $( function () { $( "[id*='$']" ).each( function () { var $this = $( this ); $this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) ); } ); $( ".tutorial-section pre, .readme-section pre, pre.prettyprint.source" ).each( function () { var $this = $( this ); var example = $this.find( "code" ); exampleText = example.html(); var lang = /{@lang (.*?)}/.exec( exampleText ); if ( lang && lang[1] ) { exampleText = exampleText.replace( lang[0], "" ); example.html( exampleText ); lang = lang[1]; } else { var langClassMatch = example.parent()[0].className.match(/lang\-(\S+)/); lang = langClas