Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,15 @@ export class PlaylistController extends videojs.EventTarget {
const offset = media.start.timeOffset;

if (offset < 0) {
startPoint = Math.max(seekableEnd + offset, seekable.start(0));
// Per HLS spec, negative TIME-OFFSET is from the end of the last Media Segment.
// For live streams, seekableEnd already has liveEdgeDelay subtracted,
// so we need to add it back to get the actual playlist end.
// Clamp to seekableEnd to avoid seeking into the unsafe live edge zone.
const main = this.mainPlaylistLoader_.main;
const liveEdgeDelay = Vhs.Playlist.liveEdgeDelay(main, media);
const actualPlaylistEnd = seekableEnd + liveEdgeDelay;

startPoint = Math.max(Math.min(actualPlaylistEnd + offset, seekableEnd), seekable.start(0));
} else {
startPoint = Math.min(seekableEnd, offset);
}
Expand Down
15 changes: 15 additions & 0 deletions test/manifests/startLiveNegative.m3u8
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#EXTM3U
#EXT-X-START:TIME-OFFSET=-30,PRECISE=YES
#EXT-X-TARGETDURATION:10
#EXTINF:10,
media-00001.ts
#EXTINF:10,
media-00002.ts
#EXTINF:10,
media-00003.ts
#EXTINF:10,
media-00004.ts
#EXTINF:10,
media-00005.ts
#EXTINF:10,
media-00006.ts
24 changes: 24 additions & 0 deletions test/videojs-http-streaming.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,30 @@ QUnit.test('seeks to negative offset point', function(assert) {
assert.strictEqual(currentTime, 35, 'seeked to negative offset');
});

QUnit.test('seeks to negative offset on live stream from actual playlist end', function(assert) {
let currentTime = 0;

this.player.autoplay(true);
this.player.on('seeking', () => {
currentTime = this.player.currentTime();
});
this.player.src({
src: 'startLiveNegative.m3u8',
type: 'application/vnd.apple.mpegurl'
});

this.clock.tick(1);

openMediaSource(this.player, this.clock);
this.player.tech_.trigger('play');
this.standardXHRResponse(this.requests.shift());
this.clock.tick(1);

// 6 segments * 10s = 60s total, liveEdgeDelay = 30, TIME-OFFSET = -30
// Per HLS spec, offset is from playlist end (60s), so startPoint = 60 - 30 = 30
assert.strictEqual(currentTime, 30, 'seeked to negative offset from actual playlist end on live stream');
});

QUnit.test(
'duration is set when the source opens after the playlist is loaded',
function(assert) {
Expand Down
Loading