> 适用平台:Android 通用框架(AAOS / 标准 Android)
> 适用版本:Android 12 / 13 / 14 / 15 / 16
AudioFlinger 是 Android 音频系统的核心引擎,运行在 system_server 进程中,负责所有音频播放的混音、路由和调度。
一、AudioFlinger 在音频架构中的位置
1.1 整体架构
┌─────────────────────────────────────────────────────────────┐
│ 应用层 (App) │
│ AudioTrack.write() / MediaPlayer / ToneGenerator │
└────────────────────────────┬────────────────────────────────┘
│ Binder IPC
┌────────────────────────────▼────────────────────────────────┐
│ AudioFlinger (system_server) │
│ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐ │
│ │ PlaybackThread │ │ RecordThread │ │ MmapThread │ │
│ │ (混音播放) │ │ (录音) │ │ (共享内存) │ │
│ └─────────────────┘ └──────────────────┘ └─────────────┘ │
└────────────────────────────┬────────────────────────────────┘
│ Passthrough / mmap
┌────────────────────────────▼────────────────────────────────┐
│ Audio HAL (hardware/interfaces/audio/) │
│ (厂商实现:AudioStreamOut / AudioStreamIn) │
└────────────────────────────┬────────────────────────────────┘
│
音频硬件 (DAC / DSP / I2S / PCM)
1.2 关键职责
| 职责 | 说明 |
|——|——|
| 音频混音 | 多个 AudioTrack 混成一个输出流 |
| 共享内存管理 | mmap 机制,App 与 AudioFlinger 之间零拷贝 |
| 线程调度 | PlaybackThread 周期性唤醒,消费数据 |
| 重采样 | 不同采样率的 Track 混音时需要重采样 |
| 音量处理 | 播放前应用音量曲线和设备选型 |
| Effects | 可选的音效处理链(低音增强、虚拟环绕等) |
二、核心数据结构
2.1 共享内存控制块(audio_track_cblk_t)
AudioTrack 与 AudioFlinger 之间通过 mmap 共享内存通信,核心是 audio_track_cblk_t 控制块:
struct audio_track_cblk_t {
void* buffers; // 共享内存基地址
uint32_t bufferCount;
uint32_t bufferSize; // 每个 buffer 字节数
uint32_t frameSize; // 每帧字节数
volatile uint32_t server; // AudioFlinger 已消费到的帧位置
volatile uint32_t user; // App 已写入的帧位置
uint32_t bufferEnd; // 共享内存环形缓冲区边界
android::Mutex mutex;
android::Condition cv;
volatile int32_t flowState;
uint32_t frameCount;
};
工作原理:
共享内存环形缓冲 ────────────────────────────────────►
[已消费][已消费][可读数据 ][可写区域][可写区域]
↑ ↑ ↑
server user bufferEnd
user - server = 尚未被消费的数据帧数
缓冲区满 → user 追上 server → App write() 阻塞
缓冲区空 → server 追上 user → AudioFlinger 输出静音
三、音频混音流程
3.1 正常播放路径
App: track.write(buffer)
→ AudioFlinger: PlaybackThread.threadLoop()
→ 依次执行:
① 检查各 Track 缓冲区状态(是否 underrun)
② 重采样(如果 Track 采样率 ≠ 输出采样率)
③ 混音(所有 Track 叠加)
④ 应用音量
⑤ 应用 Effects
⑥ 写入 HAL output stream
3.2 threadLoop 核心逻辑(简化版)
bool PlaybackThread::threadLoop() {
const uint32_t outFrames = mFrameCount;
float mixBuffer[outFrames * mChannelCount];
for (const auto& track : mTracks) {
if (track->isReady()) {
float* trackBuffer = track->getBuffer(outFrames);
for (uint32_t i = 0; i < outFrames; i++) {
mixBuffer[i] += trackBuffer[i] * track->volume;
}
}
}
mEffectsChain->process(mixBuffer, outFrames);
mOutput->write(mixBuffer, outFrames);
for (const auto& track : mTracks) {
track->updateServerPosition(outFrames);
}
return true;
}
四、线程模型
4.1 PlaybackThread 生命周期
AudioFlinger 收到 createTrack() 请求 → binder 线程处理请求 → 在 PlaybackThread 中创建 Track 对象 → PlaybackThread 被加入 ThreadLoop 调度 PlaybackThread 持续运行: → 每次 wakeUp() 后执行 threadLoop() → 从 mTracks 中收集数据混音 → 写 HAL output → 如果所有 Track 都没数据,sleep() 等待
4.2 线程优先级
| 线程 | 优先级 | 说明 |
|——|——–|——|
| PlaybackThread (low latency) | TPRIO_AUDIO_LOW | 音乐、游戏等低延迟场景 |
| PlaybackThread (mixed) | TPRIO_AUDIO_APP | 普通媒体播放 |
| RecordThread | TPRIO_AUDIO_REC | 录音 |
| FastThread (HAL callback) | TPRIO_AUDIO_SYS | FastMixer 路径,极低延迟 |
4.3 Fast Mixer(极低延迟路径)
Android 10+ 引入了 FastMixer,用于对延迟极度敏感的场景:
普通路径:App → AF(PlayThread) → HAL → 硬件(延迟 50ms+) Fast路径:App → FastTrack → FastThread → HAL(延迟 < 20ms)
FastMixer 使用更小的 buffer size,跳过部分重采样和 effects 链,直接走 HAL 的 fast path。
五、Buffer 管理与 Underrun
5.1 Underrun 产生原理
时间轴 ──────────────────────────────────────────►
[████████████ ] 缓冲区 60% 满,AudioFlinger 消费中
[ ] 缓冲区空了!
[ ] AudioFlinger 输出静音(silence)
↑
这里是 underrun
恢复瞬间:
[ ▓▓▓▓▓▓ ] 新数据到达 + DAC 突然有输出
↑
Pop 音
5.2 Underrun 的常见原因
| 原因 | 说明 |
|——|——|
| App 写数据太慢 | 主线程被 GC、CPU 抢占,write() 调用不及时 |
| Buffer 设置太小 | 缓冲区不足以覆盖主线程抖动 |
| Track 被高优先级抢占 | 导航播报等高优先级 Track 抢占 Mixer 时间片 |
| HAL 内部缓冲区过大 | 厂商实现的 HAL 内部缓冲太大,系统整体延迟上升 |
| 采样率不匹配 | 重采样开销大,AudioFlinger 来不及消费 |
5.3 Underrun 检测与日志
# 查看 AudioFlinger 状态(最重要) adb shell dumpsys media.audio_flinger # 输出示例: # PlaybackThread: # Track 0xb400007f4c4c2a00, name=Music, underruns=3 # Track 0xb400007f4c4c3b00, name=Navigation, underruns=0 # 过滤 underrun 相关 log adb logcat | grep -i "underrun|playbackthread|buffer"
5.4 Buffer Size 配置建议
| 使用场景 | bufferSizeInBytes | 缓冲时长 (48kHz) | 说明 |
|———|——————-|——————|——|
| 极低延迟(游戏/卡拉OK) | 1024-2048 | 5-10ms | 风险高,需要 App 层稳定 |
| 低延迟 | 4096 | ~21ms | 平衡之选 |
| 普通媒体 | 8192 | ~42ms | 推荐大多数场景 |
| 高稳定性(导航提示音) | 16384 | ~85ms | 抗抖动能力强 |
六、重采样机制
6.1 什么时候需要重采样
当多个 Track 采样率不一致时,AudioFlinger 必须重采样到统一的输出采样率:
Track A: 44.1kHz → 重采样到 48kHz → 混音
Track B: 48kHz → 直接混音
Track C: 96kHz → 重采样到 48kHz → 混音
↓
输出到 HAL (48kHz)
6.2 重采样性能影响
重采样计算耗时(48kHz 立体声 256 帧输出): Quality = LOW → ~0.5ms CPU 时间 Quality = MEDIUM → ~1.2ms CPU 时间 Quality = HIGH → ~2.5ms CPU 时间 如果 PlaybackThread 每 5ms 唤醒一次, 重采样占用超过 2ms 就会影响混音实时性。
七、AudioFlinger 与 Audio Effects 的交互
7.1 Effects 在音频架构中的位置
AudioFlinger 通过 EffectsFactory 和 EffectsChain 管理所有音效处理。Effects 位于混音之后、写入 HAL 之前:
各 Track 数据
↓
Resample(重采样)
↓
Track 级 Effects(可选,每个 Track 独立)
↓
混音器(Mixer)
↓
输出级 Effects(主 Effects 链)
↓
HAL Output Stream
7.2 两种 Effects 挂载方式
| 类型 | 作用域 | 典型用途 |
|——|——–|———-|
| Track Effects | 单个 AudioTrack | 卡拉OK人声消除、录音降噪 |
| Output Effects | 整个 Output Mix | 均衡器、虚拟环绕、低音增强 |
7.3 EffectsChain 核心结构
// frameworks/av/services/audioflinger/Effects.cpp
class EffectsChain {
sp<PlaybackThread> mThread; // 所属 PlaybackThread
Vector< sp<EffectModule> > mEffects; // 串起的 Effect 链表
// 输入/输出 buffer
float* mMixerBuffer; // 混音器输出
float* mOutputBuffer; // Effects 处理后输出
// 处理函数
status_t process_l(float* buffer, size_t frames);
};
EffectModule 结构:
class EffectModule {
sp<EffectInterface> mInterface; // HAL 接口代理
effect_descriptor_t mDescriptor; // Effect 描述(名称、UUID)
uint32_t mSampleRate; // 当前采样率
int mChannelMask; // 通道掩码
// 状态
bool mEnabled;
uint32_t mId; // 唯一标识
// 配置
effect_config_t mConfig; // 输入/输出配置(采样率、通道、buffer size)
};
7.4 Effects 处理流程(threadLoop 中的调用顺序)
bool PlaybackThread::threadLoop() {
// 1. 收集各 Track 数据,可能做重采样
for (track : mTracks) {
trackBuffer = track->getBuffer(frames);
// Track 级 Effects 处理(如果已attach)
for (effect : track->mEffects) {
effect->process(trackBuffer, frames);
}
}
// 2. 混音所有 Track
mixerBuffer = mixAllTracks(mTracks, frames);
// 3. Output 级 Effects 链处理
for (effect : mOutputEffectsChain->mEffects) {
effect->process(mixerBuffer, frames);
}
// 4. 写入 HAL
mOutput->write(mixerBuffer, frames);
}
7.5 创建并挂载 Effect 到 Track
// frameworks/av/media/libmedia/AudioEffect.cpp
// App 层创建 Effect
AudioEffect effect = new AudioEffect();
effect.setEffect(android.media.audiofx.BassBoost::class.java);
effect.attach(implementationId); // 指定具体 Effect 实现
// 或者通过 AudioTrack 直接创建(Android 12+)
AudioTrack track = new AudioTrack.Builder()
.setAudioAttributes(attrs)
.setBufferSizeInBytes(8192)
.build();
// 将 Effect attach 到 Track(需要 UID 权限)
track.attachEffect(effect.getId());
7.6 Output Effects 的加载与厂商实现
AudioFlinger 启动时通过 AudioPolicyService 加载 HAL 层的 Effects:
AudioPolicyService
→ AudioPolicyManager
→ 读取 audio_effects.conf 配置
→ 加载 vendor/lib/libaudioeq.so 等厂商实现
audio_effects.conf 示例:
libraries {
builtin:
path /vendor/lib/libeffects_builtin.so
bundle:
path /vendor/lib/libeffects_bundle.so
audio_visualizer:
path /vendor/lib/libvisualizer.so
audio_pre_processing:
path /vendor/lib/libaudio-pre-proc.so
}
effects {
bassboost {
library bundle
uuid f5268c21-3e19-4d30-b8e0-d5e2f7c7c3d5
}
equalizer {
library bundle
uuid 9538a8c1-4d30-4eef-a4c9-5c64e2e2e2e1
}
virtualizer {
library bundle
uuid 3c9b24f1-3e19-4d30-b8e0-d5e2f7c7c3d6
}
}
7.7 Effect UUID 与厂商实现
每个 Effect 由 UUID 唯一标识,UUID 决定使用哪个 HAL 实现:
Effect UUID 格式:
- 厂商必须实现 IEffect.hal 中定义的所有接口
- AudioFlinger 通过 UUID 查找对应 HAL 模块
- 同一个 Effect 可以有多个版本(不同厂商的优化实现)
// 常见 Effect UUIDs(标准 Android)
const effect_uuid_t EFFECT_UUID_BASSBOOST = { 0xf5268c21, ... };
const effect_uuid_t EFFECT_UUID_EQUALIZER = { 0x9538a8c1, ... };
const effect_uuid_t EFFECT_UUID_VIRTUALIZER = { 0x3c9b24f1, ... };
const effect_uuid_t EFFECT_UUID_REVERB = { 0xc8c56f21, ... };
7.8 Effect 参数配置
// 设置 Effect 参数(通过 IEffect HAL 接口) effect->setParameter(EFFECT_PARAM_BASS_LEVEL, 1000); // 0-1000 // 查询 Effect 状态 effect->getParameter(EFFECT_PARAM_BASS_LEVEL, &level); // 获取 Effect 实际生效的参数范围 effect_descriptor_t desc; effect->getDescriptor(&desc); // desc.effectUuid, desc.name, desc.implementationUuid 等
7.9 典型 Effect 处理流程示例:均衡器(Equalizer)
Equalizer 处理流程:
输入 PCM(44.1kHz 立体声)
↓
Band Split(频段分离)
├── Band 0: 20Hz-250Hz
├── Band 1: 250Hz-500Hz
├── Band 2: 500Hz-1kHz
├── Band 3: 1kHz-4kHz
└── Band 4: 4kHz-20kHz
↓
各 Band 独立增益调整(-12dB ~ +12dB)
↓
Band Merge(频段合并)
↓
输出 PCM(44.1kHz 立体声,增强后)
// Equalizer band 数量取决于厂商实现 // Android 标准定义最大 5 band,但高通/MTK 通常实现 32 band // 查询支持的频段数 int numBands = equalizer->numberOfBands(); int[] freqRange = equalizer->getBandFreqRange(); // 设置第 n 个频段的增益(单位毫贝) equalizer->setBandLevel(band, milliBels);
7.10 Effects 调试命令
# 查看当前活跃的 EffectsChain adb shell dumpsys media.audio_flinger | grep -A 20 "EffectsChain" # 查看具体 Effect 状态 adb shell dumpsys media.audio_flinger | grep -E "EffectModule|effect|Equalizer|BassBoost|Virtualizer" # 查看 HAL 层 Effect 实现 adb shell dumpsys media.audio_flinger | grep -i "uuid|descriptor" # Logcat 过滤 Effect 相关 adb logcat | grep -E "EffectModule|EffectsFactory|AudioEffect|process_l" # 抓取 Effect 处理前后的 PCM(需要 root) # 在 EffectsChain::process_l() 中加 dump
7.11 Effects 常见问题与排查
| 问题 | 可能原因 | 排查方法 |
|——|———-|———-|
| Effect 不生效 | Effect 未 attach 或未 enable | 确认 effect.attach() 和 effect.setEnable(true) 已调用 |
| 音效异常(爆音) | Effect 输出增益过大超出范围 | 检查 EFFECT_PARAM_VOLUME 设置 |
| 性能下降 | Effect 计算量大,CPU 跟不上 | systrace 看 AudioFlinger 线程是否按时完成 |
| 多 Effect 冲突 | 多个 Effect 串行处理顺序不对 | 调整 Effect attach 顺序 |
| HAL 层 Effect 加载失败 | UUID 不匹配或 so 库缺失 | 检查 audio_effects.conf 和 vendor lib |
7.12 Effect 与重采样的交互
Effects 处理通常在统一输出采样率下进行,因此:
- 各 Track 原始采样率 → 先重采样到输出采样率
- 重采样后数据 → Track Effects 处理
- 所有 Track 混音 → Output Effects 处理
如果 Track A 是 44.1kHz,Track B 是 48kHz:
Track A: 44.1kHz → 重采样到 48kHz → Track Effects → 混音
Track B: 48kHz → 直接 → Track Effects → 混音
↓
Output Effects (48kHz)
↓
HAL output
7.13 特殊场景:Voice Recognition 与 Effects
语音唤醒场景通常需要禁用所有 Output Effects,避免影响 ASR 识别:
// Voice Recognition 模式下 AudioFlinger 会 bypass 所有 Effects
// 由 CarAudioService 或 VoiceAssistantService 控制
if (usage == AudioUsage.VOICE_ASSISTANT) {
// 请求 Voice Recognition 路径(无 Effects)
audioAttributes.setFlags(AudioAttributes.FLAG_VOICE_BYPASS_EFFECTS);
}
八、与 CarAudioService 的关系
8.1 普通 Android vs 车载 Android
普通 Android:
App → AudioFlinger → AudioHAL → 硬件
车载 Android(AAOS):
App → CarAudioService → AudioFlinger → CarAudioHAL → 硬件
↑
多了 CarAudioService 作为中间层
负责多 Zone 管理、Audio Focus、车载特有路由
8.2 AudioFlinger 在车载中的角色
无论是否使用 CarAudioService,AudioFlinger 始终负责:
- 实际的混音计算
- mmap 共享内存管理
- HAL 接口调用
- 缓冲区调度
CarAudioService 在上层做 Zone 路由和 Focus 管理,混音和调度还是 AudioFlinger 完成。
九、调试方法
9.1 核心诊断命令
# 查看 AudioFlinger 全局状态(最重要) adb shell dumpsys media.audio_flinger # 查看具体 PlaybackThread 详情 adb shell dumpsys media.audio_flinger | grep -A 30 "PlaybackThread" # 查看各 Track 状态 adb shell dumpsys media.audio_flinger | grep -E "Track|underrun|latency" # 查看 HAL 层信息 adb shell dumpsys media.audio_flinger | grep -A 10 "StreamOut"
9.2 关键字段解读
| 字段 | 含义 | 正常值 |
|——|——|——–|
| underruns | underrun 次数 | 0 或偶尔 > 0 |
| latency | 端到端延迟估算 | 20-150ms |
| sampleRate | 当前输出采样率 | 44100 或 48000 |
| bufferSize | HAL 缓冲区大小 | 厂商实现相关 |
| tracks | 活跃 Track 数量 | 1-10(取决于使用场景) |
9.3 完整调试清单
adb shell dumpsys media.audio_flinger
→ 确认 PlaybackThread 存在且运行中
→ 确认 tracks 数量和 underruns 计数adb logcat | grep -i underrun
→ 找 underrun 发生的时间和频率- 确认 bufferSizeInBytes
→ < 4096 容易 underrun,建议 ≥ 4096 - 确认采样率
→ track.getSampleRate() vs 输出采样率
→ 不一致时检查 log 有无重采样警告 - 抓 HAL dump
→ 对比 Framework 层和 HAL 层数据是否一致 - systrace
→ 分析 PlaybackThread 调度是否准时 - 测试不同场景
→ 放音乐、导航播报、免提通话分别测试
十、总结
| 维度 | 说明 |
|——|——|
| 定位 | Android 音频系统核心引擎,负责混音、调度、共享内存管理 |
| 核心机制 | mmap 共享内存 + PlaybackThread 周期性混音 |
| 线程模型 | PlaybackThread 负责混音,FastThread 负责极低延迟路径 |
| Underrun 根因 | App 写数据慢、Buffer 太小、重采样开销大、HAL 缓冲过大 |
| Effects 交互 | Track Effects 在混音前单轨处理,Output Effects 在混音后全局处理 |
| 调试入口 | dumpsys media.audio_flinger + logcat + systrace |
| 与 CarAudioService | CarAudioService 在上层做路由和 Focus,实际混音仍由 AudioFlinger 完成 |