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

Refactor/protection #3793

Merged
merged 13 commits into from
Nov 1, 2021
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
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ declare namespace dashjs {
interface ProtectionController {
initializeForMedia(mediaInfo: ProtectionMediaInfo): void;

clearMediaInfoArrayByStreamId(streamId: string): void;
clearMediaInfoArray(): void;

createKeySession(initData: ArrayBuffer, cdmData: Uint8Array): void;

Expand Down Expand Up @@ -175,6 +175,7 @@ declare namespace dashjs {
},
protection?: {
keepProtectionMediaKeys?: boolean,
ignoreEmeEncryptedEvent?: boolean
},
buffer?: {
enableSeekDecorrelationFix?: boolean,
Expand Down
95 changes: 95 additions & 0 deletions samples/drm/dashif-laurl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>License server via MPD example</title>

<script src="../../dist/dash.all.debug.js"></script>

<!-- Bootstrap core CSS -->
<link href="../lib/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="../lib/main.css" rel="stylesheet">

<style>
video {
width: 640px;
height: 360px;
}
</style>

<script class="code">
function init() {
var protData = {
"com.widevine.alpha": {
"httpRequestHeaders": {
"X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU"
},
priority: 0
},
"com.microsoft.playready": {
"httpRequestHeaders": {
"X-AxDRM-Message": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJjb21fa2V5X2lkIjoiYjMzNjRlYjUtNTFmNi00YWUzLThjOTgtMzNjZWQ1ZTMxYzc4IiwibWVzc2FnZSI6eyJ0eXBlIjoiZW50aXRsZW1lbnRfbWVzc2FnZSIsImZpcnN0X3BsYXlfZXhwaXJhdGlvbiI6NjAsInBsYXlyZWFkeSI6eyJyZWFsX3RpbWVfZXhwaXJhdGlvbiI6dHJ1ZX0sImtleXMiOlt7ImlkIjoiOWViNDA1MGQtZTQ0Yi00ODAyLTkzMmUtMjdkNzUwODNlMjY2IiwiZW5jcnlwdGVkX2tleSI6ImxLM09qSExZVzI0Y3Iya3RSNzRmbnc9PSJ9XX19.FAbIiPxX8BHi9RwfzD7Yn-wugU19ghrkBFKsaCPrZmU"
},
priority: 0
}
};
var video,
player,
url = "mpds/laurl.mpd";

video = document.querySelector("video");
player = dashjs.MediaPlayer().create();
player.updateSettings({
debug: {
logLevel: 5
}
})
player.initialize(video, url, true);
player.setProtectionData(protData);
}
</script>
</head>
<body>

<main>
<div class="container py-4">
<header class="pb-3 mb-4 border-bottom">
<img class=""
src="../lib/img/dashjs-logo.png"
width="200">
</header>
<div class="row">
<div class="col-md-4">
<div class="h-100 p-5 bg-light border rounded-3">
<h3>Widevine DRM instantiation example</h3>
<p>This example shows how to specify the license server url as part of the MPD using
'dashif:laurl'. </p>
<p>For a detailed explanation on this checkout the
<a href="https://github.com/Dash-Industry-Forum/dash.js/wiki/Digital-Rights-Management-(DRM)-and-license-acquisition#licenser-server-url-via-mpd"
target="_blank">Wiki</a>.</p>
</div>
</div>
<div class="col-md-8">
<video controls="true"></video>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="code-output"></div>
</div>
</div>
<footer class="pt-3 mt-4 text-muted border-top">
&copy; DASH-IF
</footer>
</div>
</main>


<script>
document.addEventListener('DOMContentLoaded', function () {
init();
});
</script>
<script src="../highlighter.js"></script>
</body>
</html>
15 changes: 15 additions & 0 deletions samples/samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,22 @@
"Video",
"Audio"
]
},
{
"title": "License server via MPD",
"description": "This example shows how to specify the license server url as part of the MPD using 'dashif:laurl'",
"href": "drm/dashif-laurl.html",
"image": "lib/img/tos-3.jpg",
"labels": [
"VoD",
"DRM",
"Widevine",
"Playready",
"Video",
"Audio"
]
}

]
},
{
Expand Down
5 changes: 4 additions & 1 deletion src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ import {HTTPRequest} from '../streaming/vo/metrics/HTTPRequest';
* Set the value for the ProtectionController and MediaKeys life cycle.
*
* If true, the ProtectionController and then created MediaKeys and MediaKeySessions will be preserved during the MediaPlayer lifetime.
* @property {boolean} ignoreEmeEncryptedEvent
* If set to true the player will ignore "encrypted" and "needkey" events thrown by the EME.
*/

/**
Expand Down Expand Up @@ -773,7 +775,8 @@ function Settings() {
applyServiceDescription: true
},
protection: {
keepProtectionMediaKeys: false
keepProtectionMediaKeys: false,
ignoreEmeEncryptedEvent: false
},
buffer: {
enableSeekDecorrelationFix: false,
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/Stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ function Stream(config) {
if (protectionController) {
// Need to check if streamProcessors exists because streamProcessors
// could be cleared in case an error is detected while initializing DRM keysystem
protectionController.clearMediaInfoArrayByStreamId(getId());
protectionController.clearMediaInfoArray();
for (let i = 0; i < ln && streamProcessors[i]; i++) {
const type = streamProcessors[i].getType();
const mediaInfo = streamProcessors[i].getMediaInfo();
Expand Down
3 changes: 3 additions & 0 deletions src/streaming/constants/ProtectionConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class ProtectionConstants {
this.CLEARKEY_KEYSTEM_STRING = 'org.w3.clearkey';
this.WIDEVINE_KEYSTEM_STRING = 'com.widevine.alpha';
this.PLAYREADY_KEYSTEM_STRING = 'com.microsoft.playready';
this.INITIALIZATION_DATA_TYPE_CENC = 'cenc';
this.INITIALIZATION_DATA_TYPE_KEYIDS = 'keyids'
this.INITIALIZATION_DATA_TYPE_WEBM = 'webm'
}

constructor () {
Expand Down
24 changes: 12 additions & 12 deletions src/streaming/controllers/StreamController.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function StreamController() {
}

function initialize(autoPl, protData) {
checkConfig();
_checkConfig();

autoPlay = autoPl;
protectionData = protData;
Expand Down Expand Up @@ -496,7 +496,7 @@ function StreamController() {
_handleOuterPeriodSeek(e, seekToStream);
}

createPlaylistMetrics(PlayList.SEEK_START_REASON);
_createPlaylistMetrics(PlayList.SEEK_START_REASON);
}

/**
Expand Down Expand Up @@ -698,7 +698,7 @@ function StreamController() {

if (isNaN(initialBufferLevel) || initialBufferLevel <= playbackController.getBufferLevel() || (adapter.getIsDynamic() && initialBufferLevel > playbackController.getLiveDelay())) {
initialPlayback = false;
createPlaylistMetrics(PlayList.INITIAL_PLAYOUT_START_REASON);
_createPlaylistMetrics(PlayList.INITIAL_PLAYOUT_START_REASON);
playbackController.play();
}
}
Expand Down Expand Up @@ -752,9 +752,9 @@ function StreamController() {
* @private
*/
function _onPlaybackStarted( /*e*/) {
logger.debug('[onPlaybackStarted]');
if (!initialPlayback && isPaused) {
createPlaylistMetrics(PlayList.RESUME_FROM_PAUSE_START_REASON);
logger.debug('[onPlaybackStarted]');
if (!initialPlayback && isPaused) {
_createPlaylistMetrics(PlayList.RESUME_FROM_PAUSE_START_REASON);
}
if (initialPlayback) {
initialPlayback = false;
Expand Down Expand Up @@ -1233,7 +1233,7 @@ function StreamController() {
dashMetrics.addPlayList();
}

function createPlaylistMetrics(startReason) {
function _createPlaylistMetrics(startReason) {
dashMetrics.createPlaylistMetrics(playbackController.getTime() * 1000, startReason);
}

Expand Down Expand Up @@ -1324,27 +1324,27 @@ function StreamController() {
return null;
}

function checkConfig() {
function _checkConfig() {
if (!manifestLoader || !manifestLoader.hasOwnProperty('load') || !timelineConverter || !timelineConverter.hasOwnProperty('initialize') ||
!timelineConverter.hasOwnProperty('reset') || !timelineConverter.hasOwnProperty('getClientTimeOffset') || !manifestModel || !errHandler ||
!dashMetrics || !playbackController) {
throw new Error(Constants.MISSING_CONFIG_ERROR);
}
}

function checkInitialize() {
function _checkInitialize() {
if (!manifestUpdater || !manifestUpdater.hasOwnProperty('setManifest')) {
throw new Error('initialize function has to be called previously');
}
}

function load(url) {
checkConfig();
_checkConfig();
manifestLoader.load(url);
}

function loadWithManifest(manifest) {
checkInitialize();
_checkInitialize();
manifestUpdater.setManifest(manifest);
}

Expand Down Expand Up @@ -1446,7 +1446,7 @@ function StreamController() {
}

function reset() {
checkConfig();
_checkConfig();

timeSyncController.reset();

Expand Down
53 changes: 53 additions & 0 deletions src/streaming/protection/CommonEncryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

const LICENSE_SERVER_MANIFEST_CONFIGURATIONS = {
attributes: ['Laurl','laurl'],
prefixes: ['clearkey', 'dashif']
};

/**
* @class
* @ignore
Expand Down Expand Up @@ -211,6 +216,54 @@ class CommonEncryption {

return pssh;
}

static getLicenseServerUrlFromMediaInfo(mediaInfo, schemeIdUri) {
try {

if (!mediaInfo || mediaInfo.length === 0) {
return null;
}

let i = 0;
let licenseServer = null;

while (i < mediaInfo.length && !licenseServer) {
const info = mediaInfo[i];

if (info && info.contentProtection && info.contentProtection.length > 0) {
const targetProtectionData = info.contentProtection.filter((cp) => {
return cp.schemeIdUri && cp.schemeIdUri === schemeIdUri;
});

if (targetProtectionData && targetProtectionData.length > 0) {
let j = 0;
while (j < targetProtectionData.length && !licenseServer) {
const ckData = targetProtectionData[j];
let k = 0;
while (k < LICENSE_SERVER_MANIFEST_CONFIGURATIONS.attributes.length && !licenseServer) {
let l = 0;
const attribute = LICENSE_SERVER_MANIFEST_CONFIGURATIONS.attributes[k];
while (l < LICENSE_SERVER_MANIFEST_CONFIGURATIONS.prefixes.length && !licenseServer) {
const prefix = LICENSE_SERVER_MANIFEST_CONFIGURATIONS.prefixes[l];
if (ckData[attribute] && ckData[attribute].__prefix && ckData[attribute].__prefix === prefix && ckData[attribute].__text) {
licenseServer = ckData[attribute].__text;
}
l += 1;
}
k += 1;
}
j += 1;
}
}
}
i += 1;
}
return licenseServer;
} catch
(e) {
return null;
}
}
}

export default CommonEncryption;
16 changes: 8 additions & 8 deletions src/streaming/protection/Protection.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function Protection() {
protectionKeyController.setConfig({ debug: config.debug, BASE64: config.BASE64 });
protectionKeyController.initialize();

let protectionModel = getProtectionModel(config);
let protectionModel = _getProtectionModel(config);

if (!controller && protectionModel) {//TODO add ability to set external controller if still needed at all?
controller = ProtectionController(context).create({
Expand All @@ -138,7 +138,7 @@ function Protection() {
return controller;
}

function getProtectionModel(config) {
function _getProtectionModel(config) {
const debug = config.debug;
const logger = debug.getLogger(instance);
const eventBus = config.eventBus;
Expand All @@ -149,19 +149,19 @@ function Protection() {
(!videoElement || videoElement.mediaKeys !== undefined)) {
logger.info('EME detected on this user agent! (ProtectionModel_21Jan2015)');
return ProtectionModel_21Jan2015(context).create({ debug: debug, eventBus: eventBus, events: config.events });
} else if (getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) {
} else if (_getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) {
logger.info('EME detected on this user agent! (ProtectionModel_3Feb2014)');
return ProtectionModel_3Feb2014(context).create({ debug: debug, eventBus: eventBus, events: config.events, api: getAPI(videoElement, APIS_ProtectionModel_3Feb2014) });
} else if (getAPI(videoElement, APIS_ProtectionModel_01b)) {
return ProtectionModel_3Feb2014(context).create({ debug: debug, eventBus: eventBus, events: config.events, api: _getAPI(videoElement, APIS_ProtectionModel_3Feb2014) });
} else if (_getAPI(videoElement, APIS_ProtectionModel_01b)) {
logger.info('EME detected on this user agent! (ProtectionModel_01b)');
return ProtectionModel_01b(context).create({ debug: debug, eventBus: eventBus, errHandler: errHandler, events: config.events, api: getAPI(videoElement, APIS_ProtectionModel_01b) });
return ProtectionModel_01b(context).create({ debug: debug, eventBus: eventBus, errHandler: errHandler, events: config.events, api: _getAPI(videoElement, APIS_ProtectionModel_01b) });
} else {
logger.warn('No supported version of EME detected on this user agent! - Attempts to play encrypted content will fail!');
return null;
}
}

function getAPI(videoElement, apis) {
function _getAPI(videoElement, apis) {
for (let i = 0; i < apis.length; i++) {
const api = apis[i];
// detect if api is supported by browser
Expand All @@ -177,7 +177,7 @@ function Protection() {
}

instance = {
createProtectionSystem: createProtectionSystem
createProtectionSystem
};

return instance;
Expand Down
7 changes: 0 additions & 7 deletions src/streaming/protection/ProtectionEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ class ProtectionEvents extends EventsBase {
*/
this.INTERNAL_KEY_MESSAGE = 'internalKeyMessage';

/**
* Event ID for events delivered when a key system selection procedure
* completes
* @ignore
*/
this.INTERNAL_KEY_SYSTEM_SELECTED = 'internalKeySystemSelected';

/**
* Event ID for events delivered when the status of one decryption keys has changed
* @ignore
Expand Down
Loading