AndroidQ | AudioHal分析

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层的库,加载库中的一般方法,然后再对其进行抽象,供用户使用,所以这里就不赘述了。

  • 5
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android TIF (TV Input Framework) HAL层是Android系统中专门用于电视输入设备的HAL层,它提供了与电视输入设备交互的接口。本文将从以下几个方面对Android TIF HAL层进行分析: 1. TIF HAL层的结构 TIF HAL层的结构主要包括以下几个部分: - TIF HAL层接口:包含了TIF HAL层与上层应用交互的接口,包括初始化、搜索电视节目、设置电视节目等接口。 - TIF HAL层实现:包含了TIF HAL层的具体实现,与具体的电视输入设备相关。 - TIF HAL层框架:包含了TIF HAL层的框架代码,用于管理TIF HAL层的实现。 2. TIF HAL层的初始化 TIF HAL层的初始化主要包括以下几个步骤: - 加载TIF HAL层库:系统在启动时会自动加载TIF HAL层库。 - 查找TIF HAL层接口:系统通过dlsym函数查找TIF HAL层接口。 - 初始化TIF HAL层实现:系统调用TIF HAL层接口中的初始化函数初始化TIF HAL层实现。 3. TIF HAL层与电视输入设备的交互 TIF HAL层与电视输入设备的交互主要包括以下几个步骤: - 搜索电视节目:应用调用TIF HAL层接口中的搜索电视节目函数,TIF HAL层实现会向电视输入设备发送搜索电视节目的指令,并接收电视输入设备返回的电视节目信息。 - 设置电视节目:应用调用TIF HAL层接口中的设置电视节目函数,TIF HAL层实现会向电视输入设备发送设置电视节目的指令,并等待电视输入设备返回设置结果。 4. TIF HAL层的实现 TIF HAL层的具体实现与电视输入设备相关,不同的电视输入设备需要实现不同的TIF HAL层。TIF HAL层的实现需要遵循Android HAL层的规范,包括实现HAL层接口、定义HAL层结构体等。 总的来说,Android TIF HAL层是一个用于电视输入设备的HAL层,它提供了与电视输入设备交互的接口,其具体实现与电视输入设备相关。在使用Android TIF HAL层时,需要遵循Android HAL层的规范,并根据实际的电视输入设备进行相应的实现。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值