@capgo/camera-preview 8.11.1 → 8.11.2
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.
|
@@ -82,8 +82,8 @@ class CameraController: NSObject {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// 2. Reset Exposure to the center ONLY if it is not explicitly locked
|
|
86
|
-
if
|
|
85
|
+
// 2. Reset Exposure to the center ONLY if it is not explicitly locked by the user
|
|
86
|
+
if self.shouldAutoAdjustExposureOnFocus {
|
|
87
87
|
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
88
88
|
device.exposureMode = .continuousAutoExposure
|
|
89
89
|
if device.isExposurePointOfInterestSupported {
|
|
@@ -105,6 +105,14 @@ class CameraController: NSObject {
|
|
|
105
105
|
|
|
106
106
|
var captureSession: AVCaptureSession?
|
|
107
107
|
var disableFocusIndicator: Bool = false
|
|
108
|
+
/// App-level exposure preference. AVFoundation's `.autoExpose` auto-transitions to
|
|
109
|
+
/// `.locked` after one-shot metering, so device.exposureMode alone cannot tell whether
|
|
110
|
+
/// the user explicitly locked exposure via setExposureMode("LOCK").
|
|
111
|
+
private var preferredExposureMode: String = "CONTINUOUS"
|
|
112
|
+
/// AUTO/CONTINUOUS allow tap/restore to reset metering + EV; LOCK/CUSTOM must keep user settings.
|
|
113
|
+
private var shouldAutoAdjustExposureOnFocus: Bool {
|
|
114
|
+
preferredExposureMode == "AUTO" || preferredExposureMode == "CONTINUOUS"
|
|
115
|
+
}
|
|
108
116
|
private var focusExposureRestoreGeneration: UInt = 0
|
|
109
117
|
private var focusExposureRestoreTimeoutWorkItem: DispatchWorkItem?
|
|
110
118
|
private let focusExposureRestoreTimeout: TimeInterval = 3.0
|
|
@@ -1290,12 +1298,13 @@ extension CameraController {
|
|
|
1290
1298
|
self.rearCameraInput = newInput
|
|
1291
1299
|
self.currentCameraPosition = .rear
|
|
1292
1300
|
}
|
|
1293
|
-
// (Lightweight focus config; non-fatal on failure)
|
|
1301
|
+
// (Lightweight focus/exposure config; non-fatal on failure)
|
|
1294
1302
|
try? targetDevice.lockForConfiguration()
|
|
1295
1303
|
if targetDevice.isFocusModeSupported(.continuousAutoFocus) {
|
|
1296
1304
|
targetDevice.focusMode = .continuousAutoFocus
|
|
1297
1305
|
}
|
|
1298
1306
|
targetDevice.unlockForConfiguration()
|
|
1307
|
+
try? self.applyPreferredExposureMode(to: targetDevice)
|
|
1299
1308
|
|
|
1300
1309
|
// Restore audio input if it existed
|
|
1301
1310
|
if let audioInput = existingAudioInput, captureSession.canAddInput(audioInput) {
|
|
@@ -2224,7 +2233,8 @@ extension CameraController {
|
|
|
2224
2233
|
device.focusPointOfInterest = point
|
|
2225
2234
|
}
|
|
2226
2235
|
|
|
2227
|
-
|
|
2236
|
+
// Prefer app-level mode: `.autoExpose` leaves device.exposureMode as `.locked`.
|
|
2237
|
+
if adjustExposure, self.shouldAutoAdjustExposureOnFocus {
|
|
2228
2238
|
if device.isExposurePointOfInterestSupported, device.isExposureModeSupported(.autoExpose) {
|
|
2229
2239
|
device.exposureMode = .autoExpose
|
|
2230
2240
|
device.setExposureTargetBias(0.0) { _ in }
|
|
@@ -2282,7 +2292,9 @@ extension CameraController {
|
|
|
2282
2292
|
}
|
|
2283
2293
|
}
|
|
2284
2294
|
|
|
2285
|
-
|
|
2295
|
+
// Restore continuous AE for AUTO/CONTINUOUS only (preserve LOCK/CUSTOM).
|
|
2296
|
+
// Do not trust device.exposureMode here: one-shot `.autoExpose` ends in `.locked`.
|
|
2297
|
+
if self.shouldAutoAdjustExposureOnFocus {
|
|
2286
2298
|
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
2287
2299
|
device.exposureMode = .continuousAutoExposure
|
|
2288
2300
|
if device.isExposurePointOfInterestSupported {
|
|
@@ -2302,13 +2314,7 @@ extension CameraController {
|
|
|
2302
2314
|
guard let device = self.activeVideoDevice() else { return }
|
|
2303
2315
|
|
|
2304
2316
|
let centerPoint = CGPoint(x: 0.5, y: 0.5)
|
|
2305
|
-
let adjustExposure
|
|
2306
|
-
do {
|
|
2307
|
-
let exposureMode = try self.getExposureMode()
|
|
2308
|
-
adjustExposure = exposureMode == "AUTO" || exposureMode == "CONTINUOUS"
|
|
2309
|
-
} catch {
|
|
2310
|
-
adjustExposure = true
|
|
2311
|
-
}
|
|
2317
|
+
let adjustExposure = self.shouldAutoAdjustExposureOnFocus
|
|
2312
2318
|
|
|
2313
2319
|
do {
|
|
2314
2320
|
try self.applyOneShotFocusAndExposure(at: centerPoint, on: device, adjustExposure: adjustExposure)
|
|
@@ -2498,14 +2504,15 @@ extension CameraController {
|
|
|
2498
2504
|
self.rearCameraInput = newInput
|
|
2499
2505
|
self.rearCamera = targetDevice
|
|
2500
2506
|
self.currentCameraPosition = .rear
|
|
2507
|
+
}
|
|
2501
2508
|
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
}
|
|
2507
|
-
targetDevice.unlockForConfiguration()
|
|
2509
|
+
// Lightweight focus/exposure config; non-fatal on failure
|
|
2510
|
+
try? targetDevice.lockForConfiguration()
|
|
2511
|
+
if targetDevice.isFocusModeSupported(.continuousAutoFocus) {
|
|
2512
|
+
targetDevice.focusMode = .continuousAutoFocus
|
|
2508
2513
|
}
|
|
2514
|
+
targetDevice.unlockForConfiguration()
|
|
2515
|
+
try? self.applyPreferredExposureMode(to: targetDevice)
|
|
2509
2516
|
} else {
|
|
2510
2517
|
throw CameraControllerError.invalidOperation
|
|
2511
2518
|
}
|
|
@@ -2564,6 +2571,7 @@ extension CameraController {
|
|
|
2564
2571
|
|
|
2565
2572
|
self.captureSession = nil
|
|
2566
2573
|
self.currentCameraPosition = nil
|
|
2574
|
+
self.preferredExposureMode = "CONTINUOUS"
|
|
2567
2575
|
|
|
2568
2576
|
// Reset output preparation status
|
|
2569
2577
|
self.outputsPrepared = false
|
|
@@ -2576,6 +2584,39 @@ extension CameraController {
|
|
|
2576
2584
|
|
|
2577
2585
|
// MARK: - Exposure Controls
|
|
2578
2586
|
|
|
2587
|
+
private func applyPreferredExposureMode(to device: AVCaptureDevice) throws {
|
|
2588
|
+
let mode = self.preferredExposureMode
|
|
2589
|
+
let desiredMode: AVCaptureDevice.ExposureMode
|
|
2590
|
+
switch mode {
|
|
2591
|
+
case "LOCK":
|
|
2592
|
+
desiredMode = .locked
|
|
2593
|
+
case "AUTO":
|
|
2594
|
+
desiredMode = .autoExpose
|
|
2595
|
+
case "CUSTOM":
|
|
2596
|
+
desiredMode = .custom
|
|
2597
|
+
default:
|
|
2598
|
+
desiredMode = .continuousAutoExposure
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
guard device.isExposureModeSupported(desiredMode) else {
|
|
2602
|
+
// Fall back to continuous when the preferred mode is unavailable on this lens.
|
|
2603
|
+
if device.isExposureModeSupported(.continuousAutoExposure) {
|
|
2604
|
+
try device.lockForConfiguration()
|
|
2605
|
+
device.exposureMode = .continuousAutoExposure
|
|
2606
|
+
device.setExposureTargetBias(0.0) { _ in }
|
|
2607
|
+
device.unlockForConfiguration()
|
|
2608
|
+
}
|
|
2609
|
+
return
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
try device.lockForConfiguration()
|
|
2613
|
+
device.exposureMode = desiredMode
|
|
2614
|
+
if desiredMode == .autoExpose || desiredMode == .continuousAutoExposure {
|
|
2615
|
+
device.setExposureTargetBias(0.0) { _ in }
|
|
2616
|
+
}
|
|
2617
|
+
device.unlockForConfiguration()
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2579
2620
|
func getExposureModes() throws -> [String] {
|
|
2580
2621
|
var currentCamera: AVCaptureDevice?
|
|
2581
2622
|
switch currentCameraPosition {
|
|
@@ -2610,22 +2651,12 @@ extension CameraController {
|
|
|
2610
2651
|
break
|
|
2611
2652
|
}
|
|
2612
2653
|
|
|
2613
|
-
guard
|
|
2654
|
+
guard currentCamera != nil else {
|
|
2614
2655
|
throw CameraControllerError.noCamerasAvailable
|
|
2615
2656
|
}
|
|
2616
2657
|
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
return "LOCK"
|
|
2620
|
-
case .autoExpose:
|
|
2621
|
-
return "AUTO"
|
|
2622
|
-
case .continuousAutoExposure:
|
|
2623
|
-
return "CONTINUOUS"
|
|
2624
|
-
case .custom:
|
|
2625
|
-
return "CUSTOM"
|
|
2626
|
-
@unknown default:
|
|
2627
|
-
return "CONTINUOUS"
|
|
2628
|
-
}
|
|
2658
|
+
// App preference, not device.exposureMode: one-shot `.autoExpose` ends as `.locked`.
|
|
2659
|
+
return self.preferredExposureMode
|
|
2629
2660
|
}
|
|
2630
2661
|
|
|
2631
2662
|
func setExposureMode(mode: String) throws {
|
|
@@ -2662,14 +2693,15 @@ extension CameraController {
|
|
|
2662
2693
|
throw CameraControllerError.invalidOperation
|
|
2663
2694
|
}
|
|
2664
2695
|
|
|
2696
|
+
switch normalized {
|
|
2697
|
+
case "LOCK", "AUTO", "CONTINUOUS", "CUSTOM":
|
|
2698
|
+
self.preferredExposureMode = normalized
|
|
2699
|
+
default:
|
|
2700
|
+
self.preferredExposureMode = "CONTINUOUS"
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2665
2703
|
do {
|
|
2666
|
-
try
|
|
2667
|
-
device.exposureMode = finalMode
|
|
2668
|
-
// Reset EV to 0 when switching to AUTO or CONTINUOUS
|
|
2669
|
-
if finalMode == .autoExpose || finalMode == .continuousAutoExposure {
|
|
2670
|
-
device.setExposureTargetBias(0.0) { _ in }
|
|
2671
|
-
}
|
|
2672
|
-
device.unlockForConfiguration()
|
|
2704
|
+
try self.applyPreferredExposureMode(to: device)
|
|
2673
2705
|
} catch {
|
|
2674
2706
|
throw CameraControllerError.invalidOperation
|
|
2675
2707
|
}
|
|
@@ -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.11.
|
|
38
|
+
private let pluginVersion: String = "8.11.2"
|
|
39
39
|
public let identifier = "CameraPreviewPlugin"
|
|
40
40
|
public let jsName = "CameraPreview"
|
|
41
41
|
public let pluginMethods: [CAPPluginMethod] = [
|