Skip to content

Commit

Permalink
Merge pull request #1261 from canalplus/misc/eme-cleanup
Browse files Browse the repository at this point in the history
DRM: Refacto EME implementation selection mechanism
  • Loading branch information
peaBerberian authored Jul 18, 2023
2 parents e80e99a + 049201a commit 80540b6
Show file tree
Hide file tree
Showing 53 changed files with 1,057 additions and 849 deletions.
56 changes: 0 additions & 56 deletions src/compat/__tests__/has_eme_apis.test.ts

This file was deleted.

19 changes: 11 additions & 8 deletions src/compat/eme/custom_media_keys/ie11_media_keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import EventEmitter from "../../../utils/event_emitter";
import TaskCanceller from "../../../utils/task_canceller";
import wrapInPromise from "../../../utils/wrapInPromise";
import { ICompatHTMLMediaElement } from "../../browser_compatibility_types";
import * as events from "../../event_listeners";
import {
Expand Down Expand Up @@ -119,11 +120,13 @@ class IE11CustomMediaKeys implements ICustomMediaKeys {
this._mediaKeys = new MSMediaKeysConstructor(keyType);
}

_setVideo(videoElement: HTMLMediaElement): void {
this._videoElement = videoElement as ICompatHTMLMediaElement;
if (this._videoElement.msSetMediaKeys !== undefined) {
return this._videoElement.msSetMediaKeys(this._mediaKeys);
}
_setVideo(videoElement: HTMLMediaElement): Promise<unknown> {
return wrapInPromise(() => {
this._videoElement = videoElement as ICompatHTMLMediaElement;
if (this._videoElement.msSetMediaKeys !== undefined) {
this._videoElement.msSetMediaKeys(this._mediaKeys);
}
});
}

createSession(/* sessionType */): ICustomMediaKeySession {
Expand All @@ -144,7 +147,7 @@ export default function getIE11MediaKeysCallbacks() : {
setMediaKeys: (
elt: HTMLMediaElement,
mediaKeys: MediaKeys|ICustomMediaKeys|null
) => void;
) => Promise<unknown>;
} {
const isTypeSupported = (keySystem: string, type?: string|null) => {
if (MSMediaKeysConstructor === undefined) {
Expand All @@ -160,12 +163,12 @@ export default function getIE11MediaKeysCallbacks() : {
const setMediaKeys = (
elt: HTMLMediaElement,
mediaKeys: MediaKeys|ICustomMediaKeys|null
): void => {
): Promise<unknown> => {
if (mediaKeys === null) {
// msSetMediaKeys only accepts native MSMediaKeys as argument.
// Calling it with null or undefined will raise an exception.
// There is no way to unset the mediakeys in that case, so return here.
return;
return Promise.resolve(undefined);
}
if (!(mediaKeys instanceof IE11CustomMediaKeys)) {
throw new Error("Custom setMediaKeys is supposed to be called " +
Expand Down
192 changes: 8 additions & 184 deletions src/compat/eme/custom_media_keys/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
/**
* 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 { MediaError } from "../../../errors";
import assert from "../../../utils/assert";
import { ICompatHTMLMediaElement } from "../../browser_compatibility_types";
import { isIE11 } from "../../browser_detection";
import isNode from "../../is_node";
import shouldFavourCustomSafariEME from "../../should_favour_custom_safari_EME";
import CustomMediaKeySystemAccess from "./../custom_key_system_access";
import getIE11MediaKeysCallbacks, {
MSMediaKeysConstructor,
} from "./ie11_media_keys";
Expand All @@ -37,168 +14,15 @@ import {
import getWebKitMediaKeysCallbacks from "./webkit_media_keys";
import { WebKitMediaKeysConstructor } from "./webkit_media_keys_constructor";

/** Generic implementation of the navigator.requestMediaKeySystemAccess API. */
type ICompatRequestMediaKeySystemAccessFn =
(keyType : string, config : MediaKeySystemConfiguration[]) =>
Promise<MediaKeySystemAccess | CustomMediaKeySystemAccess>;

let requestMediaKeySystemAccess : ICompatRequestMediaKeySystemAccessFn | null = null;

/**
* Set the given MediaKeys on the given HTMLMediaElement.
* Emits null when done then complete.
* @param {HTMLMediaElement} elt
* @param {Object} mediaKeys
*/
let setMediaKeys :
((elt: HTMLMediaElement, mediaKeys: MediaKeys | ICustomMediaKeys | null) => unknown) =
function defaultSetMediaKeys(
mediaElement: HTMLMediaElement,
mediaKeys: MediaKeys | ICustomMediaKeys | null
) {
const elt : ICompatHTMLMediaElement = mediaElement;
/* eslint-disable @typescript-eslint/unbound-method */
if (typeof elt.setMediaKeys === "function") {
return elt.setMediaKeys(mediaKeys as MediaKeys);
}
/* eslint-enable @typescript-eslint/unbound-method */

// 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.
if (typeof elt.webkitSetMediaKeys === "function") {
return elt.webkitSetMediaKeys(mediaKeys);
}

if (typeof elt.mozSetMediaKeys === "function") {
return elt.mozSetMediaKeys(mediaKeys);
}

if (typeof elt.msSetMediaKeys === "function" && mediaKeys !== null) {
return elt.msSetMediaKeys(mediaKeys);
}
};

/**
* Since Safari 12.1, EME APIs are available without webkit prefix.
* However, it seems that since fairplay CDM implementation within the browser is not
* standard with EME w3c current spec, the requestMediaKeySystemAccess API doesn't resolve
* positively, even if the drm (fairplay in most cases) is supported.
*
* Therefore, we prefer not to use requestMediaKeySystemAccess on Safari when webkit API
* is available.
*/
if (isNode ||
(navigator.requestMediaKeySystemAccess != null && !shouldFavourCustomSafariEME())
) {
requestMediaKeySystemAccess = (...args) =>
navigator.requestMediaKeySystemAccess(...args);
} else {
let isTypeSupported: (keyType: string) => boolean;
let createCustomMediaKeys: (keyType: string) => ICustomMediaKeys;

// This is for Chrome with unprefixed EME api
if (isOldWebkitMediaElement(HTMLVideoElement.prototype)) {
const callbacks = getOldKitWebKitMediaKeyCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
// This is for WebKit with prefixed EME api
} else if (WebKitMediaKeysConstructor !== undefined) {
const callbacks = getWebKitMediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
} else if (isIE11 && MSMediaKeysConstructor !== undefined) {
const callbacks = getIE11MediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
} else if (MozMediaKeysConstructor !== undefined) {
const callbacks = getMozMediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
} else {
const MK = window.MediaKeys as unknown as typeof MediaKeys & {
isTypeSupported? : (keyType : string) => boolean;
new(keyType? : string) : ICustomMediaKeys;
};
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: string): boolean => {
checkForStandardMediaKeys();
assert(typeof MK.isTypeSupported === "function");
return MK.isTypeSupported(keyType);
};
createCustomMediaKeys = (keyType: string) => {
checkForStandardMediaKeys();
return new MK(keyType);
};
}

requestMediaKeySystemAccess = function(
keyType : string,
keySystemConfigurations : MediaKeySystemConfiguration[]
) : Promise<MediaKeySystemAccess|CustomMediaKeySystemAccess> {
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 &&
(initDataTypes == null ||
initDataTypes.some((idt) => idt === "cenc"));
supported = supported && (distinctiveIdentifier !== "required");

if (supported) {
const keySystemConfigurationResponse : MediaKeySystemConfiguration = {
initDataTypes: ["cenc"],
distinctiveIdentifier: "not-allowed" as const,
persistentState: "required" as const,
sessionTypes: ["temporary", "persistent-license"],
};
if (videoCapabilities !== undefined) {
keySystemConfigurationResponse.videoCapabilities = videoCapabilities;
}
if (audioCapabilities !== undefined) {
keySystemConfigurationResponse.audioCapabilities = audioCapabilities;
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const customMediaKeys = createCustomMediaKeys(keyType);
return Promise.resolve(
new CustomMediaKeySystemAccess(keyType,
customMediaKeys,
keySystemConfigurationResponse)
);
}
}

return Promise.reject(new Error("Unsupported configuration"));
};
}

export {
requestMediaKeySystemAccess,
setMediaKeys,
getIE11MediaKeysCallbacks,
MSMediaKeysConstructor,
getMozMediaKeysCallbacks,
MozMediaKeysConstructor,
getOldKitWebKitMediaKeyCallbacks,
isOldWebkitMediaElement,
ICustomMediaKeys,
ICustomMediaKeySession,
getWebKitMediaKeysCallbacks,
WebKitMediaKeysConstructor,
};
20 changes: 13 additions & 7 deletions src/compat/eme/custom_media_keys/moz_media_keys_constructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import wrapInPromise from "../../../utils/wrapInPromise";
import { ICompatHTMLMediaElement } from "../../browser_compatibility_types";
import isNode from "../../is_node";
import { ICustomMediaKeys } from "./types";
Expand Down Expand Up @@ -47,7 +48,7 @@ export default function getMozMediaKeysCallbacks() : {
setMediaKeys: (
elt: HTMLMediaElement,
mediaKeys: MediaKeys|ICustomMediaKeys|null
) => void;
) => Promise<unknown>;
} {
const isTypeSupported = (keySystem: string, type?: string|null) => {
if (MozMediaKeysConstructor === undefined) {
Expand All @@ -67,12 +68,17 @@ export default function getMozMediaKeysCallbacks() : {
const setMediaKeys = (
mediaElement: HTMLMediaElement,
mediaKeys: MediaKeys|ICustomMediaKeys|null
): void => {
const elt : ICompatHTMLMediaElement = mediaElement;
if (elt.mozSetMediaKeys === undefined || typeof elt.mozSetMediaKeys !== "function") {
throw new Error("Can't set video on MozMediaKeys.");
}
return elt.mozSetMediaKeys(mediaKeys);
): Promise<unknown> => {
return wrapInPromise(() => {
const elt : ICompatHTMLMediaElement = mediaElement;
if (
elt.mozSetMediaKeys === undefined ||
typeof elt.mozSetMediaKeys !== "function"
) {
throw new Error("Can't set video on MozMediaKeys.");
}
return elt.mozSetMediaKeys(mediaKeys);
});
};
return {
isTypeSupported,
Expand Down
Loading

0 comments on commit 80540b6

Please sign in to comment.