Add selectable FPS
This commit is contained in:
parent
eaebda7954
commit
b7274eb2e9
@ -13,7 +13,7 @@ import type { CameraDevice, CameraDeviceFormat, CameraProps, CameraRuntimeError,
|
||||
import { Camera } from 'react-native-vision-camera';
|
||||
import { useIsScreenFocused } from './hooks/useIsScreenFocused';
|
||||
import { compareFormats, frameRateIncluded, formatWithClosestMatchingFps, compareDevices } from './FormatFilter';
|
||||
import { CONTENT_SPACING, HIGH_FPS, MAX_ZOOM_FACTOR, SAFE_AREA_PADDING } from './Constants';
|
||||
import { CONTENT_SPACING, MAX_ZOOM_FACTOR, SAFE_AREA_PADDING } from './Constants';
|
||||
import Reanimated, { Extrapolate, interpolate, useAnimatedGestureHandler, useAnimatedProps, useSharedValue } from 'react-native-reanimated';
|
||||
import { useEffect } from 'react';
|
||||
import { useIsForeground } from './hooks/useIsForeground';
|
||||
@ -22,6 +22,8 @@ import { CaptureButton } from './views/CaptureButton';
|
||||
import { PressableOpacity } from './views/PressableOpacity';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import IonIcon from 'react-native-vector-icons/Ionicons';
|
||||
import { useSelector } from 'pipestate';
|
||||
import { FpsSelector } from './state/selectors';
|
||||
|
||||
const ReanimatedCamera = Reanimated.createAnimatedComponent(Camera);
|
||||
Reanimated.addWhitelistedNativeProps({
|
||||
@ -53,26 +55,28 @@ export const App: NavigationFunctionComponent = ({ componentId }) => {
|
||||
const formats = useMemo<CameraDeviceFormat[]>(() => device?.formats.sort(compareFormats) ?? [], [device?.formats]);
|
||||
|
||||
//#region Memos
|
||||
const [targetFps] = useSelector(FpsSelector);
|
||||
console.log(`Target FPS: ${targetFps}`);
|
||||
const fps = useMemo(() => {
|
||||
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 supportsHdrAtHighFps = formats.some((f) => f.supportsVideoHDR && f.frameRateRanges.some((r) => frameRateIncluded(r, HIGH_FPS)));
|
||||
const supportsHdrAtHighFps = formats.some((f) => f.supportsVideoHDR && f.frameRateRanges.some((r) => frameRateIncluded(r, targetFps)));
|
||||
if (enableHdr && !supportsHdrAtHighFps) {
|
||||
// User has enabled HDR, but HDR is not supported at HIGH_FPS.
|
||||
// User has enabled HDR, but HDR is not supported at targetFps.
|
||||
return 30;
|
||||
}
|
||||
|
||||
const supportsHighFps = formats.some((f) => f.frameRateRanges.some((r) => frameRateIncluded(r, HIGH_FPS)));
|
||||
const supportsHighFps = formats.some((f) => f.frameRateRanges.some((r) => frameRateIncluded(r, targetFps)));
|
||||
if (!supportsHighFps) {
|
||||
// HIGH_FPS is not supported by any format.
|
||||
// targetFps is not supported by any format.
|
||||
return 30;
|
||||
}
|
||||
// If nothing blocks us from using it, we default to HIGH_FPS.
|
||||
return HIGH_FPS;
|
||||
}, [device, enableHdr, enableNightMode, formats]);
|
||||
// If nothing blocks us from using it, we default to targetFps.
|
||||
return targetFps;
|
||||
}, [device?.supportsLowLightBoost, enableHdr, enableNightMode, formats, targetFps]);
|
||||
|
||||
const supportsCameraFlipping = useMemo(() => devices.some((d) => d.position === 'back') && devices.some((d) => d.position === 'front'), [devices]);
|
||||
const supportsFlash = device?.hasFlash ?? false;
|
||||
|
@ -28,9 +28,6 @@ export const RESOLUTION_LIMIT = Platform.select({
|
||||
// whether to use ultra-wide-angle cameras if available, or explicitly disable them. I think ultra-wide-angle cams don't support 60FPS...
|
||||
export const USE_ULTRAWIDE_IF_AVAILABLE = true;
|
||||
|
||||
// the max FPS to use if available
|
||||
export const HIGH_FPS = 50;
|
||||
|
||||
// The maximum zoom _factor_ you should be able to zoom in
|
||||
export const MAX_ZOOM_FACTOR = 16;
|
||||
|
||||
|
@ -1,15 +1,62 @@
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { StyleSheet, View, Text, Linking } from 'react-native';
|
||||
import type { NavigationFunctionComponent } from 'react-native-navigation';
|
||||
import { Navigation, NavigationFunctionComponent } from 'react-native-navigation';
|
||||
import Slider from '@react-native-community/slider';
|
||||
import { useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { Camera } from 'react-native-vision-camera';
|
||||
import { SAFE_AREA_PADDING } from './Constants';
|
||||
import { useSelector } from 'pipestate';
|
||||
import { FpsSelector } from './state/selectors';
|
||||
|
||||
export const Settings: NavigationFunctionComponent = ({ componentId }) => {
|
||||
const [fpsSelector, setFpsSelector] = useSelector(FpsSelector);
|
||||
const [fps, setFps] = useState(fpsSelector);
|
||||
|
||||
const [minFps, setMinFps] = useState<number>();
|
||||
const [maxFps, setMaxFps] = useState<number>();
|
||||
|
||||
export const Settings: NavigationFunctionComponent = () => {
|
||||
const onCuventPressed = useCallback(() => {
|
||||
Linking.openURL('https://cuvent.com');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const loadFormats = async (): Promise<void> => {
|
||||
const devices = await Camera.getAvailableCameraDevices();
|
||||
const formats = devices.flatMap((d) => d.formats);
|
||||
let max = 0,
|
||||
min = 0;
|
||||
formats.forEach((format) => {
|
||||
const frameRates = format.frameRateRanges.map((f) => f.maxFrameRate).sort((left, right) => left - right);
|
||||
const highest = frameRates[frameRates.length - 1] as number;
|
||||
const lowest = frameRates[0] as number;
|
||||
if (highest > max) max = highest;
|
||||
if (lowest < min) min = lowest;
|
||||
});
|
||||
setMaxFps(max);
|
||||
setMinFps(min);
|
||||
};
|
||||
loadFormats();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = Navigation.events().registerScreenPoppedListener((event) => {
|
||||
if (event.componentId === componentId) setFpsSelector(fps);
|
||||
});
|
||||
return () => {
|
||||
listener.remove();
|
||||
};
|
||||
}, [componentId, fps, setFpsSelector]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.vControl}>
|
||||
<Text>Frame Rate (FPS): {fps}</Text>
|
||||
{minFps != null && maxFps != null && <Slider minimumValue={minFps} maximumValue={maxFps} value={fps} onValueChange={setFps} />}
|
||||
</View>
|
||||
|
||||
<View style={styles.spacer} />
|
||||
|
||||
<Text style={styles.aboutText}>
|
||||
Vision Camera is powered by{' '}
|
||||
<Text style={styles.hyperlink} onPress={onCuventPressed}>
|
||||
@ -37,11 +84,13 @@ Settings.options = {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
alignItems: 'stretch',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'white',
|
||||
...SAFE_AREA_PADDING,
|
||||
},
|
||||
aboutText: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
color: '#A9A9A9',
|
||||
@ -52,4 +101,10 @@ const styles = StyleSheet.create({
|
||||
color: '#007aff',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
vControl: {
|
||||
width: '100%',
|
||||
},
|
||||
spacer: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { selector } from 'pipestate';
|
||||
import { FormatSettingsAtom } from './atoms';
|
||||
|
||||
export const FpsSelector = selector({
|
||||
export const FpsSelector = selector<number, []>({
|
||||
get: ({ get }) => {
|
||||
return get(FormatSettingsAtom).fps;
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user