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

Swift:录制视频时如何处理音频会话中断问题

iOS相机录制中断后无法保存视频的修复方案

核心问题分析

当前代码存在几个关键问题导致中断后相机冻结、视频无法保存:

  • 通知注册时机错误:在fileOutput(_:didStartRecordingTo:from:)中注册中断通知,每次录制都会重复注册,且中断发生后captureSession或audioDeviceInput可能被意外重置,导致回调逻辑失效。
  • removeAudioInput依赖audioDeviceInput的引用,但中断时该变量可能已被重置,无法正确移除音频输入,进而导致录制卡住。
  • 未针对不同中断类型(如来电、音频占用)执行明确的录制控制逻辑(停止保存/无音频继续录制)。

具体修复步骤

1. 提前注册中断通知

将通知注册移到CameraManager的初始化方法中,避免重复注册,确保回调始终能关联到有效的captureSession实例:

class CameraManager {
    var captureSession: AVCaptureSession?
    var audioDeviceInput: AVCaptureDeviceInput?
    var movieFileOutput: AVCaptureMovieFileOutput? // 确保持有录制输出的引用

    init() {
        setupNotifications()
    }

    private func setupNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionBegin), name: .AVCaptureSessionWasInterrupted, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnd), name: .AVCaptureSessionInterruptionEnded, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(audioSessionInterrupted), name: AVAudioSession.interruptionNotification, object: AVAudioSession.sharedInstance)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

2. 修复removeAudioInput逻辑

不再依赖audioDeviceInput的引用,直接遍历captureSession的输入找到音频输入并移除,确保即使引用丢失也能生效:

func removeAudioInput() {
    guard let session = captureSession else { return }
    session.beginConfiguration()
    // 遍历所有输入,找到音频类型的输入移除
    for input in session.inputs {
        if input is AVCaptureDeviceInput, input.device.hasMediaType(.audio) {
            session.removeInput(input)
            audioDeviceInput = nil
            break
        }
    }
    session.commitConfiguration()
}

3. 针对不同中断类型执行录制控制

添加停止录制并保存的方法,在sessionInterruptionBegin中区分中断原因,执行不同策略:

func stopRecordingAndSave() {
    guard let output = movieFileOutput, output.isRecording else { return }
    output.stopRecording()
    // 后续可在fileOutput(_:didFinishRecordingTo:from:error:)中处理文件保存逻辑
}

@objc func sessionInterruptionBegin(notification: Notification) {
    guard let reasonNumber = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as? NSNumber,
          let reason = AVCaptureSession.InterruptionReason(rawValue: reasonNumber.intValue) else {
        return
    }

    switch reason {
    case .audioDeviceInUseByAnotherClient, .audioDeviceNotAvailable:
        // 音频中断,移除音频输入后继续录制无音频视频
        removeAudioInput()
        captureSession?.startRunning()
    case .videoDeviceNotAvailableWithMultipleForegroundApps, .videoDeviceNotAvailableDueToSystemPressure:
        // 来电/系统压力等无法恢复的中断,直接停止录制并保存
        stopRecordingAndSave()
    default:
        // 其他未知中断,优先停止录制避免冻结
        stopRecordingAndSave()
    }
}

4. 优化音频中断回调逻辑

确保音频中断开始时正确移除音频输入,中断结束后按需恢复:

@objc func audioSessionInterrupted(notification: Notification) {
    guard let userInfo = notification.userInfo,
          let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
          let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
        return
    }

    switch type {
    case .began:
        removeAudioInput()
    case .ended:
        guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
        let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
        if options.contains(.shouldResume) {
            configureAudioSession()
        }
    @unknown default: ()
    }
}

5. 避免captureSession意外重置

检查代码中是否有其他地方会重置captureSession,确保中断期间captureSession的引用保持有效,不要随意重新初始化。

关键注意事项

  • 在fileOutput(_:didFinishRecordingTo:from:error:)回调中,即使返回错误,也要检查目标文件是否存在,尝试修复或保存已录制的部分内容。
  • 测试时覆盖多种中断场景:来电、闹钟、其他APP占用音频/相机、系统资源压力等。

内容的提问来源于stack exchange,提问作者p_dip

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 23:03:08