Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Third attempt for a preload API #1407

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions demo/scripts/controllers/ContentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,12 @@ function getKeySystemsOption(

function ContentList({
loadVideo,
preloadVideo,
showOptions,
onOptionToggle,
}: {
loadVideo: (opts: ILoadVideoOptions) => void;
preloadVideo: (opts: ILoadVideoOptions) => void;
showOptions: boolean;
onOptionToggle: () => void;
}): JSX.Element {
Expand Down Expand Up @@ -314,6 +316,43 @@ function ContentList({
[loadVideo],
);

/**
* Load the given displayed content through the player.
* @param {Object|null} content
*/
const preloadContent = React.useCallback(
(content: IDisplayedContent) => {
const {
url,
transport,
fallbackKeyError,
fallbackLicenseRequest,
isLowLatency,
drmInfos,
} = content;

getKeySystemsOption(drmInfos ?? [], {
fallbackKeyError: !!fallbackKeyError,
fallbackLicenseRequest: !!fallbackLicenseRequest,
}).then(
(keySystems) => {
preloadVideo({
url: url ?? "",
transport: transport ?? "",
textTrackMode: "html",
lowLatencyMode: isLowLatency,
keySystems,
});
},
() => {
/* eslint-disable-next-line no-console */
console.error("Could not construct key systems option");
},
);
},
[loadVideo],
);

/**
* Load a custom content entered in a custom link.
* @param {string} url
Expand Down Expand Up @@ -349,6 +388,41 @@ function ContentList({
],
);

/**
* Load a custom content entered in a custom link.
* @param {string} url
* @param {Array.<Object>} drmInfos
*/
const preloadUrl = React.useCallback(
(url: string, drmInfos: IDrmInfo[]) => {
getKeySystemsOption(drmInfos, {
fallbackKeyError: !!shouldFallbackOnKeyError,
fallbackLicenseRequest: !!shouldFallbackOnLicenseReqError,
}).then(
(keySystems) => {
preloadVideo({
url: url,
transport: transportType.toLowerCase(),
textTrackMode: "html",
lowLatencyMode: isLowLatencyChecked,
keySystems,
});
},
() => {
/* eslint-disable-next-line no-console */
console.error("Could not construct key systems option");
},
);
},
[
loadVideo,
shouldFallbackOnKeyError,
shouldFallbackOnLicenseReqError,
transportType,
isLowLatencyChecked,
],
);

/**
* Change the content chosen in the list.
* @param {number} index - index in the lsit
Expand Down Expand Up @@ -515,6 +589,22 @@ function ContentList({
}
};

const onClickPreload = () => {
if (contentChoiceIndex === 0) {
const drmInfos = [
{
licenseServerUrl,
serverCertificateUrl,
drm: chosenDRMType,
customKeySystem,
},
];
preloadUrl(currentManifestURL, drmInfos);
} else {
preloadContent(contentsToSelect[contentChoiceIndex]);
}
};

const saveCurrentContent = () => {
const contentToSave: IContentInfo = {
name: contentNameField,
Expand Down Expand Up @@ -751,6 +841,13 @@ function ContentList({
value={String.fromCharCode(0xf144)}
disabled={false}
/>
<Button
className="choice-input-button load-button"
ariaLabel="Preload the selected content now"
onClick={onClickPreload}
value={String.fromCharCode(0x2b07)}
disabled={false}
/>
</div>
</div>
{isCustomContent || (isLocalContent && isSavingOrUpdating) ? (
Expand Down
39 changes: 37 additions & 2 deletions demo/scripts/controllers/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,33 @@ function Player(): JSX.Element {
}
}, [playerModule]);

const startContent = useCallback(
const preloadContentCallback = useCallback(
(contentInfo: ILoadVideoOptions) => {
let playerMod = playerModule;

// TODO this risks stopping the previous content if instance options
// have been updated
if (playerMod === null || hasUpdatedPlayerOptions) {
setHasUpdatedPlayerOptions(false);
const created = createNewPlayerModule();
if (created === undefined) {
return;
}
created.actions.updateWorkerMode(relyOnWorker);
playerMod = created;
}
preloadContent(playerMod, contentInfo, loadVideoOpts);
},
[
playerModule,
relyOnWorker,
hasUpdatedPlayerOptions,
createNewPlayerModule,
loadVideoOpts,
],
);

const startContentCallback = useCallback(
(contentInfo: ILoadVideoOptions) => {
let playerMod = playerModule;
if (playerMod === null || hasUpdatedPlayerOptions) {
Expand Down Expand Up @@ -261,7 +287,8 @@ function Player(): JSX.Element {
<section className="video-player-section">
<div className="video-player-content">
<ContentList
loadVideo={startContent}
loadVideo={startContentCallback}
preloadVideo={preloadContentCallback}
showOptions={showOptions}
onOptionToggle={onOptionToggle}
/>
Expand Down Expand Up @@ -352,4 +379,12 @@ function loadContent(
playerModule.actions.load(Object.assign({}, contentInfo, loadVideoOpts));
}

function preloadContent(
playerModule: IPlayerModule,
contentInfo: ILoadVideoOptions,
loadVideoOpts: ILoadVideoSettings,
) {
playerModule.actions.preload(Object.assign({}, contentInfo, loadVideoOpts));
}

export default Player;
34 changes: 34 additions & 0 deletions demo/scripts/modules/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ export interface IPlayerModuleState {
maximumPosition: null | undefined | number;
minimumPosition: null | undefined | number;
playbackRate: number;
preloads: Array<{
url: string | undefined;
id: string;
}>;
/** Try to play contents in "multithread" mode when possible. */
relyOnWorker: boolean;
/** Currently playing a content in "multithread" mode. */
Expand Down Expand Up @@ -196,6 +200,7 @@ const PlayerModule = declareModule(
maximumPosition: undefined,
minimumPosition: undefined,
playbackRate: 1,
preloads: [],
relyOnWorker: false,
useWorker: false,
subtitle: undefined,
Expand Down Expand Up @@ -274,6 +279,35 @@ const PlayerModule = declareModule(
state.update("relyOnWorker", enabled);
},

preload(arg: ILoadVideoOptions) {
const contentInfo = player.preloadVideo(
Object.assign(
{
mode: state.get("relyOnWorker") ? "auto" : "main",
textTrackElement,
transportOptions: { checkMediaSegmentIntegrity: true },
},
arg,
) as ILoadVideoOptions,
);
const preloads = state.get("preloads");
preloads.push({
id: contentInfo.id,
url: arg.url,
});
state.update("preloads", preloads);
},

loadPreload(preloadId: string) {
player.startPreload(preloadId);
const preloads = state.get("preloads");
const index = preloads.findIndex((p) => p.id === preloadId);
if (index >= 0) {
preloads.splice(index, 1);
}
state.update("preloads", preloads);
},

load(arg: ILoadVideoOptions) {
dettachVideoThumbnailLoader();
player.loadVideo(
Expand Down
2 changes: 1 addition & 1 deletion src/compat/__tests__/add_text_track.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, beforeEach, it, expect, vi } from "vitest";
import type { IMediaElement } from "../../compat/browser_compatibility_types";
import type { IMediaElement } from "../browser_compatibility_types";

// Needed for calling require (which itself is needed to mock properly) because
// it is not type-checked:
Expand Down
4 changes: 3 additions & 1 deletion src/compat/browser_compatibility_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export interface IMediaSource extends IEventTarget<IMediaSourceEventMap> {
duration: number;
handle?: MediaProvider | IMediaSource | undefined;
readyState: "closed" | "open" | "ended";
sourceBuffers: ISourceBufferList;
sourceBuffers: ISourceBufferList | ISourceBuffer[];

addSourceBuffer(type: string): ISourceBuffer;
clearLiveSeekableRange(): void;
Expand Down Expand Up @@ -229,6 +229,8 @@ export interface IMediaElementEventMap {
* implement it.
*/
export interface IMediaElement extends IEventTarget<IMediaElementEventMap> {
FORCED_MEDIA_SOURCE?: (new () => IMediaSource) | undefined;

/* From `HTMLMediaElement`: */
autoplay: boolean;
buffered: TimeRanges;
Expand Down
Loading
Loading