diff --git a/externs/shaka/player.js b/externs/shaka/player.js index 0be58f147a..a0ea3f28ac 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -574,7 +574,8 @@ shaka.extern.AdvancedDrmConfiguration; * ((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)| * undefined), * logLicenseExchange: boolean, - * updateExpirationTime: number + * updateExpirationTime: number, + * preferredKeySystems: !Array. * }} * * @property {shaka.extern.RetryParameters} retryParameters @@ -612,6 +613,9 @@ shaka.extern.AdvancedDrmConfiguration; * @property {number} updateExpirationTime * Defaults to 1.
* The frequency in seconds with which to check the expiration of a session. + * @property {!Array.} preferredKeySystems + * Defaults to an empty array.
+ * Specifies the priorties of available DRM key systems. * * @exportDoc */ diff --git a/lib/media/drm_engine.js b/lib/media/drm_engine.js index 7f3424f708..e34314749b 100644 --- a/lib/media/drm_engine.js +++ b/lib/media/drm_engine.js @@ -1016,6 +1016,22 @@ shaka.media.DrmEngine = class { shaka.util.Error.Category.DRM, shaka.util.Error.Code.NO_RECOGNIZED_KEY_SYSTEMS); } + + // If we have configured preferredKeySystems, choose a preferred keySystem + // if available. + for (const preferredKeySystem of this.config_.preferredKeySystems) { + for (const variant of variants) { + const decodingInfo = variant.decodingInfos.find((decodingInfo) => { + return decodingInfo.supported && + decodingInfo.keySystemAccess != null && + decodingInfo.keySystemAccess.keySystem == preferredKeySystem; + }); + if (decodingInfo) { + return decodingInfo.keySystemAccess; + } + } + } + // Try key systems with configured license servers first. We only have to // try key systems without configured license servers for diagnostic // reasons, so that we can differentiate between "none of these key @@ -1071,6 +1087,24 @@ shaka.media.DrmEngine = class { } } + // If we have configured preferredKeySystems, choose the preferred one if + // available. + for (const keySystem of this.config_.preferredKeySystems) { + if (configsByKeySystem.has(keySystem)) { + const config = configsByKeySystem.get(keySystem); + try { + mediaKeySystemAccess = // eslint-disable-next-line no-await-in-loop + await navigator.requestMediaKeySystemAccess(keySystem, [config]); + return mediaKeySystemAccess; + } catch (error) { + // Suppress errors. + shaka.log.v2( + 'Requesting', keySystem, 'failed with config', config, error); + } + this.destroyer_.ensureNotDestroyed(); + } + } + // Try key systems with configured license servers first. We only have to // try key systems without configured license servers for diagnostic // reasons, so that we can differentiate between "none of these key @@ -1092,14 +1126,13 @@ shaka.media.DrmEngine = class { try { mediaKeySystemAccess = // eslint-disable-next-line no-await-in-loop - await navigator.requestMediaKeySystemAccess( - keySystem, [config]); + await navigator.requestMediaKeySystemAccess(keySystem, [config]); return mediaKeySystemAccess; } catch (error) { + // Suppress errors. shaka.log.v2( - 'Requesting', keySystem, 'failed with config', - config, error); - } // Suppress errors. + 'Requesting', keySystem, 'failed with config', config, error); + } this.destroyer_.ensureNotDestroyed(); } } diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 5cd3ebad2e..03fe4e2c54 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -75,6 +75,7 @@ shaka.util.PlayerConfiguration = class { initDataTransform: shaka.media.DrmEngine.defaultInitDataTransform, logLicenseExchange: false, updateExpirationTime: 1, + preferredKeySystems: [], }; const manifest = { diff --git a/test/media/drm_engine_unit.js b/test/media/drm_engine_unit.js index 8b276d450d..a4dbc65217 100644 --- a/test/media/drm_engine_unit.js +++ b/test/media/drm_engine_unit.js @@ -257,6 +257,30 @@ function testDrmEngine(useMediaCapabilities) { } }); + it('chooses systems by configured preferredKeySystems', async () => { + // Accept both drm.abc and drm.def. Only one can be chosen. + setRequestMediaKeySystemAccessSpy(['drm.abc', 'drm.def']); + config.preferredKeySystems = ['drm.def']; + drmEngine.configure(config); + logErrorSpy.and.stub(); + + const variants = manifest.variants; + await drmEngine.initForPlayback(variants, manifest.offlineSessionIds, + useMediaCapabilities); + + if (useMediaCapabilities) { + expect(variants[0].decodingInfos.length).toBe(2); + } else { + expect(requestMediaKeySystemAccessSpy).toHaveBeenCalledTimes(1); + // Although drm.def appears second in the manifest, it is queried first + // and also selected because it has a server configured. + const calls = requestMediaKeySystemAccessSpy.calls; + expect(calls.argsFor(0)[0]).toBe('drm.def'); + } + expect(shaka.media.DrmEngine.keySystem(drmEngine.getDrmInfo())) + .toBe('drm.def'); + }); + it('chooses systems with configured license servers', async () => { // Accept both drm.abc and drm.def. Only one can be chosen. setRequestMediaKeySystemAccessSpy(['drm.abc', 'drm.def']);