Skip to content

Commit

Permalink
feat(DRM): use preferredKeySystems to reduce requestMediaKeySystemAcc…
Browse files Browse the repository at this point in the history
…ess() calls (shaka-project#5391)

Propagate `preferredKeySystems` config to `getDecodingInfosForVariants()` method.
By doing that, shaka can only ask for `MediaKeySystemAccess` objects that will be used during playback.

If any preferred key system is available, player will stop requesting for MKSA.
If none of preferred key systems is available, player will try to get MKSA for any existing configuration, as usual.
  • Loading branch information
tykus160 committed Jul 8, 2023
1 parent 693abd5 commit 6d75d89
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 10 deletions.
3 changes: 2 additions & 1 deletion lib/media/drm_engine.js
Expand Up @@ -396,7 +396,8 @@ shaka.media.DrmEngine = class {
// We should get the decodingInfo results for the variants after we filling
// in the drm infos, and before queryMediaKeys_().
await shaka.util.StreamUtils.getDecodingInfosForVariants(variants,
this.usePersistentLicenses_, this.srcEquals_);
this.usePersistentLicenses_, this.srcEquals_,
this.config_.preferredKeySystems);

const hasDrmInfo = hadDrmInfo || Object.keys(this.config_.servers).length;
// An unencrypted content is initialized.
Expand Down
45 changes: 42 additions & 3 deletions lib/util/stream_utils.js
Expand Up @@ -428,7 +428,8 @@ shaka.util.StreamUtils = class {
'MediaCapabilities should be valid.');

await shaka.util.StreamUtils.getDecodingInfosForVariants(
manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
/** preferredKeySystems= */ []);
manifest.variants = manifest.variants.filter((variant) => {
// See: https://github.com/shaka-project/shaka-player/issues/3860
const video = variant.video;
Expand Down Expand Up @@ -575,21 +576,59 @@ shaka.util.StreamUtils = class {
* @param {!Array.<shaka.extern.Variant>} variants
* @param {boolean} usePersistentLicenses
* @param {boolean} srcEquals
* @param {!Array<string>} preferredKeySystems
* @exportDoc
*/
static async getDecodingInfosForVariants(variants, usePersistentLicenses,
srcEquals) {
srcEquals, preferredKeySystems) {
const gotDecodingInfo = variants.some((variant) =>
variant.decodingInfos.length);
if (gotDecodingInfo) {
shaka.log.debug('Already got the variants\' decodingInfo.');
return;
}

// Try to get preferred key systems first to avoid unneeded calls to CDM.
for (const preferredKeySystem of preferredKeySystems) {
let keySystemSatisfied = false;
for (const variant of variants) {
/** @type {!Array.<!MediaDecodingConfiguration>} */
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
variant, usePersistentLicenses, srcEquals)
.filter((config) => {
const keySystem = config.keySystemConfiguration &&
config.keySystemConfiguration.keySystem;
return keySystem === preferredKeySystem;
});

// The reason we are performing this await in a loop rather than
// batching into a `promise.all` is performance related.
// https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
for (const config of decodingConfigs) {
// eslint-disable-next-line no-await-in-loop
await shaka.util.StreamUtils.getDecodingInfosForVariant_(
variant, config);
}
if (variant.decodingInfos.length) {
keySystemSatisfied = true;
}
} // for (const variant of variants)
if (keySystemSatisfied) {
// Return if any preferred key system is already satisfied.
return;
}
} // for (const preferredKeySystem of preferredKeySystems)

for (const variant of variants) {
/** @type {!Array.<!MediaDecodingConfiguration>} */
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
variant, usePersistentLicenses, srcEquals);
variant, usePersistentLicenses, srcEquals)
.filter((config) => {
const keySystem = config.keySystemConfiguration &&
config.keySystemConfiguration.keySystem;
// Avoid checking preferred systems twice.
return !keySystem || !preferredKeySystems.includes(keySystem);
});

// The reason we are performing this await in a loop rather than
// batching into a `promise.all` is performance related.
Expand Down
4 changes: 3 additions & 1 deletion test/media/drm_engine_unit.js
Expand Up @@ -214,7 +214,9 @@ describe('DrmEngine', () => {
const variants = manifest.variants;
await drmEngine.initForPlayback(variants, manifest.offlineSessionIds);

expect(variants[0].decodingInfos.length).toBe(2);
// should be only one variant, as preferredKeySystems is propagated
// to getDecodingInfos
expect(variants[0].decodingInfos.length).toBe(1);
expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo()))
.toBe('drm.def');
});
Expand Down
47 changes: 42 additions & 5 deletions test/util/stream_utils_unit.js
Expand Up @@ -483,7 +483,8 @@ describe('StreamUtils', () => {
});

await StreamUtils.getDecodingInfosForVariants(manifest.variants,
/* usePersistentLicenses= */false, /* srcEquals= */ false);
/* usePersistentLicenses= */false, /* srcEquals= */ false,
/* preferredKeySystems= */ []);
expect(manifest.variants.length).toBeTruthy();
expect(manifest.variants[0].decodingInfos.length).toBe(1);
expect(manifest.variants[0].decodingInfos[0].supported).toBeTruthy();
Expand All @@ -499,7 +500,8 @@ describe('StreamUtils', () => {
});

await StreamUtils.getDecodingInfosForVariants(manifest.variants,
/* usePersistentLicenses= */false, /* srcEquals= */ true);
/* usePersistentLicenses= */false, /* srcEquals= */ true,
/* preferredKeySystems= */ []);
expect(manifest.variants.length).toBeTruthy();
expect(manifest.variants[0].decodingInfos.length).toBe(1);
expect(manifest.variants[0].decodingInfos[0].supported).toBeTruthy();
Expand Down Expand Up @@ -528,7 +530,8 @@ describe('StreamUtils', () => {
});

await StreamUtils.getDecodingInfosForVariants(manifest.variants,
/* usePersistentLicenses= */false, /* srcEquals= */ false);
/* usePersistentLicenses= */false, /* srcEquals= */ false,
/* preferredKeySystems= */ []);
expect(manifest.variants.length).toBe(1);
expect(manifest.variants[0].decodingInfos.length).toBe(0);
});
Expand Down Expand Up @@ -562,7 +565,8 @@ describe('StreamUtils', () => {
});

await StreamUtils.getDecodingInfosForVariants(manifest.variants,
/* usePersistentLicenses= */ false, /* srcEquals= */ false);
/* usePersistentLicenses= */ false, /* srcEquals= */ false,
/* preferredKeySystems= */ []);
expect(decodingInfoSpy.calls.argsFor(0)[0].video.transferFunction)
.toBe('srgb');
expect(decodingInfoSpy.calls.argsFor(1)[0].video.transferFunction)
Expand All @@ -573,6 +577,38 @@ describe('StreamUtils', () => {
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
}
});

it('includes streams only with preferred key system', async () => {
const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;

try {
navigator.mediaCapabilities.decodingInfo =
shaka.test.Util.spyFunc(decodingInfoSpy);

manifest = shaka.test.ManifestGenerator.generate((manifest) => {
manifest.addVariant(0, (variant) => {
variant.addVideo(1, (stream) => {
stream.mime('video/mp4', 'avc1.4d400d');
stream.encrypted = true;
stream.addDrmInfo('com.widevine.alpha');
stream.addDrmInfo('com.microsoft.playready');
});
});
});

await StreamUtils.getDecodingInfosForVariants(manifest.variants,
/* usePersistentLicenses= */ false, /* srcEquals= */ false,
/* preferredKeySystems= */ ['com.microsoft.playready']);

// if preferred key system satisfies us, we shouldn't check other ones.
expect(decodingInfoSpy).toHaveBeenCalledTimes(1);
expect(decodingInfoSpy.calls.argsFor(0)[0].keySystemConfiguration
.keySystem)
.toBe('com.microsoft.playready');
} finally {
navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
}
});
});

describe('filterManifest', () => {
Expand Down Expand Up @@ -906,7 +942,8 @@ describe('StreamUtils', () => {
});

await StreamUtils.getDecodingInfosForVariants(manifest.variants,
/* usePersistentLicenses= */false, /* srcEquals= */ false);
/* usePersistentLicenses= */false, /* srcEquals= */ false,
/* preferredKeySystems= */ []);

shaka.util.StreamUtils.chooseCodecsAndFilterManifest(manifest,
/* preferredVideoCodecs= */[],
Expand Down

0 comments on commit 6d75d89

Please sign in to comment.