You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:14:52