1. 程式人生 > >Android ALSA音訊系統架構分析(1)----從Loopback瞭解Audio

Android ALSA音訊系統架構分析(1)----從Loopback瞭解Audio

  1. /***********************************
  2. * Author:劉江明
  3. * Environment:MTK Android 6.0
  4. * Date:2017年05月25日
  5. ***********************************/
一. 前述

        Android音訊系統是一套基於Linux ALSA上二次封裝開發的一套音訊系統,中間進行了很多的功能封裝,但最終會用到Linux ALSA。所以在Hal層的類名都會包含ALSA。對於MTK的Android audio,MTK也有一定的介紹,先來大體瞭解一下:                   
圖1  音訊系統的Framework圖         提供給應用的功能介面有:AudioTrack與AudioRecord兩個類,分別是播放與錄音的功能介面。這兩個功能介面會通過AudioSystem呼叫AudioFlinger。AudioFlinger對管控著所有的Buf與播放,同時AudioFlinger也受AudioSystem管理,其中的管理策略來自於AudioPolicy,AudioPolicy會決定這個是什麼型別的音訊以及音訊的rounting,是音樂,還是打電話,音樂是能開啟什麼播放裝置,是開啟喇叭還是開啟聽筒播放,電話來了是否要把音樂關掉等等的音訊策略,包括MTK的音訊引數也會在這裡載入進來
          圖2  音訊系統的Hal圖         圖一的AudioFlinger會進入到Hal層,Audio Hal層有幾大重要的類:         AudioALSAStreamManager是入口管理下面的AudioALSAStreamIn和AudioALSAStreamOut         AudioALSAStreamOut管理著AudioALSAPlaybackXXXX AudioALSAStreamIn管理著AudioALSACaptureXXXX, AudioALSAPlaybackXXXX與AudioALSACaptureXXXX
這兩個類裡面的主要函式是open(),read()和write(),主要是負責對PCM buf的讀寫到Linux 的ALSA裡面。 AudioALSASpeechXXXX類是Aduio的一個演算法處理。 AudioALSAHardwareResourceManager這個類主要用於開啟和關閉硬體裝置,如MIC,喇叭等         AudioALSAVolumeController,這個類在上圖沒有體現,但是也很常用,主要用於Audio系統的音量控制,音量補償,音訊引數也在此得到應用         HAL與ALSA對接使用了TinyALSA庫,這個很重要。TinyALSA是一個輕量級的封裝庫,對ALSA介面進行了二次封裝,簡化了對ALSA的操作,具體原始碼目錄在/external/tinyalsa。這個庫銜接了Hal與Linux,這個是連線驅動的關鍵,一開始我針對Linux ALSA在HAL一頓狂找結果還是吃了閉門羹         網上有個說就法,Google為了避免與Linux有版權的爭議,自己能在原始碼上有更多的自己說話的權利,對Linux原生的ALSA進行了大量的修改,裁剪,去掉Linux GPL等協議。所有的Buf的處理交給了AudioTrack去處理,所以會有高延遲,低速率等問題,以至於Android手機無法做出高音質的手機。

二. 瞭解音訊系統架構

        音訊系統的AudioFlinger和AudioPolicy裡有很複雜的Buf調動和Buf管理,也有很複雜的音訊策略,裡面考慮到了音訊能遇到的各種狀況。從這兩個類下手很容易掉到坑裡出不來。Loopback是MTK音訊系統提供的一個工廠測試方法,其使用方法簡單粗暴,執行過程也簡單粗暴。使能它之後就可以任意的讓主副MIC與喇叭聽筒組合出聲。它就相當於HAL層的一個直接使用HAL層API的應用。對我們瞭解Android Audio系統程式碼的分佈與功能有很大的幫助。從Loopback下手再回到AudioFlinger和AudioPolicy會更好。Loopback也分為兩種模式一種是AFE模式,一種是Acoustic,通俗的說法就是前者是吹所模式,只能響應吹氣聲音,後者就是普通的聲音輸出。兩者在流程上大體是一至的,聲音模式會比吹氣模式多出了一個Speech的控制,也就是多出了一個語音的演算法處理。先從簡單的下手,兩種模式的比較也會讓我們更容易瞭解程式碼。

(1)呼叫Loopback流程

        涉及到的檔案:
        frameworks/av/media/libmedia/AudioSystem.cpp
        frameworks/av/services/audioflinger/AudioFlinger.cpp
        vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSAHardware.cpp
        vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/LoopbackManager.cpp
        vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSALoopbackController.cpp
        vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSADeviceConfigManager.cpp
        vendor//mediatek/proprietary/hardware/audio/mt6735/aud_drv/AudioALSAHardwareResourceManager.cpp
        vendor//mediatek/proprietary/hardware/audio/mt6735/aud_drv/AudioALSAVolumeController.cpp             Loopback使用方法是在APP中直接使用這個方法:AudioSystem.setParameters(“SET_LOOPBACK_TYPE=Type, OutputDevice”);。要啟用主MIC進喇叭出的吹氣模式Type為1,OutputDevice為3。這個引數會層層呼叫到HAL啟動Loopback。我們可以跟蹤這個引數的流向瞭解一下Audio系統是怎麼分佈的。         前面一小段的流程大致是這樣:AudioSystem.java-->android_media_AudioSystem.cpp-->AudioSystem.cpp
  1. //AudioSystem.cpp
  2. status_tAudioSystem::setParameters(constString8&keyValuePairs)
  3. {
  4. //第一個引數為:AUDIO_IO_HANDLE_NONE
  5. return setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs);
  6. }
  7. status_tAudioSystem::setParameters(audio_io_handle_t ioHandle,constString8&keyValuePairs)
  8. {
  9. //省略掉無關邏輯
  10. const sp<IAudioFlinger>&af =AudioSystem::get_audio_flinger();
  11. if(af ==0)return PERMISSION_DENIED;
  12. ret = af->setParameters(ioHandle, keyValuePairs);
  13. }
        上面直接呼叫到AudioFlinger的setParameters()
  1. //AudioFlinger.cpp
  2. status_tAudioFlinger::setParameters(audio_io_handle_t ioHandle,constString8& keyValuePairs)
  3. {
  4. //上面傳下來的ioHandle = AUDIO_IO_HANDLE_NONE
  5. if(ioHandle == AUDIO_IO_HANDLE_NONE){
  6. mHardwareStatus = AUDIO_HW_SET_PARAMETER;
  7. for(size_t i =0; i < mAudioHwDevs.size(); i++){
  8. //mAudioHwDevs陣列是HAL層“Dev”的集合,這裡的Dev並不是指真正的具體裝置
  9. //而是把HAL的一個大模組抽象為一個“Dev”,例如,MTK音訊HAL可以取名為“MTK Audio HAL”
  10. //然後還有高通的“Qual Audio HAL”等等,還能細分為Wifi,USB,A2DP.....
  11. audio_hw_device_t*dev = mAudioHwDevs.valueAt(i)->hwDevice();
  12. status_t result = dev->set_parameters(dev, keyValuePairs.string());
  13. }
  14. }
  15. }
        mAudioHwDevs這個陣列又是怎麼來的呢?在AudioPolicyManager的建構函式裡,會向本地檔案載入一個audio_policy.conf。該config檔案會決定音訊系統有哪些通路,USB,A2DP等等,這些通路下面有哪些裝置,還有一些裝置的引數。大概摘抄一點
  1. audio_hw_modules {
  2. primary {
  3. global_configuration {
  4. attached_output_devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE
  5. default_output_device AUDIO_DEVICE_OUT_SPEAKER
  6. attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_FM_TUNER|AUDIO_DEVICE_IN_VOICE_CALL
  7. audio_hal_version 3.0
  8. }
  9. devices {
  10. headset {
  11. type AUDIO_DEVICE_OUT_WIRED_HEADSET
  12. gains {
  13. gain_1 {
  14. mode AUDIO_GAIN_MODE_JOINT
  15. channel_mask AUDIO_CHANNEL_OUT_STEREO
  16. min_value_mB -6400
  17. max_value_mB 0
  18. default_value_mB 0
  19. step_value_mB 100
  20. min_ramp_ms 0
  21. max_ramp_ms 0
  22. }
  23. }
  24. }
  25. ......
       這些模組名字載入好後會跟據這些名字迴圈地去查詢HAL模組,把找到的模組填入到mAudioHwDevs中。這段邏輯目前看得不是很仔細,有些邏輯不嚴謹
  1. //AudioFlinger.cpp
  2. audio_module_handle_tAudioFlinger::loadHwModule_l(constchar*name)
  3. {
  4. audio_hw_device_t*dev;
  5. int rc = load_audio_interface(name,&dev);
  6. mAudioHwDevs.add(handle,newAudioHwDevice(handle, name, dev, flags));
  7. }
  8. staticint load_audio_interface(constchar*if_name,audio_hw_device_t**dev)
  9. {
  10. rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name,&mod);
  11. }
         mAudioHwDevs陣列是找到了如何初始化,setParameters()呼叫了該元素裡的set_parameters()函式繼續往下傳引數。我還要找到具體的HAL模組才知道引數如何往下傳。 hw_get_module_by_class()這個API會通過ID去找到HAL註冊了哪些模組。我們搜一下上面的AUDIO_HARDWARE_MODULE_ID就可以找到具體是載入了哪些模組,在Vendor目錄下現有兩個HAL模組
  1. 1MTK自己實現的Audio HAL,名字和ID如下
  2. id: AUDIO_HARDWARE_MODULE_ID,
  3. name:"MTK Audio HW HAL"
  4. 2)還有一個是A2DP
  5. id: AUDIO_HARDWARE_MODULE_ID,
  6. name:"A2DP Audio HW HAL"
        就這樣把HAL模組加載出來了,所有的HAL模組都明非常標準和非常明確的介面定義,對於HAL以上的邏輯,只需找到ID和相應的名字即可找到需要使用的模組,即使你有100個廠商,100個廠商又有100個模組,還是依照明確的標準去走,這個就是面向物件程式設計中的一個核心理念,面向介面程式設計,不管你邏輯如何變,介面一定不能變!這樣就能確保軟體的低耦合,可移植。
        我們用的是“MTK Audio HW HAL”這個Audio HAL module。很容易地就在裡面找到了set_parameters函式指標。指向adev_set_parameters()函式
  1. staticint adev_set_parameters(struct audio_hw_device *dev,constchar*kvpairs)
  2. {
  3. struct legacy_audio_device *ladev = to_ladev(dev);
  4. //結過轉換,呼叫到的是AudioALSAHardware.cpp
  5. return ladev->hwif->setParameters(String8(kvpairs));
  6. }
        AudioALSAHardware::setParameters()是Audio setParameters()的最終執行函式,也是一個很有趣的函式。這個函式就像Audio系統的“後門”,裡面可以粗暴有力的設定Audio引數,而影響整個Audio系統的執行。例如,可以調整音量,開啟關閉MIC,FM的音訊開關等等,知道這些暗門不知道能不能幹一點壞事。就像我們強制開啟Loopback,聲音就開始從MIC進喇叭出,普通使用者還沒有辦法關閉,除非重啟平板,細思極恐。然後裡面還保留著一些音訊裝置的測試方法和校準方法。
  1. status_tAudioALSAHardware::setParameters(constString8&keyValuePairs)
  2. {
  3. // Loopback
  4. if(param.get(keySET_LOOPBACK_TYPE, value_str)== NO_ERROR)
  5. {
  6. 相關推薦

    Android ALSA音訊系統架構分析1----Loopback瞭解Audio

    /************************************ Author:劉江明 * Environment:MTK Android 6.0* Date:2017年05月25日***********************************/

    Ext4檔案系統架構分析

    本文描述Ext4 檔案系統磁碟佈局和元資料的一些分析,同樣適用於ext2,ext3檔案系統,除了它們不支援的ext4的特性。整個分析分兩篇博文,分別概述佈局和詳細介紹各個佈局的資料結構及組織定址方式等。 1.Ext4 檔案系統佈局綜述 一個Ext4 檔案系統被分成一系列

    Android鎖屏勒索病毒分析1BWM線上

    1.樣本概況 1.1 基本資訊 樣本名稱: 刷贊. 所屬家族: 鎖屏勒索病毒(a.rogue.SimpleLocker.a) MD5值: 7626090b69cd1e2e5671a022712808eb 包名: com.binge.mohe 入口: MainActiv

    Hadoop 系統架構分析2

    YARN 概述 YARN 的本質是一個全域性的資源管理器(ResourceManager,RM),它控制整個叢集並管理基礎計算資源在應用程式之間的分配。 資源管理器RM 和它在各個節點的代理——節點管理器(NodeManager,NM)構成了整個資料計算的框架

    Android 6.0 Camera2 原始碼分析1不同的activity介面

    Camera2中主要的activity activity都在AnroidManifest.xml中有註冊。我們先通過AndroidManifest.xml來大概的瞭解下都有哪些activity AndroidManifest.xml <?xm

    Android RePlugin 使用及原始碼分析1

    1. RePlugin 概述 RePlugin是一套完整的、穩定的、適合全面使用的,佔坑類外掛化方案。 具體來說有如下特點: 完整的:讓外掛執行起來“像單品那樣”,支援大部分特性 穩定的:如此靈活完整的情況下,其框架崩潰率僅為業內很低的“萬分之一” 適

    Android應用程式啟動詳解原始碼瞭解App的啟動過程

    本文承接《Android應用程式啟動詳解(一)》繼續來學習應用程式的啟動的那些事。上文提到startActivity()方法啟動一個app後經過一翻過程就到了app的入口方法ActivityThread.main()。其實我們在之前的文章中《Android的訊息機制(二)之L

    Android系統原理與原始碼分析1:利用Java反射技術阻止通過按鈕關閉對話方塊

    本文為原創,如需轉載,請註明作者和出處,謝謝!     眾所周知,AlertDialog類用於顯示對話方塊。關於AlertDialog的基本用法在這裡就不詳細介紹了,網上有很多,讀者可以自己搜尋。那

    Android系統載入Apk檔案的時機和流程分析1--Android 4.4.4 r1的原始碼

    Android系統在啟動時安裝應用程式的過程,這些應用程式安裝好之後,還需要有一個Home應用程式來負責把它們在桌面上展示出來,在Android系統中,這個預設的Home應用程式就是Launcher了。Android系統的Home應用程式Launcher是由Activit

    Web API應用架構設計分析1

    人員管理 門面 guid orm 和平 ide 額外 簡化 響應 Web API應用架構設計分析(1) Web API 是一種應用接口框架,它能夠構建HTTP服務以支撐更廣泛的客戶端(包括瀏覽器,手機和平板電腦等移動設備)的框架, ASP.NET Web API 是一種

    基於hadoop生態系統的mahout推薦和聚類分析1

    簡介 hadoop是Apache旗下的一個開源分散式計算平臺,在分散式環境下為使用者提供處理海量資料的能力。 mahout是hadoop下的一個子專案,主要用於推薦、分類和聚類分析 一、推薦 關於推薦的演算法有很多,本次主要介紹協同過濾演算法。 (1)基於使用者的協

    【學以致用】android功能實現5---android8.0 Launcher獲取快捷方式原始碼分析1

    從其他應用往桌面建立快捷方式,android8.0統一採用requestPinShortcut的方式。 對於桌面而言,是怎麼從requestPinShortcut獲取快捷方式資訊在桌面建立快捷方式呢? Android8.0的快捷方式引數不再通過廣播傳送,而是存放在系統當中

    Spring源代碼分析1---LocalSessionFactoryBean(工廠的工廠)

    self action interface 開始 environ mac hbm upd put LocalSessionFacotoryBean其實就是適配了Configuration對象,或者說是一個工廠的工廠,他是Configuration的工廠,生成了Configu

    R語言實戰 - 基本統計分析1- 描述性統計分析

    4.3 summary eas 方法 func -- 4.4 1.0 6.5 > vars <- c("mpg", "hp", "wt") > head(mtcars[vars]) mpg hp wt Maz

    android之使用百度地圖1

    baidu man ports cte public phone sch lis stat Activity_main.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmln

    電商系統架構總結

    get 部分 上傳 其他 維護 let turn 同事 stat 最近主導了一個電商系統的設計開發過程,包括前期分析設計,框架搭建,功能模塊的具體開發(主要負責在線支付部分),成功上線後的部署維護,運維策略等等全過程。 雖然這個系統不是什麽超大型的電商系統

    電商系統架構總結

    esp 簡單 zed expire cts project scac 允許 類型 二 Redis緩存 考慮到將來服務器的升級擴展,使用redis代替.net內置緩存是比較理想的選擇。redis是非常成熟好用的緩存系統,安裝配置非常簡單,直接上官網下載安裝包 安裝啟動就行

    J2EE WEB應用架構分析

    高可用 XML 控制 之間 財務 環境 優缺點 基於 list 優缺點 優點: 一些開發商開始采用並推廣這個框架 作為開源項目,有很多先進的實現思想 對大型的應用支持的較好 有集中的網頁導航定義 缺點: 不是業屆標準 對開發工具的支持不夠 復雜的taglib,需要比較長的時

    當前流行的J2EE WEB應用架構分析

    贊助商 html form 技術資料 tex 1.3 資料 估計 情況 覆蓋 架構概述 J2EE體系包括java server pages(JSP) ,java SERVLET, enterprise bean,WEB service等技術。這些技術的出現給電子商務時代的

    算法分析1

    tex margin 輸入 存在 增長 com pan strong 圖片 算法分析 一、算法設計要求 1、正確性 2、可讀性 3、健壯性:當輸入的數據非法時,算法也能做出適當的反應或進行處理,而不會產生莫名其妙的輸出結果 4、效率和低存儲量要求 二、算法效率的