diff --git a/externs/shaka/manifest_parser.js b/externs/shaka/manifest_parser.js index 8c924f08d1..6b297ec8eb 100644 --- a/externs/shaka/manifest_parser.js +++ b/externs/shaka/manifest_parser.js @@ -112,7 +112,8 @@ shaka.extern.ManifestParser = class { * isLowLatencyMode: function():boolean, * isAutoLowLatencyMode: function():boolean, * enableLowLatencyMode: function(), - * updateDuration: function() + * updateDuration: function(), + * newDrmInfo: function(shaka.extern.Stream) * }} * * @description @@ -150,6 +151,9 @@ shaka.extern.ManifestParser = class { * Enable low latency streaming mode. * @property {function()} updateDuration * Update the presentation duration based on PresentationTimeline. + * @property {function(shaka.extern.Stream)} newDrmInfo + * Inform the player of new DRM info that needs to be processed for the given + * stream. * @exportDoc */ shaka.extern.ManifestParser.PlayerInterface; diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index f2e8166681..11c15cdae5 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -1605,6 +1605,12 @@ shaka.hls.HlsParser = class { stream.roles = realStream.roles; stream.mimeType = realStream.mimeType; + // Since we lazy-loaded this content, the player may need to create new + // sessions for the DRM info in this stream. + if (stream.drmInfos.length) { + this.playerInterface_.newDrmInfo(stream); + } + const ContentType = shaka.util.ManifestParserUtils.ContentType; if (type == ContentType.VIDEO || type == ContentType.AUDIO) { for (const otherStreamInfo of this.uriToStreamInfosMap_.values()) { diff --git a/lib/offline/storage.js b/lib/offline/storage.js index 2a507fc9ae..b64e91f9ee 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -1170,6 +1170,7 @@ shaka.offline.Storage = class { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; parser.configure(config.manifest); diff --git a/lib/player.js b/lib/player.js index fbed9bd9c6..f177f31156 100644 --- a/lib/player.js +++ b/lib/player.js @@ -1889,6 +1889,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget { this.streamingEngine_.updateDuration(); } }, + newDrmInfo: (stream) => { + // We may need to create new sessions for any new init data. + const currentDrmInfo = + this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null; + // DrmEngine.newInitData() requires mediaKeys to be available. + if (currentDrmInfo && this.drmEngine_.getMediaKeys()) { + this.processDrmInfos_(currentDrmInfo.keySystem, stream); + } + }, }; const startTime = Date.now() / 1000; @@ -5410,27 +5419,39 @@ shaka.Player = class extends shaka.util.FakeEventTarget { } // We may need to create new sessions for any new init data. - const curDrmInfo = this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null; + const currentDrmInfo = + this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null; // DrmEngine.newInitData() requires mediaKeys to be available. - if (curDrmInfo && this.drmEngine_.getMediaKeys()) { + if (currentDrmInfo && this.drmEngine_.getMediaKeys()) { for (const variant of manifest.variants) { - const videoDrmInfos = variant.video ? variant.video.drmInfos : []; - const audioDrmInfos = variant.audio ? variant.audio.drmInfos : []; - const drmInfos = videoDrmInfos.concat(audioDrmInfos); - for (const drmInfo of drmInfos) { - // Ignore any data for different key systems. - if (drmInfo.keySystem == curDrmInfo.keySystem) { - for (const initData of (drmInfo.initData || [])) { - this.drmEngine_.newInitData( - initData.initDataType, initData.initData); - } - } - } + this.processDrmInfos_(currentDrmInfo.keySystem, variant.video); + this.processDrmInfos_(currentDrmInfo.keySystem, variant.audio); } } this.checkRestrictedVariants_(manifest); } + /** + * @param {string} keySystem + * @param {?shaka.extern.Stream} stream + * @private + */ + processDrmInfos_(keySystem, stream) { + if (!stream) { + return; + } + + for (const drmInfo of stream.drmInfos) { + // Ignore any data for different key systems. + if (drmInfo.keySystem == keySystem) { + for (const initData of (drmInfo.initData || [])) { + this.drmEngine_.newInitData( + initData.initDataType, initData.initData); + } + } + } + } + /** * @private */ diff --git a/test/dash/dash_parser_content_protection_unit.js b/test/dash/dash_parser_content_protection_unit.js index 98c0f622fb..476a3dfce6 100644 --- a/test/dash/dash_parser_content_protection_unit.js +++ b/test/dash/dash_parser_content_protection_unit.js @@ -44,6 +44,7 @@ describe('DashParser ContentProtection', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; const actual = await dashParser.start( diff --git a/test/dash/dash_parser_live_unit.js b/test/dash/dash_parser_live_unit.js index f5b768ab78..12a3e538f1 100644 --- a/test/dash/dash_parser_live_unit.js +++ b/test/dash/dash_parser_live_unit.js @@ -37,6 +37,7 @@ describe('DashParser Live', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; }); diff --git a/test/dash/dash_parser_manifest_unit.js b/test/dash/dash_parser_manifest_unit.js index 3ea6a0a760..416b7ce3d4 100644 --- a/test/dash/dash_parser_manifest_unit.js +++ b/test/dash/dash_parser_manifest_unit.js @@ -53,6 +53,7 @@ describe('DashParser Manifest', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; }); diff --git a/test/dash/dash_parser_segment_base_unit.js b/test/dash/dash_parser_segment_base_unit.js index 9adf96dc09..c8dbcb2657 100644 --- a/test/dash/dash_parser_segment_base_unit.js +++ b/test/dash/dash_parser_segment_base_unit.js @@ -39,6 +39,7 @@ describe('DashParser SegmentBase', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; }); diff --git a/test/dash/dash_parser_segment_list_unit.js b/test/dash/dash_parser_segment_list_unit.js index 827a4ec66c..0dce34c149 100644 --- a/test/dash/dash_parser_segment_list_unit.js +++ b/test/dash/dash_parser_segment_list_unit.js @@ -349,6 +349,7 @@ describe('DashParser SegmentList', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; const manifest = await dashParser.start('dummy://foo', playerInterface); const stream = manifest.variants[0].video; diff --git a/test/dash/dash_parser_segment_template_unit.js b/test/dash/dash_parser_segment_template_unit.js index af0a1b6266..e02b49ddc0 100644 --- a/test/dash/dash_parser_segment_template_unit.js +++ b/test/dash/dash_parser_segment_template_unit.js @@ -48,6 +48,7 @@ describe('DashParser SegmentTemplate', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; }); diff --git a/test/hls/hls_live_unit.js b/test/hls/hls_live_unit.js index 18ab0919ed..fd313eb20e 100644 --- a/test/hls/hls_live_unit.js +++ b/test/hls/hls_live_unit.js @@ -78,6 +78,7 @@ describe('HlsParser live', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; parser = new shaka.hls.HlsParser(); diff --git a/test/hls/hls_parser_unit.js b/test/hls/hls_parser_unit.js index 0638c50429..72dade6ca6 100644 --- a/test/hls/hls_parser_unit.js +++ b/test/hls/hls_parser_unit.js @@ -23,6 +23,8 @@ describe('HlsParser', () => { let parser; /** @type {!jasmine.Spy} */ let onEventSpy; + /** @type {!jasmine.Spy} */ + let newDrmInfoSpy; /** @type {shaka.extern.ManifestParser.PlayerInterface} */ let playerInterface; /** @type {shaka.extern.ManifestConfiguration} */ @@ -86,6 +88,7 @@ describe('HlsParser', () => { config = shaka.util.PlayerConfiguration.createDefault().manifest; onEventSpy = jasmine.createSpy('onEvent'); + newDrmInfoSpy = jasmine.createSpy('newDrmInfo'); playerInterface = { modifyManifestRequest: (request, manifestInfo) => {}, modifySegmentRequest: (request, segmentInfo) => {}, @@ -99,6 +102,7 @@ describe('HlsParser', () => { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: shaka.test.Util.spyFunc(newDrmInfoSpy), }; parser = new shaka.hls.HlsParser(); @@ -2763,6 +2767,7 @@ describe('HlsParser', () => { }); await testHlsParser(master, media, manifest); + expect(newDrmInfoSpy).toHaveBeenCalled(); }); it('constructs DrmInfo for PlayReady', async () => { @@ -2803,6 +2808,7 @@ describe('HlsParser', () => { }); await testHlsParser(master, media, manifest); + expect(newDrmInfoSpy).toHaveBeenCalled(); }); it('constructs DrmInfo for FairPlay', async () => { @@ -2840,6 +2846,7 @@ describe('HlsParser', () => { }); await testHlsParser(master, media, manifest); + expect(newDrmInfoSpy).toHaveBeenCalled(); }); it('constructs DrmInfo for ClearKey with explicit KEYFORMAT', async () => { @@ -2874,6 +2881,7 @@ describe('HlsParser', () => { }); await testHlsParser(master, media, manifest); + expect(newDrmInfoSpy).toHaveBeenCalled(); }); it('constructs DrmInfo for ClearKey without explicit KEYFORMAT', async () => { @@ -2907,6 +2915,7 @@ describe('HlsParser', () => { }); await testHlsParser(master, media, manifest); + expect(newDrmInfoSpy).toHaveBeenCalled(); }); it('falls back to mp4 if HEAD request fails', async () => { diff --git a/test/test/util/dash_parser_util.js b/test/test/util/dash_parser_util.js index 6626ad0bde..cf9647d378 100644 --- a/test/test/util/dash_parser_util.js +++ b/test/test/util/dash_parser_util.js @@ -44,6 +44,7 @@ shaka.test.Dash = class { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; const manifest = await dashParser.start('dummy://foo', playerInterface); const stream = manifest.variants[0].video; @@ -79,6 +80,7 @@ shaka.test.Dash = class { isAutoLowLatencyMode: () => false, enableLowLatencyMode: () => {}, updateDuration: () => {}, + newDrmInfo: (stream) => {}, }; const p = dashParser.start('dummy://foo', playerInterface); await expectAsync(p).toBeRejectedWith(