feat: New JS API for useCameraDevice
and useCameraFormat
and much faster getAvailableCameraDevices()
(#1784)
* Update podfile * Update useCameraFormat.ts * Update API * Delete FormatFilter.md * Format CameraViewManager.m ObjC style * Make `getAvailableCameraDevices` synchronous/blocking * Create some docs * fix: Fix HardwareLevel types * fix: Use new device/format API * Use 60 FPS format as an example * Replace `Camera.getAvailableCameraDevices` with new `CameraDevices` API/Module * Fix Lint * KTLint options * Use continuation indent of 8 * Use 2 spaces for indent * Update .editorconfig * Format code * Update .editorconfig * Format more * Update VideoStabilizationMode.kt * fix: Expose `CameraDevicesManager` to ObjC * Update CameraPage.tsx * fix: `requiresMainQueueSetup() -> false` * Always prefer higher resolution * Update CameraDevicesManager.swift * Update CameraPage.tsx * Also filter pixelFormat * fix: Add AVFoundation import
This commit is contained in:
@@ -1,16 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { useRef, useState, useMemo, useCallback } from 'react';
|
||||
import { useRef, useState, useCallback } from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { PinchGestureHandler, PinchGestureHandlerGestureEvent, TapGestureHandler } from 'react-native-gesture-handler';
|
||||
import {
|
||||
CameraDeviceFormat,
|
||||
CameraRuntimeError,
|
||||
PhotoFile,
|
||||
sortFormats,
|
||||
useCameraDevices,
|
||||
useFrameProcessor,
|
||||
VideoFile,
|
||||
} from 'react-native-vision-camera';
|
||||
import { CameraRuntimeError, PhotoFile, useCameraDevice, useCameraFormat, useFrameProcessor, VideoFile } from 'react-native-vision-camera';
|
||||
import { Camera } from 'react-native-vision-camera';
|
||||
import { CONTENT_SPACING, MAX_ZOOM_FACTOR, SAFE_AREA_PADDING } from './Constants';
|
||||
import Reanimated, { Extrapolate, interpolate, useAnimatedGestureHandler, useAnimatedProps, useSharedValue } from 'react-native-reanimated';
|
||||
@@ -53,59 +45,24 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
|
||||
const [enableNightMode, setEnableNightMode] = useState(false);
|
||||
|
||||
// camera format settings
|
||||
const devices = useCameraDevices();
|
||||
const device = devices[cameraPosition];
|
||||
const formats = useMemo<CameraDeviceFormat[]>(() => {
|
||||
if (device?.formats == null) return [];
|
||||
return device.formats.sort(sortFormats);
|
||||
}, [device?.formats]);
|
||||
const device = useCameraDevice(cameraPosition);
|
||||
const format = useCameraFormat(device, {
|
||||
fps: {
|
||||
target: 60,
|
||||
priority: 1,
|
||||
},
|
||||
});
|
||||
|
||||
//#region Memos
|
||||
const [is60Fps, setIs60Fps] = useState(true);
|
||||
const fps = useMemo(() => {
|
||||
if (!is60Fps) return 30;
|
||||
const [targetFps, setTargetFps] = useState(30);
|
||||
const fps = Math.min(format?.maxFps ?? 1, targetFps);
|
||||
|
||||
if (enableNightMode && !device?.supportsLowLightBoost) {
|
||||
// User has enabled Night Mode, but Night Mode is not natively supported, so we simulate it by lowering the frame rate.
|
||||
return 30;
|
||||
}
|
||||
|
||||
const supportsHdrAt60Fps = formats.some((f) => f.supportsVideoHDR && f.maxFps >= 60);
|
||||
if (enableHdr && !supportsHdrAt60Fps) {
|
||||
// User has enabled HDR, but HDR is not supported at 60 FPS.
|
||||
return 30;
|
||||
}
|
||||
|
||||
const supports60Fps = formats.some((f) => f.maxFps >= 60);
|
||||
if (!supports60Fps) {
|
||||
// 60 FPS is not supported by any format.
|
||||
return 30;
|
||||
}
|
||||
// If nothing blocks us from using it, we default to 60 FPS.
|
||||
return 60;
|
||||
}, [device?.supportsLowLightBoost, enableHdr, enableNightMode, formats, is60Fps]);
|
||||
|
||||
const supportsCameraFlipping = useMemo(() => devices.back != null && devices.front != null, [devices.back, devices.front]);
|
||||
const supportsFlash = device?.hasFlash ?? false;
|
||||
const supportsHdr = useMemo(() => formats.some((f) => f.supportsVideoHDR || f.supportsPhotoHDR), [formats]);
|
||||
const supports60Fps = useMemo(() => formats.some((f) => f.maxFps >= 60), [formats]);
|
||||
const canToggleNightMode = enableNightMode
|
||||
? true // it's enabled so you have to be able to turn it off again
|
||||
: (device?.supportsLowLightBoost ?? false) || fps > 30; // either we have native support, or we can lower the FPS
|
||||
const supportsHdr = format?.supportsPhotoHDR;
|
||||
const supports60Fps = (format?.maxFps ?? 0) >= 60;
|
||||
const canToggleNightMode = device?.supportsLowLightBoost ?? false;
|
||||
//#endregion
|
||||
|
||||
const format = useMemo(() => {
|
||||
let result = formats;
|
||||
if (enableHdr) {
|
||||
// We only filter by HDR capable formats if HDR is set to true.
|
||||
// Otherwise we ignore the `supportsVideoHDR` property and accept formats which support HDR `true` or `false`
|
||||
result = result.filter((f) => f.supportsVideoHDR || f.supportsPhotoHDR);
|
||||
}
|
||||
|
||||
// find the first format that includes the given FPS
|
||||
return result.find((f) => f.maxFps >= fps);
|
||||
}, [formats, fps, enableHdr]);
|
||||
|
||||
//#region Animated Zoom
|
||||
// This just maps the zoom factor to a percentage value.
|
||||
// so e.g. for [min, neutr., max] values [1, 2, 128] this would result in [0, 0.0081, 1]
|
||||
@@ -249,22 +206,17 @@ export function CameraPage({ navigation }: Props): React.ReactElement {
|
||||
<StatusBarBlurBackground />
|
||||
|
||||
<View style={styles.rightButtonRow}>
|
||||
{supportsCameraFlipping && (
|
||||
<PressableOpacity style={styles.button} onPress={onFlipCameraPressed} disabledOpacity={0.4}>
|
||||
<IonIcon name="camera-reverse" color="white" size={24} />
|
||||
</PressableOpacity>
|
||||
)}
|
||||
<PressableOpacity style={styles.button} onPress={onFlipCameraPressed} disabledOpacity={0.4}>
|
||||
<IonIcon name="camera-reverse" color="white" size={24} />
|
||||
</PressableOpacity>
|
||||
{supportsFlash && (
|
||||
<PressableOpacity style={styles.button} onPress={onFlashPressed} disabledOpacity={0.4}>
|
||||
<IonIcon name={flash === 'on' ? 'flash' : 'flash-off'} color="white" size={24} />
|
||||
</PressableOpacity>
|
||||
)}
|
||||
{supports60Fps && (
|
||||
<PressableOpacity style={styles.button} onPress={() => setIs60Fps(!is60Fps)}>
|
||||
<Text style={styles.text}>
|
||||
{is60Fps ? '60' : '30'}
|
||||
{'\n'}FPS
|
||||
</Text>
|
||||
<PressableOpacity style={styles.button} onPress={() => setTargetFps((t) => (t === 30 ? 60 : 30))}>
|
||||
<Text style={styles.text}>{`${targetFps} FPS`}</Text>
|
||||
</PressableOpacity>
|
||||
)}
|
||||
{supportsHdr && (
|
||||
|
Reference in New Issue
Block a user