capacitor-community-multilens-camerapreview 5.0.1 → 6.0.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.
Files changed (37) hide show
  1. package/CapacitorCommunityMultilensCamerapreview.podspec +17 -17
  2. package/README.md +16 -16
  3. package/android/build.gradle +57 -55
  4. package/android/src/main/AndroidManifest.xml +4 -4
  5. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraActivity.java +1005 -1008
  6. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +543 -544
  7. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomSurfaceView.java +23 -23
  8. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomTextureView.java +29 -29
  9. package/android/src/main/java/com/ahm/capacitor/camera/preview/Preview.java +386 -386
  10. package/android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java +24 -24
  11. package/android/src/main/res/layout/bridge_layout_main.xml +15 -15
  12. package/android/src/main/res/layout/camera_activity.xml +68 -68
  13. package/android/src/main/res/values/camera_ids.xml +4 -4
  14. package/android/src/main/res/values/camera_theme.xml +9 -9
  15. package/android/src/main/res/values/colors.xml +3 -3
  16. package/android/src/main/res/values/strings.xml +3 -3
  17. package/android/src/main/res/values/styles.xml +3 -3
  18. package/dist/esm/definitions.d.ts +82 -80
  19. package/dist/esm/definitions.js +1 -1
  20. package/dist/esm/definitions.js.map +1 -1
  21. package/dist/esm/index.d.ts +4 -4
  22. package/dist/esm/index.js +6 -6
  23. package/dist/esm/index.js.map +1 -1
  24. package/dist/esm/web.d.ts +30 -28
  25. package/dist/esm/web.js +146 -155
  26. package/dist/esm/web.js.map +1 -1
  27. package/dist/plugin.cjs.js +146 -150
  28. package/dist/plugin.cjs.js.map +1 -1
  29. package/dist/plugin.js +147 -151
  30. package/dist/plugin.js.map +1 -1
  31. package/ios/Plugin/CameraController.swift +732 -733
  32. package/ios/Plugin/Info.plist +24 -24
  33. package/ios/Plugin/Plugin.h +10 -10
  34. package/ios/Plugin/Plugin.m +18 -18
  35. package/ios/Plugin/Plugin.swift +309 -308
  36. package/package.json +85 -78
  37. package/dist/docs.json +0 -408
@@ -1,733 +1,732 @@
1
- //
2
- // CameraController.swift
3
- // Plugin
4
- //
5
- // Created by Ariel Hernandez Musa on 7/14/19.
6
- // Copyright © 2019 Max Lynch. All rights reserved.
7
- //
8
-
9
- import AVFoundation
10
- import UIKit
11
-
12
- class CameraController: NSObject {
13
- var captureSession: AVCaptureSession?
14
-
15
- var currentCameraPosition: CameraPosition?
16
-
17
- var frontCamera: AVCaptureDevice?
18
- var frontCameraInput: AVCaptureDeviceInput?
19
-
20
- var dataOutput: AVCaptureVideoDataOutput?
21
- var photoOutput: AVCapturePhotoOutput?
22
-
23
- var rearCamera: AVCaptureDevice?
24
- var rearCameraInput: AVCaptureDeviceInput?
25
-
26
- var previewLayer: AVCaptureVideoPreviewLayer?
27
-
28
- var flashMode = AVCaptureDevice.FlashMode.off
29
- var photoCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
30
-
31
- var sampleBufferCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
32
-
33
- var highResolutionOutput: Bool = false
34
-
35
- var audioDevice: AVCaptureDevice?
36
- var audioInput: AVCaptureDeviceInput?
37
-
38
- var zoomFactor: CGFloat = 1.0
39
- }
40
- extension CameraController {
41
- func prepare(cameraPosition: String, zoomFactor: String, disableAudio: Bool, completionHandler: @escaping (Error?) -> Void) {
42
-
43
- func createCaptureSession() {
44
- self.captureSession = AVCaptureSession()
45
- }
46
-
47
- func configureCaptureDevices() throws {
48
- var session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
49
- if(zoomFactor == "ultra"){
50
- session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera], mediaType: AVMediaType.video, position: .unspecified)
51
- } else if(zoomFactor == "wide"){
52
- session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
53
- } else if(zoomFactor == "tele"){
54
- session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
55
- }
56
- let cameras = session.devices.compactMap { $0 }
57
- guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
58
-
59
- for camera in cameras {
60
- if camera.position == .front {
61
- self.frontCamera = camera
62
- }
63
-
64
- if camera.position == .back {
65
- self.rearCamera = camera
66
-
67
- try camera.lockForConfiguration()
68
- if camera.isFocusModeSupported(.continuousAutoFocus){
69
- camera.focusMode = .continuousAutoFocus
70
- }
71
- camera.unlockForConfiguration()
72
- }
73
- }
74
- if disableAudio == false {
75
- self.audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)
76
- }
77
- }
78
-
79
- func configureDeviceInputs() throws {
80
- guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
81
-
82
- if cameraPosition == "rear" {
83
- if let rearCamera = self.rearCamera {
84
- self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
85
-
86
- if captureSession.canAddInput(self.rearCameraInput!) { captureSession.addInput(self.rearCameraInput!) }
87
-
88
- self.currentCameraPosition = .rear
89
- }
90
- } else if cameraPosition == "front" {
91
- if let frontCamera = self.frontCamera {
92
- self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
93
-
94
- if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) } else { throw CameraControllerError.inputsAreInvalid }
95
-
96
- self.currentCameraPosition = .front
97
- }
98
- } else { throw CameraControllerError.noCamerasAvailable }
99
-
100
- // Add audio input
101
- if disableAudio == false {
102
- if let audioDevice = self.audioDevice {
103
- self.audioInput = try AVCaptureDeviceInput(device: audioDevice)
104
- if captureSession.canAddInput(self.audioInput!) {
105
- captureSession.addInput(self.audioInput!)
106
- } else {
107
- throw CameraControllerError.inputsAreInvalid
108
- }
109
- }
110
- }
111
- }
112
-
113
- func configurePhotoOutput() throws {
114
- guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
115
-
116
- self.photoOutput = AVCapturePhotoOutput()
117
- self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
118
- self.photoOutput?.isHighResolutionCaptureEnabled = self.highResolutionOutput
119
- if captureSession.canAddOutput(self.photoOutput!) { captureSession.addOutput(self.photoOutput!) }
120
- captureSession.startRunning()
121
- }
122
-
123
- func configureDataOutput() throws {
124
- guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
125
-
126
- self.dataOutput = AVCaptureVideoDataOutput()
127
- self.dataOutput?.videoSettings = [
128
- (kCVPixelBufferPixelFormatTypeKey as String): NSNumber(value: kCVPixelFormatType_32BGRA as UInt32)
129
- ]
130
- self.dataOutput?.alwaysDiscardsLateVideoFrames = true
131
- if captureSession.canAddOutput(self.dataOutput!) {
132
- captureSession.addOutput(self.dataOutput!)
133
- }
134
-
135
- captureSession.commitConfiguration()
136
-
137
- let queue = DispatchQueue(label: "DataOutput", attributes: [])
138
- self.dataOutput?.setSampleBufferDelegate(self, queue: queue)
139
- }
140
-
141
- DispatchQueue(label: "prepare").async {
142
- do {
143
- createCaptureSession()
144
- try configureCaptureDevices()
145
- try configureDeviceInputs()
146
- try configurePhotoOutput()
147
- try configureDataOutput()
148
- // try configureVideoOutput()
149
- } catch {
150
- DispatchQueue.main.async {
151
- completionHandler(error)
152
- }
153
-
154
- return
155
- }
156
-
157
- DispatchQueue.main.async {
158
- completionHandler(nil)
159
- }
160
- }
161
- }
162
-
163
- func displayPreview(on view: UIView) throws {
164
- guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
165
-
166
- self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
167
- self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
168
-
169
- view.layer.insertSublayer(self.previewLayer!, at: 0)
170
- self.previewLayer?.frame = view.frame
171
-
172
- updateVideoOrientation()
173
- }
174
-
175
- func setupGestures(target: UIView, enableZoom: Bool) {
176
- setupTapGesture(target: target, selector: #selector(handleTap(_:)), delegate: self)
177
- if enableZoom {
178
- setupPinchGesture(target: target, selector: #selector(handlePinch(_:)), delegate: self)
179
- }
180
- }
181
-
182
- func setupTapGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
183
- let tapGesture = UITapGestureRecognizer(target: self, action: selector)
184
- tapGesture.delegate = delegate
185
- target.addGestureRecognizer(tapGesture)
186
- }
187
-
188
- func setupPinchGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
189
- let pinchGesture = UIPinchGestureRecognizer(target: self, action: selector)
190
- pinchGesture.delegate = delegate
191
- target.addGestureRecognizer(pinchGesture)
192
- }
193
-
194
- func updateVideoOrientation() {
195
- assert(Thread.isMainThread) // UIApplication.statusBarOrientation requires the main thread.
196
-
197
- let videoOrientation: AVCaptureVideoOrientation
198
- switch UIApplication.shared.statusBarOrientation {
199
- case .portrait:
200
- videoOrientation = .portrait
201
- case .landscapeLeft:
202
- videoOrientation = .landscapeLeft
203
- case .landscapeRight:
204
- videoOrientation = .landscapeRight
205
- case .portraitUpsideDown:
206
- videoOrientation = .portraitUpsideDown
207
- case .unknown:
208
- fallthrough
209
- @unknown default:
210
- videoOrientation = .portrait
211
- }
212
-
213
- previewLayer?.connection?.videoOrientation = videoOrientation
214
- dataOutput?.connections.forEach { $0.videoOrientation = videoOrientation }
215
- photoOutput?.connections.forEach { $0.videoOrientation = videoOrientation }
216
- }
217
-
218
-
219
- func switchCameras() throws {
220
- guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
221
-
222
- captureSession.beginConfiguration()
223
-
224
- func switchToFrontCamera() throws {
225
-
226
- guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput),
227
- let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
228
-
229
- self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
230
-
231
- captureSession.removeInput(rearCameraInput)
232
-
233
- if captureSession.canAddInput(self.frontCameraInput!) {
234
- captureSession.addInput(self.frontCameraInput!)
235
-
236
- self.currentCameraPosition = .front
237
- } else {
238
- throw CameraControllerError.invalidOperation
239
- }
240
- }
241
-
242
- func switchToRearCamera() throws {
243
-
244
- guard let frontCameraInput = self.frontCameraInput, captureSession.inputs.contains(frontCameraInput),
245
- let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
246
-
247
- self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
248
-
249
- captureSession.removeInput(frontCameraInput)
250
-
251
- if captureSession.canAddInput(self.rearCameraInput!) {
252
- captureSession.addInput(self.rearCameraInput!)
253
-
254
- self.currentCameraPosition = .rear
255
- } else { throw CameraControllerError.invalidOperation }
256
- }
257
-
258
- switch currentCameraPosition {
259
- case .front:
260
- try switchToRearCamera()
261
-
262
- case .rear:
263
- try switchToFrontCamera()
264
- }
265
-
266
- captureSession.commitConfiguration()
267
- }
268
- func setZoom(lens: String) throws {
269
- print(lens)
270
- guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
271
-
272
- captureSession.beginConfiguration()
273
-
274
- guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput) else { throw CameraControllerError.invalidOperation }
275
-
276
- var session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
277
- if(lens == "ultra"){
278
- session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera], mediaType: AVMediaType.video, position: .unspecified)
279
- } else if(lens == "wide"){
280
- session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
281
- } else if(lens == "tele"){
282
- session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
283
- }
284
- let cameras = session.devices.compactMap { $0 }
285
- guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
286
-
287
- for camera in cameras {
288
- if camera.position == .front {
289
- self.frontCamera = camera
290
- }
291
-
292
- if camera.position == .back {
293
- self.rearCamera = camera
294
-
295
- try camera.lockForConfiguration()
296
- if camera.isFocusModeSupported(.continuousAutoFocus){
297
- camera.focusMode = .continuousAutoFocus
298
- }
299
- camera.unlockForConfiguration()
300
- }
301
- }
302
-
303
- if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
304
- for input in inputs {
305
- captureSession.removeInput(input)
306
- }
307
- }
308
- captureSession.commitConfiguration()
309
-
310
- self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera!)
311
-
312
- //captureSession.removeInput(frontCameraInput)
313
-
314
- if captureSession.canAddInput(self.rearCameraInput!) {
315
- captureSession.addInput(self.rearCameraInput!)
316
- self.currentCameraPosition = .rear
317
- }
318
-
319
- do {
320
- guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
321
-
322
- try device.lockForConfiguration()
323
- defer { device.unlockForConfiguration() }
324
-
325
- device.videoZoomFactor = 1.0
326
- zoomFactor = device.videoZoomFactor
327
-
328
- } catch {
329
- debugPrint(error)
330
- }
331
- }
332
-
333
- func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
334
- guard let captureSession = captureSession, captureSession.isRunning else { completion(nil, CameraControllerError.captureSessionIsMissing); return }
335
- let settings = AVCapturePhotoSettings()
336
-
337
- settings.flashMode = self.flashMode
338
- settings.isHighResolutionPhotoEnabled = self.highResolutionOutput
339
-
340
- self.photoOutput?.capturePhoto(with: settings, delegate: self)
341
- self.photoCaptureCompletionBlock = completion
342
- }
343
-
344
- func captureSample(completion: @escaping (UIImage?, Error?) -> Void) {
345
- guard let captureSession = captureSession,
346
- captureSession.isRunning else {
347
- completion(nil, CameraControllerError.captureSessionIsMissing)
348
- return
349
- }
350
-
351
- self.sampleBufferCaptureCompletionBlock = completion
352
- }
353
-
354
- func getSupportedFlashModes() throws -> [String] {
355
- var currentCamera: AVCaptureDevice?
356
- switch currentCameraPosition {
357
- case .front:
358
- currentCamera = self.frontCamera!
359
- case .rear:
360
- currentCamera = self.rearCamera!
361
- default: break
362
- }
363
-
364
- guard
365
- let device = currentCamera
366
- else {
367
- throw CameraControllerError.noCamerasAvailable
368
- }
369
-
370
- var supportedFlashModesAsStrings: [String] = []
371
- if device.hasFlash {
372
- guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
373
- throw CameraControllerError.noCamerasAvailable
374
- }
375
-
376
- for flashMode in supportedFlashModes {
377
- var flashModeValue: String?
378
- switch flashMode {
379
- case AVCaptureDevice.FlashMode.off:
380
- flashModeValue = "off"
381
- case AVCaptureDevice.FlashMode.on:
382
- flashModeValue = "on"
383
- case AVCaptureDevice.FlashMode.auto:
384
- flashModeValue = "auto"
385
- default: break
386
- }
387
- if flashModeValue != nil {
388
- supportedFlashModesAsStrings.append(flashModeValue!)
389
- }
390
- }
391
- }
392
- if device.hasTorch {
393
- supportedFlashModesAsStrings.append("torch")
394
- }
395
- return supportedFlashModesAsStrings
396
-
397
- }
398
- func getSupportedCameras() throws -> [String] {
399
- var supportedCameras: [String] = [];
400
- if let device = AVCaptureDevice.default(.builtInUltraWideCamera, for: AVMediaType.video, position: .back) {
401
- supportedCameras.append("ultra")
402
- }
403
- if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back) {
404
- supportedCameras.append("wide")
405
- }
406
- if let device = AVCaptureDevice.default(.builtInTelephotoCamera, for: AVMediaType.video, position: .back) {
407
- supportedCameras.append("tele")
408
- }
409
- return supportedCameras;
410
- }
411
-
412
- func setFlashMode(flashMode: AVCaptureDevice.FlashMode) throws {
413
- var currentCamera: AVCaptureDevice?
414
- switch currentCameraPosition {
415
- case .front:
416
- currentCamera = self.frontCamera!
417
- case .rear:
418
- currentCamera = self.rearCamera!
419
- default: break
420
- }
421
-
422
- guard let device = currentCamera else {
423
- throw CameraControllerError.noCamerasAvailable
424
- }
425
-
426
- guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
427
- throw CameraControllerError.invalidOperation
428
- }
429
- if supportedFlashModes.contains(flashMode) {
430
- do {
431
- try device.lockForConfiguration()
432
-
433
- if device.hasTorch && device.isTorchAvailable && device.torchMode == AVCaptureDevice.TorchMode.on {
434
- device.torchMode = AVCaptureDevice.TorchMode.off
435
- }
436
- self.flashMode = flashMode
437
- let photoSettings = AVCapturePhotoSettings()
438
- photoSettings.flashMode = flashMode
439
- self.photoOutput?.photoSettingsForSceneMonitoring = photoSettings
440
-
441
- device.unlockForConfiguration()
442
- } catch {
443
- throw CameraControllerError.invalidOperation
444
- }
445
- } else {
446
- throw CameraControllerError.invalidOperation
447
- }
448
- }
449
-
450
- func setTorchMode() throws {
451
- var currentCamera: AVCaptureDevice?
452
- switch currentCameraPosition {
453
- case .front:
454
- currentCamera = self.frontCamera!
455
- case .rear:
456
- currentCamera = self.rearCamera!
457
- default: break
458
- }
459
-
460
- guard
461
- let device = currentCamera,
462
- device.hasTorch,
463
- device.isTorchAvailable
464
- else {
465
- throw CameraControllerError.invalidOperation
466
- }
467
-
468
- do {
469
- try device.lockForConfiguration()
470
- if device.isTorchModeSupported(AVCaptureDevice.TorchMode.on) {
471
- device.torchMode = AVCaptureDevice.TorchMode.on
472
- } else if device.isTorchModeSupported(AVCaptureDevice.TorchMode.auto) {
473
- device.torchMode = AVCaptureDevice.TorchMode.auto
474
- } else {
475
- device.torchMode = AVCaptureDevice.TorchMode.off
476
- }
477
- device.unlockForConfiguration()
478
- } catch {
479
- throw CameraControllerError.invalidOperation
480
- }
481
-
482
- }
483
-
484
- func captureVideo(completion: @escaping (URL?, Error?) -> Void) {
485
- guard let captureSession = self.captureSession, captureSession.isRunning else {
486
- completion(nil, CameraControllerError.captureSessionIsMissing)
487
- return
488
- }
489
- let path = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
490
- let identifier = UUID()
491
- let randomIdentifier = identifier.uuidString.replacingOccurrences(of: "-", with: "")
492
- let finalIdentifier = String(randomIdentifier.prefix(8))
493
- let fileName="cpcp_video_"+finalIdentifier+".mp4"
494
-
495
- let fileUrl = path.appendingPathComponent(fileName)
496
- try? FileManager.default.removeItem(at: fileUrl)
497
- /*videoOutput!.startRecording(to: fileUrl, recordingDelegate: self)
498
- self.videoRecordCompletionBlock = completion*/
499
- }
500
-
501
- func stopRecording(completion: @escaping (Error?) -> Void) {
502
- guard let captureSession = self.captureSession, captureSession.isRunning else {
503
- completion(CameraControllerError.captureSessionIsMissing)
504
- return
505
- }
506
- // self.videoOutput?.stopRecording()
507
- }
508
- }
509
-
510
- extension CameraController: UIGestureRecognizerDelegate {
511
- func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
512
- return true
513
- }
514
-
515
- @objc
516
- func handleTap(_ tap: UITapGestureRecognizer) {
517
- guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
518
-
519
- let point = tap.location(in: tap.view)
520
- let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
521
-
522
- do {
523
- try device.lockForConfiguration()
524
- defer { device.unlockForConfiguration() }
525
-
526
- let focusMode = AVCaptureDevice.FocusMode.autoFocus
527
- if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(focusMode) {
528
- device.focusPointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
529
- device.focusMode = focusMode
530
- }
531
-
532
- let exposureMode = AVCaptureDevice.ExposureMode.autoExpose
533
- if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
534
- device.exposurePointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
535
- device.exposureMode = exposureMode
536
- }
537
- } catch {
538
- debugPrint(error)
539
- }
540
- }
541
-
542
- @objc
543
- private func handlePinch(_ pinch: UIPinchGestureRecognizer) {
544
- guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
545
-
546
- func minMaxZoom(_ factor: CGFloat) -> CGFloat { return max(1.0, min(factor, device.activeFormat.videoMaxZoomFactor)) }
547
-
548
- func update(scale factor: CGFloat) {
549
- do {
550
- try device.lockForConfiguration()
551
- defer { device.unlockForConfiguration() }
552
-
553
- device.videoZoomFactor = factor
554
- } catch {
555
- debugPrint(error)
556
- }
557
- }
558
-
559
- switch pinch.state {
560
- case .began: fallthrough
561
- case .changed:
562
- let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
563
- update(scale: newScaleFactor)
564
- case .ended:
565
- zoomFactor = device.videoZoomFactor
566
- default: break
567
- }
568
- }
569
- }
570
-
571
- extension CameraController: AVCapturePhotoCaptureDelegate {
572
- public func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
573
- resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
574
- if let error = error { self.photoCaptureCompletionBlock?(nil, error) } else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
575
- let image = UIImage(data: data) {
576
- self.photoCaptureCompletionBlock?(image.fixedOrientation(), nil)
577
- } else {
578
- self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
579
- }
580
- }
581
- }
582
-
583
- extension CameraController: AVCaptureVideoDataOutputSampleBufferDelegate {
584
- func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
585
- guard let completion = sampleBufferCaptureCompletionBlock else { return }
586
-
587
- guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
588
- completion(nil, CameraControllerError.unknown)
589
- return
590
- }
591
-
592
- CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
593
- defer { CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) }
594
-
595
- let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
596
- let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
597
- let width = CVPixelBufferGetWidth(imageBuffer)
598
- let height = CVPixelBufferGetHeight(imageBuffer)
599
- let colorSpace = CGColorSpaceCreateDeviceRGB()
600
- let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue |
601
- CGImageAlphaInfo.premultipliedFirst.rawValue
602
-
603
- let context = CGContext(
604
- data: baseAddress,
605
- width: width,
606
- height: height,
607
- bitsPerComponent: 8,
608
- bytesPerRow: bytesPerRow,
609
- space: colorSpace,
610
- bitmapInfo: bitmapInfo
611
- )
612
-
613
- guard let cgImage = context?.makeImage() else {
614
- completion(nil, CameraControllerError.unknown)
615
- return
616
- }
617
-
618
- let image = UIImage(cgImage: cgImage)
619
- completion(image.fixedOrientation(), nil)
620
-
621
- sampleBufferCaptureCompletionBlock = nil
622
- }
623
- }
624
-
625
- enum CameraControllerError: Swift.Error {
626
- case captureSessionAlreadyRunning
627
- case captureSessionIsMissing
628
- case inputsAreInvalid
629
- case invalidOperation
630
- case noCamerasAvailable
631
- case unknown
632
- }
633
-
634
- public enum CameraPosition {
635
- case front
636
- case rear
637
- }
638
-
639
- extension CameraControllerError: LocalizedError {
640
- public var errorDescription: String? {
641
- switch self {
642
- case .captureSessionAlreadyRunning:
643
- return NSLocalizedString("Capture Session is Already Running", comment: "Capture Session Already Running")
644
- case .captureSessionIsMissing:
645
- return NSLocalizedString("Capture Session is Missing", comment: "Capture Session Missing")
646
- case .inputsAreInvalid:
647
- return NSLocalizedString("Inputs Are Invalid", comment: "Inputs Are Invalid")
648
- case .invalidOperation:
649
- return NSLocalizedString("Invalid Operation", comment: "invalid Operation")
650
- case .noCamerasAvailable:
651
- return NSLocalizedString("Failed to access device camera(s)", comment: "No Cameras Available")
652
- case .unknown:
653
- return NSLocalizedString("Unknown", comment: "Unknown")
654
-
655
- }
656
- }
657
- }
658
-
659
- extension UIImage {
660
-
661
- func fixedOrientation() -> UIImage? {
662
-
663
- guard imageOrientation != UIImage.Orientation.up else {
664
- // This is default orientation, don't need to do anything
665
- return self.copy() as? UIImage
666
- }
667
-
668
- guard let cgImage = self.cgImage else {
669
- // CGImage is not available
670
- return nil
671
- }
672
-
673
- 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 {
674
- return nil // Not able to create CGContext
675
- }
676
-
677
- var transform: CGAffineTransform = CGAffineTransform.identity
678
- switch imageOrientation {
679
- case .down, .downMirrored:
680
- transform = transform.translatedBy(x: size.width, y: size.height)
681
- transform = transform.rotated(by: CGFloat.pi)
682
- print("down")
683
- break
684
- case .left, .leftMirrored:
685
- transform = transform.translatedBy(x: size.width, y: 0)
686
- transform = transform.rotated(by: CGFloat.pi / 2.0)
687
- print("left")
688
- break
689
- case .right, .rightMirrored:
690
- transform = transform.translatedBy(x: 0, y: size.height)
691
- transform = transform.rotated(by: CGFloat.pi / -2.0)
692
- print("right")
693
- break
694
- case .up, .upMirrored:
695
- break
696
- }
697
-
698
- // Flip image one more time if needed to, this is to prevent flipped image
699
- switch imageOrientation {
700
- case .upMirrored, .downMirrored:
701
- transform.translatedBy(x: size.width, y: 0)
702
- transform.scaledBy(x: -1, y: 1)
703
- break
704
- case .leftMirrored, .rightMirrored:
705
- transform.translatedBy(x: size.height, y: 0)
706
- transform.scaledBy(x: -1, y: 1)
707
- case .up, .down, .left, .right:
708
- break
709
- }
710
-
711
- ctx.concatenate(transform)
712
-
713
- switch imageOrientation {
714
- case .left, .leftMirrored, .right, .rightMirrored:
715
- ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
716
- default:
717
- ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
718
- break
719
- }
720
- guard let newCGImage = ctx.makeImage() else { return nil }
721
- return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
722
- }
723
- }
724
-
725
- extension CameraController: AVCaptureFileOutputRecordingDelegate {
726
- func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
727
- /*if error == nil {
728
- self.videoRecordCompletionBlock?(outputFileURL, nil)
729
- } else {
730
- self.videoRecordCompletionBlock?(nil, error)
731
- }*/
732
- }
733
- }
1
+ //
2
+ // CameraController.swift
3
+ // Plugin
4
+ //
5
+ // Created by Ariel Hernandez Musa on 7/14/19.
6
+ // Copyright © 2019 Max Lynch. All rights reserved.
7
+ //
8
+
9
+ import AVFoundation
10
+ import UIKit
11
+
12
+ class CameraController: NSObject {
13
+ var captureSession: AVCaptureSession?
14
+
15
+ var currentCameraPosition: CameraPosition?
16
+
17
+ var frontCamera: AVCaptureDevice?
18
+ var frontCameraInput: AVCaptureDeviceInput?
19
+
20
+ var dataOutput: AVCaptureVideoDataOutput?
21
+ var photoOutput: AVCapturePhotoOutput?
22
+
23
+ var rearCamera: AVCaptureDevice?
24
+ var rearCameraInput: AVCaptureDeviceInput?
25
+
26
+ var previewLayer: AVCaptureVideoPreviewLayer?
27
+
28
+ var flashMode = AVCaptureDevice.FlashMode.off
29
+ var photoCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
30
+
31
+ var sampleBufferCaptureCompletionBlock: ((UIImage?, Error?) -> Void)?
32
+
33
+ var highResolutionOutput: Bool = false
34
+
35
+ var audioDevice: AVCaptureDevice?
36
+ var audioInput: AVCaptureDeviceInput?
37
+
38
+ var zoomFactor: CGFloat = 1.0
39
+ }
40
+ extension CameraController {
41
+ func prepare(cameraPosition: String, zoomFactor: String, disableAudio: Bool, completionHandler: @escaping (Error?) -> Void) {
42
+
43
+ func createCaptureSession() {
44
+ self.captureSession = AVCaptureSession()
45
+ }
46
+
47
+ func configureCaptureDevices() throws {
48
+ var session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
49
+ if(zoomFactor == "ultra"){
50
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera], mediaType: AVMediaType.video, position: .unspecified)
51
+ } else if(zoomFactor == "wide"){
52
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
53
+ } else if(zoomFactor == "tele"){
54
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
55
+ }
56
+ let cameras = session.devices.compactMap { $0 }
57
+ guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
58
+
59
+ for camera in cameras {
60
+ if camera.position == .front {
61
+ self.frontCamera = camera
62
+ }
63
+
64
+ if camera.position == .back {
65
+ self.rearCamera = camera
66
+
67
+ try camera.lockForConfiguration()
68
+ if camera.isFocusModeSupported(.continuousAutoFocus){
69
+ camera.focusMode = .continuousAutoFocus
70
+ }
71
+ camera.unlockForConfiguration()
72
+ }
73
+ }
74
+ if disableAudio == false {
75
+ self.audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)
76
+ }
77
+ }
78
+
79
+ func configureDeviceInputs() throws {
80
+ guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
81
+
82
+ if cameraPosition == "rear" {
83
+ if let rearCamera = self.rearCamera {
84
+ self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
85
+
86
+ if captureSession.canAddInput(self.rearCameraInput!) { captureSession.addInput(self.rearCameraInput!) }
87
+
88
+ self.currentCameraPosition = .rear
89
+ }
90
+ } else if cameraPosition == "front" {
91
+ if let frontCamera = self.frontCamera {
92
+ self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
93
+
94
+ if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) } else { throw CameraControllerError.inputsAreInvalid }
95
+
96
+ self.currentCameraPosition = .front
97
+ }
98
+ } else { throw CameraControllerError.noCamerasAvailable }
99
+
100
+ // Add audio input
101
+ if disableAudio == false {
102
+ if let audioDevice = self.audioDevice {
103
+ self.audioInput = try AVCaptureDeviceInput(device: audioDevice)
104
+ if captureSession.canAddInput(self.audioInput!) {
105
+ captureSession.addInput(self.audioInput!)
106
+ } else {
107
+ throw CameraControllerError.inputsAreInvalid
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ func configurePhotoOutput() throws {
114
+ guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
115
+
116
+ self.photoOutput = AVCapturePhotoOutput()
117
+ self.photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
118
+ self.photoOutput?.isHighResolutionCaptureEnabled = self.highResolutionOutput
119
+ if captureSession.canAddOutput(self.photoOutput!) { captureSession.addOutput(self.photoOutput!) }
120
+ captureSession.startRunning()
121
+ }
122
+
123
+ func configureDataOutput() throws {
124
+ guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }
125
+
126
+ self.dataOutput = AVCaptureVideoDataOutput()
127
+ self.dataOutput?.videoSettings = [
128
+ (kCVPixelBufferPixelFormatTypeKey as String): NSNumber(value: kCVPixelFormatType_32BGRA as UInt32)
129
+ ]
130
+ self.dataOutput?.alwaysDiscardsLateVideoFrames = true
131
+ if captureSession.canAddOutput(self.dataOutput!) {
132
+ captureSession.addOutput(self.dataOutput!)
133
+ }
134
+
135
+ captureSession.commitConfiguration()
136
+
137
+ let queue = DispatchQueue(label: "DataOutput", attributes: [])
138
+ self.dataOutput?.setSampleBufferDelegate(self, queue: queue)
139
+ }
140
+
141
+ DispatchQueue(label: "prepare").async {
142
+ do {
143
+ createCaptureSession()
144
+ try configureCaptureDevices()
145
+ try configureDeviceInputs()
146
+ try configurePhotoOutput()
147
+ try configureDataOutput()
148
+ // try configureVideoOutput()
149
+ } catch {
150
+ DispatchQueue.main.async {
151
+ completionHandler(error)
152
+ }
153
+
154
+ return
155
+ }
156
+
157
+ DispatchQueue.main.async {
158
+ completionHandler(nil)
159
+ }
160
+ }
161
+ }
162
+
163
+ func displayPreview(on view: UIView) throws {
164
+ guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
165
+
166
+ self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
167
+ self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
168
+
169
+ view.layer.insertSublayer(self.previewLayer!, at: 0)
170
+ self.previewLayer?.frame = view.frame
171
+
172
+ updateVideoOrientation()
173
+ }
174
+
175
+ func setupGestures(target: UIView, enableZoom: Bool) {
176
+ setupTapGesture(target: target, selector: #selector(handleTap(_:)), delegate: self)
177
+ if enableZoom {
178
+ setupPinchGesture(target: target, selector: #selector(handlePinch(_:)), delegate: self)
179
+ }
180
+ }
181
+
182
+ func setupTapGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
183
+ let tapGesture = UITapGestureRecognizer(target: self, action: selector)
184
+ tapGesture.delegate = delegate
185
+ target.addGestureRecognizer(tapGesture)
186
+ }
187
+
188
+ func setupPinchGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
189
+ let pinchGesture = UIPinchGestureRecognizer(target: self, action: selector)
190
+ pinchGesture.delegate = delegate
191
+ target.addGestureRecognizer(pinchGesture)
192
+ }
193
+
194
+ func updateVideoOrientation() {
195
+ assert(Thread.isMainThread) // UIApplication.statusBarOrientation requires the main thread.
196
+
197
+ let videoOrientation: AVCaptureVideoOrientation
198
+ switch UIApplication.shared.statusBarOrientation {
199
+ case .portrait:
200
+ videoOrientation = .portrait
201
+ case .landscapeLeft:
202
+ videoOrientation = .landscapeLeft
203
+ case .landscapeRight:
204
+ videoOrientation = .landscapeRight
205
+ case .portraitUpsideDown:
206
+ videoOrientation = .portraitUpsideDown
207
+ case .unknown:
208
+ fallthrough
209
+ @unknown default:
210
+ videoOrientation = .portrait
211
+ }
212
+
213
+ previewLayer?.connection?.videoOrientation = videoOrientation
214
+ dataOutput?.connections.forEach { $0.videoOrientation = videoOrientation }
215
+ photoOutput?.connections.forEach { $0.videoOrientation = videoOrientation }
216
+ }
217
+
218
+ func switchCameras() throws {
219
+ guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
220
+
221
+ captureSession.beginConfiguration()
222
+
223
+ func switchToFrontCamera() throws {
224
+
225
+ guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput),
226
+ let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
227
+
228
+ self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
229
+
230
+ captureSession.removeInput(rearCameraInput)
231
+
232
+ if captureSession.canAddInput(self.frontCameraInput!) {
233
+ captureSession.addInput(self.frontCameraInput!)
234
+
235
+ self.currentCameraPosition = .front
236
+ } else {
237
+ throw CameraControllerError.invalidOperation
238
+ }
239
+ }
240
+
241
+ func switchToRearCamera() throws {
242
+
243
+ guard let frontCameraInput = self.frontCameraInput, captureSession.inputs.contains(frontCameraInput),
244
+ let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
245
+
246
+ self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
247
+
248
+ captureSession.removeInput(frontCameraInput)
249
+
250
+ if captureSession.canAddInput(self.rearCameraInput!) {
251
+ captureSession.addInput(self.rearCameraInput!)
252
+
253
+ self.currentCameraPosition = .rear
254
+ } else { throw CameraControllerError.invalidOperation }
255
+ }
256
+
257
+ switch currentCameraPosition {
258
+ case .front:
259
+ try switchToRearCamera()
260
+
261
+ case .rear:
262
+ try switchToFrontCamera()
263
+ }
264
+
265
+ captureSession.commitConfiguration()
266
+ }
267
+ func setZoom(lens: String) throws {
268
+ print(lens)
269
+ guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
270
+
271
+ captureSession.beginConfiguration()
272
+
273
+ guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput) else { throw CameraControllerError.invalidOperation }
274
+
275
+ var session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
276
+ if(lens == "ultra"){
277
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera], mediaType: AVMediaType.video, position: .unspecified)
278
+ } else if(lens == "wide"){
279
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
280
+ } else if(lens == "tele"){
281
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
282
+ }
283
+ let cameras = session.devices.compactMap { $0 }
284
+ guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
285
+
286
+ for camera in cameras {
287
+ if camera.position == .front {
288
+ self.frontCamera = camera
289
+ }
290
+
291
+ if camera.position == .back {
292
+ self.rearCamera = camera
293
+
294
+ try camera.lockForConfiguration()
295
+ if camera.isFocusModeSupported(.continuousAutoFocus){
296
+ camera.focusMode = .continuousAutoFocus
297
+ }
298
+ camera.unlockForConfiguration()
299
+ }
300
+ }
301
+
302
+ if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
303
+ for input in inputs {
304
+ captureSession.removeInput(input)
305
+ }
306
+ }
307
+ captureSession.commitConfiguration()
308
+
309
+ self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera!)
310
+
311
+ //captureSession.removeInput(frontCameraInput)
312
+
313
+ if captureSession.canAddInput(self.rearCameraInput!) {
314
+ captureSession.addInput(self.rearCameraInput!)
315
+ self.currentCameraPosition = .rear
316
+ }
317
+
318
+ do {
319
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
320
+
321
+ try device.lockForConfiguration()
322
+ defer { device.unlockForConfiguration() }
323
+
324
+ device.videoZoomFactor = 1.0
325
+ zoomFactor = device.videoZoomFactor
326
+
327
+ } catch {
328
+ debugPrint(error)
329
+ }
330
+ }
331
+
332
+ func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
333
+ guard let captureSession = captureSession, captureSession.isRunning else { completion(nil, CameraControllerError.captureSessionIsMissing); return }
334
+ let settings = AVCapturePhotoSettings()
335
+
336
+ settings.flashMode = self.flashMode
337
+ settings.isHighResolutionPhotoEnabled = self.highResolutionOutput
338
+
339
+ self.photoOutput?.capturePhoto(with: settings, delegate: self)
340
+ self.photoCaptureCompletionBlock = completion
341
+ }
342
+
343
+ func captureSample(completion: @escaping (UIImage?, Error?) -> Void) {
344
+ guard let captureSession = captureSession,
345
+ captureSession.isRunning else {
346
+ completion(nil, CameraControllerError.captureSessionIsMissing)
347
+ return
348
+ }
349
+
350
+ self.sampleBufferCaptureCompletionBlock = completion
351
+ }
352
+
353
+ func getSupportedFlashModes() throws -> [String] {
354
+ var currentCamera: AVCaptureDevice?
355
+ switch currentCameraPosition {
356
+ case .front:
357
+ currentCamera = self.frontCamera!
358
+ case .rear:
359
+ currentCamera = self.rearCamera!
360
+ default: break
361
+ }
362
+
363
+ guard
364
+ let device = currentCamera
365
+ else {
366
+ throw CameraControllerError.noCamerasAvailable
367
+ }
368
+
369
+ var supportedFlashModesAsStrings: [String] = []
370
+ if device.hasFlash {
371
+ guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
372
+ throw CameraControllerError.noCamerasAvailable
373
+ }
374
+
375
+ for flashMode in supportedFlashModes {
376
+ var flashModeValue: String?
377
+ switch flashMode {
378
+ case AVCaptureDevice.FlashMode.off:
379
+ flashModeValue = "off"
380
+ case AVCaptureDevice.FlashMode.on:
381
+ flashModeValue = "on"
382
+ case AVCaptureDevice.FlashMode.auto:
383
+ flashModeValue = "auto"
384
+ default: break
385
+ }
386
+ if flashModeValue != nil {
387
+ supportedFlashModesAsStrings.append(flashModeValue!)
388
+ }
389
+ }
390
+ }
391
+ if device.hasTorch {
392
+ supportedFlashModesAsStrings.append("torch")
393
+ }
394
+ return supportedFlashModesAsStrings
395
+
396
+ }
397
+ func getSupportedCameras() throws -> [String] {
398
+ var supportedCameras: [String] = [];
399
+ if let device = AVCaptureDevice.default(.builtInUltraWideCamera, for: AVMediaType.video, position: .back) {
400
+ supportedCameras.append("ultra")
401
+ }
402
+ if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back) {
403
+ supportedCameras.append("wide")
404
+ }
405
+ if let device = AVCaptureDevice.default(.builtInTelephotoCamera, for: AVMediaType.video, position: .back) {
406
+ supportedCameras.append("tele")
407
+ }
408
+ return supportedCameras;
409
+ }
410
+
411
+ func setFlashMode(flashMode: AVCaptureDevice.FlashMode) throws {
412
+ var currentCamera: AVCaptureDevice?
413
+ switch currentCameraPosition {
414
+ case .front:
415
+ currentCamera = self.frontCamera!
416
+ case .rear:
417
+ currentCamera = self.rearCamera!
418
+ default: break
419
+ }
420
+
421
+ guard let device = currentCamera else {
422
+ throw CameraControllerError.noCamerasAvailable
423
+ }
424
+
425
+ guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
426
+ throw CameraControllerError.invalidOperation
427
+ }
428
+ if supportedFlashModes.contains(flashMode) {
429
+ do {
430
+ try device.lockForConfiguration()
431
+
432
+ if device.hasTorch && device.isTorchAvailable && device.torchMode == AVCaptureDevice.TorchMode.on {
433
+ device.torchMode = AVCaptureDevice.TorchMode.off
434
+ }
435
+ self.flashMode = flashMode
436
+ let photoSettings = AVCapturePhotoSettings()
437
+ photoSettings.flashMode = flashMode
438
+ self.photoOutput?.photoSettingsForSceneMonitoring = photoSettings
439
+
440
+ device.unlockForConfiguration()
441
+ } catch {
442
+ throw CameraControllerError.invalidOperation
443
+ }
444
+ } else {
445
+ throw CameraControllerError.invalidOperation
446
+ }
447
+ }
448
+
449
+ func setTorchMode() throws {
450
+ var currentCamera: AVCaptureDevice?
451
+ switch currentCameraPosition {
452
+ case .front:
453
+ currentCamera = self.frontCamera!
454
+ case .rear:
455
+ currentCamera = self.rearCamera!
456
+ default: break
457
+ }
458
+
459
+ guard
460
+ let device = currentCamera,
461
+ device.hasTorch,
462
+ device.isTorchAvailable
463
+ else {
464
+ throw CameraControllerError.invalidOperation
465
+ }
466
+
467
+ do {
468
+ try device.lockForConfiguration()
469
+ if device.isTorchModeSupported(AVCaptureDevice.TorchMode.on) {
470
+ device.torchMode = AVCaptureDevice.TorchMode.on
471
+ } else if device.isTorchModeSupported(AVCaptureDevice.TorchMode.auto) {
472
+ device.torchMode = AVCaptureDevice.TorchMode.auto
473
+ } else {
474
+ device.torchMode = AVCaptureDevice.TorchMode.off
475
+ }
476
+ device.unlockForConfiguration()
477
+ } catch {
478
+ throw CameraControllerError.invalidOperation
479
+ }
480
+
481
+ }
482
+
483
+ func captureVideo(completion: @escaping (URL?, Error?) -> Void) {
484
+ guard let captureSession = self.captureSession, captureSession.isRunning else {
485
+ completion(nil, CameraControllerError.captureSessionIsMissing)
486
+ return
487
+ }
488
+ let path = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
489
+ let identifier = UUID()
490
+ let randomIdentifier = identifier.uuidString.replacingOccurrences(of: "-", with: "")
491
+ let finalIdentifier = String(randomIdentifier.prefix(8))
492
+ let fileName="cpcp_video_"+finalIdentifier+".mp4"
493
+
494
+ let fileUrl = path.appendingPathComponent(fileName)
495
+ try? FileManager.default.removeItem(at: fileUrl)
496
+ /*videoOutput!.startRecording(to: fileUrl, recordingDelegate: self)
497
+ self.videoRecordCompletionBlock = completion*/
498
+ }
499
+
500
+ func stopRecording(completion: @escaping (Error?) -> Void) {
501
+ guard let captureSession = self.captureSession, captureSession.isRunning else {
502
+ completion(CameraControllerError.captureSessionIsMissing)
503
+ return
504
+ }
505
+ // self.videoOutput?.stopRecording()
506
+ }
507
+ }
508
+
509
+ extension CameraController: UIGestureRecognizerDelegate {
510
+ func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
511
+ return true
512
+ }
513
+
514
+ @objc
515
+ func handleTap(_ tap: UITapGestureRecognizer) {
516
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
517
+
518
+ let point = tap.location(in: tap.view)
519
+ let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
520
+
521
+ do {
522
+ try device.lockForConfiguration()
523
+ defer { device.unlockForConfiguration() }
524
+
525
+ let focusMode = AVCaptureDevice.FocusMode.autoFocus
526
+ if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(focusMode) {
527
+ device.focusPointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
528
+ device.focusMode = focusMode
529
+ }
530
+
531
+ let exposureMode = AVCaptureDevice.ExposureMode.autoExpose
532
+ if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
533
+ device.exposurePointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
534
+ device.exposureMode = exposureMode
535
+ }
536
+ } catch {
537
+ debugPrint(error)
538
+ }
539
+ }
540
+
541
+ @objc
542
+ private func handlePinch(_ pinch: UIPinchGestureRecognizer) {
543
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
544
+
545
+ func minMaxZoom(_ factor: CGFloat) -> CGFloat { return max(1.0, min(factor, device.activeFormat.videoMaxZoomFactor)) }
546
+
547
+ func update(scale factor: CGFloat) {
548
+ do {
549
+ try device.lockForConfiguration()
550
+ defer { device.unlockForConfiguration() }
551
+
552
+ device.videoZoomFactor = factor
553
+ } catch {
554
+ debugPrint(error)
555
+ }
556
+ }
557
+
558
+ switch pinch.state {
559
+ case .began: fallthrough
560
+ case .changed:
561
+ let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
562
+ update(scale: newScaleFactor)
563
+ case .ended:
564
+ zoomFactor = device.videoZoomFactor
565
+ default: break
566
+ }
567
+ }
568
+ }
569
+
570
+ extension CameraController: AVCapturePhotoCaptureDelegate {
571
+ public func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
572
+ resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
573
+ if let error = error { self.photoCaptureCompletionBlock?(nil, error) } else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
574
+ let image = UIImage(data: data) {
575
+ self.photoCaptureCompletionBlock?(image.fixedOrientation(), nil)
576
+ } else {
577
+ self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
578
+ }
579
+ }
580
+ }
581
+
582
+ extension CameraController: AVCaptureVideoDataOutputSampleBufferDelegate {
583
+ func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
584
+ guard let completion = sampleBufferCaptureCompletionBlock else { return }
585
+
586
+ guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
587
+ completion(nil, CameraControllerError.unknown)
588
+ return
589
+ }
590
+
591
+ CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
592
+ defer { CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) }
593
+
594
+ let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
595
+ let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
596
+ let width = CVPixelBufferGetWidth(imageBuffer)
597
+ let height = CVPixelBufferGetHeight(imageBuffer)
598
+ let colorSpace = CGColorSpaceCreateDeviceRGB()
599
+ let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue |
600
+ CGImageAlphaInfo.premultipliedFirst.rawValue
601
+
602
+ let context = CGContext(
603
+ data: baseAddress,
604
+ width: width,
605
+ height: height,
606
+ bitsPerComponent: 8,
607
+ bytesPerRow: bytesPerRow,
608
+ space: colorSpace,
609
+ bitmapInfo: bitmapInfo
610
+ )
611
+
612
+ guard let cgImage = context?.makeImage() else {
613
+ completion(nil, CameraControllerError.unknown)
614
+ return
615
+ }
616
+
617
+ let image = UIImage(cgImage: cgImage)
618
+ completion(image.fixedOrientation(), nil)
619
+
620
+ sampleBufferCaptureCompletionBlock = nil
621
+ }
622
+ }
623
+
624
+ enum CameraControllerError: Swift.Error {
625
+ case captureSessionAlreadyRunning
626
+ case captureSessionIsMissing
627
+ case inputsAreInvalid
628
+ case invalidOperation
629
+ case noCamerasAvailable
630
+ case unknown
631
+ }
632
+
633
+ public enum CameraPosition {
634
+ case front
635
+ case rear
636
+ }
637
+
638
+ extension CameraControllerError: LocalizedError {
639
+ public var errorDescription: String? {
640
+ switch self {
641
+ case .captureSessionAlreadyRunning:
642
+ return NSLocalizedString("Capture Session is Already Running", comment: "Capture Session Already Running")
643
+ case .captureSessionIsMissing:
644
+ return NSLocalizedString("Capture Session is Missing", comment: "Capture Session Missing")
645
+ case .inputsAreInvalid:
646
+ return NSLocalizedString("Inputs Are Invalid", comment: "Inputs Are Invalid")
647
+ case .invalidOperation:
648
+ return NSLocalizedString("Invalid Operation", comment: "invalid Operation")
649
+ case .noCamerasAvailable:
650
+ return NSLocalizedString("Failed to access device camera(s)", comment: "No Cameras Available")
651
+ case .unknown:
652
+ return NSLocalizedString("Unknown", comment: "Unknown")
653
+
654
+ }
655
+ }
656
+ }
657
+
658
+ extension UIImage {
659
+
660
+ func fixedOrientation() -> UIImage? {
661
+
662
+ guard imageOrientation != UIImage.Orientation.up else {
663
+ // This is default orientation, don't need to do anything
664
+ return self.copy() as? UIImage
665
+ }
666
+
667
+ guard let cgImage = self.cgImage else {
668
+ // CGImage is not available
669
+ return nil
670
+ }
671
+
672
+ 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 {
673
+ return nil // Not able to create CGContext
674
+ }
675
+
676
+ var transform: CGAffineTransform = CGAffineTransform.identity
677
+ switch imageOrientation {
678
+ case .down, .downMirrored:
679
+ transform = transform.translatedBy(x: size.width, y: size.height)
680
+ transform = transform.rotated(by: CGFloat.pi)
681
+ print("down")
682
+ break
683
+ case .left, .leftMirrored:
684
+ transform = transform.translatedBy(x: size.width, y: 0)
685
+ transform = transform.rotated(by: CGFloat.pi / 2.0)
686
+ print("left")
687
+ break
688
+ case .right, .rightMirrored:
689
+ transform = transform.translatedBy(x: 0, y: size.height)
690
+ transform = transform.rotated(by: CGFloat.pi / -2.0)
691
+ print("right")
692
+ break
693
+ case .up, .upMirrored:
694
+ break
695
+ }
696
+
697
+ // Flip image one more time if needed to, this is to prevent flipped image
698
+ switch imageOrientation {
699
+ case .upMirrored, .downMirrored:
700
+ transform.translatedBy(x: size.width, y: 0)
701
+ transform.scaledBy(x: -1, y: 1)
702
+ break
703
+ case .leftMirrored, .rightMirrored:
704
+ transform.translatedBy(x: size.height, y: 0)
705
+ transform.scaledBy(x: -1, y: 1)
706
+ case .up, .down, .left, .right:
707
+ break
708
+ }
709
+
710
+ ctx.concatenate(transform)
711
+
712
+ switch imageOrientation {
713
+ case .left, .leftMirrored, .right, .rightMirrored:
714
+ ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
715
+ default:
716
+ ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
717
+ break
718
+ }
719
+ guard let newCGImage = ctx.makeImage() else { return nil }
720
+ return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
721
+ }
722
+ }
723
+
724
+ extension CameraController: AVCaptureFileOutputRecordingDelegate {
725
+ func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
726
+ /*if error == nil {
727
+ self.videoRecordCompletionBlock?(outputFileURL, nil)
728
+ } else {
729
+ self.videoRecordCompletionBlock?(nil, error)
730
+ }*/
731
+ }
732
+ }