Android AudioFlinger 深度解析

> 适用平台: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 通过 EffectsFactoryEffectsChain 管理所有音效处理。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 处理通常在统一输出采样率下进行,因此:

  1. 各 Track 原始采样率 → 先重采样到输出采样率
  2. 重采样后数据 → Track Effects 处理
  3. 所有 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 完整调试清单

  1. adb shell dumpsys media.audio_flinger
    → 确认 PlaybackThread 存在且运行中
    → 确认 tracks 数量和 underruns 计数
  2. adb logcat | grep -i underrun
    → 找 underrun 发生的时间和频率
  3. 确认 bufferSizeInBytes
    → < 4096 容易 underrun,建议 ≥ 4096
  4. 确认采样率
    → track.getSampleRate() vs 输出采样率
    → 不一致时检查 log 有无重采样警告
  5. 抓 HAL dump
    → 对比 Framework 层和 HAL 层数据是否一致
  6. systrace
    → 分析 PlaybackThread 调度是否准时
  7. 测试不同场景
    → 放音乐、导航播报、免提通话分别测试

十、总结

| 维度 | 说明 |
|——|——|
| 定位 | Android 音频系统核心引擎,负责混音、调度、共享内存管理 |
| 核心机制 | mmap 共享内存 + PlaybackThread 周期性混音 |
| 线程模型 | PlaybackThread 负责混音,FastThread 负责极低延迟路径 |
| Underrun 根因 | App 写数据慢、Buffer 太小、重采样开销大、HAL 缓冲过大 |
| Effects 交互 | Track Effects 在混音前单轨处理,Output Effects 在混音后全局处理 |
| 调试入口 | dumpsys media.audio_flinger + logcat + systrace |
| 与 CarAudioService | CarAudioService 在上层做路由和 Focus,实际混音仍由 AudioFlinger 完成 |

发表评论