From f5be0c033ad60f890f636ae5c3ec89a836591f08 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Fri, 3 Apr 2020 10:47:22 +0200 Subject: [PATCH] Fix a bug in the calculation of the start period which lead to a stall of playback --- .../controllers/PlaybackController.js | 11 ++++--- src/streaming/controllers/StreamController.js | 32 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/streaming/controllers/PlaybackController.js b/src/streaming/controllers/PlaybackController.js index 3369d06e46..705b85e976 100644 --- a/src/streaming/controllers/PlaybackController.js +++ b/src/streaming/controllers/PlaybackController.js @@ -186,7 +186,6 @@ function PlaybackController() { t -= timeOffset; } } - return t; } @@ -222,15 +221,18 @@ function PlaybackController() { * 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? + * @param {number} minBufferTime - seconds? * @returns {number} object * @memberof PlaybackController# */ - function computeLiveDelay(fragmentDuration, dvrWindowSize) { + function computeLiveDelay(fragmentDuration, dvrWindowSize, minBufferTime = NaN) { let delay, ret, r, startTime; const END_OF_PLAYLIST_PADDING = 10; + const MIN_BUFFER_TIME_FACTOR = 4; + const FRAGMENT_DURATION_FACTOR = 4; let uriParameters = uriFragmentModel.getURIFragmentData(); @@ -251,9 +253,9 @@ function PlaybackController() { } else if (settings.get().streaming.useSuggestedPresentationDelay === true && suggestedPresentationDelay !== null && !isNaN(suggestedPresentationDelay) && suggestedPresentationDelay > 0) { delay = suggestedPresentationDelay; } else if (!isNaN(fragmentDuration)) { - delay = fragmentDuration * 4; + delay = fragmentDuration * FRAGMENT_DURATION_FACTOR; } else { - delay = streamInfo.manifestInfo.minBufferTime * 4; + delay = !isNaN(minBufferTime) ? minBufferTime * MIN_BUFFER_TIME_FACTOR : streamInfo.manifestInfo.minBufferTime * MIN_BUFFER_TIME_FACTOR; } startTime = adapter.getAvailabilityStartTime(); @@ -453,7 +455,6 @@ function PlaybackController() { if (isPaused() || !isDynamic || videoModel.getReadyState() === 0) return; const currentTime = getNormalizedTime(); const actualTime = getActualPresentationTime(currentTime); - const timeChanged = (!isNaN(actualTime) && actualTime !== currentTime); if (timeChanged) { seek(actualTime); diff --git a/src/streaming/controllers/StreamController.js b/src/streaming/controllers/StreamController.js index ed87207f50..68b52ed1ce 100644 --- a/src/streaming/controllers/StreamController.js +++ b/src/streaming/controllers/StreamController.js @@ -208,7 +208,7 @@ function StreamController() { // Find out what is the right time position to jump to taking // into account state of buffer - for (let i = 0; i < streamProcessors.length; i ++) { + for (let i = 0; i < streamProcessors.length; i++) { const mediaBuffer = streamProcessors[i].getBuffer(); const ranges = mediaBuffer.getAllBufferRanges(); let nextRangeStartTime; @@ -262,7 +262,7 @@ function StreamController() { stopPreloadTimer(); } - if ( seekingStream === activeStream && preloading ) { + if (seekingStream === activeStream && preloading) { // Seeking to the current period was requested while preloading the next one, deactivate preloading one preloading.deactivate(true); } @@ -278,7 +278,7 @@ function StreamController() { createPlaylistMetrics(PlayList.SEEK_START_REASON); } - function onPlaybackStarted( /*e*/ ) { + function onPlaybackStarted( /*e*/) { logger.debug('[onPlaybackStarted]'); if (initialPlayback) { initialPlayback = false; @@ -324,9 +324,11 @@ function StreamController() { const delayPlaybackEnded = timeToEnd > 0 ? timeToEnd * 1000 : 0; const prefetchDelay = delayPlaybackEnded < PERIOD_PREFETCH_TIME ? delayPlaybackEnded / 4 : delayPlaybackEnded - PERIOD_PREFETCH_TIME; logger.debug('[toggleEndPeriodTimer] Going to fire preload in', prefetchDelay, 'milliseconds'); - prefetchTimerId = setTimeout(onStreamCanLoadNext, prefetchDelay); - logger.debug('[toggleEndPeriodTimer] start-up of timer to notify PLAYBACK_ENDED event. It will be triggered in',delayPlaybackEnded, 'milliseconds'); - playbackEndedTimerId = setTimeout(function () {eventBus.trigger(Events.PLAYBACK_ENDED, {'isLast': getActiveStreamInfo().isLast});}, delayPlaybackEnded); + prefetchTimerId = setTimeout(onStreamCanLoadNext, prefetchDelay); + logger.debug('[toggleEndPeriodTimer] start-up of timer to notify PLAYBACK_ENDED event. It will be triggered in', delayPlaybackEnded, 'milliseconds'); + playbackEndedTimerId = setTimeout(function () { + eventBus.trigger(Events.PLAYBACK_ENDED, {'isLast': getActiveStreamInfo().isLast}); + }, delayPlaybackEnded); } } } @@ -452,8 +454,7 @@ function StreamController() { audioTrackDetected = undefined; videoTrackDetected = undefined; switchStream(activeStream, nextStream, NaN); - } - else { + } else { logger.debug('StreamController no next stream found'); } flushPlaylistMetrics(nextStream ? PlayListTrace.END_OF_PERIOD_STOP_REASON : PlayListTrace.END_OF_CONTENT_STOP_REASON); @@ -667,10 +668,13 @@ function StreamController() { const initialTime = !isNaN(startTimeFormUriParameters.fragS) ? startTimeFormUriParameters.fragS : startTimeFormUriParameters.fragT; initialStream = getStreamForTime(initialTime); } - // For multiperiod streams we should avoid a switch of streams after the seek to the live edge. So we do a rough calculation of the expected seek time to find the right stream object. + // For multiperiod streams we should avoid a switch of streams after the seek to the live edge. So we do a calculation of the expected seek time to find the right stream object. if (!initialStream && adapter.getIsDynamic() && streams.length) { logger.debug('Dynamic multi-period stream: Trying to find the correct starting period'); - const targetTime = timelineConverter.calcPresentationTimeFromWallTime(new Date(), adapter.getRegularPeriods()[0]); + const manifestInfo = adapter.getStreamsInfo(undefined, 1)[0].manifestInfo; + const liveEdge = timelineConverter.calcPresentationTimeFromWallTime(new Date(), adapter.getRegularPeriods()[0]); + const targetDelay = playbackController.computeLiveDelay(NaN, manifestInfo.DVRWindowSize, manifestInfo.minBufferTime); + const targetTime = liveEdge - targetDelay; initialStream = getStreamForTime(targetTime); } switchStream(null, initialStream !== null ? initialStream : streams[0], NaN); @@ -685,7 +689,7 @@ function StreamController() { } } - function onTimeSyncCompleted( /*e*/ ) { + function onTimeSyncCompleted( /*e*/) { const manifest = manifestModel.getValue(); //TODO check if we can move this to initialize?? if (protectionController) { @@ -719,7 +723,7 @@ function StreamController() { useCalculatedLiveEdgeTime = adapter.getUseCalculatedLiveEdgeTimeForMediaInfo(mediaInfo); if (useCalculatedLiveEdgeTime) { logger.debug('SegmentTimeline detected using calculated Live Edge Time'); - const s = { streaming: { useManifestDateHeaderTimeSource: false } }; + const s = {streaming: {useManifestDateHeaderTimeSource: false}}; settings.update(s); } } @@ -956,8 +960,8 @@ function StreamController() { flushPlaylistMetrics( hasMediaError || hasInitialisationError ? - PlayListTrace.FAILURE_STOP_REASON : - PlayListTrace.USER_REQUEST_STOP_REASON + PlayListTrace.FAILURE_STOP_REASON : + PlayListTrace.USER_REQUEST_STOP_REASON ); for (let i = 0, ln = streams ? streams.length : 0; i < ln; i++) {