diff --git a/src/inheritAttributes.js b/src/inheritAttributes.js index c20429e7..534dbee0 100644 --- a/src/inheritAttributes.js +++ b/src/inheritAttributes.js @@ -175,6 +175,14 @@ export const inheritBaseUrls = const generateKeySystemInformation = (contentProtectionNodes) => { return contentProtectionNodes.reduce((acc, node) => { const attributes = parseAttributes(node); + + // Although it could be argued that according to the UUID RFC spec the UUID string (a-f chars) should be generated + // as a lowercase string it also mentions it should be treated as case-insensitive on input. Since the key system + // UUIDs in the keySystemsMap are hardcoded as lowercase in the codebase there isn't any reason not to do + // .toLowerCase() on the input UUID string from the manifest (at least I could not think of one). + if (attributes.schemeIdUri) { + attributes.schemeIdUri = attributes.schemeIdUri.toLowerCase(); + } const keySystem = keySystemsMap[attributes.schemeIdUri]; if (keySystem) { @@ -184,9 +192,8 @@ const generateKeySystemInformation = (contentProtectionNodes) => { if (psshNode) { const pssh = getContent(psshNode); - const psshBuffer = pssh && decodeB64ToUint8Array(pssh); - acc[keySystem].pssh = psshBuffer; + acc[keySystem].pssh = pssh && decodeB64ToUint8Array(pssh); } } diff --git a/test/inheritAttributes.test.js b/test/inheritAttributes.test.js index 9eca5e9f..c630b034 100644 --- a/test/inheritAttributes.test.js +++ b/test/inheritAttributes.test.js @@ -9,6 +9,7 @@ import { stringToMpdXml } from '../src/stringToMpdXml'; import errors from '../src/errors'; import QUnit from 'qunit'; import { toPlaylists } from '../src/toPlaylists'; +import decodeB64ToUint8Array from '@videojs/vhs-utils/es/decode-b64-to-uint8-array'; QUnit.module('buildBaseUrls'); @@ -2065,3 +2066,175 @@ QUnit.test('Test to check use of either Segment Template or Segment List when bo assert.equal(actual.length, 1); assert.deepEqual(actual, expected); }); + +QUnit.test('keySystem info for representation - lowercase UUIDs', function(assert) { + const NOW = Date.now(); + + const widevinePsshB64 = 'AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ=='; + const playreadyPsshB64 = 'AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA=='; + + const widevinePsshBytes = decodeB64ToUint8Array(widevinePsshB64); + const playreadyPsshBytes = decodeB64ToUint8Array(playreadyPsshB64); + + // Content protection info from dash.js demo + const actual = inheritAttributes(stringToMpdXml(` + + https://www.example.com/base/ + + + + + AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA== + xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4AYgBuAGgAeQBDAE8AZgA1AFgAMABhAGoAbwBrADUAYgBEAHYAagA2AFIAUQA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA= + + + AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ== + + + + + + + + + `), { NOW }); + + // inconsistent quoting because of quote-props + const expected = { + locations: undefined, + representationInfo: [{ + attributes: { + 'bandwidth': 5000000, + 'baseUrl': 'https://www.example.com/base/', + 'codecs': 'avc1.64001e', + 'contentProtection': { + 'com.microsoft.playready': { + attributes: { + schemeIdUri: 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95', + value: 'MSPR 2.0' + }, + pssh: playreadyPsshBytes + }, + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed', + value: 'Widevine' + }, + pssh: widevinePsshBytes + } + }, + 'height': 404, + 'id': 'test', + 'mediaPresentationDuration': 30, + 'mimeType': 'video/mp4', + 'periodStart': 0, + 'role': { + value: 'main' + }, + 'sourceDuration': 30, + 'type': 'static', + 'width': 720, + NOW, + 'clientOffset': 0, + 'xmlns:cenc': 'urn:mpeg:cenc:2013' + }, + segmentInfo: { + template: {} + } + }] + }; + + assert.equal(actual.representationInfo.length, 1); + assert.deepEqual(actual, expected); +}); + +QUnit.test('keySystem info for representation - uppercase UUIDs', function(assert) { + const NOW = Date.now(); + + const widevinePsshB64 = 'AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ=='; + const playreadyPsshB64 = 'AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA=='; + + const widevinePsshBytes = decodeB64ToUint8Array(widevinePsshB64); + const playreadyPsshBytes = decodeB64ToUint8Array(playreadyPsshB64); + + // Content protection info from dash.js demo + const actual = inheritAttributes(stringToMpdXml(` + + https://www.example.com/base/ + + + + + AAAB5HBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAcTEAQAAAQABALoBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBiAG4AaAB5AEMATwBmADUAWAAwAGEAagBvAGsANQBiAEQAdgBqADYAUgBRAD0APQA8AC8ASwBJAEQAPgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA== + xAEAAAEAAQC6ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4AYgBuAGgAeQBDAE8AZgA1AFgAMABhAGoAbwBrADUAYgBEAHYAagA2AFIAUQA9AD0APAAvAEsASQBEAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA= + + + AAAANHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABQIARIQCHJ4bvnnRl+jok5bDvj6RQ== + + + + + + + + + `), { NOW }); + + // inconsistent quoting because of quote-props + const expected = { + locations: undefined, + representationInfo: [{ + attributes: { + 'bandwidth': 5000000, + 'baseUrl': 'https://www.example.com/base/', + 'codecs': 'avc1.64001e', + 'contentProtection': { + 'com.microsoft.playready': { + attributes: { + schemeIdUri: 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95', + value: 'MSPR 2.0' + }, + pssh: playreadyPsshBytes + }, + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed', + value: 'Widevine' + }, + pssh: widevinePsshBytes + } + }, + 'height': 404, + 'id': 'test', + 'mediaPresentationDuration': 30, + 'mimeType': 'video/mp4', + 'periodStart': 0, + 'role': { + value: 'main' + }, + 'sourceDuration': 30, + 'type': 'static', + 'width': 720, + NOW, + 'clientOffset': 0, + 'xmlns:cenc': 'urn:mpeg:cenc:2013' + }, + segmentInfo: { + template: {} + } + }] + }; + + assert.equal(actual.representationInfo.length, 1); + assert.deepEqual(actual, expected); +});