From 3ba7492584d1db3742720248ed897a47cbb6bb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Tue, 2 Apr 2024 09:51:15 +0200 Subject: [PATCH] feat(HLS): Create segmentIndex when there is only one variant (#6383) --- lib/hls/hls_parser.js | 13 ++++++ test/hls/hls_parser_unit.js | 91 +++++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index bb4e762a5d..6b3c3abff6 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -893,6 +893,19 @@ shaka.hls.HlsParser = class { } this.playerInterface_.makeTextStreamsForClosedCaptions(this.manifest_); + + if (variants.length == 1) { + const createSegmentIndexPromises = []; + const variant = variants[0]; + for (const stream of [variant.video, variant.audio]) { + if (stream && !stream.segmentIndex) { + createSegmentIndexPromises.push(stream.createSegmentIndex()); + } + } + if (createSegmentIndexPromises.length > 0) { + await Promise.all(createSegmentIndexPromises); + } + } } /** diff --git a/test/hls/hls_parser_unit.js b/test/hls/hls_parser_unit.js index ae67520202..c0243d1b86 100644 --- a/test/hls/hls_parser_unit.js +++ b/test/hls/hls_parser_unit.js @@ -3499,8 +3499,7 @@ describe('HlsParser', () => { shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.MANIFEST, shaka.util.Error.Code.NO_WEB_CRYPTO_API)); - const actual = await parser.start('test:/master', playerInterface); - await expectAsync(loadAllStreamsFor(actual)) + await expectAsync(parser.start('test:/master', playerInterface)) .toBeRejectedWith(expectedError); } finally { Object.defineProperty(window, 'crypto', { @@ -3741,8 +3740,11 @@ describe('HlsParser', () => { const master = [ '#EXTM3U\n', '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",', - 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'RESOLUTION=960x540,FRAME-RATE=30\n', 'video\n', + '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",', + 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'video2\n', '#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,', 'KEYID=0X' + keyId + ',', 'KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",', @@ -3760,6 +3762,14 @@ describe('HlsParser', () => { }); }); }); + manifest.addPartialVariant((variant) => { + variant.addPartialStream(ContentType.VIDEO, (stream) => { + stream.addDrmInfo('com.widevine.alpha', (drmInfo) => { + drmInfo.addCencInitData(initDataBase64); + drmInfo.keyIds.add(keyId); + }); + }); + }); manifest.sequenceMode = sequenceMode; manifest.type = shaka.media.ManifestParser.HLS; }); @@ -3777,8 +3787,11 @@ describe('HlsParser', () => { const master = [ '#EXTM3U\n', '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",', - 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'RESOLUTION=960x540,FRAME-RATE=30\n', 'video\n', + '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",', + 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'video2\n', '#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,', 'KEYFORMAT="com.microsoft.playready",', 'URI="data:text/plain;base64,UGxheXJlYWR5",\n', @@ -3793,6 +3806,13 @@ describe('HlsParser', () => { }); }); }); + manifest.addPartialVariant((variant) => { + variant.addPartialStream(ContentType.VIDEO, (stream) => { + stream.addDrmInfo('com.microsoft.playready', (drmInfo) => { + drmInfo.addCencInitData(initDataBase64); + }); + }); + }); manifest.sequenceMode = sequenceMode; manifest.type = shaka.media.ManifestParser.HLS; }); @@ -3807,8 +3827,11 @@ describe('HlsParser', () => { const master = [ '#EXTM3U\n', '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",', - 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'RESOLUTION=960x540,FRAME-RATE=30\n', 'video\n', + '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",', + 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'video2\n', '#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,', 'KEYFORMAT="com.apple.streamingkeydelivery",', 'URI="skd://f93d4e700d7ddde90529a27735d9e7cb",\n', @@ -3823,6 +3846,13 @@ describe('HlsParser', () => { }); }); }); + manifest.addPartialVariant((variant) => { + variant.addPartialStream(ContentType.VIDEO, (stream) => { + stream.addDrmInfo('com.apple.fps', (drmInfo) => { + drmInfo.addInitData('sinf', new Uint8Array(0)); + }); + }); + }); manifest.sequenceMode = sequenceMode; manifest.type = shaka.media.ManifestParser.HLS; }); @@ -3837,13 +3867,19 @@ describe('HlsParser', () => { const master = [ '#EXTM3U\n', '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",', - 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'RESOLUTION=960x540,FRAME-RATE=30\n', 'video\n', + '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",', + 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'video2\n', '#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,', 'KEYFORMAT="identity",', 'URI="key.bin",\n', ].join(''); + const licenseServerUri ='data:application/json;base64,eyJrZXlzIjpbeyJr' + + 'dHkiOiJvY3QiLCJraWQiOiJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBIiwiayI6IlVHb' + + 'zJhRVpuZERWcFJscDBaa0pNVGpadmNUaEZaejA5In1dfQ=='; const initDataBase64 = 'eyJraWRzIjpbIkFBQUFBQUFBQUFBQUFBQUFBQUFBQUEiXX0='; const keyId = '00000000000000000000000000000000'; @@ -3853,10 +3889,16 @@ describe('HlsParser', () => { manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.VIDEO, (stream) => { stream.addDrmInfo('org.w3.clearkey', (drmInfo) => { - drmInfo.licenseServerUri = 'data:application/json;base64,eyJrZ' + - 'XlzIjpbeyJrdHkiOiJvY3QiLCJraWQiOiJBQUFBQUFBQUFBQUFBQUFBQU' + - 'FBQUFBIiwiayI6IlVHbzJhRVpuZERWcFJscDBaa0pNVGpadmNUaEZaejA' + - '5In1dfQ=='; + drmInfo.licenseServerUri = licenseServerUri; + drmInfo.keyIds.add(keyId); + drmInfo.addKeyIdsData(initDataBase64); + }); + }); + }); + manifest.addPartialVariant((variant) => { + variant.addPartialStream(ContentType.VIDEO, (stream) => { + stream.addDrmInfo('org.w3.clearkey', (drmInfo) => { + drmInfo.licenseServerUri = licenseServerUri; drmInfo.keyIds.add(keyId); drmInfo.addKeyIdsData(initDataBase64); }); @@ -3878,12 +3920,18 @@ describe('HlsParser', () => { const master = [ '#EXTM3U\n', '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",', - 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'RESOLUTION=960x540,FRAME-RATE=30\n', 'video\n', + '#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1",', + 'RESOLUTION=960x540,FRAME-RATE=60\n', + 'video2\n', '#EXT-X-SESSION-KEY:METHOD=SAMPLE-AES-CTR,', 'URI="key.bin",\n', ].join(''); + const licenseServerUri ='data:application/json;base64,eyJrZXlzIjpbeyJr' + + 'dHkiOiJvY3QiLCJraWQiOiJBQUFBQUFBQUFBQUFBQUFBQUFBQUFBIiwiayI6IlVHb' + + 'zJhRVpuZERWcFJscDBaa0pNVGpadmNUaEZaejA5In1dfQ=='; const initDataBase64 = 'eyJraWRzIjpbIkFBQUFBQUFBQUFBQUFBQUFBQUFBQUEiXX0='; const keyId = '00000000000000000000000000000000'; @@ -3893,10 +3941,16 @@ describe('HlsParser', () => { manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.VIDEO, (stream) => { stream.addDrmInfo('org.w3.clearkey', (drmInfo) => { - drmInfo.licenseServerUri = 'data:application/json;base64,eyJrZ' + - 'XlzIjpbeyJrdHkiOiJvY3QiLCJraWQiOiJBQUFBQUFBQUFBQUFBQUFBQU' + - 'FBQUFBIiwiayI6IlVHbzJhRVpuZERWcFJscDBaa0pNVGpadmNUaEZaejA' + - '5In1dfQ=='; + drmInfo.licenseServerUri = licenseServerUri; + drmInfo.keyIds.add(keyId); + drmInfo.addKeyIdsData(initDataBase64); + }); + }); + }); + manifest.addPartialVariant((variant) => { + variant.addPartialStream(ContentType.VIDEO, (stream) => { + stream.addDrmInfo('org.w3.clearkey', (drmInfo) => { + drmInfo.licenseServerUri = licenseServerUri; drmInfo.keyIds.add(keyId); drmInfo.addKeyIdsData(initDataBase64); }); @@ -4119,7 +4173,7 @@ describe('HlsParser', () => { shaka.util.Error.Category.MANIFEST, Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED); - await verifyError(master, media, error, /* onCreateSegmentIndex= */ true); + await verifyError(master, media, error); }); it('if SAMPLE-AES encryption with MSE and mp2t content', async () => { @@ -4146,7 +4200,7 @@ describe('HlsParser', () => { shaka.util.Error.Category.MANIFEST, Code.HLS_MSE_ENCRYPTED_MP2T_NOT_SUPPORTED); - await verifyError(master, media, error, /* onCreateSegmentIndex= */ true); + await verifyError(master, media, error); }); @@ -4163,8 +4217,7 @@ describe('HlsParser', () => { Code.HLS_REQUIRED_TAG_MISSING, tagName); - await verifyError( - master, media, error, /* onCreateSegmentIndex= */ true); + await verifyError(master, media, error); } it('EXTINF', async () => {