4. 音频HAL HIDL接口解析与实现
任务说明
本案例实现音频HAL层输出流的打开与关闭操作,对应Android音频HAL接口中的open_output_stream和close_output_stream。核心目标是:当上层AudioFlinger请求打开输出流时,HAL能够正确创建并初始化音频输出流句柄;当上层关闭输出流时,HAL能够正确释放资源并置空句柄。实现范围限定为输出流管理,不涉及音频数据读写与路由策略。
核心数据结构
// audio_hal_local.h
#ifndef AUDIO_HAL_LOCAL_H
#define AUDIO_HAL_LOCAL_H
#include <hardware/audio.h>
#include <system/audio.h>
#include <cutils/log.h>
#include <stdlib.h>
#include <string.h>
// 自定义输出流对象,嵌入audio_stream_out作为首个成员
struct local_stream_out {
struct audio_stream_out stream; // HAL标准接口结构体,必须位于首位
audio_devices_t devices; // 输出设备类型
uint32_t sample_rate; // 采样率
audio_format_t format; // 音频格式
audio_channel_mask_t channel_mask; // 通道掩码
int handle; // 模拟硬件句柄,实际驱动中为文件描述符或DMA通道号
};
// HAL模块结构体
struct local_audio_device {
struct audio_hw_device device; // 标准HAL设备结构体
struct local_stream_out *out_stream; // 当前活动的输出流
};
// 前向声明
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);
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream);
#endif // AUDIO_HAL_LOCAL_H
实现代码
// audio_hal_output.c
#include "audio_hal_local.h"
// 输出流标准接口函数集(仅实现必要的控制函数)
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
const struct local_stream_out *out = (const struct local_stream_out *)stream;
return out->sample_rate;
}
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
struct local_stream_out *out = (struct local_stream_out *)stream;
out->sample_rate = rate;
return 0;
}
static size_t out_get_buffer_size(const struct audio_stream *stream)
{
// 以16bit双声道为例:采样率/1000 * 帧大小(4字节) * 20ms
const struct local_stream_out *out = (const struct local_stream_out *)stream;
return (out->sample_rate / 1000) * 4 * 20;
}
static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
{
const struct local_stream_out *out = (const struct local_stream_out *)stream;
return out->channel_mask;
}
static audio_format_t out_get_format(const struct audio_stream *stream)
{
const struct local_stream_out *out = (const struct local_stream_out *)stream;
return out->format;
}
static int out_set_format(struct audio_stream *stream, audio_format_t format)
{
struct local_stream_out *out = (struct local_stream_out *)stream;
out->format = format;
return 0;
}
static int out_standby(struct audio_stream *stream)
{
// 模拟进入待机,实际驱动中在此关闭DMA或降低功耗
(void)stream;
return 0;
}
static int out_dump(const struct audio_stream *stream, int fd)
{
const struct local_stream_out *out = (const struct local_stream_out *)stream;
dprintf(fd, "local_stream_out: handle=%d rate=%u format=0x%x channels=0x%x\n",
out->handle, out->sample_rate, out->format, out->channel_mask);
return 0;
}
static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
// 本案例不处理参数设置,仅返回成功
(void)stream;
(void)kvpairs;
return 0;
}
static char *out_get_parameters(const struct audio_stream *stream, const char *keys)
{
// 返回空字符串表示无参数
(void)stream;
(void)keys;
return strdup("");
}
static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
// 模拟延迟:20ms
(void)stream;
return 20;
}
static int out_set_volume(struct audio_stream_out *stream, float left, float right)
{
// 本案例不实现音量控制
(void)stream;
(void)left;
(void)right;
return -ENOSYS;
}
static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes)
{
// 本案例仅管理流生命周期,不实现实际写入
(void)stream;
(void)buffer;
(void)bytes;
return -ENOSYS;
}
// 绑定输出流函数表
static void init_stream_out_ops(struct audio_stream_out *stream_out)
{
struct audio_stream *stream = &stream_out->common;
stream_out->get_latency = out_get_latency;
stream_out->set_volume = out_set_volume;
stream_out->write = out_write;
stream->get_sample_rate = out_get_sample_rate;
stream->set_sample_rate = out_set_sample_rate;
stream->get_buffer_size = out_get_buffer_size;
stream->get_channels = out_get_channels;
stream->get_format = out_get_format;
stream->set_format = out_set_format;
stream->standby = out_standby;
stream->dump = out_dump;
stream->set_parameters = out_set_parameters;
stream->get_parameters = out_get_parameters;
}
// 打开输出流
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)
{
struct local_audio_device *ladev = (struct local_audio_device *)dev;
struct local_stream_out *out;
int ret = 0;
// 检查是否已有输出流打开(本案例仅支持单输出流)
if (ladev->out_stream != NULL) {
ALOGE("Output stream already open");
return -EBUSY;
}
// 分配输出流对象
out = (struct local_stream_out *)calloc(1, sizeof(struct local_stream_out));
if (out == NULL) {
return -ENOMEM;
}
// 初始化标准接口
init_stream_out_ops(&out->stream);
// 保存配置参数
out->devices = devices;
out->handle = (int)handle;
// 若调用方提供了config则使用,否则使用默认值
if (config != NULL) {
out->sample_rate = config->sample_rate;
out->format = config->format;
out->channel_mask = config->channel_mask;
} else {
out->sample_rate = 48000;
out->format = AUDIO_FORMAT_PCM_16_BIT;
out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
}
// 将配置回填给调用方
if (config != NULL) {
config->sample_rate = out->sample_rate;
config->format = out->format;
config->channel_mask = out->channel_mask;
}
// 记录到设备结构体
ladev->out_stream = out;
*stream_out = &out->stream;
ALOGI("Output stream opened: handle=%d rate=%u format=0x%x channels=0x%x",
out->handle, out->sample_rate, out->format, out->channel_mask);
return ret;
}
// 关闭输出流
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
struct local_audio_device *ladev = (struct local_audio_device *)dev;
struct local_stream_out *out = (struct local_stream_out *)stream;
// 校验是否为当前活动的输出流
if (ladev->out_stream != out) {
ALOGE("Attempting to close non-active stream");
return;
}
// 释放资源
ladev->out_stream = NULL;
free(out);
ALOGI("Output stream closed");
}
// HAL模块方法表
static int adev_close(hw_device_t *device)
{
struct local_audio_device *ladev = (struct local_audio_device *)device;
// 若仍有输出流未关闭,则强制关闭
if (ladev->out_stream != NULL) {
adev_close_output_stream(&ladev->device, &ladev->out_stream->stream);
}
free(ladev);
return 0;
}
static int adev_open(const hw_module_t *module, const char *name,
hw_device_t **device)
{
struct local_audio_device *ladev;
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) {
return -EINVAL;
}
ladev = (struct local_audio_device *)calloc(1, sizeof(struct local_audio_device));
if (ladev == NULL) {
return -ENOMEM;
}
ladev->device.common.tag = HARDWARE_DEVICE_TAG;
ladev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
ladev->device.common.module = (struct hw_module_t *)module;
ladev->device.common.close = adev_close;
ladev->device.open_output_stream = adev_open_output_stream;
ladev->device.close_output_stream = adev_close_output_stream;
// 其他函数指针置NULL,表示不支持
*device = &ladev->device.common;
return 0;
}
static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
};
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "Local Audio HAL",
.author = "Android HAL Course",
.methods = &hal_module_methods,
},
};
关键逻辑说明
打开输出流时,adev_open_output_stream首先检查设备结构体中是否已有活动输出流,若存在则返回-EBUSY,保证单输出流约束。随后分配local_stream_out内存,初始化标准接口函数表,将调用方传入的audio_config参数(采样率、格式、通道掩码)保存到流对象中。若调用方未提供配置,则使用48000Hz/16bit/立体声作为默认值,并将配置回填给调用方。最后将新流对象记录到设备结构体,通过输出参数返回标准接口指针。
关闭输出流时,adev_close_output_stream校验传入的流指针是否为当前活动流,防止误关。确认后释放内存并将设备结构体中的流指针置空。设备关闭函数adev_close会检查是否有未关闭的流并强制清理,避免资源泄漏。
SVG:输出流生命周期状态图
编译与验证
# 编译(假设Android源码环境已就绪)
# 将audio_hal_local.h和audio_hal_output.c放入hardware/libhardware/modules/audio/目录
# 在Android.bp中添加编译规则:
# cc_library_shared {
# name: "audio.primary.local",
# srcs: ["audio_hal_output.c"],
# shared_libs: ["libhardware", "libcutils", "liblog"],
# }
# 单元验证方法(使用C测试程序模拟调用)
# 编写测试程序test_hal.c:
# 1. hw_get_module(AUDIO_HARDWARE_MODULE_ID, &module) 加载模块
# 2. audio_hw_device_open(module, &dev) 打开设备
# 3. 构造audio_config config = {.sample_rate=44100, .format=AUDIO_FORMAT_PCM_16_BIT, .channel_mask=AUDIO_CHANNEL_OUT_MONO}
# 4. 调用dev->open_output_stream(dev, 1, AUDIO_DEVICE_OUT_SPEAKER, AUDIO_OUTPUT_FLAG_NONE, &config, &stream)
# 5. 断言stream != NULL,且config.sample_rate == 44100
# 6. 调用dev->close_output_stream(dev, stream)
# 7. 再次调用open_output_stream应成功(前一次已关闭)
# 8. 连续两次open_output_stream第二次应返回- EBUSY
# 判定标准:
# - open_output_stream返回0且stream指针非空
# - config参数正确回填
# - close_output_stream后再次open成功
# - 重复open返回- EBUSY
# - 使用dumpsys media.audio_flinger或logcat观察ALOGI输出
验证要点
- 单流互斥:连续两次调用
open_output_stream,第二次必须返回-EBUSY,且第一次返回的流指针仍然有效。 - 参数回填:传入非默认
audio_config(如44100Hz单声道),打开后检查config结构体中的值是否与传入一致。 - 关闭后重开:关闭输出流后再次打开,应成功返回
0,证明资源已正确释放。 - 空指针保护:关闭一个非活动流指针时,函数应打印错误日志但不崩溃。