diff --git a/src/streaming/controllers/BufferController.js b/src/streaming/controllers/BufferController.js index 06af9f9447..e149b98e34 100644 --- a/src/streaming/controllers/BufferController.js +++ b/src/streaming/controllers/BufferController.js @@ -587,7 +587,7 @@ function BufferController(config) { return clearRanges; } - // if no target time is provided we clear everyhing + // if no target time is provided we clear everything if ((!seekTime && seekTime !== 0) || isNaN(seekTime)) { clearRanges.push({ start: ranges.start(0), @@ -597,7 +597,6 @@ function BufferController(config) { // otherwise we need to calculate the correct pruning range else { - const behindPruningRange = _getRangeBehindForPruning(seekTime, ranges); const aheadPruningRange = _getRangeAheadForPruning(seekTime, ranges); @@ -926,6 +925,7 @@ function BufferController(config) { function clearBuffers(ranges) { return new Promise((resolve, reject) => { if (!ranges || !sourceBufferSink || ranges.length === 0) { + _updateBufferLevel(); resolve(); return; } diff --git a/test/functional/adapter/DashJsAdapter.js b/test/functional/adapter/DashJsAdapter.js index 929ab7ba4c..400b1dfd21 100644 --- a/test/functional/adapter/DashJsAdapter.js +++ b/test/functional/adapter/DashJsAdapter.js @@ -595,6 +595,33 @@ class DashJsAdapter { delayPollInterval = setInterval(_checkDelay, 100); }) } + + async reachedTargetForwardBuffer(timeoutValue, targetBuffer, tolerance) { + return new Promise((resolve) => { + let timeout = null; + let delayPollInterval = null; + + const _onComplete = (res) => { + clearTimeout(timeout); + clearInterval(delayPollInterval); + delayPollInterval = null; + timeout = null; + resolve(res); + } + const _onTimeout = () => { + _onComplete(false); + } + + const _checkBuffer = () => { + const buffer = this.getBufferLengthByType(); + if (buffer >= targetBuffer) { + _onComplete(true); + } + }; + timeout = setTimeout(_onTimeout, timeoutValue); + delayPollInterval = setInterval(_checkBuffer, 100); + }) + } } export default DashJsAdapter; diff --git a/test/functional/src/Constants.js b/test/functional/src/Constants.js index ecb42406c7..806686d065 100644 --- a/test/functional/src/Constants.js +++ b/test/functional/src/Constants.js @@ -32,6 +32,7 @@ const TEST_TIMEOUT_THRESHOLDS = { EVENT_WAITING_TIME: 10000, BUFFER_CLEANUP: 45000, TARGET_DELAY_REACHED: 20000, + TARGET_BUFFER_REACHED: 20000, ENDED_EVENT_OFFSET: 2000, IS_FINISHED_OFFSET_TO_DURATION: 5000 }; @@ -91,6 +92,10 @@ const TEST_INPUTS = { ATTACH_WITH_POSIX: { DELAY: 30, TOLERANCE: 5 + }, + BUFFER_TO_KEEP_SEEK: { + TOLERANCE: 1, + SEEK_OFFSET: 40 } }; @@ -134,6 +139,7 @@ TESTCASES.AUDIO.SWITCH = TESTCASES.CATEGORIES.AUDIO + 'switch-audio'; TESTCASES.BUFFER.CLEANUP = TESTCASES.CATEGORIES.BUFFER + 'buffer-cleanup'; TESTCASES.BUFFER.INITIAL_TARGET = TESTCASES.CATEGORIES.BUFFER + 'initial-buffer-target'; TESTCASES.BUFFER.TARGET = TESTCASES.CATEGORIES.BUFFER + 'buffer-target'; +TESTCASES.BUFFER.TO_KEEP_SEEK = TESTCASES.CATEGORIES.BUFFER + 'buffer-to-keep-seek'; TESTCASES.FEATURE_SUPPORT.EMSG_TRIGGERED = TESTCASES.CATEGORIES.FEATURE_SUPPORT + 'emsg-triggered'; TESTCASES.FEATURE_SUPPORT.MPD_PATCHING = TESTCASES.CATEGORIES.FEATURE_SUPPORT + 'mpd-patching'; diff --git a/test/functional/test/buffer/buffer-to-keep-seek.js b/test/functional/test/buffer/buffer-to-keep-seek.js new file mode 100644 index 0000000000..9b8b2157c3 --- /dev/null +++ b/test/functional/test/buffer/buffer-to-keep-seek.js @@ -0,0 +1,88 @@ +/** + * This test checks if playback resumes after a seek in case bufferToKeep is larger than bufferTimeAtTopQuality + */ +import Constants from '../../src/Constants.js'; +import { + checkIsNotProgressing, + checkIsPlaying, + checkIsProgressing, + checkNoCriticalErrors, checkTimeWithinThresholdForDvrWindow, + initializeDashJsAdapter, + reachedTargetForwardBuffer +} from '../common/common.js'; + +const TESTCASE = Constants.TESTCASES.BUFFER.TO_KEEP_SEEK; + +const item = { + url: 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd', + type: 'vod', + name: 'Segment Template BBB' +} + +const mpd = item.url; +describe(`${TESTCASE} - ${item.name} - ${mpd}`, () => { + + let playerAdapter; + const TARGET_BUFFER = 10; + const BUFFER_TO_KEEP = TARGET_BUFFER + 100 + + before(() => { + const settings = { + streaming: { + buffer: { + bufferTimeDefault: TARGET_BUFFER, + bufferTimeAtTopQuality: TARGET_BUFFER, + bufferTimeAtTopQualityLongForm: TARGET_BUFFER, + longFormContentDurationThreshold: 6000, + bufferToKeep: BUFFER_TO_KEEP, + bufferPruningInterval: 10, + }, + } + } + playerAdapter = initializeDashJsAdapter(item, mpd, settings); + }) + + after(() => { + playerAdapter.destroy(); + }) + + it(`Checking playing state`, async () => { + await checkIsPlaying(playerAdapter, true); + }) + + it(`Checking progressing state`, async () => { + await checkIsProgressing(playerAdapter); + }); + + it(`Pause the playback`, async () => { + playerAdapter.pause(); + await checkIsPlaying(playerAdapter, false); + await checkIsNotProgressing(playerAdapter); + }); + + it(`Wait for forward buffer to be filled`, async () => { + await reachedTargetForwardBuffer(playerAdapter, TARGET_BUFFER, Constants.TEST_INPUTS.BUFFER_TO_KEEP_SEEK.TOLERANCE); + }); + + it(`Seek to an unbuffered range`, () => { + const bufferEnd = playerAdapter.getBufferLengthByType() + playerAdapter.getCurrentTime(); + const targetTime = Math.min(bufferEnd + Constants.TEST_INPUTS.BUFFER_TO_KEEP_SEEK.SEEK_OFFSET, playerAdapter.getDuration() - 10); + playerAdapter.seek(targetTime); + + checkTimeWithinThresholdForDvrWindow(playerAdapter, targetTime, Constants.TEST_INPUTS.GENERAL.MAXIMUM_ALLOWED_SEEK_DIFFERENCE); + }); + + it(`Resume playback`, async () => { + playerAdapter.play() + await checkIsPlaying(playerAdapter, true); + }) + + it(`Checking progressing state`, async () => { + await checkIsProgressing(playerAdapter); + }); + + it(`Expect no critical errors to be thrown`, () => { + checkNoCriticalErrors(playerAdapter); + }) +}) + diff --git a/test/functional/test/buffer/initial-buffer-target.js b/test/functional/test/buffer/initial-buffer-target.js index 9a849a9388..bbb6b190c4 100644 --- a/test/functional/test/buffer/initial-buffer-target.js +++ b/test/functional/test/buffer/initial-buffer-target.js @@ -1,4 +1,3 @@ -import DashJsAdapter from '../../adapter/DashJsAdapter.js'; import Constants from '../../src/Constants.js'; import Utils from '../../src/Utils.js'; import {expect} from 'chai' diff --git a/test/functional/test/common/common.js b/test/functional/test/common/common.js index 1045f3ab0c..fe860496ad 100644 --- a/test/functional/test/common/common.js +++ b/test/functional/test/common/common.js @@ -27,6 +27,11 @@ export async function checkForEndedEvent(playerAdapter) { expect(ended).to.be.true; } +export async function reachedTargetForwardBuffer(playerAdapter, targetBuffer, tolerance) { + const reachedBuffer = await playerAdapter.reachedTargetForwardBuffer(Constants.TEST_TIMEOUT_THRESHOLDS.TARGET_BUFFER_REACHED, targetBuffer, tolerance); + expect(reachedBuffer).to.be.true; +} + export function checkLiveDelay(playerAdapter, lowerThreshold, upperThreshold) { const liveDelay = playerAdapter.getCurrentLiveLatency(); expect(liveDelay).to.be.at.least(lowerThreshold);