Skip to main content
 首页 » 编程设计

Android音频(8)——HAL分析

2022年07月19日28qlqwjy

一、HAL之框架

1. tiny4412上HAL框架

audio.primary.tiny4412.so文件的Makefile:
device/friendly-arm/common/libaudio/Android.mk

LOCAL_SRC_FILES:= AudioHardware.cpp 
LOCAL_MODULE := audio.primary.$(TARGET_DEVICE)    #TARGET_DEVICE就是tiny4412,生成audio.primary.tiny4412.so,其中primary是在 /system/etc/audio_policy.conf中指定的module的名字。 
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw 
LOCAL_STATIC_LIBRARIES:= libmedia_helper 
LOCAL_SHARED_LIBRARIES:= \ 
    libutils \ 
    liblog \ 
    libhardware_legacy \        # 依赖这个库 
    libtinyalsa \                # 依赖这个库 
    libaudioutils 
LOCAL_WHOLE_STATIC_LIBRARIES := libaudiohw_legacy

libaudiohw_legacy依赖文件:

hardware/libhardware_legacy/audio/Android.mk 
LOCAL_SRC_FILES := \ 
    AudioHardwareInterface.cpp \ 
    audio_hw_hal.cpp    # 4412的5.0版Android上是这个HAL,其它的可不一定了 
 
LOCAL_MODULE := libaudiohw_legacy 
LOCAL_MODULE_TAGS := optional 
LOCAL_STATIC_LIBRARIES := libmedia_helper 
LOCAL_CFLAGS := -Wno-unused-parameter 
 
include $(BUILD_STATIC_LIBRARY)

所以库文件audio.primary.tiny4412.so至少涉及:
audio_hw_hal.cpp (hardware/libhardware_legacy/audio/audio_hw_hal.cpp)
audioHardwareInterface.cpp (hardware/libhardware_legacy/audio/audioHardwareInterface.cpp)
audioHardware.cpp (device/friendly-arm/common/libaudio/audioHardware.cpp) 生成audio.primary.tiny4412.so
作用:
audio_hw_hal.cpp:向上提供接口的函数
audioHardwareInterface.cpp:向厂商制定访问硬件接口的函数
audioHardware.cpp:厂商实现的访问声卡硬件的文件

2. 要先弄清楚其框架,才能看懂其代码。

从图片上可以看出,HAL这一层有audio的HAL,也有Audio_policy的HAL。对于Audio_policy的HAL我们不关系,因为它基本上已经废弃了。

HAL要向上层提供统一的接口,操作硬件也可能有一组接口或一个类,抓住这两点分析HAL框架。

3. 见audio_hw_hal.cpp,向上提供的接口就是struct audio_hw_device,它是对hw_device_t结构的封装。

audio_hw_hal.cpp位于hardware/libhardware_legacy/audio下,既然是旧的,那么有没有新的呢?在hardware/libhardware/modules/audio
下有个audio_hw.c文件,它就是新的Audio的HAL文件,但是在5.0板中没有使用它,它里面的函数全部为空。在版本的Android8.0中可以看出,
这个文件同样没有实现,所以libhardware_legacy下的还是起主打作用。但是在Android8.0中的高通的HAL使用的是新的。

4. audio_hw_hal.cpp
a.向上提供接口struct audio_hw_device
b.向下访问硬件class AudioHardware(实现在device/friendly-arm/common/libaudio/audioHardware.cpp) 由厂商提供,里面使用到了tinyalsa
库的接口。但是厂商肯定不能随意去实现硬件的访问接口,Android指定了一套接口给它。
类的继承关系:

AudioHardwareInterface    //hardware/AudioHardwareInterface.cpp 
    ↑ 
AudioHardwareBase         //hardware/AudioHardwareBase.h 这个应该是audio HAL给厂商定义的接口 
    ↑ 
AudioHardware            //device/friendly-arm/common/libaudio/audioHardware.cpp

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


6. Audio HAL的调用流程总结

  上层应用程序调用audio_hw_hal.cpp中的legacy_adev_open()会得到一个struct audio_hw_device结构体,这个结构体就代表上层使用硬件的接口,这个结构体中有一堆的函数,它们都依赖于厂家提供的struct AudioHardwareInterface结构(在HAL中使用,在厂商代码中实现,此例中是在audioHardware.cpp中实现的),由于HAL对上层直接提供的接口中没有read/write函数,因此,应用程序想要录音或播放声音的时候需要先打卡output或input(调用HAL的open_output_stream/open_input_stream),以使用其里面的write/read函数向声卡硬件写音频数据。

7. HAL相关的数据结构

struct legacy_audio_device { 
    //规范了向上提供的接口 
    struct audio_hw_device device; 
    //向上提供的接口依赖厂家提供的这个类来实现 
    struct AudioHardwareInterface *hwif; 
}; 
下面的legacy_stream_out和legacy_stream_in也是同理 
struct legacy_stream_out { 
    struct audio_stream_out stream; 
    AudioStreamOut *legacy_out; 
}; 
struct legacy_stream_in { 
    struct audio_stream_in stream; 
    AudioStreamIn *legacy_in; 
};

上面的也是Android源码中常用的套路。

8. http://androidxref.com/ 上有各个版本的Android源码,查看代码非常方便

9. 由于有类的继承关系,SourceInsight工程自动跳转是不准的。

10. AudioFlinger启动过程中,AudioPolicyService构造过程中会读取配置文件 /system/etc/audio_policy.conf,该配置文件中有一个module名为"primary",根据这个名字, 会去 /system/lib/hw 中打开库文件: audio.primary.XXX.so,比如: audio.primary.tiny4412.so


11. 框架详细描述
audio HAL中:
b.1 向上提供统一接口: audio_hw_device,里面设置了各种函数

b.2 这些函数要借助声卡的代码才能实现,声卡的功能抽象为AudioHardware对象

b.3 audio_hw_device中的函数只看到各种set, get,没有看到wirte, read,怎么播放声音、录制声音?

b.4 要播放声音,
b.4.1 要先open output:在audio_hw_device中有open_output_stream函数指针

b.4.2 open_output_stream返回一个audio_stream_out结构体,它是向上提供的"播放接口"

b.4.3 audio_stream_out也需要厂家提供的代码才能实现,厂家提供的代码抽象为AudioStreamOutALSA对象

b.5 对于录制声音
b.5.1 要先open input:在audio_hw_device中有open_input_stream函数指针

b.5.2 open_input_stream返回一个audio_stream_in结构体,它是向上提供的"录制接口"

b.5.3 audio_stream_in也需要厂家提供的代码才能实现,厂家提供的代码抽象为AudioStreamInALSA对象

二、HAL之调用流程源码分析

1. HAL文件一般位于/system/lib/hardware下面,音频中操作硬件的库文件名称在(厂商提供的HAL部分)在/system/etc/policy_config中指定,如module name为primary,
就对应audio.primary.tiny4412.so。

2. 直接使用系统调用控制声卡的是tinyalsa库,位于目录/external/tinyalsa下,编译生成库文件libtinyalsa.so(只涉及两个文件mixer.c,pcm.c),编译生成工具 tinycap,tinymix,tinypcminfo,tinyplay,可用于直接控制音频通道,进行录音播音测试。

tinyalsa中使用
pcm_open()来打开声卡
pcm_write()来播放音乐
pcm_read()来录音

3. HAL调用流程源码分析

a. 确定HAL文件的名字:
AudioPolicyManager的构造函数读取配置文件/system/etc/audio_policy.conf确定名字(这个名字在tiny4412上只是HAL厂商部分编译生成库的名字,HAL入口是hardware/libhardware_legacy/audio/audio_hw_hal.cpp)

b. 加载HAL对应的so文件。

c. 打开HAL文件中的open函数,在HAL中会构造audio_hw_device结构体, 该结构体中有各类函数, 特别是 open_output_stream / open_input_stream

AudioFlinger根据audio_hw_device结构体构造一个AudioHwDev对象并放入mAudioHwDevs

d. open output stream: 调用hal结构体audio_hw_device的open_output_stream, 它会构造出一个audio_stream_out结构体, 里面有write函数

AudioFlinger根据audio_stream_out结构体构造一个MixerThread

e. 播放声音(这时才真正打开声卡驱动):
thread->threadLoopg_write => stream.write ==> pcm_open, pcm_write


本文参考链接:https://www.cnblogs.com/hellokitty2/p/10943199.html