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