@capgo/camera-preview 7.2.9 → 7.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/Plugin/CameraController.swift +108 -49
- package/ios/Plugin/Plugin.swift +48 -5
- package/package.json +1 -1
|
@@ -202,21 +202,35 @@ extension CameraController {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
func updateVideoOrientation() {
|
|
205
|
-
|
|
205
|
+
if Thread.isMainThread {
|
|
206
|
+
updateVideoOrientationOnMainThread()
|
|
207
|
+
} else {
|
|
208
|
+
DispatchQueue.main.async { [weak self] in
|
|
209
|
+
self?.updateVideoOrientationOnMainThread()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
206
213
|
|
|
214
|
+
private func updateVideoOrientationOnMainThread() {
|
|
207
215
|
let videoOrientation: AVCaptureVideoOrientation
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
|
|
217
|
+
// Use window scene interface orientation
|
|
218
|
+
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
|
219
|
+
switch windowScene.interfaceOrientation {
|
|
220
|
+
case .portrait:
|
|
221
|
+
videoOrientation = .portrait
|
|
222
|
+
case .landscapeLeft:
|
|
223
|
+
videoOrientation = .landscapeLeft
|
|
224
|
+
case .landscapeRight:
|
|
225
|
+
videoOrientation = .landscapeRight
|
|
226
|
+
case .portraitUpsideDown:
|
|
227
|
+
videoOrientation = .portraitUpsideDown
|
|
228
|
+
case .unknown:
|
|
229
|
+
fallthrough
|
|
230
|
+
@unknown default:
|
|
231
|
+
videoOrientation = .portrait
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
220
234
|
videoOrientation = .portrait
|
|
221
235
|
}
|
|
222
236
|
|
|
@@ -226,53 +240,98 @@ extension CameraController {
|
|
|
226
240
|
}
|
|
227
241
|
|
|
228
242
|
func switchCameras() throws {
|
|
229
|
-
guard let currentCameraPosition = currentCameraPosition,
|
|
230
|
-
|
|
243
|
+
guard let currentCameraPosition = currentCameraPosition,
|
|
244
|
+
let captureSession = self.captureSession else {
|
|
245
|
+
throw CameraControllerError.captureSessionIsMissing
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Ensure we have the necessary cameras
|
|
249
|
+
guard (currentCameraPosition == .front && rearCamera != nil) ||
|
|
250
|
+
(currentCameraPosition == .rear && frontCamera != nil) else {
|
|
251
|
+
throw CameraControllerError.noCamerasAvailable
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Store the current running state
|
|
255
|
+
let wasRunning = captureSession.isRunning
|
|
256
|
+
if wasRunning {
|
|
257
|
+
captureSession.stopRunning()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Begin configuration
|
|
231
261
|
captureSession.beginConfiguration()
|
|
262
|
+
defer {
|
|
263
|
+
captureSession.commitConfiguration()
|
|
264
|
+
// Restart the session if it was running before
|
|
265
|
+
if wasRunning {
|
|
266
|
+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
267
|
+
self?.captureSession?.startRunning()
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Store audio input if it exists
|
|
273
|
+
let audioInput = captureSession.inputs.first { ($0 as? AVCaptureDeviceInput)?.device.hasMediaType(.audio) ?? false }
|
|
232
274
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
275
|
+
// Remove only video inputs
|
|
276
|
+
captureSession.inputs.forEach { input in
|
|
277
|
+
if (input as? AVCaptureDeviceInput)?.device.hasMediaType(.video) ?? false {
|
|
278
|
+
captureSession.removeInput(input)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Configure new camera
|
|
283
|
+
switch currentCameraPosition {
|
|
284
|
+
case .front:
|
|
285
|
+
guard let rearCamera = rearCamera else {
|
|
286
|
+
throw CameraControllerError.invalidOperation
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Configure rear camera
|
|
290
|
+
try rearCamera.lockForConfiguration()
|
|
291
|
+
if rearCamera.isFocusModeSupported(.continuousAutoFocus) {
|
|
292
|
+
rearCamera.focusMode = .continuousAutoFocus
|
|
293
|
+
}
|
|
294
|
+
rearCamera.unlockForConfiguration()
|
|
295
|
+
|
|
296
|
+
if let newInput = try? AVCaptureDeviceInput(device: rearCamera),
|
|
297
|
+
captureSession.canAddInput(newInput) {
|
|
298
|
+
captureSession.addInput(newInput)
|
|
299
|
+
rearCameraInput = newInput
|
|
300
|
+
self.currentCameraPosition = .rear
|
|
301
|
+
} else {
|
|
302
|
+
throw CameraControllerError.invalidOperation
|
|
303
|
+
}
|
|
304
|
+
case .rear:
|
|
305
|
+
guard let frontCamera = frontCamera else {
|
|
306
|
+
throw CameraControllerError.invalidOperation
|
|
307
|
+
}
|
|
244
308
|
|
|
309
|
+
// Configure front camera
|
|
310
|
+
try frontCamera.lockForConfiguration()
|
|
311
|
+
if frontCamera.isFocusModeSupported(.continuousAutoFocus) {
|
|
312
|
+
frontCamera.focusMode = .continuousAutoFocus
|
|
313
|
+
}
|
|
314
|
+
frontCamera.unlockForConfiguration()
|
|
315
|
+
|
|
316
|
+
if let newInput = try? AVCaptureDeviceInput(device: frontCamera),
|
|
317
|
+
captureSession.canAddInput(newInput) {
|
|
318
|
+
captureSession.addInput(newInput)
|
|
319
|
+
frontCameraInput = newInput
|
|
245
320
|
self.currentCameraPosition = .front
|
|
246
321
|
} else {
|
|
247
322
|
throw CameraControllerError.invalidOperation
|
|
248
323
|
}
|
|
249
324
|
}
|
|
250
325
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let rearCamera = self.rearCamera else { throw CameraControllerError.invalidOperation }
|
|
255
|
-
|
|
256
|
-
self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)
|
|
257
|
-
|
|
258
|
-
captureSession.removeInput(frontCameraInput)
|
|
259
|
-
|
|
260
|
-
if captureSession.canAddInput(self.rearCameraInput!) {
|
|
261
|
-
captureSession.addInput(self.rearCameraInput!)
|
|
262
|
-
|
|
263
|
-
self.currentCameraPosition = .rear
|
|
264
|
-
} else { throw CameraControllerError.invalidOperation }
|
|
326
|
+
// Re-add audio input if it existed
|
|
327
|
+
if let audioInput = audioInput, captureSession.canAddInput(audioInput) {
|
|
328
|
+
captureSession.addInput(audioInput)
|
|
265
329
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
case .rear:
|
|
272
|
-
try switchToFrontCamera()
|
|
330
|
+
|
|
331
|
+
// Update video orientation
|
|
332
|
+
DispatchQueue.main.async { [weak self] in
|
|
333
|
+
self?.updateVideoOrientation()
|
|
273
334
|
}
|
|
274
|
-
|
|
275
|
-
captureSession.commitConfiguration()
|
|
276
335
|
}
|
|
277
336
|
|
|
278
337
|
func captureImage(completion: @escaping (UIImage?, Error?) -> Void) {
|
package/ios/Plugin/Plugin.swift
CHANGED
|
@@ -263,11 +263,54 @@ public class CameraPreview: CAPPlugin, CAPBridgedPlugin {
|
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
@objc func flip(_ call: CAPPluginCall) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
266
|
+
guard isInitialized else {
|
|
267
|
+
call.reject("Camera not initialized")
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
DispatchQueue.main.async { [weak self] in
|
|
272
|
+
guard let self = self else {
|
|
273
|
+
call.reject("Camera controller deallocated")
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Disable user interaction during flip
|
|
278
|
+
self.previewView.isUserInteractionEnabled = false
|
|
279
|
+
|
|
280
|
+
// Perform camera switch on background thread
|
|
281
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
282
|
+
var retryCount = 0
|
|
283
|
+
let maxRetries = 3
|
|
284
|
+
|
|
285
|
+
func attemptFlip() {
|
|
286
|
+
do {
|
|
287
|
+
try self.cameraController.switchCameras()
|
|
288
|
+
|
|
289
|
+
DispatchQueue.main.async {
|
|
290
|
+
self.cameraController.previewLayer?.frame = self.previewView.bounds
|
|
291
|
+
self.cameraController.previewLayer?.videoGravity = .resizeAspectFill
|
|
292
|
+
self.previewView.isUserInteractionEnabled = true
|
|
293
|
+
call.resolve()
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
retryCount += 1
|
|
297
|
+
|
|
298
|
+
if retryCount < maxRetries {
|
|
299
|
+
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 0.5) {
|
|
300
|
+
attemptFlip()
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
DispatchQueue.main.async {
|
|
304
|
+
self.previewView.isUserInteractionEnabled = true
|
|
305
|
+
print("Failed to flip camera after \(maxRetries) attempts: \(error.localizedDescription)")
|
|
306
|
+
call.reject("Failed to flip camera: \(error.localizedDescription)")
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
attemptFlip()
|
|
313
|
+
}
|
|
271
314
|
}
|
|
272
315
|
}
|
|
273
316
|
|