From 8bef6d1a6b342dc95704d96e36882026697de5d6 Mon Sep 17 00:00:00 2001 From: Alex Barstow Date: Fri, 31 Jan 2025 16:24:08 -0500 Subject: [PATCH] clamp seekable end to start in cases where liveEdgeDelay would make it less than --- src/playlist-controller.js | 8 +--- test/playlist-controller.test.js | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/playlist-controller.js b/src/playlist-controller.js index 430e02edd..8d278bc89 100644 --- a/src/playlist-controller.js +++ b/src/playlist-controller.js @@ -1702,12 +1702,8 @@ export class PlaylistController extends videojs.EventTarget { const liveEdgeDelay = Vhs.Playlist.liveEdgeDelay(this.mainPlaylistLoader_.main, media); - // Make sure our seekable end is not negative - const calculatedEnd = Math.max(0, end - liveEdgeDelay); - - if (calculatedEnd < start) { - return null; - } + // Make sure our seekable end is not less than the seekable start + const calculatedEnd = Math.max(start, end - liveEdgeDelay); return createTimeRanges([[start, calculatedEnd]]); } diff --git a/test/playlist-controller.test.js b/test/playlist-controller.test.js index c62813039..4c4d256ba 100644 --- a/test/playlist-controller.test.js +++ b/test/playlist-controller.test.js @@ -2633,6 +2633,70 @@ QUnit.test( } ); +QUnit.test( + 'getSeekableRange_ returns a range with an end that is never less than the start', + function(assert) { + const pc = this.playlistController; + + const fakeMedia = {}; + const fakePlaylistLoader = { + media() { + return fakeMedia; + } + }; + + // Ensure mainPlaylistLoader_.main is defined (needed by liveEdgeDelay) + pc.mainPlaylistLoader_ = { main: {} }; + + const originalLiveEdgeDelay = Vhs.Playlist.liveEdgeDelay; + + // --- Scenario 1: liveEdgeDelay = 0 --- + // With a reliable sync info of start=10 and end=15, if liveEdgeDelay is 0 then: + // calculatedEnd = Math.max(10, 15 - 0) = 15 + // Expected seekable range: [10,15] + Vhs.Playlist.liveEdgeDelay = function(main, media) { + return 0; + }; + + pc.syncController_.getMediaSequenceSync = function(type) { + if (type === 'main') { + return { + isReliable: true, + start: 10, + end: 15 + }; + } + return null; + }; + + let seekable = pc.getSeekableRange_(fakePlaylistLoader, 'main'); + + timeRangesEqual( + seekable, + createTimeRanges([[10, 15]]), + 'With liveEdgeDelay 0, seekable range is [10,15]' + ); + + // --- Scenario 2: liveEdgeDelay large enough to force clamping --- + // With the same sync info (start=10, end=15), if liveEdgeDelay = 10 then: + // calculatedEnd = Math.max(10, 15 - 10) = Math.max(10, 5) = 10 + // Expected seekable range: [10,10] (the end is clamped to start) + Vhs.Playlist.liveEdgeDelay = function(main, media) { + return 10; + }; + + seekable = pc.getSeekableRange_(fakePlaylistLoader, 'main'); + + timeRangesEqual( + seekable, + createTimeRanges([[10, 10]]), + 'When liveEdgeDelay forces a negative delta, seekable range is clamped to [10,10]' + ); + + Vhs.Playlist.liveEdgeDelay = originalLiveEdgeDelay; + } +); + QUnit.test( 'syncInfoUpdate triggers seekablechanged when seekable is updated', function(assert) {