capacitor-community-multilens-camerapreview 0.0.10 → 0.0.11-a

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 +26 -16
  3. package/android/build.gradle +55 -55
  4. package/android/src/main/AndroidManifest.xml +4 -4
  5. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraActivity.java +1008 -1008
  6. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +544 -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/docs.json +11 -1
  19. package/dist/esm/definitions.d.ts +81 -80
  20. package/dist/esm/definitions.js +1 -1
  21. package/dist/esm/definitions.js.map +1 -1
  22. package/dist/esm/index.d.ts +4 -4
  23. package/dist/esm/index.js +6 -6
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/web.d.ts +29 -28
  26. package/dist/esm/web.js +158 -155
  27. package/dist/esm/web.js.map +1 -1
  28. package/dist/plugin.cjs.js +152 -149
  29. package/dist/plugin.cjs.js.map +1 -1
  30. package/dist/plugin.js +152 -149
  31. package/dist/plugin.js.map +1 -1
  32. package/ios/Plugin/CameraController.swift +783 -733
  33. package/ios/Plugin/Info.plist +24 -24
  34. package/ios/Plugin/Plugin.h +10 -10
  35. package/ios/Plugin/Plugin.m +19 -18
  36. package/ios/Plugin/Plugin.swift +316 -308
  37. package/package.json +78 -78
@@ -1,733 +1,783 @@
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
+
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 changeOrientation() throws {
269
+ guard let currentCameraPosition = currentCameraPosition, let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
270
+
271
+ captureSession.beginConfiguration()
272
+
273
+ func switchToFrontCamera() throws {
274
+
275
+ guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput),
276
+ let frontCamera = self.frontCamera else { throw CameraControllerError.invalidOperation }
277
+
278
+ self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)
279
+
280
+ captureSession.removeInput(rearCameraInput)
281
+
282
+ if captureSession.canAddInput(self.frontCameraInput!) {
283
+ captureSession.addInput(self.frontCameraInput!)
284
+
285
+ self.currentCameraPosition = .front
286
+ } else {
287
+ throw CameraControllerError.invalidOperation
288
+ }
289
+ }
290
+
291
+ func switchToRearCamera() throws {
292
+
293
+ guard let frontCameraInput = self.frontCameraInput, captureSession.inputs.contains(frontCameraInput),
294
+ let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
295
+
296
+ self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
297
+
298
+ captureSession.removeInput(frontCameraInput)
299
+
300
+ if captureSession.canAddInput(self.rearCameraInput!) {
301
+ captureSession.addInput(self.rearCameraInput!)
302
+
303
+ self.currentCameraPosition = .rear
304
+ } else { throw CameraControllerError.invalidOperation }
305
+ }
306
+
307
+ switch currentCameraPosition {
308
+ case .front:
309
+ try switchToRearCamera()
310
+
311
+ case .rear:
312
+ try switchToFrontCamera()
313
+ }
314
+
315
+ captureSession.commitConfiguration()
316
+ }
317
+
318
+ func setZoom(lens: String) throws {
319
+ print(lens)
320
+ guard let captureSession = self.captureSession, captureSession.isRunning else { throw CameraControllerError.captureSessionIsMissing }
321
+
322
+ captureSession.beginConfiguration()
323
+
324
+ guard let rearCameraInput = self.rearCameraInput, captureSession.inputs.contains(rearCameraInput) else { throw CameraControllerError.invalidOperation }
325
+
326
+ var session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
327
+ if(lens == "ultra"){
328
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInUltraWideCamera], mediaType: AVMediaType.video, position: .unspecified)
329
+ } else if(lens == "wide"){
330
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
331
+ } else if(lens == "tele"){
332
+ session = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera,.builtInTelephotoCamera], mediaType: AVMediaType.video, position: .unspecified)
333
+ }
334
+ let cameras = session.devices.compactMap { $0 }
335
+ guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }
336
+
337
+ for camera in cameras {
338
+ if camera.position == .front {
339
+ self.frontCamera = camera
340
+ }
341
+
342
+ if camera.position == .back {
343
+ self.rearCamera = camera
344
+
345
+ try camera.lockForConfiguration()
346
+ if camera.isFocusModeSupported(.continuousAutoFocus){
347
+ camera.focusMode = .continuousAutoFocus
348
+ }
349
+ camera.unlockForConfiguration()
350
+ }
351
+ }
352
+
353
+ if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
354
+ for input in inputs {
355
+ captureSession.removeInput(input)
356
+ }
357
+ }
358
+ captureSession.commitConfiguration()
359
+
360
+ self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera!)
361
+
362
+ //captureSession.removeInput(frontCameraInput)
363
+
364
+ if captureSession.canAddInput(self.rearCameraInput!) {
365
+ captureSession.addInput(self.rearCameraInput!)
366
+ self.currentCameraPosition = .rear
367
+ }
368
+
369
+ do {
370
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
371
+
372
+ try device.lockForConfiguration()
373
+ defer { device.unlockForConfiguration() }
374
+
375
+ device.videoZoomFactor = 1.0
376
+ zoomFactor = device.videoZoomFactor
377
+
378
+ } catch {
379
+ debugPrint(error)
380
+ }
381
+ }
382
+
383
+ func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
384
+ guard let captureSession = captureSession, captureSession.isRunning else { completion(nil, CameraControllerError.captureSessionIsMissing); return }
385
+ let settings = AVCapturePhotoSettings()
386
+
387
+ settings.flashMode = self.flashMode
388
+ settings.isHighResolutionPhotoEnabled = self.highResolutionOutput
389
+
390
+ self.photoOutput?.capturePhoto(with: settings, delegate: self)
391
+ self.photoCaptureCompletionBlock = completion
392
+ }
393
+
394
+ func captureSample(completion: @escaping (UIImage?, Error?) -> Void) {
395
+ guard let captureSession = captureSession,
396
+ captureSession.isRunning else {
397
+ completion(nil, CameraControllerError.captureSessionIsMissing)
398
+ return
399
+ }
400
+
401
+ self.sampleBufferCaptureCompletionBlock = completion
402
+ }
403
+
404
+ func getSupportedFlashModes() throws -> [String] {
405
+ var currentCamera: AVCaptureDevice?
406
+ switch currentCameraPosition {
407
+ case .front:
408
+ currentCamera = self.frontCamera!
409
+ case .rear:
410
+ currentCamera = self.rearCamera!
411
+ default: break
412
+ }
413
+
414
+ guard
415
+ let device = currentCamera
416
+ else {
417
+ throw CameraControllerError.noCamerasAvailable
418
+ }
419
+
420
+ var supportedFlashModesAsStrings: [String] = []
421
+ if device.hasFlash {
422
+ guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
423
+ throw CameraControllerError.noCamerasAvailable
424
+ }
425
+
426
+ for flashMode in supportedFlashModes {
427
+ var flashModeValue: String?
428
+ switch flashMode {
429
+ case AVCaptureDevice.FlashMode.off:
430
+ flashModeValue = "off"
431
+ case AVCaptureDevice.FlashMode.on:
432
+ flashModeValue = "on"
433
+ case AVCaptureDevice.FlashMode.auto:
434
+ flashModeValue = "auto"
435
+ default: break
436
+ }
437
+ if flashModeValue != nil {
438
+ supportedFlashModesAsStrings.append(flashModeValue!)
439
+ }
440
+ }
441
+ }
442
+ if device.hasTorch {
443
+ supportedFlashModesAsStrings.append("torch")
444
+ }
445
+ return supportedFlashModesAsStrings
446
+
447
+ }
448
+ func getSupportedCameras() throws -> [String] {
449
+ var supportedCameras: [String] = [];
450
+ if let device = AVCaptureDevice.default(.builtInUltraWideCamera, for: AVMediaType.video, position: .back) {
451
+ supportedCameras.append("ultra")
452
+ }
453
+ if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back) {
454
+ supportedCameras.append("wide")
455
+ }
456
+ if let device = AVCaptureDevice.default(.builtInTelephotoCamera, for: AVMediaType.video, position: .back) {
457
+ supportedCameras.append("tele")
458
+ }
459
+ return supportedCameras;
460
+ }
461
+
462
+ func setFlashMode(flashMode: AVCaptureDevice.FlashMode) throws {
463
+ var currentCamera: AVCaptureDevice?
464
+ switch currentCameraPosition {
465
+ case .front:
466
+ currentCamera = self.frontCamera!
467
+ case .rear:
468
+ currentCamera = self.rearCamera!
469
+ default: break
470
+ }
471
+
472
+ guard let device = currentCamera else {
473
+ throw CameraControllerError.noCamerasAvailable
474
+ }
475
+
476
+ guard let supportedFlashModes: [AVCaptureDevice.FlashMode] = self.photoOutput?.supportedFlashModes else {
477
+ throw CameraControllerError.invalidOperation
478
+ }
479
+ if supportedFlashModes.contains(flashMode) {
480
+ do {
481
+ try device.lockForConfiguration()
482
+
483
+ if device.hasTorch && device.isTorchAvailable && device.torchMode == AVCaptureDevice.TorchMode.on {
484
+ device.torchMode = AVCaptureDevice.TorchMode.off
485
+ }
486
+ self.flashMode = flashMode
487
+ let photoSettings = AVCapturePhotoSettings()
488
+ photoSettings.flashMode = flashMode
489
+ self.photoOutput?.photoSettingsForSceneMonitoring = photoSettings
490
+
491
+ device.unlockForConfiguration()
492
+ } catch {
493
+ throw CameraControllerError.invalidOperation
494
+ }
495
+ } else {
496
+ throw CameraControllerError.invalidOperation
497
+ }
498
+ }
499
+
500
+ func setTorchMode() throws {
501
+ var currentCamera: AVCaptureDevice?
502
+ switch currentCameraPosition {
503
+ case .front:
504
+ currentCamera = self.frontCamera!
505
+ case .rear:
506
+ currentCamera = self.rearCamera!
507
+ default: break
508
+ }
509
+
510
+ guard
511
+ let device = currentCamera,
512
+ device.hasTorch,
513
+ device.isTorchAvailable
514
+ else {
515
+ throw CameraControllerError.invalidOperation
516
+ }
517
+
518
+ do {
519
+ try device.lockForConfiguration()
520
+ if device.isTorchModeSupported(AVCaptureDevice.TorchMode.on) {
521
+ device.torchMode = AVCaptureDevice.TorchMode.on
522
+ } else if device.isTorchModeSupported(AVCaptureDevice.TorchMode.auto) {
523
+ device.torchMode = AVCaptureDevice.TorchMode.auto
524
+ } else {
525
+ device.torchMode = AVCaptureDevice.TorchMode.off
526
+ }
527
+ device.unlockForConfiguration()
528
+ } catch {
529
+ throw CameraControllerError.invalidOperation
530
+ }
531
+
532
+ }
533
+
534
+ func captureVideo(completion: @escaping (URL?, Error?) -> Void) {
535
+ guard let captureSession = self.captureSession, captureSession.isRunning else {
536
+ completion(nil, CameraControllerError.captureSessionIsMissing)
537
+ return
538
+ }
539
+ let path = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
540
+ let identifier = UUID()
541
+ let randomIdentifier = identifier.uuidString.replacingOccurrences(of: "-", with: "")
542
+ let finalIdentifier = String(randomIdentifier.prefix(8))
543
+ let fileName="cpcp_video_"+finalIdentifier+".mp4"
544
+
545
+ let fileUrl = path.appendingPathComponent(fileName)
546
+ try? FileManager.default.removeItem(at: fileUrl)
547
+ /*videoOutput!.startRecording(to: fileUrl, recordingDelegate: self)
548
+ self.videoRecordCompletionBlock = completion*/
549
+ }
550
+
551
+ func stopRecording(completion: @escaping (Error?) -> Void) {
552
+ guard let captureSession = self.captureSession, captureSession.isRunning else {
553
+ completion(CameraControllerError.captureSessionIsMissing)
554
+ return
555
+ }
556
+ // self.videoOutput?.stopRecording()
557
+ }
558
+ }
559
+
560
+ extension CameraController: UIGestureRecognizerDelegate {
561
+ func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
562
+ return true
563
+ }
564
+
565
+ @objc
566
+ func handleTap(_ tap: UITapGestureRecognizer) {
567
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
568
+
569
+ let point = tap.location(in: tap.view)
570
+ let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
571
+
572
+ do {
573
+ try device.lockForConfiguration()
574
+ defer { device.unlockForConfiguration() }
575
+
576
+ let focusMode = AVCaptureDevice.FocusMode.autoFocus
577
+ if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(focusMode) {
578
+ device.focusPointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
579
+ device.focusMode = focusMode
580
+ }
581
+
582
+ let exposureMode = AVCaptureDevice.ExposureMode.autoExpose
583
+ if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
584
+ device.exposurePointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
585
+ device.exposureMode = exposureMode
586
+ }
587
+ } catch {
588
+ debugPrint(error)
589
+ }
590
+ }
591
+
592
+ @objc
593
+ private func handlePinch(_ pinch: UIPinchGestureRecognizer) {
594
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
595
+
596
+ func minMaxZoom(_ factor: CGFloat) -> CGFloat { return max(1.0, min(factor, device.activeFormat.videoMaxZoomFactor)) }
597
+
598
+ func update(scale factor: CGFloat) {
599
+ do {
600
+ try device.lockForConfiguration()
601
+ defer { device.unlockForConfiguration() }
602
+
603
+ device.videoZoomFactor = factor
604
+ } catch {
605
+ debugPrint(error)
606
+ }
607
+ }
608
+
609
+ switch pinch.state {
610
+ case .began: fallthrough
611
+ case .changed:
612
+ let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
613
+ update(scale: newScaleFactor)
614
+ case .ended:
615
+ zoomFactor = device.videoZoomFactor
616
+ default: break
617
+ }
618
+ }
619
+ }
620
+
621
+ extension CameraController: AVCapturePhotoCaptureDelegate {
622
+ public func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
623
+ resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
624
+ if let error = error { self.photoCaptureCompletionBlock?(nil, error) } else if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil),
625
+ let image = UIImage(data: data) {
626
+ self.photoCaptureCompletionBlock?(image.fixedOrientation(), nil)
627
+ } else {
628
+ self.photoCaptureCompletionBlock?(nil, CameraControllerError.unknown)
629
+ }
630
+ }
631
+ }
632
+
633
+ extension CameraController: AVCaptureVideoDataOutputSampleBufferDelegate {
634
+ func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
635
+ guard let completion = sampleBufferCaptureCompletionBlock else { return }
636
+
637
+ guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
638
+ completion(nil, CameraControllerError.unknown)
639
+ return
640
+ }
641
+
642
+ CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
643
+ defer { CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) }
644
+
645
+ let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
646
+ let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
647
+ let width = CVPixelBufferGetWidth(imageBuffer)
648
+ let height = CVPixelBufferGetHeight(imageBuffer)
649
+ let colorSpace = CGColorSpaceCreateDeviceRGB()
650
+ let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue |
651
+ CGImageAlphaInfo.premultipliedFirst.rawValue
652
+
653
+ let context = CGContext(
654
+ data: baseAddress,
655
+ width: width,
656
+ height: height,
657
+ bitsPerComponent: 8,
658
+ bytesPerRow: bytesPerRow,
659
+ space: colorSpace,
660
+ bitmapInfo: bitmapInfo
661
+ )
662
+
663
+ guard let cgImage = context?.makeImage() else {
664
+ completion(nil, CameraControllerError.unknown)
665
+ return
666
+ }
667
+
668
+ let image = UIImage(cgImage: cgImage)
669
+ completion(image.fixedOrientation(), nil)
670
+
671
+ sampleBufferCaptureCompletionBlock = nil
672
+ }
673
+ }
674
+
675
+ enum CameraControllerError: Swift.Error {
676
+ case captureSessionAlreadyRunning
677
+ case captureSessionIsMissing
678
+ case inputsAreInvalid
679
+ case invalidOperation
680
+ case noCamerasAvailable
681
+ case unknown
682
+ }
683
+
684
+ public enum CameraPosition {
685
+ case front
686
+ case rear
687
+ }
688
+
689
+ extension CameraControllerError: LocalizedError {
690
+ public var errorDescription: String? {
691
+ switch self {
692
+ case .captureSessionAlreadyRunning:
693
+ return NSLocalizedString("Capture Session is Already Running", comment: "Capture Session Already Running")
694
+ case .captureSessionIsMissing:
695
+ return NSLocalizedString("Capture Session is Missing", comment: "Capture Session Missing")
696
+ case .inputsAreInvalid:
697
+ return NSLocalizedString("Inputs Are Invalid", comment: "Inputs Are Invalid")
698
+ case .invalidOperation:
699
+ return NSLocalizedString("Invalid Operation", comment: "invalid Operation")
700
+ case .noCamerasAvailable:
701
+ return NSLocalizedString("Failed to access device camera(s)", comment: "No Cameras Available")
702
+ case .unknown:
703
+ return NSLocalizedString("Unknown", comment: "Unknown")
704
+
705
+ }
706
+ }
707
+ }
708
+
709
+ extension UIImage {
710
+
711
+ func fixedOrientation() -> UIImage? {
712
+
713
+ guard imageOrientation != UIImage.Orientation.up else {
714
+ // This is default orientation, don't need to do anything
715
+ return self.copy() as? UIImage
716
+ }
717
+
718
+ guard let cgImage = self.cgImage else {
719
+ // CGImage is not available
720
+ return nil
721
+ }
722
+
723
+ 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 {
724
+ return nil // Not able to create CGContext
725
+ }
726
+
727
+ var transform: CGAffineTransform = CGAffineTransform.identity
728
+ switch imageOrientation {
729
+ case .down, .downMirrored:
730
+ transform = transform.translatedBy(x: size.width, y: size.height)
731
+ transform = transform.rotated(by: CGFloat.pi)
732
+ print("down")
733
+ break
734
+ case .left, .leftMirrored:
735
+ transform = transform.translatedBy(x: size.width, y: 0)
736
+ transform = transform.rotated(by: CGFloat.pi / 2.0)
737
+ print("left")
738
+ break
739
+ case .right, .rightMirrored:
740
+ transform = transform.translatedBy(x: 0, y: size.height)
741
+ transform = transform.rotated(by: CGFloat.pi / -2.0)
742
+ print("right")
743
+ break
744
+ case .up, .upMirrored:
745
+ break
746
+ }
747
+
748
+ // Flip image one more time if needed to, this is to prevent flipped image
749
+ switch imageOrientation {
750
+ case .upMirrored, .downMirrored:
751
+ transform.translatedBy(x: size.width, y: 0)
752
+ transform.scaledBy(x: -1, y: 1)
753
+ break
754
+ case .leftMirrored, .rightMirrored:
755
+ transform.translatedBy(x: size.height, y: 0)
756
+ transform.scaledBy(x: -1, y: 1)
757
+ case .up, .down, .left, .right:
758
+ break
759
+ }
760
+
761
+ ctx.concatenate(transform)
762
+
763
+ switch imageOrientation {
764
+ case .left, .leftMirrored, .right, .rightMirrored:
765
+ ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
766
+ default:
767
+ ctx.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
768
+ break
769
+ }
770
+ guard let newCGImage = ctx.makeImage() else { return nil }
771
+ return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
772
+ }
773
+ }
774
+
775
+ extension CameraController: AVCaptureFileOutputRecordingDelegate {
776
+ func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
777
+ /*if error == nil {
778
+ self.videoRecordCompletionBlock?(outputFileURL, nil)
779
+ } else {
780
+ self.videoRecordCompletionBlock?(nil, error)
781
+ }*/
782
+ }
783
+ }