2. 音频硬件抽象层(HAL)基础
音频HAL层是Android音频系统的核心桥梁,它位于Android Framework与底层音频硬件驱动之间。本章将深入剖析HAL层的接口定义、模块加载机制以及核心数据结构,为后续的驱动适配实战打下坚实基础。
2.1 HAL接口定义与模块加载机制
Android HAL采用模块化设计,每个硬件抽象层模块通过标准接口进行加载和卸载。音频HAL模块遵循这一通用规范,同时定义了音频特有的接口。
2.1.1 HAL模块结构体
每个HAL模块必须实现hw_module_t结构体,音频HAL在此基础上扩展为audio_module_t。模块加载时,系统通过hw_get_module()函数查找并加载对应的共享库。
关键点:HAL模块的加载路径通常为
/vendor/lib/hw/或/system/lib/hw/,库命名规则为<MODULE_ID>.<variant>.so,例如audio.primary.tiny.so。
// hardware/libhardware/include/hardware/hardware.h
typedef struct hw_module_t {
uint32_t tag; // 必须为 HARDWARE_MODULE_TAG
uint16_t module_api_version;
uint16_t hal_api_version;
const char *id; // 模块ID,如 AUDIO_HARDWARE_MODULE_ID
const char *name;
const char *author;
struct hw_module_methods_t* methods; // 模块方法表
void* dso; // 动态库句柄
uint32_t reserved[32];
} hw_module_t;
// audio HAL 扩展
typedef struct audio_module {
struct hw_module_t common; // 通用模块结构体
} audio_module_t;
2.1.2 模块加载流程
音频HAL模块的加载遵循以下步骤,整个过程由AudioFlinger在系统启动时触发:
- 调用
hw_get_module():传入模块ID(AUDIO_HARDWARE_MODULE_ID)和指向hw_module_t指针的地址。 - 查找共享库:系统按优先级顺序扫描
/vendor/lib/hw/、/system/lib/hw/等路径。 - 动态加载:使用
dlopen()加载共享库,并通过dlsym()获取HAL_MODULE_INFO_SYM符号。 - 验证模块:检查
tag字段和module_api_version。 - 调用
open()方法:通过hw_module_methods_t::open创建hw_device_t实例。
实战提示:在调试HAL加载问题时,可以使用
adb shell lsof | grep audio查看已加载的音频HAL库,或通过adb shell dumpsys media.audio_flinger检查HAL状态。
2.1.3 模块加载流程图
下图展示了音频HAL模块从系统调用到硬件驱动的完整加载链路:
2.2 audio_hw_device 结构体解析
audio_hw_device是音频HAL层的核心设备结构体,它封装了所有音频设备操作接口。每个音频HAL实现必须创建一个该结构体的实例,并填充其函数指针。
2.2.1 结构体定义
// hardware/libhardware/include/hardware/audio.h
struct audio_hw_device {
struct hw_device_t common; // 继承自通用设备结构体
// 模块描述信息
uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
// 初始化与配置
int (*init_check)(const struct audio_hw_device *dev);
int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
int (*set_master_volume)(struct audio_hw_device *dev, float volume);
int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);
// 输入输出流管理
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);
int (*open_output_stream)(struct audio_hw_device *dev,
audio_io_handle_t handle,
audio_devices_t devices,
struct audio_config *config,
struct audio_stream_out **stream_out,
audio_output_flags_t flags,
const char *address,
audio_source_t source);
int (*close_input_stream)(struct audio_hw_device *dev,
struct audio_stream_in *stream_in);
int (*close_output_stream)(struct audio_hw_device *dev,
struct audio_stream_out *stream_out);
// 路由与场景管理
int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
char *(*get_parameters)(const struct audio_hw_device *dev,
const char *keys);
int (*init)(const struct audio_hw_device *dev);
int (*set_audio_port_config)(const struct audio_hw_device *dev,
const struct audio_port_config *config);
// ... 其他接口
};
2.2.2 关键接口说明
| 接口函数 | 功能描述 | 适配注意事项 |
|---|---|---|
init_check() |
检查硬件是否初始化成功 | 返回0表示成功,负值表示失败 |
open_output_stream() |
创建音频输出流 | 需根据audio_output_flags_t配置不同的输出路径 |
open_input_stream() |
创建音频输入流 | 需处理audio_source_t(如MIC、CAMCORDER) |
set_parameters() |
设置设备参数(键值对形式) | 常见参数:routing、bt_sco、screen_state |
get_parameters() |
获取设备参数 | 返回以分号分隔的键值对字符串 |
常见陷阱:在实现
open_output_stream()时,必须正确处理audio_config中的采样率、通道数和格式。如果硬件不支持请求的配置,应返回-ENOSYS或尝试最接近的匹配配置,而不是直接返回成功但使用错误参数。
2.3 audio_stream_in 与 audio_stream_out 结构体解析
音频流结构体是实际进行音频数据读写的接口。输出流负责将音频数据写入硬件,输入流负责从硬件读取音频数据。
2.3.1 audio_stream_out 结构体
struct audio_stream_out {
struct audio_stream common; // 继承自通用流结构体
// 写入音频数据(阻塞)
ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,
size_t bytes);
// 获取渲染位置
int (*get_render_position)(const struct audio_stream_out *stream,
uint32_t *dsp_frames);
// 获取延迟时间(微秒)
uint32_t (*get_latency)(const struct audio_stream_out *stream);
// 设置音量
int (*set_volume)(struct audio_stream_out *stream, float left,
float right);
// 暂停/恢复
int (*pause)(struct audio_stream_out *stream);
int (*resume)(struct audio_stream_out *stream);
// 获取缓冲区大小
size_t (*get_buffer_size)(const struct audio_stream_out *stream);
// 获取帧计数
int (*get_frame_count)(const struct audio_stream_out *stream);
// 设置回调(用于低延迟场景)
int (*set_callback)(struct audio_stream_out *stream,
stream_callback_t callback, void *cookie);
};
2.3.2 audio_stream_in 结构体
struct audio_stream_in {
struct audio_stream common; // 继承自通用流结构体
// 读取音频数据(阻塞)
ssize_t (*read)(struct audio_stream_in *stream, void* buffer,
size_t bytes);
// 设置增益
int (*set_gain)(struct audio_stream_in *stream, float gain);
// 获取输入缓冲大小
size_t (*get_buffer_size)(const struct audio_stream_in *stream);
// 获取帧计数
int (*get_frame_count)(const struct audio_stream_in *stream);
// 获取捕获位置
int (*get_capture_position)(const struct audio_stream_in *stream,
int64_t *frames, int64_t *time);
// 启动/停止输入
int (*start)(struct audio_stream_in *stream);
int (*stop)(struct audio_stream_in *stream);
// 获取输入帧大小(字节)
size_t (*get_input_frames_lost)(struct audio_stream_in *stream);
};
2.3.3 通用流结构体
输入输出流都继承自audio_stream,该结构体定义了流的通用属性和操作:
struct audio_stream {
// 流的句柄
audio_hw_device_t *device;
// 流参数
audio_format_t format; // 音频格式(如PCM_16_BIT)
uint32_t sample_rate; // 采样率(如44100)
audio_channel_mask_t channel_mask; // 通道掩码
// 流操作
uint32_t (*get_sample_rate)(const struct audio_stream *stream);
int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
size_t (*get_buffer_size)(const struct audio_stream *stream);
audio_format_t (*get_format)(const struct audio_stream *stream);
int (*set_format)(struct audio_stream *stream, audio_format_t format);
audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
audio_channel_mask_t (*set_channels)(struct audio_stream *stream,
audio_channel_mask_t mask);
int (*standby)(struct audio_stream *stream);
int (*dump)(int fd, const struct audio_stream *stream);
int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs);
char *(*get_parameters)(const struct audio_stream *stream,
const char *keys);
int (*add_audio_effect)(const struct audio_stream *stream,
effect_handle_t effect);
int (*remove_audio_effect)(const struct audio_stream *stream,
effect_handle_t effect);
};
2.4 音频流生命周期与数据流向
下图展示了音频输出流从创建到销毁的完整生命周期,以及音频数据在HAL层的流动路径:
2.5 核心接口对比总结
| 对比维度 | audio_hw_device | audio_stream_out | audio_stream_in |
|---|---|---|---|
| 职责 | 设备级管理(初始化、路由、参数) | 音频输出(播放) | 音频输入(录音) |
| 核心函数 | open_*_stream、set_parameters |
write()、get_latency() |
read()、set_gain() |
| 生命周期 | 进程启动时创建,进程退出时销毁 | 每次播放会话创建/销毁 | 每次录音会话创建/销毁 |
| 线程安全 | 需要实现内部锁机制 | 通常由AudioFlinger保证单线程访问 | 通常由AudioFlinger保证单线程访问 |
| 调试关键 | 检查init_check()返回值 |
检查write()返回字节数是否匹配 |
检查read()返回数据是否有效 |
适配经验:在实现音频HAL时,建议先实现
open_output_stream()和write(),因为播放功能是验证HAL是否正常工作的最快路径。使用tinyplay工具可以快速测试输出流,使用tinycap测试输入流。
2.6 本章小结
本章深入剖析了音频HAL层的三大核心要素:
- 模块加载机制:从
hw_get_module()到dlopen()的完整链路,以及模块库的命名和路径规范。 - 设备结构体:
audio_hw_device作为设备级接口,负责流管理、参数设置和路由控制。 - 流结构体:
audio_stream_out和audio_stream_in分别封装了播放和录音的核心操作。