UNPKG

videojs-contrib-eme

Version:

Supports Encrypted Media Extensions for playback of encrypted content in Video.js

936 lines (812 loc) 32.7 kB
/*! @name videojs-contrib-eme @version 3.4.0 @license Apache-2.0 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('video.js'), require('global/window'), require('global/document')) : typeof define === 'function' && define.amd ? define(['exports', 'video.js', 'global/window', 'global/document'], factory) : (factory((global.videojsContribEme = {}),global.videojs,global.window,global.document)); }(this, (function (exports,videojs,window,document) { 'use strict'; videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs; window = window && window.hasOwnProperty('default') ? window['default'] : window; document = document && document.hasOwnProperty('default') ? document['default'] : document; /** * Parses the EME key message XML to extract HTTP headers and the Challenge element to use * in the PlayReady license request. * * @param {ArrayBuffer} message key message from EME * @return {Object} an object containing headers and the message body to use in the * license request */ var getMessageContents = function getMessageContents(message) { var xml = new window.DOMParser().parseFromString( // TODO do we want to support UTF-8? String.fromCharCode.apply(null, new Uint16Array(message)), 'application/xml'); var headersElement = xml.getElementsByTagName('HttpHeaders')[0]; var headers = {}; if (headersElement) { var headerNames = headersElement.getElementsByTagName('name'); var headerValues = headersElement.getElementsByTagName('value'); for (var i = 0; i < headerNames.length; i++) { headers[headerNames[i].childNodes[0].nodeValue] = headerValues[i].childNodes[0].nodeValue; } } var challengeElement = xml.getElementsByTagName('Challenge')[0]; var challenge; if (challengeElement) { challenge = window.atob(challengeElement.childNodes[0].nodeValue); } return { headers: headers, message: challenge }; }; var requestPlayreadyLicense = function requestPlayreadyLicense(url, messageBuffer, callback) { var _getMessageContents = getMessageContents(messageBuffer), headers = _getMessageContents.headers, message = _getMessageContents.message; videojs.xhr({ uri: url, method: 'post', headers: headers, body: message, responseType: 'arraybuffer' }, callback); }; var getSupportedKeySystem = function 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). var promise; Object.keys(keySystems).forEach(function (keySystem) { // TODO use initDataTypes when appropriate var systemOptions = {}; var audioContentType = keySystems[keySystem].audioContentType; var videoContentType = keySystems[keySystem].videoContentType; if (audioContentType) { systemOptions.audioCapabilities = [{ contentType: audioContentType }]; } if (videoContentType) { systemOptions.videoCapabilities = [{ contentType: videoContentType }]; } if (!promise) { promise = window.navigator.requestMediaKeySystemAccess(keySystem, [systemOptions]); } else { promise = promise.catch(function (e) { return window.navigator.requestMediaKeySystemAccess(keySystem, [systemOptions]); }); } }); return promise; }; var makeNewRequest = function makeNewRequest(_ref) { var mediaKeys = _ref.mediaKeys, initDataType = _ref.initDataType, initData = _ref.initData, options = _ref.options, getLicense = _ref.getLicense, removeSession = _ref.removeSession, eventBus = _ref.eventBus; var keySession = mediaKeys.createSession(); keySession.addEventListener('message', function (event) { getLicense(options, event.message).then(function (license) { return keySession.update(license); }).catch(videojs.log.error.bind(videojs.log.error, 'failed to get and set license')); }, false); keySession.addEventListener('keystatuseschange', function (event) { var expired = false; // based on https://www.w3.org/TR/encrypted-media/#example-using-all-events keySession.keyStatuses.forEach(function (status, keyId) { // Trigger an event so that outside listeners can take action if appropriate. // For instance, the `output-restricted` status should result in an // error being thrown. eventBus.trigger({ keyId: keyId, status: status, target: keySession, type: 'keystatuschange' }); switch (status) { case 'expired': // If one key is expired in a session, all keys are expired. From // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-expired, "All other // keys in the session must have this status." expired = true; break; case 'internal-error': // "This value is not actionable by the application." // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-internal-error videojs.log.warn('Key status reported as "internal-error." Leaving the session open since we ' + 'don\'t have enough details to know if this error is fatal.', event); break; } }); if (expired) { // Close session and remove it from the session list to ensure that a new // session can be created. // // TODO convert to videojs.log.debug and add back in // https://github.com/videojs/video.js/pull/4780 // videojs.log.debug('Session expired, closing the session.'); keySession.close().then(function () { removeSession(initData); }); } }, false); keySession.generateRequest(initDataType, initData).catch(videojs.log.error.bind(videojs.log.error, 'Unable to create or initialize key session')); }; var addSession = function addSession(_ref2) { var video = _ref2.video, initDataType = _ref2.initDataType, initData = _ref2.initData, options = _ref2.options, getLicense = _ref2.getLicense, removeSession = _ref2.removeSession, eventBus = _ref2.eventBus; if (video.mediaKeysObject) { makeNewRequest({ mediaKeys: video.mediaKeysObject, initDataType: initDataType, initData: initData, options: options, getLicense: getLicense, removeSession: removeSession, eventBus: eventBus }); } else { video.pendingSessionData.push({ initDataType: initDataType, initData: initData }); } }; var setMediaKeys = function setMediaKeys(_ref3) { var video = _ref3.video, certificate = _ref3.certificate, createdMediaKeys = _ref3.createdMediaKeys, options = _ref3.options, getLicense = _ref3.getLicense, removeSession = _ref3.removeSession, eventBus = _ref3.eventBus; video.mediaKeysObject = createdMediaKeys; if (certificate) { createdMediaKeys.setServerCertificate(certificate); } for (var i = 0; i < video.pendingSessionData.length; i++) { var data = video.pendingSessionData[i]; makeNewRequest({ mediaKeys: video.mediaKeysObject, initDataType: data.initDataType, initData: data.initData, options: options, getLicense: getLicense, removeSession: removeSession, eventBus: eventBus }); } video.pendingSessionData = []; return video.setMediaKeys(createdMediaKeys); }; var defaultPlayreadyGetLicense = function defaultPlayreadyGetLicense(url) { return function (emeOptions, keyMessage, callback) { requestPlayreadyLicense(url, keyMessage, function (err, response, responseBody) { if (err) { callback(err); return; } callback(null, responseBody); }); }; }; var defaultGetLicense = function defaultGetLicense(url) { return function (emeOptions, keyMessage, callback) { videojs.xhr({ uri: url, method: 'POST', responseType: 'arraybuffer', body: keyMessage, headers: { 'Content-type': 'application/octet-stream' } }, function (err, response, responseBody) { if (err) { callback(err); return; } callback(null, responseBody); }); }; }; var promisifyGetLicense = function promisifyGetLicense(getLicenseFn, eventBus) { return function (emeOptions, keyMessage) { return new Promise(function (resolve, reject) { getLicenseFn(emeOptions, keyMessage, function (err, license) { if (eventBus) { eventBus.trigger('licenserequestattempted'); } if (err) { reject(err); } resolve(license); }); }); }; }; var standardizeKeySystemOptions = function standardizeKeySystemOptions(keySystem, keySystemOptions) { if (typeof keySystemOptions === 'string') { keySystemOptions = { url: keySystemOptions }; } if (!keySystemOptions.url && !keySystemOptions.getLicense) { throw new Error('Neither URL nor getLicense function provided to get license'); } if (keySystemOptions.url && !keySystemOptions.getLicense) { keySystemOptions.getLicense = keySystem === 'com.microsoft.playready' ? defaultPlayreadyGetLicense(keySystemOptions.url) : defaultGetLicense(keySystemOptions.url); } return keySystemOptions; }; var standard5July2016 = function standard5July2016(_ref4) { var video = _ref4.video, initDataType = _ref4.initDataType, initData = _ref4.initData, options = _ref4.options, removeSession = _ref4.removeSession, eventBus = _ref4.eventBus; var keySystemPromise = Promise.resolve(); if (typeof video.mediaKeysObject === 'undefined') { // Prevent entering this path again. video.mediaKeysObject = null; // Will store all initData until the MediaKeys is ready. video.pendingSessionData = []; var certificate; var keySystemOptions; keySystemPromise = getSupportedKeySystem(options.keySystems); if (!keySystemPromise) { videojs.log.error('No supported key system found'); return Promise.resolve(); } keySystemPromise = keySystemPromise.then(function (keySystemAccess) { return new Promise(function (resolve, reject) { // save key system for adding sessions video.keySystem = keySystemAccess.keySystem; keySystemOptions = standardizeKeySystemOptions(keySystemAccess.keySystem, options.keySystems[keySystemAccess.keySystem]); if (!keySystemOptions.getCertificate) { resolve(keySystemAccess); return; } keySystemOptions.getCertificate(options, function (err, cert) { if (err) { reject(err); return; } certificate = cert; resolve(keySystemAccess); }); }); }).then(function (keySystemAccess) { return keySystemAccess.createMediaKeys(); }).then(function (createdMediaKeys) { return setMediaKeys({ video: video, certificate: certificate, createdMediaKeys: createdMediaKeys, options: options, getLicense: promisifyGetLicense(keySystemOptions.getLicense, eventBus), removeSession: removeSession, eventBus: eventBus }); }).catch(videojs.log.error.bind(videojs.log.error, 'Failed to create and initialize a MediaKeys object')); } return keySystemPromise.then(function () { addSession({ video: video, initDataType: initDataType, initData: initData, options: options, // if key system has not been determined then addSession doesn't need getLicense getLicense: video.keySystem ? promisifyGetLicense(standardizeKeySystemOptions(video.keySystem, options.keySystems[video.keySystem]).getLicense, eventBus) : null, removeSession: removeSession, eventBus: eventBus }); }); }; var stringToUint16Array = function stringToUint16Array(string) { // 2 bytes for each char var buffer = new ArrayBuffer(string.length * 2); var array = new Uint16Array(buffer); for (var i = 0; i < string.length; i++) { array[i] = string.charCodeAt(i); } return array; }; var uint8ArrayToString = function uint8ArrayToString(array) { return String.fromCharCode.apply(null, new Uint16Array(array.buffer)); }; var getHostnameFromUri = function getHostnameFromUri(uri) { var link = document.createElement('a'); link.href = uri; return link.hostname; }; var arrayBuffersEqual = function arrayBuffersEqual(arrayBuffer1, arrayBuffer2) { if (arrayBuffer1 === arrayBuffer2) { return true; } if (arrayBuffer1.byteLength !== arrayBuffer2.byteLength) { return false; } var dataView1 = new DataView(arrayBuffer1); var dataView2 = new DataView(arrayBuffer2); for (var i = 0; i < dataView1.byteLength; i++) { if (dataView1.getUint8(i) !== dataView2.getUint8(i)) { return false; } } return true; }; var arrayBufferFrom = function arrayBufferFrom(bufferOrTypedArray) { if (bufferOrTypedArray instanceof Uint8Array || bufferOrTypedArray instanceof Uint16Array) { return bufferOrTypedArray.buffer; } return bufferOrTypedArray; }; var FAIRPLAY_KEY_SYSTEM = 'com.apple.fps.1_0'; var concatInitDataIdAndCertificate = function concatInitDataIdAndCertificate(_ref) { var initData = _ref.initData, id = _ref.id, cert = _ref.cert; if (typeof id === 'string') { id = stringToUint16Array(id); } // layout: // [initData] // [4 byte: idLength] // [idLength byte: id] // [4 byte:certLength] // [certLength byte: cert] var offset = 0; var buffer = new ArrayBuffer(initData.byteLength + 4 + id.byteLength + 4 + cert.byteLength); var dataView = new DataView(buffer); var initDataArray = new Uint8Array(buffer, offset, initData.byteLength); initDataArray.set(initData); offset += initData.byteLength; dataView.setUint32(offset, id.byteLength, true); offset += 4; var idArray = new Uint16Array(buffer, offset, id.length); idArray.set(id); offset += idArray.byteLength; dataView.setUint32(offset, cert.byteLength, true); offset += 4; var certArray = new Uint8Array(buffer, offset, cert.byteLength); certArray.set(cert); return new Uint8Array(buffer, 0, buffer.byteLength); }; var addKey = function addKey(_ref2) { var video = _ref2.video, contentId = _ref2.contentId, initData = _ref2.initData, cert = _ref2.cert, options = _ref2.options, getLicense = _ref2.getLicense, eventBus = _ref2.eventBus; return new Promise(function (resolve, reject) { if (!video.webkitKeys) { video.webkitSetMediaKeys(new window.WebKitMediaKeys(FAIRPLAY_KEY_SYSTEM)); } if (!video.webkitKeys) { reject('Could not create MediaKeys'); return; } var keySession = video.webkitKeys.createSession('video/mp4', concatInitDataIdAndCertificate({ id: contentId, initData: initData, cert: cert })); if (!keySession) { reject('Could not create key session'); return; } keySession.contentId = contentId; keySession.addEventListener('webkitkeymessage', function (event) { getLicense(options, contentId, event.message, function (err, license) { if (eventBus) { eventBus.trigger('licenserequestattempted'); } if (err) { reject(err); return; } keySession.update(new Uint8Array(license)); }); }); keySession.addEventListener('webkitkeyadded', function (event) { resolve(event); }); // for testing purposes, adding webkitkeyerror must be the last item in this method keySession.addEventListener('webkitkeyerror', function (event) { reject(event); }); }); }; var defaultGetCertificate = function defaultGetCertificate(certificateUri) { return function (emeOptions, callback) { videojs.xhr({ uri: certificateUri, responseType: 'arraybuffer' }, function (err, response, responseBody) { if (err) { callback(err); return; } callback(null, new Uint8Array(responseBody)); }); }; }; var defaultGetContentId = function defaultGetContentId(emeOptions, initData) { return getHostnameFromUri(uint8ArrayToString(initData)); }; var defaultGetLicense$1 = function defaultGetLicense(licenseUri) { return function (emeOptions, contentId, keyMessage, callback) { videojs.xhr({ uri: licenseUri, method: 'POST', responseType: 'arraybuffer', body: keyMessage, headers: { 'Content-type': 'application/octet-stream' } }, function (err, response, responseBody) { if (err) { callback(err); return; } callback(null, responseBody); }); }; }; var fairplay = function fairplay(_ref3) { var video = _ref3.video, initData = _ref3.initData, options = _ref3.options, eventBus = _ref3.eventBus; var fairplayOptions = options.keySystems[FAIRPLAY_KEY_SYSTEM]; var getCertificate = fairplayOptions.getCertificate || defaultGetCertificate(fairplayOptions.certificateUri); var getContentId = fairplayOptions.getContentId || defaultGetContentId; var getLicense = fairplayOptions.getLicense || defaultGetLicense$1(fairplayOptions.licenseUri); return new Promise(function (resolve, reject) { getCertificate(options, function (err, cert) { if (err) { reject(err); return; } resolve(cert); }); }).then(function (cert) { return addKey({ video: video, cert: cert, initData: initData, getLicense: getLicense, options: options, contentId: getContentId(options, initData), eventBus: eventBus }); }).catch(videojs.log.error.bind(videojs.log.error)); }; var PLAYREADY_KEY_SYSTEM = 'com.microsoft.playready'; var addKeyToSession = function addKeyToSession(options, session, event, eventBus) { var playreadyOptions = options.keySystems[PLAYREADY_KEY_SYSTEM]; if (typeof playreadyOptions.getKey === 'function') { playreadyOptions.getKey(options, event.destinationURL, event.message.buffer, function (err, key) { if (err) { videojs.log.error('Unable to get key: ' + err); return; } session.update(key); }); return; } if (typeof playreadyOptions === 'string') { playreadyOptions = { url: playreadyOptions }; } var url = playreadyOptions.url || event.destinationURL; requestPlayreadyLicense(url, event.message.buffer, function (err, response) { if (eventBus) { eventBus.trigger('licenserequestattempted'); } if (err) { videojs.log.error('Unable to request key from url: ' + url); return; } session.update(new Uint8Array(response.body)); }); }; var createSession = function createSession(video, initData, options, eventBus) { var session = video.msKeys.createSession('video/mp4', initData); if (!session) { videojs.log.error('Could not create key session.'); return; } // Note that mskeymessage may not always be called for PlayReady: // // "If initData contains a PlayReady object that contains an OnDemand header, only a // keyAdded event is returned (as opposed to a keyMessage event as described in the // Encrypted Media Extension draft). Similarly, if initData contains a PlayReady object // that contains a key identifier in the hashed data storage (HDS), only a keyAdded // event is returned." // eslint-disable-next-line max-len // @see [PlayReady License Acquisition]{@link https://msdn.microsoft.com/en-us/library/dn468979.aspx} session.addEventListener('mskeymessage', function (event) { addKeyToSession(options, session, event, eventBus); }); session.addEventListener('mskeyerror', function (event) { videojs.log.error('Unexpected key error from key session with ' + ("code: " + session.error.code + " and systemCode: " + session.error.systemCode)); }); }; var msPrefixed = (function (_ref) { var video = _ref.video, initData = _ref.initData, options = _ref.options, eventBus = _ref.eventBus; // Although by the standard examples the presence of video.msKeys is checked first to // verify that we aren't trying to create a new session when one already exists, here // sessions are managed earlier (on the player.eme object), meaning that at this point // any existing keys should be cleaned up. if (video.msKeys) { delete video.msKeys; } try { video.msSetMediaKeys(new window.MSMediaKeys(PLAYREADY_KEY_SYSTEM)); } catch (e) { videojs.log.error('Unable to create media keys for PlayReady key system. Error: ' + e.message); return; } createSession(video, initData, options, eventBus); }); var hasSession = function hasSession(sessions, initData) { for (var i = 0; i < sessions.length; i++) { // Other types of sessions may be in the sessions array that don't store the initData // (for instance, PlayReady sessions on IE11). if (!sessions[i].initData) { continue; } // initData should be an ArrayBuffer by the spec: // eslint-disable-next-line max-len // @see [Media Encrypted Event initData Spec]{@link https://www.w3.org/TR/encrypted-media/#mediaencryptedeventinit} // // However, on some browsers it may come back with a typed array view of the buffer. // This is the case for IE11, however, since IE11 sessions are handled differently // (following the msneedkey PlayReady path), this coversion may not be important. It // is safe though, and might be a good idea to retain in the short term (until we have // catalogued the full range of browsers and their implementations). var sessionBuffer = arrayBufferFrom(sessions[i].initData); var initDataBuffer = arrayBufferFrom(initData); if (arrayBuffersEqual(sessionBuffer, initDataBuffer)) { return true; } } return false; }; var removeSession = function removeSession(sessions, initData) { for (var i = 0; i < sessions.length; i++) { if (sessions[i].initData === initData) { sessions.splice(i, 1); return; } } }; var handleEncryptedEvent = function handleEncryptedEvent(event, options, sessions, eventBus) { if (!options || !options.keySystems) { // return silently since it may be handled by a different system return Promise.resolve(); } var initData = event.initData; return getSupportedKeySystem(options.keySystems).then(function (_ref) { var keySystem = _ref.keySystem; // Use existing init data from options if provided if (options.keySystems[keySystem] && options.keySystems[keySystem].pssh) { initData = options.keySystems[keySystem].pssh; } // "Initialization Data must be a fixed value for a given set of stream(s) or media // data. It must only contain information related to the keys required to play a given // set of stream(s) or media data." // eslint-disable-next-line max-len // @see [Initialization Data Spec]{@link https://www.w3.org/TR/encrypted-media/#initialization-data} if (hasSession(sessions, initData)) { // TODO convert to videojs.log.debug and add back in // https://github.com/videojs/video.js/pull/4780 // videojs.log('eme', // 'Already have a configured session for init data, ignoring event.'); return; } sessions.push({ initData: initData }); return standard5July2016({ video: event.target, initDataType: event.initDataType, initData: initData, options: options, removeSession: removeSession.bind(null, sessions), eventBus: eventBus }); }); }; var handleWebKitNeedKeyEvent = function handleWebKitNeedKeyEvent(event, options, eventBus) { if (!options.keySystems || !options.keySystems[FAIRPLAY_KEY_SYSTEM]) { // return silently since it may be handled by a different system return; } // From Apple's example Safari FairPlay integration code, webkitneedkey is not repeated // for the same content. Unless documentation is found to present the opposite, handle // all webkitneedkey events the same (even if they are repeated). return fairplay({ video: event.target, initData: event.initData, options: options, eventBus: eventBus }); }; var handleMsNeedKeyEvent = function handleMsNeedKeyEvent(event, options, sessions, eventBus) { if (!options.keySystems || !options.keySystems[PLAYREADY_KEY_SYSTEM]) { // return silently since it may be handled by a different system return; } // "With PlayReady content protection, your Web app must handle the first needKey event, // but it must then ignore any other needKey event that occurs." // eslint-disable-next-line max-len // @see [PlayReady License Acquisition]{@link https://msdn.microsoft.com/en-us/library/dn468979.aspx} // // Usually (and as per the example in the link above) this is determined by checking for // the existence of video.msKeys. However, since the video element may be reused, it's // easier to directly manage the session. if (sessions.reduce(function (acc, session) { return acc || session.playready; }, false)) { // TODO convert to videojs.log.debug and add back in // https://github.com/videojs/video.js/pull/4780 // videojs.log('eme', // 'An \'msneedkey\' event was receieved earlier, ignoring event.'); return; } var initData = event.initData; // Use existing init data from options if provided if (options.keySystems[PLAYREADY_KEY_SYSTEM] && options.keySystems[PLAYREADY_KEY_SYSTEM].pssh) { initData = options.keySystems[PLAYREADY_KEY_SYSTEM].pssh; } sessions.push({ playready: true, initData: initData }); msPrefixed({ video: event.target, initData: initData, options: options, eventBus: eventBus }); }; var getOptions = function getOptions(player) { return videojs.mergeOptions(player.currentSource(), player.eme.options); }; /** * Configure a persistent sessions array and activeSrc property to ensure we properly * handle each independent source's events. Should be run on any encrypted or needkey * style event to ensure that the sessions reflect the active source. * * @function setupSessions * @param {Player} player */ var setupSessions = function setupSessions(player) { var src = player.src(); if (src !== player.eme.activeSrc) { player.eme.activeSrc = src; player.eme.sessions = []; } }; /** * Function to invoke when the player is ready. * * This is a great place for your plugin to initialize itself. When this * function is called, the player will have its DOM and child components * in place. * * @function onPlayerReady * @param {Player} player * @param {Object} [options={}] */ var onPlayerReady = function onPlayerReady(player) { if (player.$('.vjs-tech').tagName.toLowerCase() !== 'video') { return; } setupSessions(player); // Support EME 05 July 2016 // Chrome 42+, Firefox 47+, Edge player.tech_.el_.addEventListener('encrypted', function (event) { // TODO convert to videojs.log.debug and add back in // https://github.com/videojs/video.js/pull/4780 // videojs.log('eme', 'Received an \'encrypted\' event'); setupSessions(player); handleEncryptedEvent(event, getOptions(player), player.eme.sessions, player.tech_); }); // Support Safari EME with FairPlay // (also used in early Chrome or Chrome with EME disabled flag) player.tech_.el_.addEventListener('webkitneedkey', function (event) { // TODO convert to videojs.log.debug and add back in // https://github.com/videojs/video.js/pull/4780 // videojs.log('eme', 'Received a \'webkitneedkey\' event'); // TODO it's possible that the video state must be cleared if reusing the same video // element between sources setupSessions(player); handleWebKitNeedKeyEvent(event, getOptions(player), player.tech_); }); // EDGE still fires msneedkey, but should use encrypted instead if (videojs.browser.IS_EDGE) { return; } // IE11 Windows 8.1+ player.tech_.el_.addEventListener('msneedkey', function (event) { // TODO convert to videojs.log.debug and add back in // https://github.com/videojs/video.js/pull/4780 // videojs.log('eme', 'Received an \'msneedkey\' event'); setupSessions(player); handleMsNeedKeyEvent(event, getOptions(player), player.eme.sessions, player.tech_); }); }; /** * A video.js plugin. * * In the plugin function, the value of `this` is a video.js `Player` * instance. You cannot rely on the player being in a "ready" state here, * depending on how the plugin is invoked. This may or may not be important * to you; if not, remove the wait for "ready"! * * @function eme * @param {Object} [options={}] * An object of options left to the plugin author to define. */ var eme = function eme(options) { if (options === void 0) { options = {}; } var player = this; player.ready(function () { return 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: function initializeMediaKeys(emeOptions, callback) { if (emeOptions === void 0) { emeOptions = {}; } if (callback === void 0) { callback = function callback() {}; } // TODO: this should be refactored and renamed to be less tied // to encrypted events var mergedEmeOptions = videojs.mergeOptions(player.currentSource(), options, emeOptions); // fake an encrypted event for handleEncryptedEvent var 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(function () { return callback(); }).catch(function (error) { return callback(error); }); } else if (player.tech_.el_.msSetMediaKeys) { handleMsNeedKeyEvent(mockEncryptedEvent, mergedEmeOptions, player.eme.sessions, player.tech_); callback(); } }, options: options }; }; // Register the plugin with video.js. var registerPlugin = videojs.registerPlugin || videojs.plugin; registerPlugin('eme', eme); exports.hasSession = hasSession; exports.removeSession = removeSession; exports.handleEncryptedEvent = handleEncryptedEvent; exports.handleWebKitNeedKeyEvent = handleWebKitNeedKeyEvent; exports.handleMsNeedKeyEvent = handleMsNeedKeyEvent; exports.getOptions = getOptions; exports.setupSessions = setupSessions; exports.default = eme; Object.defineProperty(exports, '__esModule', { value: true }); })));