UNPKG

@t-mrtgu/react-native-vision-camera-fix-ios-codescan-corner

Version:

A powerful, high-performance React Native Camera library.

202 lines (186 loc) 6.98 kB
import { CameraRuntimeError } from './CameraError'; // only import typescript types import { CameraModule } from './NativeCameraModule'; import { assertJSIAvailable } from './JSIHelper'; /** * An initialized native instance of a FrameProcessorPlugin. * All memory allocated by this plugin will be deleted once this value goes out of scope. */ const errorMessage = 'Frame Processors are not available, react-native-worklets-core is not installed!'; let hasWorklets = false; let isAsyncContextBusy = { value: false }; let runOnAsyncContext = (_frame, _func) => { throw new CameraRuntimeError('system/frame-processors-unavailable', errorMessage); }; let throwJSError = error => { throw error; }; try { assertJSIAvailable(); // eslint-disable-next-line @typescript-eslint/no-var-requires const { Worklets } = require('react-native-worklets-core'); const throwErrorOnJS = Worklets.createRunInJsFn((message, stack) => { const error = new Error(); error.message = message; error.stack = stack; error.name = 'Frame Processor Error'; // @ts-expect-error this is react-native specific error.jsEngine = 'VisionCamera'; // From react-native: // @ts-ignore the reportFatalError method is an internal method of ErrorUtils not exposed in the type definitions global.ErrorUtils.reportFatalError(error); }); throwJSError = error => { 'worklet'; const safeError = error; const message = safeError != null && 'message' in safeError ? safeError.message : 'Frame Processor threw an error.'; throwErrorOnJS(message, safeError === null || safeError === void 0 ? void 0 : safeError.stack); }; isAsyncContextBusy = Worklets.createSharedValue(false); const asyncContext = Worklets.createContext('VisionCamera.async'); runOnAsyncContext = Worklets.createRunInContextFn((frame, func) => { 'worklet'; try { // Call long-running function func(); } catch (e) { // Re-throw error on JS Thread throwJSError(e); } finally { // Potentially delete Frame if we were the last ref const internal = frame; internal.decrementRefCount(); isAsyncContextBusy.value = false; } }, asyncContext); hasWorklets = true; } catch (e) { // Worklets are not installed, so Frame Processors are disabled. } let proxy = { initFrameProcessorPlugin: () => { throw new CameraRuntimeError('system/frame-processors-unavailable', errorMessage); }, removeFrameProcessor: () => { throw new CameraRuntimeError('system/frame-processors-unavailable', errorMessage); }, setFrameProcessor: () => { throw new CameraRuntimeError('system/frame-processors-unavailable', errorMessage); }, throwJSError: throwJSError }; if (hasWorklets) { // Install native Frame Processor Runtime Manager const result = CameraModule.installFrameProcessorBindings(); if (result !== true) throw new CameraRuntimeError('system/frame-processors-unavailable', 'Failed to install Frame Processor JSI bindings!'); // @ts-expect-error global is untyped, it's a C++ host-object proxy = global.VisionCameraProxy; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (proxy == null) { throw new CameraRuntimeError('system/frame-processors-unavailable', 'Failed to install VisionCameraProxy. Are Frame Processors properly enabled?'); } } export const VisionCameraProxy = { initFrameProcessorPlugin: proxy.initFrameProcessorPlugin, removeFrameProcessor: proxy.removeFrameProcessor, setFrameProcessor: proxy.setFrameProcessor, throwJSError: throwJSError, // TODO: Remove this in the next version // @ts-expect-error getFrameProcessorPlugin: (name, options) => { console.warn('"getFrameProcessorPlugin" has been renamed to "initFrameProcessorPlugin". This function will be removed in the next release.'); return proxy.initFrameProcessorPlugin(name, options); } }; function getLastFrameProcessorCall(frameProcessorFuncId) { 'worklet'; var _global$__frameProces; return ((_global$__frameProces = global.__frameProcessorRunAtTargetFpsMap) === null || _global$__frameProces === void 0 ? void 0 : _global$__frameProces[frameProcessorFuncId]) ?? 0; } function setLastFrameProcessorCall(frameProcessorFuncId, value) { 'worklet'; if (global.__frameProcessorRunAtTargetFpsMap == null) global.__frameProcessorRunAtTargetFpsMap = {}; global.__frameProcessorRunAtTargetFpsMap[frameProcessorFuncId] = value; } /** * Runs the given function at the given target FPS rate. * * For example, if you want to run a heavy face detection algorithm * only once per second, you can use `runAtTargetFps(1, ...)` to * throttle it to 1 FPS. * * @param fps The target FPS rate at which the given function should be executed * @param func The function to execute. * @returns The result of the function if it was executed, or `undefined` otherwise. * @example * * ```ts * const frameProcessor = useFrameProcessor((frame) => { * 'worklet' * console.log('New Frame') * runAtTargetFps(5, () => { * 'worklet' * const faces = detectFaces(frame) * console.log(`Detected a new face: ${faces[0]}`) * }) * }) * ``` */ export function runAtTargetFps(fps, func) { 'worklet'; // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const funcId = func.__workletHash ?? '1'; const targetIntervalMs = 1000 / fps; // <-- 60 FPS => 16,6667ms interval const now = performance.now(); const diffToLastCall = now - getLastFrameProcessorCall(funcId); if (diffToLastCall >= targetIntervalMs) { setLastFrameProcessorCall(funcId, now); // Last Frame Processor call is already so long ago that we want to make a new call return func(); } return undefined; } /** * Runs the given function asynchronously, while keeping a strong reference to the Frame. * * For example, if you want to run a heavy face detection algorithm * while still drawing to the screen at 60 FPS, you can use `runAsync(...)` * to offload the face detection algorithm to a separate thread. * * @param frame The current Frame of the Frame Processor. * @param func The function to execute. * @example * * ```ts * const frameProcessor = useFrameProcessor((frame) => { * 'worklet' * console.log('New Frame') * runAsync(frame, () => { * 'worklet' * const faces = detectFaces(frame) * const face = [faces0] * console.log(`Detected a new face: ${face}`) * }) * }) * ``` */ export function runAsync(frame, func) { '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; internal.incrementRefCount(); isAsyncContextBusy.value = true; // Call in separate background context runOnAsyncContext(frame, func); } //# sourceMappingURL=FrameProcessorPlugins.js.map