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 的实现机制,有助于优化音频性能(如低延迟)、解决兼容性问题,或为特定硬件定制音频功能。
相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习