From 48c255de048a2978cd24d3f34207cb19f034eda3 Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 10 Jul 2024 17:44:48 +0200 Subject: [PATCH 1/7] fix(safari): video not starting because key are never considered usable for a track --- src/main_thread/decrypt/content_decryptor.ts | 29 ++++++++++++++++++- .../init/directfile_content_initializer.ts | 1 + .../init/media_source_content_initializer.ts | 1 + .../init/multi_thread_content_initializer.ts | 4 ++- .../utils/initialize_content_decryption.ts | 4 ++- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main_thread/decrypt/content_decryptor.ts b/src/main_thread/decrypt/content_decryptor.ts index 8da27ae06d..cba3269745 100644 --- a/src/main_thread/decrypt/content_decryptor.ts +++ b/src/main_thread/decrypt/content_decryptor.ts @@ -15,6 +15,7 @@ */ import type { IMediaElement } from "../../compat/browser_compatibility_types"; +import { isSafariDesktop } from "../../compat/browser_detection"; import type { ICustomMediaKeys, ICustomMediaKeySystemAccess } from "../../compat/eme"; import eme, { getInitData } from "../../compat/eme"; import config from "../../config"; @@ -58,6 +59,10 @@ import { } from "./utils/key_id_comparison"; import type KeySessionRecord from "./utils/key_session_record"; +export interface IContext { + isDirectFile: boolean; +} + /** * Module communicating with the Content Decryption Module (or CDM) to be able * to decrypt contents. @@ -131,6 +136,10 @@ export default class ContentDecryptor extends EventEmitter( { initializationState: { type: "uninitialized", value: null }, diff --git a/src/main_thread/init/utils/initialize_content_decryption.ts b/src/main_thread/init/utils/initialize_content_decryption.ts index a4659946c0..c08ea4cd4e 100644 --- a/src/main_thread/init/utils/initialize_content_decryption.ts +++ b/src/main_thread/init/utils/initialize_content_decryption.ts @@ -10,6 +10,7 @@ import type { CancellationSignal } from "../../../utils/task_canceller"; import { ContentDecryptorState } from "../../decrypt"; import type IContentDecryptor from "../../decrypt"; import type { IProcessedProtectionData } from "../../decrypt"; +import type { IContext } from "../../decrypt/content_decryptor"; /** * Initialize content decryption capabilities on the given `HTMLMediaElement`. @@ -46,6 +47,7 @@ export default function initializeContentDecryption( }) => void; onCodecSupportUpdate?: () => void; }, + context: IContext, cancelSignal: CancellationSignal, ): { statusRef: IReadOnlySharedReference; @@ -82,7 +84,7 @@ export default function initializeContentDecryption( } log.debug("Init: Creating ContentDecryptor"); - const contentDecryptor = new ContentDecryptor(mediaElement, keySystems); + const contentDecryptor = new ContentDecryptor(mediaElement, keySystems, context); const onStateChange = (state: ContentDecryptorState) => { if (state > ContentDecryptorState.Initializing) { From d23ef3ca071ec4a0445f43c6e745bc6853ba81c0 Mon Sep 17 00:00:00 2001 From: Florent Date: Mon, 29 Jul 2024 18:46:40 +0200 Subject: [PATCH 2/7] code review --- src/main_thread/decrypt/content_decryptor.ts | 72 ++++++++++++++++---- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/src/main_thread/decrypt/content_decryptor.ts b/src/main_thread/decrypt/content_decryptor.ts index cba3269745..bd0c942f2c 100644 --- a/src/main_thread/decrypt/content_decryptor.ts +++ b/src/main_thread/decrypt/content_decryptor.ts @@ -15,7 +15,6 @@ */ import type { IMediaElement } from "../../compat/browser_compatibility_types"; -import { isSafariDesktop } from "../../compat/browser_detection"; import type { ICustomMediaKeys, ICustomMediaKeySystemAccess } from "../../compat/eme"; import eme, { getInitData } from "../../compat/eme"; import config from "../../config"; @@ -729,19 +728,6 @@ export default class ContentDecryptor extends EventEmitter + log.error("DRM: Cannot close the session from the loaded session store"), + ); + } + + /** + * If set, a currently-used key session is already compatible to this + * initialization data. + */ + const compatibleSessionInfo = arrayFind(this._currentSessions, (x) => + x.record.isCompatibleWith(initData), + ); + if (compatibleSessionInfo === undefined) { + return; + } + /** Remove the session from the currentSessions */ + const indexOf = this._currentSessions.indexOf(compatibleSessionInfo); + if (indexOf !== -1) { + log.debug( + "DRM: A session from a processed init is removed due to forceSessionRecreation policy.", + ); + this._currentSessions.splice(indexOf, 1); + } + } + /** * Callback that should be called if an error that made the current * `ContentDecryptor` instance unusable arised. From 169e7f6625fdd94fff1a7c3511206228fc6bc8b7 Mon Sep 17 00:00:00 2001 From: Florent Date: Tue, 30 Jul 2024 15:58:50 +0200 Subject: [PATCH 3/7] set forceSessionRecreation on encrypted event --- src/compat/eme/eme-api-implementation.ts | 15 ++++++++- src/compat/eme/get_init_data.ts | 8 +++-- src/compat/event_listeners.ts | 32 +++++++++++++++----- src/main_thread/decrypt/content_decryptor.ts | 11 ++++--- src/main_thread/decrypt/types.ts | 2 ++ 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/compat/eme/eme-api-implementation.ts b/src/compat/eme/eme-api-implementation.ts index 96e7885deb..f6996b5e9b 100644 --- a/src/compat/eme/eme-api-implementation.ts +++ b/src/compat/eme/eme-api-implementation.ts @@ -1,8 +1,10 @@ import { MediaError } from "../../errors"; +import type { ICustomMediaEncryptedEvent } from "../../main_thread/decrypt/content_decryptor"; 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 object_assign from "../../utils/object_assign"; import type { CancellationSignal } from "../../utils/task_canceller"; import type { IMediaElement } from "../browser_compatibility_types"; import { isIE11 } from "../browser_detection"; @@ -160,7 +162,18 @@ function getEmeApiImplementation( implementation = "older-webkit"; // This is for WebKit with prefixed EME api } else if (WebKitMediaKeysConstructor !== undefined) { - onEncrypted = createCompatibleEventListener(["needkey"]); + const patchWebkitNeedKeyEvent = (event: Event): ICustomMediaEncryptedEvent => { + const patchedEvent = object_assign( + { forceSessionRecreation: true }, + event as MediaEncryptedEvent, + ); + return patchedEvent; + }; + onEncrypted = createCompatibleEventListener( + ["needkey"], + undefined /* prefixes */, + patchWebkitNeedKeyEvent, + ); const callbacks = getWebKitMediaKeysCallbacks(); isTypeSupported = callbacks.isTypeSupported; createCustomMediaKeys = callbacks.createCustomMediaKeys; diff --git a/src/compat/eme/get_init_data.ts b/src/compat/eme/get_init_data.ts index f9bde2c990..f850137bc6 100644 --- a/src/compat/eme/get_init_data.ts +++ b/src/compat/eme/get_init_data.ts @@ -15,6 +15,7 @@ */ import log from "../../log"; +import type { ICustomMediaEncryptedEvent } from "../../main_thread/decrypt/content_decryptor"; import { getPsshSystemID } from "../../parsers/containers/isobmff"; import areArraysOfNumbersEqual from "../../utils/are_arrays_of_numbers_equal"; import { be4toi } from "../../utils/byte_parsing"; @@ -50,6 +51,7 @@ export interface IEncryptedEventData { */ data: Uint8Array; }>; + forceSessionRecreation?: boolean | undefined; } /** @@ -145,9 +147,9 @@ function isPSSHAlreadyEncountered( * encountered in the given event. */ export default function getInitData( - encryptedEvent: MediaEncryptedEvent, + encryptedEvent: ICustomMediaEncryptedEvent, ): IEncryptedEventData | null { - const { initData, initDataType } = encryptedEvent; + const { initData, initDataType, forceSessionRecreation } = encryptedEvent; if (isNullOrUndefined(initData)) { log.warn("Compat: No init data found on media encrypted event."); return null; @@ -155,5 +157,5 @@ export default function getInitData( const initDataBytes = new Uint8Array(initData); const values = getInitializationDataValues(initDataBytes); - return { type: initDataType, values }; + return { type: initDataType, values, forceSessionRecreation }; } diff --git a/src/compat/event_listeners.ts b/src/compat/event_listeners.ts index 0556b5ac4e..ba37cc81c5 100644 --- a/src/compat/event_listeners.ts +++ b/src/compat/event_listeners.ts @@ -82,8 +82,14 @@ function eventPrefixed(eventNames: string[], prefixes?: string[]): string[] { } export interface IEventEmitterLike { - addEventListener: (eventName: string, handler: () => void) => void; - removeEventListener: (eventName: string, handler: () => void) => void; + addEventListener: ( + eventName: string, + handler: EventListenerOrEventListenerObject, + ) => void; + removeEventListener: ( + eventName: string, + handler: EventListenerOrEventListenerObject, + ) => void; } export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter; @@ -102,9 +108,10 @@ export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter Event, ): ( element: IEventTargetLike, - listener: (event?: unknown) => void, + listener: (event?: Event) => void, cancelSignal: CancellationSignal, ) => void { let mem: string | undefined; @@ -112,12 +119,21 @@ function createCompatibleEventListener( return ( element: IEventTargetLike, - listener: (event?: unknown) => void, + listener: (event?: Event) => void, cancelSignal: CancellationSignal, ) => { if (cancelSignal.isCancelled()) { return; } + let patchedListener: EventListener; + if (patchFn !== undefined) { + patchedListener = (event: Event) => { + const patchedEvent = patchFn(event); + listener(patchedEvent); + }; + } else { + patchedListener = listener; + } // if the element is a HTMLElement we can detect // the supported event, and memoize it in `mem` @@ -127,10 +143,10 @@ function createCompatibleEventListener( } if (isNonEmptyString(mem)) { - element.addEventListener(mem, listener); + element.addEventListener(mem, patchedListener); cancelSignal.register(() => { if (mem !== undefined) { - element.removeEventListener(mem, listener); + element.removeEventListener(mem, patchedListener); } }); } else { @@ -148,7 +164,7 @@ function createCompatibleEventListener( prefixedEvents.forEach((eventName) => { let hasSetOnFn = false; if (typeof element.addEventListener === "function") { - (element as IEventEmitterLike).addEventListener(eventName, listener); + (element as IEventEmitterLike).addEventListener(eventName, patchedListener); } else { hasSetOnFn = true; /* eslint-disable @typescript-eslint/no-unsafe-member-access */ @@ -158,7 +174,7 @@ function createCompatibleEventListener( } cancelSignal.register(() => { if (typeof element.removeEventListener === "function") { - (element as IEventEmitterLike).removeEventListener(eventName, listener); + (element as IEventEmitterLike).removeEventListener(eventName, patchedListener); } if (hasSetOnFn) { /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/src/main_thread/decrypt/content_decryptor.ts b/src/main_thread/decrypt/content_decryptor.ts index bd0c942f2c..8a49b5b1cd 100644 --- a/src/main_thread/decrypt/content_decryptor.ts +++ b/src/main_thread/decrypt/content_decryptor.ts @@ -62,6 +62,10 @@ export interface IContext { isDirectFile: boolean; } +export interface ICustomMediaEncryptedEvent extends MediaEncryptedEvent { + forceSessionRecreation?: boolean; +} + /** * Module communicating with the Content Decryption Module (or CDM) to be able * to decrypt contents. @@ -190,7 +194,7 @@ export default class ContentDecryptor extends EventEmitter { log.debug("DRM: Encrypted event received from media element."); - const initData = getInitData(evt as MediaEncryptedEvent); + const initData = getInitData(evt as ICustomMediaEncryptedEvent); if (initData !== null) { this.onInitializationData(initData); } @@ -750,9 +754,8 @@ export default class ContentDecryptor extends EventEmitter Date: Wed, 31 Jul 2024 11:07:43 +0200 Subject: [PATCH 4/7] simplifie code --- src/compat/eme/custom_media_keys/types.ts | 4 ++ src/compat/eme/eme-api-implementation.ts | 52 +++++++++++++++---- src/compat/eme/get_init_data.ts | 2 +- src/compat/event_listeners.ts | 19 ++----- src/main_thread/decrypt/content_decryptor.ts | 20 +------ .../init/directfile_content_initializer.ts | 1 - .../init/media_source_content_initializer.ts | 1 - .../init/multi_thread_content_initializer.ts | 4 +- .../utils/initialize_content_decryption.ts | 4 +- 9 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/compat/eme/custom_media_keys/types.ts b/src/compat/eme/custom_media_keys/types.ts index 143006fa8d..d81509a418 100644 --- a/src/compat/eme/custom_media_keys/types.ts +++ b/src/compat/eme/custom_media_keys/types.ts @@ -60,3 +60,7 @@ export interface IMediaKeySessionEvents { // "keyerror" /* "error" */ } + +export interface ICustomMediaEncryptedEvent extends MediaEncryptedEvent { + forceSessionRecreation?: boolean | undefined; +} diff --git a/src/compat/eme/eme-api-implementation.ts b/src/compat/eme/eme-api-implementation.ts index f6996b5e9b..780e194bb9 100644 --- a/src/compat/eme/eme-api-implementation.ts +++ b/src/compat/eme/eme-api-implementation.ts @@ -1,5 +1,4 @@ import { MediaError } from "../../errors"; -import type { ICustomMediaEncryptedEvent } from "../../main_thread/decrypt/content_decryptor"; import assert from "../../utils/assert"; import globalScope from "../../utils/global_scope"; import isNode from "../../utils/is_node"; @@ -145,7 +144,28 @@ function getEmeApiImplementation( let createCustomMediaKeys: (keyType: string) => ICustomMediaKeys; if (preferredApiType === "webkit" && WebKitMediaKeysConstructor !== undefined) { - onEncrypted = createCompatibleEventListener(["needkey"]); + const compatibleEventListener = createCompatibleEventListener( + ["needkey"], + undefined /* prefixes */, + ); + onEncrypted = ( + target: IEventTargetLike, + listener: (event?: Event) => void, + cancelSignal: CancellationSignal, + ) => { + compatibleEventListener( + target, + (event?: Event) => { + const patchedEvent = object_assign( + { forceSessionRecreation: true }, + event as MediaEncryptedEvent, + ); + listener(patchedEvent); + }, + cancelSignal, + ); + }; + const callbacks = getWebKitMediaKeysCallbacks(); isTypeSupported = callbacks.isTypeSupported; createCustomMediaKeys = callbacks.createCustomMediaKeys; @@ -162,18 +182,28 @@ function getEmeApiImplementation( implementation = "older-webkit"; // This is for WebKit with prefixed EME api } else if (WebKitMediaKeysConstructor !== undefined) { - const patchWebkitNeedKeyEvent = (event: Event): ICustomMediaEncryptedEvent => { - const patchedEvent = object_assign( - { forceSessionRecreation: true }, - event as MediaEncryptedEvent, - ); - return patchedEvent; - }; - onEncrypted = createCompatibleEventListener( + const compatibleEventListener = createCompatibleEventListener( ["needkey"], undefined /* prefixes */, - patchWebkitNeedKeyEvent, ); + onEncrypted = ( + target: IEventTargetLike, + listener: (event?: Event) => void, + cancelSignal: CancellationSignal, + ) => { + compatibleEventListener( + target, + (event?: Event) => { + const patchedEvent = object_assign( + { forceSessionRecreation: true }, + event as MediaEncryptedEvent, + ); + listener(patchedEvent); + }, + cancelSignal, + ); + }; + const callbacks = getWebKitMediaKeysCallbacks(); isTypeSupported = callbacks.isTypeSupported; createCustomMediaKeys = callbacks.createCustomMediaKeys; diff --git a/src/compat/eme/get_init_data.ts b/src/compat/eme/get_init_data.ts index f850137bc6..985f3edd1c 100644 --- a/src/compat/eme/get_init_data.ts +++ b/src/compat/eme/get_init_data.ts @@ -15,12 +15,12 @@ */ import log from "../../log"; -import type { ICustomMediaEncryptedEvent } from "../../main_thread/decrypt/content_decryptor"; import { getPsshSystemID } from "../../parsers/containers/isobmff"; import areArraysOfNumbersEqual from "../../utils/are_arrays_of_numbers_equal"; import { be4toi } from "../../utils/byte_parsing"; import isNullOrUndefined from "../../utils/is_null_or_undefined"; import { PSSH_TO_INTEGER } from "./constants"; +import type { ICustomMediaEncryptedEvent } from "./custom_media_keys/types"; /** Data recuperated from parsing the payload of an `encrypted` event. */ export interface IEncryptedEventData { diff --git a/src/compat/event_listeners.ts b/src/compat/event_listeners.ts index ba37cc81c5..073bd8545d 100644 --- a/src/compat/event_listeners.ts +++ b/src/compat/event_listeners.ts @@ -108,7 +108,6 @@ export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter Event, ): ( element: IEventTargetLike, listener: (event?: Event) => void, @@ -125,16 +124,6 @@ function createCompatibleEventListener( if (cancelSignal.isCancelled()) { return; } - let patchedListener: EventListener; - if (patchFn !== undefined) { - patchedListener = (event: Event) => { - const patchedEvent = patchFn(event); - listener(patchedEvent); - }; - } else { - patchedListener = listener; - } - // if the element is a HTMLElement we can detect // the supported event, and memoize it in `mem` if (typeof HTMLElement !== "undefined" && element instanceof HTMLElement) { @@ -143,10 +132,10 @@ function createCompatibleEventListener( } if (isNonEmptyString(mem)) { - element.addEventListener(mem, patchedListener); + element.addEventListener(mem, listener); cancelSignal.register(() => { if (mem !== undefined) { - element.removeEventListener(mem, patchedListener); + element.removeEventListener(mem, listener); } }); } else { @@ -164,7 +153,7 @@ function createCompatibleEventListener( prefixedEvents.forEach((eventName) => { let hasSetOnFn = false; if (typeof element.addEventListener === "function") { - (element as IEventEmitterLike).addEventListener(eventName, patchedListener); + (element as IEventEmitterLike).addEventListener(eventName, listener); } else { hasSetOnFn = true; /* eslint-disable @typescript-eslint/no-unsafe-member-access */ @@ -174,7 +163,7 @@ function createCompatibleEventListener( } cancelSignal.register(() => { if (typeof element.removeEventListener === "function") { - (element as IEventEmitterLike).removeEventListener(eventName, patchedListener); + (element as IEventEmitterLike).removeEventListener(eventName, listener); } if (hasSetOnFn) { /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/src/main_thread/decrypt/content_decryptor.ts b/src/main_thread/decrypt/content_decryptor.ts index 8a49b5b1cd..53c034da06 100644 --- a/src/main_thread/decrypt/content_decryptor.ts +++ b/src/main_thread/decrypt/content_decryptor.ts @@ -17,6 +17,7 @@ import type { IMediaElement } from "../../compat/browser_compatibility_types"; import type { ICustomMediaKeys, ICustomMediaKeySystemAccess } from "../../compat/eme"; import eme, { getInitData } from "../../compat/eme"; +import type { ICustomMediaEncryptedEvent } from "../../compat/eme/custom_media_keys/types"; import config from "../../config"; import { EncryptedMediaError, OtherError } from "../../errors"; import log from "../../log"; @@ -58,14 +59,6 @@ import { } from "./utils/key_id_comparison"; import type KeySessionRecord from "./utils/key_session_record"; -export interface IContext { - isDirectFile: boolean; -} - -export interface ICustomMediaEncryptedEvent extends MediaEncryptedEvent { - forceSessionRecreation?: boolean; -} - /** * Module communicating with the Content Decryption Module (or CDM) to be able * to decrypt contents. @@ -139,10 +132,6 @@ export default class ContentDecryptor extends EventEmitter( { initializationState: { type: "uninitialized", value: null }, diff --git a/src/main_thread/init/utils/initialize_content_decryption.ts b/src/main_thread/init/utils/initialize_content_decryption.ts index c08ea4cd4e..a4659946c0 100644 --- a/src/main_thread/init/utils/initialize_content_decryption.ts +++ b/src/main_thread/init/utils/initialize_content_decryption.ts @@ -10,7 +10,6 @@ import type { CancellationSignal } from "../../../utils/task_canceller"; import { ContentDecryptorState } from "../../decrypt"; import type IContentDecryptor from "../../decrypt"; import type { IProcessedProtectionData } from "../../decrypt"; -import type { IContext } from "../../decrypt/content_decryptor"; /** * Initialize content decryption capabilities on the given `HTMLMediaElement`. @@ -47,7 +46,6 @@ export default function initializeContentDecryption( }) => void; onCodecSupportUpdate?: () => void; }, - context: IContext, cancelSignal: CancellationSignal, ): { statusRef: IReadOnlySharedReference; @@ -84,7 +82,7 @@ export default function initializeContentDecryption( } log.debug("Init: Creating ContentDecryptor"); - const contentDecryptor = new ContentDecryptor(mediaElement, keySystems, context); + const contentDecryptor = new ContentDecryptor(mediaElement, keySystems); const onStateChange = (state: ContentDecryptorState) => { if (state > ContentDecryptorState.Initializing) { From 9c2c23fe0c754b8ab4e241a8d99c0b49dd63542f Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 31 Jul 2024 11:39:04 +0200 Subject: [PATCH 5/7] factorize code --- src/compat/eme/eme-api-implementation.ts | 74 ++++++++++-------------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/compat/eme/eme-api-implementation.ts b/src/compat/eme/eme-api-implementation.ts index 780e194bb9..0fc9e924f1 100644 --- a/src/compat/eme/eme-api-implementation.ts +++ b/src/compat/eme/eme-api-implementation.ts @@ -144,29 +144,8 @@ function getEmeApiImplementation( let createCustomMediaKeys: (keyType: string) => ICustomMediaKeys; if (preferredApiType === "webkit" && WebKitMediaKeysConstructor !== undefined) { - const compatibleEventListener = createCompatibleEventListener( - ["needkey"], - undefined /* prefixes */, - ); - onEncrypted = ( - target: IEventTargetLike, - listener: (event?: Event) => void, - cancelSignal: CancellationSignal, - ) => { - compatibleEventListener( - target, - (event?: Event) => { - const patchedEvent = object_assign( - { forceSessionRecreation: true }, - event as MediaEncryptedEvent, - ); - listener(patchedEvent); - }, - cancelSignal, - ); - }; - const callbacks = getWebKitMediaKeysCallbacks(); + onEncrypted = createOnEncryptedForWebkit(); isTypeSupported = callbacks.isTypeSupported; createCustomMediaKeys = callbacks.createCustomMediaKeys; setMediaKeys = callbacks.setMediaKeys; @@ -182,28 +161,7 @@ function getEmeApiImplementation( implementation = "older-webkit"; // This is for WebKit with prefixed EME api } else if (WebKitMediaKeysConstructor !== undefined) { - const compatibleEventListener = createCompatibleEventListener( - ["needkey"], - undefined /* prefixes */, - ); - onEncrypted = ( - target: IEventTargetLike, - listener: (event?: Event) => void, - cancelSignal: CancellationSignal, - ) => { - compatibleEventListener( - target, - (event?: Event) => { - const patchedEvent = object_assign( - { forceSessionRecreation: true }, - event as MediaEncryptedEvent, - ); - listener(patchedEvent); - }, - cancelSignal, - ); - }; - + onEncrypted = createOnEncryptedForWebkit(); const callbacks = getWebKitMediaKeysCallbacks(); isTypeSupported = callbacks.isTypeSupported; createCustomMediaKeys = callbacks.createCustomMediaKeys; @@ -317,6 +275,34 @@ function getEmeApiImplementation( implementation, }; } +/** + * Create an event listener for the "webkitneedkey" event + * @returns + */ +function createOnEncryptedForWebkit(): IEmeApiImplementation["onEncrypted"] { + const compatibleEventListener = createCompatibleEventListener( + ["needkey"], + undefined /* prefixes */, + ); + const onEncrypted = ( + target: IEventTargetLike, + listener: (event?: Event) => void, + cancelSignal: CancellationSignal, + ) => { + compatibleEventListener( + target, + (event?: Event) => { + const patchedEvent = object_assign( + { forceSessionRecreation: true }, + event as MediaEncryptedEvent, + ); + listener(patchedEvent); + }, + cancelSignal, + ); + }; + return onEncrypted; +} /** * Set the given MediaKeys on the given HTMLMediaElement. From 20d60cf318f08ab7018ce9ec4c9aaf51e3346358 Mon Sep 17 00:00:00 2001 From: Florent Date: Wed, 31 Jul 2024 15:33:09 +0200 Subject: [PATCH 6/7] use function overloading for better types --- src/compat/eme/eme-api-implementation.ts | 9 ++++++--- src/compat/event_listeners.ts | 20 ++++++++++++++++++++ src/main_thread/decrypt/content_decryptor.ts | 3 +-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/compat/eme/eme-api-implementation.ts b/src/compat/eme/eme-api-implementation.ts index 0fc9e924f1..5a8c2833d4 100644 --- a/src/compat/eme/eme-api-implementation.ts +++ b/src/compat/eme/eme-api-implementation.ts @@ -20,7 +20,10 @@ import getMozMediaKeysCallbacks, { import getOldKitWebKitMediaKeyCallbacks, { isOldWebkitMediaElement, } from "./custom_media_keys/old_webkit_media_keys"; -import type { ICustomMediaKeys } from "./custom_media_keys/types"; +import type { + ICustomMediaEncryptedEvent, + ICustomMediaKeys, +} from "./custom_media_keys/types"; import getWebKitMediaKeysCallbacks from "./custom_media_keys/webkit_media_keys"; import { WebKitMediaKeysConstructor } from "./custom_media_keys/webkit_media_keys_constructor"; @@ -64,7 +67,7 @@ export interface IEmeApiImplementation { */ onEncrypted: ( target: IEventTargetLike, - listener: (evt: unknown) => void, + listener: (evt: ICustomMediaEncryptedEvent) => void, cancelSignal: CancellationSignal, ) => void; @@ -286,7 +289,7 @@ function createOnEncryptedForWebkit(): IEmeApiImplementation["onEncrypted"] { ); const onEncrypted = ( target: IEventTargetLike, - listener: (event?: Event) => void, + listener: (event: ICustomMediaEncryptedEvent) => void, cancelSignal: CancellationSignal, ) => { compatibleEventListener( diff --git a/src/compat/event_listeners.ts b/src/compat/event_listeners.ts index 073bd8545d..9500fb9a3c 100644 --- a/src/compat/event_listeners.ts +++ b/src/compat/event_listeners.ts @@ -30,6 +30,7 @@ import type { IEventTarget, IMediaElement, } from "./browser_compatibility_types"; +import type { ICustomMediaEncryptedEvent } from "./eme/custom_media_keys/types"; const BROWSER_PREFIXES = ["", "webkit", "moz", "ms"]; @@ -105,6 +106,16 @@ export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter, + prefixes?: string[], +): ( + element: IEventTargetLike, + listener: (event: ICustomMediaEncryptedEvent) => void, + cancelSignal: CancellationSignal, +) => void; + function createCompatibleEventListener( eventNames: string[], prefixes?: string[], @@ -112,6 +123,15 @@ function createCompatibleEventListener( element: IEventTargetLike, listener: (event?: Event) => void, cancelSignal: CancellationSignal, +) => void; + +function createCompatibleEventListener( + eventNames: string[] | Array<"needkey" | "encrypted">, + prefixes?: string[], +): ( + element: IEventTargetLike, + listener: (event?: Event | MediaEncryptedEvent) => void, + cancelSignal: CancellationSignal, ) => void { let mem: string | undefined; const prefixedEvents = eventPrefixed(eventNames, prefixes); diff --git a/src/main_thread/decrypt/content_decryptor.ts b/src/main_thread/decrypt/content_decryptor.ts index 53c034da06..07726b706a 100644 --- a/src/main_thread/decrypt/content_decryptor.ts +++ b/src/main_thread/decrypt/content_decryptor.ts @@ -17,7 +17,6 @@ import type { IMediaElement } from "../../compat/browser_compatibility_types"; import type { ICustomMediaKeys, ICustomMediaKeySystemAccess } from "../../compat/eme"; import eme, { getInitData } from "../../compat/eme"; -import type { ICustomMediaEncryptedEvent } from "../../compat/eme/custom_media_keys/types"; import config from "../../config"; import { EncryptedMediaError, OtherError } from "../../errors"; import log from "../../log"; @@ -178,7 +177,7 @@ export default class ContentDecryptor extends EventEmitter { log.debug("DRM: Encrypted event received from media element."); - const initData = getInitData(evt as ICustomMediaEncryptedEvent); + const initData = getInitData(evt); if (initData !== null) { this.onInitializationData(initData); } From d563ea251f3a26b42cc6491853cd54744452c940 Mon Sep 17 00:00:00 2001 From: Paul Berberian Date: Mon, 5 Aug 2024 11:31:32 +0200 Subject: [PATCH 7/7] DRM: fix object_assign function naming --- src/compat/eme/eme-api-implementation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compat/eme/eme-api-implementation.ts b/src/compat/eme/eme-api-implementation.ts index 5a8c2833d4..b91b963291 100644 --- a/src/compat/eme/eme-api-implementation.ts +++ b/src/compat/eme/eme-api-implementation.ts @@ -3,7 +3,7 @@ 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 object_assign from "../../utils/object_assign"; +import objectAssign from "../../utils/object_assign"; import type { CancellationSignal } from "../../utils/task_canceller"; import type { IMediaElement } from "../browser_compatibility_types"; import { isIE11 } from "../browser_detection"; @@ -295,7 +295,7 @@ function createOnEncryptedForWebkit(): IEmeApiImplementation["onEncrypted"] { compatibleEventListener( target, (event?: Event) => { - const patchedEvent = object_assign( + const patchedEvent = objectAssign( { forceSessionRecreation: true }, event as MediaEncryptedEvent, );