diff --git a/samples/drm/clearkey.html b/samples/drm/clearkey.html index 0811f6c34c..816be7ae6f 100644 --- a/samples/drm/clearkey.html +++ b/samples/drm/clearkey.html @@ -28,7 +28,7 @@ }; var video, player, - url = "https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_ClearKey.mpd"; + url = "https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p_ClearKey.mpd"; video = document.querySelector("video"); player = dashjs.MediaPlayer().create(); diff --git a/samples/drm/mpds/laurl.mpd b/samples/drm/mpds/laurl.mpd index ea198174d6..3793e7f4a5 100644 --- a/samples/drm/mpds/laurl.mpd +++ b/samples/drm/mpds/laurl.mpd @@ -1,7 +1,20 @@ + + + xmlns:dashif="https://dashif.org/" + xmlns:cenc="urn:mpeg:cenc:2013"> https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/ xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ARABRAFcAMABuAGsAdgBrAEEAawBpAFQATABpAGYAWABVAEkAUABpAFoAZwA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA= - https://drm-widevine-licensing.axtest.net/AcquireLicense + https://drm-playready-licensing.axtest.net/AcquireLicense https://drm-widevine-licensing.axtest.net/AcquireLicense @@ -45,20 +58,30 @@ xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ARABRAFcAMABuAGsAdgBrAEEAawBpAFQATABpAGYAWABVAEkAUABpAFoAZwA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA= - https://drm-widevine-licensing.axtest.net/AcquireLicense + https://drm-playready-licensing.axtest.net/AcquireLicense - AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQnrQFDeRLSAKTLifXUIPiZg== https://drm-widevine-licensing.axtest.net/AcquireLicense + AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQnrQFDeRLSAKTLifXUIPiZg== + duration="95232" + initialization="$RepresentationID$/init.mp4"/> + + + + + + \ No newline at end of file diff --git a/samples/drm/system-string-priority.html b/samples/drm/system-string-priority.html new file mode 100644 index 0000000000..de5e9bfd27 --- /dev/null +++ b/samples/drm/system-string-priority.html @@ -0,0 +1,119 @@ + + + + + DRM system string priority example + + + + + + + + + + + + + +
+
+
+ +
+
+
+ +
+
+
+
+
+

DRM system string priority example

+

This example shows how to specify the system string priority for the call to + requestMediaKeySystemAccess. For example, Playready might be supported + with the system strings "com.microsoft.playready.recommendation" and + "com.microsoft.playready".

+

For a detailed explanation on DRM playback in dash.js checkout the + Wiki.

+
+
+
+ +
+
+
+
+
+
+
+ +
+
+ + + + + + diff --git a/samples/samples.json b/samples/samples.json index 06bab83074..a9bddfd11c 100644 --- a/samples/samples.json +++ b/samples/samples.json @@ -412,6 +412,20 @@ "Audio" ] }, + { + "title": "Keysystem string priority", + "description": "This example shows how to specify the system string priority for the call to requestMediaKeySystemAccess. For example, Playready might be supported with the system strings \"com.microsoft.playready.recommendation\" and \"com.microsoft.playready\". ", + "href": "drm/system-string-priority.html", + "image": "lib/img/tos-1.jpg", + "labels": [ + "VoD", + "DRM", + "Widevine", + "Playready", + "Video", + "Audio" + ] + }, { "title": "License server via MPD", "description": "This example shows how to specify the license server url as part of the MPD using 'dashif:laurl'", diff --git a/src/streaming/constants/ProtectionConstants.js b/src/streaming/constants/ProtectionConstants.js index a47c1c0c6d..904ac9c217 100644 --- a/src/streaming/constants/ProtectionConstants.js +++ b/src/streaming/constants/ProtectionConstants.js @@ -40,6 +40,7 @@ class ProtectionConstants { this.CLEARKEY_KEYSTEM_STRING = 'org.w3.clearkey'; this.WIDEVINE_KEYSTEM_STRING = 'com.widevine.alpha'; this.PLAYREADY_KEYSTEM_STRING = 'com.microsoft.playready'; + this.PLAYREADY_RECOMMENDATION_KEYSTEM_STRING = 'com.microsoft.playready.recommendation'; this.INITIALIZATION_DATA_TYPE_CENC = 'cenc'; this.INITIALIZATION_DATA_TYPE_KEYIDS = 'keyids' this.INITIALIZATION_DATA_TYPE_WEBM = 'webm' diff --git a/src/streaming/protection/controllers/ProtectionController.js b/src/streaming/protection/controllers/ProtectionController.js index 74c60bf63a..b61be0c5b5 100644 --- a/src/streaming/protection/controllers/ProtectionController.js +++ b/src/streaming/protection/controllers/ProtectionController.js @@ -184,7 +184,8 @@ function ProtectionController(config) { const keySystemConfiguration = _getKeySystemConfiguration(supportedKS[i]); requestedKeySystems.push({ ks: supportedKS[i].ks, - configs: [keySystemConfiguration] + configs: [keySystemConfiguration], + protData: supportedKS[i].protData }); } @@ -193,7 +194,8 @@ function ProtectionController(config) { protectionModel.requestKeySystemAccess(requestedKeySystems) .then((event) => { keySystemAccess = event.data; - logger.info('DRM: KeySystem Access Granted (' + keySystemAccess.keySystem.systemString + ')! Selecting key system...'); + let selectedSystemString = keySystemAccess.mksa && keySystemAccess.mksa.selectedSystemString ? keySystemAccess.mksa.selectedSystemString : keySystemAccess.keySystem.systemString; + logger.info('DRM: KeySystem Access Granted for system string (' + selectedSystemString + ')! Selecting key system...'); return protectionModel.selectKeySystem(keySystemAccess); }) .then((keySystem) => { diff --git a/src/streaming/protection/models/ProtectionModel_21Jan2015.js b/src/streaming/protection/models/ProtectionModel_21Jan2015.js index e9fdc89a0b..a1ede63f4a 100644 --- a/src/streaming/protection/models/ProtectionModel_21Jan2015.js +++ b/src/streaming/protection/models/ProtectionModel_21Jan2015.js @@ -45,6 +45,11 @@ import KeyMessage from '../vo/KeyMessage'; import KeySystemAccess from '../vo/KeySystemAccess'; import ProtectionConstants from '../../constants/ProtectionConstants'; +const SYSTEM_STRING_PRIORITY = {}; +SYSTEM_STRING_PRIORITY[ProtectionConstants.PLAYREADY_KEYSTEM_STRING] = [ProtectionConstants.PLAYREADY_RECOMMENDATION_KEYSTEM_STRING, ProtectionConstants.PLAYREADY_KEYSTEM_STRING]; +SYSTEM_STRING_PRIORITY[ProtectionConstants.WIDEVINE_KEYSTEM_STRING] = [ProtectionConstants.WIDEVINE_KEYSTEM_STRING]; +SYSTEM_STRING_PRIORITY[ProtectionConstants.CLEARKEY_KEYSTEM_STRING] = [ProtectionConstants.CLEARKEY_KEYSTEM_STRING]; + function ProtectionModel_21Jan2015(config) { config = config || {}; @@ -153,6 +158,8 @@ function ProtectionModel_21Jan2015(config) { * @private */ function _requestKeySystemAccessInternal(ksConfigurations, idx, resolve, reject) { + + // In case requestMediaKeySystemAccess is not available we can not proceed and dispatch an error if (navigator.requestMediaKeySystemAccess === undefined || typeof navigator.requestMediaKeySystemAccess !== 'function') { const msg = 'Insecure origins are not allowed'; @@ -161,32 +168,75 @@ function ProtectionModel_21Jan2015(config) { return; } - const keySystem = ksConfigurations[idx].ks; + // If a systemStringPriority is defined by the application we use these values. Otherwise we use the default system string + // This is useful for DRM systems such as Playready for which multiple system strings are possible for instance com.microsoft.playready and com.microsoft.playready.recommendation + const protDataSystemStringPriority = ksConfigurations[idx].protData && ksConfigurations[idx].protData.systemStringPriority ? ksConfigurations[idx].protData.systemStringPriority : null; const configs = ksConfigurations[idx].configs; - let systemString = keySystem.systemString; + const currentKeySystem = ksConfigurations[idx].ks; + let systemString = currentKeySystem.systemString; - // Patch to support persistent licenses on Edge browser (see issue #2658) - if (systemString === ProtectionConstants.PLAYREADY_KEYSTEM_STRING && configs[0].persistentState === 'required') { - systemString += '.recommendation'; - } + // Use the default values in case no values are provided by the application + const systemStringsToApply = protDataSystemStringPriority ? protDataSystemStringPriority : SYSTEM_STRING_PRIORITY[systemString] ? SYSTEM_STRING_PRIORITY[systemString] : [systemString]; - navigator.requestMediaKeySystemAccess(systemString, configs) + // Check all the available system strings and the available configurations for support + _checkAccessForKeySystem(systemStringsToApply, configs) .then((mediaKeySystemAccess) => { const configuration = (typeof mediaKeySystemAccess.getConfiguration === 'function') ? mediaKeySystemAccess.getConfiguration() : null; - const keySystemAccess = new KeySystemAccess(keySystem, configuration); + const keySystemAccess = new KeySystemAccess(currentKeySystem, configuration); keySystemAccess.mksa = mediaKeySystemAccess; eventBus.trigger(events.KEY_SYSTEM_ACCESS_COMPLETE, { data: keySystemAccess }); resolve({ data: keySystemAccess }); }) - .catch((error) => { + .catch((e) => { if (idx + 1 < ksConfigurations.length) { _requestKeySystemAccessInternal(ksConfigurations, idx + 1, resolve, reject); } else { const errorMessage = 'Key system access denied! '; - eventBus.trigger(events.KEY_SYSTEM_ACCESS_COMPLETE, { error: errorMessage + error.message }); - reject({ error: errorMessage + error.message }); + eventBus.trigger(events.KEY_SYSTEM_ACCESS_COMPLETE, { error: errorMessage + e.message }); + reject({ error: errorMessage + e.message }); + } + }) + } + + /** + * For a specific key system: Iterate over the possible system strings and resolve once a valid configuration was found + * @param {array} systemStringsToApply + * @param {object} configs + * @return {Promise} + * @private + */ + function _checkAccessForKeySystem(systemStringsToApply, configs) { + return new Promise((resolve, reject) => { + _checkAccessForSystemStrings(systemStringsToApply, configs, 0, resolve, reject); + }) + } + + /** + * Recursively iterate over the possible system strings until a supported configuration is found or we ran out of options + * @param {array} systemStringsToApply + * @param {object} configs + * @param {number} idx + * @param {function} resolve + * @param {function} reject + * @private + */ + function _checkAccessForSystemStrings(systemStringsToApply, configs, idx, resolve, reject) { + const systemString = systemStringsToApply[idx]; + + logger.debug(`Requesting key system access for system string ${systemString}`); + + navigator.requestMediaKeySystemAccess(systemString, configs) + .then((mediaKeySystemAccess) => { + mediaKeySystemAccess.selectedSystemString = systemString; + resolve(mediaKeySystemAccess); + }) + .catch((e) => { + if (idx + 1 < systemStringsToApply.length) { + _checkAccessForSystemStrings(systemStringsToApply, configs, idx + 1, resolve, reject); + } else { + reject(e); } }); } diff --git a/test/functional/config/smokeVectors.json b/test/functional/config/smokeVectors.json index d9aa2f158f..e1a45ab6ee 100644 --- a/test/functional/config/smokeVectors.json +++ b/test/functional/config/smokeVectors.json @@ -37,6 +37,28 @@ "url": "https://dash.akamaized.net/dash264/TestCases/5a/nomor/1.mpd", "name": "Multiperiod with presentationTimeOffset", "description": "Multiperiod with presentationTimeOffset" + }, + { + "name": "1080p with PlayReady and Widevine DRM, single key", + "url": "https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd", + "protData": { + "com.widevine.alpha": { + "serverURL": "https://drm-widevine-licensing.axtest.net/AcquireLicense", + "httpRequestHeaders": { + "X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.4lWwW46k-oWcah8oN18LPj5OLS5ZU-_AQv7fe0JhNjA" + }, + "httpTimeout": 5000 + }, + "com.microsoft.playready": { + "serverURL": "https://drm-playready-licensing.axtest.net/AcquireLicense", + "httpRequestHeaders": { + "X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.4lWwW46k-oWcah8oN18LPj5OLS5ZU-_AQv7fe0JhNjA" + }, + "httpTimeout": 5000 + } + }, + "moreInfo": "https://github.com/Axinom/dash-test-vectors", + "provider": "axinom" } ] },