Skip to content

Commit

Permalink
feat: support setting robustness and supportedConfigurations (#100)
Browse files Browse the repository at this point in the history
* feat: support setting robustness level

* Add support for supportedConfigurations in requestMediaKeySystemAccess

* Add note to README about supportedConfigurations overriding individual options

Co-authored-by: Declan Rek <declanrek@gmail.com>
  • Loading branch information
gesinger and decrek committed Feb 12, 2020
1 parent f3322b5 commit 502c8ea
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 19 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Maintenance Status: Stable
- [Other DRM Systems](#other-drm-systems)
- [Get License By URL](#get-license-by-url)
- [Get License By Function](#get-license-by-function)
- [MediaKeySystemConfiguration and supportedConfigurations](#mediakeysystemconfiguration-and-supportedconfigurations)
- [Get License Errors](#get-license-errors)
- [API](#api)
- [Available Options](#available-options)
Expand Down Expand Up @@ -279,6 +280,34 @@ Below is an example of using one of these DRM systems and custom `getLicense()`
}
```

### MediaKeySystemConfiguration and supportedConfigurations

In addition to `audioContentType` and `videoContentType` posted above, it is possible to directly provide the `supportedConfigurations` array to use for the `requestMediaKeySystemAccess` call. This allows for the entire range of options specified by the [MediaKeySystemConfiguration] object.

Note that if `supportedConfigurations` is provided, it will override `audioContentType`, `videoContentType`, `audioRobustness`, and `videoRobustness`.

Example:

```js
{
keySystems: {
'org.w3.clearkey': {
supportedConfigurations: [{
videoCapabilities: [{
contentType: 'video/webm; codecs="vp9"',
robustness: 'SW_SECURE_CRYPTO'
}],
audioCapabilities: [{
contentType: 'audio/webm; codecs="vorbis"',
robustness: 'SW_SECURE_CRYPTO'
}]
}],
'org.w3.clearkey': '<YOUR_LICENSE_URL>'
}
}
}
```

### Get License Errors

The default `getLicense()` functions pass an error to the callback if the license request returns a 4xx or 5xx response code. Depending on how the license server is configured, it is possible in some cases that a valid license could still be returned even if the response code is in that range. If you wish not to pass an error for 4xx and 5xx response codes, you may pass your own `getLicense()` function with the `keySystems` as described above.
Expand Down Expand Up @@ -526,3 +555,5 @@ This event is triggered directly from the underlying `keystatuseschange` event,
## License

Apache License, Version 2.0. [View the license file](LICENSE)

[MediaKeySystemConfiguration]: https://www.w3.org/TR/encrypted-media/#dom-mediakeysystemconfiguration
67 changes: 49 additions & 18 deletions src/eme.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,65 @@ import { requestPlayreadyLicense } from './playready';
import window from 'global/window';
import {mergeAndRemoveNull} from './utils';

/**
* Returns an array of MediaKeySystemConfigurationObjects provided in the keySystem
* options.
*
* @see {@link https://www.w3.org/TR/encrypted-media/#dom-mediakeysystemconfiguration|MediaKeySystemConfigurationObject}
*
* @param {Object} keySystemOptions
* Options passed into videojs-contrib-eme for a specific keySystem
* @return {Object[]}
* Array of MediaKeySystemConfigurationObjects
*/
export const getSupportedConfigurations = (keySystemOptions) => {
if (keySystemOptions.supportedConfigurations) {
return keySystemOptions.supportedConfigurations;
}

// TODO use initDataTypes when appropriate
const supportedConfiguration = {};
const audioContentType = keySystemOptions.audioContentType;
const audioRobustness = keySystemOptions.audioRobustness;
const videoContentType = keySystemOptions.videoContentType;
const videoRobustness = keySystemOptions.videoRobustness;

if (audioContentType || audioRobustness) {
supportedConfiguration.audioCapabilities = [
Object.assign({},
(audioContentType ? { contentType: audioContentType } : {}),
(audioRobustness ? { robustness: audioRobustness } : {})
)
];
}

if (videoContentType || videoRobustness) {
supportedConfiguration.videoCapabilities = [
Object.assign({},
(videoContentType ? { contentType: videoContentType } : {}),
(videoRobustness ? { robustness: videoRobustness } : {})
)
];
}

return [supportedConfiguration];
};

export const getSupportedKeySystem = (keySystems) => {
// As this happens after the src is set on the video, we rely only on the set src (we
// do not change src based on capabilities of the browser in this plugin).

let promise;

Object.keys(keySystems).forEach((keySystem) => {
// TODO use initDataTypes when appropriate
const systemOptions = {};
const audioContentType = keySystems[keySystem].audioContentType;
const videoContentType = keySystems[keySystem].videoContentType;

if (audioContentType) {
systemOptions.audioCapabilities = [{
contentType: audioContentType
}];
}
if (videoContentType) {
systemOptions.videoCapabilities = [{
contentType: videoContentType
}];
}
const supportedConfigurations = getSupportedConfigurations(keySystems[keySystem]);

if (!promise) {
promise = window.navigator.requestMediaKeySystemAccess(keySystem, [systemOptions]);
promise =
window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations);
} else {
promise = promise.catch(
(e) => window.navigator.requestMediaKeySystemAccess(keySystem, [systemOptions]));
promise = promise.catch((e) =>
window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations));
}
});

Expand Down
102 changes: 101 additions & 1 deletion test/eme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
makeNewRequest,
getSupportedKeySystem,
addSession,
addPendingSessions
addPendingSessions,
getSupportedConfigurations
} from '../src/eme';
import sinon from 'sinon';

Expand Down Expand Up @@ -1043,3 +1044,102 @@ QUnit.test('addPendingSessions reuses saved options', function(assert) {
done();
});
});

QUnit.module('videojs-contrib-eme getSupportedConfigurations');

QUnit.test('includes audio and video content types', function(assert) {
assert.deepEqual(
getSupportedConfigurations({
audioContentType: 'audio/mp4; codecs="mp4a.40.2"',
videoContentType: 'video/mp4; codecs="avc1.42E01E"'
}),
[{
audioCapabilities: [{
contentType: 'audio/mp4; codecs="mp4a.40.2"'
}],
videoCapabilities: [{
contentType: 'video/mp4; codecs="avc1.42E01E"'
}]
}],
'included audio and video content types'
);
});

QUnit.test('includes audio and video robustness', function(assert) {
assert.deepEqual(
getSupportedConfigurations({
audioRobustness: 'SW_SECURE_CRYPTO',
videoRobustness: 'SW_SECURE_CRYPTO'
}),
[{
audioCapabilities: [{
robustness: 'SW_SECURE_CRYPTO'
}],
videoCapabilities: [{
robustness: 'SW_SECURE_CRYPTO'
}]
}],
'included audio and video robustness'
);
});

QUnit.test('includes audio and video content types and robustness', function(assert) {
assert.deepEqual(
getSupportedConfigurations({
audioContentType: 'audio/mp4; codecs="mp4a.40.2"',
audioRobustness: 'SW_SECURE_CRYPTO',
videoContentType: 'video/mp4; codecs="avc1.42E01E"',
videoRobustness: 'SW_SECURE_CRYPTO'
}),
[{
audioCapabilities: [{
contentType: 'audio/mp4; codecs="mp4a.40.2"',
robustness: 'SW_SECURE_CRYPTO'
}],
videoCapabilities: [{
contentType: 'video/mp4; codecs="avc1.42E01E"',
robustness: 'SW_SECURE_CRYPTO'
}]
}],
'included audio and video content types and robustness'
);
});

QUnit.test('uses supportedConfigurations directly if provided', function(assert) {
assert.deepEqual(
getSupportedConfigurations({
supportedConfigurations: [{
initDataTypes: ['cenc'],
audioCapabilities: [{
contentType: 'audio/mp4; codecs="mp4a.40.2"',
robustness: 'SW_SECURE_CRYPTO',
extraOption: 'audio-extra'
}],
videoCapabilities: [{
contentType: 'video/mp4; codecs="avc1.42E01E"',
robustness: 'SW_SECURE_CRYPTO',
extraOption: 'video-extra'
}]
}],
// should not be used
audioContentType: 'audio/mp4; codecs="mp4a.40.5"',
audioRobustness: 'HW_SECURE_CRYPTO',
videoContentType: 'video/mp4; codecs="avc1.42001e"',
videoRobustness: 'HW_SECURE_CRYPTO'
}),
[{
initDataTypes: ['cenc'],
audioCapabilities: [{
contentType: 'audio/mp4; codecs="mp4a.40.2"',
robustness: 'SW_SECURE_CRYPTO',
extraOption: 'audio-extra'
}],
videoCapabilities: [{
contentType: 'video/mp4; codecs="avc1.42E01E"',
robustness: 'SW_SECURE_CRYPTO',
extraOption: 'video-extra'
}]
}],
'used supportedConfigurations directly'
);
});

0 comments on commit 502c8ea

Please sign in to comment.