Android音频学习(十九)——音频HAL层简介

1. 功能

1.1设备管理

  • 控制音频设备(如扬声器、耳机、蓝牙)的打开/关闭、参数配置(采样率、声道数)。
  • 处理设备状态变化(如耳机插入检测)。

1.2音频数据传输

  • 提供 PCM 数据的读写接口(audio_stream_out 和 audio_stream_in)。
  • 管理 DMA 缓冲区,实现硬件与系统之间的高效数据传输。

1.3音效处理

  • 支持硬件加速的音效(如杜比全景声、均衡器)。
  • 通过 Effects HAL 接口应用音效链。

1.4低延迟支持

实现 AAudio 的 mmap(内存映射)接口,支持零拷贝低延迟播放/录音。

1.5策略执行

响应 AudioPolicyManager 的路由决策,切换输出/输入设备。

2. 处理流程

以下以 音频播放 为例,展示 Audio HAL 在完整流程中的角色:

2.1初始化阶段

  • 加载 HAL 模块
    AudioFlinger 通过 hw_get_module(AUDIO_HARDWARE_MODULE_ID) 加载厂商实现的 HAL 动态库(如 audio.primary.[platform].so)。
  • 创建设备实例
    调用 audio_hw_device_open(),返回 audio_hw_device 结构体,包含设备操作函数指针。

2.2配置音频流

  • 打开输出流
    调用 open_output_stream(),创建 audio_stream_out 对象,初始化 PCM 参数(采样率、位深、声道数)。
  • 设置硬件参数
    通过 set_parameters() 配置设备(如 sampling_rate=48000)。

2.3数据传输阶段

  • 写入数据
    AudioFlinger 调用 audio_stream_out->write(),将混合后的 PCM 数据传递给 HAL。
  • 硬件交互
    HAL 将数据写入 DMA 缓冲区,触发硬件中断驱动音频播放。

2.4设备切换与释放

  • 写入数据
    AudioFlinger 调用 audio_stream_out->write(),将混合后的 PCM 数据传递给 HAL。
  • 硬件交互
    HAL 将数据写入 DMA 缓冲区,触发硬件中断驱动音频播放。

3. 关键代码

3.1HAL接口定义

核心结构体

audio_hw_device,定义设备级操作接口,如设置参数,打开/关闭输出/输入流等,结构体定义路径:libhardware/include/hardware/audio.h

cpp 复制代码
struct audio_hw_device {
......
    /* set/get global audio parameters */
    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);

    /*
     * Returns a pointer to a heap allocated string. The caller is responsible
     * for freeing the memory for it using free().
     */
    char * (*get_parameters)(const struct audio_hw_device *dev,
                             const char *keys);
......
    int (*open_output_stream)(struct audio_hw_device *dev,
                              audio_io_handle_t handle,
                              audio_devices_t devices,
                              audio_output_flags_t flags,
                              struct audio_config *config,
                              struct audio_stream_out **stream_out,
                              const char *address);

    void (*close_output_stream)(struct audio_hw_device *dev,
                                struct audio_stream_out* stream_out);

    /** This method creates and opens the audio hardware input stream */
    int (*open_input_stream)(struct audio_hw_device *dev,
                             audio_io_handle_t handle,
                             audio_devices_t devices,
                             struct audio_config *config,
                             struct audio_stream_in **stream_in,
                             audio_input_flags_t flags,
                             const char *address,
                             audio_source_t source);

    void (*close_input_stream)(struct audio_hw_device *dev,
                               struct audio_stream_in *stream_in);
......

}

3.2厂商接口定义

以高通为例,代码路径:hardware/qcom/audio/hal/audio_hw.c,部分代码实现如下:

cpp 复制代码
static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address __unused)
{
......
        out->usecase = USECASE_AUDIO_PLAYBACK_VOIP;
        out->config = pcm_config_voip;
        out->config.rate = out->sample_rate;
        out->config.format = pcm_format_from_audio_format(out->format);
        buffer_size = get_stream_buffer_size(VOIP_PLAYBACK_PERIOD_DURATION_MSEC,
                                             out->sample_rate,
                                             out->format,
                                             out->config.channels,
                                             false /*is_low_latency*/);
        frame_size = audio_bytes_per_sample(out->format) * out->config.channels;
        out->config.period_size = buffer_size / frame_size;
        out->config.period_count = VOIP_PLAYBACK_PERIOD_COUNT;
        out->af_period_multiplier = 1;
......
}

该函数主要是设置硬件参数。

3.3HAL与AudioFlinger的交互

在之前得章节中已经介绍过,在此简单的回顾下:

  • HAL 模块加载

AudioFlinger 在初始化时通过 loadHwModule() 加载 HAL,

代码路径:

frameworks/av/services/audioflinger/AudioFlinger.cpp

sp<DeviceHalInterface> dev = mDevicesFactoryHal->openDevice(name.c_str());

  • 数据写入调用链 AudioFlinger::PlaybackThread::threadLoop_write() → HAL::AudioStreamOut::write()。

4.HAL版本与兼容性

  • 传统 HAL(Legacy HAL)
  1. 基于 hw_module_t 和函数指针,Android 7.0 及之前版本使用。
  2. 接口定义在 audio.h 和 audio_policy.h。
  • HIDL HAL (Android 8.0+)
  1. 使用 HIDL(Hal Interface Definition Language)定义接口,提升稳定性和兼容性。
  2. 接口文件示例:
    hardware/interfaces/audio/core/4.0/IDevicesFactory.hal
  • AIDL HAL (Android 12+)
  1. 进一步升级为 AIDL(Android Interface Definition Language),支持更灵活的扩展。

5.调试与工具

5.1查看HAL日志

adb logcat | grep audio_hw

5.2参数查看与修改

adb shell tinymix -D 0 # 查看 Mixer 控件

adb shell tinypcminfo # 查看 PCM 设备信息

5.3直接测试硬件

adb shell tinyplay test.wav # 播放音频文件

adb shell tinycap record.wav # 录制音频

6.常见问题与解决

6.1无声音或杂音

  • 检查 HAL 配置 :确认 PCM 格式(采样率、位深)与硬件匹配。
  • 调试 DMA 传输 :通过 tinymix 确认数据路径是否正常。

6.2高延迟

  • 启用低延迟模式 :配置 AUDIO_OUTPUT_FLAG_FAST 标志,使用 AAudio API
  • 优化缓冲区大小 :确保 HAL 缓冲区与 AudioFlinger 配置一致。

6.3设备切换失败

  • 验证路由策略 :检查 set_parameters() 是否正确处理设备 ID。
  • HAL 日志分析 :确认设备打开/关闭流程无错误。

7.总结

  • 功能定位 :Audio HAL 是 Android 音频系统的硬件桥梁,标准化硬件操作,隔离厂商差异。
  • 处理流程 :从初始化、配置、数据传输到资源释放,贯穿音频生命周期。
  • 关键代码 :以 audio_hw_device 和 audio_stream_out 为核心的结构体与接口实现。
  • 调试要点 :结合日志、系统工具(如 tinymix)和硬件验证,定位 HAL 层问题。
  • 理解 Audio HAL 的实现机制,有助于优化音频性能(如低延迟)、解决兼容性问题,或为特定硬件定制音频功能。
相关推荐
木木子99994 小时前
MongoDB集合学习笔记
笔记·学习·mongodb
Nan_Shu_6145 小时前
学习:uniapp全栈微信小程序vue3后台 (25)
前端·学习·微信小程序·小程序·uni-app
darkb1rd5 小时前
命令注入(Command Injection)漏洞学习笔记
笔记·学习
charlie1145141915 小时前
Chrome 学习小记5——demo:(动态壁纸基础)
chrome·windows·学习
weixin_377634847 小时前
【大模型-金融】Trading-R1 多阶段课程学习
学习·金融
今天也好累9 小时前
贪心算法之会议安排问题
c++·笔记·学习·算法·贪心算法
无敌的大魔王10 小时前
学习Java遇到的一些问题
学习
创业之路&下一个五年13 小时前
高系分二十:微服务系统分析与设计
学习·微服务·总结
GoldenaArcher13 小时前
Postman 学习笔记 II:测试、断言与变量管理
笔记·学习·postman