@capgo/camera-preview 3.2.3

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