UNPKG

react-native-vision-camera

Version:

VisionCamera is the fastest and most powerful Camera for react-native.

87 lines (86 loc) 3.83 kB
import { useEffect, useMemo, useState } from 'react'; import { useMemoizedArray } from './useMemoizedArray'; import { useStableCallback } from './useStableCallback'; /** * Connect the given {@linkcode device} to the given {@linkcode outputs} * with the given {@linkcode mirrorMode} applied to each output, * and return a {@linkcode CameraController}. * * @note The {@linkcode outputs} have to be explicitly memoized. */ export function useCameraController(session, device, outputs, { mirrorMode = 'auto', constraints = [], onSessionConfigSelected, allowBackgroundAudioPlayback, getInitialExposureBias, onConfigured, getInitialZoom, } = {}) { const [controller, setController] = useState(); // Keep some things stable so the `useEffect` doesn't re-trigger const stableOutputs = useMemoizedArray(outputs); const stableGetInitialExposureBias = useStableCallback(getInitialExposureBias ?? (() => undefined)); const stableGetInitialZoom = useStableCallback(getInitialZoom ?? (() => undefined)); const stableOnConfigured = useStableCallback(onConfigured ?? (() => { })); const stableOnSessionConfigSelected = useStableCallback(onSessionConfigSelected ?? (() => { })); // TODO: Can we use something like useSyncExternalStore or whatever to avoid "wrong" dependencies? // biome-ignore lint/correctness/useExhaustiveDependencies: It's an array of objects, we either have to deep-memo or just stringify. const stableConstraints = useMemo(() => { return [ ...constraints, ...stableOutputs.map((o) => ({ resolutionBias: o })), ]; }, [JSON.stringify(constraints), stableOutputs]); // This effect re-configures the CameraSession and returns a `controller`. // This is expensive and should only be done if any inputs change. // To prevent shallow inequality causing a re-configuration, // we memoize all objects, callbacks or arrays beforehand. useEffect(() => { if (session == null) { // default: null -> empty controllers setController(undefined); return; } let isCanceled = false; const load = async () => { if (device == null) { // No device, configure with empty devices session.configure([], {}); setController(undefined); } else { // Device + outputs - configure session const controllers = await session.configure([ { input: device, outputs: stableOutputs.map((o) => ({ output: o, mirrorMode: mirrorMode, })), constraints: stableConstraints, initialExposureBias: stableGetInitialExposureBias?.(), initialZoom: stableGetInitialZoom?.(), onSessionConfigSelected: stableOnSessionConfigSelected, }, ], { allowBackgroundAudioPlayback: allowBackgroundAudioPlayback }); if (isCanceled) { controllers.forEach((c) => { c.dispose(); }); return; } stableOnConfigured?.(); setController(controllers[0]); } }; load(); return () => { isCanceled = true; }; }, [ device, mirrorMode, session, allowBackgroundAudioPlayback, stableOutputs, stableOnConfigured, stableGetInitialExposureBias, stableGetInitialZoom, stableOnSessionConfigSelected, stableConstraints, ]); return controller; }