Skip to content

Commit bf63f40

Browse files
gorkemgdsilhavyShikiSeiren
authored
Add support for Certurl element (Dash-Industry-Forum#4876)
* CertUrl support added Co-authored-by: Daniel Silhavy <daniel.silhavy@fokus.fraunhofer.de> Co-authored-by: bjoern altmann <bjoern.altmann@fokus.fraunhofer.de>
1 parent 1679fa9 commit bf63f40

File tree

14 files changed

+607
-29
lines changed

14 files changed

+607
-29
lines changed

index.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,11 @@ export class CMCDParameters extends DescriptorType {
725725
version: number;
726726
}
727727

728+
export interface CertUrlDescriptor {
729+
url: string;
730+
certType: string | null;
731+
}
732+
728733
export class ContentProtection extends DescriptorType {
729734
cencDefaultKid: any;
730735
keyId: any;
@@ -734,6 +739,7 @@ export class ContentProtection extends DescriptorType {
734739
ref: any;
735740
refId: any;
736741
robustness: any;
742+
certUrls: CertUrlDescriptor[];
737743

738744
init(data: any): void;
739745

@@ -868,6 +874,7 @@ export interface IContentProtection {
868874
"cenc:default_KID"?: string;
869875
value?: string;
870876
pssh?: IPssh | IPssh[];
877+
certUrls?: CertUrlDescriptor[];
871878
}
872879

873880
export interface IPssh {
@@ -1708,6 +1715,7 @@ export class MediaPlayerSettingClass {
17081715
ignoreEmeEncryptedEvent?: boolean,
17091716
detectPlayreadyMessageFormat?: boolean,
17101717
ignoreKeyStatuses?: boolean,
1718+
certificateRetryAttempts?: number,
17111719
},
17121720
buffer?: {
17131721
enableSeekDecorrelationFix?: boolean,
@@ -4668,6 +4676,9 @@ export interface ProtectionData {
46684676

46694677
/** Priority level of the key system to be selected (0 is the highest prority, -1 for undefined priority) */
46704678
priority?: number;
4679+
4680+
/** Optional certificate URLs; entries may be raw strings or manifest-parsed objects */
4681+
certUrls?: Array<string | CertUrlDescriptor | { __text?: string; '@certType'?: string; certType?: string; url?: string }>;
46714682
}
46724683

46734684
export interface SessionToken {

samples/dash-if-reference-player/app/main.js

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
185185
licenseServerUrl: '',
186186
httpRequestHeaders: {},
187187
serverCertificate: '',
188+
serverCertificateURLs: [],
188189
httpTimeout: 5000,
189190
priority: 1,
190191
audioRobustness: '',
@@ -198,6 +199,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
198199
licenseServerUrl: '',
199200
httpRequestHeaders: {},
200201
serverCertificate: '',
202+
serverCertificateURLs: [],
201203
httpTimeout: 5000,
202204
priority: 0,
203205
audioRobustness: '',
@@ -211,6 +213,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
211213
licenseServerUrl: '',
212214
httpRequestHeaders: {},
213215
serverCertificate: '',
216+
serverCertificateURLs: [],
214217
httpTimeout: 5000,
215218
kid: '',
216219
key: '',
@@ -225,6 +228,12 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
225228

226229
$scope.clearkeyRequestHeaders = [];
227230

231+
$scope.playreadyServerCertificateURLs = '';
232+
233+
$scope.widevineServerCertificateURLs = '';
234+
235+
$scope.clearkeyServerCertificateURLs = '';
236+
228237
$scope.additionalClearkeyPairs = [];
229238

230239
$scope.protData = {};
@@ -1036,6 +1045,9 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
10361045
$scope.playreadyRequestHeaders = [];
10371046
$scope.widevineRequestHeaders = [];
10381047
$scope.clearkeyRequestHeaders = [];
1048+
$scope.playreadyServerCertificateURLs = '';
1049+
$scope.widevineServerCertificateURLs = '';
1050+
$scope.clearkeyServerCertificateURLs = '';
10391051
$scope.clearkeys = [];
10401052
$scope.additionalClearkeyPairs = [];
10411053
}
@@ -1219,7 +1231,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
12191231
$scope.protData = $scope.protectionData;
12201232
}
12211233
// Execute if setDrm() has been called with manually entered values
1222-
else if ($scope.protectionData !== {}) {
1234+
else if ($scope.protectionData && Object.keys($scope.protectionData).length) {
12231235
$scope.setDrm();
12241236
$scope.protData = $scope.protectionData;
12251237
} else if ($scope.drmLicenseURL !== '' && $scope.drmKeySystem !== '') {
@@ -1466,6 +1478,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
14661478
let protectionData = {};
14671479

14681480
$scope.handleRequestHeaders();
1481+
$scope.handleServerCertificateURLs();
14691482
$scope.handleClearkeys();
14701483

14711484
for (let input of drmInputs) {
@@ -1474,7 +1487,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
14741487
// Check if the provided DRM is Clearkey and whether KID=KEY or LicenseServer + Header is selected; Default is KID=KEY
14751488
if (input.hasOwnProperty('inputMode') && input.inputMode === 'kidKey') {
14761489
//Check clearkeys has at least one entry
1477-
if (input.clearkeys !== {}) {
1490+
if (input.clearkeys && Object.keys(input.clearkeys).length) {
14781491
// Check if priority is enabled
14791492
protectionData[input.drmKeySystem] = {
14801493
'clearkeys': {},
@@ -1499,11 +1512,18 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
14991512
key !== 'priority' &&
15001513
key !== 'kid' &&
15011514
key !== 'key' &&
1502-
key !== 'inputMode') {
1515+
key !== 'serverCertificateURLs' &&
1516+
key !== 'inputMode'&&
1517+
key !== 'isCustomRobustness') {
15031518
protectionData[input.drmKeySystem][key] = input[key];
15041519
}
15051520
}
15061521

1522+
const certUrls = Array.isArray(input.serverCertificateURLs) ? input.serverCertificateURLs.filter(Boolean) : [];
1523+
if (certUrls.length) {
1524+
protectionData[input.drmKeySystem].certUrls = certUrls;
1525+
}
1526+
15071527
if (!angular.equals(input.httpRequestHeaders, {})) {
15081528
protectionData[input.drmKeySystem]['httpRequestHeaders'] = input.httpRequestHeaders;
15091529
}
@@ -1546,7 +1566,9 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
15461566
key !== 'drmKeySystem' &&
15471567
key !== 'licenseServerUrl' &&
15481568
key !== 'httpRequestHeaders' &&
1549-
key !== 'priority') {
1569+
key !== 'priority' &&
1570+
key !== 'serverCertificateURLs' &&
1571+
key !== 'isCustomRobustness') {
15501572
protectionData[input.drmKeySystem][key] = input[key];
15511573
}
15521574
}
@@ -1556,6 +1578,11 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
15561578
protectionData[input.drmKeySystem]['httpRequestHeaders'] = input.httpRequestHeaders;
15571579
}
15581580

1581+
const certUrls = Array.isArray(input.serverCertificateURLs) ? input.serverCertificateURLs.filter(Boolean) : [];
1582+
if (certUrls.length) {
1583+
protectionData[input.drmKeySystem].certUrls = certUrls;
1584+
}
1585+
15591586
if (input.audioRobustness) {
15601587
protectionData[input.drmKeySystem]['audioRobustness'] = input.audioRobustness;
15611588
}
@@ -1602,6 +1629,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
16021629
kid: '',
16031630
key: ''
16041631
})
1632+
break;
16051633
}
16061634
}
16071635

@@ -1641,6 +1669,12 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
16411669
}
16421670
}
16431671

1672+
$scope.handleServerCertificateURLs = function () {
1673+
$scope.drmPlayready.serverCertificateURLs = $scope.playreadyServerCertificateURLs.split(/\s+/);
1674+
$scope.drmWidevine.serverCertificateURLs = $scope.widevineServerCertificateURLs.split(/\s+/);
1675+
$scope.drmClearkey.serverCertificateURLs = $scope.clearkeyServerCertificateURLs.split(/\s+/);
1676+
}
1677+
16441678
/** Handle multiple clearkeys */
16451679
$scope.handleClearkeys = function () {
16461680
// Initialize with empty
@@ -1655,7 +1689,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
16551689
$scope.drmClearkey.clearkeys[clearkey.kid] = clearkey.key;
16561690
}
16571691
// if clearkey property is empty, alert
1658-
if ($scope.additionalClearkeyPairs === {}) {
1692+
if (!Object.keys($scope.drmClearkey.clearkeys).length) {
16591693
alert('You must specify at least one KID=KEY pair!');
16601694
}
16611695
}
@@ -1677,10 +1711,17 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
16771711
value: protectionData[data]['httpRequestHeaders'][header]
16781712
});
16791713
}
1714+
const playreadyCertUrls = protectionData[data].certUrls || protectionData[data].serverCertificateURLs;
1715+
if (Array.isArray(playreadyCertUrls) && playreadyCertUrls.length) {
1716+
$scope.drmPlayready.serverCertificateURLs = playreadyCertUrls;
1717+
$scope.playreadyServerCertificateURLs = playreadyCertUrls.join(' ');
1718+
}
16801719
// Add any additional parameters
16811720
for (let parameter in protectionData[data]) {
16821721
if (parameter !== 'serverURL' &&
1683-
parameter !== 'httpRequestHeaders') {
1722+
parameter !== 'httpRequestHeaders' &&
1723+
parameter !== 'certUrls' &&
1724+
parameter !== 'serverCertificateURLs') {
16841725
$scope.drmPlayready[parameter] = protectionData[data][parameter];
16851726
}
16861727
}
@@ -1699,10 +1740,17 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
16991740
value: protectionData[data]['httpRequestHeaders'][header]
17001741
});
17011742
}
1743+
const widevineCertUrls = protectionData[data].certUrls || protectionData[data].serverCertificateURLs;
1744+
if (Array.isArray(widevineCertUrls) && widevineCertUrls.length) {
1745+
$scope.drmWidevine.serverCertificateURLs = widevineCertUrls;
1746+
$scope.widevineServerCertificateURLs = widevineCertUrls.join(' ');
1747+
}
17021748
// Add any additional parameters
17031749
for (let parameter in protectionData[data]) {
17041750
if (parameter !== 'serverURL' &&
1705-
parameter !== 'httpRequestHeaders') {
1751+
parameter !== 'httpRequestHeaders' &&
1752+
parameter !== 'certUrls' &&
1753+
parameter !== 'serverCertificateURLs') {
17061754
$scope.drmWidevine[parameter] = protectionData[data][parameter];
17071755
}
17081756
}
@@ -1731,15 +1779,15 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
17311779
// Handle clearkey data if specified using KID=KEY.
17321780
else {
17331781
let first = true;
1734-
if (protectionData[data]['clearkeys'] !== {}) {
1782+
if (protectionData[data]['clearkeys'] && Object.keys(protectionData[data]['clearkeys']).length) {
17351783
for (let kid in protectionData[data]['clearkeys']) {
17361784
// For the first KID=Key pair, set drmClearkey properties so that it shows in the main text boxes
17371785
if (first === true) {
17381786
$scope.drmClearkey.kid = kid;
17391787
$scope.drmClearkey.key = protectionData[data]['clearkeys'][kid];
17401788
delete protectionData[data]['clearkeys'][kid];
17411789
first = false;
1742-
} else if (protectionData[data]['clearkeys'] !== {}) {
1790+
} else if (protectionData[data]['clearkeys'] && Object.keys(protectionData[data]['clearkeys']).length) {
17431791
$scope.additionalClearkeyPairs.push({
17441792
id: $scope.additionalClearkeyPairs.length + 1,
17451793
kid: kid,
@@ -1749,11 +1797,18 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
17491797
}
17501798
}
17511799
}
1800+
const clearkeyCertUrls = protectionData[data].certUrls || protectionData[data].serverCertificateURLs;
1801+
if (Array.isArray(clearkeyCertUrls) && clearkeyCertUrls.length) {
1802+
$scope.drmClearkey.serverCertificateURLs = clearkeyCertUrls;
1803+
$scope.clearkeyServerCertificateURLs = clearkeyCertUrls.join(' ');
1804+
}
17521805
// Add any additional parameters
17531806
for (let parameter in protectionData[data]) {
17541807
if (parameter !== 'serverURL' &&
17551808
parameter !== 'httpRequestHeaders' &&
1756-
parameter !== 'clearkeys') {
1809+
parameter !== 'clearkeys' &&
1810+
parameter !== 'certUrls' &&
1811+
parameter !== 'serverCertificateURLs') {
17571812
$scope.drmClearkey[parameter] = protectionData[data][parameter];
17581813
}
17591814
}
@@ -1990,7 +2045,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
19902045
}
19912046

19922047
for (var settingCategory of Object.keys(settingsObject)) {
1993-
if (settingsObject !== {} &&
2048+
if (settingsObject[settingCategory] &&
19942049
(settingCategory === 'playready' ||
19952050
settingCategory === 'widevine' ||
19962051
settingCategory === 'clearkey') &&
@@ -2013,7 +2068,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
20132068

20142069
for (var drm in drmObject) {
20152070
if (drmObject[drm].hasOwnProperty('inputMode') && drmObject[drm].inputMode === 'kidKey') {
2016-
if (drmObject[drm].clearkeys !== {}) {
2071+
if (drmObject[drm].clearkeys && Object.keys(drmObject[drm].clearkeys).length) {
20172072
queryProtectionData[drmObject[drm].drmKeySystem] = {
20182073
'clearkeys': {},
20192074
'priority': 0
@@ -2043,7 +2098,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
20432098
}
20442099
}
20452100

2046-
if (drmObject[drm].httpRequestHeaders !== {}) {
2101+
if (drmObject[drm].httpRequestHeaders && Object.keys(drmObject[drm].httpRequestHeaders).length) {
20472102
queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders;
20482103
}
20492104
} else {
@@ -2057,7 +2112,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
20572112
'serverURL': decodeURIComponent(drmObject[drm].licenseServerUrl),
20582113
'priority': parseInt(drmObject[drm].priority)
20592114
}
2060-
if (drmObject[drm].httpRequestHeaders !== {})
2115+
if (drmObject[drm].httpRequestHeaders && Object.keys(drmObject[drm].httpRequestHeaders).length)
20612116
queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders;
20622117

20632118
} else {
@@ -2078,7 +2133,7 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
20782133
}
20792134

20802135
// Only set request header if any have been specified
2081-
if (drmObject[drm].httpRequestHeaders !== {}) {
2136+
if (drmObject[drm].httpRequestHeaders && Object.keys(drmObject[drm].httpRequestHeaders).length) {
20822137
queryProtectionData[drmObject[drm].drmKeySystem]['httpRequestHeaders'] = drmObject[drm].httpRequestHeaders;
20832138
}
20842139
}

samples/dash-if-reference-player/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@
364364
<input type="text" class="form-control" placeholder=""
365365
ng-model="drmPlayready.serverCertificate">
366366

367+
<label class="options-label" data-toggle="tooltip" data-placement="right"
368+
title="Enter one or more server certificate URLs separated by a white space.">Server Certifacte URLs</label>
369+
<input type="text" class="form-control" placeholder="" ng-model="playreadyServerCertificateURLs" ng-change="handleServerCertificateURLs()">
370+
367371
<label class="options-label">HTTP Timeout</label>
368372
<input type="text" class="form-control" placeholder="" ng-model="drmPlayready.httpTimeout">
369373

@@ -467,6 +471,10 @@
467471
<label class="options-label">Server Certificate</label>
468472
<input type="text" class="form-control" placeholder="" ng-model="drmWidevine.serverCertificate">
469473

474+
<label class="options-label" data-toggle="tooltip" data-placement="right"
475+
title="Enter one or more server certificate URLs separated by a white space.">Server Certifacte URLs</label>
476+
<input type="text" class="form-control" placeholder="" ng-model="widevineServerCertificateURLs" ng-change="handleServerCertificateURLs()">
477+
470478
<label class="options-label">HTTP Timeout</label>
471479
<input type="text" class="form-control" placeholder="" ng-model="drmWidevine.httpTimeout">
472480

@@ -584,6 +592,10 @@
584592
<label class="options-label">Server Certificate</label>
585593
<input type="text" class="form-control" placeholder="" ng-model="drmClearkey.serverCertificate">
586594

595+
<label class="options-label" data-toggle="tooltip" data-placement="right"
596+
title="Enter one or more server certificate URLs separated by a white space.">Server Certifacte URLs</label>
597+
<input type="text" class="form-control" placeholder="" ng-model="clearkeyServerCertificateURLs" ng-change="handleServerCertificateURLs()">
598+
587599
<label class="options-label">HTTP Timeout</label>
588600
<input type="text" class="form-control" placeholder="" ng-model="drmClearkey.httpTimeout">
589601

src/core/Settings.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ import SwitchRequest from '../streaming/rules/SwitchRequest.js';
109109
* keepProtectionMediaKeysMaximumOpenSessions: -1,
110110
* ignoreEmeEncryptedEvent: false,
111111
* detectPlayreadyMessageFormat: true,
112-
* ignoreKeyStatuses: false
112+
* ignoreKeyStatuses: false,
113+
* certificateRetryAttempts: 2
113114
* },
114115
* buffer: {
115116
* enableSeekDecorrelationFix: false,
@@ -724,6 +725,9 @@ import SwitchRequest from '../streaming/rules/SwitchRequest.js';
724725
*
725726
* @property {boolean} [ignoreKeyStatuses=false]
726727
* If set to true the player will ignore the status of a key and try to play the corresponding track regardless whether the key is usable or not.
728+
*
729+
* @property {number} [certificateRetryAttempts=2]
730+
* Number of retry attempts per certificate URL before moving to the next candidate when fetching DRM server certificates via Certurl elements.
727731
*/
728732

729733
/**
@@ -1199,7 +1203,8 @@ function Settings() {
11991203
keepProtectionMediaKeysMaximumOpenSessions: -1,
12001204
ignoreEmeEncryptedEvent: false,
12011205
detectPlayreadyMessageFormat: true,
1202-
ignoreKeyStatuses: false
1206+
ignoreKeyStatuses: false,
1207+
certificateRetryAttempts: 2
12031208
},
12041209
buffer: {
12051210
enableSeekDecorrelationFix: false,

0 commit comments

Comments
 (0)