19、音频策略与录音:录音场景路由策略、录音设备选择、录音与播放并发路由

录音这个场景,说实话,比播放要复杂得多。播放嘛,声音出去就完了。录音不一样,你得考虑从哪录、录什么、录的时候播放怎么办。我在项目中遇到过好几次,录音路由没处理好,导致用户录出来的声音要么是空的,要么是杂音,要么干脆没声音。嗯,今天我们就来把录音路由这件事彻底讲清楚。

19.1 录音场景的路由策略

Android 的录音路由,核心逻辑其实就一句话:根据音频源类型选择最合适的输入设备。但这句话背后,藏着不少细节。

我们先看一张图,了解一下录音路由的整体流程:

录音路由决策流程 应用发起录音请求 判断 AudioSource 类型 VOICE_RECOGNITION CAMCORDER / MIC 优先选择麦克风阵列 选择主麦克风 执行路由策略 + 设备选择

这张图展示的是最核心的决策路径。你想想看,不同的录音场景,对麦克风的要求完全不一样。比如语音识别,它需要的是清晰的单声道信号,最好能降噪。而录像录音,它需要的是立体声,要能捕捉环境音。

19.2 录音设备选择

Android 系统里,录音设备的选择有一套完整的优先级逻辑。我个人的习惯是,先看 AudioSource,再看设备可用性,最后看策略配置。

常见的录音设备类型有这些:

设备类型 描述 典型场景
BUILTIN_MIC 内置主麦克风 普通录音、通话
BUILTIN_BACK_MIC 内置后置麦克风 降噪、视频录制
WIRED_HEADSET 有线耳机麦克风 语音通话、K歌
BLUETOOTH_SCO 蓝牙SCO麦克风 蓝牙通话
USB_HEADSET USB耳机麦克风 专业录音、直播

这里有个坑,我曾经踩过。有些设备上,BUILTIN_BACK_MIC 和 BUILTIN_MIC 其实是同一个物理麦克风,只是路由策略不同。你如果强行指定后置麦克风,在某些平台上反而会出问题。

核心原则:录音设备的选择,永远遵循「场景优先,设备次之」的原则。不要试图绕过 AudioSource 直接指定设备,除非你清楚知道自己在做什么。

19.3 录音与播放并发路由

这是整个音频路由里最头疼的部分。录音和播放同时进行时,路由策略会变得非常复杂。我举个例子你就明白了:

假设你在用微信语音通话,这时候又打开抖音看视频。微信占用了录音通道,抖音占用了播放通道。问题来了——抖音的声音应该从哪里出来?

Android 的处理逻辑是这样的:

  1. 先判断是否有活跃的录音会话。如果有,播放路由会受到限制。
  2. 检查录音的 AudioSource。如果是 VOICE_COMMUNICATION,系统会强制将播放路由到与录音相同的设备上。
  3. 处理设备独占性。有些设备(比如 USB 耳机)在录音时会被独占,播放只能走其他通道。

说白了,录音和播放的并发路由,核心就是解决「谁让谁」的问题。系统会优先保证录音的稳定性,因为录音对延迟和连续性要求更高。

小技巧:如果你在做直播或者K歌应用,建议使用 AudioManager 的 setPreferredDeviceForRecording() 方法来明确指定录音设备。这样可以避免系统自动路由带来的不确定性。

19.4 实际项目中的避坑指南

我曾经在一个项目中,遇到过一个非常诡异的问题。用户插上蓝牙耳机后,录音一直没声音。查了半天,发现是蓝牙 SCO 连接没有建立成功。系统以为蓝牙耳机可用,但实际上音频通道没打通。

这里给大家几个建议:

  • 录音前一定要检查设备状态。用 AudioDeviceInfo.isSource() 确认设备是否真的可用。
  • 监听设备变化。注册 AudioDeviceCallback,当录音设备被拔出时及时处理。
  • 不要假设设备类型。有些设备会报告自己是 WIRED_HEADSET,但实际上是 USB 耳机。
  • 处理并发时要考虑用户体验。比如录音时突然插入耳机,是切换录音设备还是保持原设备?这个需要产品层面决定。

特别注意:在 Android 10 及以上版本,录音权限管理更加严格。即使路由策略正确,如果没有获得 RECORD_AUDIO 权限,录音依然会失败。另外,某些设备上同时录音和播放可能会导致回声,需要配合回声消除(AEC)使用。

19.5 代码层面的实现思路

虽然我们不贴大段代码,但核心逻辑可以这样理解:

// 伪代码示意
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
AudioDeviceInfo[] devices = am.getDevices(AudioManager.GET_DEVICES_INPUTS);

// 根据场景选择设备
for (AudioDeviceInfo device : devices) {
    if (场景是语音识别) {
        if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC) {
            // 选择内置麦克风
            break;
        }
    } else if (场景是录像) {
        if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_BACK_MIC) {
            // 选择后置麦克风
            break;
        }
    }
}

// 设置录音设备
am.setPreferredDeviceForRecording(selectedDevice);

嗯,这里要注意,setPreferredDeviceForRecording 只是一个建议,系统不一定会采纳。真正可靠的方案,是在 AudioRecord 创建时直接指定设备。

好了,录音路由这块内容,说白了就是「场景驱动设备选择,并发时录音优先」。你在实际项目中遇到问题时,多从这两个角度去思考,基本都能找到答案。


公众号:蓝海资料掘金营,微信deep3321