Skip to content

Commit

Permalink
Merge pull request #1497 from canalplus/misc/revert-safari-fix
Browse files Browse the repository at this point in the history
Revert #1479 (Safari DRM audio track issue)
  • Loading branch information
peaBerberian authored Aug 13, 2024
2 parents 3f8b756 + ac1d54c commit b7e6d44
Show file tree
Hide file tree
Showing 6 changed files with 13 additions and 135 deletions.
4 changes: 0 additions & 4 deletions src/compat/eme/custom_media_keys/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,3 @@ export interface IMediaKeySessionEvents {
// "keyerror"
/* "error" */
}

export interface ICustomMediaEncryptedEvent extends MediaEncryptedEvent {
forceSessionRecreation?: boolean | undefined;
}
40 changes: 4 additions & 36 deletions src/compat/eme/eme-api-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ 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 type { CancellationSignal } from "../../utils/task_canceller";
import type { IMediaElement } from "../browser_compatibility_types";
import { isIE11 } from "../browser_detection";
Expand All @@ -20,10 +19,7 @@ import getMozMediaKeysCallbacks, {
import getOldKitWebKitMediaKeyCallbacks, {
isOldWebkitMediaElement,
} from "./custom_media_keys/old_webkit_media_keys";
import type {
ICustomMediaEncryptedEvent,
ICustomMediaKeys,
} from "./custom_media_keys/types";
import type { 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";

Expand Down Expand Up @@ -67,7 +63,7 @@ export interface IEmeApiImplementation {
*/
onEncrypted: (
target: IEventTargetLike,
listener: (evt: ICustomMediaEncryptedEvent) => void,
listener: (evt: unknown) => void,
cancelSignal: CancellationSignal,
) => void;

Expand Down Expand Up @@ -147,8 +143,8 @@ function getEmeApiImplementation(
let createCustomMediaKeys: (keyType: string) => ICustomMediaKeys;

if (preferredApiType === "webkit" && WebKitMediaKeysConstructor !== undefined) {
onEncrypted = createCompatibleEventListener(["needkey"]);
const callbacks = getWebKitMediaKeysCallbacks();
onEncrypted = createOnEncryptedForWebkit();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
setMediaKeys = callbacks.setMediaKeys;
Expand All @@ -164,7 +160,7 @@ function getEmeApiImplementation(
implementation = "older-webkit";
// This is for WebKit with prefixed EME api
} else if (WebKitMediaKeysConstructor !== undefined) {
onEncrypted = createOnEncryptedForWebkit();
onEncrypted = createCompatibleEventListener(["needkey"]);
const callbacks = getWebKitMediaKeysCallbacks();
isTypeSupported = callbacks.isTypeSupported;
createCustomMediaKeys = callbacks.createCustomMediaKeys;
Expand Down Expand Up @@ -278,34 +274,6 @@ 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: ICustomMediaEncryptedEvent) => void,
cancelSignal: CancellationSignal,
) => {
compatibleEventListener(
target,
(event?: Event) => {
const patchedEvent = objectAssign(
{ forceSessionRecreation: true },
event as MediaEncryptedEvent,
);
listener(patchedEvent);
},
cancelSignal,
);
};
return onEncrypted;
}

/**
* Set the given MediaKeys on the given HTMLMediaElement.
Expand Down
8 changes: 3 additions & 5 deletions src/compat/eme/get_init_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ 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 {
Expand Down Expand Up @@ -51,7 +50,6 @@ export interface IEncryptedEventData {
*/
data: Uint8Array;
}>;
forceSessionRecreation?: boolean | undefined;
}

/**
Expand Down Expand Up @@ -147,15 +145,15 @@ function isPSSHAlreadyEncountered(
* encountered in the given event.
*/
export default function getInitData(
encryptedEvent: ICustomMediaEncryptedEvent,
encryptedEvent: MediaEncryptedEvent,
): IEncryptedEventData | null {
const { initData, initDataType, forceSessionRecreation } = encryptedEvent;
const { initData, initDataType } = encryptedEvent;
if (isNullOrUndefined(initData)) {
log.warn("Compat: No init data found on media encrypted event.");
return null;
}

const initDataBytes = new Uint8Array(initData);
const values = getInitializationDataValues(initDataBytes);
return { type: initDataType, values, forceSessionRecreation };
return { type: initDataType, values };
}
35 changes: 5 additions & 30 deletions src/compat/event_listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type {
IEventTarget,
IMediaElement,
} from "./browser_compatibility_types";
import type { ICustomMediaEncryptedEvent } from "./eme/custom_media_keys/types";

const BROWSER_PREFIXES = ["", "webkit", "moz", "ms"];

Expand Down Expand Up @@ -83,14 +82,8 @@ function eventPrefixed(eventNames: string[], prefixes?: string[]): string[] {
}

export interface IEventEmitterLike {
addEventListener: (
eventName: string,
handler: EventListenerOrEventListenerObject,
) => void;
removeEventListener: (
eventName: string,
handler: EventListenerOrEventListenerObject,
) => void;
addEventListener: (eventName: string, handler: () => void) => void;
removeEventListener: (eventName: string, handler: () => void) => void;
}

export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter<unknown>;
Expand All @@ -106,44 +99,26 @@ export type IEventTargetLike = HTMLElement | IEventEmitterLike | IEventEmitter<u
* @returns {Function} - Returns function allowing to easily add a callback to
* be triggered when that event is emitted on a given event target.
*/

function createCompatibleEventListener(
eventNames: Array<"needkey" | "encrypted">,
prefixes?: string[],
): (
element: IEventTargetLike,
listener: (event: ICustomMediaEncryptedEvent) => void,
cancelSignal: CancellationSignal,
) => void;

function createCompatibleEventListener(
eventNames: string[],
prefixes?: string[],
): (
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,
listener: (event?: unknown) => void,
cancelSignal: CancellationSignal,
) => void {
let mem: string | undefined;
const prefixedEvents = eventPrefixed(eventNames, prefixes);

return (
element: IEventTargetLike,
listener: (event?: Event) => void,
listener: (event?: unknown) => void,
cancelSignal: CancellationSignal,
) => {
if (cancelSignal.isCancelled()) {
return;
}

// if the element is a HTMLElement we can detect
// the supported event, and memoize it in `mem`
if (typeof HTMLElement !== "undefined" && element instanceof HTMLElement) {
Expand Down
59 changes: 1 addition & 58 deletions src/main_thread/decrypt/content_decryptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
mediaElement,
(evt) => {
log.debug("DRM: Encrypted event received from media element.");
const initData = getInitData(evt);
const initData = getInitData(evt as MediaEncryptedEvent);
if (initData !== null) {
this.onInitializationData(initData);
}
Expand Down Expand Up @@ -730,21 +730,6 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
return false;
}

/**
* On Safari using Directfile, the old EME implementation triggers
* the "webkitneedkey" event instead of "encrypted". There's an issue in Safari
* where "webkitneedkey" fires too early before all tracks are added from an HLS playlist.
* Safari incorrectly assumes some keys are missing for these tracks,
* leading to repeated "webkitneedkey" events. Because RxPlayer recognizes
* it already has a session for these keys and ignores the events,
* the content remains frozen. To resolve this, the session is re-created.
*/
const forceSessionRecreation = initializationData.forceSessionRecreation;
if (forceSessionRecreation === true) {
this.removeSessionForInitData(initializationData, mediaKeysData);
return false;
}

// Check if the compatible session is blacklisted
const blacklistedSessionError = compatibleSessionInfo.blacklistedSessionError;
if (!isNullOrUndefined(blacklistedSessionError)) {
Expand Down Expand Up @@ -849,48 +834,6 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
return false;
}

/**
* Remove the session corresponding to the initData provided, and close it.
* It does nothing if no session was found for this initData.
* @param {Object} initData : The initialization data corresponding to the session
* that need to be removed
* @param {Object} mediaKeysData : The media keys data
*/
private removeSessionForInitData(
initData: IProcessedProtectionData,
mediaKeysData: IAttachedMediaKeysData,
) {
const { stores } = mediaKeysData;
/** Remove the session and close it from the loadedSessionStore */
const entry = stores.loadedSessionsStore.reuse(initData);
if (entry !== null) {
stores.loadedSessionsStore
.closeSession(entry.mediaKeySession)
.catch(() =>
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.
Expand Down
2 changes: 0 additions & 2 deletions src/main_thread/decrypt/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ export interface IProtectionData {
content?: IContent;
/** Every initialization data for that type. */
values: IInitDataValue[];

forceSessionRecreation?: boolean | undefined;
}

/** Protection initialization data actually processed by the `ContentDecryptor`. */
Expand Down

0 comments on commit b7e6d44

Please sign in to comment.