Android Framework 音频子系统(12)HAL层分析

该系列文章总纲链接:专题分纲目录 Android Framework 音频子系统​​​​​​​


本章关键点总结 & 说明:

本章节主要关注➕ 以上思维导图左上 HAL层分析 部分 即可。主要说明了HAL层的框架分析,后面通过源码分析了 读音频数据流程、写音频数据流程、设置参数流程、获取参数流程 来深入的理解 HAL层的调用流程。


1 HAL层框架分析

音频系统的 整个框架图如下所示:

@1 HAL层的两个部分

这里关注HAL层,这里有audio的HAL,也有Audio_policy的HAL(Audio_policy的HAL我们不关心,基本废弃)。HAL层下一层使用TiniAlsa(AlSA库 裁剪版)。HAL层分为两部分:

  1. 一部分为各种音频设备,每种音频设备由一个独立的库文件实现:如audio.a2dp.default.so(管理蓝牙a2dp音频),audio.usb.default.so(管理usb外接的音频),audio.primary.default.so(管理设备上的大部分音频)。
  2. 一部分为厂家自己实现的音频策略比如:audio.primary.tiny4412.so。

@2 关键类与结构体

一般来讲,为了方便,HAL要向上层提供统一的接口,操作硬件也会有一组接口/一个类。分别为:

  • 向上提供接口struct audio_hw_device:struct audio_hw_device接口在audio_hw_hal.cpp文件中,它是对hw_device_t结构的封装。audio_hw_hal.cpp位于hardware/libhardware_legacy/audio下(新的架构在hardware/libhardware/modules/audio下有个audio_hw.c文件,它就是新的Audio的HAL文件,但是在5.0板中没有使用它,它里面的函数全部为空。这个文件同样没有实现,所以libhardware_legacy下的还是起主打作用)。
  • 向下访问硬件class AudioHardware:一般在device/"平台厂商"/common/libaudio/audioHardware.cpp中实现, 由厂商提供,里面使用到了tinyalsa库的接口。厂商实现硬件的访问接口,Android指定了一套接口给它。几个关键类的继承关系为:
AudioHardwareInterface   //hardware/AudioHardwareInterface.cpp 最基础 接口类
    ↑ (继承)
AudioHardwareBase        //hardware/AudioHardwareBase.h 这个应该是Audio HAL给厂商定义的接口
    ↑ (继承)
AudioHardware            //device/"平台厂商"/common/libaudio/audioHardware.cpp 厂商的实现

在厂商的HAL中,AudioHardware (audioHardware.cpp中)表示一个声卡,它使用audio_stream_out结构来表示输出,使用audio_stream_in来表示输入。(audio_stream_out中有write(),audio_stream_in中有read())。

@3 HAL相关的数据结构总结:

/* 上下衔接
 * Audio HAL的调用流程总结上层应用程序调用
 * audio_hw_hal.cpp中的legacy_adev_open()
 * 会得到一个struct audio_hw_device结构体,
 * 这个结构体代表上层使用硬件的接口,这个
 * 结构体中的函数都依赖于厂家提供的
 * struct AudioHardwareInterface结构。
 */
struct legacy_audio_device {
    struct audio_hw_device device;      //规范了向上提供的接口
    struct AudioHardwareInterface *hwif;//向下访问硬件,指向厂家的AudioHardware
};

/*由于HAL对上层直接提供的接口中没有read/write函数
 *因此,应用程序想要录音或播放声音的时候需要先打开
 *output或input(audio_hw_device中的open_output_stream/open_input_stream),
 *进而使用其里面的write/read函数通过声卡硬件读/写音频数据。
 *这也就是audio_hw_device与audio_stream_out/audio_stream_in之间的关系
 */
struct legacy_stream_out {
    struct audio_stream_out stream; //规范了向上提供的接口
    AudioStreamOut *legacy_out;     //向下访问硬件,指向厂家的AudioStreamOutALSA
};

struct legacy_stream_in {
    struct audio_stream_in stream;//规范了向上提供的接口
    AudioStreamIn *legacy_in;     //向下访问硬件,指向厂家的AudioStreamInALSA
};

2 HAL层源码分析

接下来 主要分析几个关键流程(写数据操作、读数据操作、获取参数、设置参数)来解读HAL的调用过程。

AudioFlinger加载库的过程如下:

AudioFlinger::loadHwModule
->AudioFlinger::loadHwModule_l
-->load_audio_interface
--->audio_hw_device_open(mod, dev);//获取audio_hw_device_t结构体以及它的操作
---->module->methods->open //这里对应的是legacy_adev_open

这里对中调用到了HAL层的legacy_adev_open方法,代码实现如下:

static int legacy_adev_open(const hw_module_t* module, const char* name,
                            hw_device_t** device)
{
    struct legacy_audio_device *ladev;
    int ret;

    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
        return -EINVAL;

    ladev = (struct legacy_audio_device *)calloc(1, sizeof(*ladev));
    if (!ladev)
        return -ENOMEM;

    //结构体赋值
    ladev->device.common.tag = HARDWARE_DEVICE_TAG;
    ladev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    ladev->device.common.module = const_cast<hw_module_t*>(module);
    ladev->device.common.close = legacy_adev_close;
    ladev->device.init_check = adev_init_check;
    ladev->device.set_voice_volume = adev_set_voice_volume;
    ladev->device.set_master_volume = adev_set_master_volume;
    ladev->device.get_master_volume = adev_get_master_volume;
    ladev->device.set_mode = adev_set_mode;
    ladev->device.set_mic_mute = adev_set_mic_mute;
    ladev->device.get_mic_mute = adev_get_mic_mute;
    ladev->device.set_parameters = adev_set_parameters;
    ladev->device.get_parameters = adev_get_parameters;
    ladev->device.get_input_buffer_size = adev_get_input_buffer_size;
    ladev->device.open_output_stream = adev_open_output_stream;
    ladev->device.close_output_stream = adev_close_output_stream;
    ladev->device.open_input_stream = adev_open_input_stream;
    ladev->device.close_input_stream = adev_close_input_stream;
    ladev->device.dump = adev_dump;
    /* 关键点:
     * audio_hw_device_t结构体 和 hwif(hardwareInterface)接口之间建立联系
     * 这里通过createAudioHardware 获取 实现hardwareInterface接口的厂商指针
     * 后面调用hwif的相关操作 <=等价=> 使用厂商的库函数中的方法
     */
    ladev->hwif = createAudioHardware();
    if (!ladev->hwif) {
        ret = -EIO;
        goto err_create_audio_hw;
    }
    *device = &ladev->device.common;
    return 0;

err_create_audio_hw:
    free(ladev);
    return ret;
}

通过上面的分析,这里从Framework Native层到HAL层框架,再到第三方平台厂商库的调用,他们之间的关系就打通了。

2.1 写数据操作

从Framework的Native层开始,audio_hw_device_t 结构体首先会通过adev_open_output_stream获取audio_stream_out,因此从它开始分析。代码如下:

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)
{
    struct legacy_audio_device *ladev = to_ladev(dev);
    status_t status;
    struct legacy_stream_out *out;
    int ret;

    //这里的legacy_stream_out <=结构体类型 等价=>audio_stream_out
    out = (struct legacy_stream_out *)calloc(1, sizeof(*out));
    if (!out)
        return -ENOMEM;

    devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);
    //这里将audio_stream_out与ladev->hwif之间建立联系
    out->legacy_out = ladev->hwif->openOutputStreamWithFlags(devices, flags,
                                                    (int *) &config->format,
                                                    &config->channel_mask,
                                                    &config->sample_rate, &status);
    if (!out->legacy_out) {
        ret = status;
        goto err_open;
    }

    out->stream.common.get_sample_rate = out_get_sample_rate;
    out->stream.common.set_sample_rate = out_set_sample_rate;
    out->stream.common.get_buffer_size = out_get_buffer_size;
    out->stream.common.get_channels = out_get_channels;
    out->stream.common.get_format = out_get_format;
    out->stream.common.set_format = out_set_format;
    out->stream.common.standby = out_standby;
    out->stream.common.dump = out_dump;
    out->stream.common.set_parameters = out_set_parameters;
    out->stream.common.get_parameters = out_get_parameters;
    out->stream.common.add_audio_effect = out_add_audio_effect;
    out->stream.common.remove_audio_effect = out_remove_audio_effect;
    out->stream.get_latency = out_get_latency;
    out->stream.set_volume = out_set_volume;
    out->stream.write = out_write;
    out->stream.get_render_position = out_get_render_position;
    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;

    //将out->stream写回到参数 stream_out中
    *stream_out = &out->stream;
    return 0;

err_open:
    free(out);
    *stream_out = NULL;
    return ret;
}

这里通过hwif获取了音频输出流(audio_stream_out类型),然后对out->stream进行初始化,注册了 写入函数out_write 之后将其返回给传递进来的指针变量stream_out(audio_stream_out类型),当上层进行写操作时,就会执行这个out_write函数,代码如下:

static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
                         size_t bytes)
{
    struct legacy_stream_out *out = reinterpret_cast<struct legacy_stream_out *>(stream);
    return out->legacy_out->write(buffer, bytes);
}

这里直接调用到 第三方平台厂商库的write(out->legacy_out->write)方法(如果这里是 高通平台,则所谓的hwif就是AudioHardwareALSA,所谓的write就是AudioStreamOutALSA的write方法,最终会带哦用pcm_write进行写数据操作)。

2.2 读数据操作

从Framework的Native层开始,audio_hw_device_t 结构体首先会通过adev_open_Input_stream获取audio_stream_in,因此从它开始分析。代码如下:


/** This method creates and opens the audio hardware input stream */
static int adev_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 __unused,
                                  const char *address __unused,
                                  audio_source_t source __unused)
{
    struct legacy_audio_device *ladev = to_ladev(dev);
    status_t status;
    struct legacy_stream_in *in;
    int ret;
	//这里的legacy_stream_in <=结构体类型 等价=>audio_stream_in
    in = (struct legacy_stream_in *)calloc(1, sizeof(*in));
    if (!in)
        return -ENOMEM;

    devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);
	//这里将audio_stream_in与ladev->hwif之间建立联系
    in->legacy_in = ladev->hwif->openInputStream(devices, (int *) &config->format,
                                                 &config->channel_mask, &config->sample_rate,
                                                 &status, (AudioSystem::audio_in_acoustics)0);
    if (!in->legacy_in) {
        ret = status;
        goto err_open;
    }

    in->stream.common.get_sample_rate = in_get_sample_rate;
    in->stream.common.set_sample_rate = in_set_sample_rate;
    in->stream.common.get_buffer_size = in_get_buffer_size;
    in->stream.common.get_channels = in_get_channels;
    in->stream.common.get_format = in_get_format;
    in->stream.common.set_format = in_set_format;
    in->stream.common.standby = in_standby;
    in->stream.common.dump = in_dump;
    in->stream.common.set_parameters = in_set_parameters;
    in->stream.common.get_parameters = in_get_parameters;
    in->stream.common.add_audio_effect = in_add_audio_effect;
    in->stream.common.remove_audio_effect = in_remove_audio_effect;
    in->stream.set_gain = in_set_gain;
    in->stream.read = in_read;
    in->stream.get_input_frames_lost = in_get_input_frames_lost;
	//将in->stream写回到参数 stream_in中
    *stream_in = &in->stream;
    return 0;

err_open:
    free(in);
    *stream_in = NULL;
    return ret;
}

这里通过hwif获取了音频输入流(audio_stream_in类型),然后对in->stream进行初始化,注册了 写入函数in_read 之后将其返回给传递进来的指针变量stream_in(audio_stream_in类型),当上层进行写操作时,就会执行这个in_read函数,代码如下:

static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
                       size_t bytes)
{
    struct legacy_stream_in *in =
        reinterpret_cast<struct legacy_stream_in *>(stream);
    return in->legacy_in->read(buffer, bytes);
}

这里直接调用到 第三方平台厂商库的read(out->legacy_in->read)方法(如果这里是 高通平台,则所谓的hwif就是AudioHardwareALSA,所谓的read就是AudioStreamInALSA的read方法,最终会带哦用pcm_read进行读数据操作)。

2.3 获取参数

从Framework的Native层开始,audio_hw_device_t 结构体的获取参数的操作最后一定会调用到 adev_get_parameters,因此从它开始分析。代码如下:

static char * adev_get_parameters(const struct audio_hw_device *dev,
                                  const char *keys)
{
    const struct legacy_audio_device *ladev = to_cladev(dev);
    String8 s8;

    s8 = ladev->hwif->getParameters(String8(keys));
    return strdup(s8.string());
}

这里直接调用到 第三方平台厂商库的getParameters方法。

2.4 设置参数

从Framework的Native层开始,audio_hw_device_t 结构体的设置参数的操作最后一定会调用到 adev_set_parameters,因此从它开始分析。代码如下:

static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
    struct legacy_audio_device *ladev = to_ladev(dev);
    return ladev->hwif->setParameters(String8(kvpairs));
}

这里直接调用到 第三方平台厂商库的setParameters方法。

2.5 过程总结

  1. 从配置文件确定库文件的名字。 HAL文件一般位于/system/lib/hardware下面,音频中操作硬件的库文件名称在(厂商提供的HAL部分)在/system/etc/policy_config中指定。
  2. 加载库(*.so)文件。打开HAL文件中的open函数,在HAL中会构造audio_hw_device结构体, 该结构体中有各类函数, 特别是 open_output_stream / open_input_stream。AudioFlinger根据audio_hw_device结构体构造一个AudioHwDev对象并放入mAudioHwDevs。
  3. 调用HAL结构体audio_hw_device的open_input_stream/open_output_stream,它会构造audio_stream_in/audio_stream_out结构体。
  4. 写入/读出数据,调用tinyALSA的一些操作,直接使用系统调用控制声卡的是tinyalsa库,位于目录/external/tinyalsa下,编译生成库文件libtinyalsa.so(只涉及两个文件mixer.c,pcm.c),编译生成工具 tinycap,tinymix,tinypcminfo,tinyplay,可用于直接控制音频通道,进行录音播音测试。使用pcm_XXX操作来操作声卡,是驱动层的封装。
  5. tinyALSA库再操作音频驱动程序,音频驱动程序再操作硬件声卡设备。
发布了290 篇原创文章 · 获赞 47 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/vviccc/article/details/105417542