From bf6376aabcad62f95257492afd5d25c60272a4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Tue, 31 Dec 2024 14:51:07 +0100 Subject: [PATCH] fix: Fix ClearKey license on old CDMs (#7816) Backported to v4.12.x --- lib/media/drm_engine.js | 47 ++++++++++++++++++++++++++++++++++++- lib/util/drm_utils.js | 20 ++++++++++++++++ test/util/drm_utils_unit.js | 32 +++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/lib/media/drm_engine.js b/lib/media/drm_engine.js index a5dbeb2e06f..facfc17f41b 100644 --- a/lib/media/drm_engine.js +++ b/lib/media/drm_engine.js @@ -1433,7 +1433,12 @@ shaka.media.DrmEngine = class { } // NOTE: allowCrossSiteCredentials can be set in a request filter. - if (shaka.util.DrmUtils.isPlayReadyKeySystem( + if (shaka.drm.DrmUtils.isClearKeySystem( + this.currentDrmInfo_.keySystem)) { + this.fixClearKeyRequest_(request, this.currentDrmInfo_); + } + + if (shaka.drm.DrmUtils.isPlayReadyKeySystem( this.currentDrmInfo_.keySystem)) { this.unpackPlayReadyRequest_(request); } @@ -1584,6 +1589,31 @@ shaka.media.DrmEngine = class { /** @type{string} */(shaka.util.TXml.getTextContents(challenge))); } + /** + * Some old ClearKey CDMs don't include the type in the body request. + * + * @param {shaka.extern.Request} request + * @param {shaka.extern.DrmInfo} drmInfo + * @private + */ + fixClearKeyRequest_(request, drmInfo) { + try { + const body = shaka.util.StringUtils.fromBytesAutoDetect(request.body); + if (body) { + const licenseBody = + /** @type {shaka.drm.DrmEngine.ClearKeyLicenceRequestFormat} */ ( + JSON.parse(body)); + if (!licenseBody.type) { + licenseBody.type = drmInfo.sessionType; + request.body = + shaka.util.StringUtils.toUTF8(JSON.stringify(licenseBody)); + } + } + } catch (e) { + shaka.log.info('Error unpacking ClearKey license', e); + } + } + /** * @param {!Event} event * @private @@ -2603,6 +2633,21 @@ shaka.media.DrmEngine.SessionMetaData; */ shaka.media.DrmEngine.PlayerInterface; +/** + * @typedef {{ + * kids: !Array., + * type: string + * }} + * + * @property {!Array.} kids + * An array of key IDs. Each element of the array is the base64url encoding of + * the octet sequence containing the key ID value. + * @property {string} type + * The requested MediaKeySessionType. + * @see https://www.w3.org/TR/encrypted-media/#clear-key-request-format + */ +shaka.drm.DrmEngine.ClearKeyLicenceRequestFormat; + /** * The amount of time, in seconds, we wait to consider a session closed. * This allows us to work around Chrome bug https://crbug.com/1108158. diff --git a/lib/util/drm_utils.js b/lib/util/drm_utils.js index 3bee650c000..2fead5d1b37 100644 --- a/lib/util/drm_utils.js +++ b/lib/util/drm_utils.js @@ -113,6 +113,26 @@ shaka.util.DrmUtils = class { return drmInfo ? drmInfo.keySystem : ''; } + /** + * @param {?string} keySystem + * @return {boolean} + */ + static isClearKeySystem(keySystem) { + return keySystem === 'org.w3.clearkey'; + } + + /** + * @param {?string} keySystem + * @return {boolean} + */ + static isWidevineKeySystem(keySystem) { + if (keySystem) { + return !!keySystem.match(/^com\.widevine\.alpha/); + } + + return false; + } + /** * @param {?string} keySystem * @return {boolean} diff --git a/test/util/drm_utils_unit.js b/test/util/drm_utils_unit.js index c87ecc2fa57..a10e61e5260 100644 --- a/test/util/drm_utils_unit.js +++ b/test/util/drm_utils_unit.js @@ -143,6 +143,38 @@ describe('DrmUtils', () => { }); }); // describe('getCommonDrmInfos') + describe('isClearKeySystem', () => { + it('should return true for ClearKey', () => { + expect(shaka.drm.DrmUtils.isClearKeySystem( + 'org.w3.clearkey')).toBe(true); + }); + + it('should return false for non-ClearKey key systems', () => { + expect(shaka.drm.DrmUtils.isClearKeySystem( + 'com.widevine.alpha')).toBe(false); + expect(shaka.drm.DrmUtils.isClearKeySystem( + 'com.microsoft.playready')).toBe(false); + expect(shaka.drm.DrmUtils.isClearKeySystem( + 'com.apple.fps')).toBe(false); + }); + }); + + describe('isWidevineKeySystem', () => { + it('should return true for Widevine', () => { + expect(shaka.drm.DrmUtils.isWidevineKeySystem( + 'com.widevine.alpha')).toBe(true); + expect(shaka.drm.DrmUtils.isWidevineKeySystem( + 'com.widevine.alpha.anything')).toBe(true); + }); + + it('should return false for non-Widevine key systems', () => { + expect(shaka.drm.DrmUtils.isWidevineKeySystem( + 'com.microsoft.playready')).toBe(false); + expect(shaka.drm.DrmUtils.isWidevineKeySystem( + 'com.apple.fps')).toBe(false); + }); + }); + describe('isPlayReadyKeySystem', () => { it('should return true for MS & Chromecast PlayReady', () => { expect(shaka.util.DrmUtils.isPlayReadyKeySystem(