diff --git a/src/inheritAttributes.js b/src/inheritAttributes.js index 5849006a..29545d6e 100644 --- a/src/inheritAttributes.js +++ b/src/inheritAttributes.js @@ -4,6 +4,14 @@ import { findChildren, getContent } from './utils/xml'; import { parseAttributes } from './parseAttributes'; import resolveUrl from './utils/resolveUrl'; import errors from './errors'; +import decodeB64ToUint8Array from './utils/decodeB64ToUint8Array'; + +const keySystemsMap = { + 'urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b': 'org.w3.clearkey', + 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed': 'com.widevine.alpha', + 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready', + 'urn:uuid:f239e769-efa3-4850-9c16-a903c6932efb': 'com.adobe.primetime' +}; /** * Builds a list of urls that is the product of the reference urls and BaseURL values @@ -154,6 +162,37 @@ export const inheritBaseUrls = }); }; +/** + * Tranforms a series of content protection nodes to + * an object containing pssh data by key system + * + * @param {Node[]} contentProtectionNodes + * Content protection nodes + * @return {Object} + * Object containing pssh data by key system + */ +const generateKeySystemInformation = (contentProtectionNodes) => { + return contentProtectionNodes.reduce((acc, node) => { + const attributes = parseAttributes(node); + const keySystem = keySystemsMap[attributes.schemeIdUri]; + + if (keySystem) { + acc[keySystem] = { attributes }; + + const psshNode = findChildren(node, 'cenc:pssh')[0]; + + if (psshNode) { + const pssh = getContent(psshNode); + const psshBuffer = pssh && decodeB64ToUint8Array(pssh); + + acc[keySystem].pssh = psshBuffer; + } + } + + return acc; + }, {}); +}; + /** * Maps an AdaptationSet node to a list of Representation information objects * @@ -185,9 +224,18 @@ export const toRepresentations = findChildren(adaptationSet, 'BaseURL')); const role = findChildren(adaptationSet, 'Role')[0]; const roleAttributes = { role: parseAttributes(role) }; - const attrs = merge(periodAttributes, - adaptationSetAttributes, - roleAttributes); + + let attrs = merge(periodAttributes, + adaptationSetAttributes, + roleAttributes); + + const contentProtection = generateKeySystemInformation( + findChildren(adaptationSet, 'ContentProtection')); + + if (Object.keys(contentProtection).length) { + attrs = merge(attrs, { contentProtection }); + } + const segmentInfo = getSegmentInformation(adaptationSet); const representations = findChildren(adaptationSet, 'Representation'); const adaptationSetSegmentInfo = merge(periodSegmentInfo, segmentInfo); diff --git a/src/toM3u8.js b/src/toM3u8.js index 76cb841a..fcb73361 100644 --- a/src/toM3u8.js +++ b/src/toM3u8.js @@ -1,5 +1,5 @@ export const formatAudioPlaylist = ({ attributes, segments }) => { - return { + const playlist = { attributes: { NAME: attributes.id, BANDWIDTH: attributes.bandwidth, @@ -14,6 +14,12 @@ export const formatAudioPlaylist = ({ attributes, segments }) => { segments, mediaSequence: segments.length ? segments[0].number : 1 }; + + if (attributes.contentProtection) { + playlist.contentProtection = attributes.contentProtection; + } + + return playlist; }; export const formatVttPlaylist = ({ attributes, segments }) => { @@ -98,7 +104,7 @@ export const organizeVttPlaylists = playlists => { }; export const formatVideoPlaylist = ({ attributes, segments }) => { - return { + const playlist = { attributes: { NAME: attributes.id, AUDIO: 'audio', @@ -119,6 +125,12 @@ export const formatVideoPlaylist = ({ attributes, segments }) => { segments, mediaSequence: segments.length ? segments[0].number : 1 }; + + if (attributes.contentProtection) { + playlist.contentProtection = attributes.contentProtection; + } + + return playlist; }; export const toM3u8 = dashPlaylists => { diff --git a/src/utils/decodeB64ToUint8Array.js b/src/utils/decodeB64ToUint8Array.js new file mode 100644 index 00000000..d95bdb55 --- /dev/null +++ b/src/utils/decodeB64ToUint8Array.js @@ -0,0 +1,11 @@ +import window from 'global/window'; + +export default function decodeB64ToUint8Array(b64Text) { + const decodedString = window.atob(b64Text); + const array = new Uint8Array(decodedString.length); + + for (let i = 0; i < decodedString.length; i++) { + array[i] = decodedString.charCodeAt(i); + } + return array; +} diff --git a/test/manifests/maat_vtt_segmentTemplate.js b/test/manifests/maat_vtt_segmentTemplate.js index eb48fcf7..f2992248 100644 --- a/test/manifests/maat_vtt_segmentTemplate.js +++ b/test/manifests/maat_vtt_segmentTemplate.js @@ -18,6 +18,14 @@ export const parsedManifest = { NAME: '125000', ['PROGRAM-ID']: 1 }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, endList: true, mediaSequence: 0, targetDuration: 1.984, @@ -79,6 +87,14 @@ export const parsedManifest = { NAME: '125000', ['PROGRAM-ID']: 1 }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, endList: true, targetDuration: 1.984, mediaSequence: 0, @@ -203,6 +219,14 @@ export const parsedManifest = { width: 482 } }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, endList: true, targetDuration: 1.9185833333333333, mediaSequence: 0, @@ -263,6 +287,14 @@ export const parsedManifest = { width: 720 } }, + contentProtection: { + 'com.widevine.alpha': { + attributes: { + schemeIdUri: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + }, + pssh: new Uint8Array([181, 235, 45]) + } + }, endList: true, targetDuration: 1.9185833333333333, mediaSequence: 0, diff --git a/test/manifests/maat_vtt_segmentTemplate.mpd b/test/manifests/maat_vtt_segmentTemplate.mpd index fe4d6019..eb058f93 100644 --- a/test/manifests/maat_vtt_segmentTemplate.mpd +++ b/test/manifests/maat_vtt_segmentTemplate.mpd @@ -1,9 +1,14 @@ - + https://www.example.com/base + + + test + + @@ -11,7 +16,13 @@ - + + + + + test + + @@ -21,6 +32,11 @@ + + + test + +