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

iOS中使用AVAudioRecorder同时播放录音,如何避免捕获播放声音

解决方案:实现播放与录音的音频隔离

要在iOS上实现播放音频时录音不捕获播放输出,核心是利用系统内置的声学回声消除(AEC)能力,同时通过正确配置音频会话或使用更灵活的音频引擎来控制音频流路径。

方法1:优化AVAudioSession配置

AVAudioSession的.playAndRecord类别本身支持回声消除,但需要搭配特定模式和选项才能触发系统的AEC功能:

  • 使用.voiceChat模式:该模式默认启用强回声消除,专门针对语音通话场景优化
  • 移除.mixWithOthers选项:该选项会导致系统混合外部音频,可能干扰回声消除逻辑
  • 保留.defaultToSpeaker和.allowBluetoothA2DP以支持扬声器和蓝牙设备

示例代码:

import AVFoundation

do {
    let session = AVAudioSession.sharedInstance()
    try session.setCategory(.playAndRecord, 
                            mode: .voiceChat, 
                            options: [.defaultToSpeaker, .allowBluetoothA2DP])
    try session.setActive(true)
} catch {
    print("音频会话配置失败:\(error.localizedDescription)")
}

方法2:使用AVAudioEngine手动控制音频流

如果会话配置仍有问题,可以用AVAudioEngine直接管理输入输出,完全隔离播放和录音路径:

  1. 单独创建播放节点和录音处理逻辑
  2. 麦克风输入直接写入文件,不经过播放链路
  3. 音频文件通过独立播放节点输出,避免回路干扰

示例代码:

import AVFoundation

class AudioHandler {
    private let engine = AVAudioEngine()
    private let playerNode = AVAudioPlayerNode()
    private var recordingFile: AVAudioFile?
    
    func setupAudioSession() throws {
        let session = AVAudioSession.sharedInstance()
        try session.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
        try session.setActive(true)
    }
    
    func prepareRecordingAndPlayback() throws {
        // 配置播放链路
        engine.attach(playerNode)
        let mainMixer = engine.mainMixerNode
        engine.connect(playerNode, to: mainMixer, format: nil)
        
        // 配置录音链路
        let inputNode = engine.inputNode
        let inputFormat = inputNode.inputFormat(forBus: 0)
        
        // 创建录音文件
        let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let recordingURL = docsDir.appendingPathComponent("recording.caf")
        recordingFile = try AVAudioFile(forWriting: recordingURL, settings: inputFormat.settings)
        
        // 绑定麦克风输入到录音文件
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { [weak self] buffer, time in
            guard let self = self, let file = self.recordingFile else { return }
            try? file.write(from: buffer)
        }
        
        try engine.start()
    }
    
    func playAudio(from url: URL) throws {
        let audioFile = try AVAudioFile(forReading: url)
        playerNode.scheduleFile(audioFile, at: nil)
        playerNode.play()
    }
    
    func stopRecording() throws {
        recordingFile?.close()
        recordingFile = nil
    }
}

关键注意事项

  • 设备兼容性:iPhone 8及以后的设备内置AEC效果较好,旧款设备可能需要额外测试
  • 权限配置:确保已在Info.plist中添加NSMicrophoneUsageDescription和NSAppleMusicUsageDescription权限
  • 后台运行:若需后台操作,需开启"Background Modes"中的"Audio, AirPlay, and Picture in Picture"选项

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 17:30:02