Skip to content

Commit

Permalink
Implement Dummy media API
Browse files Browse the repository at this point in the history
  • Loading branch information
peaBerberian committed Aug 1, 2024
1 parent 5a6f6a1 commit 8b6d058
Show file tree
Hide file tree
Showing 16 changed files with 2,862 additions and 7 deletions.
23 changes: 23 additions & 0 deletions demo/scripts/components/Options/Playback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ function PlaybackConfig({
onAutoPlayChange,
tryRelyOnWorker,
onTryRelyOnWorkerChange,
useDummyMediaElement,
onUseDummyMediaElementChange,
}: {
autoPlay: boolean;
onAutoPlayChange: (val: boolean) => void;
tryRelyOnWorker: boolean;
onTryRelyOnWorkerChange: (val: boolean) => void;
useDummyMediaElement: boolean;
onUseDummyMediaElementChange: (val: boolean) => void;
}): JSX.Element {
return (
<>
Expand Down Expand Up @@ -51,6 +55,25 @@ function PlaybackConfig({
: "Currently running the RxPlayer's main logic only in main thread."}
</span>
</li>

<li>
<Checkbox
className="playerOptionsCheckBox playerOptionsCheckBoxTitle"
name="useDummyMediaElement"
ariaLabel="Rely in a WebWorker when possible"
checked={useDummyMediaElement}
onChange={onUseDummyMediaElementChange}
>
Dummy Media API
</Checkbox>
<span className="option-desc">
{useDummyMediaElement
? "Use mocked media API: The content will not really play but the RxPlayer " +
"will believe it does. Useful for debugging the RxPlayer's logic even on " +
"undecipherable or undecodable content."
: "Actually play the chosen content."}
</span>
</li>
</>
);
}
Expand Down
32 changes: 30 additions & 2 deletions demo/scripts/controllers/Player.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from "react";
import DummyMediaElement from "../../../src/experimental/tools/DummyMediaElement";
import type {
IAudioRepresentationsSwitchingMode,
ILoadVideoOptions,
Expand All @@ -17,6 +18,7 @@ import type {
ILoadVideoSettings,
IConstructorSettings,
} from "../lib/defaultOptionsValues";
import { toDummyDrmConfiguration } from "../lib/parseDRMConfigurations";

const { useCallback, useEffect, useRef, useState } = React;

Expand Down Expand Up @@ -45,6 +47,7 @@ function Player(): JSX.Element {
defaultOptionsValues.loadVideo,
);
const [relyOnWorker, setRelyOnWorker] = useState(false);
const [useDummyMediaElement, setUseDummyMediaElement] = useState(false);
const [hasUpdatedPlayerOptions, setHasUpdatedPlayerOptions] = useState(false);
const displaySpinnerTimeoutRef = useRef<number | null>(null);

Expand Down Expand Up @@ -156,11 +159,14 @@ function Player(): JSX.Element {
if (playerModule) {
playerModule.destroy();
}
const videoElement = useDummyMediaElement
? (new DummyMediaElement() as unknown as HTMLMediaElement)
: videoElementRef.current;
const playerMod = new PlayerModule(
Object.assign(
{},
{
videoElement: videoElementRef.current,
videoElement,
textTrackElement: textTrackElementRef.current,
debugElement: debugElementRef.current,
},
Expand All @@ -169,7 +175,24 @@ function Player(): JSX.Element {
);
setPlayerModule(playerMod);
return playerMod;
}, [playerOpts, playerModule]);
}, [useDummyMediaElement, playerOpts, playerModule]);

useEffect(() => {
if (playerModule === null) {
return;
}
const mediaElement = playerModule.actions.getMediaElement();
if (mediaElement === null) {
return;
}
if (useDummyMediaElement) {
if (!(mediaElement instanceof DummyMediaElement)) {
setHasUpdatedPlayerOptions(true);
}
} else if (mediaElement !== videoElementRef.current) {
setHasUpdatedPlayerOptions(true);
}
}, [setHasUpdatedPlayerOptions, useDummyMediaElement, playerModule]);

const onVideoClick = useCallback(() => {
if (playerModule === null) {
Expand Down Expand Up @@ -202,6 +225,9 @@ function Player(): JSX.Element {
created.actions.updateWorkerMode(relyOnWorker);
playerMod = created;
}
if (useDummyMediaElement && contentInfo.keySystems !== undefined) {
contentInfo.keySystems = toDummyDrmConfiguration(contentInfo.keySystems);
}
loadContent(playerMod, contentInfo, loadVideoOpts);
},
[
Expand Down Expand Up @@ -285,6 +311,8 @@ function Player(): JSX.Element {
}
tryRelyOnWorker={relyOnWorker}
updateTryRelyOnWorker={setRelyOnWorker}
useDummyMediaElement={useDummyMediaElement}
updateUseDummyMediaElement={setUseDummyMediaElement}
/>
<div className="video-player-wrapper" ref={playerWrapperElementRef}>
<div className="video-screen-parent">
Expand Down
13 changes: 13 additions & 0 deletions demo/scripts/controllers/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ function Settings({
updateDefaultVideoRepresentationsSwitchingMode,
tryRelyOnWorker,
updateTryRelyOnWorker,
useDummyMediaElement,
updateUseDummyMediaElement,
}: {
playerOptions: IConstructorSettings;
updatePlayerOptions: (
Expand All @@ -50,6 +52,8 @@ function Settings({
) => void;
tryRelyOnWorker: boolean;
updateTryRelyOnWorker: (tryRelyOnWorker: boolean) => void;
useDummyMediaElement: boolean;
updateUseDummyMediaElement: (useDummyMediaElement: boolean) => void;
showOptions: boolean;
}): JSX.Element | null {
const {
Expand Down Expand Up @@ -123,6 +127,13 @@ function Settings({
[updateLoadVideoOptions],
);

const onUseDummyMediaElementChange = useCallback(
(useDummyMediaElement: boolean) => {
updateUseDummyMediaElement(useDummyMediaElement);
},
[updateUseDummyMediaElement],
);

const onVideoResolutionLimitChange = useCallback(
(videoResolutionLimitArg: { value: string }) => {
updatePlayerOptions((prevOptions) => {
Expand Down Expand Up @@ -366,6 +377,8 @@ function Settings({
onAutoPlayChange={onAutoPlayChange}
tryRelyOnWorker={tryRelyOnWorker}
onTryRelyOnWorkerChange={onTryRelyOnWorkerChange}
useDummyMediaElement={useDummyMediaElement}
onUseDummyMediaElementChange={onUseDummyMediaElementChange}
/>
</Option>
<Option title="Video adaptive settings">
Expand Down
41 changes: 41 additions & 0 deletions demo/scripts/lib/parseDRMConfigurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,47 @@ export default async function parseDRMConfigurations(
return keySystems.filter((ks): ks is IKeySystemOption => ks !== undefined);
}

export function toDummyDrmConfiguration(
baseOptions: IKeySystemOption[],
): IKeySystemOption[] {
return baseOptions.map((ks) => {
return {
...ks,
getLicense(...args: Parameters<IKeySystemOption["getLicense"]>) {
try {
const challenge = args[0];
const challengeStr = utf8ToStr(challenge);
const challengeObj = JSON.parse(challengeStr) as {
certificate: string | null;
persistent: boolean;
keyIds: string[];
};
const keys: Record<
string,
{
policyLevel: number;
}
> = {};
challengeObj.keyIds.forEach((kid) => {
keys[kid] = {
policyLevel: 50,
};
});
const license = {
type: "license",
persistent: false,
keys,
};
const licenseU8 = strToUtf8(JSON.stringify(license));
return licenseU8.buffer;
} catch (e) {
return ks.getLicense(...args);
}
},
};
});
}

function getServerCertificate(url: string): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
Expand Down
14 changes: 14 additions & 0 deletions demo/scripts/modules/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ const PlayerModule = declareModule(
player.setVolume(volume);
},

getMediaElement(): HTMLMediaElement | null {
return player.getVideoElement();
},

updateWorkerMode(enabled: boolean) {
if (enabled && !hasAttachedMultithread) {
attachMultithread(player);
Expand Down Expand Up @@ -302,6 +306,11 @@ const PlayerModule = declareModule(
if (!isStopped && !hasEnded) {
state.update("isPaused", false);
}
setTimeout(() => {
if (!isStopped && !hasEnded && player.isPaused() && !state.get("isPaused")) {
state.update("isPaused", true);
}
}, 100);
},

pause() {
Expand All @@ -312,6 +321,11 @@ const PlayerModule = declareModule(
if (!isStopped && !hasEnded) {
state.update("isPaused", true);
}
setTimeout(() => {
if (!isStopped && !hasEnded && !player.isPaused() && state.get("isPaused")) {
state.update("isPaused", false);
}
}, 100);
},

stop() {
Expand Down
6 changes: 4 additions & 2 deletions src/compat/browser_compatibility_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import type { IListener } from "../utils/event_emitter";
import globalScope from "../utils/global_scope";
import isNullOrUndefined from "../utils/is_null_or_undefined";
import type { IEmeApiImplementation } from "./eme";

/**
* Browser implementation of a VTTCue constructor.
Expand Down Expand Up @@ -229,6 +230,8 @@ export interface IMediaElement extends IEventTarget<IMediaElementEventMap> {
*/
FORCED_MEDIA_SOURCE?: new () => IMediaSource;

FORCED_EME_API?: IEmeApiImplementation;

/* From `HTMLMediaElement`: */
autoplay: boolean;
buffered: TimeRanges;
Expand All @@ -255,11 +258,10 @@ export interface IMediaElement extends IEventTarget<IMediaElementEventMap> {

addTextTrack: (kind: TextTrackKind) => TextTrack;
appendChild<T extends Node>(x: T): void;
hasAttribute(attr: string): boolean;
hasChildNodes(): boolean;
pause(): void;
play(): Promise<void>;
removeAttribute(attr: string): void;
removeAttribute(attr: "src"): void;
removeChild(x: unknown): void;
setMediaKeys(x: IMediaKeys | null): Promise<void>;

Expand Down
Loading

0 comments on commit 8b6d058

Please sign in to comment.