Android14音频子系统-Audio HAL分析
文章目录
- 1)概述
- 2)HAL的打开流程
- 3)HAL库的实现(Qualcomm)
- 4)tinyalsa
- 5)数据结构
- 6)代码流程
1)概述
1、回顾HAL、tinyalsa与linux driver的关系
2、与AudioFlinger的关系
3、
1、如何判断当前平台用的是哪个库?
android\hardware\libhardware\modules\audio
android\hardware\libhardware_legacy\audio\audio_hw_hal.cpp
可尝试注入错误代码,单编 验证一下 > 但编了也不一定会用!2、hal的作用?隐藏了什么细节,还是说只是为了符合Android框架而写?
>>播放数据之前的,设置的步骤和参数就是厂家要保护的内容3、HAL如何对接tinyalsa?
把tinyalsa当做一个库接口使用即可
2)HAL的打开流程
1、由AudioFlinger负责加载
android\frameworks\av\services\audioflinger\AudioFlinger.cpp
audio_module_handle_t AudioFlinger::loadHwModule(const char *name) //name为动态库的名字2、数据结构:
/android/frameworks/av/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
//接口与audio_hw_hal.cpp一一对应android\frameworks\av\services\audioflinger\AudioHwDevice.h //存放device
class AudioHwDevice {
private:const audio_module_handle_t mHandle;const char * const mModuleName;sp<DeviceHalInterface> mHwDevice;const Flags mFlags;
};3、以aidl / hidl为例,传统hal实现则直接使用hw_get_module得到对应的库
AudioHwDevice* AudioFlinger::loadHwModule_l(const char *name)
mDevicesFactoryHal = DevicesFactoryHalInterface::create(); //根据版本选择aidl / hidl进程4、
android\frameworks\av\media\libaudiohal\FactoryHal.cpp
void *createPreferredImpl(bool isDevice) {
if (createHalService(std::max(*ifaceVersionIt, *siblingVersionIt), isDevice,&rawInterface)) {return rawInterface;}
}mDevicesFactoryHal->openDevice(name, &dev);
--AServiceManager_waitForService(serviceName.c_str()); 5、厂商实现的Audio aidl hal service在哪里?
android\hardware\interfaces\audio\aidl\default\main.cpp
原本传统hal库由audio policy解析audio_policy_config而来,Audio aidl hal service也要去解析audio_policy_config去dlopen库?按理来说是的
3)HAL库的实现(Qualcomm)
1、目录介绍:
Qualcomm开源的hal实现
android\hardware\qcom\audio\hal //HAL新架构
android\hardware\qcom\audio\legacy\alsa_sound //HAL旧架构
android\hardware\qcom\audio\legacy\libalsa-intf //alsa-lib2、由于Android系统的发展变化(这些混合的代码 实在让人看着难受,还费时间去对比筛选),存在多种架构
1)hal实现分为旧架构和新架构,官方实现的demo代码对应路径,厂商需要根据这个去填充自家代码实现
旧架构:android\hardware\libhardware_legacy
新架构:android\hardware\libhardware\modules\audio2)alsa-lib与tinyalsa
在Android 4.0之前是使用这alsa-lib接口,之后简化演变成现在的tinyalsa
qcom实现的alsa-lib : android\hardware\qcom\audio\legacy\libalsa-intf
tinyalsa :android\external\tinyalsa本文以HAL旧架构 + tinyalsa为例子分析hal实现3、通信模型大致如下
AudioFlinger -> aidl -> hal -> hal库(qualcomm) -> tinyalsa -> linux driver4、常见音效:
音质:acoustics
音频后处理效果:Post Processing Effects
强化低频响应:Bass Boost
动态范围压缩:Dynamic Range Compression
虚拟环绕声:Virtualizer
均衡器:EQ5、submix
虚拟设备,实现内部音频流混合与重定向,支持屏幕录制、音频转发等高级功能
AudioUsbALSA.cpp -> 外接USB声卡6、基本代码分析
1)HAL入口函数 - 谁来调用?AudioFlinger
static int legacy_adev_open(const hw_module_t* module, const char* name,hw_device_t** device)
2)
static struct hw_module_methods_t legacy_audio_module_methods = {open: legacy_adev_open
};struct legacy_audio_module HAL_MODULE_INFO_SYM = {module: {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: "LEGACY Audio HW HAL",author: "The Android Open Source Project",methods: &legacy_audio_module_methods,dso : NULL,reserved : {0},},},
};3)hwif 即厂商实现的 对接hal接口
struct legacy_stream_out {struct audio_stream_out stream;AudioStreamOut *legacy_out;
};struct legacy_stream_in {struct audio_stream_in stream;AudioStreamIn *legacy_in;
};static int adev_open_output_stream()
{out->legacy_out = ladev->hwif->openOutputStreamWithFlags(devices, flags,(int *) &config->format,&raw_channel_mask,&config->sample_rate, &status);
}4)AudioHardwareALSA()构造函数中
打开库ro.hardware.alsa.default
AudioHardwareALSA()
{hw_device_t* device;err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);if (err == 0) {mALSADevice = (alsa_device_t *)device;mALSADevice->init(mALSADevice, mDeviceList);}
}ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{n = pcm_write(mHandle->handle, (char *)buffer + sent, period_size);
}5)
#define HAL_MODULE_INFO_SYM HWI //hal module info
#define HAL_MODULE_INFO_SYM_AS_STR "HMI" //#define ALSA_HARDWARE_MODULE_ID "alsa"
#define ALSA_HARDWARE_NAME "alsa"6)alsa_handle_t
typedef List < alsa_handle_t > ALSAHandleList;struct alsa_handle_t {alsa_device_t *module;uint32_t devices;strcut pcm *handle;snd_pcm_format_t format;
}struct alsa_device_t {hw_device_t common;status_t (*init)(alsa_device_t *, ALSAHandleList &);status_t (*open)(alsa_handle_t *);...void (*setVoiceVolume)(int);
}typedef struct hw_deivce_t {struct hw_module_t* module;int (*close)(struct hw_device_t* device);
} hw_device_t;
4)tinyalsa
1、tinyalsa这套框架带来了什么?
>>操作linux driver需要open/read/write,tinyalsa根据asoc协议封装对应的操作,提供统一接口给到hal,hal只需少量参数即可操作pcm设备节点,实现功能2、测试工具tinyplay/tinycap/tinymix
源码位置:
android\external\tinyalsa\tinyplay.c
板卡位置
/system/bin/tinyplay //播放工具
/system/bin/tinycap //录音工具
/system/bin/tinymix //控制工具(设置音量等操作),注意不是mix混音示例:tinyplay /data/M1F1-int16WE-AFsp.wav -D 1 -d 13、音频格式:
1)PCM与RAW介绍 : https://blog.csdn.net/weixin_42654603/article/details/143757958
2)tiny只支持wav格式?> 仅支持 PCM(原始数据格式)/ RAW(直接从音频源-如MIC,不经过编码和压缩的原始数据)格式
3)*.wav :Waveform audio File Format (windows系统的标准音频格式之一) - 不涉及加解密,是PCM流 储存的文件格式(在PCM流的基础上加一些控制信息)
4)常说的DOlby音效是什么格式?一般采用AC-3编码技术
5)WACE Sample Files下载 : https://blog.csdn.net/touzani/article/details/16553284、在TV上,digital output选择PCM和RAW 又分别代表什么?
PCM:会将音频数据 转换 PCM流,兼容绝大多数音频设备;
RAM:不会处理,直接输出给音频设备,音频设备要求很高;5、tinymix的使用
1)查看tinymix可控的寄存器列表
root@# tinymix
Mixer name: 'audiocodec'
Number of controls: 16
ctl type num name value
1 INT 1 "digital volume" 0
2 INT 1 "LINEIN to output mixer gain control" 3
3 BOOL 1 "LINEOUT Switch" On
...2)设置音量
tinymix "LINEOUT volume" "2"
5)数据结构
1、module与device
1)qcom_audio_device与audio_hw_device的"继承"用法
这样设计的目的是什么?为什么转换来转换去?
struct qcom_audio_device {struct audio_hw_device device; //向上层提供标准接口struct AudioHardwareInterface *hwif; //向下是指向厂商实现的接口
};先来看看如何使用
static int qcom_adev_open(const hw_module_t* module, const char* name,hw_device_t** device)
{...qadev->device.init_check = adev_init_check;qadev->hwif = createAudioHardware();...*device = &qadev->device.common; //将构造好的device返回给上层调用者保存
}static int adev_init_check(const struct audio_hw_device *dev)
{const struct qcom_audio_device *qadev = to_cladev(dev); //将上层传递下来的device转换一下,相当于父子指针转换return qadev->hwif->initCheck(); //调用厂家实现的接口
}小结:向上提供的接口需要是稳定的,具体的实现则由厂商自行设计,这样是灵活的
1)父子对象可以互相转换,对于上层和hal可以用同一个指针地址,不用额外设计对应关系;
2)如果不分开audio_hw_device、AudioHardwareInterface两个结构体,则厂商的灵活性会差些,反之厂商可以根据实际情况去调整,更加灵活;
2、stream音频流
1)HAL库的qcom_stream_out和qcom_stream_in类设计
struct qcom_stream_out {struct audio_stream_out stream; //向上提供接口AudioStreamOut *qcom_out; //指向厂家的接口 AudioStreamOutALSA
};struct qcom_stream_in {struct audio_stream_in stream; //向上提供接口AudioStreamIn *qcom_in; //指向厂家的接口 AudioStreamInALSA
};设计目的与qcom_audio_device同理
2)qcom_stream_out与AudioStreamOutALSA的继承""用法
1、out->qcom_out = qadev->hwif->openOutputStream(devices,..);
2、AudioStreamOut * AudioHardwareALSA::openOutputStream(){
AudioStreamOutALSA *out = 0;
...
return out;
}
先把父类"继承"过来,再将其它子类独有成员一一 赋值
3)类的层级关系设计如下
handle(module的容器) -> module -> device -> stream
device //代表一个音频设备
stream //由于音频流操作颇多,抽象出单独的类,负责控制音频流,播放,暂停等
3、AlSAMixer与ALSAControl
1)主要作用是读写设备节点“controlC0”,设置声卡参数
6)代码流程
2、上层三个步骤使用声卡播放
qcom_adev_open() //hal层的open,分配资源
adev_open_output_session() // 打开声卡设备,构造stream方法
qcom_stream_out->stream.write() //写声卡数据