Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature clearkey by server #3381

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/streaming/protection/CommonEncryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

/**
* @class
* @ignore
*/
/**
* @class
* @ignore
*/
class CommonEncryption {
/**
* Find and return the ContentProtection element in the given array
Expand All @@ -47,7 +47,7 @@ class CommonEncryption {
for (let i = 0; i < cpArray.length; ++i) {
let cp = cpArray[i];
if (cp.schemeIdUri.toLowerCase() === 'urn:mpeg:dash:mp4protection:2011' &&
cp.value.toLowerCase() === 'cenc')
(cp.value.toLowerCase() === 'cenc' || cp.value.toLowerCase() === 'cbcs'))
retVal = cp;
}
return retVal;
Expand Down Expand Up @@ -106,7 +106,7 @@ class CommonEncryption {
if ('pssh' in cpData) {

// Remove whitespaces and newlines from pssh text
cpData.pssh.__text = cpData.pssh.__text.replace(/\r?\n|\r/g,'').replace(/\s+/g,'');
cpData.pssh.__text = cpData.pssh.__text.replace(/\r?\n|\r/g, '').replace(/\s+/g, '');

return BASE64.decodeArray(cpData.pssh.__text).buffer;
}
Expand Down
64 changes: 45 additions & 19 deletions src/streaming/protection/controllers/ProtectionController.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,18 @@ function ProtectionController(config) {
try {
protectionModel.createKeySession(initDataForKS, protData, getSessionType(keySystem), cdmData);
} catch (error) {
eventBus.trigger(events.KEY_SESSION_CREATED, {data: null, error: new DashJSError(ProtectionErrors.KEY_SESSION_CREATED_ERROR_CODE, ProtectionErrors.KEY_SESSION_CREATED_ERROR_MESSAGE + error.message)});
eventBus.trigger(events.KEY_SESSION_CREATED, {
data: null,
error: new DashJSError(ProtectionErrors.KEY_SESSION_CREATED_ERROR_CODE, ProtectionErrors.KEY_SESSION_CREATED_ERROR_MESSAGE + error.message)
});
}
} else if (initData) {
protectionModel.createKeySession(initData, protData, getSessionType(keySystem), cdmData);
} else {
eventBus.trigger(events.KEY_SESSION_CREATED, {data: null, error: new DashJSError(ProtectionErrors.KEY_SESSION_CREATED_ERROR_CODE, ProtectionErrors.KEY_SESSION_CREATED_ERROR_MESSAGE + 'Selected key system is ' + (keySystem ? keySystem.systemString : null) + '. needkey/encrypted event contains no initData corresponding to that key system!')});
eventBus.trigger(events.KEY_SESSION_CREATED, {
data: null,
error: new DashJSError(ProtectionErrors.KEY_SESSION_CREATED_ERROR_CODE, ProtectionErrors.KEY_SESSION_CREATED_ERROR_MESSAGE + 'Selected key system is ' + (keySystem ? keySystem.systemString : null) + '. needkey/encrypted event contains no initData corresponding to that key system!')
});
}
}

Expand Down Expand Up @@ -351,7 +357,7 @@ function ProtectionController(config) {
protectionModel = null;
}

needkeyRetries.forEach( retryTimeout => clearTimeout(retryTimeout));
needkeyRetries.forEach(retryTimeout => clearTimeout(retryTimeout));
needkeyRetries = [];

mediaInfoArr = [];
Expand Down Expand Up @@ -420,7 +426,10 @@ function ProtectionController(config) {
for (ksIdx = 0; ksIdx < supportedKS.length; ksIdx++) {
if (keySystem === supportedKS[ksIdx].ks) {

requestedKeySystems.push({ks: supportedKS[ksIdx].ks, configs: [getKeySystemConfiguration(keySystem)]});
requestedKeySystems.push({
ks: supportedKS[ksIdx].ks,
configs: [getKeySystemConfiguration(keySystem)]
});

// Ensure that we would be granted key system access using the key
// system and codec information
Expand All @@ -447,15 +456,17 @@ function ProtectionController(config) {
break;
}
}
}
else if (keySystem === undefined) {
} else if (keySystem === undefined) {
// First time through, so we need to select a key system
keySystem = null;
pendingNeedKeyData.push(supportedKS);

// Add all key systems to our request list since we have yet to select a key system
for (let i = 0; i < supportedKS.length; i++) {
requestedKeySystems.push({ks: supportedKS[i].ks, configs: [getKeySystemConfiguration(supportedKS[i].ks)]});
requestedKeySystems.push({
ks: supportedKS[i].ks,
configs: [getKeySystemConfiguration(supportedKS[i].ks)]
});
}

let keySystemAccess;
Expand All @@ -465,7 +476,10 @@ function ProtectionController(config) {
keySystem = undefined;
eventBus.off(events.INTERNAL_KEY_SYSTEM_SELECTED, onKeySystemSelected, self);
if (!fromManifest) {
eventBus.trigger(events.KEY_SYSTEM_SELECTED, {data: null, error: new DashJSError(ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE, ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE + event.error)});
eventBus.trigger(events.KEY_SYSTEM_SELECTED, {
data: null,
error: new DashJSError(ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE, ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE + event.error)
});
}
} else {
keySystemAccess = event.data;
Expand All @@ -490,11 +504,13 @@ function ProtectionController(config) {
for (let i = 0; i < pendingNeedKeyData.length; i++) {
for (ksIdx = 0; ksIdx < pendingNeedKeyData[i].length; ksIdx++) {
if (keySystem === pendingNeedKeyData[i][ksIdx].ks) {
// For Clearkey: if parameters for generating init data was provided by the user, use them for generating
// initData and overwrite possible initData indicated in encrypted event (EME)
if (protectionKeyController.isClearKey(keySystem) && protData && protData.hasOwnProperty('clearkeys')) {
const initData = { kids: Object.keys(protData.clearkeys) };
pendingNeedKeyData[i][ksIdx].initData = new TextEncoder().encode(JSON.stringify(initData));
if (protectionKeyController.isClearKey(keySystem)) {
// For Clearkey: if parameters for generating init data was provided by the user, use them for generating
// initData and overwrite possible initData indicated in encrypted event (EME)
if (protData && protData.hasOwnProperty('clearkeys')) {
const initData = {kids: Object.keys(protData.clearkeys)};
pendingNeedKeyData[i][ksIdx].initData = new TextEncoder().encode(JSON.stringify(initData));
}
}
if (pendingNeedKeyData[i][ksIdx].sessionId) {
// Load MediaKeySession with sessionId
Expand All @@ -510,7 +526,10 @@ function ProtectionController(config) {
} else {
keySystem = undefined;
if (!fromManifest) {
eventBus.trigger(events.KEY_SYSTEM_SELECTED, {data: null, error: new DashJSError(ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE, ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE + 'Error selecting key system! -- ' + event.error)});
eventBus.trigger(events.KEY_SYSTEM_SELECTED, {
data: null,
error: new DashJSError(ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_CODE, ProtectionErrors.KEY_SYSTEM_ACCESS_DENIED_ERROR_MESSAGE + 'Error selecting key system! -- ' + event.error)
});
}
}
};
Expand Down Expand Up @@ -547,7 +566,7 @@ function ProtectionController(config) {
const protData = getProtData(keySystem);
const keySystemString = keySystem ? keySystem.systemString : null;
const licenseServerData = protectionKeyController.getLicenseServer(keySystem, protData, messageType);
const eventData = { sessionToken: sessionToken, messageType: messageType };
const eventData = {sessionToken: sessionToken, messageType: messageType};

// Ensure message from CDM is not empty
if (!message || message.byteLength === 0) {
Expand All @@ -565,7 +584,7 @@ function ProtectionController(config) {
// Perform any special handling for ClearKey
if (protectionKeyController.isClearKey(keySystem)) {
const clearkeys = protectionKeyController.processClearKeyLicenseRequest(keySystem, protData, message);
if (clearkeys) {
if (clearkeys) {
logger.debug('DRM: ClearKey license request handled by application!');
sendLicenseRequestCompleteEvent(eventData);
protectionModel.updateKeySession(sessionToken, clearkeys);
Expand All @@ -587,9 +606,15 @@ function ProtectionController(config) {
// TODO: Deprecated!
url = protData.laURL;
} else {
url = keySystem.getLicenseServerURLFromInitData(CommonEncryption.getPSSHData(sessionToken.initData));
if (!url) {
url = e.data.laURL;
// For clearkey use the url defined in the manifest
if (protectionKeyController.isClearKey(keySystem)) {
url = keySystem.getLicenseServerUrlFromMediaInfo(mediaInfoArr);
} else {
const psshData = CommonEncryption.getPSSHData(sessionToken.initData);
url = keySystem.getLicenseServerURLFromInitData(psshData);
if (!url) {
url = e.data.laURL;
}
}
}
// Possibly update or override the URL based on the message
Expand Down Expand Up @@ -661,6 +686,7 @@ function ProtectionController(config) {
xhr.statusText + '" (' + xhr.status + '), readyState is ' + xhr.readyState));
};

//const reqPayload = keySystem.getLicenseRequestFromMessage(message);
const reqPayload = keySystem.getLicenseRequestFromMessage(message);
const reqMethod = licenseServerData.getHTTPMethod(messageType);
const responseType = licenseServerData.getResponseType(keySystemString, messageType);
Expand Down
19 changes: 10 additions & 9 deletions src/streaming/protection/controllers/ProtectionKeyController.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,20 @@ function ProtectionKeyController() {
let keySystem;

// PlayReady
keySystem = KeySystemPlayReady(context).getInstance({ BASE64: BASE64 });
keySystem = KeySystemPlayReady(context).getInstance({BASE64: BASE64});
keySystems.push(keySystem);

// Widevine
keySystem = KeySystemWidevine(context).getInstance({ BASE64: BASE64 });
keySystem = KeySystemWidevine(context).getInstance({BASE64: BASE64});
keySystems.push(keySystem);

// ClearKey
keySystem = KeySystemClearKey(context).getInstance({ BASE64: BASE64 });
keySystem = KeySystemClearKey(context).getInstance({BASE64: BASE64});
keySystems.push(keySystem);
clearkeyKeySystem = keySystem;

// W3C ClearKey
keySystem = KeySystemW3CClearKey(context).getInstance({ BASE64: BASE64, debug: debug });
keySystem = KeySystemW3CClearKey(context).getInstance({BASE64: BASE64, debug: debug});
keySystems.push(keySystem);
clearkeyW3CKeySystem = keySystem;
}
Expand Down Expand Up @@ -202,13 +202,14 @@ function ProtectionKeyController() {
let supportedKS = [];

if (cps) {
const cencContentProtection = CommonEncryption.findCencContentProtection(cps);
for (ksIdx = 0; ksIdx < keySystems.length; ++ksIdx) {
ks = keySystems[ksIdx];
for (cpIdx = 0; cpIdx < cps.length; ++cpIdx) {
cp = cps[cpIdx];
if (cp.schemeIdUri.toLowerCase() === ks.schemeIdURI) {
// Look for DRM-specific ContentProtection
let initData = ks.getInitData(cp);
let initData = ks.getInitData(cp, cencContentProtection);

supportedKS.push({
ks: keySystems[ksIdx],
Expand All @@ -231,7 +232,7 @@ function ProtectionKeyController() {
*
* @param {ArrayBuffer} initData Concatenated PSSH data for all DRMs
* supported by the content
* @param {ProtectionDataSet} protDataSet user specified protection data - license server url etc
* @param {ProtectionData} protDataSet user specified protection data - license server url etc
* supported by the content
* @returns {Array.<Object>} array of objects indicating which supported key
* systems were found. Empty array is returned if no
Expand Down Expand Up @@ -266,7 +267,7 @@ function ProtectionKeyController() {
*
* @param {KeySystem} keySystem the key system
* associated with this license request
* @param {ProtectionDataSet} protData protection data to use for the
* @param {ProtectionData} protData protection data to use for the
* request
* @param {string} [messageType="license-request"] the message type associated with this
* request. Supported message types can be found
Expand All @@ -288,7 +289,7 @@ function ProtectionKeyController() {

let licenseServerData = null;
if (protData && protData.hasOwnProperty('drmtoday')) {
licenseServerData = DRMToday(context).getInstance({ BASE64: BASE64 });
licenseServerData = DRMToday(context).getInstance({BASE64: BASE64});
} else if (keySystem.systemString === ProtectionConstants.WIDEVINE_KEYSTEM_STRING) {
licenseServerData = Widevine(context).getInstance();
} else if (keySystem.systemString === ProtectionConstants.PLAYREADY_KEYSTEM_STRING) {
Expand All @@ -304,7 +305,7 @@ function ProtectionKeyController() {
* Allows application-specific retrieval of ClearKey keys.
*
* @param {KeySystem} clearkeyKeySystem They exact ClearKey System to be used
* @param {ProtectionDataSet} protData protection data to use for the
* @param {ProtectionData} protData protection data to use for the
* request
* @param {ArrayBuffer} message the key message from the CDM
* @return {ClearKeyKeySet|null} the clear keys associated with
Expand Down
6 changes: 4 additions & 2 deletions src/streaming/protection/drm/KeySystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
* @param {Object} contentProtection the json-style contentProtection
* object representing the ContentProtection element parsed from the
* MPD XML document.
* @param {Object} cencContentProtection the Common Encryption content protection element or
* null if not specified.
* @returns {ArrayBuffer} EME initialization data
*/

Expand Down Expand Up @@ -117,15 +119,15 @@
* @returns {?string} The license server URL or null if URL is not available in initData
*/

/**
/**
* Returns specific CDM (custom) data.
*
* @function
* @name MediaPlayer.dependencies.protection.KeySystem#getCDMData
* @returns {ArrayBuffer} the CDM (custom) data
*/

/**
/**
* Returns MediaKeySession session ID.
*
* @function
Expand Down
Loading