AndroidQ | AudioHal分析
AndroidHAL层的代码为framework层抽象出了一系列接口,隐藏了硬件驱动细节;本文来了解AudioHal层的相关代码,相关源文件在目录frameworks\av\media\libaudiohal中;在AndroidAudio子系统中,直接操作AudioHal的是AudioFlinger,在AudioFlinger中保存了所有可用的音频设备
AndroidHAL层的代码为framework层抽象出了一系列接口,隐藏了硬件驱动细节;本文来了解AudioHal层的相关代码,相关源文件在目录frameworks\av\media\libaudiohal中;在AndroidAudio子系统中,直接操作AudioHal的是AudioFlinger,在AudioFlinger中保存了所有可用的音频设备
1.抽象音频硬件接口
虽然各个音频硬件大不相同,但是涉及到操作大体上可以归为两类:设备操作和流操作。对于这两种操作,我们设计两个接口来抽象它们:DeviceHalInterface和StreamHalInterface,对于流类型来说还分输入/输出:StreamOutHalInterface和StreamInHalInterface。
早期的Android一般是把操作音频驱动的代码封装成一个库,然后AudioFlinger在初始化的时候会加载这些库,然后封装成一个对象,取得操作音频驱动的接口;后来Android准备要执行treble计划,这样就彻底隔离厂商自定义的代码和Android Framework的代码,双方的通信基于binder,相对于AIDL,google提供了另一个接口HIDL。针对这两种方式,在AudioHal层也分别提供了对应的实现,具体是以Local和Hidl结尾来表示。我们来看下这些类的关系:
2.创建具体硬件设备
具体音频硬件对象是在AudioFlinger中实现,我们一步一步来看:
2.1 DevicesFactoryHalInterface
AudioFlinger::AudioFlinger()
...
{
...
mDevicesFactoryHal = DevicesFactoryHalInterface::create();----从名字可以看出这是"设备工厂"
...
}
sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
return createPreferredImpl<DevicesFactoryHalInterface>(
"android.hardware.audio", "IDevicesFactory");--------创建最合适的"工厂类"的实例
}
我们来看看接口的设计:
class DevicesFactoryHalInterface : public RefBase
{
...
virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device) = 0;---接口的真正功能,打开设备
...
static sp<DevicesFactoryHalInterface> create();------------创建接口的具体实现
...
};
DevicesFactoryHalInterface有两种类继承于它,如下图:
从上面可以得知,这是版本迭代引入的两种实现方式,我们看看local是怎么实现openDevice的
static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev)
{
...
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
...
rc = audio_hw_device_open(mod, dev);
...
}
status_t DevicesFactoryHalLocal::openDevice(const char *name, sp<DeviceHalInterface> *device) {
audio_hw_device_t *dev;
status_t rc = load_audio_interface(name, &dev);----加载库提取audio_hw_device_t,这个结构体我们在第三节介绍
if (rc == OK) {
*device = new DeviceHalLocal(dev);-------------创建具体音频硬件设备
}
return rc;
}
从这里我们看到了重要的类DeviceHalLocal,这个类是framework层对具体硬件设备的抽象。接下来看AudioFlinger调用openDevice的地方:
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
//遍历是否已加载
}
sp<DeviceHalInterface> dev;
int rc = mDevicesFactoryHal->openDevice(name, &dev);
...
audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
...
}
loadHwModule_l根据名字去打开相应的设备,并分配一个唯一id,然后创建一个重要的类AudioHwDevice
2.2 AudioHwDevice
AudioFlinger中管理audio设备的类
class AudioHwDevice {
...
AudioHwDevice(audio_module_handle_t handle,
const char *moduleName,
sp<DeviceHalInterface> hwDevice,
Flags flags)
: mHandle(handle)
, mModuleName(strdup(moduleName))
, mHwDevice(hwDevice)
, mFlags(flags) { }
...
audio_module_handle_t handle() const { return mHandle; }
const char *moduleName() const { return mModuleName; }
sp<DeviceHalInterface> hwDevice() const { return mHwDevice; }
...
status_t openOutputStream(
AudioStreamOut **ppStreamOut,
audio_io_handle_t handle,
audio_devices_t deviceType,
audio_output_flags_t flags,
struct audio_config *config,
const char *address);
...
const audio_module_handle_t mHandle;
const char * const mModuleName;
sp<DeviceHalInterface> mHwDevice;
const Flags mFlags;
};
status_t AudioHwDevice::openOutputStream(
AudioStreamOut **ppStreamOut,
audio_io_handle_t handle,
audio_devices_t deviceType,
audio_output_flags_t flags,
struct audio_config *config,
const char *address)
{
...
AudioStreamOut *outputStream = new AudioStreamOut(this, flags);----------建立流对象
...
status_t status = outputStream->open(handle, deviceType, config, address);------看2.3小节AudioStreamOut的实现
...
*ppStreamOut = outputStream;
return status;
}
说完了音频设备,下面介绍流的管理,openOutputStream就是要打开相应的流
2.3 AudioStreamOut
AudioFlinger中管理流用AudioStreamOut来封装
class AudioStreamOut {
...
AudioHwDevice * const audioHwDev;
sp<StreamOutHalInterface> stream;-------hal层对流对象的接口封装
const audio_output_flags_t flags;
sp<DeviceHalInterface> hwDev() const;
AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags);
virtual status_t open(
audio_io_handle_t handle,
audio_devices_t deviceType,
struct audio_config *config,
const char *address);
...
};
接上上面的调用流程继续
sp<DeviceHalInterface> AudioStreamOut::hwDev() const
{
return audioHwDev->hwDevice();
}
status_t AudioStreamOut::open(
audio_io_handle_t handle,
audio_devices_t deviceType,
struct audio_config *config,
const char *address)
{
...
int status = hwDev()->openOutputStream(
handle,
deviceType,
customFlags,
config,
address,
&outStream);-------------最后调用devicehallocal的openOutputStream
...
if (status == NO_ERROR) {
stream = outStream;
...
}
}
status_t DeviceHalLocal::openOutputStream(
audio_io_handle_t handle,
audio_devices_t deviceType,
audio_output_flags_t flags,
struct audio_config *config,
const char *address,
sp<StreamOutHalInterface> *outStream) {
audio_stream_out_t *halStream;------------------------这个结构体在第三节介绍
...
int openResut = mDev->open_output_stream(-------------mDev就是audio_hw_device_t,在第三节介绍
mDev, handle, deviceType, flags, config, &halStream, address);
if (openResut == OK) {
*outStream = new StreamOutHalLocal(halStream, this);-----创建local实现的流对象
}
...
}
3.底层实现
从上面两节我们知道两个重要的结构体需要在C层实现,C层是比hal更底层的audio功能实现,这一层基本是vendor需要实现的;这一层会基于tinyalsa库来实现audio的功能。我们继续对上面两个结构体做个介绍。
对于和硬件相关的实现都是在hardware目录下,这两个结构体的定义也在此目录下
3.1 audio_hw_device_t
hardware/libhardware/include/hardware/audio.h
typedef struct audio_hw_device audio_hw_device_t;
struct audio_hw_device {
struct hw_device_t common;--------------第一个字段必须是此结构体,为了强转需要
...
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);--------c层open_output_stream的实现
...
};
audio_hw_device中有丰富的函数功能实现,我们只截取了一部分,为了了解open_output_stream流程。
audio_hw_device是一个通用的实现,一般来说vendor开发会基于此在外层继续包装,添加自己的一些字段,我们以google提供的例子分析,看下hardware\libhardware\modules\audio\audio_hw.c文件。
struct stub_audio_device {
struct audio_hw_device device;-----必须在开头
};
这里给了一个很简单的封装,但是一般会增加vendor特有的字段,不过audio_hw_device一定要在开头,为了HAL层强转需要。
现在来看下HAL是怎么得到此结构体audio_hw_device的,通过load_audio_interface中我们知道dlopen动态库加载来实现的,那么就一定要在代码中加入相应的识别symbol,还是在audio_hw.c中
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 = "Default audio HW HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
};
具体细节不分析了,最终会调用adev_open来得到audio_hw_device
static int adev_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
...
struct stub_audio_device *adev;
...
adev = calloc(1, sizeof(struct stub_audio_device));
...
adev->device.open_output_stream = adev_open_output_stream;
...
*device = &adev->device.common;
...
}
可以看到实际新建的是stub_audio_device这个结构体,但是因为audio_hw_device在头部,进行强转就可以了。
3.2 audio_stream_out_t
stream也是类似的
typedef struct audio_stream_out audio_stream_out_t;
struct stub_stream_out {
struct audio_stream_out stream;
int64_t last_write_time_us;
uint32_t sample_rate;
audio_channel_mask_t channel_mask;
audio_format_t format;
size_t frame_count;
};
然后是adev_open_output_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,
const char *address __unused)
{
ALOGV("adev_open_output_stream...");
*stream_out = NULL;
struct stub_stream_out *out =
(struct stub_stream_out *)calloc(1, sizeof(struct stub_stream_out));
...
*stream_out = &out->stream;
return 0;
}
4 Effect
在AudioHal中,还有另外一种模块就是音效处理,比如高音,低音,杜比音效等等,我们在目录中看到的Effect相关的文件都是其代码实现,他的具体过程和device是一样的,都是通过加载hal层的库,加载库中的一般方法,然后再对其进行抽象,供用户使用,所以这里就不赘述了。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)