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

feat: support in-manifest DRM data #23

Merged
merged 2 commits into from
Mar 30, 2018
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
54 changes: 51 additions & 3 deletions src/inheritAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 14 additions & 2 deletions src/toM3u8.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const formatAudioPlaylist = ({ attributes, segments }) => {
return {
const playlist = {
attributes: {
NAME: attributes.id,
BANDWIDTH: attributes.bandwidth,
Expand All @@ -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 }) => {
Expand Down Expand Up @@ -98,7 +104,7 @@ export const organizeVttPlaylists = playlists => {
};

export const formatVideoPlaylist = ({ attributes, segments }) => {
return {
const playlist = {
attributes: {
NAME: attributes.id,
AUDIO: 'audio',
Expand All @@ -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 => {
Expand Down
11 changes: 11 additions & 0 deletions src/utils/decodeB64ToUint8Array.js
Original file line number Diff line number Diff line change
@@ -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;
}
32 changes: 32 additions & 0 deletions test/manifests/maat_vtt_segmentTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 18 additions & 2 deletions test/manifests/maat_vtt_segmentTemplate.mpd
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="static" mediaPresentationDuration="PT6S" minBufferTime="PT2.000S">
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="static" mediaPresentationDuration="PT6S" minBufferTime="PT2.000S">
<BaseURL>https://www.example.com/base</BaseURL>
<Period>

<AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1" lang="en">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="aaa"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>test</cenc:pssh>
</ContentProtection>

<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
<SegmentTemplate duration="95232" initialization="$RepresentationID$/init.m4f" media="$RepresentationID$/$Number$.m4f" startNumber="0" timescale="48000"></SegmentTemplate>
<Representation audioSamplingRate="48000" bandwidth="63000" codecs="mp4a.40.2" id="63000">
</Representation>
<Representation audioSamplingRate="48000" bandwidth="125000" codecs="mp4a.40.2" id="125000">
</Representation>
</AdaptationSet>
<AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1" lang="es">

<AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1" lang="es">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="aaa"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>test</cenc:pssh>
</ContentProtection>

<Role schemeIdUri="urn:mpeg:dash:role:2011"></Role>
<SegmentTemplate duration="95232" initialization="$RepresentationID$/es/init.m4f" media="$RepresentationID$/es/$Number$.m4f" startNumber="0" timescale="48000"></SegmentTemplate>
<Representation audioSamplingRate="48000" bandwidth="63000" codecs="mp4a.40.2" id="63000">
Expand All @@ -21,6 +32,11 @@
</AdaptationSet>

<AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="aaa"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>test</cenc:pssh>
</ContentProtection>

<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
<SegmentTemplate duration="46046" initialization="$RepresentationID$/init.m4f" media="$RepresentationID$/$Number$.m4f" startNumber="0" timescale="24000"></SegmentTemplate>
<Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482">
Expand Down