AVFoundation中如何判断当前摄像头并实现前后切换?
问题描述
我有一个CaptureDelegate类,包含setupCaptureSession方法,会在.task中调用:
class CaptureDelegate: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject { let captureSession = AVCaptureSession() // 捕获设备摄像头输入内容 let captureSessionQueue = DispatchQueue(label: "captureSessionQueue") // 处理图像的队列 @Published var cameraIsActive: Bool = false func setupCaptureSession(captureDevice: AVCaptureDevice?) { let videoCaptureDevice: AVCaptureDevice if let captureDevice { videoCaptureDevice = captureDevice } else { // 选择最佳后置摄像头(适配iPhone SE等单摄像头设备) if let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) { videoCaptureDevice = device } else if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) { videoCaptureDevice = device } else { fatalError("Missing expected back camera device.") } } guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else { // 后置摄像头输入 print( "Failed to create AVCaptureDeviceInput") return } let videoOutput = AVCaptureVideoDataOutput() // 摄像头捕获的输出 videoOutput.setSampleBufferDelegate(self, queue: captureSessionQueue) captureSession.beginConfiguration( ) if captureSession.canAddInput(videoDeviceInput) { captureSession.addInput(videoDeviceInput) } else { print("Failed to add input to capture session") } if captureSession.canAddOutput(videoOutput) { captureSession.addOutput(videoOutput) } else { print("Failed to add output to capture session") } captureSession.sessionPreset = .high captureSession.commitConfiguration() // 将新配置提交到CaptureSession DispatchQueue.global().async { self.captureSession.startRunning() } } }
该方法接收可选AVCaptureDevice参数,应用首次启动时会默认初始化后置摄像头。我希望允许用户在前后摄像头间切换,因此尝试在CaptureDelegate中添加一个可通过方法调用切换的@Published属性:
@Published var currentCamera: AVCaptureDevice? // 切换前后摄像头 func toggleCaptureDevice() { captureSession.stopRunning() // 这里需要判断当前使用的是前置还是后置摄像头,定义要切换的目标设备 // 调用setupCaptureSession并传入目标设备 }
我的问题是:如何判断应用当前使用的是前置还是后置摄像头,从而完成切换逻辑并更新currentCamera属性?
解决方案
1. 先给currentCamera赋值
在setupCaptureSession方法末尾添加一行代码,确保每次配置会话时都更新当前摄像头的引用:
// 在DispatchQueue.global().async之前添加 self.currentCamera = videoCaptureDevice
2. 通过position属性判断摄像头类型
AVCaptureDevice的position属性是枚举类型,包含.front(前置)、.back(后置)、.unspecified三种值,直接用它就能判断当前摄像头类型。
3. 完整实现切换逻辑
更新toggleCaptureDevice方法,处理移除旧输入、切换目标摄像头、重新配置会话的逻辑:
func toggleCaptureDevice() { // 所有会话操作放到专用队列,避免线程冲突 captureSessionQueue.async { self.captureSession.stopRunning() // 移除当前会话中的所有摄像头输入 self.captureSession.inputs.forEach { input in if let deviceInput = input as? AVCaptureDeviceInput { self.captureSession.removeInput(deviceInput) } } // 确定目标摄像头位置 let targetPosition: AVCaptureDevice.Position if let currentCam = self.currentCamera, currentCam.position == .back { targetPosition = .front } else { targetPosition = .back } // 获取目标摄像头设备 guard let targetDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: targetPosition) else { print("无法获取目标摄像头设备") self.captureSession.startRunning() return } // 更新currentCamera并重新配置会话 self.currentCamera = targetDevice self.setupCaptureSession(captureDevice: targetDevice) } }
关键注意点
- 必须移除旧输入:如果不先移除当前的
AVCaptureDeviceInput,直接添加新输入会导致会话报错,因为一个会话不能同时存在多个同类型的输入。 - 队列安全:
captureSession不是线程安全的,所有操作(停止、修改配置、启动)都要放到captureSessionQueue中执行,避免主线程阻塞或崩溃。 - 容错处理:获取目标摄像头时要做判空处理,避免部分特殊设备没有前置摄像头导致崩溃。
内容的提问来源于stack exchange,提问作者john smith
相关产品推荐
相关产品推荐

