From 090554b9ed284e10e6de4305aa5ec7797ff8da3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Mon, 12 Jun 2023 12:08:02 +0200 Subject: [PATCH] feat(HLS): Add support to blocking playlist reload by adding the CAN-BLOCK-RELOAD=YES (#5279) --- lib/hls/hls_parser.js | 32 ++++++++++++++++++++++---------- test/hls/hls_live_unit.js | 34 ++++++++++------------------------ 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index eb9aa69b9f..bd38cb55f9 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -349,16 +349,18 @@ shaka.hls.HlsParser = class { // 'EXT-X-SKIP' tag in the media playlist. queryData.add('_HLS_skip', 'YES'); } - if (streamInfo.nextMediaSequence >= 0) { - // Indicates that the server must hold the request until a Playlist - // contains a Media Segment with Media Sequence - queryData.add('_HLS_msn', String(streamInfo.nextMediaSequence)); - } - if (streamInfo.nextPart >= 0) { - // Indicates, in combination with _HLS_msn, that the server must hold - // the request until a Playlist contains Partial Segment N of Media - // Sequence Number M or later. - queryData.add('_HLS_part', String(streamInfo.nextPart)); + if (streamInfo.canBlockReload) { + if (streamInfo.nextMediaSequence >= 0) { + // Indicates that the server must hold the request until a Playlist + // contains a Media Segment with Media Sequence + queryData.add('_HLS_msn', String(streamInfo.nextMediaSequence)); + } + if (streamInfo.nextPart >= 0) { + // Indicates, in combination with _HLS_msn, that the server must hold + // the request until a Playlist contains Partial Segment N of Media + // Sequence Number M or later. + queryData.add('_HLS_part', String(streamInfo.nextPart)); + } } if (queryData.getCount()) { uriObj.setQueryData(queryData); @@ -1960,6 +1962,7 @@ shaka.hls.HlsParser = class { maxTimestamp: 0, mediaSequenceToStartTime: new Map(), canSkipSegments: false, + canBlockReload: false, hasEndList: false, firstSequenceNumber: -1, nextMediaSequence: -1, @@ -2007,6 +2010,7 @@ shaka.hls.HlsParser = class { streamInfo.minTimestamp = realStreamInfo.minTimestamp; streamInfo.maxTimestamp = realStreamInfo.maxTimestamp; streamInfo.canSkipSegments = realStreamInfo.canSkipSegments; + streamInfo.canBlockReload = realStreamInfo.canBlockReload; streamInfo.hasEndList = realStreamInfo.hasEndList; streamInfo.mediaSequenceToStartTime = realStreamInfo.mediaSequenceToStartTime; @@ -2297,6 +2301,8 @@ shaka.hls.HlsParser = class { playlist.tags, 'EXT-X-SERVER-CONTROL'); const canSkipSegments = serverControlTag ? serverControlTag.getAttribute('CAN-SKIP-UNTIL') != null : false; + const canBlockReload = serverControlTag ? + serverControlTag.getAttribute('CAN-BLOCK-RELOAD') != null : false; const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber( playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0); @@ -2320,6 +2326,7 @@ shaka.hls.HlsParser = class { minTimestamp: firstStartTime, maxTimestamp: lastEndTime, canSkipSegments, + canBlockReload, hasEndList: false, firstSequenceNumber: -1, nextMediaSequence, @@ -3695,6 +3702,7 @@ shaka.hls.HlsParser = class { * maxTimestamp: number, * mediaSequenceToStartTime: !Map., * canSkipSegments: boolean, + * canBlockReload: boolean, * hasEndList: boolean, * firstSequenceNumber: number, * nextMediaSequence: number, @@ -3727,6 +3735,10 @@ shaka.hls.HlsParser = class { * @property {boolean} canSkipSegments * True if the server supports delta playlist updates, and we can send a * request for a playlist that can skip older media segments. + * @property {boolean} canBlockReload + * True if the server supports blocking playlist reload, and we can send a + * request for a playlist that can block reload until some segments are + * present. * @property {boolean} hasEndList * True if the stream has an EXT-X-ENDLIST tag. * @property {number} firstSequenceNumber diff --git a/test/hls/hls_live_unit.js b/test/hls/hls_live_unit.js index 7995b113a1..6e06d4109d 100644 --- a/test/hls/hls_live_unit.js +++ b/test/hls/hls_live_unit.js @@ -163,28 +163,14 @@ describe('HlsParser live', () => { * @param {shaka.extern.Manifest} manifest * @param {string} updatedMedia * @param {Array=} updatedReferences - * @param {?number=} sequenceNumber */ - async function testUpdate(manifest, updatedMedia, updatedReferences=null, - sequenceNumber=null) { + async function testUpdate(manifest, updatedMedia, updatedReferences=null) { // Replace the entries with the updated values. - if (sequenceNumber == null) { - fakeNetEngine - .setResponseText('test:/video', updatedMedia) - .setResponseText('test:/redirected/video', updatedMedia) - .setResponseText('test:/video2', updatedMedia) - .setResponseText('test:/audio', updatedMedia); - } else { - fakeNetEngine - .setResponseText('test:/video?_HLS_msn=' + sequenceNumber, - updatedMedia) - .setResponseText('test:/redirected/video?_HLS_msn=' + sequenceNumber, - updatedMedia) - .setResponseText('test:/video2?_HLS_msn=' + sequenceNumber, - updatedMedia) - .setResponseText('test:/audio?_HLS_msn=' + sequenceNumber, - updatedMedia); - } + fakeNetEngine + .setResponseText('test:/video', updatedMedia) + .setResponseText('test:/redirected/video', updatedMedia) + .setResponseText('test:/video2', updatedMedia) + .setResponseText('test:/audio', updatedMedia); await delayForUpdatePeriod(); @@ -916,7 +902,7 @@ describe('HlsParser live', () => { '#EXT-X-TARGETDURATION:5\n', '#EXT-X-MEDIA-SEQUENCE:0\n', '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', - '#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=60.0\n', + '#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,CAN-SKIP-UNTIL=60.0,\n', '#EXTINF:2,\n', 'main.mp4\n', '#EXTINF:2,\n', @@ -928,7 +914,7 @@ describe('HlsParser live', () => { '#EXT-X-TARGETDURATION:5\n', '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', '#EXT-X-MEDIA-SEQUENCE:1\n', - '#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=60.0\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', 'main2.mp4\n', @@ -981,7 +967,7 @@ describe('HlsParser live', () => { // and ref1 should be in the SegmentReferences list. // ref3 should be appended to the SegmentReferences list. await testUpdate( - manifest, mediaWithSkippedSegments, [ref1, ref2, ref3], 2); + manifest, mediaWithSkippedSegments, [ref1, ref2, ref3]); }); it('skips older segments with discontinuity', async () => { @@ -1046,7 +1032,7 @@ describe('HlsParser live', () => { // and ref1,ref2 should be in the SegmentReferences list. // ref3,ref4 should be appended to the SegmentReferences list. await testUpdate( - manifest, mediaWithSkippedSegments2, [ref1, ref2, ref3, ref4], 3); + manifest, mediaWithSkippedSegments2, [ref1, ref2, ref3, ref4]); }); it('updates encryption keys', async () => {