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

[MSS] Widevine pssh generation #2397

Merged
1 change: 1 addition & 0 deletions externals/base64.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,5 @@ if (typeof exports !== 'undefined') {
exports.decode = BASE64.decode;
exports.decodeArray = BASE64.decodeArray;
exports.encode = BASE64.encode;
exports.encodeASCII = BASE64.encodeASCII;
}
93 changes: 52 additions & 41 deletions src/mss/parser/MssParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
*/
function MssParser(config) {
config = config || {};
const protectionController = config.protectionController;
const BASE64 = config.BASE64;
const log = config.log;
const errorHandler = config.errHandler;
Expand Down Expand Up @@ -459,52 +458,64 @@ function MssParser(config) {


function createPRContentProtection(protectionHeader) {
const keySystems = protectionController ? protectionController.getKeySystems() : null;
let ksPlayReady;

for (let i = 0; i < keySystems.length; i++) {
if (keySystems[i].systemString && keySystems[i].systemString.indexOf('playready') !== -1) {
ksPlayReady = keySystems[i];
break;
}
}

let contentProtection = {};
let pro;

pro = {
let pro = {
__text: protectionHeader.firstChild.data,
__prefix: 'mspr'
};

if (ksPlayReady) {
contentProtection.schemeIdUri = ksPlayReady.schemeIdURI;
contentProtection.value = ksPlayReady.systemString;
contentProtection.pro = pro;
contentProtection.pro_asArray = pro;
}

return contentProtection;
return {
schemeIdUri: 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95',
value: 'com.microsoft.playready',
pro: pro,
pro_asArray: pro
};
}

function createWidevineContentProtection(/*protectionHeader*/) {
const keySystems = protectionController ? protectionController.getKeySystems() : null;
let ksWidevine;

for (let i = 0; i < keySystems.length; i++) {
if (keySystems[i].systemString && keySystems[i].systemString.indexOf('widevine') !== -1) {
ksWidevine = keySystems[i];
break;
}
}
function createWidevineContentProtection(protectionHeader, KID) {
// Create Widevine CENC header (Protocol Buffer) with KID value
let wvCencHeader = new Uint8Array(2 + KID.length);
wvCencHeader[0] = 0x12;
wvCencHeader[1] = 0x10;
wvCencHeader.set(KID, 2);

var contentProtection = {};
if (ksWidevine) {
contentProtection.schemeIdUri = ksWidevine.schemeIdURI;
contentProtection.value = ksWidevine.systemString;
}
// Create a pssh box
let length = 12 /* box length, type, version and flags */ + 16 /* SystemID */ + 4 /* data length */ + wvCencHeader.length;
let pssh = new Uint8Array(length);
let i = 0;

return contentProtection;
// Set box length value
pssh[i++] = (length & 0xFF000000) >> 24;
pssh[i++] = (length & 0x00FF0000) >> 16;
pssh[i++] = (length & 0x0000FF00) >> 8;
pssh[i++] = (length & 0x000000FF);

// Set type ('pssh'), version (0) and flags (0)
pssh.set([0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00], i);
i += 8;

// Set SystemID ('edef8ba9-79d6-4ace-a3c8-27dcd51d21ed')
pssh.set([0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed], i);
i += 16;

// Set data length value
pssh[i++] = (wvCencHeader.length & 0xFF000000) >> 24;
pssh[i++] = (wvCencHeader.length & 0x00FF0000) >> 16;
pssh[i++] = (wvCencHeader.length & 0x0000FF00) >> 8;
pssh[i++] = (wvCencHeader.length & 0x000000FF);

// Copy Widevine CENC header
pssh.set(wvCencHeader, i);

// Convert to BASE64 string
pssh = String.fromCharCode.apply(null, pssh);
pssh = BASE64.encodeASCII(pssh);

return {
schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed',
value: 'com.widevine.alpha',
pssh: {
__text: pssh
}
};
}

function processManifest(xmlDoc, manifestLoadedTime) {
Expand Down Expand Up @@ -564,7 +575,7 @@ function MssParser(config) {
contentProtections.push(contentProtection);

// Create ContentProtection for Widevine (as a CENC protection)
contentProtection = createWidevineContentProtection(protectionHeader);
contentProtection = createWidevineContentProtection(protectionHeader, KID);
contentProtection['cenc:default_KID'] = KID;
contentProtections.push(contentProtection);

Expand Down
44 changes: 1 addition & 43 deletions src/streaming/protection/drm/KeySystemWidevine.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,50 +56,8 @@ function KeySystemWidevine(config) {
}
}

function replaceKID(pssh, KID) {
let pssh_array;
let replace = true;
let kidLen = 16;
let pos;
let i, j;

pssh_array = new Uint8Array(pssh);

for (i = 0; i <= pssh_array.length - (kidLen + 2); i++) {
if (pssh_array[i] === 0x12 && pssh_array[i + 1] === 0x10) {
pos = i + 2;
for (j = pos; j < (pos + kidLen); j++) {
if (pssh_array[j] !== 0xFF) {
replace = false;
break;
}
}
break;
}
}

if (replace) {
pssh_array.set(KID, pos);
}

return pssh_array.buffer;
}

function getInitData(cp) {
var pssh = null;
// Get pssh from protectionData or from manifest
if (protData && protData.pssh) {
pssh = BASE64.decodeArray(protData.pssh).buffer;
} else {
pssh = CommonEncryption.parseInitDataFromContentProtection(cp, BASE64);
}

// Check if KID within pssh is empty, in that case set KID value according to 'cenc:default_KID' value
if (pssh) {
pssh = replaceKID(pssh, cp['cenc:default_KID']);
}

return pssh;
return CommonEncryption.parseInitDataFromContentProtection(cp, BASE64);
}

function getRequestHeadersFromMessage( /*message*/ ) {
Expand Down