Compare commits

..
4 Commits
9 changed files with 76 additions and 17 deletions
+3
View File
@@ -52,6 +52,9 @@ export function usePlayerState<Key extends keyof OmniPlayerState>(
case "status":
em.addPlayerStatusListener(setState);
return () => em.removePlayerStatusListener(setState);
case "castStatus":
// TODO: implement this
return;
}
}, [player, key]);
+7
View File
@@ -6,6 +6,7 @@ import {
selectPlayback,
selectPlaybackRate,
selectQuality,
selectRemotePlayback,
selectTextTrack,
selectTime,
selectVolume,
@@ -177,6 +178,12 @@ export const stateMapper = {
if (!q) return true;
return !q.videoRenditionList.some((r) => r.selected);
}),
...createMapper("castStatus", selectRemotePlayback, (r) => {
if (!r) return "unavailable";
if (r.remotePlaybackState === "connecting") return "connecting";
if (r.remotePlaybackState === "connected") return "connected";
return r.remotePlaybackAvailability
}),
};
export function usePlayerState<Key extends keyof OmniPlayerState>(
+2
View File
@@ -2,6 +2,7 @@ export { useEvent, usePlayerState } from "./events";
export { OmniProvider, usePlayer } from "./provider";
export type { OmniEvents } from "./types/events";
export type {
CastStatus,
OmniPlayer,
OmniPlayerState,
PlayerStatus,
@@ -9,6 +10,7 @@ export type {
Track,
} from "./types/player";
export type {
CastOptions,
Metadata,
MixAudioMode,
Source,
+11 -1
View File
@@ -7,12 +7,13 @@ import {
} from "@videojs/react";
import { stateMapper } from "./events.web";
import type {
CastStatus,
OmniPlayer,
PlayerStatus,
Rendition,
Track,
} from "./types/player";
import type { Source, Subtitle } from "./types/source";
import type { CastOptions, Source, Subtitle } from "./types/source";
export type SubtitleFormat = "vtt" | "ass" | "pgs" | "native";
@@ -51,6 +52,15 @@ export class WebOmniPlayer implements OmniPlayer {
this._store = store;
}
get castStatus(): CastStatus {
return stateMapper.castStatus.mapper(this._store.state);
}
toggleCastStatus(): void {
this._store.state.toggleRemotePlayback();
}
castOptions: CastOptions | null = null;
_source: Source | null = null;
private _showNotification = false;
+3 -1
View File
@@ -2,7 +2,7 @@ import { createContext, type ReactNode, useContext, useEffect } from "react";
import { NitroModules } from "react-native-nitro-modules";
import type { OmniPlayerFactory } from "./specs/omni-player.nitro";
import type { OmniPlayer } from "./types/player";
import type { Source } from "./types/source";
import type { CastOptions, Source } from "./types/source";
import { useLazyRef } from "./utils/lazy-ref";
const ProviderFactory =
@@ -14,8 +14,10 @@ export const OmniProvider = ({
children,
source,
showNotification = false,
cast: _,
}: {
source: Source;
cast?: CastOptions;
children: ReactNode;
showNotification?: boolean;
}) => {
+15 -3
View File
@@ -9,7 +9,7 @@ import {
} from "react";
import { WebOmniPlayer } from "./player.web";
import type { OmniPlayer } from "./types/player";
import type { Source } from "./types/source";
import type { CastOptions, Source } from "./types/source";
import { useLazyRef } from "./utils/lazy-ref";
export const VideoPlayer = createPlayer({ features: videoFeatures });
@@ -19,15 +19,21 @@ const PlayerCtx = createContext<OmniPlayer>(null!);
export const OmniProvider = ({
children,
source,
cast,
showNotification = false,
}: {
source: Source;
cast?: CastOptions;
children: ReactNode;
showNotification?: boolean;
}) => {
return (
<VideoPlayer.Provider>
<PlayerInitializer source={source} showNotification={showNotification}>
<PlayerInitializer
source={source}
cast={cast}
showNotification={showNotification}
>
{children}
</PlayerInitializer>
</VideoPlayer.Provider>
@@ -37,10 +43,12 @@ export const OmniProvider = ({
const PlayerInitializer = ({
children,
source,
cast,
showNotification,
}: {
children: ReactNode;
source: Source;
cast?: CastOptions;
showNotification: boolean;
}) => {
const store = VideoPlayer.usePlayer();
@@ -52,10 +60,14 @@ const PlayerInitializer = ({
const uri = source.src[0]?.uri;
if (uri !== seekedForSrc.current) {
seekedForSrc.current = uri;
if (source.startTime) store.seek(source.startTime)
if (source.startTime) store.seek(source.startTime);
}
}, [source, store]);
useEffect(() => {
player.castOptions = cast ?? null;
}, [cast]);
useEffect(() => {
player.showNotification = showNotification;
}, [showNotification]);
+10
View File
@@ -5,6 +5,8 @@ export interface OmniPlayer extends OmniPlayerState {
pause(): void;
seekBy(offset: number): void;
toggleCastStatus(): void;
// trigger the prev/next event manually, the user has to implement the event
playPrev(): void;
playNext(): void;
@@ -32,10 +34,18 @@ export interface OmniPlayerState {
volume: number;
muted: boolean;
readonly isAutoQuality: boolean;
readonly castStatus: CastStatus;
}
export type PlayerStatus = "idle" | "loading" | "readyToPlay" | "error";
export type CastStatus =
| "connecting"
| "connected"
| "available"
| "unavailable"
| "unsupported";
export interface Track {
readonly id: string;
readonly label?: string;
+7
View File
@@ -7,6 +7,13 @@ export interface Source {
fonts?: string[];
metadata?: Metadata;
mixAudio?: MixAudioMode;
// Opaque, consumer-defined payload forwarded verbatim to the cast receiver
// as the load request's customData. omni does not interpret it.
castData?: unknown;
}
export interface CastOptions {
receiverApplicationId?: string;
}
export interface VideoSrc {
+18 -12
View File
@@ -116,20 +116,26 @@ export const OmniView = ({
const headersRef = useRef(src?.headers);
headersRef.current = src?.headers;
const castData = player.source?.castData;
const config = useMemo<HlsMediaConfig>(
() => ({
hlsJs: {
xhrSetup: (xhr: XMLHttpRequest) => {
const headers = headersRef.current;
if (!headers) return;
for (const [key, value] of Object.entries(headers)) {
if (value) xhr.setRequestHeader(key, value);
}
() =>
({
hlsJs: {
xhrSetup: (xhr: XMLHttpRequest) => {
const headers = headersRef.current;
if (!headers) return;
for (const [key, value] of Object.entries(headers)) {
if (value) xhr.setRequestHeader(key, value);
}
},
},
},
}),
[],
googleCast: {
receiver: player.castOptions?.receiverApplicationId,
customData: castData ?? null,
},
}) as HlsMediaConfig,
[player.castOptions, castData],
);
return (
@@ -141,7 +147,7 @@ export const OmniView = ({
<Tech
ref={ref}
src={src.uri}
config={isHls ? config : undefined}
config={config}
autoPlay={autoplay}
playsInline
crossOrigin="anonymous"