rx-player
Version:
Canal+ HTML5 Video Player
213 lines (212 loc) • 10 kB
JavaScript
import { MediaError } from "../../errors";
import assert from "../../utils/assert";
import globalScope from "../../utils/global_scope";
import isNode from "../../utils/is_node";
import isNullOrUndefined from "../../utils/is_null_or_undefined";
import objectAssign from "../../utils/object_assign";
import { isIE11 } from "../browser_detection";
import { createCompatibleEventListener } from "../event_listeners";
import shouldFavourCustomSafariEME from "../should_favour_custom_safari_EME";
import CustomMediaKeySystemAccess from "./custom_key_system_access";
import getIE11MediaKeysCallbacks, { MSMediaKeysConstructor, } from "./custom_media_keys/ie11_media_keys";
import getMozMediaKeysCallbacks, { MozMediaKeysConstructor, } from "./custom_media_keys/moz_media_keys_constructor";
import getOldKitWebKitMediaKeyCallbacks, { isOldWebkitMediaElement, } from "./custom_media_keys/old_webkit_media_keys";
import getWebKitMediaKeysCallbacks from "./custom_media_keys/webkit_media_keys";
import { WebKitMediaKeysConstructor } from "./custom_media_keys/webkit_media_keys_constructor";
/**
* Automatically detect and set which EME implementation should be used in the
* current platform.
*
* You can call `getEmeApiImplementation` for a different implementation.
*/
const defaultEmeImplementation = getEmeApiImplementation("auto");
export default defaultEmeImplementation;
/**
* Returns the current EME implementation based on what's present on the device
* and the given preference.
* @param {string} preferredApiType - EME API preference
* (@see IPreferredEmeApiType).
* @returns {Object}
*/
function getEmeApiImplementation(preferredApiType) {
var _a;
let requestMediaKeySystemAccess;
let onEncrypted;
let setMediaKeys = defaultSetMediaKeys;
let implementation;
if ((preferredApiType === "standard" ||
(preferredApiType === "auto" && !shouldFavourCustomSafariEME())) &&
// eslint-disable-next-line @typescript-eslint/unbound-method
(isNode || !isNullOrUndefined(navigator.requestMediaKeySystemAccess))) {
requestMediaKeySystemAccess = (...args) => navigator.requestMediaKeySystemAccess(...args);
onEncrypted = createCompatibleEventListener(["encrypted"]);
implementation = "standard";
}
else {
let isTypeSupported;
let createCustomMediaKeys;
if (preferredApiType === "webkit" && WebKitMediaKeysConstructor !== undefined) {
const callbacks = getWebKitMediaKeysCallbacks();
onEncrypted = createOnEncryptedForWebkit();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
implementation = "webkit";
}
else {
// This is for Chrome with unprefixed EME api
if (isOldWebkitMediaElement((_a = globalScope.HTMLVideoElement) === null || _a === void 0 ? void 0 : _a.prototype)) {
onEncrypted = createCompatibleEventListener(["needkey"]);
const callbacks = getOldKitWebKitMediaKeyCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
implementation = "older-webkit";
// This is for WebKit with prefixed EME api
}
else if (WebKitMediaKeysConstructor !== undefined) {
onEncrypted = createOnEncryptedForWebkit();
const callbacks = getWebKitMediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
implementation = "webkit";
}
else if (isIE11 && MSMediaKeysConstructor !== undefined) {
onEncrypted = createCompatibleEventListener(["encrypted", "needkey"]);
const callbacks = getIE11MediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
implementation = "ms";
}
else if (MozMediaKeysConstructor !== undefined) {
onEncrypted = createCompatibleEventListener(["encrypted", "needkey"]);
const callbacks = getMozMediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
implementation = "moz";
}
else {
onEncrypted = createCompatibleEventListener(["encrypted", "needkey"]);
const MK = globalScope.MediaKeys;
const checkForStandardMediaKeys = () => {
if (MK === undefined) {
throw new MediaError("MEDIA_KEYS_NOT_SUPPORTED", "No `MediaKeys` implementation found " + "in the current browser.");
}
if (typeof MK.isTypeSupported === "undefined") {
const message = "This browser seems to be unable to play encrypted " +
"contents currently." +
"Note: Some browsers do not allow decryption " +
"in some situations, like when not using HTTPS.";
throw new Error(message);
}
};
isTypeSupported = (keyType) => {
checkForStandardMediaKeys();
assert(typeof MK.isTypeSupported === "function");
return MK.isTypeSupported(keyType);
};
createCustomMediaKeys = (keyType) => {
checkForStandardMediaKeys();
return new MK(keyType);
};
implementation = "unknown";
}
}
requestMediaKeySystemAccess = function (keyType, keySystemConfigurations) {
if (!isTypeSupported(keyType)) {
return Promise.reject(new Error("Unsupported key type"));
}
for (let i = 0; i < keySystemConfigurations.length; i++) {
const keySystemConfiguration = keySystemConfigurations[i];
const { videoCapabilities, audioCapabilities, initDataTypes, distinctiveIdentifier, } = keySystemConfiguration;
let supported = true;
supported =
supported &&
(isNullOrUndefined(initDataTypes) ||
initDataTypes.some((idt) => idt === "cenc"));
supported = supported && distinctiveIdentifier !== "required";
if (supported) {
const keySystemConfigurationResponse = {
initDataTypes: ["cenc"],
distinctiveIdentifier: "not-allowed",
persistentState: "required",
sessionTypes: ["temporary", "persistent-license"],
};
if (videoCapabilities !== undefined) {
keySystemConfigurationResponse.videoCapabilities = videoCapabilities;
}
if (audioCapabilities !== undefined) {
keySystemConfigurationResponse.audioCapabilities = audioCapabilities;
}
const customMediaKeys = createCustomMediaKeys(keyType);
return Promise.resolve(new CustomMediaKeySystemAccess(keyType, customMediaKeys, keySystemConfigurationResponse));
}
}
return Promise.reject(new Error("Unsupported configuration"));
};
}
return {
requestMediaKeySystemAccess,
onEncrypted,
setMediaKeys,
implementation,
};
}
/**
* Create an event listener for the "webkitneedkey" event
* @returns
*/
function createOnEncryptedForWebkit() {
const compatibleEventListener = createCompatibleEventListener(["needkey"], undefined /* prefixes */);
const onEncrypted = (target, listener, cancelSignal) => {
compatibleEventListener(target, (event) => {
const patchedEvent = objectAssign(event, {
forceSessionRecreation: true,
});
listener(patchedEvent);
}, cancelSignal);
};
return onEncrypted;
}
/**
* Set the given MediaKeys on the given HTMLMediaElement.
* Emits null when done then complete.
* @param {HTMLMediaElement} elt
* @param {Object} mediaKeys
* @returns {Promise}
*/
function defaultSetMediaKeys(elt, mediaKeys) {
try {
let ret;
if (typeof elt.setMediaKeys === "function") {
// eslint-disable-next-line @typescript-eslint/no-restricted-types
ret = elt.setMediaKeys(mediaKeys);
}
// If we get in the following code, it means that no compat case has been
// found and no standard setMediaKeys API exists. This case is particulary
// rare. We will try to call each API with native media keys.
else if (typeof elt.webkitSetMediaKeys === "function") {
ret = elt.webkitSetMediaKeys(mediaKeys);
}
else if (typeof elt.mozSetMediaKeys === "function") {
ret = elt.mozSetMediaKeys(mediaKeys);
}
else if (typeof elt.msSetMediaKeys === "function" && mediaKeys !== null) {
ret = elt.msSetMediaKeys(mediaKeys);
}
if (typeof ret === "object" &&
ret !== null &&
typeof ret.then === "function") {
return ret;
}
else {
return Promise.resolve(ret);
}
}
catch (err) {
return Promise.reject(err);
}
}