rx-player
Version:
Canal+ HTML5 Video Player
185 lines (184 loc) • 7.34 kB
JavaScript
/**
* Copyright 2015 CANAL+ Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { setMediaKeys } from "../../../compat/eme/set_media_keys";
import { EncryptedMediaError } from "../../../errors";
import log from "../../../log";
import isNullOrUndefined from "../../../utils/is_null_or_undefined";
// Store the MediaKeys infos attached to a media element.
const currentMediaState = new WeakMap();
export default {
/**
* Attach new MediaKeys infos set on a HMTLMediaElement.
* @param {HTMLMediaElement} mediaElement
* @param {Object} mediaKeysInfo
* @returns {Promise}
*/
async attach(mediaElement, mediaKeysInfo) {
const previousState = currentMediaState.get(mediaElement);
const pendingTask = attachMediaKeys(mediaElement, previousState, mediaKeysInfo).then(() => {
currentMediaState.set(mediaElement, {
pendingTask: null,
mediaKeysState: mediaKeysInfo,
});
}, () => {
currentMediaState.set(mediaElement, {
pendingTask: null,
mediaKeysState: null,
});
});
currentMediaState.set(mediaElement, {
pendingTask,
mediaKeysState: mediaKeysInfo,
});
return pendingTask;
},
/**
* Get MediaKeys information expected to be linked to the given
* `HTMLMediaElement`.
*
* Unlike `getAttachedMediaKeysState`, this method is synchronous and will
* also return the expected state when `MediaKeys` attachment is still
* pending and thus when that state is not truly applied (and where it
* might fail before being applied).
*
* As such, only call this method if you want the currently expected state,
* not the actual one.
* @param {HTMLMediaElement} mediaElement
* @returns {Array}
*/
getAwaitedState(mediaElement) {
var _a;
const currentState = currentMediaState.get(mediaElement);
return (_a = currentState === null || currentState === void 0 ? void 0 : currentState.mediaKeysState) !== null && _a !== void 0 ? _a : null;
},
/**
* Get MediaKeys information set on a HMTLMediaElement.
*
* This method is asynchronous because that state may still be in a process
* of being attached to the `HTMLMediaElement` (and the state we're
* currently setting may not work out).
* @param {HTMLMediaElement} mediaElement
* @returns {Object|null}
*/
async getAttachedMediaKeysState(mediaElement) {
const currentState = currentMediaState.get(mediaElement);
if (currentState === undefined) {
return null;
}
if (currentState.pendingTask !== null) {
await currentState.pendingTask;
return this.getAttachedMediaKeysState(mediaElement);
}
return currentState.mediaKeysState;
},
/**
* Remove MediaKeys currently set on a HMTLMediaElement and update state
* accordingly.
* @param {HTMLMediaElement} mediaElement
* @returns {Promise}
*/
clearMediaKeys(mediaElement) {
const previousState = currentMediaState.get(mediaElement);
const pendingTask = clearMediaKeys(mediaElement, previousState).then(() => {
currentMediaState.set(mediaElement, {
pendingTask: null,
mediaKeysState: null,
});
}, () => {
currentMediaState.set(mediaElement, {
pendingTask: null,
mediaKeysState: null,
});
});
currentMediaState.set(mediaElement, {
pendingTask,
mediaKeysState: null,
});
return pendingTask;
},
};
/**
* Ensure that the last `MediaKeys` set on the given HTMLMediaElement is
* attached.
*
* The returned Promise never rejects, it will just log an error if the
* previous attachment failed.
*
* @param {Object} previousState
* @returns {Promise.<undefined>}
*/
async function awaitMediaKeysAttachment(previousState) {
const promise = previousState.pendingTask;
if (isNullOrUndefined(promise)) {
return;
}
log.info("DRM", "Awaiting previous MediaKeys attachment operation");
try {
await previousState.pendingTask;
}
catch (err) {
log.info("DRM", "previous MediaKeys attachment operation failed", err instanceof Error ? err : "Unknown error");
}
}
async function attachMediaKeys(mediaElement, previousState, mediaKeysInfo) {
if (previousState !== undefined) {
if (previousState.pendingTask !== null) {
// Ensure the `MediaKeys` has been fully attached to the HTMLMediaElement before
// resetting things, to avoid browser errors due to an invalid state.
await awaitMediaKeysAttachment(previousState);
}
const closeAllSessions = !isNullOrUndefined(previousState.mediaKeysState) &&
previousState.mediaKeysState.loadedSessionsStore !==
mediaKeysInfo.loadedSessionsStore
? previousState.mediaKeysState.loadedSessionsStore.closeAllSessions()
: Promise.resolve();
await closeAllSessions;
if (mediaElement.mediaKeys === mediaKeysInfo.mediaKeys) {
log.debug("DRM", "Right MediaKeys already set");
return;
}
}
log.info("DRM", "Attaching MediaKeys to the media element");
try {
await setMediaKeys(mediaKeysInfo.emeImplementation, mediaElement, mediaKeysInfo.mediaKeys);
log.info("DRM", "MediaKeys attached with success");
}
catch (err) {
const errMessage = err instanceof Error ? err.toString() : "Unknown Error";
throw new EncryptedMediaError("MEDIA_KEYS_ATTACHMENT_ERROR", "Could not attach the MediaKeys to the media element: " + errMessage, {
keyStatuses: undefined,
keySystemConfiguration: mediaKeysInfo.mediaKeySystemAccess.getConfiguration(),
keySystem: mediaKeysInfo.mediaKeySystemAccess.keySystem,
});
}
}
async function clearMediaKeys(mediaElement, previousState) {
if (previousState === undefined) {
return;
}
if (previousState.pendingTask !== null) {
// Ensure the `MediaKeys` has been fully attached to the HTMLMediaElement before
// resetting things, to avoid browser errors due to an invalid state.
await awaitMediaKeysAttachment(previousState);
}
if (previousState.mediaKeysState === null) {
return;
}
log.info("DRM", "Disposing of the current MediaKeys");
const { loadedSessionsStore } = previousState.mediaKeysState;
await loadedSessionsStore.closeAllSessions();
return setMediaKeys(previousState.mediaKeysState.emeImplementation, mediaElement, null);
}