12、音频路由与策略:AudioPolicyService、路由策略(听筒/扬声器/蓝牙)、动态切换对延时的影响
音频路由,说白了就是决定声音从哪条路出去、从哪条路进来。你插上耳机,声音自动切到耳机;拔掉耳机,声音回到扬声器。这个看似简单的动作,背后是 AudioPolicyService 在忙活。
我个人习惯把音频路由比作「交通调度」。AudioPolicyService 就是那个交通指挥中心,它要根据当前的路况(设备连接状态)、乘客的请求(音频流类型)、以及交通规则(策略配置),来决定走哪条路。
12.1 AudioPolicyService 的核心职责
AudioPolicyService(简称 APS)是 Android 音频系统的策略大脑。它不负责具体的音频数据搬运,那是 AudioFlinger 的事。APS 只做一件事:决策。
- 设备选择:当前应该用哪个音频设备输出?扬声器?耳机?蓝牙?
- 路由切换:当设备插拔时,如何平滑切换?
- 策略执行:不同应用、不同音频类型,走不同的路由策略。
我在项目中遇到过一个问题:某个定制平板,插上 USB-C 耳机后,声音还是从扬声器出来。查了半天,发现是 APS 的音频策略配置文件(audio_policy_configuration.xml)里,USB 设备的优先级配错了。嗯,这种坑其实挺常见的。
12.2 路由策略:听筒、扬声器、蓝牙
Android 定义了几种典型的音频路由策略。每种策略对应不同的使用场景,也对应不同的延时特性。
| 路由策略 | 典型场景 | 路径延时 | 特点 |
|---|---|---|---|
| 听筒(Earpiece) | 通话 | 低(~10ms) | 近距离、小功率、低延时 |
| 扬声器(Speaker) | 免提、媒体播放 | 中(~20ms) | 功率大、可能经过 DSP 处理 |
| 有线耳机(Wired Headset) | 音乐、通话 | 低(~10ms) | 模拟直连,延时最低 |
| 蓝牙 SCO | 蓝牙通话 | 高(~40-60ms) | 需要编解码、传输、缓冲 |
| 蓝牙 A2DP | 蓝牙音乐 | 高(~100-200ms) | 高音质、大缓冲、高延时 |
你想想看,为什么蓝牙通话的延时比蓝牙音乐低那么多?因为 SCO 走的是同步面向连接链路,数据包小、间隔短。而 A2DP 为了音质,用了更大的缓冲区和更复杂的编码器。这就是典型的「延时 vs 质量」的取舍。
12.3 动态切换:延时杀手
动态路由切换,是低延时优化里最容易被忽视的环节。我见过太多项目,静态延时测出来很漂亮,但一切换设备,咔,卡顿、爆音、延时飙升全来了。
为什么会这样?因为切换不是瞬间完成的。APS 需要:
- 检测到设备状态变化(比如蓝牙断开)
- 查询策略配置,确定新设备
- 通知 AudioFlinger 关闭旧设备、打开新设备
- 重新建立音频通路
- 清空旧缓冲区,填充新缓冲区
每一步都有开销。尤其是第 5 步,缓冲区清空和重新填充,如果处理不好,就会引入几十甚至上百毫秒的额外延时。
12.4 路由策略配置:audio_policy_configuration.xml
APS 的策略配置,核心文件是 audio_policy_configuration.xml。这个文件定义了所有音频设备、设备之间的连接关系、以及路由规则。
我曾经在一个项目中,为了降低蓝牙切换的延时,修改了这个配置文件。具体做法是:
- 减少蓝牙设备的路由候选数量,让 APS 更快做出决策
- 为通话场景单独配置一条「快速路径」,跳过不必要的 DSP 处理
下面是一个简化的配置片段:
<!-- audio_policy_configuration.xml 片段 -->
<audioPolicyConfiguration version="1.0">
<globalConfiguration speaker_drc_enabled="false"/>
<modules>
<module name="primary" halVersion="3.0">
<attachedDevices>
<item>Speaker</item>
<item>Earpiece</item>
<item>Wired Headset</item>
</attachedDevices>
<routeStrategy name="phone_call">
<!-- 通话优先走听筒 -->
<device address="Earpiece" />
<!-- 如果听筒不可用,走有线耳机 -->
<device address="Wired Headset" />
<!-- 最后才走蓝牙 SCO -->
<device address="BT SCO" />
</routeStrategy>
</module>
</modules>
</audioPolicyConfiguration>
注意看 routeStrategy 里的设备顺序。这个顺序决定了 APS 选择设备的优先级。如果你把蓝牙 SCO 放在第一位,那每次通话都会走蓝牙,即使有线耳机插着。这显然不是我们想要的。
12.5 动态切换的延时优化技巧
说几个我实际用过的优化手段:
在设备切换前,提前创建好目标设备的 AudioPatch。这样切换时只需要做「切换」操作,不需要「创建+切换」。我在一个车载项目中用过这个方案,切换延时从 80ms 降到了 15ms。
路由策略的层级越深,APS 决策越慢。尽量把常用的设备放在顶层,减少条件判断。说白了,就是让 APS 少动脑子。
有些应用会在蓝牙连接不稳定时,反复在扬声器和蓝牙之间切换。每次切换都会触发路由重建,导致音频流频繁中断。我曾经遇到一个 case,蓝牙信号弱的时候,每秒切换 3-4 次,音频完全没法听。解决方案是加一个「切换去抖」逻辑:检测到设备状态变化后,等待 200ms 再执行切换,避免抖动。
12.6 路由策略对延时的综合影响
下面这张图,是我自己总结的音频路由决策流程。你可以看到,从应用发起请求到最终声音输出,中间经过了多个决策节点。每个节点都可能引入延时。
从这张图可以看得很清楚:路由策略的选择,直接决定了后续路径的延时基数。你选了 A2DP,那后面再怎么优化,也绕不开蓝牙编解码和传输的延时。所以,低延时优化的第一步,就是选对路由策略。
12.7 实战建议
最后,给几个我自己的实战建议:
- 能走有线,别走无线。有线耳机的延时是最低的,没有之一。
- 通话场景,优先 SCO。如果必须用蓝牙,SCO 的延时比 A2DP 低得多。
- 动态切换时,加去抖。别让 APS 在设备不稳定时反复切换。
- 预创建 AudioPatch。这是降低切换延时的最有效手段,没有之一。
- 定期检查策略配置。有时候问题就出在 audio_policy_configuration.xml 里,设备优先级配错了。
嗯,关于音频路由和策略,今天就聊到这儿。这部分内容虽然看起来偏「策略」层面,但它对延时的影响,一点都不比底层优化小。选对路,比跑得快更重要。
公众号:蓝海资料掘金营,微信deep3321