Skip to content

Commit

Permalink
add additional eme errors
Browse files Browse the repository at this point in the history
  • Loading branch information
adrums86 committed Mar 12, 2024
1 parent 04a9341 commit bce846b
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 85 deletions.
190 changes: 110 additions & 80 deletions src/eme.js
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand All @@ -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);
});
}
});

Expand All @@ -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);
}
};

/*
Expand Down Expand Up @@ -217,7 +234,8 @@ export const addSession = ({
getLicense,
contentId,
removeSession,
eventBus
eventBus,
emeError
}) => {
const sessionData = {
initDataType,
Expand All @@ -226,7 +244,8 @@ export const addSession = ({
getLicense,
removeSession,
eventBus,
contentId
contentId,
emeError
};

if (video.mediaKeysObject) {
Expand Down Expand Up @@ -262,15 +281,18 @@ 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
video.mediaKeysObject = createdMediaKeys;
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++) {
Expand All @@ -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);
};
Expand Down Expand Up @@ -388,7 +414,8 @@ export const standard5July2016 = ({
keySystemAccess,
options,
removeSession,
eventBus
eventBus,
emeError
}) => {
let keySystemPromise = Promise.resolve();
const keySystem = keySystemAccess.keySystem;
Expand Down Expand Up @@ -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) {
Expand All @@ -468,7 +497,8 @@ export const standard5July2016 = ({
getLicense,
contentId,
removeSession,
eventBus
eventBus,
emeError
});
});
};
11 changes: 6 additions & 5 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ 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();
}

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
Expand Down Expand Up @@ -90,7 +90,8 @@ export const handleEncryptedEvent = (player, event, options, sessions, eventBus)
keySystemAccess,
options,
removeSession: removeSession.bind(null, sessions),
eventBus
eventBus,
emeError
});
});
};
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit bce846b

Please sign in to comment.