第十九章 音频系统(AudioFlinger):从启动到HAL的完整链路

音频系统,说白了就是Android设备发声和录音的“总指挥部”。我刚开始接触这部分时,觉得它就是个播放音乐的模块。后来才发现,音频系统涉及进程通信、策略决策、硬件抽象,复杂度远超想象。

这一章,我们聚焦AudioFlinger——它是音频系统的核心服务。我会从启动流程讲起,带你走通音频流管理、策略控制,最后落到HAL层交互。嗯,这些都是我在实际项目中踩过坑的地方。

19.1 AudioFlinger的启动:一场精心编排的初始化

AudioFlinger是Android的native服务,由mediaserver进程启动。它的启动顺序,决定了整个音频系统能否正常工作。

启动流程大致如下:

  1. mediaserver入口:main_mediaserver.cpp中调用AudioFlinger::instantiate()
  2. 服务注册:AudioFlinger继承自BnAudioFlinger,注册到ServiceManager
  3. 硬件抽象加载:通过HAL接口加载音频设备(primary、a2dp、usb等)
  4. 线程池创建:初始化MixerThread、RecordThread等工作线程
  5. 策略服务绑定:与AudioPolicyService建立双向通信

核心要点:AudioFlinger启动时,会加载HAL库中的audio_hw_device_t结构体。这个结构体包含了所有硬件操作函数指针。如果HAL加载失败,整个音频系统都会瘫痪。

我记得有一次,客户反馈设备没有声音。排查了半天,发现是HAL库的so文件权限不对,导致AudioFlinger加载时直接返回NULL。嗯,这种问题在系统集成时特别常见。

19.2 音频流管理:AudioTrack与AudioRecord

音频流管理,说白了就是“谁在播放,谁在录音”。Android提供了两个核心类:

  • AudioTrack:用于音频输出(播放)
  • AudioRecord:用于音频输入(录音)

它们都是客户端API,底层通过Binder与AudioFlinger通信。

19.2.1 AudioTrack的工作流程

创建一个AudioTrack,大致经历以下步骤:

  1. 应用层调用AudioTrack构造函数,传入采样率、通道数、格式等参数
  2. AudioTrack通过Binder调用AudioFlinger的createTrack()
  3. AudioFlinger根据音频策略,选择合适的输出设备和线程
  4. 在MixerThread中分配一个Track对象,返回给客户端
  5. 客户端通过共享内存(AudioBufferProvider)写入音频数据
// 典型的AudioTrack创建代码
AudioTrack* track = new AudioTrack(
    AUDIO_STREAM_MUSIC,       // 流类型
    44100,                    // 采样率
    AUDIO_FORMAT_PCM_16_BIT,  // 格式
    AUDIO_CHANNEL_OUT_STEREO, // 通道
    bufferSize,               // 缓冲区大小
    AUDIO_OUTPUT_FLAG_NONE    // 标志
);
track->start();

个人经验:缓冲区大小的选择很关键。太小会导致音频卡顿(underrun),太大会增加延迟。我一般建议游戏应用使用较小的缓冲区(~10ms),音乐播放使用较大的缓冲区(~50ms)。

19.2.2 AudioRecord的录音链路

录音流程与播放类似,但方向相反:

  • 客户端创建AudioRecord,指定音频源(如MIC)
  • AudioFlinger创建RecordThread,从HAL读取数据
  • 数据通过共享内存传递到客户端

这里有个坑:录音权限检查是在AudioFlinger侧做的。我曾经遇到一个bug,应用明明申请了权限,但录音还是失败。后来发现是AudioPolicyService的路由策略把录音源路由到了错误的设备上。

19.3 音频策略:AudioPolicyService的决策艺术

AudioPolicyService(简称APS)是音频系统的“大脑”。它决定:

  • 哪个应用的声音该从哪个设备输出
  • 多个音频流如何混合
  • 插入耳机时是否切换输出设备

APS与AudioFlinger的关系,就像指挥官与执行者。APS制定策略,AudioFlinger负责执行。

19.3.1 策略规则配置

策略规则定义在audio_policy_configuration.xml中。这个文件描述了:

配置项 说明
设备模块 primary、a2dp、usb等HAL模块
输出描述符 每个输出设备的属性(采样率、通道数等)
路由规则 流类型到输出设备的映射

举个例子,当插入耳机时,APS会检查路由规则,将AUDIO_STREAM_MUSIC从扬声器切换到耳机。这个过程是实时的,不需要应用层干预。

避坑指南:我曾经在定制车载系统时,修改了audio_policy_configuration.xml,结果导致蓝牙电话没有声音。原因是忘记配置AUDIO_STREAM_VOICE_CALL到蓝牙 SCO 的路由规则。所以,修改策略文件时一定要全面测试所有音频场景。

19.4 HAL层交互:AudioFlinger与硬件的桥梁

HAL(Hardware Abstraction Layer)是AudioFlinger与硬件驱动之间的接口。Android定义了标准的HAL接口,芯片厂商(如高通、MTK)负责实现。

19.4.1 HAL接口结构

核心结构体是audio_hw_device_t

struct audio_hw_device_t {
    struct hw_device_t common;
    // 打开/关闭输出流
    int (*open_output_stream)(...);
    int (*close_output_stream)(...);
    // 打开/关闭输入流
    int (*open_input_stream)(...);
    int (*close_input_stream)(...);
    // 设置参数
    int (*set_parameters)(...);
    int (*get_parameters)(...);
};

AudioFlinger通过dlopen()加载HAL库,然后调用这些函数指针。每个函数对应一个硬件操作。

19.4.2 数据流路径

从应用层到硬件的完整数据流:

  1. 应用写入AudioTrack缓冲区(用户空间)
  2. AudioFlinger的MixerThread混合多个Track
  3. 混合后的数据写入共享内存
  4. HAL的write()函数将数据送入内核驱动
  5. 驱动通过DMA传输到音频编解码器(Codec)
  6. Codec输出模拟信号到扬声器/耳机

录音路径则相反:麦克风信号 → Codec → DMA → 内核 → HAL read() → AudioFlinger → 应用。

关键点:HAL层的write()和read()是阻塞调用。如果硬件响应慢,会导致整个音频管道阻塞。我遇到过一个问题:某款低端芯片的HAL write()实现中,加了一个不必要的usleep(),导致音频播放出现周期性卡顿。去掉后问题解决。

19.5 知识体系总览

为了让你更直观地理解整个音频系统的架构,我画了一张图:

Android音频系统架构图 应用层 AudioTrack / AudioRecord / MediaPlayer / MediaRecorder Framework层 AudioFlinger(核心服务) AudioPolicyService(策略决策) HAL层 audio_hw_device_t / audio_stream_out_t / audio_stream_in_t 内核层 ALSA驱动 / DMA / 音频编解码器(Codec) Binder IPC 函数调用 硬件交互

这张图展示了音频数据的完整流转路径。从应用层到内核层,每一层都有明确的职责。AudioFlinger处于承上启下的位置,既要处理上层的音频流请求,又要通过HAL控制底层硬件。

19.6 实战中的常见问题

最后,分享几个我在项目中遇到的典型问题:

  • 音频焦点冲突:两个应用同时播放音乐,导致声音混乱。解决方案是正确使用AudioManager的requestAudioFocus()。
  • 设备切换延迟:插入耳机后,声音还在扬声器播放。原因是AudioPolicyService的路由切换逻辑有延迟,需要优化策略配置。
  • HAL兼容性问题:升级Android版本后,旧的HAL库不兼容新接口。建议在HAL实现中严格遵循版本规范。

我的建议:调试音频问题时,善用dumpsys media.audio_flingerdumpsys media.audio_policy命令。它们能输出当前音频系统的完整状态,包括所有Track、线程、设备信息。这是定位问题的第一手资料。

音频系统是个大话题,但核心脉络就是启动、管理、策略、HAL这四块。你只要把这条主线理清楚,遇到问题就能快速定位到具体环节。嗯,下一章我们会深入音频策略的细节,看看AudioPolicyService是如何做出那些“聪明”的决策的。


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