From 3ed24a474d92156cd7ca3ee43acb218cd726b2e9 Mon Sep 17 00:00:00 2001 From: Garrett Singer Date: Mon, 20 Dec 2021 14:26:03 -0500 Subject: [PATCH] feat: set up required key sessions on waitingforkey event (#1232) If a waitingforkey event is fired, try to set up new key sessions, as the manifest may have new key information. Co-authored-by: Gary Katsevman --- src/videojs-http-streaming.js | 59 +++++++++++++++++++++-------- test/videojs-http-streaming.test.js | 39 +++++++++++++++++++ 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/videojs-http-streaming.js b/src/videojs-http-streaming.js index d379bb302..3dfb14cd3 100644 --- a/src/videojs-http-streaming.js +++ b/src/videojs-http-streaming.js @@ -1004,6 +1004,41 @@ class VhsHandler extends Component { this.tech_.src(this.mediaSourceUrl_); } + createKeySessions_() { + const audioPlaylistLoader = + this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader; + + this.logger_('waiting for EME key session creation'); + waitForKeySessionCreation({ + player: this.player_, + sourceKeySystems: this.source_.keySystems, + audioMedia: audioPlaylistLoader && audioPlaylistLoader.media(), + mainPlaylists: this.playlists.master.playlists + }).then(() => { + this.logger_('created EME key session'); + this.masterPlaylistController_.sourceUpdater_.initializedEme(); + }).catch((err) => { + this.logger_('error while creating EME key session', err); + this.player_.error({ + message: 'Failed to initialize media keys for EME', + code: 3 + }); + }); + } + + handleWaitingForKey_() { + // If waitingforkey is fired, it's possible that the data that's necessary to retrieve + // the key is in the manifest. While this should've happened on initial source load, it + // may happen again in live streams where the keys change, and the manifest info + // reflects the update. + // + // Because videojs-contrib-eme compares the PSSH data we send to that of PSSH data it's + // already requested keys for, we don't have to worry about this generating extraneous + // requests. + this.logger_('waitingforkey fired, attempting to create any new key sessions'); + this.createKeySessions_(); + } + /** * If necessary and EME is available, sets up EME options and waits for key session * creation. @@ -1033,6 +1068,9 @@ class VhsHandler extends Component { } }); + this.handleWaitingForKey_ = this.handleWaitingForKey_.bind(this); + this.player_.tech_.on('waitingforkey', this.handleWaitingForKey_); + // In IE11 this is too early to initialize media keys, and IE11 does not support // promises. if (videojs.browser.IE_VERSION === 11 || !didSetupEmeOptions) { @@ -1041,22 +1079,7 @@ class VhsHandler extends Component { return; } - this.logger_('waiting for EME key session creation'); - waitForKeySessionCreation({ - player: this.player_, - sourceKeySystems: this.source_.keySystems, - audioMedia: audioPlaylistLoader && audioPlaylistLoader.media(), - mainPlaylists: this.playlists.master.playlists - }).then(() => { - this.logger_('created EME key session'); - this.masterPlaylistController_.sourceUpdater_.initializedEme(); - }).catch((err) => { - this.logger_('error while creating EME key session', err); - this.player_.error({ - message: 'Failed to initialize media keys for EME', - code: 3 - }); - }); + this.createKeySessions_(); } /** @@ -1171,6 +1194,10 @@ class VhsHandler extends Component { this.mediaSourceUrl_ = null; } + if (this.tech_) { + this.tech_.off('waitingforkey', this.handleWaitingForKey_); + } + super.dispose(); } diff --git a/test/videojs-http-streaming.test.js b/test/videojs-http-streaming.test.js index cc25c6587..f8c3f4c0c 100644 --- a/test/videojs-http-streaming.test.js +++ b/test/videojs-http-streaming.test.js @@ -4713,6 +4713,45 @@ QUnit.test('eme handles keystatuschange where status is usable', function(assert assert.deepEqual(excludes, [], 'did not exclude anything'); }); +QUnit.test('eme waitingforkey event triggers another setup', function(assert) { + this.player.eme = { options: { setting: 1 } }; + this.player.src({ + src: 'manifest/master.m3u8', + type: 'application/x-mpegURL', + keySystems: { keySystem1: { url: 'url1' } } + }); + + this.clock.tick(1); + + const media = { + attributes: { CODECS: 'avc1.420015, mp4a.40.2c' }, + contentProtection: { keySystem1: { pssh: 'test' } } + }; + + const vhs = this.player.tech_.vhs; + + vhs.playlists = { + master: { playlists: [media] }, + media: () => media + }; + + const origCreateKeySessions = vhs.createKeySessions_.bind(vhs); + let createKeySessionCalls = 0; + + vhs.createKeySessions_ = () => { + createKeySessionCalls++; + origCreateKeySessions(); + }; + + vhs.masterPlaylistController_.sourceUpdater_.trigger('createdsourcebuffers'); + + assert.equal(createKeySessionCalls, 1, 'called createKeySessions_ once'); + + this.player.tech_.trigger({type: 'waitingforkey', status: 'usable'}); + + assert.equal(createKeySessionCalls, 2, 'called createKeySessions_ again'); +}); + QUnit.test('integration: configures eme for DASH on source buffer creation', function(assert) { assert.timeout(3000); const done = assert.async();