Skip to content

Delta Playlist Update Discontinuity Regression Issue #7282

@seungkwan-yang

Description

@seungkwan-yang

What version of Hls.js are you using?

v1.6.2

What browser (including version) are you using?

Chrome 137.0.7151.41 (Official Build) (arm64)

What OS (including version) are you using?

Apple M1 Pro MacOS 15.5 (24F74)

Test stream

No response

Configuration

{
      "debug": true,   
      "enableWorker": true,    
      "lowLatencyMode": true,    
      "backBufferLength": 90
}

Additional player setup steps

No response

Checklist

Steps to reproduce

  1. Start the stream.
  2. Confirm normal playback.
  3. Stop the stream and then restart it.
  4. A discontinuity occurs.
  5. Observe that the full chunklist.m3u8 contains #EXT-X-DISCONTINUITY, but it's missing from the chunklist.m3u8 in the delta playlist update.
  6. Refresh the browser.
  7. HLS.js playback stops.

Expected behaviour

Playback should be normal.

What actually happened?

  • Playback did not occur, or it played briefly and then stopped.

  • This issue occurs when the full chunklist contains #EXT-X-DISCONTINUITY, but the delta playlist update chunklist does not.

  • For example:

    1. Download the initial full chunklist
    #EXTM3U
    #EXT-X-VERSION:10
    #EXT-X-INDEPENDENT-SEGMENTS
    #EXT-X-TARGETDURATION:3
    #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.126000,CAN-SKIP-UNTIL=20.0
    #EXT-X-PART-INF:PART-TARGET=1.000000
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-DISCONTINUITY-SEQUENCE:0
    #EXT-X-DATERANGE:ID="nmss-daterange",START-DATE="2025-05-22T07:59:38.809Z"
    
    #EXT-X-MAP:URI="720p_0_0.m4s"
    #EXTINF:2.500000,
    720p_0.m4v
    #EXTINF:2.500000,
    720p_1.m4v
    #EXTINF:2.500000,
    720p_2.m4v
    #EXTINF:2.184667,
    #EXT-X-DISCONTINUITY
    #EXT-X-MAP:URI="720p_0_1.m4s
    #EXTINF:2.500000,
    720p_3.m4v 
    #EXTINF:2.500000,
    720p_4.m4v
    #EXTINF:2.500000,
    720p_5.m4v
    #EXTINF:2.500000,
    720p_6.m4v
    #EXTINF:2.500000,
    720p_7.m4v
    #EXTINF:2.500000,
    720p_8.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_9_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_9_1.m4v"
    #EXT-X-PART:DURATION=0.500000,URI="720p_9_2.m4v"
    #EXTINF:2.500000,
    720p_9.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_10_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_10_1.m4v"
    #EXT-X-PART:DURATION=0.500000,URI="720p_10_2.m4v"
    #EXTINF:2.500000,
    720p_10.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_11_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_11_1.m4v"
    #EXT-X-PART:DURATION=0.500000,URI="720p_11_2.m4v"
    #EXTINF:2.500000,
    720p_11.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
    #EXT-X-PRELOAD-HINT:TYPE=PART,URI="720p_12_2.m4v"
    1. Download the subsequent delta playlist update chunklist
      #EXTM3U
    #EXT-X-VERSION:10
    #EXT-X-INDEPENDENT-SEGMENTS
    #EXT-X-TARGETDURATION:3
    #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.126000,CAN-SKIP-UNTIL=20.0
    #EXT-X-PART-INF:PART-TARGET=1.000000
    #EXT-X-MEDIA-SEQUENCE:0
    #EXT-X-DISCONTINUITY-SEQUENCE:0
    #EXT-X-DATERANGE:ID="nmss-daterange",START-DATE="2025-05-22T07:59:38.809Z"
    #EXT-X-SKIP:SKIPPED-SEGMENTS=4
    
    #EXT-X-MAP:URI="720p_0_1.m4s"
    720p_4.m4v
    #EXTINF:2.500000,
    720p_5.m4v
    #EXTINF:2.500000,
    720p_6.m4v
    #EXTINF:2.500000,
    720p_7.m4v
    #EXTINF:2.500000,
    720p_8.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_9_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_9_1.m4v"
    #EXT-X-PART:DURATION=0.500000,URI="720p_9_2.m4v"
    #EXTINF:2.500000,
    720p_9.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_10_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_10_1.m4v"
    #EXT-X-PART:DURATION=0.500000,URI="720p_10_2.m4v"
    #EXTINF:2.500000,
    720p_10.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_11_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_11_1.m4v"
    #EXT-X-PART:DURATION=0.500000,URI="720p_11_2.m4v"
    #EXTINF:2.500000,
    720p_11.m4v
    #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v",INDEPENDENT=YES
    #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
    #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
    #EXTINF:2.500000,
    720p_12.m4v
    #EXT-X-PRELOAD-HINT:TYPE=PART,URI="720p_13_0.m4
    1. Until #EXT-X-DISCONTINUITY is removed from the full chunklist.m3u8, errors will continue to occur when loading the delta playlist update chunklist.m3u8.
  • For reference, If you modify newFragments[0].cc; to newFragments[newFragIndex].cc; at the following location, playback works, but I'm unsure if this is the correct solution:

    newDetails.startCC =
    getFragmentWithSN(oldDetails, newDetails.startSN - 1)?.cc ??
    newFragments[0].cc;
    newDetails.endCC = newFragments[newFragments.length - 1].cc;

Console output

Using Hls.js config: {debug: true, enableWorker: true, lowLatencyMode: true, backBufferLength: 90}
logger.ts:102 [log] > Debug logs enabled for "Hls instance" in hls.js version 1.6.3-0.canary.11238
hls.ts:579 [log] > stopLoad
hls.ts:513 [log] > loadSource:https://mydomain.com/test/720p_playlist.m3u8
stream-controller.ts:611 [log] > [stream-controller]: Trigger BUFFER_RESET
hls.ts:466 [log] > attachMedia
buffer-controller.ts:320 [log] > [buffer-controller]: created media source: MediaSource
buffer-controller.ts:1513 [log] > [buffer-controller]: Media source opened
buffer-controller.ts:1342 [log] > [buffer-controller]: checkPendingTracks (pending: 0 codec events expected: 0) {}
level-controller.ts:370 [log] > [level-controller]: manifest loaded, 1 level(s) found, first bitrate: 3192000
abr-controller.ts:62 [log] > [abr]: setting initial bwe to 3192000
buffer-controller.ts:266 [log] > [buffer-controller]: 1 bufferCodec event(s) expected.
playlist-loader.ts:405 [log] > auto startLoad with configured startPosition -1
hls.ts:556 [log] > startLoad(-1)
level-controller.ts:468 [log] > [level-controller]: Switching to level 0 (720p SDR avc1,mp4a @3192000) from level -1
level-controller.ts:673 [log] > [level-controller]: Loading level index 0 https://mydomain.com/test/720p/chunklist.m3u8
base-stream-controller.ts:2106 [log] > [stream-controller]: STOPPED->IDLE
base-stream-controller.ts:2106 [log] > [subtitle-stream-controller]: STOPPED->IDLE
10Unchecked runtime.lastError: The message port closed before a response was received.
content.js:9 Current exchange rate: 1379.01
base-playlist-controller.ts:216 [log] > [level-controller]: live playlist 0 REFRESHED 594-1
level-controller.ts:673 [log] > [level-controller]: Loading level index 0 at sn 594 part 2 https://mydomain.com/test/720p/chunklist.m3u8?_HLS_msn=594&_HLS_part=2&_HLS_skip=YES
base-stream-controller.ts:2106 [log] > [stream-controller]: IDLE->WAITING_LEVEL
stream-controller.ts:671 [log] > [stream-controller]: Level 0 loaded [0,593][part-594-1], cc [0, 5] duration:1481.4523330000002
base-stream-controller.ts:1678 [log] > [stream-controller]: Live playlist sliding: 0.00 start-sn: na->0 fragments: 594
base-stream-controller.ts:1733 [log] > [stream-controller]: Setting startPosition to -1 to start at live edge 1478.330333
interstitials-controller.ts:1058 [log] > [interstitials]: setSchedulePosition 0, undefined
interstitials-controller.ts:1883 [log] > [interstitials]: buffered to boundary [primary: 0.00-Infinity]
interstitials-controller.ts:1296 [log] > [interstitials]: resuming [primary: 0.00-Infinity]
buffer-controller.ts:1319 [log] > [buffer-controller]: Updating MediaSource duration to 1481.452
base-stream-controller.ts:2106 [log] > [stream-controller]: WAITING_LEVEL->IDLE
base-stream-controller.ts:1298 [log] > [stream-controller]: LL-Part loading ON for initial live fragment
base-stream-controller.ts:1309 [log] > [stream-controller]: Setting startPosition to 1478.330333 to match start frag at live edge. mainStart: 1478.330333 liveSyncPosition: 1478.3373330000002 frag.start: 1476.9523330000002
base-stream-controller.ts:912 [log] > [stream-controller]: Loading main sn: initSegment of level 0 (frag:[251.619-251.619]) cc: 5 [0-593], target: 251.619
base-stream-controller.ts:2106 [log] > [stream-controller]: IDLE->FRAG_LOADING
timeline-chart.ts:754  Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. See: https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-will-read-frequently
drawLineX @ timeline-chart.ts:754
drawCurrentTime @ timeline-chart.ts:728
afterRender @ timeline-chart.ts:73
notify @ Chart.js:8032
onComplete @ Chart.js:9785
render @ Chart.js:9811
update @ Chart.js:9681
update @ timeline-chart.ts:252
(anonymous) @ timeline-chart.ts:263
requestAnimationFrame
updateOnRepaint @ timeline-chart.ts:263
updateLevelOrTrack @ timeline-chart.ts:464
(anonymous) @ main.js:1682
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
onLevelLoaded @ stream-controller.ts:711
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
handlePlaylistLoaded @ playlist-loader.ts:747
handleTrackOrLevelPlaylist @ playlist-loader.ts:563
onSuccess @ playlist-loader.ts:360
readystatechange @ xhr-loader.ts:232
XMLHttpRequest.send
openAndSendXhr @ xhr-loader.ts:166
loadInternal @ xhr-loader.ts:125
load @ xhr-loader.ts:83
load @ playlist-loader.ts:393
onLevelLoading @ playlist-loader.ts:171
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
loadingPlaylist @ level-controller.ts:681
scheduleLoading @ base-playlist-controller.ts:335
loadPlaylist @ level-controller.ts:659
set @ level-controller.ts:517
set @ level-controller.ts:700
set @ hls.ts:776
startLoad @ stream-controller.ts:161
startLoad @ hls.ts:565
checkAutostartLoad @ playlist-loader.ts:408
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
handleMasterPlaylist @ playlist-loader.ts:486
onSuccess @ playlist-loader.ts:368
readystatechange @ xhr-loader.ts:232
XMLHttpRequest.send
openAndSendXhr @ xhr-loader.ts:166
loadInternal @ xhr-loader.ts:125
load @ xhr-loader.ts:83
load @ playlist-loader.ts:393
onManifestLoading @ playlist-loader.ts:158
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
loadSource @ hls.ts:524
loadSelectedStream @ main.js:383
(anonymous) @ main.js:225
j @ jquery.min.js:2
fireWith @ jquery.min.js:2
ready @ jquery.min.js:2
I @ jquery.min.js:2
base-stream-controller.ts:2106 [log] > [stream-controller]: FRAG_LOADING->IDLE
base-stream-controller.ts:842 [log] > [stream-controller]: Loading main sn: 593 part: 0 (6/10) of level 0 (part:[1476.952-1477.952]INDEPENDENT=YES) cc: 5 [0-593], target: 1478.33
base-stream-controller.ts:2106 [log] > [stream-controller]: IDLE->FRAG_LOADING
[NEW] Explain Console errors by using Copilot in Edge: click
         
         to explain an error. 
        Learn more
        Don't show again
transmuxer-interface.ts:88 [log] > injecting Web Worker for "main"
transmuxer-interface.ts:238 [log] > [transmuxer-interface]: Starting new transmux session for main sn: 593 part: 0 level: 0 id: 1
        discontinuity: true
        trackSwitch: true
        contiguous: false
        accurateTimeOffset: false
        timeOffset: 1476.9523330000002
        initSegmentChange: true
d47bde4a-d81a-4259-9cf5-321a3992f85b:1168 [log] > Debug logs enabled for "main" in hls.js version 1.6.3-0.canary.11238
base-stream-controller.ts:2106 [log] > [stream-controller]: FRAG_LOADING->PARSING
stream-controller.ts:1505 [log] > [stream-controller]: Init audiovideo buffer, container:video/mp4, codecs[level/parsed]=[avc1.640028,mp4a.40.2/mp4a.40.2,avc1.640028]
buffer-controller.ts:590 [log] > [buffer-controller]: BUFFER_CODECS: "audiovideo" (current SB count 0)
buffer-controller.ts:1342 [log] > [buffer-controller]: checkPendingTracks (pending: 1 codec events expected: 1) {"audiovideo":{"listeners":[],"codec":"mp4a.40.2,avc1.640028","container":"video/mp4","id":"main"}}
buffer-controller.ts:1411 [log] > [buffer-controller]: creating sourceBuffer(video/mp4;codecs=mp4a.40.2,avc1.640028) {"listeners":[],"codec":"mp4a.40.2,avc1.640028","container":"video/mp4","id":"main"}
buffer-controller.ts:1380 [log] > [buffer-controller]: SourceBuffers created. Running queue: 
video: (none) 
audio: (none) 
audiovideo: (SourceBuffer) }
audio-stream-controller.ts:153 [log] > [audio-stream-controller]: InitPTS for cc: 5 found from main: 2879.0159999877214/48000
transmuxer-interface.ts:396 [log] > [transmuxer.ts]: Flushed main sn: 593 part: 2 of level 0
base-stream-controller.ts:2106 [log] > [stream-controller]: PARSING->PARSED
base-stream-controller.ts:2053 [log] > [stream-controller]: Parsed main sn: 593 part: 2 of level 0 (part:[1478.952-1479.452]INDEPENDENT=NO)
base-stream-controller.ts:721 [log] > [stream-controller]: Buffered main sn: 593 part: 2 of level 0 (part:[1478.952-1479.452]INDEPENDENT=NO > buffer:[1476.952-1479.448])
base-stream-controller.ts:2106 [log] > [stream-controller]: PARSED->IDLE
stream-controller.ts:1162 [log] > [stream-controller]: seek to target start position 1478.330333 from current time 0 buffer start 1476.952312
base-stream-controller.ts:842 [log] > [stream-controller]: Loading main sn: 594 part: 0 (9/10) of level 0 (part:[1479.452-1480.452]INDEPENDENT=YES) cc: 5 [0-593], target: 1479.452
base-stream-controller.ts:2106 [log] > [stream-controller]: IDLE->FRAG_LOADING
base-stream-controller.ts:348 [log] > [stream-controller]: media seeking to 1478.330, state: FRAG_LOADING
base-stream-controller.ts:348 [log] > [audio-stream-controller]: media seeking to 1478.330, state: STOPPED
base-stream-controller.ts:348 [log] > [subtitle-stream-controller]: media seeking to 1478.330, state: IDLE
stream-controller.ts:583 [log] > [stream-controller]: Media seeked to 1478.330
base-stream-controller.ts:2106 [log] > [stream-controller]: FRAG_LOADING->PARSING
transmuxer-interface.ts:396 [log] > [transmuxer.ts]: Flushed main sn: 594 part: 1 of level 0
base-stream-controller.ts:2106 [log] > [stream-controller]: PARSING->PARSED
base-stream-controller.ts:2053 [log] > [stream-controller]: Parsed main sn: 594 part: 1 of level 0 (part:[1480.452-1481.452]INDEPENDENT=NO)
base-stream-controller.ts:721 [log] > [stream-controller]: Buffered main sn: 594 part: 1 of level 0 (part:[1480.452-1481.452]INDEPENDENT=NO > buffer:[1476.952-1481.475])
base-stream-controller.ts:2106 [log] > [stream-controller]: PARSED->IDLE
level-helper.ts:178  Uncaught TypeError: Cannot read properties of null (reading 'cc')
    at level-helper.ts:178:27
    at mapFragmentIntersection (level-helper.ts:414:7)
    at mergeDetails (level-helper.ts:167:3)
    at LevelController.playlistLoaded (base-playlist-controller.ts:177:9)
    at LevelController.onLevelLoaded (level-controller.ts:649:12)
    at EventEmitter.emit (index.js:203:33)
    at Hls.emit (hls.ts:386:26)
    at Hls.trigger (hls.ts:394:19)
    at PlaylistLoader.handlePlaylistLoaded (playlist-loader.ts:747:13)
    at PlaylistLoader.handleTrackOrLevelPlaylist (playlist-loader.ts:563:10)
(anonymous) @ level-helper.ts:178
mapFragmentIntersection @ level-helper.ts:414
mergeDetails @ level-helper.ts:167
playlistLoaded @ base-playlist-controller.ts:177
onLevelLoaded @ level-controller.ts:649
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
handlePlaylistLoaded @ playlist-loader.ts:747
handleTrackOrLevelPlaylist @ playlist-loader.ts:563
onSuccess @ playlist-loader.ts:360
readystatechange @ xhr-loader.ts:232
XMLHttpRequest.send
openAndSendXhr @ xhr-loader.ts:166
loadInternal @ xhr-loader.ts:125
load @ xhr-loader.ts:83
load @ playlist-loader.ts:393
onLevelLoading @ playlist-loader.ts:171
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
loadingPlaylist @ level-controller.ts:681
playlistLoaded @ base-playlist-controller.ts:306
onLevelLoaded @ level-controller.ts:649
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
handlePlaylistLoaded @ playlist-loader.ts:747
handleTrackOrLevelPlaylist @ playlist-loader.ts:563
onSuccess @ playlist-loader.ts:360
readystatechange @ xhr-loader.ts:232
XMLHttpRequest.send
openAndSendXhr @ xhr-loader.ts:166
loadInternal @ xhr-loader.ts:125
load @ xhr-loader.ts:83
load @ playlist-loader.ts:393
onLevelLoading @ playlist-loader.ts:171
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
loadingPlaylist @ level-controller.ts:681
scheduleLoading @ base-playlist-controller.ts:335
loadPlaylist @ level-controller.ts:659
set @ level-controller.ts:517
set @ level-controller.ts:700
set @ hls.ts:776
startLoad @ stream-controller.ts:161
startLoad @ hls.ts:565
checkAutostartLoad @ playlist-loader.ts:408
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
handleMasterPlaylist @ playlist-loader.ts:486
onSuccess @ playlist-loader.ts:368
readystatechange @ xhr-loader.ts:232
XMLHttpRequest.send
openAndSendXhr @ xhr-loader.ts:166
loadInternal @ xhr-loader.ts:125
load @ xhr-loader.ts:83
load @ playlist-loader.ts:393
onManifestLoading @ playlist-loader.ts:158
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
loadSource @ hls.ts:524
loadSelectedStream @ main.js:383
(anonymous) @ main.js:225
j @ jquery.min.js:2
fireWith @ jquery.min.js:2
ready @ jquery.min.js:2
I @ jquery.min.js:2
gap-controller.ts:514  [warn] > [gap-controller]: Playback stalling at @1481.406521 due to low buffer ({"len":0.068456999999853,"start":1476.952312,"end":1481.474978,"buffered":[{"start":1476.952312,"end":1481.474978}],"bufferedIndex":0})
_reportStall @ gap-controller.ts:514
poll @ gap-controller.ts:316
tick @ gap-controller.ts:146
setInterval
setInterval @ task-loop.ts:71
onMediaAttached @ gap-controller.ts:83
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
BufferController._this2._onMediaSourceOpen @ buffer-controller.ts:1522
latency-controller.ts:205  [warn] > [latency-controller]: Stall detected, adjusting target latency
onError @ latency-controller.ts:205
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
_reportStall @ gap-controller.ts:515
poll @ gap-controller.ts:316
tick @ gap-controller.ts:146
setInterval
setInterval @ task-loop.ts:71
onMediaAttached @ gap-controller.ts:83
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
BufferController._this2._onMediaSourceOpen @ buffer-controller.ts:1522
main.js:745  Error event: {type: 'mediaError', details: 'bufferStalledError', fatal: false, error: Error: Playback stalling at @1481.406521 due to low buffer ({"len":0.068456999999853,"start":1476.9…, buffer: 0.068456999999853, …}
(anonymous) @ main.js:745
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
_reportStall @ gap-controller.ts:515
poll @ gap-controller.ts:316
tick @ gap-controller.ts:146
setInterval
setInterval @ task-loop.ts:71
onMediaAttached @ gap-controller.ts:83
emit @ index.js:203
emit @ hls.ts:386
trigger @ hls.ts:394
BufferController._this2._onMediaSourceOpen @ buffer-controller.ts:1522

Chrome media internals output

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    To do

    Status

    Top priorities

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions