文章目錄
- 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() //寫聲卡數據