Android 4.1 Audio系統變化說明
阿新 • • 發佈:2017-05-21
興趣 oop cau none bstr sta 解決 應用 sco
轉自Android 4.1 Audio系統變化說明
Android 4.1,英文代號簡稱JB。在國人眼裏,JB這個詞還和動物有點關系。Google如此頻繁修改Android,終於推出了一個可以被大家整天JB JB掛在嘴上的版本。以後我的文章也可以一面用JB表示版本號,一面用JB表示毛主席常說的”戰略上的鄙視了“。請大家根據上下文揣摩我寫下JB一詞的心情。
今天將稍深入得介紹一下JB 4.1在Audio系統做的翻天覆地的改動。這裏先啰嗦幾句:就像80後經常抱怨自己晚生了幾年一樣,馬上就會有很多碼農抱怨接觸Android太晚了。為何?JB Audio系統的難度相對4.0, 2.3, 2.2已經非常非常大了。99%的情況下,在你沒有看到這個NB(這不是臟話,4.1 Audio系統中就有一個類叫NBAIO,籃球控不要搞錯成NBA了,原意是Non-block Audio I/O。看到了吧,非阻塞I/O ,各位自問下,有多少人對這個東西有深刻理解?)東西演化的基礎上,不太可能能看懂JB Audio系統。所以,建議這99%中的沒有見識過Audio演化歷史的屌絲同學們,先仔細研究(以前我僅僅建議大家看看,現在提高要求為仔細研究)《深入理解Android 卷I》Audio系統。BTW,此書在某個章節裏特意提醒過大家要去研究下各種I/O模型,不知道有幾個人屌過我了。
本文將分幾個部分,事前沒有打草稿,所以會有點亂。
先從Java層AudioTrack類說起
一 AudioTrack Java類變化說明
- 聲道數上,以前只有單聲道(MONO)和立體聲(STEREO),現在拓展到最NB八聲道(7.1 HiFi啊)。參數名為CHANNEL_OUT_7POINT1_SURROUND。看到這個參數,我下巴咣當就掉下來了。這玩意,一時半會我還弄不明白是個什麽道理。有知曉的屌絲碼農們不妨告訴大家。 當然,最終的輸出還是雙聲道。多聲道(大於2)的時候會使用downmixer處理(下變換處理,同學們可搜索之)
- 其他的變化也有,但不大了。我這裏先挑一些吸引眼球的。BTW,放心,不會像那個瀧澤蘿拉首秀片子一樣只讓大家看見大鼻孔的。
- JNI層變化不大。
- Audio Native核心代碼移到了framework/av下。對,你沒看錯。真的是av。這就是JB Audio一個比較大的變化。Audio Native核心代碼全部移到了frameworks/AV目錄下。
- AudioTrack增加了一個變量,用於控制使用它的進程的調度優先級(前文說錯了,這裏確實設置的是nicer值)。如果處於播放狀態的話,將設置進程調度優先級為ANDROID_PRIORITY_AUDIO。就像你們看到馬賽克時一定會嘟喃一樣。我這裏也要特別啰嗦幾句。在單核CPU的情況下,設置優先級是比較愚蠢的(ANDROID_PRIORITY_AUDIO的值為-16,優先級極高,單核設置個這麽高的怪物,不知道其他app還怎麽玩。如果你不知道我在說什麽,先看看這篇文章吧,http://blog.csdn.net/innost/article/details/6940136)。但現在2核,4核已經比較常見了,這裏就可以來玩玩調度方面的事情。對屌絲碼農的真正考驗是:多核並行編程,linux os的原理,需要各位屌絲同學努力掌握。Audio已經不那麽能輕易被你們任意蹂躪了。另外,低端手機,求求你們別移植4.1了,這個真的不是低端能玩的。
- AudioTrack升級為父親了。JB為它定義了一個莫名其妙的的TimedAudioTrack子類。這個類在編解碼的aah_rtp(我現在還不知道aah是什麽)裏邊用到了。從註釋上看,該類是一個帶時間戳(有時間戳,就可以做同步了)的音頻輸出接口。詳細理解的話,就需要結合具體應用場景去分析了(主要是rtp這一塊)。搞編解碼的同學們,抓緊了!
- 另外一個超級復雜的變化,是Audio定義了幾個輸出flag(見audio.h的audio_output_flags_t枚舉定義)。根據註釋,該值有兩個作用,一個是AT的使用者可以指明自己想使用怎樣的outputDevice。另外一個是設備廠商可以通過它聲明自己支持的輸出設備(看來,設備初始化的時候,又增添了參數讀取和配置這方面的工作)。不過,從該枚舉的定義來看,我還看不出它和硬件有什麽關系。它定義的值如下:
- AudioTrack其他變化不大。AudioTrack.cpp總共才1600多行,so easy!
- AF創建,包括其onFirstRef函數
- openOutput函數及MixerThread對象的創建
- AudioTrack調用createTrack函數
- AudioTrack調用start函數
- AF混音,然後輸出
- 現在對Primary設備的音量有了更為細致的控制,例如有些設備能設音量,有些不能設置音量,所以定義了一個master_volume_support(AudioFlinger.h)枚舉,用來判斷Primary設備的音量控制能力。
- 以前播放過程的standby時間(就是為了節電而用)是寫死的,現在可由ro.audio.flinger_standbytime_ms控制,如果沒有這個屬性,則默認是3秒。AF還增加了其他變量控制,例如有一個gScreenState變量,用來表示屏幕是開還是關。可通過AudioSystem::setParameters來控制。另外還定義了一個和藍牙SCO相關的mBtNrecIsOff變量,是用於控制藍牙SCO(錄音時用,藍牙上的一個專業術語叫,NREC。不知道是什麽,用懂的人告訴我一下)時禁止AEC和NS特效的。請參考AudioParameter.cpp
- ThreadBase從Thread派生,所以它會運行在一個單獨的線程中(啰嗦一句,線程和對象其實沒有關系的,不懂多線程編程的碼農們請務必認真學習多線程)。它定義了一個枚舉type_t,用來表示子類的類型,這幾個類型包括MIXER,DIRECT,RECORD,DUPLICATING等。這個應該比較好懂吧?
- ThreadBase的內部類TrackBase從ExtendedAudioBufferProvider派生,這個應該是新增加的。TrackBase嘛,大家把它理解成一個Buffer Container就好了。
- ThreadBase的內部類PMDeathRecipient用來監聽PowerManagerService的死亡消息。這個設計有點搞,因為PMS運行在SystemService中,只有SS掛了,PMS才會掛。而SS掛了,mediaserver也會被init.rc的規則給弄死,所以AudioFlinger也會死。既然大家都一起死,速度很快。故,設置這個PMDeathRecipient有何大的意義呢?
- 其定義了一個枚舉mixer_state,用來反映當前混音工作的狀態,有MIXER_IDLE,MIXER_READY和MIXER_ENABLED
- 定義了幾個虛函數,需要子類實現,包括threadLoop_mix,prepareTracks_l等。這幾個函數的抽象工作做得還是可以。但變化之大讓人防不勝防啊。
- Track類增加了從VolumeProvider派生,這個VP是用來控制音量的。根據前面的介紹,在JB中,音量管理比以前來得細致
- 新增定義了TimedTrack。這個類的作用和前面提到的rtp aah有關。等同學們學完本篇,即可開展相應研究,打響殲滅戰!
- MixerThread從PlaybackThread派生,這個關系至始至終不會變化,相信以後也不會。
- MT最大的變化是其中幾個重要的成員變量。大家肯定認識其中的AudioMixer,它是用來混音的。
- 新增一個Soaker對象(由編譯宏控制),它是一個線程。這個單詞的前綴soak在webster詞典(相信經歷過,那些年,我們一起GRE的日子 的人知道什麽是webster)中最貼切的一條解釋是to cause to pay an exorbitant amount。還是不很明白是幹嘛的?再一看代碼。原來,soaker就是一個專職玩弄CPU的線程。它的工作就是不斷得做運算,拉高CPU使用率。它的存在應該是為了測試新AF框架在多核CPU上的效率等等等的問題。所以,低端智能機們,你們不要玩JB了。
- 另外一條證明低端智能機不能隨便玩JB的鐵證就是:我們看到MT中新增了一個FastMixer,它也是一個線程。明白了?在JB中,多核智能機上,混音工作可以放到FastMixer所在的線程來做,當然速度,效率會高了。
- FastMixer工作流程比較復雜,又牽扯到多線程同步。所以,這裏定義了一個FastMixerStateQueue,它由typedef StateQueue<FastMixerState>得到。首先它是一個StateQueue(簡單把它當做數組吧)。其數組元素的類型為FastMixerState。一個StateQueue通過mStats變量保存4個FasetMixerState成員。
- FasetMixerState類似狀態機,有一個enum Command,用來控制狀態的。FastMixerState中含有一個八元組的FastTracks數組。FastTrack是用來完成FastMixer的一個功能類。
- 每個FastTrack都有一個mBufferProvider,該成員類型為SourceAudioBufferProvider。
- NBAIO包括三個主要類,一個是NBAIO_Port,代表I/O端點,其中定義了一個negotiate函數,用於調用者和I/O端點進行參數協調。註意,並不是為I/O端點設置參數。因為I/O端點往往和硬件相關,而硬件有些參數是不能像軟件一般隨意變化的。例如硬件只支持最多44.1KHZ的采樣率,而調用者傳遞48KHz的采樣率,這直接就需要一個協商和匹配的過程。這個函數的比較難用,主要是規則較多。同學們可以參考其註釋說明。
- NBAIO_Sink對應output端點,其定義了write和writeVia函數,writeVia函數需要傳遞一個回調函數via,其內部將調用這個via函數獲取數據。類似數據的推/拉兩種模式。
- NBAIO_Source對應input端點,其定義了read和readVia函數。意義同NBAIO_Sink。
- 定義一個MonoPipe和MonoPipeReader。Pipe即管道,MonoPipe和LINUX中的IPC通信Pipe沒毛關系,只不過借用了這個管道概念和思路。MonoPipe即只支持單個讀者的Pipe(AF中,它是MonoPipeReader)。這兩個Pipe,代表了Audio的Output和Input端點。
- MT中由mOutputSink指向AudioStreamOutSink,此類用NBAIO_Sink派生,用於普通的mixer的輸出。mPipeSink指向MonoPipe,本意是用於FastMixer的。另外,還有一個變量mNormalSink,它將根據FastMixer的情況,指向mPipeSink,或者是mOutputSink。這段控制的邏輯如下:
- PlaybackThread的threadLoop定義了整個音頻處理的大體流程,具體的細節通過幾個虛函數(如prepareTracks_l,threadLoop_mix,threadLoop_write)交給子類去實現了
- MT變化大的首先是prepareTracks_l,首先處理的是FastMix類型的Track,判斷標準是該Track是否設置了TRACK_FAST標誌(爽了,目前JB中還沒有哪個地方使用了這個標誌)。這部分判斷比較復雜。首先FastMixer維護了一個狀態機,另外,這個FastMixer運行在自己的線程裏,所以線程同步是必須的。這裏采用的是狀態來控制FastMixer的工作流程。由於涉及到多線程,所以音頻的underrun,overrun狀態(不知道是什麽嗎?看前面提到的參考書!)也是一個需要處理的棘手問題。另外,一個MT是帶一個AudioMixer對象,這個對象將完成數據的混音,下變換等等超難度,數字音頻處理等方面的工作。也就是說,對於混音來說,前期的prepare工作還是由MT線程來完成,因為這樣可以做到統一管理(有些Track並不需要使用FastMixer。但仔細一想,誰都希望處理越快越好,在多核CPU上,將混音工作交給多個線程處理是充分利用CPU資源的典範,這應該是未來Android演化的趨勢。所以,我估計這個JB還沒完全長大....)。對FastMixer感興趣的屌絲們,請務必認真研究prepareTracks_l函數。
- MT下一個重要函數就是threadLoop_mix了,由於存在一個TimedTrack類,那麽AudioMixer的process函數就帶上了一個時間戳,PTS,presentation timestamp。從編解碼角度來說,還有一個DTS,Decode timestamp。這裏要閑扯下PTS和DTS的區別了。DTS是解碼時間,但編碼的時候由於有可能會根據未來幀來編碼當前幀。所以,解碼的時候會先解未來幀,然後解出當前幀,但是。你播放的時候可不能先播未來幀。只能老老實實得按播放順序來先播當前幀,然後播未來幀(盡管先解出來的是未來幀)。關於PTS/DTS,請屌絲們研究下IBP相關的知識吧。回到MT,這個PTS是從硬件hal對象取的,應該是HAL內部維護的時間戳。這個時間戳原則上會比較準確。
- 混音完了,再做特效處理(和以前的版本差不多),然後調用threadLoop_write。MT的threadLoop_write函數的輸出端點就是前面那個坑爹的mNormalSink,如果不為空,就調用它的write函數。想著是調用NBAIO_Sink的非阻塞的write函數。根據圖2的分析,它有可能是那個MonoPipe,也有可能就是AudioStreamOutputSink,這個sink節點用得就是以前的AudioStreamOutput。而MonoPipe的write其內部就是一個buffer。並沒有和真實的AUDIO HAL Output掛上關系。這.....咋整??(大膽假設,小心求證。只能是FastMixer把這個buffer取出來,然後再寫到真實的Audio HAL中了。因為在MixerThread構造函數中,曾經為FastTrack保存過mOutputSink,這個就是用來和AudioStreamOutput聯系的)
- FastMixer內部還使用了一個AudioMixer,用於它的混音
- 然後再write出去.....
- 非常註重調試了,加了大量的XXXDump類。看來,Google自己開發的時候也碰到不少問題。簡單的功能,誰會想著去dump呢?
- 增加AudioWatchdog類,用來監控AF性能的,如CPU使用情況等。
- 要充分利用多核資源,所以FastMixer的出現是必然。還包括NBAIO接口。感覺對HAL編寫會有大的挑戰。
- 增加TimedTrack和SyncEvent,對於RTP或者多個player間的同步會帶來比較好的用戶體驗。
- 增加native層往java層通知的接口。
Android 4.1 Audio系統變化說明