feat: Add minFocusDistance prop to CameraDevice (#2392)
				
					
				
			* docs: Link `videoHdr` * Update PERFORMANCE.mdx * docs: Add isActive to perf * docs: Update errors * feat: Add `minFocusDistance` prop * Format * Update Podfile.lock * fix: To Double * fix: Import AVFoundation * fix: Move from format -> device * fix: Use centi-meters (cm) instead of meters * Fix deadloop * fix: Avoid -1 values
This commit is contained in:
		| @@ -55,7 +55,7 @@ See [the `CameraError.ts` file](https://github.com/mrousavy/react-native-vision- | |||||||
|  |  | ||||||
| ### Runtime Errors | ### Runtime Errors | ||||||
|  |  | ||||||
| The `CameraRuntimeError` represents any kind of error that occured while mounting the Camera view, or an error that occured during the runtime. | The [`CameraRuntimeError`](/docs/api/classes/CameraRuntimeError) represents any kind of error that occured while mounting the Camera view, or an error that occured during the runtime. | ||||||
|  |  | ||||||
| The `<Camera />` UI Component provides an `onError` function that will be invoked every time an unexpected runtime error occured. | The `<Camera />` UI Component provides an `onError` function that will be invoked every time an unexpected runtime error occured. | ||||||
|  |  | ||||||
| @@ -71,7 +71,7 @@ function App() { | |||||||
|  |  | ||||||
| ### Capture Errors | ### Capture Errors | ||||||
|  |  | ||||||
| The `CameraCaptureError` represents any kind of error that occured only while taking a photo or recording a video. | The [`CameraCaptureError`](/docs/api/classes/CameraCaptureError) represents any kind of error that occured only while taking a photo or recording a video. | ||||||
|  |  | ||||||
| ```tsx | ```tsx | ||||||
| function App() { | function App() { | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ Note: By default (when not passing the options object), a simpler device is alre | |||||||
|  |  | ||||||
| ### No Video HDR | ### No Video HDR | ||||||
|  |  | ||||||
| Video HDR uses 10-bit formats and/or additional processing steps that come with additional computation overhead. Disable Video HDR (don't pass `videoHdr` to the `<Camera>`) for higher efficiency. | Video HDR uses 10-bit formats and/or additional processing steps that come with additional computation overhead. Disable [`videoHdr`](/docs/api/interfaces/CameraProps#videohdr) for higher efficiency. | ||||||
|  |  | ||||||
| ### Buffer Compression | ### Buffer Compression | ||||||
|  |  | ||||||
| @@ -83,7 +83,11 @@ By default, the `native` [`PixelFormat`](/docs/api#pixelformat) is used, which i | |||||||
|  |  | ||||||
| ### Disable unneeded pipelines | ### Disable unneeded pipelines | ||||||
|  |  | ||||||
| Only enable [`photo`](/docs/api/interfaces/CameraProps#photo) and [`video`](/docs/api/interfaces/CameraProps#video) if needed. | Only enable [`photo`](/docs/api/interfaces/CameraProps#photo), [`video`](/docs/api/interfaces/CameraProps#video), [`codeScanner`](/docs/api/interfaces/CameraProps#codescanner) or [`frameProcessor`](/docs/api/interfaces/CameraProps#frameprocessor) if needed. | ||||||
|  |  | ||||||
|  | ### Using `isActive` | ||||||
|  |  | ||||||
|  | The [`isActive`](/docs/api/interfaces/CameraProps#isactive) prop controls whether the Camera should actively stream frames. Instead of fully unmounting the `<Camera>` component and remounting it again, keep it mounted and just switch `isActive` on or off. This makes the Camera resume much faster as it internally keeps the session warmed up. | ||||||
|  |  | ||||||
| ### Fast Photos | ### Fast Photos | ||||||
|  |  | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ class CameraDeviceDetails(val cameraManager: CameraManager, val cameraId: String | |||||||
|       ?: floatArrayOf(35f) |       ?: floatArrayOf(35f) | ||||||
|   val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)!! |   val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)!! | ||||||
|   val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!! |   val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!! | ||||||
|  |   val minFocusDistance = getMinFocusDistanceCm() | ||||||
|   val name = ( |   val name = ( | ||||||
|     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { |     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { | ||||||
|       characteristics.get(CameraCharacteristics.INFO_VERSION) |       characteristics.get(CameraCharacteristics.INFO_VERSION) | ||||||
| @@ -97,6 +98,13 @@ class CameraDeviceDetails(val cameraManager: CameraManager, val cameraId: String | |||||||
|     return false |     return false | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private fun getMinFocusDistanceCm(): Double { | ||||||
|  |     val distance = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE) | ||||||
|  |     if (distance == null || distance == 0f) return 0.0 | ||||||
|  |     // distance is in "diopters", meaning 1/meter. Convert to meters, then centi-meters | ||||||
|  |     return 1.0 / distance * 100.0 | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private fun createStabilizationModes(): ReadableArray { |   private fun createStabilizationModes(): ReadableArray { | ||||||
|     val array = Arguments.createArray() |     val array = Arguments.createArray() | ||||||
|     digitalStabilizationModes.forEach { videoStabilizationMode -> |     digitalStabilizationModes.forEach { videoStabilizationMode -> | ||||||
| @@ -198,6 +206,7 @@ class CameraDeviceDetails(val cameraManager: CameraManager, val cameraId: String | |||||||
|     map.putString("name", name) |     map.putString("name", name) | ||||||
|     map.putBoolean("hasFlash", hasFlash) |     map.putBoolean("hasFlash", hasFlash) | ||||||
|     map.putBoolean("hasTorch", hasFlash) |     map.putBoolean("hasTorch", hasFlash) | ||||||
|  |     map.putDouble("minFocusDistance", minFocusDistance) | ||||||
|     map.putBoolean("isMultiCam", isMultiCam) |     map.putBoolean("isMultiCam", isMultiCam) | ||||||
|     map.putBoolean("supportsRawCapture", supportsRawCapture) |     map.putBoolean("supportsRawCapture", supportsRawCapture) | ||||||
|     map.putBoolean("supportsLowLightBoost", supportsLowLightBoost) |     map.putBoolean("supportsLowLightBoost", supportsLowLightBoost) | ||||||
|   | |||||||
| @@ -729,4 +729,4 @@ SPEC CHECKSUMS: | |||||||
|  |  | ||||||
| PODFILE CHECKSUM: 27f53791141a3303d814e09b55770336416ff4eb | PODFILE CHECKSUM: 27f53791141a3303d814e09b55770336416ff4eb | ||||||
|  |  | ||||||
| COCOAPODS: 1.14.3 | COCOAPODS: 1.11.3 | ||||||
|   | |||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | // | ||||||
|  | //  AVCaptureDevice+minFocusDistance.swift | ||||||
|  | //  VisionCamera | ||||||
|  | // | ||||||
|  | //  Created by Marc Rousavy on 15.01.24. | ||||||
|  | //  Copyright © 2024 mrousavy. All rights reserved. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import AVFoundation | ||||||
|  | import Foundation | ||||||
|  |  | ||||||
|  | extension AVCaptureDevice { | ||||||
|  |   /** | ||||||
|  |    * The minimum distance this device can focus to, in centi-meters. | ||||||
|  |    */ | ||||||
|  |   var minFocusDistance: Double { | ||||||
|  |     guard #available(iOS 15.0, *), minimumFocusDistance > 0 else { | ||||||
|  |       // focus distance is unknown/unavailable | ||||||
|  |       return 0 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // convert from millimeters to centimeters | ||||||
|  |     return Double(minimumFocusDistance) / 10 | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -19,6 +19,7 @@ extension AVCaptureDevice { | |||||||
|       "name": localizedName, |       "name": localizedName, | ||||||
|       "hasFlash": hasFlash, |       "hasFlash": hasFlash, | ||||||
|       "hasTorch": hasTorch, |       "hasTorch": hasTorch, | ||||||
|  |       "minFocusDistance": minFocusDistance, | ||||||
|       "minZoom": minAvailableVideoZoomFactor, |       "minZoom": minAvailableVideoZoomFactor, | ||||||
|       "maxZoom": maxAvailableVideoZoomFactor, |       "maxZoom": maxAvailableVideoZoomFactor, | ||||||
|       "neutralZoom": neutralZoomFactor, |       "neutralZoom": neutralZoomFactor, | ||||||
|   | |||||||
| @@ -64,6 +64,7 @@ | |||||||
| 		B88751A725E0102000DB86D6 /* CameraView+Zoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518225E0102000DB86D6 /* CameraView+Zoom.swift */; }; | 		B88751A725E0102000DB86D6 /* CameraView+Zoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518225E0102000DB86D6 /* CameraView+Zoom.swift */; }; | ||||||
| 		B88751A825E0102000DB86D6 /* CameraError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518325E0102000DB86D6 /* CameraError.swift */; }; | 		B88751A825E0102000DB86D6 /* CameraError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518325E0102000DB86D6 /* CameraError.swift */; }; | ||||||
| 		B88751A925E0102000DB86D6 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518425E0102000DB86D6 /* CameraView.swift */; }; | 		B88751A925E0102000DB86D6 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518425E0102000DB86D6 /* CameraView.swift */; }; | ||||||
|  | 		B88977BE2B556DBA0095C92C /* AVCaptureDevice+minFocusDistance.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88977BD2B556DBA0095C92C /* AVCaptureDevice+minFocusDistance.swift */; }; | ||||||
| 		B8994E6C263F03E100069589 /* JSINSObjectConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = B8994E6B263F03E100069589 /* JSINSObjectConversion.mm */; }; | 		B8994E6C263F03E100069589 /* JSINSObjectConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = B8994E6B263F03E100069589 /* JSINSObjectConversion.mm */; }; | ||||||
| 		B8A1AEC42AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */; }; | 		B8A1AEC42AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */; }; | ||||||
| 		B8A1AEC62AD7F08E00169C0D /* CameraView+Focus.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC52AD7F08E00169C0D /* CameraView+Focus.swift */; }; | 		B8A1AEC62AD7F08E00169C0D /* CameraView+Focus.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC52AD7F08E00169C0D /* CameraView+Focus.swift */; }; | ||||||
| @@ -161,6 +162,7 @@ | |||||||
| 		B887518325E0102000DB86D6 /* CameraError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraError.swift; sourceTree = "<group>"; }; | 		B887518325E0102000DB86D6 /* CameraError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraError.swift; sourceTree = "<group>"; }; | ||||||
| 		B887518425E0102000DB86D6 /* CameraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = "<group>"; }; | 		B887518425E0102000DB86D6 /* CameraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = "<group>"; }; | ||||||
| 		B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorPlugin.h; sourceTree = "<group>"; }; | 		B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorPlugin.h; sourceTree = "<group>"; }; | ||||||
|  | 		B88977BD2B556DBA0095C92C /* AVCaptureDevice+minFocusDistance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureDevice+minFocusDistance.swift"; sourceTree = "<group>"; }; | ||||||
| 		B8994E6B263F03E100069589 /* JSINSObjectConversion.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSINSObjectConversion.mm; sourceTree = "<group>"; }; | 		B8994E6B263F03E100069589 /* JSINSObjectConversion.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSINSObjectConversion.mm; sourceTree = "<group>"; }; | ||||||
| 		B89A79692B3EF60F005E0357 /* UIImageOrientation+descriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImageOrientation+descriptor.h"; sourceTree = "<group>"; }; | 		B89A79692B3EF60F005E0357 /* UIImageOrientation+descriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIImageOrientation+descriptor.h"; sourceTree = "<group>"; }; | ||||||
| 		B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureVideoDataOutput+pixelFormat.swift"; sourceTree = "<group>"; }; | 		B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureVideoDataOutput+pixelFormat.swift"; sourceTree = "<group>"; }; | ||||||
| @@ -284,6 +286,7 @@ | |||||||
| 				B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */, | 				B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */, | ||||||
| 				B8207AAC2B0E5DD70002990F /* AVCaptureSession+synchronizeBuffer.swift */, | 				B8207AAC2B0E5DD70002990F /* AVCaptureSession+synchronizeBuffer.swift */, | ||||||
| 				B8207AAE2B0E67460002990F /* AVCaptureVideoDataOutput+recommendedVideoSettings.swift */, | 				B8207AAE2B0E67460002990F /* AVCaptureVideoDataOutput+recommendedVideoSettings.swift */, | ||||||
|  | 				B88977BD2B556DBA0095C92C /* AVCaptureDevice+minFocusDistance.swift */, | ||||||
| 			); | 			); | ||||||
| 			path = Extensions; | 			path = Extensions; | ||||||
| 			sourceTree = "<group>"; | 			sourceTree = "<group>"; | ||||||
| @@ -488,6 +491,7 @@ | |||||||
| 				B881D35E2ABC775E009B21C8 /* AVCaptureDevice+toDictionary.swift in Sources */, | 				B881D35E2ABC775E009B21C8 /* AVCaptureDevice+toDictionary.swift in Sources */, | ||||||
| 				B87B11BF2A8E63B700732EBF /* PixelFormat.swift in Sources */, | 				B87B11BF2A8E63B700732EBF /* PixelFormat.swift in Sources */, | ||||||
| 				B88751A625E0102000DB86D6 /* CameraViewManager.swift in Sources */, | 				B88751A625E0102000DB86D6 /* CameraViewManager.swift in Sources */, | ||||||
|  | 				B88977BE2B556DBA0095C92C /* AVCaptureDevice+minFocusDistance.swift in Sources */, | ||||||
| 				B80175EC2ABDEBD000E7DE90 /* ResizeMode.swift in Sources */, | 				B80175EC2ABDEBD000E7DE90 /* ResizeMode.swift in Sources */, | ||||||
| 				B887519F25E0102000DB86D6 /* AVCaptureDevice.DeviceType+physicalDeviceDescriptor.swift in Sources */, | 				B887519F25E0102000DB86D6 /* AVCaptureDevice.DeviceType+physicalDeviceDescriptor.swift in Sources */, | ||||||
| 				B88685ED2AD6A5E600E93869 /* CameraSession+CodeScanner.swift in Sources */, | 				B88685ED2AD6A5E600E93869 /* CameraSession+CodeScanner.swift in Sources */, | ||||||
|   | |||||||
| @@ -164,6 +164,10 @@ export interface CameraDevice { | |||||||
|    * Specifies whether this camera supports continuously enabling the flash to act like a torch (flash with video capture) |    * Specifies whether this camera supports continuously enabling the flash to act like a torch (flash with video capture) | ||||||
|    */ |    */ | ||||||
|   hasTorch: boolean |   hasTorch: boolean | ||||||
|  |   /** | ||||||
|  |    * The minimum distance this device can properly focus to (in centimeters/cm) or `0` if unknown. | ||||||
|  |    */ | ||||||
|  |   minFocusDistance: number | ||||||
|   /** |   /** | ||||||
|    * A property indicating whether the device is a virtual multi-camera consisting of multiple combined physical cameras. |    * A property indicating whether the device is a virtual multi-camera consisting of multiple combined physical cameras. | ||||||
|    * |    * | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user