From bce846bfed3f88b04863e51a567e67abad25c1e1 Mon Sep 17 00:00:00 2001 From: Adam Waldron Date: Mon, 11 Mar 2024 18:22:10 -0700 Subject: [PATCH] add additional eme errors --- src/eme.js | 190 +++++++++++++++++++++++++++++--------------------- src/plugin.js | 11 +-- 2 files changed, 116 insertions(+), 85 deletions(-) diff --git a/src/eme.js b/src/eme.js index 8c883ef..3c28f02 100644 --- a/src/eme.js +++ b/src/eme.js @@ -71,7 +71,7 @@ export const getSupportedConfigurations = (keySystem, keySystemOptions) => { return [supportedConfiguration]; }; -export const getSupportedKeySystem = (keySystems) => { +export const getSupportedKeySystem = (keySystems, emeError) => { // 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). @@ -82,10 +82,14 @@ export const getSupportedKeySystem = (keySystems) => { if (!promise) { promise = - window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations); + window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations).catch((error) => { + emeError(error, videojs.Error.EMEFailedToRequestMediaKeySystemAccess); + }); } else { - promise = promise.catch((e) => - window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations)); + promise = promise.catch(() => { + // TODO: Not sure if we want to call emeError here or not. + window.navigator.requestMediaKeySystemAccess(keySystem, supportedConfigurations); + }); } }); @@ -101,84 +105,97 @@ export const makeNewRequest = (player, requestOptions) => { getLicense, removeSession, eventBus, - contentId + contentId, + emeError } = requestOptions; - const keySession = mediaKeys.createSession(); - - eventBus.trigger('keysessioncreated'); + try { + const keySession = mediaKeys.createSession(); - player.on('dispose', () => { - keySession.close(); - }); + eventBus.trigger('keysessioncreated'); - return new Promise((resolve, reject) => { - keySession.addEventListener('message', (event) => { - // all other types will be handled by keystatuseschange - if (event.messageType !== 'license-request' && event.messageType !== 'license-renewal') { - return; - } + player.on('dispose', () => { + keySession.close().catch((error) => { + emeError(error, videojs.Error.EMEFailedToCloseSession); + }); + }); - getLicense(options, event.message, contentId) - .then((license) => { - resolve(keySession.update(license)); - }) - .catch((err) => { - reject(err); - }); - }, false); - - keySession.addEventListener('keystatuseschange', (event) => { - let expired = false; - - // based on https://www.w3.org/TR/encrypted-media/#example-using-all-events - keySession.keyStatuses.forEach((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, - 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': - const message = - 'Key status reported as "internal-error." Leaving the session open since we ' + - 'don\'t have enough details to know if this error is fatal.'; - - // "This value is not actionable by the application." - // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-internal-error - videojs.log.warn(message, event); - break; + return new Promise((resolve, reject) => { + keySession.addEventListener('message', (event) => { + // all other types will be handled by keystatuseschange + if (event.messageType !== 'license-request' && event.messageType !== 'license-renewal') { + return; } - }); - 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(() => { - removeSession(initData); - makeNewRequest(player, requestOptions); + getLicense(options, event.message, contentId) + .then((license) => { + resolve(keySession.update(license).catch((error) => { + emeError(error, videojs.Error.EMEFailedToUpdateSessionWithReceivedLicenseKeys); + })); + }) + .catch((err) => { + reject(err); + }); + }, false); + + keySession.addEventListener('keystatuseschange', (event) => { + let expired = false; + + // based on https://www.w3.org/TR/encrypted-media/#example-using-all-events + keySession.keyStatuses.forEach((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, + 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': + const message = + 'Key status reported as "internal-error." Leaving the session open since we ' + + 'don\'t have enough details to know if this error is fatal.'; + + // "This value is not actionable by the application." + // https://www.w3.org/TR/encrypted-media/#dom-mediakeystatus-internal-error + videojs.log.warn(message, event); + break; + } }); - } - }, false); - keySession.generateRequest(initDataType, initData).catch(() => { - reject('Unable to create or initialize key session'); + 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(() => { + removeSession(initData); + makeNewRequest(player, requestOptions); + }).catch((error) => { + emeError(error, videojs.Error.EMEFailedToCloseSession); + }); + } + }, false); + + keySession.generateRequest(initDataType, initData).catch((error) => { + emeError(error, videojs.Error.EMEFailedToGenerateLicenseRequest); + reject('Unable to create or initialize key session'); + }); }); - }); + + } catch (error) { + emeError(error, videojs.Error.EMEFailedToCreateMediaKeySession); + } }; /* @@ -217,7 +234,8 @@ export const addSession = ({ getLicense, contentId, removeSession, - eventBus + eventBus, + emeError }) => { const sessionData = { initDataType, @@ -226,7 +244,8 @@ export const addSession = ({ getLicense, removeSession, eventBus, - contentId + contentId, + emeError }; if (video.mediaKeysObject) { @@ -262,7 +281,8 @@ export const addPendingSessions = ({ player, video, certificate, - createdMediaKeys + createdMediaKeys, + emeError }) => { // save media keys on the video element to act as a reference for other functions so // that they don't recreate the keys @@ -270,7 +290,9 @@ export const addPendingSessions = ({ const promises = []; if (certificate) { - promises.push(createdMediaKeys.setServerCertificate(certificate)); + promises.push(createdMediaKeys.setServerCertificate(certificate).catch((error) => { + emeError(error, videojs.Error.EMEFailedToSetServerCertificate); + })); } for (let i = 0; i < video.pendingSessionData.length; i++) { @@ -284,13 +306,17 @@ export const addPendingSessions = ({ getLicense: data.getLicense, removeSession: data.removeSession, eventBus: data.eventBus, - contentId: data.contentId + contentId: data.contentId, + emeError: data.emeError })); } video.pendingSessionData = []; - promises.push(video.setMediaKeys(createdMediaKeys)); + promises.push(video.setMediaKeys(createdMediaKeys).catch((error) => { + // EMEFailedToAttachMediaKeysToVideoElement + emeError(error, videojs.Error.EMEFailedToAttachMediaKeysToVideoElement); + })); return Promise.all(promises); }; @@ -388,7 +414,8 @@ export const standard5July2016 = ({ keySystemAccess, options, removeSession, - eventBus + eventBus, + emeError }) => { let keySystemPromise = Promise.resolve(); const keySystem = keySystemAccess.keySystem; @@ -445,6 +472,8 @@ export const standard5July2016 = ({ createdMediaKeys }); }).catch((err) => { + emeError(err, videojs.Error.EMEFailedToCreateMediaKeys); + // EMEFailedToCreateMediaKeys // if we have a specific error message, use it, otherwise show a more // generic one if (err) { @@ -468,7 +497,8 @@ export const standard5July2016 = ({ getLicense, contentId, removeSession, - eventBus + eventBus, + emeError }); }); }; diff --git a/src/plugin.js b/src/plugin.js index 7afd3bd..0170b4f 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -50,7 +50,7 @@ export const removeSession = (sessions, initData) => { } }; -export const handleEncryptedEvent = (player, event, options, sessions, eventBus) => { +export const handleEncryptedEvent = (player, event, options, sessions, eventBus, emeError) => { if (!options || !options.keySystems) { // return silently since it may be handled by a different system return Promise.resolve(); @@ -58,7 +58,7 @@ export const handleEncryptedEvent = (player, event, options, sessions, eventBus) let initData = event.initData; - return getSupportedKeySystem(options.keySystems).then((keySystemAccess) => { + return getSupportedKeySystem(options.keySystems, emeError).then((keySystemAccess) => { const keySystem = keySystemAccess.keySystem; // Use existing init data from options if provided @@ -90,7 +90,8 @@ export const handleEncryptedEvent = (player, event, options, sessions, eventBus) keySystemAccess, options, removeSession: removeSession.bind(null, sessions), - eventBus + eventBus, + emeError }); }); }; @@ -245,7 +246,7 @@ const onPlayerReady = (player, emeError) => { player.tech_.el_.addEventListener('encrypted', (event) => { videojs.log.debug('eme', 'Received an \'encrypted\' event'); setupSessions(player); - handleEncryptedEvent(player, event, getOptions(player), player.eme.sessions, player.tech_) + handleEncryptedEvent(player, event, getOptions(player), player.eme.sessions, player.tech_, emeError) .catch((error) => { emeError(error, videojs.Error.EMEEncryptedError); }); @@ -329,7 +330,7 @@ const eme = function(options = {}) { setupSessions(player); if (window.MediaKeys) { - handleEncryptedEvent(player, mockEncryptedEvent, mergedEmeOptions, player.eme.sessions, player.tech_) + handleEncryptedEvent(player, mockEncryptedEvent, mergedEmeOptions, player.eme.sessions, player.tech_, emeError) .then(() => callback()) .catch((error) => { callback(error);