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"
}
]
},