Skip to content

Commit

Permalink
feat: added API to set media keys directly (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
forbesjo committed Oct 24, 2018
1 parent 746e5ed commit 57701b9
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 3 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Maintenance Status: Stable
- [Source Options](#source-options)
- [Plugin Options](#plugin-options)
- [emeOptions](#emeoptions)
- [initializeMediaKeys](#initializemediakeys)
- [Passing methods seems complicated](#passing-methods-seems-complicated)
- [Special Events](#special-events)
- [Getting Started](#getting-started)
Expand Down Expand Up @@ -328,6 +329,28 @@ player.src({
});
```

### initializeMediaKeys
Type: `function`

`player.eme.initializeMediaKeys()` sets up MediaKeys immediately on demand. This is useful for setting up the video element for DRM before loading any content. Otherwise the video element is set up for DRM on `encrypted` events. This is not supported in Safari.

```javascript
// additional plugin options
var emeOptions = {
keySystems: {
'org.w3.clearkey': {...}
}
};

player.eme.initializeMediaKeys(emeOptions, function(error) {
if (error) {
// do something with error
}

// do something else
});
```

### Passing methods seems complicated

While simple URLs are supported for many EME implementations, we wanted to provide as much
Expand Down
47 changes: 44 additions & 3 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,50 @@ const onPlayerReady = (player) => {
* An object of options left to the plugin author to define.
*/
const eme = function(options = {}) {
this.eme.options = options;

this.ready(() => onPlayerReady(this));
const player = this;

player.ready(() => onPlayerReady(player));

// Plugin API
player.eme = {
/**
* Sets up MediaKeys on demand
* Works around https://bugs.chromium.org/p/chromium/issues/detail?id=895449
*
* @function initializeMediaKeys
* @param {Object} [emeOptions={}]
* An object of eme plugin options.
* @param {Function} [callback=function(){}]
*/
initializeMediaKeys(emeOptions = {}, callback = function() {}) {
// TODO: this should be refactored and renamed to be less tied
// to encrypted events
const mergedEmeOptions = videojs.mergeOptions(
player.currentSource(),
options,
emeOptions
);

// fake an encrypted event for handleEncryptedEvent
const mockEncryptedEvent = {
initDataType: 'cenc',
initData: null,
target: player.tech_.el_
};

setupSessions(player);

if (player.tech_.el_.setMediaKeys) {
handleEncryptedEvent(mockEncryptedEvent, mergedEmeOptions, player.eme.sessions, player.tech_)
.then(() => callback())
.catch((error) => callback(error));
} else if (player.tech_.el_.msSetMediaKeys) {
handleMsNeedKeyEvent(mockEncryptedEvent, mergedEmeOptions, player.eme.sessions, player.tech_);
callback();
}
},
options
};
};

// Register the plugin with video.js.
Expand Down
69 changes: 69 additions & 0 deletions test/plugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,23 @@ QUnit.module('videojs-contrib-eme', {
this.video = document.createElement('video');
this.fixture.appendChild(this.video);
this.player = videojs(this.video);

this.origRequestMediaKeySystemAccess = window.navigator.requestMediaKeySystemAccess;

window.navigator.requestMediaKeySystemAccess = (keySystem, options) => {
return Promise.resolve({
keySystem: 'org.w3.clearkey',
createMediaKeys: () => {
return {
createSession: () => new videojs.EventTarget()
};
}
});
};
},

afterEach() {
window.navigator.requestMediaKeySystemAccess = this.origRequestMediaKeySystemAccess;
this.player.dispose();
this.clock.restore();
}
Expand Down Expand Up @@ -82,6 +96,61 @@ QUnit.test('exposes options', function(assert) {
'exposes publisherId');
});

// skip test for Safari
if (!window.WebKitMediaKeys) {
QUnit.test('initializeMediaKeys standard', function(assert) {
const done = assert.async();
const initData = new Uint8Array([1, 2, 3]).buffer;

this.player.eme();

this.player.eme.initializeMediaKeys({
keySystems: {
'org.w3.clearkey': {
pssh: initData
}
}
}, () => {
const sessions = this.player.eme.sessions;

assert.equal(sessions.length, 1, 'created a session when keySystems in options');
assert.deepEqual(sessions[0].initData, initData, 'captured initData in the session');
done();
});
});
}

QUnit.test('initializeMediaKeys ms-prefix', function(assert) {
const done = assert.async();
// stub setMediaKeys
const setMediaKeys = this.player.tech_.el_.setMediaKeys;

this.player.tech_.el_.setMediaKeys = null;
this.player.tech_.el_.msSetMediaKeys = () => {};

const initData = new Uint8Array([1, 2, 3]).buffer;

this.player.eme();

this.player.eme.initializeMediaKeys({
keySystems: {
'com.microsoft.playready': {
pssh: initData
}
}
}, () => {
const sessions = this.player.eme.sessions;

assert.equal(sessions.length, 1, 'created a session when keySystems in options');
assert.deepEqual(sessions[0].initData, initData, 'captured initData in the session');

done();
});

this.player.tech_.el_.msSetMediaKeys = null;
this.player.tech_.el_.setMediaKeys = setMediaKeys;
});

QUnit.module('plugin guard functions', {
beforeEach() {
this.options = {
Expand Down

0 comments on commit 57701b9

Please sign in to comment.