@l5i/dashjs
Version:
A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.
71 lines (70 loc) • 24.4 kB
JavaScript
/**
* 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.
*/'use strict';Object.defineProperty(exports,'__esModule',{value:true});function _interopRequireDefault(obj){return obj && obj.__esModule?obj:{'default':obj};}var _constantsConstants=require('../constants/Constants');var _constantsConstants2=_interopRequireDefault(_constantsConstants);var _BufferController=require('./BufferController');var _BufferController2=_interopRequireDefault(_BufferController);var _modelsURIFragmentModel=require('../models/URIFragmentModel');var _modelsURIFragmentModel2=_interopRequireDefault(_modelsURIFragmentModel);var _coreEventBus=require('../../core/EventBus');var _coreEventBus2=_interopRequireDefault(_coreEventBus);var _coreEventsEvents=require('../../core/events/Events');var _coreEventsEvents2=_interopRequireDefault(_coreEventsEvents);var _coreFactoryMaker=require('../../core/FactoryMaker');var _coreFactoryMaker2=_interopRequireDefault(_coreFactoryMaker);var _coreDebug=require('../../core/Debug');var _coreDebug2=_interopRequireDefault(_coreDebug);var LIVE_UPDATE_PLAYBACK_TIME_INTERVAL_MS=500;var DEFAULT_CATCHUP_PLAYBACK_RATE=0.05; // Start catching up mechanism for low latency live streaming
// when latency goes beyong targetDelay * (1 + LIVE_CATCHUP_START_THRESHOLD)
var LIVE_CATCHUP_START_THRESHOLD=0.35;function PlaybackController(){var context=this.context;var eventBus=(0,_coreEventBus2['default'])(context).getInstance();var instance=undefined,logger=undefined,streamController=undefined,metricsModel=undefined,dashMetrics=undefined,manifestModel=undefined,dashManifestModel=undefined,adapter=undefined,videoModel=undefined,currentTime=undefined,liveStartTime=undefined,wallclockTimeIntervalId=undefined,commonEarliestTime=undefined,liveDelay=undefined,bufferedRange=undefined,streamInfo=undefined,isDynamic=undefined,mediaPlayerModel=undefined,playOnceInitialized=undefined,lastLivePlaybackTime=undefined,originalPlaybackRate=undefined,availabilityStartTime=undefined,compatibleWithPreviousStream=undefined;var catchUpPlaybackRate=DEFAULT_CATCHUP_PLAYBACK_RATE;function setup(){logger = (0,_coreDebug2['default'])(context).getInstance().getLogger(instance);reset();}function initialize(StreamInfo,compatible){streamInfo = StreamInfo;addAllListeners();isDynamic = streamInfo.manifestInfo.isDynamic;liveStartTime = streamInfo.start;compatibleWithPreviousStream = compatible;eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED,onDataUpdateCompleted,this);eventBus.on(_coreEventsEvents2['default'].BYTES_APPENDED_END_FRAGMENT,onBytesAppended,this);eventBus.on(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED,onBufferLevelStateChanged,this);eventBus.on(_coreEventsEvents2['default'].PERIOD_SWITCH_STARTED,onPeriodSwitchStarted,this);eventBus.on(_coreEventsEvents2['default'].PLAYBACK_PROGRESS,onPlaybackProgression,this);eventBus.on(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED,onPlaybackProgression,this);eventBus.on(_coreEventsEvents2['default'].PLAYBACK_ENDED,onPlaybackEnded,this);if(playOnceInitialized){playOnceInitialized = false;play();}}function onPeriodSwitchStarted(e){if(!isDynamic && e.fromStreamInfo && commonEarliestTime[e.fromStreamInfo.id] !== undefined){delete bufferedRange[e.fromStreamInfo.id];delete commonEarliestTime[e.fromStreamInfo.id];}}function getTimeToStreamEnd(){return parseFloat((getStreamEndTime() - getTime()).toFixed(5));}function getStreamEndTime(){var startTime=getStreamStartTime(true);var offset=isDynamic?startTime - streamInfo.start:0;return startTime + (streamInfo.duration - offset);}function play(){if(streamInfo && videoModel && videoModel.getElement()){videoModel.play();}else {playOnceInitialized = true;}}function isPaused(){return streamInfo && videoModel?videoModel.isPaused():null;}function pause(){if(streamInfo && videoModel){videoModel.pause();}}function isSeeking(){return streamInfo && videoModel?videoModel.isSeeking():null;}function seek(time,stickToBuffered,internalSeek){if(streamInfo && videoModel){if(internalSeek === true){if(time !== videoModel.getTime()){ // Internal seek = seek video model only (disable 'seeking' listener),
// buffer(s) are already appended at given time (see onBytesAppended())
videoModel.removeEventListener('seeking',onPlaybackSeeking);logger.info('Requesting seek to time: ' + time);videoModel.setCurrentTime(time,stickToBuffered);}}else {eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_SEEK_ASKED);logger.info('Requesting seek to time: ' + time);videoModel.setCurrentTime(time,stickToBuffered);}}}function getTime(){return streamInfo && videoModel?videoModel.getTime():null;}function getPlaybackRate(){return streamInfo && videoModel?videoModel.getPlaybackRate():null;}function getPlayedRanges(){return streamInfo && videoModel?videoModel.getPlayedRanges():null;}function getEnded(){return streamInfo && videoModel?videoModel.getEnded():null;}function getIsDynamic(){return isDynamic;}function getStreamController(){return streamController;}function setLiveStartTime(value){liveStartTime = value;}function getLiveStartTime(){return liveStartTime;}function setCatchUpPlaybackRate(value){catchUpPlaybackRate = value; // If value == 0.0, deactivate catchup mechanism
if(value === 0.0 && getPlaybackRate() > 1.0){stopPlaybackCatchUp();}}function getCatchUpPlaybackRate(){return catchUpPlaybackRate;} /**
* Computes the desirable delay for the live edge to avoid a risk of getting 404 when playing at the bleeding edge
* @param {number} fragmentDuration - seconds?
* @param {number} dvrWindowSize - seconds?
* @returns {number} object
* @memberof PlaybackController#
*/function computeLiveDelay(fragmentDuration,dvrWindowSize){var mpd=dashManifestModel.getMpd(manifestModel.getValue());var delay=undefined;var ret=undefined;var END_OF_PLAYLIST_PADDING=10;if(mediaPlayerModel.getUseSuggestedPresentationDelay() && mpd.hasOwnProperty(_constantsConstants2['default'].SUGGESTED_PRESENTATION_DELAY)){delay = mpd.suggestedPresentationDelay;}else if(mediaPlayerModel.getLowLatencyEnabled()){delay = 0;}else if(mediaPlayerModel.getLiveDelay()){delay = mediaPlayerModel.getLiveDelay(); // If set by user, this value takes precedence
}else if(!isNaN(fragmentDuration)){delay = fragmentDuration * mediaPlayerModel.getLiveDelayFragmentCount();}else {delay = streamInfo.manifestInfo.minBufferTime * 2;}if(mpd.availabilityStartTime){availabilityStartTime = mpd.availabilityStartTime.getTime();}if(dvrWindowSize > 0){ // cap target latency to:
// - dvrWindowSize / 2 for short playlists
// - dvrWindowSize - END_OF_PLAYLIST_PADDING for longer playlists
var targetDelayCapping=Math.max(dvrWindowSize - END_OF_PLAYLIST_PADDING,dvrWindowSize / 2);ret = Math.min(delay,targetDelayCapping);}else {ret = delay;}liveDelay = ret;return ret;}function getLiveDelay(){return liveDelay;}function getCurrentLiveLatency(){if(!isDynamic || isNaN(availabilityStartTime)){return NaN;}var currentTime=getTime();if(isNaN(currentTime) || currentTime === 0){return 0;}return (Math.round(new Date().getTime() - (currentTime * 1000 + availabilityStartTime)) / 1000).toFixed(3);}function startPlaybackCatchUp(){if(videoModel){var playbackRate=1 + getCatchUpPlaybackRate();var currentRate=getPlaybackRate();if(playbackRate !== currentRate){logger.info('Starting live catchup mechanism. Setting playback rate to',playbackRate);originalPlaybackRate = currentRate;videoModel.setPlaybackRate(playbackRate);eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_CATCHUP_START,{sender:instance});}}}function stopPlaybackCatchUp(){if(videoModel){var playbackRate=originalPlaybackRate || 1;if(playbackRate !== getPlaybackRate()){logger.info('Stopping live catchup mechanism. Setting playback rate to',playbackRate);videoModel.setPlaybackRate(playbackRate);eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_CATCHUP_END,{sender:instance});}}}function reset(){currentTime = 0;liveStartTime = NaN;playOnceInitialized = false;commonEarliestTime = {};liveDelay = 0;availabilityStartTime = 0;catchUpPlaybackRate = DEFAULT_CATCHUP_PLAYBACK_RATE;bufferedRange = {};if(videoModel){eventBus.off(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED,onDataUpdateCompleted,this);eventBus.off(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED,onBufferLevelStateChanged,this);eventBus.off(_coreEventsEvents2['default'].BYTES_APPENDED_END_FRAGMENT,onBytesAppended,this);eventBus.off(_coreEventsEvents2['default'].PERIOD_SWITCH_STARTED,onPeriodSwitchStarted,this);eventBus.off(_coreEventsEvents2['default'].PLAYBACK_PROGRESS,onPlaybackProgression,this);eventBus.off(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED,onPlaybackProgression,this);eventBus.off(_coreEventsEvents2['default'].PLAYBACK_ENDED,onPlaybackEnded,this);stopUpdatingWallclockTime();removeAllListeners();}wallclockTimeIntervalId = null;videoModel = null;streamInfo = null;isDynamic = null;}function setConfig(config){if(!config)return;if(config.streamController){streamController = config.streamController;}if(config.metricsModel){metricsModel = config.metricsModel;}if(config.dashMetrics){dashMetrics = config.dashMetrics;}if(config.manifestModel){manifestModel = config.manifestModel;}if(config.dashManifestModel){dashManifestModel = config.dashManifestModel;}if(config.mediaPlayerModel){mediaPlayerModel = config.mediaPlayerModel;}if(config.adapter){adapter = config.adapter;}if(config.videoModel){videoModel = config.videoModel;}}function getStartTimeFromUriParameters(){var fragData=(0,_modelsURIFragmentModel2['default'])(context).getInstance().getURIFragmentData();var uriParameters=undefined;if(fragData){uriParameters = {};var r=parseInt(fragData.r,10);if(r >= 0 && streamInfo && r < streamInfo.manifestInfo.DVRWindowSize && fragData.t === null){fragData.t = Math.floor(Date.now() / 1000) - streamInfo.manifestInfo.DVRWindowSize + r;}uriParameters.fragS = parseFloat(fragData.s);uriParameters.fragT = parseFloat(fragData.t);}return uriParameters;} /**
* @param {boolean} ignoreStartOffset - ignore URL fragment start offset if true
* @param {number} liveEdge - liveEdge value
* @returns {number} object
* @memberof PlaybackController#
*/function getStreamStartTime(ignoreStartOffset,liveEdge){var presentationStartTime=undefined;var startTimeOffset=NaN;var uriParameters=getStartTimeFromUriParameters();if(uriParameters){if(!ignoreStartOffset){startTimeOffset = !isNaN(uriParameters.fragS)?uriParameters.fragS:uriParameters.fragT;}else {startTimeOffset = streamInfo.start;}}else { // handle case where no media fragments are parsed from the manifest URL
startTimeOffset = 0;}if(isDynamic){if(!isNaN(startTimeOffset)){presentationStartTime = startTimeOffset - streamInfo.manifestInfo.availableFrom.getTime() / 1000;if(presentationStartTime > liveStartTime || presentationStartTime < (!isNaN(liveEdge)?liveEdge - streamInfo.manifestInfo.DVRWindowSize:NaN)){presentationStartTime = null;}}presentationStartTime = presentationStartTime || liveStartTime;}else {if(!isNaN(startTimeOffset) && startTimeOffset < Math.max(streamInfo.manifestInfo.duration,streamInfo.duration) && startTimeOffset >= 0){presentationStartTime = startTimeOffset;}else {var earliestTime=commonEarliestTime[streamInfo.id]; //set by ready bufferStart after first onBytesAppended
presentationStartTime = earliestTime !== undefined?Math.max(earliestTime.audio !== undefined?earliestTime.audio:0,earliestTime.video !== undefined?earliestTime.video:0,streamInfo.start):streamInfo.start;}}return presentationStartTime;}function getActualPresentationTime(currentTime){var metrics=metricsModel.getReadOnlyMetricsFor(_constantsConstants2['default'].VIDEO) || metricsModel.getReadOnlyMetricsFor(_constantsConstants2['default'].AUDIO);var DVRMetrics=dashMetrics.getCurrentDVRInfo(metrics);var DVRWindow=DVRMetrics?DVRMetrics.range:null;var actualTime=undefined;if(!DVRWindow)return NaN;if(currentTime > DVRWindow.end){actualTime = Math.max(DVRWindow.end - streamInfo.manifestInfo.minBufferTime * 2,DVRWindow.start);}else if(currentTime + 0.250 < DVRWindow.start){ // Checking currentTime plus 250ms as the 'timeupdate' is fired with a frequency between 4Hz and 66Hz
// https://developer.mozilla.org/en-US/docs/Web/Events/timeupdate
// http://w3c.github.io/html/single-page.html#offsets-into-the-media-resource
actualTime = DVRWindow.start;}else {return currentTime;}return actualTime;}function startUpdatingWallclockTime(){if(wallclockTimeIntervalId !== null)return;var tick=function tick(){onWallclockTime();};wallclockTimeIntervalId = setInterval(tick,mediaPlayerModel.getWallclockTimeUpdateInterval());}function stopUpdatingWallclockTime(){clearInterval(wallclockTimeIntervalId);wallclockTimeIntervalId = null;}function updateCurrentTime(){if(isPaused() || !isDynamic || videoModel.getReadyState() === 0)return;var currentTime=getTime();var actualTime=getActualPresentationTime(currentTime);var timeChanged=!isNaN(actualTime) && actualTime !== currentTime;if(timeChanged){seek(actualTime);}}function onDataUpdateCompleted(e){if(e.error)return;var representationInfo=adapter.convertDataToRepresentationInfo(e.currentRepresentation);var info=representationInfo.mediaInfo.streamInfo;if(streamInfo.id !== info.id)return;streamInfo = info;updateCurrentTime();}function onCanPlay(){eventBus.trigger(_coreEventsEvents2['default'].CAN_PLAY);}function onPlaybackStart(){logger.info('Native video element event: play');updateCurrentTime();startUpdatingWallclockTime();eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_STARTED,{startTime:getTime()});}function onPlaybackWaiting(){logger.info('Native video element event: waiting');eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_WAITING,{playingTime:getTime()});}function onPlaybackPlaying(){logger.info('Native video element event: playing');eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_PLAYING,{playingTime:getTime()});}function onPlaybackPaused(){logger.info('Native video element event: pause');eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_PAUSED,{ended:getEnded()});}function onPlaybackSeeking(){var seekTime=getTime();logger.info('Seeking to: ' + seekTime);startUpdatingWallclockTime();eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_SEEKING,{seekTime:seekTime});}function onPlaybackSeeked(){logger.info('Native video element event: seeked');eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_SEEKED); // Reactivate 'seeking' event listener (see seek())
videoModel.addEventListener('seeking',onPlaybackSeeking);}function onPlaybackTimeUpdated(){var time=getTime();currentTime = time;eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED,{timeToEnd:getTimeToStreamEnd(),time:time});}function updateLivePlaybackTime(){var now=Date.now();if(!lastLivePlaybackTime || now > lastLivePlaybackTime + LIVE_UPDATE_PLAYBACK_TIME_INTERVAL_MS){lastLivePlaybackTime = now;onPlaybackTimeUpdated();}}function onPlaybackProgress(){eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_PROGRESS);}function onPlaybackRateChanged(){var rate=getPlaybackRate();logger.info('Native video element event: ratechange: ',rate);eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_RATE_CHANGED,{playbackRate:rate});}function onPlaybackMetaDataLoaded(){logger.info('Native video element event: loadedmetadata');eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_METADATA_LOADED);startUpdatingWallclockTime();} // Event to handle the native video element ended event
function onNativePlaybackEnded(){logger.info('Native video element event: ended');pause();stopUpdatingWallclockTime();eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_ENDED,{'isLast':streamController.getActiveStreamInfo().isLast});} // Handle DASH PLAYBACK_ENDED event
function onPlaybackEnded(e){if(wallclockTimeIntervalId && e.isLast){ // PLAYBACK_ENDED was triggered elsewhere, react.
logger.info('[PlaybackController] onPlaybackEnded -- PLAYBACK_ENDED but native video element didn\'t fire ended');videoModel.setCurrentTime(getStreamEndTime());pause();stopUpdatingWallclockTime();}}function onPlaybackError(event){var target=event.target || event.srcElement;eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_ERROR,{error:target.error});}function onWallclockTime(){eventBus.trigger(_coreEventsEvents2['default'].WALLCLOCK_TIME_UPDATED,{isDynamic:isDynamic,time:new Date()}); // Updates playback time for paused dynamic streams
// (video element doesn't call timeupdate when the playback is paused)
if(getIsDynamic() && isPaused()){updateLivePlaybackTime();}}function checkTimeInRanges(time,ranges){if(ranges && ranges.length > 0){for(var i=0,len=ranges.length;i < len;i++) {if(time >= ranges.start(i) && time < ranges.end(i)){return true;}}}return false;}function onPlaybackProgression(){if(isDynamic && mediaPlayerModel.getLowLatencyEnabled() && getCatchUpPlaybackRate() > 0.0){if(!isCatchingUp() && needToCatchUp()){startPlaybackCatchUp();}else if(stopCatchingUp()){stopPlaybackCatchUp();}}}function needToCatchUp(){return getCurrentLiveLatency() > mediaPlayerModel.getLiveDelay() * (1 + LIVE_CATCHUP_START_THRESHOLD);}function stopCatchingUp(){return getCurrentLiveLatency() <= mediaPlayerModel.getLiveDelay();}function isCatchingUp(){return getCatchUpPlaybackRate() + 1 === getPlaybackRate();}function onBytesAppended(e){var earliestTime=undefined,initialStartTime=undefined;var ranges=e.bufferedRanges;if(!ranges || !ranges.length)return;if(commonEarliestTime[streamInfo.id] && commonEarliestTime[streamInfo.id].started === true){ //stream has already been started.
return;}var type=e.sender.getType();if(bufferedRange[streamInfo.id] === undefined){bufferedRange[streamInfo.id] = [];}bufferedRange[streamInfo.id][type] = ranges;if(commonEarliestTime[streamInfo.id] === undefined){commonEarliestTime[streamInfo.id] = [];commonEarliestTime[streamInfo.id].started = false;}if(commonEarliestTime[streamInfo.id][type] === undefined){commonEarliestTime[streamInfo.id][type] = Math.max(ranges.start(0),streamInfo.start);}var hasVideoTrack=streamController.isTrackTypePresent(_constantsConstants2['default'].VIDEO);var hasAudioTrack=streamController.isTrackTypePresent(_constantsConstants2['default'].AUDIO);initialStartTime = getStreamStartTime(false);if(hasAudioTrack && hasVideoTrack){ //current stream has audio and video contents
if(!isNaN(commonEarliestTime[streamInfo.id].audio) && !isNaN(commonEarliestTime[streamInfo.id].video)){if(commonEarliestTime[streamInfo.id].audio < commonEarliestTime[streamInfo.id].video){ // common earliest is video time
// check buffered audio range has video time, if ok, we seek, otherwise, we wait some other data
earliestTime = commonEarliestTime[streamInfo.id].video > initialStartTime?commonEarliestTime[streamInfo.id].video:initialStartTime;ranges = bufferedRange[streamInfo.id].audio;}else { // common earliest is audio time
// check buffered video range has audio time, if ok, we seek, otherwise, we wait some other data
earliestTime = commonEarliestTime[streamInfo.id].audio > initialStartTime?commonEarliestTime[streamInfo.id].audio:initialStartTime;ranges = bufferedRange[streamInfo.id].video;}if(checkTimeInRanges(earliestTime,ranges)){if(!isSeeking() && !compatibleWithPreviousStream && earliestTime !== 0){seek(earliestTime,true,true);}commonEarliestTime[streamInfo.id].started = true;}}}else { //current stream has only audio or only video content
if(commonEarliestTime[streamInfo.id][type]){earliestTime = commonEarliestTime[streamInfo.id][type] > initialStartTime?commonEarliestTime[streamInfo.id][type]:initialStartTime;if(!isSeeking() && !compatibleWithPreviousStream){seek(earliestTime,false,true);}commonEarliestTime[streamInfo.id].started = true;}}}function onBufferLevelStateChanged(e){ // do not stall playback when get an event from Stream that is not active
if(e.streamInfo.id !== streamInfo.id)return;videoModel.setStallState(e.mediaType,e.state === _BufferController2['default'].BUFFER_EMPTY);}function onPlaybackStalled(e){eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_STALLED,{e:e});}function addAllListeners(){videoModel.addEventListener('canplay',onCanPlay);videoModel.addEventListener('play',onPlaybackStart);videoModel.addEventListener('waiting',onPlaybackWaiting);videoModel.addEventListener('playing',onPlaybackPlaying);videoModel.addEventListener('pause',onPlaybackPaused);videoModel.addEventListener('error',onPlaybackError);videoModel.addEventListener('seeking',onPlaybackSeeking);videoModel.addEventListener('seeked',onPlaybackSeeked);videoModel.addEventListener('timeupdate',onPlaybackTimeUpdated);videoModel.addEventListener('progress',onPlaybackProgress);videoModel.addEventListener('ratechange',onPlaybackRateChanged);videoModel.addEventListener('loadedmetadata',onPlaybackMetaDataLoaded);videoModel.addEventListener('stalled',onPlaybackStalled);videoModel.addEventListener('ended',onNativePlaybackEnded);}function removeAllListeners(){videoModel.removeEventListener('canplay',onCanPlay);videoModel.removeEventListener('play',onPlaybackStart);videoModel.removeEventListener('waiting',onPlaybackWaiting);videoModel.removeEventListener('playing',onPlaybackPlaying);videoModel.removeEventListener('pause',onPlaybackPaused);videoModel.removeEventListener('error',onPlaybackError);videoModel.removeEventListener('seeking',onPlaybackSeeking);videoModel.removeEventListener('seeked',onPlaybackSeeked);videoModel.removeEventListener('timeupdate',onPlaybackTimeUpdated);videoModel.removeEventListener('progress',onPlaybackProgress);videoModel.removeEventListener('ratechange',onPlaybackRateChanged);videoModel.removeEventListener('loadedmetadata',onPlaybackMetaDataLoaded);videoModel.removeEventListener('stalled',onPlaybackStalled);videoModel.removeEventListener('ended',onNativePlaybackEnded);}instance = {initialize:initialize,setConfig:setConfig,getStartTimeFromUriParameters:getStartTimeFromUriParameters,getStreamStartTime:getStreamStartTime,getTimeToStreamEnd:getTimeToStreamEnd,getTime:getTime,getPlaybackRate:getPlaybackRate,getPlayedRanges:getPlayedRanges,getEnded:getEnded,getIsDynamic:getIsDynamic,getStreamController:getStreamController,setCatchUpPlaybackRate:setCatchUpPlaybackRate,setLiveStartTime:setLiveStartTime,getLiveStartTime:getLiveStartTime,computeLiveDelay:computeLiveDelay,getLiveDelay:getLiveDelay,getCurrentLiveLatency:getCurrentLiveLatency,play:play,isPaused:isPaused,pause:pause,isSeeking:isSeeking,seek:seek,reset:reset};setup();return instance;}PlaybackController.__dashjs_factory_name = 'PlaybackController';exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(PlaybackController);module.exports = exports['default'];
//# sourceMappingURL=PlaybackController.js.map