From 9a618d479b14ef3b5e00989a3d6dedef1e907d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Tyczy=C5=84ski?= Date: Tue, 11 Feb 2025 12:18:15 +0100 Subject: [PATCH] fix(DASH): Fix playback of ClearKey when only part of keys are defined (#8055) Fixes #8052 Key ids were not used when looking for stream duplicates, so it was possible to filter out streams without accompanying configuration. Now we will use key ids as well for duplicates detection. --- lib/util/periods.js | 6 ++- test/util/periods_unit.js | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/lib/util/periods.js b/lib/util/periods.js index 90cb3e9cd4..7d064c9f06 100644 --- a/lib/util/periods.js +++ b/lib/util/periods.js @@ -836,7 +836,7 @@ shaka.util.PeriodCombiner = class { clone.roles = clone.roles.slice(); clone.segmentIndex = null; clone.emsgSchemeIdUris = []; - clone.keyIds = new Set(); + clone.keyIds = new Set(stream.keyIds); clone.closedCaptions = stream.closedCaptions ? new Map(stream.closedCaptions) : null; clone.trickModeVideo = null; @@ -860,7 +860,7 @@ shaka.util.PeriodCombiner = class { clone.roles = clone.roles.slice(); // These are wiped out now and rebuilt later from the various per-period // streams that match this output. - clone.keyIds = new Set(); + clone.keyIds = new Set(streamDb.keyIds); clone.segments = []; clone.variantIds = []; clone.closedCaptions = streamDb.closedCaptions ? @@ -1774,6 +1774,7 @@ shaka.util.PeriodCombiner = class { v.roles, v.closedCaptions ? Array.from(v.closedCaptions.entries()) : null, v.bandwidth, + Array.from(v.keyIds), ]); } @@ -1796,6 +1797,7 @@ shaka.util.PeriodCombiner = class { a.roles, a.audioSamplingRate, a.primary, + Array.from(a.keyIds), ]); } diff --git a/test/util/periods_unit.js b/test/util/periods_unit.js index f1b297af02..3bb19e8732 100644 --- a/test/util/periods_unit.js +++ b/test/util/periods_unit.js @@ -603,6 +603,84 @@ describe('PeriodCombiner', () => { } }); + it('Filters out duplicates based on keyids', async () => { + // v1 and v3 are duplicates + const v1 = makeVideoStream(1280); + v1.frameRate = 30000/1001; + v1.originalId = 'v1'; + v1.bandwidth = 6200000; + v1.keyIds = new Set(['deadbeefdeadbeefdeadbeefdeadbeef']); + + const v2 = makeVideoStream(1280); + v2.frameRate = 30000/1001; + v2.originalId = 'v2'; + v2.bandwidth = 6200000; + v2.keyIds = new Set(['02030507011013017019023029031037']); + + const v3 = makeVideoStream(1280); + v3.frameRate = 30000/1001; + v3.originalId = 'v3'; + v3.bandwidth = 6200000; + v3.keyIds = new Set(['deadbeefdeadbeefdeadbeefdeadbeef']); + + // a1 and a2 are duplicates. + const a1 = makeAudioStream('en', /* channels= */ 2); + a1.originalId = 'a1'; + a1.bandwidth = 65106; + a1.roles = ['role1', 'role2']; + a1.codecs = 'mp4a.40.2'; + a1.keyIds = new Set(['deadbeefdeadbeefdeadbeefdeadbeef']); + + const a2 = makeAudioStream('en', /* channels= */ 2); + a2.originalId = 'a2'; + a2.bandwidth = 65106; + a2.roles = ['role1', 'role2']; + a2.codecs = 'mp4a.40.2'; + a2.keyIds = new Set(['deadbeefdeadbeefdeadbeefdeadbeef']); + + const a3 = makeAudioStream('en', /* channels= */ 2); + a3.originalId = 'a3'; + a3.bandwidth = 65106; + a3.roles = ['role1', 'role2']; + a3.codecs = 'mp4a.40.2'; + a3.keyIds = new Set(['02030507011013017019023029031037']); + + /** @type {!Array} */ + const periods = [ + { + id: '1', + videoStreams: [ + v1, + v2, + v3, + ], + audioStreams: [ + a1, + a2, + a3, + ], + textStreams: [], + imageStreams: [], + }, + ]; + + await combiner.combinePeriods(periods, /* isDynamic= */ true); + const variants = combiner.getVariants(); + expect(variants.length).toBe(4); + + // v1 should've been filtered out + const videoIds = variants.map((v) => v.video.originalId); + for (const id of videoIds) { + expect(id).not.toBe('v1'); + } + + // a1 should've been filtered out + const audioIds = variants.map((v) => v.audio.originalId); + for (const id of audioIds) { + expect(id).not.toBe('a1'); + } + }); + // Regression test for #6054, where we failed on multi-period content with // different numbers of forced-subtitle streams per period. it('Does not combine subtitle and forced-subtitle tracks', async () => {