第九板块:Android 多媒体体系 | 第二十三篇:AudioFlinger 与 AudioPolicyService 音频架构
所属板块:第九板块 --- Android 多媒体体系
前置知识:第二十二篇中的网络体系、Linux 内核驱动、Binder IPC、SELinux、Cgroup 资源隔离
本篇定位 :这是 Android 设备听觉体验的中央处理器 。如果说屏幕是输出视觉,那么音频系统就是输出声音的神经中枢 。本篇将彻底拆解 AudioFlinger 的混音(Mixing)引擎、AudioPolicyService 的策略决策逻辑、音频流(AudioTrack/AudioRecord) 的生命周期、音频焦点(Audio Focus) 的仲裁机制、低延迟音频(Fast Mixer) 的实现。我们将深入 HAL(Hardware Abstraction Layer) 、Kernel ALSA 驱动 与 Framework Native 层,揭示 Android 如何在多应用同时发声时,既保证低延迟又保证互不干扰。全程无音频播放代码、无音效调优指南,仅保留 Android 音频架构的底层定义与系统级调度规范。
1. 核心结论先行(Thesis Statement)
Android 的音频系统是一个基于策略与合成的实时流水线。
- AudioFlinger 的本质 :音频合成工厂 。它运行在 System Server 之外的独立 Native 进程中,负责接收所有应用的音频数据(PCM),进行混音(Mixing) 、重采样(Resampling) 、格式转换,并最终写入 HAL 接口。
- AudioPolicyService 的本质 :音频交通警。它运行在 System Server 中,负责制定规则:哪个设备(Speaker/Headset/BT)发声、音量多大、是否允许某个应用发声(音频焦点)。
- 音频流的本质 :共享内存(Shared Memory)的搬运 。应用通过
AudioTrack将 PCM 数据写入匿名共享内存(Ashmem),AudioFlinger 读取并混音。 - 低延迟的本质 :绕过普通混音器 。通过 Fast Mixer 线程,建立从应用到底层驱动的直通路径(Passthrough),减少缓冲区拷贝。
2. 音频架构全景图
2.1 从应用播放到喇叭发声
#mermaid-svg-A47lCb6tN3bwTzkF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-A47lCb6tN3bwTzkF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-A47lCb6tN3bwTzkF .error-icon{fill:#552222;}#mermaid-svg-A47lCb6tN3bwTzkF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-A47lCb6tN3bwTzkF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-A47lCb6tN3bwTzkF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-A47lCb6tN3bwTzkF .marker.cross{stroke:#333333;}#mermaid-svg-A47lCb6tN3bwTzkF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-A47lCb6tN3bwTzkF p{margin:0;}#mermaid-svg-A47lCb6tN3bwTzkF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-A47lCb6tN3bwTzkF .cluster-label text{fill:#333;}#mermaid-svg-A47lCb6tN3bwTzkF .cluster-label span{color:#333;}#mermaid-svg-A47lCb6tN3bwTzkF .cluster-label span p{background-color:transparent;}#mermaid-svg-A47lCb6tN3bwTzkF .label text,#mermaid-svg-A47lCb6tN3bwTzkF span{fill:#333;color:#333;}#mermaid-svg-A47lCb6tN3bwTzkF .node rect,#mermaid-svg-A47lCb6tN3bwTzkF .node circle,#mermaid-svg-A47lCb6tN3bwTzkF .node ellipse,#mermaid-svg-A47lCb6tN3bwTzkF .node polygon,#mermaid-svg-A47lCb6tN3bwTzkF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-A47lCb6tN3bwTzkF .rough-node .label text,#mermaid-svg-A47lCb6tN3bwTzkF .node .label text,#mermaid-svg-A47lCb6tN3bwTzkF .image-shape .label,#mermaid-svg-A47lCb6tN3bwTzkF .icon-shape .label{text-anchor:middle;}#mermaid-svg-A47lCb6tN3bwTzkF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-A47lCb6tN3bwTzkF .rough-node .label,#mermaid-svg-A47lCb6tN3bwTzkF .node .label,#mermaid-svg-A47lCb6tN3bwTzkF .image-shape .label,#mermaid-svg-A47lCb6tN3bwTzkF .icon-shape .label{text-align:center;}#mermaid-svg-A47lCb6tN3bwTzkF .node.clickable{cursor:pointer;}#mermaid-svg-A47lCb6tN3bwTzkF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-A47lCb6tN3bwTzkF .arrowheadPath{fill:#333333;}#mermaid-svg-A47lCb6tN3bwTzkF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-A47lCb6tN3bwTzkF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-A47lCb6tN3bwTzkF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A47lCb6tN3bwTzkF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-A47lCb6tN3bwTzkF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A47lCb6tN3bwTzkF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-A47lCb6tN3bwTzkF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-A47lCb6tN3bwTzkF .cluster text{fill:#333;}#mermaid-svg-A47lCb6tN3bwTzkF .cluster span{color:#333;}#mermaid-svg-A47lCb6tN3bwTzkF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-A47lCb6tN3bwTzkF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-A47lCb6tN3bwTzkF rect.text{fill:none;stroke-width:0;}#mermaid-svg-A47lCb6tN3bwTzkF .icon-shape,#mermaid-svg-A47lCb6tN3bwTzkF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-A47lCb6tN3bwTzkF .icon-shape p,#mermaid-svg-A47lCb6tN3bwTzkF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-A47lCb6tN3bwTzkF .icon-shape .label rect,#mermaid-svg-A47lCb6tN3bwTzkF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-A47lCb6tN3bwTzkF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-A47lCb6tN3bwTzkF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-A47lCb6tN3bwTzkF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 硬件
Linux 内核
硬件抽象层
System Server
应用进程
- 写入 PCM 数据
- 请求焦点
- 决策 (选设备/算音量)
- 混音/重采样
- pcm_write()
- DMA 传输
- I2S 信号
- 模拟信号
AudioTrack (Java/Native)
AudioManager (请求焦点)
AudioPolicyService
AudioFlinger
audio.primary.so
ALSA Driver (snd_pcm)
/dev/snd/pcmC0D0p
Audio Codec
Speaker/Headset
2.2 核心组件职责表
| 组件 | 层级 | 职责 | 学术定义 |
|---|---|---|---|
| AudioPolicyService | Framework | 决策者 | 管理音频路由、音量曲线、音频焦点、设备选择。 |
| AudioFlinger | Native | 执行者 | 管理音频流、混音线程、重采样、与 HAL 交互。 |
| PlaybackThread | Native | 工作线程 | AudioFlinger 中的线程,负责特定输出设备的混音循环。 |
| AudioTrack | Framework | 客户端接口 | 应用向音频系统输送数据的接口。 |
| AudioHAL | HAL | 硬件接口 | 厂商实现的库,屏蔽不同硬件的差异。 |
3. AudioFlinger 的混音引擎
3.1 音频流(Track)的生命周期
AudioFlinger 为每个播放请求创建一个 Track。
#mermaid-svg-JGb6gyKQEldgavi2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JGb6gyKQEldgavi2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JGb6gyKQEldgavi2 .error-icon{fill:#552222;}#mermaid-svg-JGb6gyKQEldgavi2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JGb6gyKQEldgavi2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JGb6gyKQEldgavi2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JGb6gyKQEldgavi2 .marker.cross{stroke:#333333;}#mermaid-svg-JGb6gyKQEldgavi2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JGb6gyKQEldgavi2 p{margin:0;}#mermaid-svg-JGb6gyKQEldgavi2 defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-JGb6gyKQEldgavi2 g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-JGb6gyKQEldgavi2 g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-JGb6gyKQEldgavi2 g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-JGb6gyKQEldgavi2 g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-JGb6gyKQEldgavi2 g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-JGb6gyKQEldgavi2 .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-JGb6gyKQEldgavi2 .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-JGb6gyKQEldgavi2 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-JGb6gyKQEldgavi2 .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-JGb6gyKQEldgavi2 .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-JGb6gyKQEldgavi2 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-JGb6gyKQEldgavi2 .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-JGb6gyKQEldgavi2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JGb6gyKQEldgavi2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JGb6gyKQEldgavi2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JGb6gyKQEldgavi2 .edgeLabel .label text{fill:#333;}#mermaid-svg-JGb6gyKQEldgavi2 .label div .edgeLabel{color:#333;}#mermaid-svg-JGb6gyKQEldgavi2 .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-JGb6gyKQEldgavi2 .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-JGb6gyKQEldgavi2 .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-JGb6gyKQEldgavi2 .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-JGb6gyKQEldgavi2 .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-JGb6gyKQEldgavi2 .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JGb6gyKQEldgavi2 .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JGb6gyKQEldgavi2 #statediagram-barbEnd{fill:#333333;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JGb6gyKQEldgavi2 .cluster-label,#mermaid-svg-JGb6gyKQEldgavi2 .nodeLabel{color:#131300;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-JGb6gyKQEldgavi2 .note-edge{stroke-dasharray:5;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-note text{fill:black;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram-note .nodeLabel{color:black;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagram .edgeLabel{color:red;}#mermaid-svg-JGb6gyKQEldgavi2 #dependencyStart,#mermaid-svg-JGb6gyKQEldgavi2 #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-JGb6gyKQEldgavi2 .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JGb6gyKQEldgavi2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建 AudioTrack
start()
flush()
pause()
start()
stop()
释放资源
Idle
Active
Flushing
Paused
Stopped
3.2 混音线程(PlaybackThread)
AudioFlinger 针对不同的输出设备创建不同的线程。
| 线程类型 | 特点 | 适用场景 |
|---|---|---|
| MixerThread | 最常用。支持多路混音,重采样。 | 音乐、游戏音效。 |
| DirectOutputThread | 直通。不经过混音器,直接写入 HAL。 | HDMI 源码输出、USB 音频。 |
| OffloadThread | 硬解码。将压缩数据(MP3/AAC)直接送给 DSP 解码。 | 音乐播放(省电)。 |
3.3 混音算法(Mixing)
AudioFlinger 使用 Fixed-point Arithmetic(定点运算) 进行混音,以保证性能和精度。
学术定义:
- 混音公式 :Output=∑(Inputi×Volumei)Output = \sum (Input_i \times Volume_i)Output=∑(Inputi×Volumei)
- 重采样(Resampling) :如果应用的采样率(如 44.1kHz)与硬件采样率(如 48kHz)不匹配,必须进行重采样。AudioFlinger 使用 SRC(Sample Rate Converter) 算法。
- 溢出处理 :混音后的值可能超过 16-bit 范围(-32768 ~ 32767),需要进行 Clipping(削波) 处理。
4. AudioPolicyService 的策略决策
4.1 音频焦点(Audio Focus)仲裁
当多个应用同时请求播放时,APS 决定谁有权发声。
学术定义:
- 焦点类型 :
AUDIOFOCUS_GAIN:长时间播放(音乐)。AUDIOFOCUS_GAIN_TRANSIENT:短暂播放(导航提示)。AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:短暂播放,允许原声音量降低(通知音)。
- 仲裁逻辑 :
- 新应用请求焦点。
- APS 检查当前焦点持有者。
- 根据焦点类型和优先级,决定是 抢占(Abandon) 还是 拒绝(Fail)。
AudioPolicyService 音乐应用 导航应用 AudioPolicyService 音乐应用 导航应用 #mermaid-svg-dOTHWSENFxpR1N0Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dOTHWSENFxpR1N0Y .error-icon{fill:#552222;}#mermaid-svg-dOTHWSENFxpR1N0Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dOTHWSENFxpR1N0Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dOTHWSENFxpR1N0Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dOTHWSENFxpR1N0Y .marker.cross{stroke:#333333;}#mermaid-svg-dOTHWSENFxpR1N0Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dOTHWSENFxpR1N0Y p{margin:0;}#mermaid-svg-dOTHWSENFxpR1N0Y .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dOTHWSENFxpR1N0Y text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-dOTHWSENFxpR1N0Y .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-dOTHWSENFxpR1N0Y .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-dOTHWSENFxpR1N0Y #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-dOTHWSENFxpR1N0Y .sequenceNumber{fill:white;}#mermaid-svg-dOTHWSENFxpR1N0Y #sequencenumber{fill:#333;}#mermaid-svg-dOTHWSENFxpR1N0Y #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-dOTHWSENFxpR1N0Y .messageText{fill:#333;stroke:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dOTHWSENFxpR1N0Y .labelText,#mermaid-svg-dOTHWSENFxpR1N0Y .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .loopText,#mermaid-svg-dOTHWSENFxpR1N0Y .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-dOTHWSENFxpR1N0Y .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-dOTHWSENFxpR1N0Y .noteText,#mermaid-svg-dOTHWSENFxpR1N0Y .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-dOTHWSENFxpR1N0Y .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dOTHWSENFxpR1N0Y .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dOTHWSENFxpR1N0Y .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dOTHWSENFxpR1N0Y .actorPopupMenu{position:absolute;}#mermaid-svg-dOTHWSENFxpR1N0Y .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-dOTHWSENFxpR1N0Y .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dOTHWSENFxpR1N0Y .actor-man circle,#mermaid-svg-dOTHWSENFxpR1N0Y line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-dOTHWSENFxpR1N0Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} requestFocus(GAIN)授予焦点 (AUDIOFOCUS_REQUEST_GRANTED)开始播放requestFocus(GAIN_TRANSIENT_MAY_DUCK)通知降低音量 (onAudioFocusChange)授予焦点播放导航音 (音乐音量变小)
4.2 设备路由选择
APS 根据设备连接状态和策略选择输出设备。
| 设备状态 | 路由决策 |
|---|---|
| 仅扬声器 | 路由到 Speaker。 |
| 插入有线耳机 | 路由到 Headset(强制切换)。 |
| 连接蓝牙 A2DP | 路由到 Bluetooth(如果未插耳机)。 |
| HDMI 连接 | 路由到 HDMI(数字音频)。 |
5. 低延迟音频(Fast Mixer)
5.1 普通路径 vs 快速路径
| 特性 | 普通路径 (MixerThread) | 快速路径 (Fast Mixer) |
|---|---|---|
| 缓冲区大小 | 大 (20-100ms) | 极小 (2-5ms) |
| 线程优先级 | 普通 | SCHED_FIFO (实时) |
| 重采样 | 支持 | 不支持 (必须匹配硬件) |
| 混音 | 多路混音 | 单路直通 |
| 适用场景 | 音乐、视频 | 游戏、VOIP、乐器 |
5.2 Fast Mixer 的实现
Fast Mixer 是一个独立的线程,绑定到特定的 CPU 核心,使用 FIFO 调度策略。
学术定义:
- SCHED_FIFO:实时调度策略。一旦线程运行,除非主动放弃或更高优先级抢占,否则不会被切换出去。
- CPU Affinity:将线程绑定到特定的 CPU 核心,避免缓存失效和上下文切换延迟。
6. 关键源码深度解析
6.1 AudioFlinger 的混音循环
cpp
// frameworks/av/services/audioflinger/Threads.cpp
void AudioFlinger::MixerThread::threadLoop() {
while (!exitPending()) {
// 1. 等待音频硬件就绪 (poll)
waitForPcmSync();
// 2. 准备混音缓冲区
memset(mMixBuffer, 0, mMixBufferSize);
// 3. 遍历所有活跃的 Track
for (const sp<Track>& track : mActiveTracks) {
// 读取 Track 数据
track->getNextBuffer(&buffer);
// 重采样
resample(buffer);
// 混音 (定点运算)
mix(mMixBuffer, buffer, track->volume);
}
// 4. 写入 HAL
mOutput->write(mMixBuffer, mMixBufferSize);
}
}
6.2 AudioPolicyService 的焦点管理
cpp
// frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
status_t AudioPolicyService::requestAudioFocus(...) {
// 1. 检查是否已有焦点持有者
sp<AudioFocusInfo> current = mFocusStack.top();
// 2. 检查优先级
if (newFocus.priority <= current.priority) {
// 抢占当前焦点
current->client->onAudioFocusChange(AUDIOFOCUS_LOSS);
mFocusStack.pop();
}
// 3. 授予新焦点
mFocusStack.push(newFocus);
return NO_ERROR;
}
7. 音频系统的常见误区
| 误区 | 学术解释 |
|---|---|
| 音量越大,声音越响 | 音量是数字增益(Digital Gain),过大会导致 Clipping(削波失真)。 |
| 采样率越高,音质越好 | 如果源文件是 44.1kHz,强行升到 48kHz 会有损音质(SRC 误差)。 |
| 蓝牙音频是无损的 | 绝大多数蓝牙音频使用 SBC 编码(有损压缩),LDAC/aptX HD 才是接近无损。 |
| 应用可以直接控制硬件 | 不可以。所有音频必须经过 AudioFlinger 混音,应用只能控制自己的 Track 音量。 |
8. 本篇总结(Knowledge Closure)
| 关键点 | 纯学术定义 |
|---|---|
| AudioFlinger 的本质 | 音频合成工厂,负责混音、重采样、格式转换。 |
| AudioPolicyService 的本质 | 音频交通警,负责路由、音量、焦点仲裁。 |
| 音频流路径 | 应用 -> AudioTrack -> Shared Memory -> AudioFlinger -> HAL -> Kernel。 |
| 低延迟实现 | Fast Mixer + SCHED_FIFO + CPU Affinity + 小缓冲区。 |
| 音频焦点 | 基于优先级的抢占式仲裁机制。 |
9. 第九板块结语
至此,第九板块:Android 多媒体体系 已完成核心架构解析。
我们从 AudioFlinger 的混音引擎 出发,深入 AudioPolicyService 的策略决策 ,探索 音频焦点的仲裁逻辑 ,最终抵达 低延迟音频的实时实现。
我们揭示了 Android 音频系统的设计哲学:用策略隔离应用,用混音共享硬件,用实时线程保障延迟。
下一篇预告 :第九板块:Android 多媒体体系 | 第二十四篇:Camera Service 与 HAL3 成像流水线