From b7d2305a60c0a27630ed66981a9f22ddcf811cc8 Mon Sep 17 00:00:00 2001 From: Diogo Azevedo Date: Wed, 19 Jul 2023 20:56:41 +0200 Subject: [PATCH] fix(HLS): Consider skipped segments to calculate next media sequence (#5414) The `_HLS_msn` directive must be the next media sequence number to block playlist reload. The actual logic uses the `EXT-X-MEDIA-SEQUENCE` plus the number of segments. However, a playlist may skip segments, so it should also consider `EXT-X-SKIP:SKIPPED-SEGMENTS`. --- lib/hls/hls_parser.js | 16 ++++++++++------ test/hls/hls_live_unit.js | 29 ++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 9b5bc923e8..e50c459271 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -349,11 +349,6 @@ shaka.hls.HlsParser = class { const manifestUri = streamInfo.absoluteMediaPlaylistUri; const uriObj = new goog.Uri(manifestUri); const queryData = new goog.Uri.QueryData(); - if (streamInfo.canSkipSegments) { - // Enable delta updates. This will replace older segments with - // 'EXT-X-SKIP' tag in the media playlist. - queryData.add('_HLS_skip', 'YES'); - } if (streamInfo.canBlockReload) { if (streamInfo.nextMediaSequence >= 0) { // Indicates that the server must hold the request until a Playlist @@ -367,6 +362,11 @@ shaka.hls.HlsParser = class { queryData.add('_HLS_part', String(streamInfo.nextPart)); } } + if (streamInfo.canSkipSegments) { + // Enable delta updates. This will replace older segments with + // 'EXT-X-SKIP' tag in the media playlist. + queryData.add('_HLS_skip', 'YES'); + } if (queryData.getCount()) { uriObj.setQueryData(queryData); } @@ -418,9 +418,13 @@ shaka.hls.HlsParser = class { if (segments.length) { const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber( playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0); + const skipTag = shaka.hls.Utils.getFirstTagWithName( + playlist.tags, 'EXT-X-SKIP'); + const skippedSegments = + skipTag ? Number(skipTag.getAttributeValue('SKIPPED-SEGMENTS')) : 0; const {nextMediaSequence, nextPart} = this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments); - streamInfo.nextMediaSequence = nextMediaSequence; + streamInfo.nextMediaSequence = nextMediaSequence + skippedSegments; streamInfo.nextPart = nextPart; const playlistStartTime = mediaSequenceToStartTime.get( mediaSequenceNumber); diff --git a/test/hls/hls_live_unit.js b/test/hls/hls_live_unit.js index de6279371c..bd4b94285f 100644 --- a/test/hls/hls_live_unit.js +++ b/test/hls/hls_live_unit.js @@ -996,12 +996,12 @@ describe('HlsParser live', () => { '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', '#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,CAN-SKIP-UNTIL=60.0,\n', '#EXTINF:2,\n', - 'main.mp4\n', + 'main0.mp4\n', '#EXTINF:2,\n', - 'main2.mp4\n', + 'main1.mp4\n', ].join(''); - const mediaWithSkippedSegments = [ + const mediaWithSkippedSegments1 = [ '#EXTM3U\n', '#EXT-X-TARGETDURATION:5\n', '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', @@ -1010,22 +1010,41 @@ describe('HlsParser live', () => { '#EXT-X-SKIP:SKIPPED-SEGMENTS=1\n', '#EXTINF:2,\n', 'main2.mp4\n', + ].join(''); + + const mediaWithSkippedSegments2 = [ + '#EXTM3U\n', + '#EXT-X-TARGETDURATION:5\n', + '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', + '#EXT-X-MEDIA-SEQUENCE:2\n', + '#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,CAN-SKIP-UNTIL=60.0,\n', + '#EXT-X-SKIP:SKIPPED-SEGMENTS=1\n', '#EXTINF:2,\n', 'main3.mp4\n', ].join(''); fakeNetEngine.setResponseText( - 'test:/video?_HLS_skip=YES&_HLS_msn=2', mediaWithSkippedSegments); + 'test:/video?_HLS_msn=2&_HLS_skip=YES', mediaWithSkippedSegments1); + + fakeNetEngine.setResponseText( + 'test:/video?_HLS_msn=3&_HLS_skip=YES', mediaWithSkippedSegments2); playerInterface.isLowLatencyMode = () => true; await testInitialManifest(master, mediaWithDeltaUpdates); fakeNetEngine.request.calls.reset(); + await delayForUpdatePeriod(); + fakeNetEngine.expectRequest( + 'test:/video?_HLS_msn=2&_HLS_skip=YES', + shaka.net.NetworkingEngine.RequestType.MANIFEST, + {type: + shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_PLAYLIST}); + await delayForUpdatePeriod(); fakeNetEngine.expectRequest( - 'test:/video?_HLS_skip=YES&_HLS_msn=2', + 'test:/video?_HLS_msn=3&_HLS_skip=YES', shaka.net.NetworkingEngine.RequestType.MANIFEST, {type: shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_PLAYLIST});