From 69987b8815413c2ce88361d0aee61ac4fd939f60 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Thu, 26 Nov 2020 15:55:14 +0100 Subject: [PATCH 1/5] Increase version number --- package.json | 2 +- src/core/Version.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9e41b32fc1..cb922182cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dashjs", - "version": "3.1.4", + "version": "3.2.0", "description": "A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.", "main": "build/es5/index.js", "types": "build/typings/index.d.ts", diff --git a/src/core/Version.js b/src/core/Version.js index 934ccdcbe3..939f2ccaa7 100644 --- a/src/core/Version.js +++ b/src/core/Version.js @@ -1,4 +1,4 @@ -const VERSION = '3.1.4'; +const VERSION = '3.2.0'; export function getVersionString() { return VERSION; } From 3b5a573d2348c865a3f03ca04495182757fd910c Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Thu, 26 Nov 2020 15:56:16 +0100 Subject: [PATCH 2/5] Reduce threshold for buffer completed check --- src/streaming/controllers/BufferController.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/streaming/controllers/BufferController.js b/src/streaming/controllers/BufferController.js index 6522e550f8..9079abb420 100644 --- a/src/streaming/controllers/BufferController.js +++ b/src/streaming/controllers/BufferController.js @@ -44,6 +44,7 @@ import Errors from '../../core/errors/Errors'; import {HTTPRequest} from '../vo/metrics/HTTPRequest'; const STALL_THRESHOLD = 0.5; +const BUFFERING_COMPLETED_THRESHOLD = 0.1; const BUFFER_END_THRESHOLD = 0.5; const BUFFER_RANGE_CALCULATION_THRESHOLD = 0.01; const QUOTA_EXCEEDED_ERROR_CODE = 22; @@ -564,7 +565,7 @@ function BufferController(config) { // No need to check buffer if type is not audio or video (for example if several errors occur during text parsing, so that the buffer cannot be filled, no error must occur on video playback) if (type !== Constants.AUDIO && type !== Constants.VIDEO) return; - if (seekClearedBufferingCompleted && !isBufferingCompleted && bufferLevel > 0 && playbackController && playbackController.getTimeToStreamEnd() - bufferLevel < STALL_THRESHOLD) { + if (seekClearedBufferingCompleted && !isBufferingCompleted && bufferLevel > 0 && playbackController && playbackController.getTimeToStreamEnd() - bufferLevel < BUFFERING_COMPLETED_THRESHOLD) { seekClearedBufferingCompleted = false; isBufferingCompleted = true; logger.debug('checkIfSufficientBuffer trigger BUFFERING_COMPLETED for type ' + type); From 146943113ed2baf56a3c5b9a923d4ce45afc3f40 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Thu, 26 Nov 2020 15:57:31 +0100 Subject: [PATCH 3/5] Adjust gap jumping logic for live streams --- src/streaming/controllers/GapController.js | 77 ++++++++++++++-------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/src/streaming/controllers/GapController.js b/src/streaming/controllers/GapController.js index 0ed3754936..d53814845a 100644 --- a/src/streaming/controllers/GapController.js +++ b/src/streaming/controllers/GapController.js @@ -34,7 +34,7 @@ import Events from '../../core/events/Events'; import EventBus from '../../core/EventBus'; const GAP_HANDLER_INTERVAL = 100; -const THRESHOLD_TO_STALLS = 10; +const THRESHOLD_TO_STALLS = 30; const GAP_THRESHOLD = 0.1; function GapController() { @@ -52,6 +52,7 @@ function GapController() { videoModel, timelineConverter, adapter, + jumpTimeoutHandler, logger; function initialize() { @@ -74,6 +75,7 @@ function GapController() { gapHandlerInterval = null; lastGapJumpPosition = NaN; wallclockTicked = 0; + jumpTimeoutHandler = null; } function setConfig(config) { @@ -101,12 +103,14 @@ function GapController() { } function registerEvents() { - eventBus.on(Events.WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, instance); + eventBus.on(Events.WALLCLOCK_TIME_UPDATED, _onWallclockTimeUpdated, this); + eventBus.on(Events.PLAYBACK_SEEKING, _onPlaybackSeeking, this); eventBus.on(Events.BYTES_APPENDED_END_FRAGMENT, onBytesAppended, instance); } function unregisterEvents() { - eventBus.off(Events.WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, instance); + eventBus.off(Events.WALLCLOCK_TIME_UPDATED, _onWallclockTimeUpdated, this); + eventBus.off(Events.PLAYBACK_SEEKING, _onPlaybackSeeking, this); eventBus.off(Events.BYTES_APPENDED_END_FRAGMENT, onBytesAppended, instance); } @@ -116,13 +120,14 @@ function GapController() { } } - function _shouldCheckForGaps() { - return settings.get().streaming.jumpGaps && streamController.getActiveStreamProcessors().length > 0 && - (!playbackController.isSeeking() || streamController.hasStreamFinishedBuffering(streamController.getActiveStream())) && !playbackController.isPaused() && !streamController.getIsStreamSwitchInProgress() && - !streamController.getHasMediaOrIntialisationError(); + function _onPlaybackSeeking() { + if (jumpTimeoutHandler) { + clearTimeout(jumpTimeoutHandler); + jumpTimeoutHandler = null; + } } - function onWallclockTimeUpdated(/*e*/) { + function _onWallclockTimeUpdated(/*e*/) { if (!_shouldCheckForGaps()) { return; } @@ -140,23 +145,29 @@ function GapController() { } } - function getNextRangeStartTime(currentTime) { + function _shouldCheckForGaps() { + return settings.get().streaming.jumpGaps && streamController.getActiveStreamProcessors().length > 0 && + (!playbackController.isSeeking() || streamController.hasStreamFinishedBuffering(streamController.getActiveStream())) && !playbackController.isPaused() && !streamController.getIsStreamSwitchInProgress() && + !streamController.getHasMediaOrIntialisationError(); + } + + function getNextRangeIndex(ranges, currentTime) { try { - const ranges = videoModel.getBufferRange(); + if (!ranges || (ranges.length <= 1 && currentTime > 0)) { - return null; + return NaN; } - let nextRangeStartTime = null; + let nextRangeIndex = NaN; let j = 0; - while (!nextRangeStartTime && j < ranges.length) { + while (isNaN(nextRangeIndex) && j < ranges.length) { const rangeEnd = j > 0 ? ranges.end(j - 1) : 0; if (currentTime < ranges.start(j) && rangeEnd - currentTime < GAP_THRESHOLD) { - nextRangeStartTime = ranges.start(j); + nextRangeIndex = j; } j += 1; } - return nextRangeStartTime; + return nextRangeIndex; } catch (e) { return null; @@ -192,35 +203,49 @@ function GapController() { function jumpGap(currentTime, playbackStalled = false) { const smallGapLimit = settings.get().streaming.smallGapLimit; const jumpLargeGaps = settings.get().streaming.jumpLargeGaps; - let nextRangeStartTime = null; + const ranges = videoModel.getBufferRange(); + let nextRangeIndex; let seekToPosition = NaN; let jumpToStreamEnd = false; // Get the range just after current time position - nextRangeStartTime = getNextRangeStartTime(currentTime); + nextRangeIndex = getNextRangeIndex(ranges, currentTime); - if (nextRangeStartTime && nextRangeStartTime > 0) { - const gap = nextRangeStartTime - currentTime; + if (!isNaN(nextRangeIndex)) { + const start = ranges.start(nextRangeIndex); + const gap = start - currentTime; if (gap > 0 && (gap <= smallGapLimit || jumpLargeGaps)) { - seekToPosition = nextRangeStartTime; + seekToPosition = start; } } // Playback has stalled before period end. We seek to the end of the period const timeToStreamEnd = playbackController.getTimeToStreamEnd(); - if (isNaN(seekToPosition) && playbackStalled && isFinite(timeToStreamEnd) && !isNaN(timeToStreamEnd) && ((timeToStreamEnd < smallGapLimit) || streamController.hasStreamFinishedBuffering(streamController.getActiveStream()))) { + if (isNaN(seekToPosition) && playbackStalled && isFinite(timeToStreamEnd) && !isNaN(timeToStreamEnd) && timeToStreamEnd < smallGapLimit) { seekToPosition = parseFloat(playbackController.getStreamEndTime().toFixed(5)); jumpToStreamEnd = true; } - if (seekToPosition > 0 && lastGapJumpPosition !== seekToPosition) { + if (seekToPosition > 0 && lastGapJumpPosition !== seekToPosition && seekToPosition > currentTime && !jumpTimeoutHandler) { + const timeUntilGapEnd = seekToPosition - currentTime; + if (jumpToStreamEnd) { - logger.warn(`Jumping to end of stream because of gap from ${currentTime} to ${seekToPosition}. Gap duration: ${seekToPosition - currentTime}`); - eventBus.trigger(Events.GAP_CAUSED_SEEK_TO_PERIOD_END, { seekTime: seekToPosition }); + const nextStream = streamController.getStreamForTime(seekToPosition); + const internalSeek = nextStream && !!nextStream.getPreloaded(); + + logger.warn(`Jumping to end of stream because of gap from ${currentTime} to ${seekToPosition}. Gap duration: ${timeUntilGapEnd}`); + playbackController.seek(seekToPosition, true, internalSeek); } else { - logger.warn(`Jumping gap from ${currentTime} to ${seekToPosition}. Gap duration: ${seekToPosition - currentTime}`); - playbackController.seek(seekToPosition, true, true); + const isDynamic = playbackController.getIsDynamic(); + const start = nextRangeIndex > 0 ? ranges.end(nextRangeIndex - 1) : currentTime; + const timeToWait = !isDynamic ? 0 : timeUntilGapEnd * 1000; + + jumpTimeoutHandler = window.setTimeout(() => { + playbackController.seek(seekToPosition, true, true); + logger.warn(`Jumping gap starting at ${start} and ending at ${seekToPosition}. Jumping by: ${timeUntilGapEnd}`); + jumpTimeoutHandler = null; + }, timeToWait); } lastGapJumpPosition = seekToPosition; } From f446a1acd4d645ecebd013778b8299b5a40d9f79 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Thu, 26 Nov 2020 15:57:52 +0100 Subject: [PATCH 4/5] calcSegmentAvailabilityRangeFromTimeline to false by default --- src/core/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Settings.js b/src/core/Settings.js index 7625ce0139..b6542ddc18 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -394,7 +394,7 @@ function Settings() { scheduleWhilePaused: true, fastSwitchEnabled: false, flushBufferAtTrackSwitch: false, - calcSegmentAvailabilityRangeFromTimeline: true, + calcSegmentAvailabilityRangeFromTimeline: false, bufferPruningInterval: 10, bufferToKeep: 20, jumpGaps: true, From 42072127820991064363ea49d9a905c98750ab40 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Fri, 27 Nov 2020 08:17:50 +0100 Subject: [PATCH 5/5] Stick to "old" jumpToStreamEnd logic for now --- src/streaming/controllers/GapController.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/streaming/controllers/GapController.js b/src/streaming/controllers/GapController.js index d53814845a..884c4bf7af 100644 --- a/src/streaming/controllers/GapController.js +++ b/src/streaming/controllers/GapController.js @@ -231,11 +231,8 @@ function GapController() { const timeUntilGapEnd = seekToPosition - currentTime; if (jumpToStreamEnd) { - const nextStream = streamController.getStreamForTime(seekToPosition); - const internalSeek = nextStream && !!nextStream.getPreloaded(); - logger.warn(`Jumping to end of stream because of gap from ${currentTime} to ${seekToPosition}. Gap duration: ${timeUntilGapEnd}`); - playbackController.seek(seekToPosition, true, internalSeek); + eventBus.trigger(Events.GAP_CAUSED_SEEK_TO_PERIOD_END, { seekTime: seekToPosition }); } else { const isDynamic = playbackController.getIsDynamic(); const start = nextRangeIndex > 0 ? ranges.end(nextRangeIndex - 1) : currentTime;