引言
AudioFlinger是Android音頻系統的兩大服務之一,另一個服務是AudioPolicyService,這兩大服務都在系統啟動時有MediaSever載入,載入的代碼位於:frameworks/base/media/mediaserver/main_mediaserver.cpp。AudioPolicyService的相關內容請參考另一編文章:《Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager 》
http://blog.csdn.net/DroidPhone/archive/2010/10/18/5949280.aspx
本文主要介紹AudioFlinger,AudioFlinger向下訪問AudioHardware,實現輸出音頻資料,控制音頻參數。同時,AudioFlinger向上通過IAudioFinger介面提供服務。所以,AudioFlinger在Android的音頻系統架構中起著承上啟下的作用,地位相當重要。AudioFlinger的相關代碼主要在:frameworks/base/libs/audioflinger,也有部分相關的代碼在frameworks/base/media/libmedia裡。
AudioFlinger的類結構
下面的圖示描述了AudioFlinger類的內部結構和關係:
圖一 AudioFlinger的類結構
不知道各位是否和我一樣,第一次看到AudioFlinger類的定義的時候都很鬱悶--這個類實在是龐大和臃腫,可是當你理清他的關係以後,你會覺得相當合理。下面我們一一展開討論。
這是AudioFlinger向外提供服務的介面,例如openOutput,openInput,createTrack,openRecord等等,應用程式或者其他service通過ServiceManager可以獲得該介面。該介面通過繼承BnAudioFlinger得到。
在AudioFlinger中,Android為每一個放音/錄音裝置均建立一個處理線程,負責音頻資料的I/O和合成,ThreadBase是這些線程的基類,所有的播放和錄音線程都派生自ThreadBase
應用程式每建立一個音軌(AudioTrack/AudioRecord),在AudioFlinger中都會建立一個對應的Track執行個體,TrackBase就是這些Track的基類,他的衍生類別有:
-
- PlaybackTread::Track // 用於普通播放,對應於應用程式層的AudioTrack
- PlaybackThread::OutputTrack // 用於多重裝置輸出,當藍芽播放開啟時使用
- RecordThread::RecordTrack // 用於錄音,對應於應用程式層的AudioRecord
- 播放
預設的播放線程是MixerThread,它由AudioPolicyManager建立,在AudioPolicyManager的建構函式中,有以下代碼:
mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,<br /> &outputDesc->mSamplingRate,<br /> &outputDesc->mFormat,<br /> &outputDesc->mChannels,<br /> &outputDesc->mLatency,<br /> outputDesc->mFlags);
最終會進入AudioFlinger的openOut函數:
......<br />thread = new MixerThread(this, output, ++mNextThreadId);<br />......<br />mPlaybackThreads.add(mNextThreadId, thread);<br />......<br />return mNextThreadId;
可以看到,建立好的線程會把該線程和它的Id儲存在AudioFlinger的成員變數mPlaybackThreads中,mPlaybackThreads是一個Vector,AudioFlinger建立的線程都會儲存在裡面,最後,openOutput返回該線程的Id,該Id也就是所謂的audio_io_handle_t,AudioFlinger的調用者這能看到這個audio_io_handle_t,當需要訪問時傳入該audio_io_handle_t,AudioFlinger會通過mPlaybackThreads,得到該線程的指標。
要播放聲音,應用程式首先要通過IAudioFlinger介面,調用createTrack(),關於createTrack的流程,可以參看我的另一篇文章:
http://blog.csdn.net/DroidPhone/archive/2010/10/14/5941344.aspx
createTrack會調用PlaybackThread類的createTrack_l函數:
track = thread->createTrack_l(client, streamType, sampleRate, format,<br /> channelCount, frameCount, sharedBuffer, &lStatus);
再跟入createTrack_l函數中,可以看到建立了PlaybackThread::Track類,然後加入播放線程的track列表mTracks中。
track = thread->createTrack_l(client, streamType, sampleRate, format,<br /> channelCount, frameCount, sharedBuffer, &lStatus);<br />......<br />mTracks.add(track);
在createTrack的最後,建立了TrackHandle類並返回,TrackHandle繼承了IAudioTrack介面,以後,createTrack的調用者可以通過IAudioTrack介面與AudioFlinger中對應的Track執行個體互動。
trackHandle = new TrackHandle(track);<br />......<br />return trackHandle;
最後,在系統運行時,AudioFlinger中的線程和Track的結構大致如所示:它會擁有多個背景工作執行緒,每個線程擁有多個Track。
圖二 AudioFlinger的線程結構
播放線程實際上是MixerThread的一個執行個體,MixerThread的threadLoop()中,會把該線程中的各個Track進行混合,必要時還要進行ReSample(重採樣)的動作,轉換為統一的採樣率(44.1K),然後通過音頻系統的AudioHardware層輸出音頻資料。
錄音的流程和放音差不多,只不過資料流動的方向相反,錄音線程變成RecordThread,Track變成了RecordTrack,openRecord返回RecordHandle,詳細的暫且不表。
AudioFlinger中有一個特殊的線程類:DuplicatingThread,從圖一可以知道,它是MixerThread的子類。當系統中有兩個裝置要同時輸出時,DuplicatingThread將被建立,通過IAudioFlinger的openDuplicateOutput方法建立DuplicatingThread。
int AudioFlinger::openDuplicateOutput(int output1, int output2)<br />{<br /> Mutex::Autolock _l(mLock);<br /> MixerThread *thread1 = checkMixerThread_l(output1);<br /> MixerThread *thread2 = checkMixerThread_l(output2);<br /> ......<br /> DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId);<br /> thread->addOutputTrack(thread2);<br /> mPlaybackThreads.add(mNextThreadId, thread);<br /> return mNextThreadId;<br />}
建立 DuplicatingThread時,傳入2個需要同時輸出的目標線程Id,openDuplicateOutput先從mPlaybackThreads中根據Id取得相應輸出線程的執行個體,然後為每個線程建立一個虛擬AudioTrack----OutputTrack,然後把這個虛擬AudioTrack加入到目標線程的mTracks列表中,DuplicatingThread在它的threadLoop()中,把Mixer好的資料同時寫入兩個虛擬OutputTrack中,因為這兩個OutputTrack已經加入到目標線程的mTracks列表,所以,兩個目標線程會同時輸出DuplicatingThread的聲音。
實際上,建立DuplicatingThread的工作是有AudioPolicyService中的AudioPolicyManager裡發起的。主要是當藍芽耳機和本機輸出都開啟時,AudioPolicyManager會做出以下動作:
- 首先開啟(或建立)藍芽輸出線程A2dpOutput
- 以HardwareOutput和A2dpOutput作為參數,調用openDuplicateOutput,建立DuplicatingThread
- 把屬於STRATEGY_MEDIA類型的Track移到A2dpOutput中
- 把屬於STRATEGY_DTMF類型的Track移到A2dpOutput中
- 把屬於STRATEGY_SONIFICATION類型的Track移到DuplicateOutput中
結果是,音樂和DTMF只會在藍芽耳機中輸出,而按鍵音和鈴聲等提示音會同時在本機和藍芽耳機中輸出。
圖三 本機播放時的Thread和Track
圖四 藍芽播放時的Thread和Track