Skip to content

Commit

Permalink
Add support for multiple system strings for a single key system (#3859)
Browse files Browse the repository at this point in the history
* Add support for multiple system strings for a single key system

* Add default system string priority

* Change description text of system-string-priority.html

* Fix wrong link in Playready DRM demo

* Update Clearkey sample

* Update log output for selected system string

* Add DRM stream to list of DRM smoke vectors
  • Loading branch information
dsilhavy authored Jan 31, 2022
1 parent e8ed11d commit 2e4661a
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 19 deletions.
2 changes: 1 addition & 1 deletion samples/drm/clearkey.html
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
33 changes: 28 additions & 5 deletions samples/drm/mpds/laurl.mpd
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Version information:
Axinom.MediaProcessing v3.0.0 targeting General Purpose Media Format specification v7
ffmpeg version N-81423-g61fac0e Copyright (c) 2000-2016 the FFmpeg developers
x265 [info]: HEVC encoder version 2.0+12-49a0d1176aef5bc6
x264 0.148.2705 3f5ed56
MP4Box - GPAC version 0.6.2-DEV-rev683-g7b29fbe-master
MediaInfoLib - v0.7.87
For more info about this video, see https://github.com/Axinom/dash-test-vectors
-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" minBufferTime="PT1.500S" type="static"
mediaPresentationDuration="PT0H12M14.000S" maxSegmentDuration="PT0H0M4.000S"
profiles="urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash264"
xmlns:cenc="urn:mpeg:cenc:2013" xmlns:dashif="https://dashif.org/">
xmlns:dashif="https://dashif.org/"
xmlns:cenc="urn:mpeg:cenc:2013">
<Period duration="PT0H12M14.000S">
<BaseURL>https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/</BaseURL>
<AdaptationSet segmentAlignment="true" group="1" maxWidth="1920" maxHeight="1080" maxFrameRate="24" par="16:9"
Expand All @@ -15,7 +28,7 @@
<pro xmlns="urn:microsoft:playready">
xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ARABRAFcAMABuAGsAdgBrAEEAawBpAFQATABpAGYAWABVAEkAUABpAFoAZwA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=
</pro>
<dashif:Laurl>https://drm-widevine-licensing.axtest.net/AcquireLicense</dashif:Laurl>
<dashif:Laurl>https://drm-playready-licensing.axtest.net/AcquireLicense</dashif:Laurl>
</ContentProtection>
<ContentProtection value="Widevine" schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<dashif:Laurl>https://drm-widevine-licensing.axtest.net/AcquireLicense</dashif:Laurl>
Expand Down Expand Up @@ -45,20 +58,30 @@
<pro xmlns="urn:microsoft:playready">
xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ARABRAFcAMABuAGsAdgBrAEEAawBpAFQATABpAGYAWABVAEkAUABpAFoAZwA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=
</pro>
<dashif:Laurl>https://drm-widevine-licensing.axtest.net/AcquireLicense</dashif:Laurl>
<dashif:Laurl>https://drm-playready-licensing.axtest.net/AcquireLicense</dashif:Laurl>
</ContentProtection>
<ContentProtection value="Widevine" schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQnrQFDeRLSAKTLifXUIPiZg==</cenc:pssh>
<dashif:Laurl>https://drm-widevine-licensing.axtest.net/AcquireLicense</dashif:Laurl>
<cenc:pssh>AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQnrQFDeRLSAKTLifXUIPiZg==</cenc:pssh>
</ContentProtection>
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
<SegmentTemplate timescale="24000" media="$RepresentationID$/$Number%04d$.m4s" startNumber="1"
duration="95232" initialization="$RepresentationID$/init.mp4"/>
duration="95232"
initialization="$RepresentationID$/init.mp4"/>
<Representation id="15" mimeType="audio/mp4" codecs="mp4a.40.29" audioSamplingRate="48000" startWithSAP="1"
bandwidth="134642">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
value="2"/>
</Representation>
</AdaptationSet>
<AdaptationSet segmentAlignment="true" group="3" lang="en">
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="subtitle"/>
<SegmentTemplate timescale="1000" media="$RepresentationID$/$Number%04d$.m4s" startNumber="1"
duration="4000"
initialization="$RepresentationID$/init.mp4"/>
<Representation id="18" mimeType="application/mp4" codecs="wvtt" startWithSAP="1"
bandwidth="428"></Representation>
</AdaptationSet>
</Period>
</MPD>
119 changes: 119 additions & 0 deletions samples/drm/system-string-priority.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DRM system string priority example</title>

<script src="../../dist/dash.all.debug.js"></script>

<!-- Bootstrap core CSS -->
<link href="../lib/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="../lib/main.css" rel="stylesheet">

<style>
video {
width: 640px;
height: 360px;
}
</style>

<script class="code">
function init() {
var protData = {
'com.widevine.alpha': {
'serverURL': 'https://drm-widevine-licensing.axtest.net/AcquireLicense',
'systemStringPriority': ['com.widevine.something', 'com.widevine.alpha'],
'priority': 2,
'httpRequestHeaders': {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
}
},
'com.microsoft.playready': {
'serverURL': 'https://drm-playready-licensing.axtest.net/AcquireLicense',
'systemStringPriority': ['com.microsoft.playready.something', 'com.microsoft.playready.recommendation', 'com.microsoft.playready.hardware', 'com.microsoft.playready'],
'priority': 1,
'httpRequestHeaders': {
'X-AxDRM-Message': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU'
}
}
};
var video,
player,
url = 'https://media.axprod.net/TestVectors/v7-MultiDRM-SingleKey/Manifest_1080p.mpd';

video = document.querySelector('video');
player = dashjs.MediaPlayer().create();
player.updateSettings({
debug: {
logLevel: 5
}
});
player.initialize(video, url, true);
player.setProtectionData(protData);
}

function check() {
if (location.protocol === 'http:' && location.hostname !== 'localhost') {
var out = 'This page has been loaded under http. This might result in the EME APIs not being available to the player and any DRM-protected content will fail to play. ' +
'If you wish to test manifest URLs that require EME support, then <a href=\'https:' + window.location.href.substring(window.location.protocol.length) + '\'>reload this page under https</a>.'
var div = document.getElementById('http-warning');
div.innerHTML = out;
div.style.display = ''
}
}
</script>
</head>
<body>

<main>
<div class="container py-4">
<header class="pb-3 mb-4 border-bottom">
<img class=""
src="../lib/img/dashjs-logo.png"
width="200">
</header>
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger" role="alert" style="display: none" id="http-warning">

</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="h-100 p-5 bg-light border rounded-3">
<h3>DRM system string priority example</h3>
<p>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". </p>
<p>For a detailed explanation on DRM playback in dash.js checkout the
<a href="https://github.com/Dash-Industry-Forum/dash.js/wiki/Digital-Rights-Management-(DRM)-and-license-acquisition"
target="_blank">Wiki</a>.</p>
</div>
</div>
<div class="col-md-8">
<video controls="true"></video>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="code-output"></div>
</div>
</div>
<footer class="pt-3 mt-4 text-muted border-top">
&copy; DASH-IF
</footer>
</div>
</main>


<script>
document.addEventListener('DOMContentLoaded', function () {
check();
init();
});
</script>
<script src="../highlighter.js"></script>
</body>
</html>
14 changes: 14 additions & 0 deletions samples/samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -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'",
Expand Down
1 change: 1 addition & 0 deletions src/streaming/constants/ProtectionConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
6 changes: 4 additions & 2 deletions src/streaming/protection/controllers/ProtectionController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
}

Expand All @@ -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) => {
Expand Down
72 changes: 61 additions & 11 deletions src/streaming/protection/models/ProtectionModel_21Jan2015.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 || {};
Expand Down Expand Up @@ -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';
Expand All @@ -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);
}
});
}
Expand Down
Loading

0 comments on commit 2e4661a

Please sign in to comment.