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

fix: initialize EME for all playlists and PSSH values #872

Merged
merged 2 commits into from
Jul 7, 2020
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
98 changes: 94 additions & 4 deletions src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,75 @@ const emeKeySystems = (keySystemOptions, videoPlaylist, audioPlaylist) => {
return videojs.mergeOptions(keySystemOptions, keySystemContentTypes);
};

/**
* @typedef {Object} KeySystems
*
* keySystems configuration for https://github.com/videojs/videojs-contrib-eme
* Note: not all options are listed here.
*
* @property {Uint8Array} [pssh]
* Protection System Specific Header
*/

/**
* Goes through all the playlists and collects an array of KeySystems options objects
* containing each playlist's keySystems and their pssh values, if available.
*
* @param {Object[]} playlists
* The playlists to look through
* @param {string[]} keySystems
* The keySystems to collect pssh values for
*
* @return {KeySystems[]}
* An array of KeySystems objects containing available key systems and their
* pssh values
*/
const getAllPsshKeySystemsOptions = (playlists, keySystems) => {
return playlists.reduce((keySystemsArr, playlist) => {
if (!playlist.contentProtection) {
return keySystemsArr;
}

const keySystemsOptions = keySystems.reduce((keySystemsObj, keySystem) => {
const keySystemOptions = playlist.contentProtection[keySystem];

if (keySystemOptions && keySystemOptions.pssh) {
keySystemsObj[keySystem] = { pssh: keySystemOptions.pssh };
}

return keySystemsObj;
}, {});

if (Object.keys(keySystemsOptions).length) {
keySystemsArr.push(keySystemsOptions);
}

return keySystemsArr;
}, []);
};

/**
* If the [eme](https://github.com/videojs/videojs-contrib-eme) plugin is available, and
* there are keySystems on the source, sets up source options to prepare the source for
* eme and tries to initialize it early via eme's initializeMediaKeys API (if available).
*
* @param {Object} player
* The player instance
* @param {Object[]} sourceKeySystems
* The key systems options from the player source
* @param {Object} media
* The active media playlist
* @param {Object} [audioMedia]
* The active audio media playlist (optional)
* @param {Object[]} mainPlaylists
* The playlists found on the master playlist object
*/
const setupEmeOptions = ({
player,
sourceKeySystems,
media,
audioMedia
audioMedia,
mainPlaylists
gkatsev marked this conversation as resolved.
Show resolved Hide resolved
}) => {
if (!player.eme) {
return;
Expand All @@ -196,7 +260,31 @@ const setupEmeOptions = ({
return;
}

player.eme.initializeMediaKeys();
// TODO should all audio PSSH values be initialized for DRM?
//
// All unique video rendition pssh values are initialized for DRM, but here only
// the initial audio playlist license is initialized. In theory, an encrypted
// event should be fired if the user switches to an alternative audio playlist
// where a license is required, but this case hasn't yet been tested. In addition, there
// may be many alternate audio playlists unlikely to be used (e.g., multiple different
// languages).
const playlists = audioMedia ? mainPlaylists.concat([audioMedia]) : mainPlaylists;

const keySystemsOptionsArr = getAllPsshKeySystemsOptions(
playlists,
Object.keys(sourceKeySystems)
);

// Since PSSH values are interpreted as initData, EME will dedupe any duplicates. The
// only place where it should not be deduped is for ms-prefixed APIs, but the early
// return for IE11 above, and the existence of modern EME APIs in addition to
// ms-prefixed APIs on Edge should prevent this from being a concern.
// initializeMediaKeys also won't use the webkit-prefixed APIs.
keySystemsOptionsArr.forEach((keySystemsOptions) => {
player.eme.initializeMediaKeys({
keySystems: keySystemsOptions
});
});
};

const getVhsLocalStorage = () => {
Expand Down Expand Up @@ -738,7 +826,8 @@ class VhsHandler extends Component {
player: this.player_,
sourceKeySystems: this.source_.keySystems,
media: this.playlists.media(),
audioMedia: audioPlaylistLoader && audioPlaylistLoader.media()
audioMedia: audioPlaylistLoader && audioPlaylistLoader.media(),
mainPlaylists: this.playlists.master.playlists
});
});

Expand Down Expand Up @@ -1003,5 +1092,6 @@ export {
emeKeySystems,
simpleTypeFromSourceType,
expandDataUri,
setupEmeOptions
setupEmeOptions,
getAllPsshKeySystemsOptions
};
12 changes: 6 additions & 6 deletions test/master-playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2639,13 +2639,13 @@ QUnit.test('trigger event when a video fMP4 stream is detected', function(assert
const mpc = this.player.tech(true).vhs.masterPlaylistController_;
const loader = mpc.mainSegmentLoader_;

// media
this.standardXHRResponse(this.requests.shift());

return setupMediaSource(loader.mediaSource_, loader.sourceUpdater_, {
videoEl: this.player.tech_.el_,
isVideoOnly: true
}).then(() => {
// media
this.standardXHRResponse(this.requests.shift());

assert.equal(hlsFmp4Events, 0, 'an fMP4 stream is not detected');

const initSegmentRequest = this.requests.shift();
Expand Down Expand Up @@ -2727,13 +2727,13 @@ QUnit.test('trigger event when an audio fMP4 stream is detected', function(asser
const mpc = this.player.tech(true).vhs.masterPlaylistController_;
const loader = mpc.mainSegmentLoader_;

// media
this.standardXHRResponse(this.requests.shift());

return setupMediaSource(loader.mediaSource_, loader.sourceUpdater_, {
videoEl: this.player.tech_.el_,
isAudioOnly: true
}).then(() => {
// media
this.standardXHRResponse(this.requests.shift());

assert.equal(vhsFmp4Events, 0, 'an fMP4 stream is not detected');
assert.equal(hlsFmp4Events, 0, 'an fMP4 stream is not detected');

Expand Down
Loading