AVCaptureSession添加音频输入延迟及FaceTime冲突问题求助
问题背景
开发的相机应用支持拍照和带声音录像,用户处于FaceTime通话状态时,相机画面冻结并抛出运行时错误:
AVError(_nsError: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (561017449), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x300723240 {Error Domain=NSOSStatusErrorDomain Code=561017449 "(null)" UserInfo={AVErrorFourCharCode='!pri'}}})
已排查信息
问题根源是AVCaptureSession初始化阶段激活麦克风的代码,原代码如下:
if let audioDevice = AVCaptureDevice.default(for: .audio), let audioDeviceInput = try? AVCaptureDeviceInput(device: audioDevice), session.canAddInput(audioDeviceInput) { session.addInput(audioDeviceInput) }
当前矛盾:
- 初始化时执行:无录制延迟,但FaceTime通话时相机直接报错
- 移到录制前执行:FaceTime下相机可正常工作,但录制触发后有明显延迟和短暂黑屏
- 尝试过在相机逻辑的DispatchQueue内/外执行、放在
session.beginConfiguration()和commitConfiguration()之间,均无改善
解决方案
1. 提前检测音频设备占用状态
初始化时先检查麦克风是否被其他应用(如FaceTime)占用,避免直接添加音频输入触发冲突:
func checkAudioDeviceAvailability() -> Bool { guard let audioDevice = AVCaptureDevice.default(for: .audio) else { return false } do { try audioDevice.lockForConfiguration() let isAvailable = !audioDevice.isExclusiveAccessGranted audioDevice.unlockForConfiguration() return isAvailable } catch { return false } }
初始化阶段只添加视频输入,若检测到麦克风可用则添加音频输入;若不可用,等到录制前再尝试获取。
2. 预加载音频输入但延迟激活
初始化时创建音频输入对象但不添加到session,录制前快速添加以减少开销:
// 类内存储预创建的音频输入 private var audioInput: AVCaptureDeviceInput? // 初始化流程 override func viewDidLoad() { super.viewDidLoad() setupCaptureSession() // 预创建音频输入,暂不加入session if let audioDevice = AVCaptureDevice.default(for: .audio) { do { audioInput = try AVCaptureDeviceInput(device: audioDevice) } catch { print("创建音频输入失败: \(error)") } } } // 录制按钮触发逻辑 func startRecording() { if let audioInput = audioInput, session.canAddInput(audioInput) { session.beginConfiguration() session.addInput(audioInput) session.commitConfiguration() } // 启动录制逻辑 }
预创建音频输入可避免录制时的设备枚举和初始化开销,降低延迟和黑屏概率。
3. 监听session错误并动态调整
添加AVCaptureSession错误监听,检测到音频权限冲突时自动移除音频输入并重启session:
session.addObserver(self, forKeyPath: "running", options: .new, context: nil) override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "running", let isRunning = change?[.newKey] as? Bool, !isRunning { guard let error = session.runtimeError as? NSError, error.code == -11800 else { return } // 移除音频输入 session.beginConfiguration() session.inputs.forEach { if let input = $0 as? AVCaptureDeviceInput, input.device.type == .audio { session.removeInput(input) } } session.commitConfiguration() // 重启session恢复相机功能 session.startRunning() } }
关键原理
- FaceTime通话时会独占麦克风的排他访问权限,初始化阶段直接添加音频输入会触发权限冲突,抛出
!pri(权限)相关错误 - 预创建音频输入对象能减少录制时的初始化开销,避免延迟和黑屏
- 动态检测设备状态并调整session输入,可在不影响正常录制体验的前提下兼容多应用音频占用场景
内容的提问来源于stack exchange,提问作者Xavi Moll

