UNPKG

react-native-vision-camera

Version:

A powerful, high-performance React Native Camera library.

101 lines (91 loc) 3.38 kB
import { WorkletsProxy } from '../dependencies/WorkletsProxy' import type { Frame, FrameInternal } from '../types/Frame' import { FrameProcessorsUnavailableError } from './FrameProcessorsUnavailableError' import { throwErrorOnJS } from './throwErrorOnJS' /** * A synchronized Shared Value to indicate whether the async context is currently executing */ let isAsyncContextBusy: { value: boolean } /** * Runs the given function on the async context, and sets {@linkcode isAsyncContextBusy} to false after it finished executing. */ let runOnAsyncContext: (frame: Frame, func: () => void) => void try { const Worklets = WorkletsProxy.Worklets isAsyncContextBusy = Worklets.createSharedValue(false) const asyncContext = Worklets.createContext('VisionCamera.async') runOnAsyncContext = asyncContext.createRunAsync((frame: Frame, func: () => void) => { 'worklet' try { // Call long-running function func() } catch (e) { // Re-throw error on JS Thread throwErrorOnJS(e) } finally { // Potentially delete Frame if we were the last ref const internal = frame as FrameInternal internal.decrementRefCount() // free up async context again, new calls can be made isAsyncContextBusy.value = false } }) } catch (e) { // react-native-worklets-core is not installed! // Just use dummy implementations that will throw when the user tries to use Frame Processors. isAsyncContextBusy = { value: false } runOnAsyncContext = () => { throw new FrameProcessorsUnavailableError(e) } } /** * Runs the given {@linkcode func} asynchronously on a separate thread, * allowing the Frame Processor to continue executing without dropping a Frame. * * Only one {@linkcode runAsync} call will execute at the same time, * so {@linkcode runAsync} is **not parallel**, **but asynchronous**. * * * For example, if your Camera is running at 60 FPS (16ms per frame), and a * heavy ML face detection Frame Processor Plugin takes 500ms to execute, * you have two options: * - Run the plugin normally (synchronously in `useFrameProcessor`) * but drop a lot of Frames, as we can only run at 2 FPS (500ms per frame) * - Call the plugin inside {@linkcode runAsync} to allow the Camera to still * run at 60 FPS, but offload the heavy ML face detection plugin to the * asynchronous context, where it will run at 2 FPS. * * @note {@linkcode runAsync} cannot be used to draw to a Frame in a Skia Frame Processor. * @param frame The current Frame of the Frame Processor. * @param func The function to execute. * @worklet * @example * * ```ts * const frameProcessor = useFrameProcessor((frame) => { * 'worklet' * console.log('New Frame arrived!') * * runAsync(frame, () => { * 'worklet' * const faces = detectFaces(frame) * const face = faces[0] * console.log(`Detected a new face: ${face}`) * }) * }) * ``` */ export function runAsync(frame: Frame, func: () => void): void { 'worklet' if (isAsyncContextBusy.value) { // async context is currently busy, we cannot schedule new work in time. // drop this frame/runAsync call. return } // Increment ref count by one const internal = frame as FrameInternal internal.incrementRefCount() isAsyncContextBusy.value = true // Call in separate background context runOnAsyncContext(frame, func) }