capacitor-camera-view 2.0.1 → 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,7 +114,8 @@ 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
|
|
|
@@ -123,18 +128,18 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
123
128
|
|
|
124
129
|
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
125
130
|
self?.captureSession.stopRunning()
|
|
126
|
-
}
|
|
127
131
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
+
}
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
completion?()
|
|
@@ -168,9 +173,11 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
168
173
|
|
|
169
174
|
// Ensure proper orientation
|
|
170
175
|
if let photoConnection = avPhotoOutput.connection(with: .video),
|
|
171
|
-
|
|
176
|
+
let previewConnection = videoPreviewLayer.connection
|
|
177
|
+
{
|
|
172
178
|
if photoConnection.isVideoOrientationSupported {
|
|
173
|
-
photoConnection.videoOrientation =
|
|
179
|
+
photoConnection.videoOrientation =
|
|
180
|
+
previewConnection.videoOrientation
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
|
|
@@ -181,7 +188,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
181
188
|
/// Capture a snapshot of the current camera view. This is faster than actually processing a
|
|
182
189
|
/// photo via capturePhoto
|
|
183
190
|
/// - Parameter completion: called with the captured UIImage or an error.
|
|
184
|
-
public func captureSnapshot(
|
|
191
|
+
public func captureSnapshot(
|
|
192
|
+
completion: @escaping (UIImage?, Error?) -> Void
|
|
193
|
+
) {
|
|
185
194
|
guard currentCameraDevice != nil else {
|
|
186
195
|
completion(nil, CameraError.cameraUnavailable)
|
|
187
196
|
return
|
|
@@ -194,24 +203,33 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
194
203
|
|
|
195
204
|
// Ensure proper orientation
|
|
196
205
|
if let videoConnection = avVideoDataOutput.connection(with: .video),
|
|
197
|
-
|
|
206
|
+
let previewConnection = videoPreviewLayer.connection
|
|
207
|
+
{
|
|
198
208
|
if videoConnection.isVideoOrientationSupported {
|
|
199
|
-
videoConnection.videoOrientation =
|
|
209
|
+
videoConnection.videoOrientation =
|
|
210
|
+
previewConnection.videoOrientation
|
|
200
211
|
}
|
|
201
212
|
}
|
|
202
213
|
|
|
203
214
|
// Create a serial queue for sample buffer processing
|
|
204
|
-
let sampleBufferQueue = DispatchQueue(
|
|
215
|
+
let sampleBufferQueue = DispatchQueue(
|
|
216
|
+
label: "com.michaelwolz.capacitorcameraview.snapshotQueue"
|
|
217
|
+
)
|
|
205
218
|
|
|
206
219
|
// Set the delegate for a single frame capture
|
|
207
220
|
snapshotCompletionHandler = completion
|
|
208
|
-
avVideoDataOutput.setSampleBufferDelegate(
|
|
221
|
+
avVideoDataOutput.setSampleBufferDelegate(
|
|
222
|
+
self,
|
|
223
|
+
queue: sampleBufferQueue
|
|
224
|
+
)
|
|
209
225
|
}
|
|
210
226
|
|
|
211
227
|
/// Flips the camera to the opposite position (front to back or back to front).
|
|
212
228
|
public func flipCamera() throws {
|
|
213
|
-
let currentPosition: AVCaptureDevice.Position =
|
|
214
|
-
|
|
229
|
+
let currentPosition: AVCaptureDevice.Position =
|
|
230
|
+
currentCameraDevice?.position ?? .back
|
|
231
|
+
let newPosition: AVCaptureDevice.Position =
|
|
232
|
+
currentPosition == .back ? .front : .back
|
|
215
233
|
|
|
216
234
|
let newCamera = try getCameraDevice(for: newPosition)
|
|
217
235
|
try setInput(with: newCamera)
|
|
@@ -222,7 +240,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
222
240
|
/// - Parameter mode: The desired flash mode (.on, .of, or .auto).
|
|
223
241
|
/// - Throws: An error if the flash mode cannot be set or is not supported.
|
|
224
242
|
public func setFlashMode(_ mode: AVCaptureDevice.FlashMode) throws {
|
|
225
|
-
guard let camera = currentCameraDevice else {
|
|
243
|
+
guard let camera = currentCameraDevice else {
|
|
244
|
+
throw CameraError.cameraUnavailable
|
|
245
|
+
}
|
|
226
246
|
guard camera.hasFlash else { throw CameraError.unsupportedFlashMode }
|
|
227
247
|
guard avPhotoOutput.supportedFlashModes.contains(mode)
|
|
228
248
|
else {
|
|
@@ -275,7 +295,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
275
295
|
/// - level: The torch intensity level (0.0 to 1.0)
|
|
276
296
|
/// - Throws: An error if the torch mode cannot be set or is not supported.
|
|
277
297
|
public func setTorchMode(enabled: Bool, level: Float = 1.0) throws {
|
|
278
|
-
guard let camera = currentCameraDevice else {
|
|
298
|
+
guard let camera = currentCameraDevice else {
|
|
299
|
+
throw CameraError.cameraUnavailable
|
|
300
|
+
}
|
|
279
301
|
guard camera.hasTorch else { throw CameraError.torchUnavailable }
|
|
280
302
|
|
|
281
303
|
do {
|
|
@@ -297,7 +319,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
297
319
|
/// because some devices report very high zoom factors that aren't useful.
|
|
298
320
|
///
|
|
299
321
|
/// - Returns: A tuple containing the minimum, maximum, and current zoom factors.
|
|
300
|
-
public func getSupportedZoomFactors() -> (
|
|
322
|
+
public func getSupportedZoomFactors() -> (
|
|
323
|
+
min: CGFloat, max: CGFloat, current: CGFloat
|
|
324
|
+
) {
|
|
301
325
|
guard let currentDevice = currentCameraDevice else {
|
|
302
326
|
return (
|
|
303
327
|
min: 1.0,
|
|
@@ -307,7 +331,10 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
307
331
|
}
|
|
308
332
|
|
|
309
333
|
let minZoomFactor = currentDevice.minAvailableVideoZoomFactor
|
|
310
|
-
let maxZoomFactor = min(
|
|
334
|
+
let maxZoomFactor = min(
|
|
335
|
+
currentDevice.activeFormat.videoMaxZoomFactor,
|
|
336
|
+
10.0
|
|
337
|
+
)
|
|
311
338
|
let currentZoomFactor = currentDevice.videoZoomFactor
|
|
312
339
|
|
|
313
340
|
return (
|
|
@@ -324,10 +351,15 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
324
351
|
/// - ramp: If enabled the zoom will be applied via ramp
|
|
325
352
|
/// - Throws: An error if the zoom factor cannot be set.
|
|
326
353
|
public func setZoomFactor(_ factor: CGFloat, ramp: Bool = true) throws {
|
|
327
|
-
guard let device = currentCameraDevice else {
|
|
354
|
+
guard let device = currentCameraDevice else {
|
|
355
|
+
throw CameraError.cameraUnavailable
|
|
356
|
+
}
|
|
328
357
|
|
|
329
358
|
let supportedZoomFactors = getSupportedZoomFactors()
|
|
330
|
-
guard
|
|
359
|
+
guard
|
|
360
|
+
factor >= supportedZoomFactors.min
|
|
361
|
+
&& factor <= supportedZoomFactors.max
|
|
362
|
+
else {
|
|
331
363
|
throw CameraError.zoomFactorOutOfRange
|
|
332
364
|
}
|
|
333
365
|
|
|
@@ -349,7 +381,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
349
381
|
///
|
|
350
382
|
/// - Parameters:
|
|
351
383
|
/// - configuration: The configuration object for the camera session.
|
|
352
|
-
private func initiateCaptureSession(
|
|
384
|
+
private func initiateCaptureSession(
|
|
385
|
+
configuration: CameraSessionConfiguration
|
|
386
|
+
) throws {
|
|
353
387
|
captureSession.beginConfiguration()
|
|
354
388
|
defer { captureSession.commitConfiguration() }
|
|
355
389
|
|
|
@@ -418,7 +452,6 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
418
452
|
}
|
|
419
453
|
}
|
|
420
454
|
|
|
421
|
-
|
|
422
455
|
/// Enables barcode detection by adding metadata output to the running session.
|
|
423
456
|
/// Somehow adding the metadata output with the session not being started yet
|
|
424
457
|
/// caused issues on some devices (iPad 7th Gen) where the session would just
|
|
@@ -468,19 +501,26 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
468
501
|
/// - position: The position of the camera device to get
|
|
469
502
|
/// - Returns: The camera device for the specified position
|
|
470
503
|
/// - Throws: An error if no camera device is found.
|
|
471
|
-
private func getCameraDevice(for position: AVCaptureDevice.Position?) throws
|
|
504
|
+
private func getCameraDevice(for position: AVCaptureDevice.Position?) throws
|
|
505
|
+
-> AVCaptureDevice
|
|
506
|
+
{
|
|
472
507
|
let preferredDevices = getPreferredCameraDevices()
|
|
473
508
|
|
|
474
509
|
// First try to get the best match based on the users preferred camera device types
|
|
475
|
-
if let match = preferredDevices.first(where: { $0.position == position }
|
|
510
|
+
if let match = preferredDevices.first(where: { $0.position == position }
|
|
511
|
+
) {
|
|
476
512
|
return match
|
|
477
513
|
}
|
|
478
514
|
|
|
479
515
|
// If we haven't found one we try to get a best match for the position by iterating all supported device types
|
|
480
516
|
// Only doing this when preferredCameraDeviceTypes size differs from SUPPORTED_CAMERA_DEVICE_TYPES, otherwise
|
|
481
517
|
// we don't have to initialize a new discovery session
|
|
482
|
-
if preferredCameraDeviceTypes.count
|
|
483
|
-
|
|
518
|
+
if preferredCameraDeviceTypes.count
|
|
519
|
+
< SUPPORTED_CAMERA_DEVICE_TYPES.count,
|
|
520
|
+
let match = getAvailableDevices().first(where: {
|
|
521
|
+
$0.position == position
|
|
522
|
+
})
|
|
523
|
+
{
|
|
484
524
|
return match
|
|
485
525
|
}
|
|
486
526
|
|
|
@@ -490,8 +530,12 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
490
530
|
}
|
|
491
531
|
|
|
492
532
|
// Log when we're falling back to a device with different position than requested
|
|
493
|
-
if let requestedPosition = position,
|
|
494
|
-
|
|
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
|
+
)
|
|
495
539
|
}
|
|
496
540
|
|
|
497
541
|
return device
|
|
@@ -503,8 +547,14 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
503
547
|
/// - deviceId: The unique identifier of the camera device to get
|
|
504
548
|
/// - Returns: The camera device for the specified position
|
|
505
549
|
/// - Throws: An error if no camera device is found.
|
|
506
|
-
private func getCameraDeviceById(_ deviceId: String) throws
|
|
507
|
-
|
|
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 {
|
|
508
558
|
throw CameraError.cameraUnavailable
|
|
509
559
|
}
|
|
510
560
|
return device
|
|
@@ -519,7 +569,10 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
519
569
|
/// - view: The view that will display the camera preview.
|
|
520
570
|
/// - completion: The completion handler after successfully adding the previewLayer to the provided view
|
|
521
571
|
/// - Throws: An error if the preview layer cannot be set up.
|
|
522
|
-
private func displayPreview(
|
|
572
|
+
private func displayPreview(
|
|
573
|
+
on view: UIView,
|
|
574
|
+
completion: @escaping (Error?) -> Void
|
|
575
|
+
) {
|
|
523
576
|
guard captureSession.isRunning else {
|
|
524
577
|
completion(CameraError.sessionNotRunning)
|
|
525
578
|
return
|
|
@@ -580,7 +633,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
580
633
|
try self.setZoomFactor(2.0, ramp: false)
|
|
581
634
|
} catch {
|
|
582
635
|
// Fail silently if we can't upgrade to the triple camera
|
|
583
|
-
print(
|
|
636
|
+
print(
|
|
637
|
+
"Failed to upgrade to triple camera: \(error.localizedDescription)"
|
|
638
|
+
)
|
|
584
639
|
}
|
|
585
640
|
|
|
586
641
|
self.captureSession.commitConfiguration()
|
|
@@ -612,7 +667,9 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
612
667
|
/// Removes the blur overlay with a fade out animation to have a smooth transition
|
|
613
668
|
/// - Parameter duration: The duration of the fade out animation
|
|
614
669
|
@MainActor
|
|
615
|
-
private func removeBlurOverlayWithAnimation(duration: TimeInterval = 0.3)
|
|
670
|
+
private func removeBlurOverlayWithAnimation(duration: TimeInterval = 0.3)
|
|
671
|
+
async
|
|
672
|
+
{
|
|
616
673
|
guard let blurEffectView = blurOverlayView else { return }
|
|
617
674
|
|
|
618
675
|
await withCheckedContinuation { continuation in
|
|
@@ -625,7 +682,8 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
625
682
|
blurEffectView.removeFromSuperview()
|
|
626
683
|
self.blurOverlayView = nil
|
|
627
684
|
continuation.resume()
|
|
628
|
-
}
|
|
685
|
+
}
|
|
686
|
+
)
|
|
629
687
|
}
|
|
630
688
|
}
|
|
631
689
|
|
|
@@ -644,11 +702,15 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
644
702
|
|
|
645
703
|
/// Updates the preview layer orientation based on the current device orientation.
|
|
646
704
|
private func updatePreviewOrientation() {
|
|
647
|
-
guard let connection = self.videoPreviewLayer.connection,
|
|
705
|
+
guard let connection = self.videoPreviewLayer.connection,
|
|
706
|
+
connection.isVideoOrientationSupported
|
|
707
|
+
else {
|
|
648
708
|
return
|
|
649
709
|
}
|
|
650
710
|
|
|
651
|
-
let interfaceOrientation =
|
|
711
|
+
let interfaceOrientation =
|
|
712
|
+
UIApplication.shared.windows.first?.windowScene?
|
|
713
|
+
.interfaceOrientation ?? .portrait
|
|
652
714
|
let videoOrientation: AVCaptureVideoOrientation
|
|
653
715
|
|
|
654
716
|
switch interfaceOrientation {
|
|
@@ -688,7 +750,8 @@ internal let SUPPORTED_CAMERA_DEVICE_TYPES: [AVCaptureDevice.DeviceType] = [
|
|
|
688
750
|
self,
|
|
689
751
|
selector: #selector(handleAppDidBecomeActive),
|
|
690
752
|
name: UIApplication.didBecomeActiveNotification,
|
|
691
|
-
object: nil
|
|
753
|
+
object: nil
|
|
754
|
+
)
|
|
692
755
|
}
|
|
693
756
|
|
|
694
757
|
/// Handles the app going to background by pausing the camera session.
|
package/package.json
CHANGED