UNPKG

react-native-whip-whep

Version:

An implementation of WHIP and WHEP protocols functionalities for React Native.

91 lines 4.11 kB
import { requireNativeViewManager } from "expo-modules-core"; import * as React from "react"; import { PlayerType, } from "./ReactNativeMobileWhepClient.types"; const NativeViewBase = requireNativeViewManager("ReactNativeMobileWhepClientViewModule"); const NativeView = NativeViewBase; /** * A component that renders a native view for WHEP player type. * * This component accepts style and other props defined in * ReactNativeMobileWhepClientViewProps. * * @param {ReactNativeMobileWhepClientViewProps & { ref?: React.Ref<WhepClientViewRef> }} props - The properties to customize the component. * @returns {JSX.Element} The rendered component. */ export function WhepClientView(props) { const { ref, ...rest } = props; const nativeRef = React.useRef(null); /** * WORKAROUND: Race condition with Expo's ConcurrentFunctionDefinition * * The native view is created on a different queue than the function initialization, * which can cause functions to be called before the view is fully created. * * This results in errors like: * ``` * Error: Calling the function has failed * Caused by: The 1st argument cannot be cast to type View<ReactNativeMobileWhepClientView> * Caused by: Unable to find the 'ReactNativeMobileWhepClientView' view with tag 'XXX' * code: 'ERR_ARGUMENT_CAST' * ``` * * Additionally, without this workaround, the video renderer may not have a valid surface, * causing "EglRenderer: Dropping frame - No surface" errors. * * The `setTimeout(0)` defers execution to the next event loop tick, which seems like enough for the view to be created. * * This is the how they are trying to solving as of writing this comment: https://github.com/expo/expo/blob/3f3ad4f6f9ea096bb003c4345f0a09f2a41b3f25/packages/expo-modules-core/ios/Core/Functions/AsyncFunctionDefinition.swift#L125 * This might get fixed in the future and setTimeout won't be needed anymore. */ React.useImperativeHandle(ref, () => ({ createWhepClient: async (configurationOptions, preferredVideoCodecs, preferredAudioCodecs) => { return new Promise((resolve, reject) => { setTimeout(() => { nativeRef.current ?.createWhepClient(configurationOptions, preferredVideoCodecs, preferredAudioCodecs) .then(() => { resolve(); }) .catch((error) => { reject(error); }); }, 0); }); }, connect: async (options) => { await nativeRef.current?.connect(options); }, disconnect: async () => { await nativeRef.current?.disconnect(); }, pause: async () => { await nativeRef.current?.pause(); }, unpause: async () => { await nativeRef.current?.unpause(); }, getSupportedReceiverVideoCodecsNames: async () => { return ((await nativeRef.current?.getSupportedReceiverVideoCodecsNames()) ?? []); }, getSupportedReceiverAudioCodecsNames: async () => { return ((await nativeRef.current?.getSupportedReceiverAudioCodecsNames()) ?? []); }, setPreferredReceiverVideoCodecs: async (preferredCodecs) => { await nativeRef.current?.setPreferredReceiverVideoCodecs(preferredCodecs); }, setPreferredReceiverAudioCodecs: async (preferredCodecs) => { await nativeRef.current?.setPreferredReceiverAudioCodecs(preferredCodecs); }, startPip: async () => { await nativeRef.current?.startPip(); }, stopPip: async () => { await nativeRef.current?.stopPip(); }, togglePip: async () => { await nativeRef.current?.togglePip(); }, })); return <NativeView {...rest} playerType={PlayerType.WHEP} ref={nativeRef}/>; } //# sourceMappingURL=ReactNativeMobileWhepClientView.js.map