@capgo/camera-preview 8.8.0 → 8.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -105,6 +105,10 @@ class CameraController: NSObject {
105
105
 
106
106
  var captureSession: AVCaptureSession?
107
107
  var disableFocusIndicator: Bool = false
108
+ private var focusExposureRestoreGeneration: UInt = 0
109
+ private var focusExposureRestoreTimeoutWorkItem: DispatchWorkItem?
110
+ private let focusExposureRestoreTimeout: TimeInterval = 3.0
111
+ private let focusExposureRestorePollInterval: TimeInterval = 0.05
108
112
 
109
113
  var currentCameraPosition: CameraPosition?
110
114
 
@@ -1195,6 +1199,7 @@ extension CameraController {
1195
1199
  }
1196
1200
 
1197
1201
  func switchCameras() throws {
1202
+ self.cancelPendingFocusExposureRestore()
1198
1203
  configuredVideoFrameRate = nil
1199
1204
  guard let currentCameraPosition = currentCameraPosition,
1200
1205
  let captureSession = self.captureSession else {
@@ -2179,46 +2184,116 @@ extension CameraController {
2179
2184
  }
2180
2185
  }
2181
2186
 
2182
- private func triggerAutoFocus() {
2183
- var currentCamera: AVCaptureDevice?
2184
- switch currentCameraPosition {
2185
- case .front:
2186
- currentCamera = self.frontCamera
2187
- case .rear:
2188
- currentCamera = self.rearCamera
2189
- default: break
2187
+
2188
+ private func cancelPendingFocusExposureRestore() {
2189
+ self.focusExposureRestoreGeneration &+= 1
2190
+ self.focusExposureRestoreTimeoutWorkItem?.cancel()
2191
+ self.focusExposureRestoreTimeoutWorkItem = nil
2192
+ }
2193
+
2194
+ private func applyOneShotFocusAndExposure(at point: CGPoint, on device: AVCaptureDevice, adjustExposure: Bool) throws {
2195
+ try device.lockForConfiguration()
2196
+ defer { device.unlockForConfiguration() }
2197
+
2198
+ if device.isFocusModeSupported(.autoFocus) {
2199
+ device.focusMode = .autoFocus
2200
+ } else if device.isFocusModeSupported(.continuousAutoFocus) {
2201
+ device.focusMode = .continuousAutoFocus
2190
2202
  }
2191
2203
 
2192
- guard let device = currentCamera else {
2204
+ if device.isFocusPointOfInterestSupported {
2205
+ device.focusPointOfInterest = point
2206
+ }
2207
+
2208
+ if adjustExposure, device.exposureMode != .locked {
2209
+ if device.isExposurePointOfInterestSupported, device.isExposureModeSupported(.autoExpose) {
2210
+ device.exposureMode = .autoExpose
2211
+ device.setExposureTargetBias(0.0) { _ in }
2212
+ device.exposurePointOfInterest = point
2213
+ }
2214
+ }
2215
+
2216
+ device.isSubjectAreaChangeMonitoringEnabled = true
2217
+ }
2218
+
2219
+ private func scheduleFocusExposureRestore(for device: AVCaptureDevice) {
2220
+ self.focusExposureRestoreGeneration &+= 1
2221
+ let generation = self.focusExposureRestoreGeneration
2222
+
2223
+ self.focusExposureRestoreTimeoutWorkItem?.cancel()
2224
+
2225
+ let timeoutWork = DispatchWorkItem { [weak self] in
2226
+ self?.restoreContinuousFocusAndExposureIfCurrent(on: device, generation: generation)
2227
+ }
2228
+ self.focusExposureRestoreTimeoutWorkItem = timeoutWork
2229
+
2230
+ self.pollFocusExposureAdjustmentCompletion(on: device, generation: generation)
2231
+ DispatchQueue.main.asyncAfter(deadline: .now() + self.focusExposureRestoreTimeout, execute: timeoutWork)
2232
+ }
2233
+
2234
+ private func pollFocusExposureAdjustmentCompletion(on device: AVCaptureDevice, generation: UInt) {
2235
+ guard generation == self.focusExposureRestoreGeneration else { return }
2236
+
2237
+ if device.isAdjustingFocus || device.isAdjustingExposure {
2238
+ DispatchQueue.main.asyncAfter(deadline: .now() + self.focusExposureRestorePollInterval) { [weak self] in
2239
+ self?.pollFocusExposureAdjustmentCompletion(on: device, generation: generation)
2240
+ }
2193
2241
  return
2194
2242
  }
2195
2243
 
2196
- // Focus on the center of the preview (0.5, 0.5)
2197
- let centerPoint = CGPoint(x: 0.5, y: 0.5)
2244
+ self.restoreContinuousFocusAndExposureIfCurrent(on: device, generation: generation)
2245
+ }
2246
+
2247
+ private func restoreContinuousFocusAndExposureIfCurrent(on device: AVCaptureDevice, generation: UInt) {
2248
+ guard generation == self.focusExposureRestoreGeneration else { return }
2249
+
2250
+ self.focusExposureRestoreTimeoutWorkItem?.cancel()
2251
+ self.focusExposureRestoreTimeoutWorkItem = nil
2198
2252
 
2199
2253
  do {
2200
2254
  try device.lockForConfiguration()
2255
+ defer { device.unlockForConfiguration() }
2201
2256
 
2202
- // Set focus mode to auto if supported
2203
- if device.isFocusModeSupported(.autoFocus) {
2204
- device.focusMode = .autoFocus
2205
- if device.isFocusPointOfInterestSupported {
2206
- device.focusPointOfInterest = centerPoint
2207
- }
2208
- } else if device.isFocusModeSupported(.continuousAutoFocus) {
2257
+ let centerPoint = CGPoint(x: 0.5, y: 0.5)
2258
+
2259
+ if device.isFocusModeSupported(.continuousAutoFocus) {
2209
2260
  device.focusMode = .continuousAutoFocus
2210
2261
  if device.isFocusPointOfInterestSupported {
2211
2262
  device.focusPointOfInterest = centerPoint
2212
2263
  }
2213
2264
  }
2214
2265
 
2215
- if device.isExposurePointOfInterestSupported {
2216
- let exposureMode = try getExposureMode()
2217
- if exposureMode == "AUTO" || exposureMode == "CONTINUOUS" {
2218
- device.exposurePointOfInterest = centerPoint
2266
+ if device.exposureMode != .locked {
2267
+ if device.isExposureModeSupported(.continuousAutoExposure) {
2268
+ device.exposureMode = .continuousAutoExposure
2269
+ if device.isExposurePointOfInterestSupported {
2270
+ device.exposurePointOfInterest = centerPoint
2271
+ }
2272
+ device.setExposureTargetBias(0.0) { _ in }
2219
2273
  }
2220
2274
  }
2221
- device.unlockForConfiguration()
2275
+
2276
+ device.isSubjectAreaChangeMonitoringEnabled = false
2277
+ } catch {
2278
+ print("[CameraPreview] Failed to restore continuous focus/exposure: \(error)")
2279
+ }
2280
+ }
2281
+
2282
+ private func triggerAutoFocus() {
2283
+ guard let device = self.activeVideoDevice() else { return }
2284
+
2285
+ let centerPoint = CGPoint(x: 0.5, y: 0.5)
2286
+ let adjustExposure: Bool
2287
+ do {
2288
+ let exposureMode = try self.getExposureMode()
2289
+ adjustExposure = exposureMode == "AUTO" || exposureMode == "CONTINUOUS"
2290
+ } catch {
2291
+ adjustExposure = true
2292
+ }
2293
+
2294
+ do {
2295
+ try self.applyOneShotFocusAndExposure(at: centerPoint, on: device, adjustExposure: adjustExposure)
2296
+ self.scheduleFocusExposureRestore(for: device)
2222
2297
  } catch {
2223
2298
  // Silently ignore errors during autofocus
2224
2299
  }
@@ -2257,33 +2332,8 @@ extension CameraController {
2257
2332
  }
2258
2333
 
2259
2334
  do {
2260
- try device.lockForConfiguration()
2261
-
2262
- // Set focus mode to auto if supported
2263
- if device.isFocusModeSupported(.autoFocus) {
2264
- device.focusMode = .autoFocus
2265
- } else if device.isFocusModeSupported(.continuousAutoFocus) {
2266
- device.focusMode = .continuousAutoFocus
2267
- }
2268
-
2269
- // Set the focus point
2270
- device.focusPointOfInterest = point
2271
-
2272
- // Skip exposure point if exposure locked
2273
- if device.exposureMode != .locked {
2274
-
2275
- // Also set exposure point if supported
2276
- if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(.autoExpose) {
2277
- device.exposureMode = .autoExpose
2278
- device.setExposureTargetBias(0.0) { _ in }
2279
- device.exposurePointOfInterest = point
2280
- }
2281
- }
2282
-
2283
- // Turn on subject area monitor for switch to continuous focus if needed
2284
- device.isSubjectAreaChangeMonitoringEnabled = true
2285
-
2286
- device.unlockForConfiguration()
2335
+ try self.applyOneShotFocusAndExposure(at: point, on: device, adjustExposure: true)
2336
+ self.scheduleFocusExposureRestore(for: device)
2287
2337
  } catch {
2288
2338
  throw CameraControllerError.unknown
2289
2339
  }
@@ -2372,6 +2422,7 @@ extension CameraController {
2372
2422
  }
2373
2423
 
2374
2424
  func swapToDevice(deviceId: String) throws {
2425
+ self.cancelPendingFocusExposureRestore()
2375
2426
  guard let captureSession = self.captureSession else {
2376
2427
  throw CameraControllerError.captureSessionIsMissing
2377
2428
  }
@@ -2460,6 +2511,7 @@ extension CameraController {
2460
2511
  }
2461
2512
 
2462
2513
  func cleanup() {
2514
+ self.cancelPendingFocusExposureRestore()
2463
2515
  configuredVideoFrameRate = nil
2464
2516
  stopBarcodeScanner()
2465
2517
  if let captureSession = self.captureSession {
@@ -2914,38 +2966,16 @@ extension CameraController: UIGestureRecognizerDelegate {
2914
2966
 
2915
2967
  @objc
2916
2968
  func handleTap(_ tap: UITapGestureRecognizer) {
2917
- guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
2918
-
2919
- let point = tap.location(in: tap.view)
2920
- let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
2921
-
2922
- // Show focus indicator at the tap point if not disabled
2923
- if !self.disableFocusIndicator, let view = tap.view {
2924
- showFocusIndicator(at: point, in: view)
2969
+ guard let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: tap.location(in: tap.view)) else {
2970
+ return
2925
2971
  }
2926
2972
 
2927
2973
  do {
2928
- try device.lockForConfiguration()
2929
- defer { device.unlockForConfiguration() }
2930
-
2931
- let focusMode = AVCaptureDevice.FocusMode.autoFocus
2932
- if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(focusMode) {
2933
- device.focusPointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
2934
- device.focusMode = focusMode
2935
- }
2936
- // Skip exposure point if locked
2937
- if device.exposureMode != .locked {
2938
- let exposureMode = AVCaptureDevice.ExposureMode.autoExpose
2939
- if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
2940
- device.exposurePointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
2941
- device.exposureMode = exposureMode
2942
- device.setExposureTargetBias(0.0) { _ in }
2943
- }
2944
- }
2945
-
2946
- // Turn on subject area monitor for switch to continuous focus if needed
2947
- device.isSubjectAreaChangeMonitoringEnabled = true
2948
-
2974
+ try self.setFocus(
2975
+ at: devicePoint,
2976
+ showIndicator: !self.disableFocusIndicator,
2977
+ in: tap.view
2978
+ )
2949
2979
  } catch {
2950
2980
  debugPrint(error)
2951
2981
  }
@@ -35,7 +35,7 @@ extension UIWindow {
35
35
  */
36
36
  @objc(CameraPreview)
37
37
  public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate {
38
- private let pluginVersion: String = "8.8.0"
38
+ private let pluginVersion: String = "8.8.1"
39
39
  public let identifier = "CameraPreviewPlugin"
40
40
  public let jsName = "CameraPreview"
41
41
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "8.8.0",
3
+ "version": "8.8.1",
4
4
  "description": "Camera preview",
5
5
  "license": "MPL-2.0",
6
6
  "repository": {