From 8dc7557a129f8a9f7460d19ea9ab9faee4147a0e Mon Sep 17 00:00:00 2001 From: Alex Barstow Date: Fri, 5 Aug 2022 13:04:33 -0400 Subject: [PATCH 1/4] modify output-restricted handling to exclude all HD playlists and perform fastQualityChange_, update test --- src/videojs-http-streaming.js | 33 +++++++++++-- test/videojs-http-streaming.test.js | 72 +++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/src/videojs-http-streaming.js b/src/videojs-http-streaming.js index bb1690197..c100cc7c0 100644 --- a/src/videojs-http-streaming.js +++ b/src/videojs-http-streaming.js @@ -1062,11 +1062,34 @@ class VhsHandler extends Component { this.player_.tech_.on('keystatuschange', (e) => { if (e.status === 'output-restricted') { - this.masterPlaylistController_.blacklistCurrentPlaylist({ - playlist: this.masterPlaylistController_.media(), - message: `DRM keystatus changed to ${e.status}. Playlist will fail to play. Check for HDCP content.`, - blacklistDuration: Infinity - }); + const masterPlaylist = this.masterPlaylistController_.media(); + const excludedHDPlaylists = []; + + if (masterPlaylist && masterPlaylist.playlists) { + // Assume all HD streams are unplayable and exclude them from ABR selection, then perform + // a fastQualityChange to clear the buffer since it may already contain unplayable segments + masterPlaylist.playlists.forEach(playlist => { + if (playlist && playlist.attributes && playlist.attributes.RESOLUTION && + playlist.attributes.RESOLUTION.height >= 720) { + if (!playlist.excludeUntil || playlist.excludeUntil < Infinity) { + + playlist.excludeUntil = Infinity; + excludedHDPlaylists.push(playlist); + } + } + }); + + if (excludedHDPlaylists.length) { + videojs.log.warn( + 'DRM keystatus changed to "output-restricted." Removing the following HD playlists ' + + 'that will most likely fail to play and clearing already buffered HD segments. ' + + 'Check for HDCP content.', + ...excludedHDPlaylists + ); + + this.masterPlaylistController_.fastQualityChange_(); + } + } } }); diff --git a/test/videojs-http-streaming.test.js b/test/videojs-http-streaming.test.js index 310f4ceee..126dd3ede 100644 --- a/test/videojs-http-streaming.test.js +++ b/test/videojs-http-streaming.test.js @@ -4617,12 +4617,21 @@ QUnit.test('configures eme for HLS on source buffer creation', function(assert) }, 'set source eme options'); }); -QUnit.test('eme handles keystatuschange where status is output-restricted', function(assert) { +QUnit.only('eme handles keystatuschange where status is output-restricted', function(assert) { + const originalWarn = videojs.log.warn; + let warning = ''; + let qualitySwitches = 0; + + videojs.log.warn = (...text) => { + warning += [...text].join(''); + }; + this.player.eme = { options: { previousSetting: 1 } }; + this.player.src({ src: 'manifest/master.m3u8', type: 'application/x-mpegURL', @@ -4635,36 +4644,63 @@ QUnit.test('eme handles keystatuschange where status is output-restricted', func this.clock.tick(1); - const media = { - attributes: { - CODECS: 'avc1.420015, mp4a.40.2c' + const playlists = [ + { + attributes: { + RESOLUTION: { + width: 1280, + height: 720 + } + } }, - contentProtection: { - keySystem1: { - pssh: 'test' + { + attributes: { + RESOLUTION: { + width: 1920, + height: 1080 + } + } + }, + { + attributes: { + RESOLUTION: { + width: 848, + height: 480 + } } } - }; + ]; this.player.tech_.vhs.playlists = { - master: { playlists: [media] }, - media: () => media + master: { playlists }, + media: () => playlists[0] }; - const excludes = []; + this.player.tech_.vhs.masterPlaylistController_.media = () => { + return { + playlists + }; + }; - this.player.tech_.vhs.masterPlaylistController_.blacklistCurrentPlaylist = (exclude) => { - excludes.push(exclude); + this.player.tech_.vhs.masterPlaylistController_.fastQualityChange_ = () => { + qualitySwitches++; }; this.player.tech_.vhs.masterPlaylistController_.sourceUpdater_.trigger('createdsourcebuffers'); this.player.tech_.trigger({type: 'keystatuschange', status: 'output-restricted'}); - assert.deepEqual(excludes, [{ - blacklistDuration: Infinity, - message: 'DRM keystatus changed to output-restricted. Playlist will fail to play. Check for HDCP content.', - playlist: undefined - }], 'excluded playlist'); + assert.equal(playlists[0].excludeUntil, Infinity, 'first HD playlist excluded'); + assert.equal(playlists[1].excludeUntil, Infinity, 'second HD playlist excluded'); + assert.equal(playlists[2].excludeUntil, undefined, 'non-HD playlist not excluded'); + assert.equal(qualitySwitches, 1, 'fastQualityChange_ called once'); + assert.equal( + warning, + 'DRM keystatus changed to "output-restricted." Removing the following HD playlists ' + + 'that will most likely fail to play and clearing already buffered HD segments. ' + + 'Check for HDCP content.' + [playlists[0], playlists[1]].join('') + ); + + videojs.log.warn = originalWarn; }); QUnit.test('eme handles keystatuschange where status is usable', function(assert) { From 97a650f76fcc50e4e7bae5b43587950bfada01ef Mon Sep 17 00:00:00 2001 From: Alex Barstow Date: Fri, 5 Aug 2022 13:10:33 -0400 Subject: [PATCH 2/4] remove .only --- test/videojs-http-streaming.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/videojs-http-streaming.test.js b/test/videojs-http-streaming.test.js index 126dd3ede..c6d46f910 100644 --- a/test/videojs-http-streaming.test.js +++ b/test/videojs-http-streaming.test.js @@ -4617,7 +4617,7 @@ QUnit.test('configures eme for HLS on source buffer creation', function(assert) }, 'set source eme options'); }); -QUnit.only('eme handles keystatuschange where status is output-restricted', function(assert) { +QUnit.test('eme handles keystatuschange where status is output-restricted', function(assert) { const originalWarn = videojs.log.warn; let warning = ''; let qualitySwitches = 0; From 999726ca8af10491185304bbd29709cc905562ad Mon Sep 17 00:00:00 2001 From: Alex Barstow Date: Mon, 15 Aug 2022 15:10:44 -0400 Subject: [PATCH 3/4] code review changes --- src/videojs-http-streaming.js | 55 ++++++++++++++++------------- test/videojs-http-streaming.test.js | 2 +- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/videojs-http-streaming.js b/src/videojs-http-streaming.js index c100cc7c0..fdcb5c085 100644 --- a/src/videojs-http-streaming.js +++ b/src/videojs-http-streaming.js @@ -1061,35 +1061,40 @@ class VhsHandler extends Component { }); this.player_.tech_.on('keystatuschange', (e) => { - if (e.status === 'output-restricted') { - const masterPlaylist = this.masterPlaylistController_.media(); - const excludedHDPlaylists = []; - - if (masterPlaylist && masterPlaylist.playlists) { - // Assume all HD streams are unplayable and exclude them from ABR selection, then perform - // a fastQualityChange to clear the buffer since it may already contain unplayable segments - masterPlaylist.playlists.forEach(playlist => { - if (playlist && playlist.attributes && playlist.attributes.RESOLUTION && - playlist.attributes.RESOLUTION.height >= 720) { - if (!playlist.excludeUntil || playlist.excludeUntil < Infinity) { - - playlist.excludeUntil = Infinity; - excludedHDPlaylists.push(playlist); - } - } - }); + if (e.status !== 'output-restricted') { + return; + } + + const masterPlaylist = this.masterPlaylistController_.master(); + + if (masterPlaylist && masterPlaylist.playlists) { + return; + } + + const excludedHDPlaylists = []; - if (excludedHDPlaylists.length) { - videojs.log.warn( - 'DRM keystatus changed to "output-restricted." Removing the following HD playlists ' + - 'that will most likely fail to play and clearing already buffered HD segments. ' + - 'Check for HDCP content.', - ...excludedHDPlaylists - ); + // Assume all HD streams are unplayable and exclude them from ABR selection + masterPlaylist.playlists.forEach(playlist => { + if (playlist && playlist.attributes && playlist.attributes.RESOLUTION && + playlist.attributes.RESOLUTION.height >= 720) { + if (!playlist.excludeUntil || playlist.excludeUntil < Infinity) { - this.masterPlaylistController_.fastQualityChange_(); + playlist.excludeUntil = Infinity; + excludedHDPlaylists.push(playlist); } } + }); + + if (excludedHDPlaylists.length) { + videojs.log.warn( + 'DRM keystatus changed to "output-restricted." Removing the following HD playlists ' + + 'that will most likely fail to play and clearing the buffer. ' + + 'This may be due to HDCP restrictions on the stream and the capabilities of the current device.', + ...excludedHDPlaylists + ); + + // Clear the buffer before switching playlists, since it may already contain unplayable segments + this.masterPlaylistController_.fastQualityChange_(); } }); diff --git a/test/videojs-http-streaming.test.js b/test/videojs-http-streaming.test.js index c6d46f910..70a49b653 100644 --- a/test/videojs-http-streaming.test.js +++ b/test/videojs-http-streaming.test.js @@ -4676,7 +4676,7 @@ QUnit.test('eme handles keystatuschange where status is output-restricted', func media: () => playlists[0] }; - this.player.tech_.vhs.masterPlaylistController_.media = () => { + this.player.tech_.vhs.masterPlaylistController_.master = () => { return { playlists }; From 4f0153dbcd59b3dec9a7f50addc57de31fabf160 Mon Sep 17 00:00:00 2001 From: Alex Barstow Date: Mon, 15 Aug 2022 16:00:09 -0400 Subject: [PATCH 4/4] fix conditional and test --- src/videojs-http-streaming.js | 2 +- test/videojs-http-streaming.test.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/videojs-http-streaming.js b/src/videojs-http-streaming.js index fdcb5c085..49df8d611 100644 --- a/src/videojs-http-streaming.js +++ b/src/videojs-http-streaming.js @@ -1067,7 +1067,7 @@ class VhsHandler extends Component { const masterPlaylist = this.masterPlaylistController_.master(); - if (masterPlaylist && masterPlaylist.playlists) { + if (!masterPlaylist || !masterPlaylist.playlists) { return; } diff --git a/test/videojs-http-streaming.test.js b/test/videojs-http-streaming.test.js index 70a49b653..4e6595e18 100644 --- a/test/videojs-http-streaming.test.js +++ b/test/videojs-http-streaming.test.js @@ -4696,8 +4696,9 @@ QUnit.test('eme handles keystatuschange where status is output-restricted', func assert.equal( warning, 'DRM keystatus changed to "output-restricted." Removing the following HD playlists ' + - 'that will most likely fail to play and clearing already buffered HD segments. ' + - 'Check for HDCP content.' + [playlists[0], playlists[1]].join('') + 'that will most likely fail to play and clearing the buffer. ' + + 'This may be due to HDCP restrictions on the stream and the capabilities of the current device.' + + [playlists[0], playlists[1]].join('') ); videojs.log.warn = originalWarn;