fix: Fix runAtTargetFps
for multiple invocations per FP
This commit is contained in:
parent
e1973b9b8d
commit
af4e366312
@ -6,7 +6,7 @@ import type { ErrorWithCause } from './CameraError';
|
||||
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
|
||||
import type { CameraProps } from './CameraProps';
|
||||
import type { Frame } from './Frame';
|
||||
import { assertFrameProcessorsAvailable } from './JSIHelper';
|
||||
import { assertFrameProcessorsAvailable, assertJSIAvailable } from './JSIHelper';
|
||||
import { CameraModule } from './NativeCameraModule';
|
||||
import type { PhotoFile, TakePhotoOptions } from './PhotoFile';
|
||||
import type { Point } from './Point';
|
||||
@ -310,6 +310,7 @@ export class Camera extends React.PureComponent<CameraProps> {
|
||||
* Install JSI Bindings for Frame Processors
|
||||
*/
|
||||
public static installFrameProcessorBindings(): void {
|
||||
assertJSIAvailable();
|
||||
const result = CameraModule.installFrameProcessorBindings() as unknown;
|
||||
if (result !== true) throw new Error('Failed to install Frame Processor JSI bindings!');
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
import type { Frame, FrameInternal } from './Frame';
|
||||
import { Camera } from './Camera';
|
||||
import { Worklets } from 'react-native-worklets/src';
|
||||
import { assertJSIAvailable } from './JSIHelper';
|
||||
|
||||
assertJSIAvailable();
|
||||
|
||||
// Install VisionCamera Frame Processor JSI Bindings and Plugins
|
||||
Camera.installFrameProcessorBindings();
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var __frameProcessorRunAtTargetFpsMap: Record<string, number | undefined> | undefined;
|
||||
}
|
||||
|
||||
type BasicParameterType = string | number | boolean | undefined;
|
||||
type ParameterType = BasicParameterType | BasicParameterType[] | Record<string, BasicParameterType | undefined>;
|
||||
type FrameProcessor = (frame: Frame, parameters?: Record<string, ParameterType | undefined>) => unknown;
|
||||
@ -19,7 +21,15 @@ type TFrameProcessorPlugins = Record<string, FrameProcessor>;
|
||||
// @ts-expect-error The global JSI Proxy object is not typed.
|
||||
export const FrameProcessorPlugins = global.FrameProcessorPlugins as TFrameProcessorPlugins;
|
||||
|
||||
const lastFrameProcessorCall = Worklets.createSharedValue(performance.now());
|
||||
function getLastFrameProcessorCall(frameProcessorFuncId: string): number {
|
||||
'worklet';
|
||||
return global.__frameProcessorRunAtTargetFpsMap?.[frameProcessorFuncId] ?? 0;
|
||||
}
|
||||
function setLastFrameProcessorCall(frameProcessorFuncId: string, value: number): void {
|
||||
'worklet';
|
||||
if (global.__frameProcessorRunAtTargetFpsMap == null) global.__frameProcessorRunAtTargetFpsMap = {};
|
||||
global.__frameProcessorRunAtTargetFpsMap[frameProcessorFuncId] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given function at the given target FPS rate.
|
||||
@ -37,22 +47,25 @@ const lastFrameProcessorCall = Worklets.createSharedValue(performance.now());
|
||||
* const frameProcessor = useFrameProcessor((frame) => {
|
||||
* 'worklet'
|
||||
* console.log('New Frame')
|
||||
* const face = runAtTargetFps(5, () => {
|
||||
* runAtTargetFps(5, () => {
|
||||
* 'worklet'
|
||||
* const faces = detectFaces(frame)
|
||||
* return faces[0]
|
||||
* console.log(`Detected a new face: ${faces[0]}`)
|
||||
* })
|
||||
* if (face != null) console.log(`Detected a new face: ${face}`)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function runAtTargetFps<T>(fps: number, func: () => T): T | undefined {
|
||||
'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 - lastFrameProcessorCall.value;
|
||||
const diffToLastCall = now - getLastFrameProcessorCall(funcId);
|
||||
if (diffToLastCall >= targetIntervalMs) {
|
||||
lastFrameProcessorCall.value = now;
|
||||
setLastFrameProcessorCall(funcId, now);
|
||||
// Last Frame Processor call is already so long ago that we want to make a new call
|
||||
return func();
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ export function assertJSIAvailable(): void {
|
||||
}
|
||||
|
||||
export function assertFrameProcessorsAvailable(): void {
|
||||
assertJSIAvailable();
|
||||
|
||||
// @ts-expect-error JSI functions aren't typed
|
||||
if (global.setFrameProcessor == null || global.unsetFrameProcessor == null) {
|
||||
throw new CameraRuntimeError(
|
||||
|
Loading…
Reference in New Issue
Block a user