From 8082d4407917d7efb0f8dfb8d9cb969e8a7b7ebb Mon Sep 17 00:00:00 2001 From: Matt Stevens Date: Tue, 19 Apr 2022 11:59:59 +0100 Subject: [PATCH] fix: Prevent exception when PlayReady CDM returns UTF-8 unwrapped message --- src/core/Settings.js | 10 ++++++++-- src/streaming/protection/Protection.js | 2 +- .../controllers/ProtectionKeyController.js | 7 ++++++- .../protection/drm/KeySystemPlayReady.js | 18 ++++++++++++++++++ ...eaming.protection.drm.KeySystemPlayReady.js | 7 ++++++- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/core/Settings.js b/src/core/Settings.js index 2961cd5711..8c5a4b611b 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -83,7 +83,9 @@ import Events from './events/Events'; * useSuggestedPresentationDelay: true * }, * protection: { - * keepProtectionMediaKeys: false + * keepProtectionMediaKeys: false, + * ignoreEmeEncryptedEvent: false, + * detectPlayreadyMessageFormat: true, * }, * buffer: { * enableSeekDecorrelationFix: true, @@ -513,6 +515,9 @@ import Events from './events/Events'; * If true, the ProtectionController and then created MediaKeys and MediaKeySessions will be preserved during the MediaPlayer lifetime. * @property {boolean} ignoreEmeEncryptedEvent * If set to true the player will ignore "encrypted" and "needkey" events thrown by the EME. + * + * @property {boolean} detectPlayreadyMessageFormat + * If set to true the player will use the raw unwrapped message from the Playready CDM */ /** @@ -775,7 +780,8 @@ function Settings() { }, protection: { keepProtectionMediaKeys: false, - ignoreEmeEncryptedEvent: false + ignoreEmeEncryptedEvent: false, + detectPlayreadyMessageFormat: true, }, buffer: { enableSeekDecorrelationFix: false, diff --git a/src/streaming/protection/Protection.js b/src/streaming/protection/Protection.js index f97217a677..8b3044d991 100644 --- a/src/streaming/protection/Protection.js +++ b/src/streaming/protection/Protection.js @@ -116,7 +116,7 @@ function Protection() { let controller = null; const protectionKeyController = ProtectionKeyController(context).getInstance(); - protectionKeyController.setConfig({ debug: config.debug, BASE64: config.BASE64 }); + protectionKeyController.setConfig({ debug: config.debug, BASE64: config.BASE64, settings: config.settings }); protectionKeyController.initialize(); let protectionModel = _getProtectionModel(config); diff --git a/src/streaming/protection/controllers/ProtectionKeyController.js b/src/streaming/protection/controllers/ProtectionKeyController.js index 0b4ea13872..ee7f1d808d 100644 --- a/src/streaming/protection/controllers/ProtectionKeyController.js +++ b/src/streaming/protection/controllers/ProtectionKeyController.js @@ -53,6 +53,7 @@ function ProtectionKeyController() { logger, keySystems, BASE64, + settings, clearkeyKeySystem, clearkeyW3CKeySystem; @@ -67,6 +68,10 @@ function ProtectionKeyController() { if (config.BASE64) { BASE64 = config.BASE64; } + + if(config.settings) { + settings = config.settings + } } function initialize() { @@ -75,7 +80,7 @@ function ProtectionKeyController() { let keySystem; // PlayReady - keySystem = KeySystemPlayReady(context).getInstance({BASE64: BASE64}); + keySystem = KeySystemPlayReady(context).getInstance({BASE64: BASE64, settings: settings}); keySystems.push(keySystem); // Widevine diff --git a/src/streaming/protection/drm/KeySystemPlayReady.js b/src/streaming/protection/drm/KeySystemPlayReady.js index e3ded4bee1..968d278b38 100644 --- a/src/streaming/protection/drm/KeySystemPlayReady.js +++ b/src/streaming/protection/drm/KeySystemPlayReady.js @@ -49,6 +49,7 @@ function KeySystemPlayReady(config) { let instance; let messageFormat = 'utf-16'; const BASE64 = config.BASE64; + const settings = config.settings; function checkConfig() { if (!BASE64 || !BASE64.hasOwnProperty('decodeArray') || !BASE64.hasOwnProperty('decodeArray') ) { @@ -61,6 +62,15 @@ function KeySystemPlayReady(config) { xmlDoc; const headers = {}; const parser = new DOMParser(); + + if (settings && settings.get().streaming.protection.detectPlayreadyMessageFormat) { + // If message format configured/defaulted to utf-16 AND number of bytes is odd, assume 'unwrapped' raw CDM message. + if (messageFormat === 'utf-16' && message && message.byteLength % 2 === 1) { + headers['Content-Type'] = 'text/xml; charset=utf-8'; + return headers; + } + } + const dataview = (messageFormat === 'utf-16') ? new Uint16Array(message) : new Uint8Array(message); msg = String.fromCharCode.apply(null, dataview); @@ -89,6 +99,14 @@ function KeySystemPlayReady(config) { function getLicenseRequestFromMessage(message) { let licenseRequest = null; const parser = new DOMParser(); + + if (settings && settings.get().streaming.protection.detectPlayreadyMessageFormat) { + // If message format configured/defaulted to utf-16 AND number of bytes is odd, assume 'unwrapped' raw CDM message. + if (messageFormat === 'utf-16' && message && message.byteLength % 2 === 1) { + return message; + } + } + const dataview = (messageFormat === 'utf-16') ? new Uint16Array(message) : new Uint8Array(message); checkConfig(); diff --git a/test/unit/streaming.protection.drm.KeySystemPlayReady.js b/test/unit/streaming.protection.drm.KeySystemPlayReady.js index a42734db8b..0d22b29092 100644 --- a/test/unit/streaming.protection.drm.KeySystemPlayReady.js +++ b/test/unit/streaming.protection.drm.KeySystemPlayReady.js @@ -1,16 +1,21 @@ import KeySystemPlayReady from '../../src/streaming/protection/drm/KeySystemPlayReady.js'; import BASE64 from '../../externals/base64'; +import Settings from "../../src/core/Settings"; const expect = require('chai').expect; const jsdom = require('jsdom').JSDOM; describe('KeySystemPlayready', function () { + let context = {}; + let settings = Settings(context).getInstance(); let keySystem, DOMParser; let cdmData = null; + + const protData = { cdmData: '2lfuDn3JoEo0dM324cA5tSv1gNNw65mgysBqNJqtxGUk7ShUOE03N6LK0cryu2roCQtDghmF7cC6xyt1WTA86CmrUNFRjo1tcxQtTVEW9Xw68pH7/yU2GbtK4zbctx49sffi4fYy8fGEUB5079CesBONxoKli5j2ADM8CWz93a5mYegZWraOq3EH0nvwvRXZ' }; @@ -50,7 +55,7 @@ describe('KeySystemPlayready', function () { describe('Well initialized', () => { beforeEach(function () { - keySystem = KeySystemPlayReady(context).getInstance({BASE64: BASE64}); + keySystem = KeySystemPlayReady(context).getInstance({BASE64: BASE64, settings: settings}); if (typeof DOMParser === 'undefined') { global.DOMParser = new jsdom().window.DOMParser; }