feature: Frame Processors (iOS) (#2)
* Clean up Frame Processor * Create FrameProcessorHolder * Create FrameProcessorDelegate in ObjC++ * Move frame processor to FrameProcessorDelegate * Decorate runtime, check for null * Update FrameProcessorDelegate.mm * Cleanup FrameProcessorBindings.mm * Fix RuntimeDecorator.h import * Update FrameProcessorDelegate.mm * "React" -> "React Helper" to avoid confusion * Rename folders again * Fix podspec flattening a lot of headers, causing REA nameclash * Fix header imports to avoid REA naming collision * Lazily initialize jsi::Runtime on DispatchQueue * Install frame processor bindings from Swift * First try to call jsi::Function (frame processor) 👀 * Call viewForReactTag on RCT main thread * Fix bridge accessing * Add more logs * Update CameraViewManager.swift * Add more TODOs * Re-indent .cpp files * Fix RCTTurboModule import podspec * Remove unnecessary include check for swift umbrella header * Merge branch 'main' into frame-processors * Docs: use static width for images (283) * Create validate-cpp.yml * Update a lot of packages to latest * Set SWIFT_VERSION to 5.2 in podspec * Create clean.sh * Delete unused C++ files * podspec: Remove CLANG_CXX_LANGUAGE_STANDARD and OTHER_CFLAGS * Update pod lockfiles * Regenerate lockfiles * Remove IOSLogger * Use NSLog * Create FrameProcessorManager (inherits from REA RuntimeManager) * Create reanimated::RuntimeManager shared_ptr * Re-integrate pods * Add react-native-reanimated >=2 peerDependency * Add metro-config * blacklist -> exclusionList * Try to call worklet * Fix jsi::Value* initializer * Call ShareableValue::adapt (makeShareable) with React/JS Runtime * Add null-checks * Lift runtime manager creation out of delegate, into bindings * Remove debug statement * Make RuntimeManager unique_ptr * Set _FRAME_PROCESSOR * Extract convertJSIFunctionToFrameProcessorCallback * Print frame * Merge branch 'main' into frame-processors * Reformat Swift code * Install reanimated from npm again * Re-integrate Pods * Dependabot: Also scan example/ and docs/ * Update validate-cpp.yml * Create FrameProcessorUtils * Create Frame.h * Abstract HostObject creation away * Fix types * Fix frame processor call * Add todo * Update lockfiles * Add C++ contributing instructions * Update CONTRIBUTING.md * Add android/src/main/cpp to cpplint * Update cpplint.sh * Fix a few cpplint errors * Fix globals * Fix a few more cpplint errors * Update App.tsx * Update AndroidLogger.cpp * Format * Fix cpplint script (check-cpp) * Try to simplify frame processor * y * Update FrameProcessorUtils.mm * Update FrameProcessorBindings.mm * Update CameraView.swift * Update CameraViewManager.m * Restructure everything * fix * Fix `@objc` export (make public) * Refactor installFrameProcessorBindings into FrameProcessorRuntimeManager * Add swift RCTBridge.runOnJS helper * Fix run(onJS) * Add pragma once * Add `&self` to lambda * Update FrameProcessorRuntimeManager.mm * reorder imports * Fix imports * forward declare * Rename extension * Destroy buffer after execution * Add FrameProcessorPluginRegistry base * Merge branch 'main' into frame-processors * Add frameProcessor to types * Update Camera.tsx * Fix rebase merge * Remove movieOutput * Use `useFrameProcessor` * Fix bad merge * Add additional ESLint rules * Update lockfiles * Update CameraViewManager.m * Add support for V8 runtime * Add frame processor plugins API * Print plugin invoke * Fix React Utils in podspec * Fix runOnJS swift name * Remove invalid redecl of `captureSession` * Use REA 2.1.0 which includes all my big PRs 🎉 * Update validate-cpp.yml * Update Podfile.lock * Remove Flipper * Fix dereferencing * Capture `self` by value. Fucking hell, what a dumb mistake. * Override a few HostObject functions * Expose isReady, width, height, bytesPerRow and planesCount * use hook again * Expose property names * FrameProcessor -> Frame * Update CameraView+RecordVideo.swift * Add Swift support for Frame Processors Plugins * Add macros for plugin installation * Add ObjC frame processor plugin * Correctly install frame processor plugins * Don't require custom name for macro * Check if plugin already exists * Implement QR Code Frame Processor Plugin in Swift * Adjust ObjC style frame processor macro * optimize * Add `frameProcessorFrameDropRate` * Fix types * Only log once * Log if it executes slowly * Implement `frameProcessorFps` * Implement manual encoded video recordings * Use recommended video settings * Add fileType types * Ignore if input is not ready for media data * Add completion handler * Add audio buffer sampling * Init only for video frame * use AVAssetWriterInputPixelBufferAdaptor * Remove AVAssetWriterInputPixelBufferAdaptor * Rotate VideoWriter * Always assume portrait orientation * Update RecordingSession.swift * Use a separate Queue for Audio * Format Swift * Update CameraView+RecordVideo.swift * Use `videoQueue` instead of `cameraQueue` * Move example plugins to example app * Fix hardcoded name in plugin macro * QRFrame... -> QRCodeFrame... * Update FrameProcessorPlugin.h * Add example frame processors to JS base * Update QRCodeFrameProcessorPluginSwift.m * Add docs to create FP Plugins * Update FRAME_PROCESSORS_CREATE.mdx * Update FRAME_PROCESSORS_CREATE.mdx * Use `AVAssetWriterInputPixelBufferAdaptor` for efficient pixel buffer recycling * Add customizable `pixelFormat` * Use native format if available * Update project.pbxproj * Set video width and height as source-pixel-buffer attributes * Catch * Update App.tsx * Don't explicitly set video dimensions, let CVPixelBufferPool handle it * Add a few logs * Cleanup * Update CameraView+RecordVideo.swift * Eagerly initialize asset writer to fix stutter at first frame * Use `cameraQueue` DispatchQueue to not block CaptureDataOutputDelegate * Fix duration calculation * cleanup * Cleanup * Swiftformat * Return available video codecs * Only show frame drop notification for video output * Remove photo and video codec functionality It was too much complexity and probably never used anyways. * Revert all android related changes for now * Cleanup * Remove unused header * Update AVAssetWriter.Status+descriptor.swift * Only call Frame Processor for Video Frames * Fix `if` * Add support for Frame Processor plugin parameters/arguments * Fix arg support * Move to JSIUtils.mm * Update JSIUtils.h * Update FRAME_PROCESSORS_CREATE.mdx * Update FRAME_PROCESSORS_CREATE.mdx * Upgrade packages for docs/ * fix docs * Rename * highlight lines * docs * community plugins * Update FRAME_PROCESSOR_CREATE_FINAL.mdx * Update FRAME_PROCESSOR_PLUGIN_LIST.mdx * Update FRAME_PROCESSOR_PLUGIN_LIST.mdx * Update dependencies (1/2) * Update dependencies (2/2) * Update Gemfile.lock * add FP docs * Update README.md * Make `lastFrameProcessor` private * add `frameProcessor` docs * fix docs * adjust docs * Update DEVICES.mdx * fix * s * Add logs demo * add metro restart note * Update FRAME_PROCESSOR_CREATE_PLUGIN_IOS.mdx * Mirror video device * Update AVCaptureVideoDataOutput+mirror.swift * Create .swift-version * Enable whole module optimization * Fix recording mirrored video * Swift format * Clean dictionary on `markInvalid` * Fix cleanup * Add docs for disabling frame processors * Update project.pbxproj * Revert "Update project.pbxproj" This reverts commit e67861e51b88b4888a6940e2d20388f3044211d0. * Log frame drop reason * Format * add more samples * Add clang-format * also check .mm * Revert "also check .mm" This reverts commit 8b9d5e2c29866b05909530d104f6633d6c49eadd. * Revert "Add clang-format" This reverts commit 7643ac808e0fc34567ea1f814e73d84955381636. * Use `kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange` as default * Read matching video attributes from videoSettings * Add TODO * Swiftformat * Conditionally disable frame processors * Assert if trying to use frame processors when disabled * Add frame-processors demo gif * Allow disabling frame processors via `VISION_CAMERA_DISABLE_FRAME_PROCESSORS` * Update FrameProcessorRuntimeManager.mm * Update FRAME_PROCESSORS.mdx * Update project.pbxproj * Update FRAME_PROCESSORS_CREATE_OVERVIEW.mdx
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { requireNativeComponent, NativeModules, NativeSyntheticEvent, findNodeHandle, NativeMethods, Platform } from 'react-native';
|
||||
import type { CameraPhotoCodec, CameraVideoCodec } from './CameraCodec';
|
||||
import type { CameraDevice } from './CameraDevice';
|
||||
import type { ErrorWithCause } from './CameraError';
|
||||
import { CameraCaptureError, CameraRuntimeError, tryParseNativeCameraError, isErrorWithCause } from './CameraError';
|
||||
import type { CameraProps } from './CameraProps';
|
||||
import type { Frame } from './Frame';
|
||||
import type { PhotoFile, TakePhotoOptions } from './PhotoFile';
|
||||
import type { Point } from './Point';
|
||||
import type { TakeSnapshotOptions } from './Snapshot';
|
||||
@@ -19,7 +19,7 @@ interface OnErrorEvent {
|
||||
message: string;
|
||||
cause?: ErrorWithCause;
|
||||
}
|
||||
type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onError'> & {
|
||||
type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onError' | 'frameProcessor'> & {
|
||||
cameraId: string;
|
||||
onInitialized?: (event: NativeSyntheticEvent<void>) => void;
|
||||
onError?: (event: NativeSyntheticEvent<OnErrorEvent>) => void;
|
||||
@@ -78,6 +78,7 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
||||
* @internal
|
||||
*/
|
||||
displayName = Camera.displayName;
|
||||
private lastFrameProcessor: ((frame: Frame) => void) | undefined;
|
||||
|
||||
private readonly ref: React.RefObject<RefType>;
|
||||
|
||||
@@ -90,6 +91,7 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
||||
this.onInitialized = this.onInitialized.bind(this);
|
||||
this.onError = this.onError.bind(this);
|
||||
this.ref = React.createRef<RefType>();
|
||||
this.lastFrameProcessor = undefined;
|
||||
}
|
||||
|
||||
private get handle(): number | null {
|
||||
@@ -232,39 +234,6 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
||||
throw tryParseNativeCameraError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of video codecs the current camera supports. Returned values are ordered by efficiency (descending).
|
||||
*
|
||||
* This function can only be called after the camera has been initialized,
|
||||
* so only use this after the {@linkcode onInitialized | onInitialized()} event has fired.
|
||||
*
|
||||
* @platform iOS
|
||||
* @throws {@linkcode CameraRuntimeError} When any kind of error occured while getting available video codecs. Use the {@linkcode CameraRuntimeError.code | code} property to get the actual error
|
||||
*/
|
||||
public async getAvailableVideoCodecs(): Promise<CameraVideoCodec[]> {
|
||||
try {
|
||||
return await CameraModule.getAvailableVideoCodecs(this.handle);
|
||||
} catch (e) {
|
||||
throw tryParseNativeCameraError(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get a list of photo codecs the current camera supports. Returned values are ordered by efficiency (descending).
|
||||
*
|
||||
* This function can only be called after the camera has been initialized,
|
||||
* so only use this after the {@linkcode onInitialized | onInitialized()} event has fired.
|
||||
*
|
||||
* @platform iOS
|
||||
* @throws {@linkcode CameraRuntimeError} When any kind of error occured while getting available photo codecs. Use the {@linkcode CameraRuntimeError.code | code} property to get the actual error
|
||||
*/
|
||||
public async getAvailablePhotoCodecs(): Promise<CameraPhotoCodec[]> {
|
||||
try {
|
||||
return await CameraModule.getAvailablePhotoCodecs(this.handle);
|
||||
} catch (e) {
|
||||
throw tryParseNativeCameraError(e);
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Static Functions (NativeModule)
|
||||
@@ -382,11 +351,53 @@ export class Camera extends React.PureComponent<CameraProps, CameraState> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public render(): React.ReactNode {
|
||||
if (this.state.cameraId == null) throw new Error('CameraId was null! Did you pass a valid `device`?');
|
||||
private assertFrameProcessorsEnabled(): void {
|
||||
// @ts-expect-error JSI functions aren't typed
|
||||
if (global.setFrameProcessor == null || global.unsetFrameProcessor == null)
|
||||
throw new Error('Frame Processors are not enabled. Make sure you install react-native-reanimated 2.1.0 or above!');
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
componentWillUnmount(): void {
|
||||
this.assertFrameProcessorsEnabled();
|
||||
// @ts-expect-error JSI functions aren't typed
|
||||
global.unsetFrameProcessor(this.handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
componentDidUpdate(): void {
|
||||
if (this.props.frameProcessor !== this.lastFrameProcessor) {
|
||||
this.assertFrameProcessorsEnabled();
|
||||
// frameProcessor argument changed. Update native to reflect the change.
|
||||
if (this.props.frameProcessor != null) {
|
||||
// 1. Spawn threaded JSI Runtime (if not already done)
|
||||
// 2. Add video data output to Camera stream (if not already done)
|
||||
// 3. Workletize the frameProcessor and prepare it for being called with frames
|
||||
// @ts-expect-error JSI functions aren't typed
|
||||
global.setFrameProcessor(this.handle, this.props.frameProcessor);
|
||||
} else {
|
||||
// 1. Destroy the threaded runtime
|
||||
// 2. remove the frame processor
|
||||
// 3. Remove the video data output
|
||||
// @ts-expect-error JSI functions aren't typed
|
||||
global.unsetFrameProcessor(this.handle);
|
||||
}
|
||||
this.lastFrameProcessor = this.props.frameProcessor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public render(): React.ReactNode {
|
||||
if (this.state.cameraId == null) throw new Error('CameraID is null! Did you pass a valid `device`?');
|
||||
// We remove the big `device` object from the props because we only need to pass `cameraId` to native.
|
||||
const { device: _, ...props } = this.props;
|
||||
const { device: _, frameProcessor: __, ...props } = this.props;
|
||||
|
||||
return (
|
||||
<NativeCameraView
|
||||
{...props}
|
||||
|
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Available Video Codec types used for recording a video.
|
||||
*
|
||||
* * `"hevc"`: The HEVC video codec. _(iOS 11.0+)_
|
||||
* * `"h264"`: The H.264 (`avc1`) video codec. _(iOS 11.0+)_
|
||||
* * `"jpeg"`: The JPEG (`jpeg`) video codec. _(iOS 11.0+)_
|
||||
* * `"pro-res-4444"`: The Apple ProRes 4444 (`ap4h`) video codec. _(iOS 11.0+)_
|
||||
* * `"pro-res-422"`: The Apple ProRes 422 (`apcn`) video codec. _(iOS 11.0+)_
|
||||
* * `"pro-res-422-hq"`: The Apple ProRes 422 HQ (`apch`) video codec. _(iOS 13.0+)_
|
||||
* * `"pro-res-422-lt"`: The Apple ProRes 422 LT (`apcs`) video codec. _(iOS 13.0+)_
|
||||
* * `"pro-res-422-proxy"`: The Apple ProRes 422 Proxy (`apco`) video codec. _(iOS 13.0+)_
|
||||
* * `"hevc-alpha"`: The HEVC (`muxa`) video codec that supports an alpha channel. This constant is used to select the appropriate encoder, but is NOT used on the encoded content, which is backwards compatible and hence uses `"hvc1"` as its codec type. _(iOS 13.0+)_
|
||||
*/
|
||||
export type CameraVideoCodec =
|
||||
| 'h264'
|
||||
| 'hevc'
|
||||
| 'hevc-alpha'
|
||||
| 'jpeg'
|
||||
| 'pro-res-4444'
|
||||
| 'pro-res-422'
|
||||
| 'pro-res-422-hq'
|
||||
| 'pro-res-422-lt'
|
||||
| 'pro-res-422-proxy';
|
||||
|
||||
// TODO: Support RAW photo codec
|
||||
/**
|
||||
* Available Photo Codec types used for taking a photo.
|
||||
*
|
||||
* * `"hevc"`: The HEVC video codec. _(iOS 11.0+)_
|
||||
* * `"jpeg"`: The JPEG (`jpeg`) video codec. _(iOS 11.0+)_
|
||||
* * `"hevc-alpha"`: The HEVC (`muxa`) video codec that supports an alpha channel. This constant is used to select the appropriate encoder, but is NOT used on the encoded content, which is backwards compatible and hence uses `"hvc1"` as its codec type. _(iOS 13.0+)_
|
||||
*/
|
||||
export type CameraPhotoCodec = 'hevc' | 'jpeg' | 'hevc-alpha';
|
@@ -29,6 +29,7 @@ export type CaptureError =
|
||||
| 'capture/no-recording-in-progress'
|
||||
| 'capture/file-io-error'
|
||||
| 'capture/create-temp-file-error'
|
||||
| 'capture/create-recorder-error'
|
||||
| 'capture/invalid-photo-codec'
|
||||
| 'capture/not-bound-error'
|
||||
| 'capture/capture-type-not-supported'
|
||||
|
@@ -2,6 +2,7 @@ import type { ViewProps } from 'react-native';
|
||||
import type { CameraDevice, CameraDeviceFormat, ColorSpace } from './CameraDevice';
|
||||
import type { CameraRuntimeError } from './CameraError';
|
||||
import type { CameraPreset } from './CameraPreset';
|
||||
import type { Frame } from './Frame';
|
||||
|
||||
export interface CameraProps extends ViewProps {
|
||||
/**
|
||||
@@ -126,5 +127,35 @@ export interface CameraProps extends ViewProps {
|
||||
* Called when the camera was successfully initialized.
|
||||
*/
|
||||
onInitialized?: () => void;
|
||||
/**
|
||||
* A worklet which will be called for every frame the Camera "sees". Throttle the Frame Processor's frame rate with {@linkcode frameProcessorFps}.
|
||||
*
|
||||
* > See [the Frame Processors documentation](https://cuvent.github.io/react-native-vision-camera/docs/guides/frame-processors) for more information
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const frameProcessor = useFrameProcessor((frame) => {
|
||||
* 'worklet'
|
||||
* const qrCodes = scanQRCodes(frame)
|
||||
* console.log(`Detected QR Codes: ${qrCodes}`)
|
||||
* }, [])
|
||||
*
|
||||
* return <Camera {...cameraProps} frameProcessor={frameProcessor} />
|
||||
* ```
|
||||
*/
|
||||
frameProcessor?: (frame: Frame) => void;
|
||||
/**
|
||||
* Specifies the maximum frame rate the frame processor can use, independent of the Camera's frame rate (`fps` property).
|
||||
*
|
||||
* * A value of `1` (default) indicates that the frame processor gets executed once per second, perfect for code scanning.
|
||||
* * A value of `10` indicates that the frame processor gets executed 10 times per second, perfect for more realtime use-cases.
|
||||
* * A value of `25` indicates that the frame processor gets executed 30 times per second, perfect for high-speed realtime use-cases.
|
||||
*
|
||||
* If you're using higher values, always check your Xcode/Android Studio Logs to make sure your frame processors are executing fast enough
|
||||
* without blocking the video recording queue.
|
||||
*
|
||||
* @default 1
|
||||
*/
|
||||
frameProcessorFps?: number;
|
||||
//#endregion
|
||||
}
|
||||
|
42
src/Frame.ts
Normal file
42
src/Frame.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* A single frame, as seen by the camera.
|
||||
*/
|
||||
export interface Frame {
|
||||
/**
|
||||
* The raw pixel buffer.
|
||||
*/
|
||||
buffer: unknown[];
|
||||
/**
|
||||
* Whether the underlying buffer is still valid or not. The buffer will be released after the frame processor returns.
|
||||
*/
|
||||
isValid: boolean;
|
||||
/**
|
||||
* Whether the underlying buffer is marked as "ready" or not.
|
||||
*/
|
||||
isReady: boolean;
|
||||
/**
|
||||
* Returns the width of the frame, in pixels.
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* Returns the height of the frame, in pixels.
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* Returns the amount of bytes per row.
|
||||
*/
|
||||
bytesPerRow: number;
|
||||
/**
|
||||
* Returns the number of planes this frame contains.
|
||||
*/
|
||||
planesCount: number;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the frame.
|
||||
* @example
|
||||
* ```ts
|
||||
* console.log(frame.toString()) // -> "3840 x 2160 Frame"
|
||||
* ```
|
||||
*/
|
||||
toString(): string;
|
||||
}
|
@@ -1,14 +1,6 @@
|
||||
import type { CameraPhotoCodec } from './CameraCodec';
|
||||
import type { TemporaryFile } from './TemporaryFile';
|
||||
|
||||
export interface TakePhotoOptions {
|
||||
/**
|
||||
* Specify the photo codec to use. To get a list of available photo codecs use the {@linkcode Camera.getAvailablePhotoCodecs | getAvailablePhotoCodecs()} function.
|
||||
*
|
||||
* @platform iOS
|
||||
* @default undefined
|
||||
*/
|
||||
photoCodec?: CameraPhotoCodec;
|
||||
/**
|
||||
* Indicates how photo quality should be prioritized against speed.
|
||||
*
|
||||
|
@@ -1,39 +1,18 @@
|
||||
// /**
|
||||
// * not yet implemented.
|
||||
// */
|
||||
// declare interface RecordVideoOptions<TCodec extends CameraVideoCodec> {
|
||||
// /**
|
||||
// * Specify the video codec to use. To get a list of available video codecs use the `getAvailableVideoCodecs()` function.
|
||||
// *
|
||||
// * @default undefined
|
||||
// */
|
||||
// videoCodec?: TCodec;
|
||||
// /**
|
||||
// * Specify the average video bitrate in bits per second. (H.264 only)
|
||||
// */
|
||||
// bitrate?: TCodec extends "h264" ? number : never;
|
||||
// /**
|
||||
// * Specify the video quality. (`0.0` - `1.0`, where `1.0` means 100% quality. JPEG, HEIC and Apple ProRAW only. With HEIC and Apple ProRAW, 1.0 indicates lossless compression)
|
||||
// */
|
||||
// quality?: TCodec extends "jpeg" | "hevc" | "hevc-alpha" ? number : never;
|
||||
// /**
|
||||
// * Maximum number of frames per interval, `1` specifies to only use key frames. (H.264 only)
|
||||
// */
|
||||
// maxKeyFrameInterval?: TCodec extends "h264" ? number : never;
|
||||
// /**
|
||||
// * Maximum duration of a key frame interval in seconds, where as `0.0` means no limit. (H.264 only)
|
||||
// */
|
||||
// maxKeyFrameIntervalDuration?: TCodec extends "h264" ? number : never;
|
||||
// }
|
||||
|
||||
import type { CameraCaptureError } from './CameraError';
|
||||
import type { TemporaryFile } from './TemporaryFile';
|
||||
|
||||
export type VideoFileType = 'mov' | 'avci' | 'm4v' | 'mp4';
|
||||
|
||||
export interface RecordVideoOptions {
|
||||
/**
|
||||
* Set the video flash mode. Natively, this just enables the torch while recording.
|
||||
*/
|
||||
flash?: 'on' | 'off' | 'auto';
|
||||
/**
|
||||
* Sets the file type to use for the Video Recording.
|
||||
* @default "mov"
|
||||
*/
|
||||
fileType?: VideoFileType;
|
||||
/**
|
||||
* Called when there was an unexpected runtime error while recording the video.
|
||||
*/
|
||||
@@ -58,12 +37,4 @@ export interface VideoFile extends TemporaryFile {
|
||||
* @platform iOS
|
||||
*/
|
||||
duration?: number;
|
||||
/**
|
||||
* Represents the file size of the recorded Video File, in bytes.
|
||||
*
|
||||
* This is `undefined` on Android, see [issue #77](https://github.com/cuvent/react-native-vision-camera/issues/77)
|
||||
*
|
||||
* @platform iOS
|
||||
*/
|
||||
size?: number;
|
||||
}
|
||||
|
19
src/globals.d.ts
vendored
Normal file
19
src/globals.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/* eslint-disable no-var */
|
||||
|
||||
/**
|
||||
* `true` if currently running in a Frame Processor runtime
|
||||
*/
|
||||
declare var _FRAME_PROCESSOR: true | undefined;
|
||||
/**
|
||||
* `true` if currently running in a reanimated UI runtime
|
||||
*/
|
||||
declare var _UI: true | undefined;
|
||||
/**
|
||||
* `true` if currently running in a Worklet runtime (frame processor, multithreading, reanimated)
|
||||
*/
|
||||
declare var _WORKLET: true | undefined;
|
||||
|
||||
/**
|
||||
* A native logging function (outputs to Xcode console/Android Logcat)
|
||||
*/
|
||||
declare var _log: (message: string) => void | undefined;
|
@@ -22,6 +22,7 @@ export function useCameraFormat(device?: CameraDevice, cameraViewSize?: Size): C
|
||||
const bestFormat = sorted[0];
|
||||
if (bestFormat == null) return [];
|
||||
const bestFormatResolution = bestFormat.photoHeight * bestFormat.photoWidth;
|
||||
|
||||
return sorted.filter((f) => {
|
||||
// difference in resolution in percent (e.g. 100x100 is 0.5 of 200x200)
|
||||
const resolutionDiff = (bestFormatResolution - f.photoHeight * f.photoWidth) / bestFormatResolution;
|
||||
|
27
src/hooks/useFrameProcessor.ts
Normal file
27
src/hooks/useFrameProcessor.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { DependencyList, useCallback } from 'react';
|
||||
import type { Frame } from 'src/Frame';
|
||||
|
||||
/**
|
||||
* Returns a memoized Frame Processor function wich you can pass to the `<Camera>`. (See ["Frame Processors"](https://cuvent.github.io/react-native-vision-camera/docs/guides/frame-processors))
|
||||
*
|
||||
* > If you are using the [react-hooks ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks), make sure to add `useFrameProcessor` to `additionalHooks` inside your ESLint config. (See ["advanced configuration"](https://www.npmjs.com/package/eslint-plugin-react-hooks#advanced-configuration))
|
||||
*
|
||||
* @param frameProcessor The Frame Processor
|
||||
* @param dependencies The React dependencies which will be copied into the VisionCamera JS-Runtime.
|
||||
* @returns The memoized Frame Processor.
|
||||
* @example
|
||||
* ```ts
|
||||
* const frameProcessor = useFrameProcessor((frame) => {
|
||||
* 'worklet'
|
||||
* const qrCodes = scanQRCodes(frame)
|
||||
* _log(`QR Codes: ${qrCodes}`)
|
||||
* }, [])
|
||||
* ```
|
||||
*/
|
||||
export function useFrameProcessor(frameProcessor: (frame: Frame) => void, dependencies: DependencyList): (frame: Frame) => void {
|
||||
return useCallback((frame: Frame) => {
|
||||
'worklet';
|
||||
return frameProcessor(frame);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, dependencies);
|
||||
}
|
@@ -1,15 +1,19 @@
|
||||
export * from './Camera';
|
||||
export * from './CameraCodec';
|
||||
export * from './CameraDevice';
|
||||
export * from './CameraError';
|
||||
export * from './CameraPosition';
|
||||
export * from './CameraPreset';
|
||||
export * from './CameraProps';
|
||||
export * from './Frame';
|
||||
export * from './CameraProps';
|
||||
export * from './PhotoFile';
|
||||
export * from './Point';
|
||||
export * from './Snapshot';
|
||||
export * from './TemporaryFile';
|
||||
export * from './VideoFile';
|
||||
|
||||
export * from './hooks/useCameraDevices';
|
||||
export * from './hooks/useCameraFormat';
|
||||
export * from './hooks/useFrameProcessor';
|
||||
|
||||
export * from './utils/FormatFilter';
|
||||
|
Reference in New Issue
Block a user