@capacitor-community/camera-preview 2.0.0 → 3.1.0

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,20 +11,20 @@ import UIKit
11
11
 
12
12
  class CameraController: NSObject {
13
13
  var captureSession: AVCaptureSession?
14
-
14
+
15
15
  var currentCameraPosition: CameraPosition?
16
-
16
+
17
17
  var frontCamera: AVCaptureDevice?
18
18
  var frontCameraInput: AVCaptureDeviceInput?
19
-
19
+
20
20
  var dataOutput: AVCaptureVideoDataOutput?
21
21
  var photoOutput: AVCapturePhotoOutput?
22
-
22
+
23
23
  var rearCamera: AVCaptureDevice?
24
24
  var rearCameraInput: AVCaptureDeviceInput?
25
-
25
+
26
26
  var previewLayer: AVCaptureVideoPreviewLayer?
27
-
27
+
28
28
  var flashMode = AVCaptureDevice.FlashMode.off
29
29
  var photoCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
30
30
 
@@ -34,10 +34,12 @@ class CameraController: NSObject {
34
34
 
35
35
  var audioDevice: AVCaptureDevice?
36
36
  var audioInput: AVCaptureDeviceInput?
37
+
38
+ var zoomFactor: CGFloat = 1.0
37
39
  }
38
40
 
39
41
  extension CameraController {
40
- func prepare(cameraPosition: String, completionHandler: @escaping (Error?) -> Void) {
42
+ func prepare(cameraPosition: String, disableAudio: Bool, completionHandler: @escaping (Error?) -> Void) {
41
43
  func createCaptureSession() {
42
44
  self.captureSession = AVCaptureSession()
43
45
  }
@@ -62,7 +64,9 @@ extension CameraController {
62
64
  camera.unlockForConfiguration()
63
65
  }
64
66
  }
65
- self.audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)
67
+ if disableAudio == false {
68
+ self.audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)
69
+ }
66
70
  }
67
71
 
68
72
  func configureDeviceInputs() throws {
@@ -80,20 +84,21 @@ extension CameraController {
80
84
  if let frontCamera = self.frontCamera {
81
85
  self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
82
86
 
83
- if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) }
84
- else { throw CameraControllerError.inputsAreInvalid }
87
+ if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) } else { throw CameraControllerError.inputsAreInvalid }
85
88
 
86
89
  self.currentCameraPosition = .front
87
90
  }
88
91
  } else { throw CameraControllerError.noCamerasAvailable }
89
92
 
90
93
  // Add audio input
91
- if let audioDevice = self.audioDevice {
92
- self.audioInput = try AVCaptureDeviceInput(device: audioDevice)
93
- if captureSession.canAddInput(self.audioInput!) {
94
- captureSession.addInput(self.audioInput!)
95
- } else {
96
- throw CameraControllerError.inputsAreInvalid
94
+ if disableAudio == false {
95
+ if let audioDevice = self.audioDevice {
96
+ self.audioInput = try AVCaptureDeviceInput(device: audioDevice)
97
+ if captureSession.canAddInput(self.audioInput!) {
98
+ captureSession.addInput(self.audioInput!)
99
+ } else {
100
+ throw CameraControllerError.inputsAreInvalid
101
+ }
97
102
  }
98
103
  }
99
104
  }
@@ -102,7 +107,7 @@ extension CameraController {
102
107
  guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
103
108
 
104
109
  self.photoOutput = AVCapturePhotoOutput()
105
- self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil)
110
+ self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
106
111
  self.photoOutput?.isHighResolutionCaptureEnabled = self.highResolutionOutput
107
112
  if captureSession.canAddOutput(self.photoOutput!) { captureSession.addOutput(self.photoOutput!) }
108
113
  captureSession.startRunning()
@@ -133,10 +138,8 @@ extension CameraController {
133
138
  try configureDeviceInputs()
134
139
  try configurePhotoOutput()
135
140
  try configureDataOutput()
136
- //try configureVideoOutput()
137
- }
138
-
139
- catch {
141
+ // try configureVideoOutput()
142
+ } catch {
140
143
  DispatchQueue.main.async {
141
144
  completionHandler(error)
142
145
  }
@@ -145,8 +148,6 @@ extension CameraController {
145
148
  }
146
149
 
147
150
  DispatchQueue.main.async {
148
- self.updateVideoOrientation()
149
-
150
151
  completionHandler(nil)
151
152
  }
152
153
  }
@@ -160,38 +161,46 @@ extension CameraController {
160
161
 
161
162
  view.layer.insertSublayer(self.previewLayer!, at: 0)
162
163
  self.previewLayer?.frame = view.frame
164
+
165
+ updateVideoOrientation()
166
+ }
167
+
168
+ func setupGestures(target: UIView, enableZoom: Bool) {
169
+ setupTapGesture(target: target, selector: #selector(handleTap(_:)), delegate: self)
170
+ if enableZoom {
171
+ setupPinchGesture(target: target, selector: #selector(handlePinch(_:)), delegate: self)
172
+ }
173
+ }
174
+
175
+ func setupTapGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
176
+ let tapGesture = UITapGestureRecognizer(target: self, action: selector)
177
+ tapGesture.delegate = delegate
178
+ target.addGestureRecognizer(tapGesture)
179
+ }
180
+
181
+ func setupPinchGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
182
+ let pinchGesture = UIPinchGestureRecognizer(target: self, action: selector)
183
+ pinchGesture.delegate = delegate
184
+ target.addGestureRecognizer(pinchGesture)
163
185
  }
164
186
 
165
187
  func updateVideoOrientation() {
166
188
  assert(Thread.isMainThread) // UIApplication.statusBarOrientation requires the main thread.
167
189
 
168
190
  let videoOrientation: AVCaptureVideoOrientation
169
- switch UIDevice.current.orientation {
191
+ switch UIApplication.shared.statusBarOrientation {
170
192
  case .portrait:
171
193
  videoOrientation = .portrait
172
194
  case .landscapeLeft:
173
- videoOrientation = .landscapeRight
174
- case .landscapeRight:
175
195
  videoOrientation = .landscapeLeft
196
+ case .landscapeRight:
197
+ videoOrientation = .landscapeRight
176
198
  case .portraitUpsideDown:
177
199
  videoOrientation = .portraitUpsideDown
178
- case .faceUp, .faceDown, .unknown:
200
+ case .unknown:
179
201
  fallthrough
180
202
  @unknown default:
181
- switch UIApplication.shared.statusBarOrientation {
182
- case .portrait:
183
- videoOrientation = .portrait
184
- case .landscapeLeft:
185
- videoOrientation = .landscapeLeft
186
- case .landscapeRight:
187
- videoOrientation = .landscapeRight
188
- case .portraitUpsideDown:
189
- videoOrientation = .portraitUpsideDown
190
- case .unknown:
191
- fallthrough
192
- @unknown default:
193
- videoOrientation = .portrait
194
- }
203
+ videoOrientation = .portrait
195
204
  }
196
205
 
197
206
  previewLayer?.connection?.videoOrientation = videoOrientation
@@ -206,7 +215,7 @@ extension CameraController {
206
215
  func switchToFrontCamera() throws {
207
216
 
208
217
  guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput),
209
- let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
218
+ let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
210
219
 
211
220
  self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
212
221
 
@@ -216,9 +225,7 @@ extension CameraController {
216
225
  captureSession.addInput(self.frontCameraInput!)
217
226
 
218
227
  self.currentCameraPosition = .front
219
- }
220
-
221
- else {
228
+ } else {
222
229
  throw CameraControllerError.invalidOperation
223
230
  }
224
231
  }
@@ -226,7 +233,7 @@ extension CameraController {
226
233
  func switchToRearCamera() throws {
227
234
 
228
235
  guard let frontCameraInput = self.frontCameraInput, captureSession.inputs.contains(frontCameraInput),
229
- let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
236
+ let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
230
237
 
231
238
  self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
232
239
 
@@ -236,9 +243,7 @@ extension CameraController {
236
243
  captureSession.addInput(self.rearCameraInput!)
237
244
 
238
245
  self.currentCameraPosition = .rear
239
- }
240
-
241
- else { throw CameraControllerError.invalidOperation }
246
+ } else { throw CameraControllerError.invalidOperation }
242
247
  }
243
248
 
244
249
  switch currentCameraPosition {
@@ -257,7 +262,7 @@ extension CameraController {
257
262
  let settings = AVCapturePhotoSettings()
258
263
 
259
264
  settings.flashMode = self.flashMode
260
- settings.isHighResolutionPhotoEnabled = self.highResolutionOutput;
265
+ settings.isHighResolutionPhotoEnabled = self.highResolutionOutput
261
266
 
262
267
  self.photoOutput?.capturePhoto(with: settings, delegate: self)
263
268
  self.photoCaptureCompletionBlock = completion
@@ -272,23 +277,23 @@ extension CameraController {
272
277
 
273
278
  self.sampleBufferCaptureCompletionBlock = completion
274
279
  }
275
-
280
+
276
281
  func getSupportedFlashModes() throws -> [String] {
277
282
  var currentCamera: AVCaptureDevice?
278
283
  switch currentCameraPosition {
279
- case .front:
280
- currentCamera = self.frontCamera!;
281
- case .rear:
282
- currentCamera = self.rearCamera!;
283
- default: break;
284
+ case .front:
285
+ currentCamera = self.frontCamera!
286
+ case .rear:
287
+ currentCamera = self.rearCamera!
288
+ default: break
284
289
  }
285
-
290
+
286
291
  guard
287
292
  let device = currentCamera
288
293
  else {
289
294
  throw CameraControllerError.noCamerasAvailable
290
295
  }
291
-
296
+
292
297
  var supportedFlashModesAsStrings: [String] = []
293
298
  if device.hasFlash {
294
299
  guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
@@ -298,13 +303,13 @@ extension CameraController {
298
303
  for flashMode in supportedFlashModes {
299
304
  var flashModeValue: String?
300
305
  switch flashMode {
301
- case AVCaptureDevice.FlashMode.off:
302
- flashModeValue = "off"
303
- case AVCaptureDevice.FlashMode.on:
304
- flashModeValue = "on"
305
- case AVCaptureDevice.FlashMode.auto:
306
- flashModeValue = "auto"
307
- default: break;
306
+ case AVCaptureDevice.FlashMode.off:
307
+ flashModeValue = "off"
308
+ case AVCaptureDevice.FlashMode.on:
309
+ flashModeValue = "on"
310
+ case AVCaptureDevice.FlashMode.auto:
311
+ flashModeValue = "auto"
312
+ default: break
308
313
  }
309
314
  if flashModeValue != nil {
310
315
  supportedFlashModesAsStrings.append(flashModeValue!)
@@ -315,38 +320,38 @@ extension CameraController {
315
320
  supportedFlashModesAsStrings.append("torch")
316
321
  }
317
322
  return supportedFlashModesAsStrings
318
-
323
+
319
324
  }
320
-
325
+
321
326
  func setFlashMode(flashMode: AVCaptureDevice.FlashMode) throws {
322
327
  var currentCamera: AVCaptureDevice?
323
328
  switch currentCameraPosition {
324
- case .front:
325
- currentCamera = self.frontCamera!;
326
- case .rear:
327
- currentCamera = self.rearCamera!;
328
- default: break;
329
+ case .front:
330
+ currentCamera = self.frontCamera!
331
+ case .rear:
332
+ currentCamera = self.rearCamera!
333
+ default: break
329
334
  }
330
-
335
+
331
336
  guard let device = currentCamera else {
332
337
  throw CameraControllerError.noCamerasAvailable
333
338
  }
334
-
339
+
335
340
  guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
336
341
  throw CameraControllerError.invalidOperation
337
342
  }
338
343
  if supportedFlashModes.contains(flashMode) {
339
344
  do {
340
345
  try device.lockForConfiguration()
341
-
342
- if(device.hasTorch && device.isTorchAvailable && device.torchMode == AVCaptureDevice.TorchMode.on) {
346
+
347
+ if device.hasTorch && device.isTorchAvailable && device.torchMode == AVCaptureDevice.TorchMode.on {
343
348
  device.torchMode = AVCaptureDevice.TorchMode.off
344
349
  }
345
350
  self.flashMode = flashMode
346
351
  let photoSettings = AVCapturePhotoSettings()
347
352
  photoSettings.flashMode = flashMode
348
353
  self.photoOutput?.photoSettingsForSceneMonitoring = photoSettings
349
-
354
+
350
355
  device.unlockForConfiguration()
351
356
  } catch {
352
357
  throw CameraControllerError.invalidOperation
@@ -355,17 +360,17 @@ extension CameraController {
355
360
  throw CameraControllerError.invalidOperation
356
361
  }
357
362
  }
358
-
363
+
359
364
  func setTorchMode() throws {
360
365
  var currentCamera: AVCaptureDevice?
361
366
  switch currentCameraPosition {
362
- case .front:
363
- currentCamera = self.frontCamera!;
364
- case .rear:
365
- currentCamera = self.rearCamera!;
366
- default: break;
367
+ case .front:
368
+ currentCamera = self.frontCamera!
369
+ case .rear:
370
+ currentCamera = self.rearCamera!
371
+ default: break
367
372
  }
368
-
373
+
369
374
  guard
370
375
  let device = currentCamera,
371
376
  device.hasTorch,
@@ -376,9 +381,9 @@ extension CameraController {
376
381
 
377
382
  do {
378
383
  try device.lockForConfiguration()
379
- if (device.isTorchModeSupported(AVCaptureDevice.TorchMode.on)) {
384
+ if device.isTorchModeSupported(AVCaptureDevice.TorchMode.on) {
380
385
  device.torchMode = AVCaptureDevice.TorchMode.on
381
- } else if (device.isTorchModeSupported(AVCaptureDevice.TorchMode.auto)) {
386
+ } else if device.isTorchModeSupported(AVCaptureDevice.TorchMode.auto) {
382
387
  device.torchMode = AVCaptureDevice.TorchMode.auto
383
388
  } else {
384
389
  device.torchMode = AVCaptureDevice.TorchMode.off
@@ -387,7 +392,7 @@ extension CameraController {
387
392
  } catch {
388
393
  throw CameraControllerError.invalidOperation
389
394
  }
390
-
395
+
391
396
  }
392
397
 
393
398
  func captureVideo(completion: @escaping (URL?, Error?) -> Void) {
@@ -401,10 +406,10 @@ extension CameraController {
401
406
  let finalIdentifier = String(randomIdentifier.prefix(8))
402
407
  let fileName="cpcp_video_"+finalIdentifier+".mp4"
403
408
 
404
- let fileUrl = path.appendingPathComponent(fileName)
409
+ let fileUrl = path.appendingPathComponent(fileName)
405
410
  try? FileManager.default.removeItem(at: fileUrl)
406
411
  /*videoOutput!.startRecording(to: fileUrl, recordingDelegate: self)
407
- self.videoRecordCompletionBlock = completion*/
412
+ self.videoRecordCompletionBlock = completion*/
408
413
  }
409
414
 
410
415
  func stopRecording(completion: @escaping (Error?) -> Void) {
@@ -412,21 +417,78 @@ extension CameraController {
412
417
  completion(CameraControllerError.captureSessionIsMissing)
413
418
  return
414
419
  }
415
- //self.videoOutput?.stopRecording()
420
+ // self.videoOutput?.stopRecording()
421
+ }
422
+ }
423
+
424
+ extension CameraController: UIGestureRecognizerDelegate {
425
+ func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
426
+ return true
427
+ }
428
+
429
+ @objc
430
+ func handleTap(_ tap: UITapGestureRecognizer) {
431
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
432
+
433
+ let point = tap.location(in: tap.view)
434
+ let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
435
+
436
+ do {
437
+ try device.lockForConfiguration()
438
+ defer { device.unlockForConfiguration() }
439
+
440
+ let focusMode = AVCaptureDevice.FocusMode.autoFocus
441
+ if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(focusMode) {
442
+ device.focusPointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
443
+ device.focusMode = focusMode
444
+ }
445
+
446
+ let exposureMode = AVCaptureDevice.ExposureMode.autoExpose
447
+ if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
448
+ device.exposurePointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
449
+ device.exposureMode = exposureMode
450
+ }
451
+ } catch {
452
+ debugPrint(error)
453
+ }
454
+ }
455
+
456
+ @objc
457
+ private func handlePinch(_ pinch: UIPinchGestureRecognizer) {
458
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
459
+
460
+ func minMaxZoom(_ factor: CGFloat) -> CGFloat { return max(1.0, min(factor, device.activeFormat.videoMaxZoomFactor)) }
461
+
462
+ func update(scale factor: CGFloat) {
463
+ do {
464
+ try device.lockForConfiguration()
465
+ defer { device.unlockForConfiguration() }
466
+
467
+ device.videoZoomFactor = factor
468
+ } catch {
469
+ debugPrint(error)
470
+ }
471
+ }
472
+
473
+ switch pinch.state {
474
+ case .began: fallthrough
475
+ case .changed:
476
+ let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
477
+ update(scale: newScaleFactor)
478
+ case .ended:
479
+ zoomFactor = device.videoZoomFactor
480
+ default: break
481
+ }
416
482
  }
417
483
  }
418
484
 
419
485
  extension CameraController: AVCapturePhotoCaptureDelegate {
420
486
  public func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
421
487
  resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
422
- if let error = error { self.photoCaptureCompletionBlock?(nil, error) }
423
-
424
- else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
425
- let image = UIImage(data: data) {
488
+ if let error = error { self.photoCaptureCompletionBlock?(nil, error) } else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
489
+ let image = UIImage(data: data) {
426
490
  self.photoCaptureCompletionBlock?(image.fixedOrientation(), nil)
427
- }
428
-
429
- else {
491
+ } else {
430
492
  self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
431
493
  }
432
494
  }
@@ -474,9 +536,6 @@ extension CameraController: AVCaptureVideoDataOutputSampleBufferDelegate {
474
536
  }
475
537
  }
476
538
 
477
-
478
-
479
-
480
539
  enum CameraControllerError: Swift.Error {
481
540
  case captureSessionAlreadyRunning
482
541
  case captureSessionIsMissing
@@ -514,21 +573,21 @@ extension CameraControllerError: LocalizedError {
514
573
  extension UIImage {
515
574
 
516
575
  func fixedOrientation() -> UIImage? {
517
-
576
+
518
577
  guard imageOrientation != UIImage.Orientation.up else {
519
- //This is default orientation, don't need to do anything
578
+ // This is default orientation, don't need to do anything
520
579
  return self.copy() as? UIImage
521
580
  }
522
-
581
+
523
582
  guard let cgImage = self.cgImage else {
524
- //CGImage is not available
583
+ // CGImage is not available
525
584
  return nil
526
585
  }
527
586
 
528
587
  guard let colorSpace = cgImage.colorSpace, let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
529
- return nil //Not able to create CGContext
588
+ return nil // Not able to create CGContext
530
589
  }
531
-
590
+
532
591
  var transform: CGAffineTransform = CGAffineTransform.identity
533
592
  switch imageOrientation {
534
593
  case .down, .downMirrored:
@@ -549,8 +608,8 @@ extension UIImage {
549
608
  case .up, .upMirrored:
550
609
  break
551
610
  }
552
-
553
- //Flip image one more time if needed to, this is to prevent flipped image
611
+
612
+ // Flip image one more time if needed to, this is to prevent flipped image
554
613
  switch imageOrientation {
555
614
  case .upMirrored, .downMirrored:
556
615
  transform.translatedBy(x: size.width, y: 0)
@@ -562,9 +621,9 @@ extension UIImage {
562
621
  case .up, .down, .left, .right:
563
622
  break
564
623
  }
565
-
624
+
566
625
  ctx.concatenate(transform)
567
-
626
+
568
627
  switch imageOrientation {
569
628
  case .left, .leftMirrored, .right, .rightMirrored:
570
629
  ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
@@ -580,9 +639,9 @@ extension UIImage {
580
639
  extension CameraController: AVCaptureFileOutputRecordingDelegate {
581
640
  func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
582
641
  /*if error == nil {
583
- self.videoRecordCompletionBlock?(outputFileURL, nil)
584
- } else {
585
- self.videoRecordCompletionBlock?(nil, error)
586
- }*/
642
+ self.videoRecordCompletionBlock?(outputFileURL, nil)
643
+ } else {
644
+ self.videoRecordCompletionBlock?(nil, error)
645
+ }*/
587
646
  }
588
647
  }