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:
29
package/src/hooks/useCameraDevice.ts
Normal file
29
package/src/hooks/useCameraDevice.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useMemo } from 'react';
|
||||
import { CameraDevice, CameraPosition } from '../CameraDevice';
|
||||
import { getCameraDevice, DeviceFilter } from '../devices/getCameraDevice';
|
||||
import { useCameraDevices } from './useCameraDevices';
|
||||
|
||||
/**
|
||||
* Get the best matching Camera device that best satisfies your requirements using a sorting filter.
|
||||
* @param position The position of the Camera device relative to the phone.
|
||||
* @param filter The filter you want to use. The Camera device that matches your filter the closest will be returned
|
||||
* @returns The Camera device that matches your filter the closest.
|
||||
* @example
|
||||
* ```ts
|
||||
* const [position, setPosition] = useState<CameraPosition>('back')
|
||||
* const device = useCameraDevice(position, {
|
||||
* physicalDevices: ['wide-angle-camera']
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function useCameraDevice(position: CameraPosition, filter?: DeviceFilter): CameraDevice | undefined {
|
||||
const devices = useCameraDevices();
|
||||
|
||||
const device = useMemo(
|
||||
() => getCameraDevice(devices, position, filter),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[devices, position, JSON.stringify(filter)],
|
||||
);
|
||||
|
||||
return device;
|
||||
}
|
@@ -1,78 +1,23 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { CameraPosition } from '../CameraPosition';
|
||||
import { sortDevices } from '../utils/FormatFilter';
|
||||
import { Camera } from '../Camera';
|
||||
import { CameraDevice, LogicalCameraDeviceType, parsePhysicalDeviceTypes, PhysicalCameraDeviceType } from '../CameraDevice';
|
||||
|
||||
export type CameraDevices = {
|
||||
[key in CameraPosition]: CameraDevice | undefined;
|
||||
};
|
||||
const DefaultCameraDevices: CameraDevices = {
|
||||
back: undefined,
|
||||
external: undefined,
|
||||
front: undefined,
|
||||
unspecified: undefined,
|
||||
};
|
||||
import type { CameraDevice } from '../CameraDevice';
|
||||
import { CameraDevices } from '../CameraDevices';
|
||||
|
||||
/**
|
||||
* Gets the best available {@linkcode CameraDevice}. Devices with more cameras are preferred.
|
||||
* Get all available Camera Devices this phone has.
|
||||
*
|
||||
* @returns The best matching {@linkcode CameraDevice}.
|
||||
* @throws {@linkcode CameraRuntimeError} if no device was found.
|
||||
* @example
|
||||
* ```tsx
|
||||
* const device = useCameraDevice()
|
||||
* // ...
|
||||
* return <Camera device={device} />
|
||||
* ```
|
||||
* Camera Devices attached to this phone (`back` or `front`) are always available,
|
||||
* while `external` devices might be plugged in or out at any point,
|
||||
* so the result of this function might update over time.
|
||||
*/
|
||||
export function useCameraDevices(): CameraDevices;
|
||||
|
||||
/**
|
||||
* Gets a {@linkcode CameraDevice} for the requested device type.
|
||||
*
|
||||
* @param {PhysicalCameraDeviceType | LogicalCameraDeviceType} deviceType Specifies a device type which will be used as a device filter.
|
||||
* @returns A {@linkcode CameraDevice} for the requested device type.
|
||||
* @throws {@linkcode CameraRuntimeError} if no device was found.
|
||||
* @example
|
||||
* ```tsx
|
||||
* const device = useCameraDevice('wide-angle-camera')
|
||||
* // ...
|
||||
* return <Camera device={device} />
|
||||
* ```
|
||||
*/
|
||||
export function useCameraDevices(deviceType: PhysicalCameraDeviceType | LogicalCameraDeviceType): CameraDevices;
|
||||
|
||||
export function useCameraDevices(deviceType?: PhysicalCameraDeviceType | LogicalCameraDeviceType): CameraDevices {
|
||||
const [cameraDevices, setCameraDevices] = useState<CameraDevices>(DefaultCameraDevices);
|
||||
export function useCameraDevices(): CameraDevice[] {
|
||||
const [devices, setDevices] = useState(() => CameraDevices.getAvailableCameraDevices());
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
const listener = CameraDevices.addCameraDevicesChangedListener((newDevices) => {
|
||||
setDevices(newDevices);
|
||||
});
|
||||
return () => listener.remove();
|
||||
}, []);
|
||||
|
||||
const loadDevice = async (): Promise<void> => {
|
||||
let devices = await Camera.getAvailableCameraDevices();
|
||||
if (!isMounted) return;
|
||||
|
||||
devices = devices.sort(sortDevices);
|
||||
if (deviceType != null) {
|
||||
devices = devices.filter((d) => {
|
||||
const parsedType = parsePhysicalDeviceTypes(d.devices);
|
||||
return parsedType === deviceType;
|
||||
});
|
||||
}
|
||||
setCameraDevices({
|
||||
back: devices.find((d) => d.position === 'back'),
|
||||
external: devices.find((d) => d.position === 'external'),
|
||||
front: devices.find((d) => d.position === 'front'),
|
||||
unspecified: devices.find((d) => d.position === 'unspecified'),
|
||||
});
|
||||
};
|
||||
loadDevice();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [deviceType]);
|
||||
|
||||
return cameraDevices;
|
||||
return devices;
|
||||
}
|
||||
|
@@ -1,16 +1,27 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { CameraDevice, CameraDeviceFormat } from '../CameraDevice';
|
||||
import { sortFormats } from '../utils/FormatFilter';
|
||||
import { CameraDevice, CameraDeviceFormat } from '../CameraDevice';
|
||||
import { FormatFilter, getCameraFormat } from '../devices/getCameraFormat';
|
||||
|
||||
/**
|
||||
* Returns the best format for the given camera device.
|
||||
*
|
||||
* This function tries to choose a format with the highest possible photo-capture resolution and best matching aspect ratio.
|
||||
*
|
||||
* @param {CameraDevice} device The Camera Device
|
||||
*
|
||||
* @returns The best matching format for the given camera device, or `undefined` if the camera device is `undefined`.
|
||||
* Get the best matching Camera format for the given device that satisfies your requirements using a sorting filter. By default, formats are sorted by highest to lowest resolution.
|
||||
* @param device The Camera Device you're currently using
|
||||
* @param filter The filter you want to use. The format that matches your filter the closest will be returned
|
||||
* @returns The format that matches your filter the closest.
|
||||
* @example
|
||||
* ```ts
|
||||
* const device = useCameraDevice(...)
|
||||
* const format = useCameraFormat(device, {
|
||||
* videoResolution: { target: { width: 3048, height: 2160 }, priority: 2 },
|
||||
* fps: { target: 60, priority: 1 }
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function useCameraFormat(device?: CameraDevice): CameraDeviceFormat | undefined {
|
||||
return useMemo(() => device?.formats.sort(sortFormats)[0], [device?.formats]);
|
||||
export function useCameraFormat(device: CameraDevice | undefined, filter: FormatFilter): CameraDeviceFormat | undefined {
|
||||
const format = useMemo(() => {
|
||||
if (device == null) return undefined;
|
||||
return getCameraFormat(device, filter);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [device, JSON.stringify(filter)]);
|
||||
|
||||
return format;
|
||||
}
|
||||
|
Reference in New Issue
Block a user