You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Seek back to unbuffered start of VOD re-downloads first fragment in infinite loop (video track never appended) on 1.6.16 — appears fixed in 1.7.0-beta.2 #7932
1.6.16 (reproduces). Not reproducible on 1.7.0-beta.2 — details below.
What browser (including version) are you using?
Chrome 149 (macOS 15 / Darwin 25.5.0)
What OS (including version) are you using?
macOS 15
Test stream
https://video.deeplearning.ai/JupyterAI/C1/L0/sc-JupyterAI-C1-L0-master.m3u8 (VOD, TS segments, 3 variants, CORS enabled, EXT-X-MEDIA-SEQUENCE:1, 10.000s segments). Segments are healthy: continuous PTS across the playlist (seg1 video starts 0.067s, seg2 10.077s, seg3 20.087s per ffprobe), identical characteristics across levels.
Configuration
{startPosition: 28.8}
plus a pinned quality level at startup (hls.currentLevel = 1 inside MANIFEST_PARSED). Setting hls.startLevel instead does not change the outcome as long as the first fragment is not buffered before seeking back.
Steps to reproduce
Start playback mid-stream with a pinned level, so the beginning of the VOD is never buffered. E.g. new Hls({ startPosition: 28.8 }) and set hls.currentLevel = 1 at MANIFEST_PARSED. Buffered ranges become [20, …] — sn=1/sn=2 are (expectedly) not loaded.
After a few seconds of playback, seek back: video.currentTime = 0.
The seek to 0 loads sn=1 once, appends both tracks, and playback resumes from 0 within roughly one fragment-load time.
What actually happened?
hls.js enters an infinite re-download loop of sn=1 (one request per ~100ms tick, indefinitely — hundreds of downloads of the same segment). Each cycle, FRAG_BUFFERED fires and the audio SourceBuffer gains [0.00, 9.99], but the video SourceBuffer never gains the range — so video.buffered (track intersection) never covers position 0, the stream controller sees the hole persist, re-requests sn=1, and the element stays at readyState=1 (HAVE_METADATA) forever. No fatal error is raised.
Event log excerpt (timestamps relative to page load; seek to 0 at +6000ms):
+6180ms FRAG_LOADING sn=1 level=1
+6199ms BUFFER_APPENDED sn=1 video=[20.00,80.06] audio=[0.00,9.99] [20.00,80.07]
+6201ms FRAG_BUFFERED sn=1 level=1 [0.00-9.99]
+6207ms FRAG_LOADING sn=1 level=1 <-- 100ms later, same fragment again
+6228ms BUFFER_APPENDED sn=1 video=[20.00,80.06] audio=[0.00,9.99] [20.00,80.07]
... repeats every ~100ms indefinitely (observed >150 downloads over 15s)
The loop only breaks if an ABR level switch happens: with auto level, once the loader switched levels, sn=1 of the other level appended fine (both tracks) and playback recovered. With a manually pinned level there is no switch, so it never recovers.
Version matrix (same page, same stream, same steps)
Version
Result
1.6.16 (latest stable)
Infinite sn=1 reload loop, video track never appended, stuck at readyState=1
1.7.0-beta.2
Recovers in ~100ms: single sn=1 fetch, video [0.05,10.06] + audio [0.01,10.06] appended, then sn=2 backfills
Infinite loop loading first fragment with a hole #5609 has a similar first-fragment loop signature but was triggered by corrupted media; the segments here are healthy (continuous PTS, plays fine from 0 when sn=1 is buffered at startup).
Is a backport to 1.6.x feasible? 1.6.16 is the latest stable and the loop is easy to hit for any player that pins a quality level and restores a saved playback position (we hit it in production behind Vidstack).
What version of Hls.js are you using?
1.6.16 (reproduces). Not reproducible on 1.7.0-beta.2 — details below.
What browser (including version) are you using?
Chrome 149 (macOS 15 / Darwin 25.5.0)
What OS (including version) are you using?
macOS 15
Test stream
https://video.deeplearning.ai/JupyterAI/C1/L0/sc-JupyterAI-C1-L0-master.m3u8(VOD, TS segments, 3 variants, CORS enabled,EXT-X-MEDIA-SEQUENCE:1, 10.000s segments). Segments are healthy: continuous PTS across the playlist (seg1 video starts 0.067s, seg2 10.077s, seg3 20.087s per ffprobe), identical characteristics across levels.Configuration
plus a pinned quality level at startup (
hls.currentLevel = 1insideMANIFEST_PARSED). Settinghls.startLevelinstead does not change the outcome as long as the first fragment is not buffered before seeking back.Steps to reproduce
new Hls({ startPosition: 28.8 })and sethls.currentLevel = 1atMANIFEST_PARSED. Buffered ranges become[20, …]— sn=1/sn=2 are (expectedly) not loaded.video.currentTime = 0.Minimal self-contained repro page:
Expected behaviour
The seek to 0 loads sn=1 once, appends both tracks, and playback resumes from 0 within roughly one fragment-load time.
What actually happened?
hls.js enters an infinite re-download loop of sn=1 (one request per ~100ms tick, indefinitely — hundreds of downloads of the same segment). Each cycle,
FRAG_BUFFEREDfires and the audio SourceBuffer gains[0.00, 9.99], but the video SourceBuffer never gains the range — sovideo.buffered(track intersection) never covers position 0, the stream controller sees the hole persist, re-requests sn=1, and the element stays atreadyState=1(HAVE_METADATA) forever. No fatal error is raised.Event log excerpt (timestamps relative to page load; seek to 0 at +6000ms):
The loop only breaks if an ABR level switch happens: with auto level, once the loader switched levels, sn=1 of the other level appended fine (both tracks) and playback recovered. With a manually pinned level there is no switch, so it never recovers.
Version matrix (same page, same stream, same steps)
[0.05,10.06]+ audio[0.01,10.06]appended, then sn=2 backfillsNotes / possibly related
Ask
Checklist