capacitor-camera-view 2.0.0 → 2.0.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.
|
@@ -11,7 +11,7 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
11
11
|
.builtInDualCamera,
|
|
12
12
|
.builtInDualWideCamera,
|
|
13
13
|
.builtInTripleCamera,
|
|
14
|
-
.builtInTrueDepthCamera
|
|
14
|
+
.builtInTrueDepthCamera,
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
/// A camera implementation that handles camera session management and photo capture.
|
|
@@ -66,8 +66,12 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
66
66
|
webView: UIView,
|
|
67
67
|
completion: @escaping (Error?) -> Void
|
|
68
68
|
) {
|
|
69
|
-
if let preferredCameraDeviceTypes = configuration
|
|
70
|
-
|
|
69
|
+
if let preferredCameraDeviceTypes = configuration
|
|
70
|
+
.preferredCameraDeviceTypes
|
|
71
|
+
{
|
|
72
|
+
self.preferredCameraDeviceTypes = convertToNativeCameraTypes(
|
|
73
|
+
preferredCameraDeviceTypes
|
|
74
|
+
)
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
@@ -110,29 +114,35 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
110
114
|
await self.upgradeToTripleCameraIfAvailable()
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
|
-
}
|
|
117
|
+
}
|
|
118
|
+
)
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
/// Stops the current capture session.
|
|
118
|
-
public func stopSession() {
|
|
119
|
-
guard captureSession.isRunning else {
|
|
123
|
+
public func stopSession(completion: (() -> Void)? = nil) {
|
|
124
|
+
guard captureSession.isRunning else {
|
|
125
|
+
completion?()
|
|
126
|
+
return
|
|
127
|
+
}
|
|
120
128
|
|
|
121
129
|
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
122
130
|
self?.captureSession.stopRunning()
|
|
123
|
-
}
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
DispatchQueue.main.async { [weak self] in
|
|
133
|
+
guard let self = self else { return }
|
|
134
|
+
self.videoPreviewLayer.removeFromSuperlayer()
|
|
135
|
+
self.webView?.isOpaque = true
|
|
136
|
+
self.webView?.backgroundColor = nil
|
|
137
|
+
self.webView = nil
|
|
138
|
+
|
|
139
|
+
if let blurOverlayView = self.blurOverlayView {
|
|
140
|
+
blurOverlayView.removeFromSuperview()
|
|
141
|
+
self.blurOverlayView = nil
|
|
142
|
+
}
|
|
135
143
|
}
|
|
144
|
+
|
|
145
|
+
completion?()
|
|
136
146
|
}
|
|
137
147
|
}
|
|
138
148
|
|
|
@@ -163,9 +173,11 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
163
173
|
|
|
164
174
|
// Ensure proper orientation
|
|
165
175
|
if let photoConnection = avPhotoOutput.connection(with: .video),
|
|
166
|
-
|
|
176
|
+
let previewConnection = videoPreviewLayer.connection
|
|
177
|
+
{
|
|
167
178
|
if photoConnection.isVideoOrientationSupported {
|
|
168
|
-
photoConnection.videoOrientation =
|
|
179
|
+
photoConnection.videoOrientation =
|
|
180
|
+
previewConnection.videoOrientation
|
|
169
181
|
}
|
|
170
182
|
}
|
|
171
183
|
|
|
@@ -176,7 +188,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
176
188
|
/// Capture a snapshot of the current camera view. This is faster than actually processing a
|
|
177
189
|
/// photo via capturePhoto
|
|
178
190
|
/// - Parameter completion: called with the captured UIImage or an error.
|
|
179
|
-
public func captureSnapshot(
|
|
191
|
+
public func captureSnapshot(
|
|
192
|
+
completion: @escaping (UIImage?, Error?) -> Void
|
|
193
|
+
) {
|
|
180
194
|
guard currentCameraDevice != nil else {
|
|
181
195
|
completion(nil, CameraError.cameraUnavailable)
|
|
182
196
|
return
|
|
@@ -189,24 +203,33 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
189
203
|
|
|
190
204
|
// Ensure proper orientation
|
|
191
205
|
if let videoConnection = avVideoDataOutput.connection(with: .video),
|
|
192
|
-
|
|
206
|
+
let previewConnection = videoPreviewLayer.connection
|
|
207
|
+
{
|
|
193
208
|
if videoConnection.isVideoOrientationSupported {
|
|
194
|
-
videoConnection.videoOrientation =
|
|
209
|
+
videoConnection.videoOrientation =
|
|
210
|
+
previewConnection.videoOrientation
|
|
195
211
|
}
|
|
196
212
|
}
|
|
197
213
|
|
|
198
214
|
// Create a serial queue for sample buffer processing
|
|
199
|
-
let sampleBufferQueue = DispatchQueue(
|
|
215
|
+
let sampleBufferQueue = DispatchQueue(
|
|
216
|
+
label: "com.michaelwolz.capacitorcameraview.snapshotQueue"
|
|
217
|
+
)
|
|
200
218
|
|
|
201
219
|
// Set the delegate for a single frame capture
|
|
202
220
|
snapshotCompletionHandler = completion
|
|
203
|
-
avVideoDataOutput.setSampleBufferDelegate(
|
|
221
|
+
avVideoDataOutput.setSampleBufferDelegate(
|
|
222
|
+
self,
|
|
223
|
+
queue: sampleBufferQueue
|
|
224
|
+
)
|
|
204
225
|
}
|
|
205
226
|
|
|
206
227
|
/// Flips the camera to the opposite position (front to back or back to front).
|
|
207
228
|
public func flipCamera() throws {
|
|
208
|
-
let currentPosition: AVCaptureDevice.Position =
|
|
209
|
-
|
|
229
|
+
let currentPosition: AVCaptureDevice.Position =
|
|
230
|
+
currentCameraDevice?.position ?? .back
|
|
231
|
+
let newPosition: AVCaptureDevice.Position =
|
|
232
|
+
currentPosition == .back ? .front : .back
|
|
210
233
|
|
|
211
234
|
let newCamera = try getCameraDevice(for: newPosition)
|
|
212
235
|
try setInput(with: newCamera)
|
|
@@ -217,7 +240,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
217
240
|
/// - Parameter mode: The desired flash mode (.on, .of, or .auto).
|
|
218
241
|
/// - Throws: An error if the flash mode cannot be set or is not supported.
|
|
219
242
|
public func setFlashMode(_ mode: AVCaptureDevice.FlashMode) throws {
|
|
220
|
-
guard let camera = currentCameraDevice else {
|
|
243
|
+
guard let camera = currentCameraDevice else {
|
|
244
|
+
throw CameraError.cameraUnavailable
|
|
245
|
+
}
|
|
221
246
|
guard camera.hasFlash else { throw CameraError.unsupportedFlashMode }
|
|
222
247
|
guard avPhotoOutput.supportedFlashModes.contains(mode)
|
|
223
248
|
else {
|
|
@@ -270,7 +295,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
270
295
|
/// - level: The torch intensity level (0.0 to 1.0)
|
|
271
296
|
/// - Throws: An error if the torch mode cannot be set or is not supported.
|
|
272
297
|
public func setTorchMode(enabled: Bool, level: Float = 1.0) throws {
|
|
273
|
-
guard let camera = currentCameraDevice else {
|
|
298
|
+
guard let camera = currentCameraDevice else {
|
|
299
|
+
throw CameraError.cameraUnavailable
|
|
300
|
+
}
|
|
274
301
|
guard camera.hasTorch else { throw CameraError.torchUnavailable }
|
|
275
302
|
|
|
276
303
|
do {
|
|
@@ -292,7 +319,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
292
319
|
/// because some devices report very high zoom factors that aren't useful.
|
|
293
320
|
///
|
|
294
321
|
/// - Returns: A tuple containing the minimum, maximum, and current zoom factors.
|
|
295
|
-
public func getSupportedZoomFactors() -> (
|
|
322
|
+
public func getSupportedZoomFactors() -> (
|
|
323
|
+
min: CGFloat, max: CGFloat, current: CGFloat
|
|
324
|
+
) {
|
|
296
325
|
guard let currentDevice = currentCameraDevice else {
|
|
297
326
|
return (
|
|
298
327
|
min: 1.0,
|
|
@@ -302,7 +331,10 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
302
331
|
}
|
|
303
332
|
|
|
304
333
|
let minZoomFactor = currentDevice.minAvailableVideoZoomFactor
|
|
305
|
-
let maxZoomFactor = min(
|
|
334
|
+
let maxZoomFactor = min(
|
|
335
|
+
currentDevice.activeFormat.videoMaxZoomFactor,
|
|
336
|
+
10.0
|
|
337
|
+
)
|
|
306
338
|
let currentZoomFactor = currentDevice.videoZoomFactor
|
|
307
339
|
|
|
308
340
|
return (
|
|
@@ -319,10 +351,15 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
319
351
|
/// - ramp: If enabled the zoom will be applied via ramp
|
|
320
352
|
/// - Throws: An error if the zoom factor cannot be set.
|
|
321
353
|
public func setZoomFactor(_ factor: CGFloat, ramp: Bool = true) throws {
|
|
322
|
-
guard let device = currentCameraDevice else {
|
|
354
|
+
guard let device = currentCameraDevice else {
|
|
355
|
+
throw CameraError.cameraUnavailable
|
|
356
|
+
}
|
|
323
357
|
|
|
324
358
|
let supportedZoomFactors = getSupportedZoomFactors()
|
|
325
|
-
guard
|
|
359
|
+
guard
|
|
360
|
+
factor >= supportedZoomFactors.min
|
|
361
|
+
&& factor <= supportedZoomFactors.max
|
|
362
|
+
else {
|
|
326
363
|
throw CameraError.zoomFactorOutOfRange
|
|
327
364
|
}
|
|
328
365
|
|
|
@@ -344,7 +381,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
344
381
|
///
|
|
345
382
|
/// - Parameters:
|
|
346
383
|
/// - configuration: The configuration object for the camera session.
|
|
347
|
-
private func initiateCaptureSession(
|
|
384
|
+
private func initiateCaptureSession(
|
|
385
|
+
configuration: CameraSessionConfiguration
|
|
386
|
+
) throws {
|
|
348
387
|
captureSession.beginConfiguration()
|
|
349
388
|
defer { captureSession.commitConfiguration() }
|
|
350
389
|
|
|
@@ -413,7 +452,6 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
413
452
|
}
|
|
414
453
|
}
|
|
415
454
|
|
|
416
|
-
|
|
417
455
|
/// Enables barcode detection by adding metadata output to the running session.
|
|
418
456
|
/// Somehow adding the metadata output with the session not being started yet
|
|
419
457
|
/// caused issues on some devices (iPad 7th Gen) where the session would just
|
|
@@ -463,19 +501,26 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
463
501
|
/// - position: The position of the camera device to get
|
|
464
502
|
/// - Returns: The camera device for the specified position
|
|
465
503
|
/// - Throws: An error if no camera device is found.
|
|
466
|
-
private func getCameraDevice(for position: AVCaptureDevice.Position?) throws
|
|
504
|
+
private func getCameraDevice(for position: AVCaptureDevice.Position?) throws
|
|
505
|
+
-> AVCaptureDevice
|
|
506
|
+
{
|
|
467
507
|
let preferredDevices = getPreferredCameraDevices()
|
|
468
508
|
|
|
469
509
|
// First try to get the best match based on the users preferred camera device types
|
|
470
|
-
if let match = preferredDevices.first(where: { $0.position == position }
|
|
510
|
+
if let match = preferredDevices.first(where: { $0.position == position }
|
|
511
|
+
) {
|
|
471
512
|
return match
|
|
472
513
|
}
|
|
473
514
|
|
|
474
515
|
// If we haven't found one we try to get a best match for the position by iterating all supported device types
|
|
475
516
|
// Only doing this when preferredCameraDeviceTypes size differs from SUPPORTED_CAMERA_DEVICE_TYPES, otherwise
|
|
476
517
|
// we don't have to initialize a new discovery session
|
|
477
|
-
if preferredCameraDeviceTypes.count
|
|
478
|
-
|
|
518
|
+
if preferredCameraDeviceTypes.count
|
|
519
|
+
< SUPPORTED_CAMERA_DEVICE_TYPES.count,
|
|
520
|
+
let match = getAvailableDevices().first(where: {
|
|
521
|
+
$0.position == position
|
|
522
|
+
})
|
|
523
|
+
{
|
|
479
524
|
return match
|
|
480
525
|
}
|
|
481
526
|
|
|
@@ -485,8 +530,12 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
485
530
|
}
|
|
486
531
|
|
|
487
532
|
// Log when we're falling back to a device with different position than requested
|
|
488
|
-
if let requestedPosition = position,
|
|
489
|
-
|
|
533
|
+
if let requestedPosition = position,
|
|
534
|
+
device.position != requestedPosition
|
|
535
|
+
{
|
|
536
|
+
print(
|
|
537
|
+
"Warning: Falling back to camera at position \(device.position) when \(requestedPosition) was requested"
|
|
538
|
+
)
|
|
490
539
|
}
|
|
491
540
|
|
|
492
541
|
return device
|
|
@@ -498,8 +547,14 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
498
547
|
/// - deviceId: The unique identifier of the camera device to get
|
|
499
548
|
/// - Returns: The camera device for the specified position
|
|
500
549
|
/// - Throws: An error if no camera device is found.
|
|
501
|
-
private func getCameraDeviceById(_ deviceId: String) throws
|
|
502
|
-
|
|
550
|
+
private func getCameraDeviceById(_ deviceId: String) throws
|
|
551
|
+
-> AVCaptureDevice
|
|
552
|
+
{
|
|
553
|
+
guard
|
|
554
|
+
let device = getAvailableDevices().first(where: {
|
|
555
|
+
$0.uniqueID == deviceId
|
|
556
|
+
})
|
|
557
|
+
else {
|
|
503
558
|
throw CameraError.cameraUnavailable
|
|
504
559
|
}
|
|
505
560
|
return device
|
|
@@ -514,7 +569,10 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
514
569
|
/// - view: The view that will display the camera preview.
|
|
515
570
|
/// - completion: The completion handler after successfully adding the previewLayer to the provided view
|
|
516
571
|
/// - Throws: An error if the preview layer cannot be set up.
|
|
517
|
-
private func displayPreview(
|
|
572
|
+
private func displayPreview(
|
|
573
|
+
on view: UIView,
|
|
574
|
+
completion: @escaping (Error?) -> Void
|
|
575
|
+
) {
|
|
518
576
|
guard captureSession.isRunning else {
|
|
519
577
|
completion(CameraError.sessionNotRunning)
|
|
520
578
|
return
|
|
@@ -575,7 +633,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
575
633
|
try self.setZoomFactor(2.0, ramp: false)
|
|
576
634
|
} catch {
|
|
577
635
|
// Fail silently if we can't upgrade to the triple camera
|
|
578
|
-
print(
|
|
636
|
+
print(
|
|
637
|
+
"Failed to upgrade to triple camera: \(error.localizedDescription)"
|
|
638
|
+
)
|
|
579
639
|
}
|
|
580
640
|
|
|
581
641
|
self.captureSession.commitConfiguration()
|
|
@@ -607,7 +667,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
607
667
|
/// Removes the blur overlay with a fade out animation to have a smooth transition
|
|
608
668
|
/// - Parameter duration: The duration of the fade out animation
|
|
609
669
|
@MainActor
|
|
610
|
-
private func removeBlurOverlayWithAnimation(duration: TimeInterval = 0.3)
|
|
670
|
+
private func removeBlurOverlayWithAnimation(duration: TimeInterval = 0.3)
|
|
671
|
+
async
|
|
672
|
+
{
|
|
611
673
|
guard let blurEffectView = blurOverlayView else { return }
|
|
612
674
|
|
|
613
675
|
await withCheckedContinuation { continuation in
|
|
@@ -620,7 +682,8 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
620
682
|
blurEffectView.removeFromSuperview()
|
|
621
683
|
self.blurOverlayView = nil
|
|
622
684
|
continuation.resume()
|
|
623
|
-
}
|
|
685
|
+
}
|
|
686
|
+
)
|
|
624
687
|
}
|
|
625
688
|
}
|
|
626
689
|
|
|
@@ -639,11 +702,15 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
639
702
|
|
|
640
703
|
/// Updates the preview layer orientation based on the current device orientation.
|
|
641
704
|
private func updatePreviewOrientation() {
|
|
642
|
-
guard let connection = self.videoPreviewLayer.connection,
|
|
705
|
+
guard let connection = self.videoPreviewLayer.connection,
|
|
706
|
+
connection.isVideoOrientationSupported
|
|
707
|
+
else {
|
|
643
708
|
return
|
|
644
709
|
}
|
|
645
710
|
|
|
646
|
-
let interfaceOrientation =
|
|
711
|
+
let interfaceOrientation =
|
|
712
|
+
UIApplication.shared.windows.first?.windowScene?
|
|
713
|
+
.interfaceOrientation ?? .portrait
|
|
647
714
|
let videoOrientation: AVCaptureVideoOrientation
|
|
648
715
|
|
|
649
716
|
switch interfaceOrientation {
|
|
@@ -683,7 +750,8 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
683
750
|
self,
|
|
684
751
|
selector: #selector(handleAppDidBecomeActive),
|
|
685
752
|
name: UIApplication.didBecomeActiveNotification,
|
|
686
|
-
object: nil
|
|
753
|
+
object: nil
|
|
754
|
+
)
|
|
687
755
|
}
|
|
688
756
|
|
|
689
757
|
/// Handles the app going to background by pausing the camera session.
|
|
@@ -95,8 +95,9 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
@objc func stop(_ call: CAPPluginCall) {
|
|
98
|
-
implementation.stopSession
|
|
99
|
-
|
|
98
|
+
implementation.stopSession {
|
|
99
|
+
call.resolve()
|
|
100
|
+
}
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
@objc func isRunning(_ call: CAPPluginCall) {
|
|
@@ -316,7 +317,7 @@ public class CameraViewPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
let level = call.getFloat("level") ?? 1.0
|
|
319
|
-
|
|
320
|
+
|
|
320
321
|
guard level >= 0.0 && level <= 1.0 else {
|
|
321
322
|
call.reject("Level must be between 0.0 and 1.0")
|
|
322
323
|
return
|
package/package.json
CHANGED