【轉】Android Audio System

http://blog.csdn.net/DroidPhone/archive/2010/10/14/5941344.aspx

Android Audio System 之一:AudioTrack如何與AudioFlinger交換音頻數據

引子

Android Framework的音頻子系統中,每一個音頻流對應着一個AudioTrack類的一個實例,每個AudioTrack會在創建時註冊到AudioFlinger中,由AudioFlinger把所有的AudioTrack進行混合(Mixer),然後輸送到AudioHardware中進行播放,目前Android的Froyo版本設定了同時最多可以創建32個音頻流,也就是說,Mixer最多會同時處理32個AudioTrack的數據流。

如何使用AudioTrack

AudioTrack的主要代碼位於 frameworks/base/media/libmedia/audiotrack.cpp中。現在先通過一個例子來了解一下如何使用AudioTrack,ToneGenerator是android中產生電話撥號音和其他音調波形的一個實現,我們就以它爲例子:

ToneGenerator的初始化函數:

1. bool ToneGenerator::initAudioTrack() { 

2. // Open audio track in mono, PCM 16bit, default sampling rate, default buffer size

3.     mpAudioTrack = new AudioTrack(); 

4.     mpAudioTrack->set(mStreamType, 

5.                       0, 

6.                       AudioSystem::PCM_16_BIT, 

7.                       AudioSystem::CHANNEL_OUT_MONO, 

8.                       0, 

9.                       0, 

10.                       audioCallback, 

11. this

12.                       0, 

13.                       0, 

14.                       mThreadCanCallJava); 

15. if (mpAudioTrack->initCheck() != NO_ERROR) { 

16.         LOGE("AudioTrack->initCheck failed"); 

17. goto initAudioTrack_exit; 

18.     } 

19.     mpAudioTrack->setVolume(mVolume, mVolume); 

20.     mState = TONE_INIT; 

21.     ...... 

22.  } 

可見,創建步驟很簡單,先new一個AudioTrack的實例,然後調用set成員函數完成參數的設置並註冊到AudioFlinger中,然後可以調用其他諸如設置音量等函數進一步設置音頻參數。其中,一個重要的參數是audioCallback,audioCallback是一個回調函數,負責響應AudioTrack的通知,例如填充數據、循環播放、播放位置觸發等等。回調函數的寫法通常像這樣:

1. void ToneGenerator::audioCallback(int event, void* user, void *info) { 

2. if (event != AudioTrack::EVENT_MORE_DATA) return

3.     AudioTrack::Buffer *buffer = static_cast<AudioTrack::Buffer *>(info); 

4.     ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); 

5. short *lpOut = buffer->i16; 

6.     unsigned int lNumSmp = buffer->size/sizeof(short); 

7. const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc; 

8. if (buffer->size == 0) return

9.

10. // Clear output buffer: WaveGenerator accumulates into lpOut buffer

11.     memset(lpOut, 0, buffer->size); 

12.     ...... 

13. // 以下是產生音調數據的代碼,略....

14. } 

該函數首先判斷事件的類型是否是EVENT_MORE_DATA,如果是,則後續的代碼會填充相應的音頻數據後返回,當然你可以處理其他事件,以下是可用的事件類型:

1. enum event_type { 

2.         EVENT_MORE_DATA = 0,        // Request to write more data to PCM buffer.

3.         EVENT_UNDERRUN = 1,         // PCM buffer underrun occured.

4.         EVENT_LOOP_END = 2,         // Sample loop end was reached; playback restarted from loop start if loop count was not 0.

5.         EVENT_MARKER = 3,           // Playback head is at the specified marker position (See setMarkerPosition()).

6.         EVENT_NEW_POS = 4,          // Playback head is at a new position (See setPositionUpdatePeriod()).

7.         EVENT_BUFFER_END = 5        // Playback head is at the end of the buffer.

8.     }; 

開始播放:

1. mpAudioTrack->start(); 

停止播放:

1. mpAudioTrack->stop(); 

只要簡單地調用成員函數start()和stop()即可。

AudioTrack和AudioFlinger的通信機制

通常,AudioTrack和AudioFlinger並不在同一個進程中,它們通過android中的binder機制建立聯繫。

AudioFlinger是android中的一個service,在android啓動時就已經被加載。下面這張圖展示了他們兩個的關係:

clip_image002

圖一 AudioTrack和AudioFlinger的關係

我們可以這樣理解這張圖的含義:

· audio_track_cblk_t實現了一個環形FIFO;

· AudioTrack是FIFO的數據生產者;

· AudioFlinger是FIFO的數據消費者。

建立聯繫的過程

下面的序列圖展示了AudioTrack和AudioFlinger建立聯繫的過程:

clip_image004

圖二 AudioTrack和AudioFlinger建立聯繫

解釋一下過程:

· Framework或者Java層通過JNI,new AudioTrack();

· 根據StreamType等參數,通過一系列的調用getOutput();

· 如有必要,AudioFlinger根據StreamType打開不同硬件設備;

· AudioFlinger爲該輸出設備創建混音線程: MixerThread(),並把該線程的id作爲getOutput()的返回值返回給AudioTrack;

· AudioTrack通過binder機制調用AudioFlinger的createTrack();

· AudioFlinger註冊該AudioTrack到MixerThread中;

· AudioFlinger創建一個用於控制的TrackHandle,並以IAudioTrack這一接口作爲createTrack()的返回值;

· AudioTrack通過IAudioTrack接口,得到在AudioFlinger中創建的FIFO(audio_track_cblk_t);

· AudioTrack創建自己的監控線程:AudioTrackThread;

自此,AudioTrack建立了和AudioFlinger的全部聯繫工作,接下來,AudioTrack可以:

· 通過IAudioTrack接口控制該音軌的狀態,例如start,stop,pause等等;

· 通過對FIFO的寫入,實現連續的音頻播放;

· 監控線程監控事件的發生,並通過audioCallback回調函數與用戶程序進行交互;

FIFO的管理

audio_track_cblk_t

audio_track_cblk_t這個結構是FIFO實現的關鍵,該結構是在createTrack的時候,由AudioFlinger申請相應的內存,然後通過IMemory接口返回AudioTrack的,這樣AudioTrack和AudioFlinger管理着同一個audio_track_cblk_t,通過它實現了環形FIFO,AudioTrack向FIFO中寫入音頻數據,AudioFlinger從FIFO中讀取音頻數據,經Mixer後送給AudioHardware進行播放。

audio_track_cblk_t的主要數據成員:

    user             -- AudioTrack當前的寫位置的偏移
    userBase     -- AudioTrack寫偏移的基準位置,結合user的值方可確定真實的FIFO地址指針
    server          -- AudioFlinger當前的讀位置的偏移
    serverBase  -- AudioFlinger讀偏移的基準位置,結合server的值方可確定真實的FIFO地址指針

    frameCount -- FIFO的大小,以音頻數據的幀爲單位,16bit的音頻每幀的大小是2字節

    buffers         -- 指向FIFO的起始地址

    out               -- 音頻流的方向,對於AudioTrack,out=1,對於AudioRecord,out=0

audio_track_cblk_t的主要成員函數:

framesAvailable_l()和framesAvailable()用於獲取FIFO中可寫的空閒空間的大小,只是加鎖和不加鎖的區別。

1. uint32_t audio_track_cblk_t::framesAvailable_l() 

2. { 

3.     uint32_t u = this->user; 

4.     uint32_t s = this->server; 

5. if (out) { 

6.         uint32_t limit = (s < loopStart) ? s : loopStart; 

7. return limit + frameCount - u; 

8.     } else

9. return frameCount + u - s; 

10.     } 

11. } 

framesReady()用於獲取FIFO中可讀取的空間大小。

1. uint32_t audio_track_cblk_t::framesReady() 

2. { 

3.     uint32_t u = this->user; 

4.     uint32_t s = this->server; 

5. if (out) { 

6. if (u < loopEnd) { 

7. return u - s; 

8.         } else

9.             Mutex::Autolock _l(lock); 

10. if (loopCount >= 0) { 

11. return (loopEnd - loopStart)*loopCount + u - s; 

12.             } else

13. return UINT_MAX; 

14.             } 

15.         } 

16.     } else

17. return s - u; 

18.     } 

19. } 

我們看看下面的示意圖:

               _____________________________________________

               ^                          ^                             ^                           ^

        buffer_start              server(s)                 user(u)                  buffer_end

很明顯,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount - u + s

可能有人會問,應爲這是一個環形的buffer,一旦user越過了buffer_end以後,應該會發生下面的情況:

                _____________________________________________

               ^                ^             ^                                                     ^

        buffer_start     user(u)     server(s)                                   buffer_end

這時候u在s的前面,用上面的公式計算就會錯誤,但是android使用了一些技巧,保證了上述公式一直成立。我們先看完下面三個函數的代碼再分析:

1. uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) 

2. { 

3.     uint32_t u = this->user; 

4.     u += frameCount; 

5.     ...... 

6. if (u >= userBase + this->frameCount) { 

7.         userBase += this->frameCount; 

8.     } 

9. this->user = u; 

10.     ...... 

11. return u; 

12. } 

 

1. bool audio_track_cblk_t::stepServer(uint32_t frameCount) 

2. { 

3. // the code below simulates lock-with-timeout

4. // we MUST do this to protect the AudioFlinger server

5. // as this lock is shared with the client.

6.     status_t err; 

7.     err = lock.tryLock(); 

8. if (err == -EBUSY) { // just wait a bit

9.         usleep(1000); 

10.         err = lock.tryLock(); 

11.     } 

12. if (err != NO_ERROR) { 

13. // probably, the client just died.

14. return false

15.     } 

16.     uint32_t s = this->server; 

17.     s += frameCount; 

18. // 省略部分代碼

19. // ......

20. if (s >= serverBase + this->frameCount) { 

21.         serverBase += this->frameCount; 

22.     } 

23. this->server = s; 

24.     cv.signal(); 

25. lock.unlock(); 

26. return true

27. } 

 

1. void* audio_track_cblk_t::buffer(uint32_t offset) const

2. { 

3. return (int8_t *)this->buffers + (offset - userBase) * this->frameSize; 

4. } 

stepUser()和stepServer的作用是調整當前偏移的位置,可以看到,他們僅僅是把成員變量user或server的值加上需要移動的數量,user和server的值並不考慮FIFO的邊界問題,隨着數據的不停寫入和讀出,user和server的值不斷增加,只要處理得當,user總是出現在server的後面,因此frameAvalible()和frameReady()中的算法纔會一直成立。根據這種算法,user和server的值都可能大於FIFO的大小:framCount,那麼,如何確定真正的寫指針的位置呢?這裏需要用到userBase這一成員變量,在stepUser()中,每當user的值越過(userBase+frameCount),userBase就會增加frameCount,這樣,映射到FIFO中的偏移總是可以通過(user-userBase)獲得。因此,獲得當前FIFO的寫地址指針可以通過成員函數buffer()返回:

p = mClbk->buffer(mclbk->user);

在AudioTrack中,封裝了兩個函數:obtainBuffer()和releaseBuffer()操作FIFO,obtainBuffer()獲得當前可寫的數量和寫指針的位置,releaseBuffer()則在寫入數據後被調用,它其實就是簡單地調用stepUser()來調整偏移的位置。

IMemory接口

在createTrack的過程中,AudioFlinger會根據傳入的frameCount參數,申請一塊內存,AudioTrack可以通過IAudioTrack接口的getCblk()函數獲得指向該內存塊的IMemory接口,然後AudioTrack通過該IMemory接口的pointer()函數獲得指向該內存塊的指針,這塊內存的開始部分就是audio_track_cblk_t結構,緊接着是大小爲frameSize的FIFO內存。

IMemory->pointer() ---->|_______________________________________________________

                                     |__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|

看看AudioTrack的createTrack()的代碼就明白了:

1. sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), 

2.                                                       streamType, 

3.                                                       sampleRate, 

4.                                                       format, 

5.                                                       channelCount, 

6.                                                       frameCount, 

7.                                                       ((uint16_t)flags) << 16, 

8.                                                       sharedBuffer, 

9.                                                       output, 

10.                                                       &status); 

11. // 得到IMemory接口

12.     sp<IMemory> cblk = track->getCblk();                        

13.     mAudioTrack.clear(); 

14.     mAudioTrack = track; 

15.     mCblkMemory.clear(); 

16.     mCblkMemory = cblk; 

17. // 得到audio_track_cblk_t結構

18.     mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());  

19. // 該FIFO用於輸出

20.     mCblk->out = 1;                                             

21. // Update buffer size in case it has been limited by AudioFlinger during track creation

22.     mFrameCount = mCblk->frameCount; 

23. if (sharedBuffer == 0) { 

24. // 給FIFO的起始地址賦值

25.         mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); 

26.     } else

27.         ..........         

28.     } 

Android Audio System 之二:AudioFlinger

引言

    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類的內部結構和關係:

clip_image006

圖一   AudioFlinger的類結構

不知道各位是否和我一樣,第一次看到AudioFlinger類的定義的時候都很鬱悶--這個類實在是龐大和臃腫,可是當你理清他的關係以後,你會覺得相當合理。下面我們一一展開討論。

· IAudioFlinger接口

這是AudioFlinger向外提供服務的接口,例如openOutput,openInput,createTrack,openRecord等等,應用程序或者其他service通過ServiceManager可以獲得該接口。該接口通過繼承BnAudioFlinger得到。

· ThreadBase

在AudioFlinger中,Android爲每一個放音/錄音設備均創建一個處理線程,負責音頻數據的I/O和合成,ThreadBase是這些線程的基類,所有的播放和錄音線程都派生自ThreadBase

· TrackBase

應用程序每創建一個音軌(AudioTrack/AudioRecord),在AudioFlinger中都會創建一個對應的Track實例,TrackBase就是這些Track的基類,他的派生類有:

·

· PlaybackTread::Track    // 用於普通播放,對應於應用層的AudioTrack

· PlaybackThread::OutputTrack    // 用於多重設備輸出,當藍牙播放開啓時使用

· RecordThread::RecordTrack    // 用於錄音,對應於應用層的AudioRecord

· 播放

默認的播放線程是MixerThread,它由AudioPolicyManager創建,在AudioPolicyManager的構造函數中,有以下代碼:

1. mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, 

2.                                     &outputDesc->mSamplingRate, 

3.                                     &outputDesc->mFormat, 

4.                                     &outputDesc->mChannels, 

5.                                     &outputDesc->mLatency, 

6.                                     outputDesc->mFlags); 

最終會進入AudioFlinger的openOut函數:

1. ...... 

2. thread = new MixerThread(this, output, ++mNextThreadId); 

3. ...... 

4. mPlaybackThreads.add(mNextThreadId, thread); 

5. ...... 

6. 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函數:

1. track = thread->createTrack_l(client, streamType, sampleRate, format, 

2.                 channelCount, frameCount, sharedBuffer, &lStatus); 

再跟入createTrack_l函數中,可以看到創建了PlaybackThread::Track類,然後加入播放線程的track列表mTracks中。

1. track = thread->createTrack_l(client, streamType, sampleRate, format, 

2.                 channelCount, frameCount, sharedBuffer, &lStatus); 

3. ...... 

4. mTracks.add(track); 

在createTrack的最後,創建了TrackHandle類並返回,TrackHandle繼承了IAudioTrack接口,以後,createTrack的調用者可以通過IAudioTrack接口與AudioFlinger中對應的Track實例交互。

1. trackHandle = new TrackHandle(track); 

2. ...... 

3. return trackHandle; 

最後,在系統運行時,AudioFlinger中的線程和Track的結構大致如下圖所示:它會擁有多個工作線程,每個線程擁有多個Track。

clip_image008

圖二     AudioFlinger的線程結構

播放線程實際上是MixerThread的一個實例,MixerThread的threadLoop()中,會把該線程中的各個Track進行混合,必要時還要進行ReSample(重採樣)的動作,轉換爲統一的採樣率(44.1K),然後通過音頻系統的AudioHardware層輸出音頻數據。

· 錄音

錄音的流程和放音差不多,只不過數據流動的方向相反,錄音線程變成RecordThread,Track變成了RecordTrack,openRecord返回RecordHandle,詳細的暫且不表。

· DuplicatingThread

    AudioFlinger中有一個特殊的線程類:DuplicatingThread,從圖一可以知道,它是MixerThread的子類。當系統中有兩個設備要同時輸出時,DuplicatingThread將被創建,通過IAudioFlinger的openDuplicateOutput方法創建DuplicatingThread。

1. int AudioFlinger::openDuplicateOutput(int output1, int output2) 

2. { 

3.     Mutex::Autolock _l(mLock); 

4.     MixerThread *thread1 = checkMixerThread_l(output1); 

5.     MixerThread *thread2 = checkMixerThread_l(output2); 

6.     ...... 

7.     DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId); 

8.     thread->addOutputTrack(thread2); 

9.     mPlaybackThreads.add(mNextThreadId, thread); 

10. return mNextThreadId; 

11. } 

創建 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只會在藍牙耳機中輸出,而按鍵音和鈴聲等提示音會同時在本機和藍牙耳機中輸出。

clip_image010

圖三 本機播放時的Thread和Track

clip_image012

圖四 藍牙播放時的Thread和Track

Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager

引言

    AudioPolicyService是Android音頻系統的兩大服務之一,另一個服務是AudioFlinger,這兩大服務都在系統啓動時有MediaSever加載,加載的代碼位於:frameworks/base/media/mediaserver/main_mediaserver.cpp。AudioFlinger主要負責管理音頻數據處理以及和硬件抽象層相關的工作。本文主要介紹AudioPolicyService。

AudioPolicyService

    AudioPolicyService主要完成以下任務:

· JAVA應用層通過JNI,經由IAudioPolicyService接口,訪問AudioPolicyService提供的服務

· 輸入輸出設備的連接狀態

· 系統的音頻策略(strategy)的切換

· 音量/音頻參數的設置

    AudioPolicyService的構成

下面這張圖描述了AudioPolicyService的靜態結構:

clip_image014

進一步說明:

1. AudioPolicyService繼承了IAudioPolicyService接口,這樣AudioPolicyService就可以基於Android的Binder機制,向外部提供服務;

2. AudioPolicyService同時也繼承了AudioPolicyClientInterface類,他有一個AudioPolicyInterface類的成員指針mpPolicyManager,實際上就是指向了AudioPolicyManager;

3. AudioPolicyManager類繼承了AudioPolicyInterface類以便向AudioPolicyService提供服務,反過來同時還有一個AudioPolicyClientInterface指針,該指針在構造函數中被初始化,指向了AudioPolicyService,實際上,AudioPolicyService是通過成員指針mpPolicyManager訪問AudioPolicyManager,而AudioPolicyManager則通過AudioPolicyClientInterface(mpClientInterface)訪問AudioPolicyService;

4. AudioPolicyService有一個內部線程類AudioCommandThread,顧名思義,所有的命令(音量控制,輸入、輸出的切換等)最終都會在該線程中排隊執行;

AudioPolicyManager

    AudioPolicyService的很大一部分管理工作都是在AudioPolicyManager中完成的。包括音量管理,音頻策略(strategy)管理,輸入輸出設備管理。

輸入輸出設備管理

音頻系統爲音頻設備定義了一個枚舉:AudioSystem::audio_devices,例如:DEVICE_OUT_SPEAKER,DEVICE_OUT_WIRED_HEADPHONE,DEVICE_OUT_BLUETOOTH_A2DP,DEVICE_IN_BUILTIN_MIC,DEVICE_IN_VOICE_CALL等等,每一個枚舉值其實對應一個32bit整數的某一個位,所以這些值是可以進行位或操作的,例如我希望同時打開揚聲器和耳機,那麼可以這樣:

1. newDevice = DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADPHONE;

2. setOutputDevice(mHardwareOutput, newDevice); 

AudioPolicyManager中有兩個成員變量:mAvailableOutputDevices和mAvailableInputDevices,他們記錄了當前可用的輸入和輸出設備,當系統檢測到耳機或者藍牙已連接好時,會調用AudioPolicyManager的成員函數:

1. status_t AudioPolicyManager::setDeviceConnectionState(AudioSystem::audio_devices device, 

2.                                                   AudioSystem::device_connection_state state, 

3. const char *device_address) 

該函數根據傳入的device值和state(DEVICE_STATE_AVAILABLE/DEVICE_STATE_UNAVAILABLE)設置mAvailableOutputDevices或者mAvailableInputDevices,然後選擇相應的輸入或者輸出設備。

其他一些相關的函數:

· setForceUse()  設置某種場合強制使用某一設備,例如setForceUse(FOR_MEDIA, FORCE_SPEAKER)會在播放音樂時打開揚聲器

· startOutput()/stopOutput()

· startInput()/stopInput()

音量管理

AudioPolicyManager提供了一下幾個與音量相關的函數:

· initStreamVolume(AudioSystem::stream_type stream, int indexMin, int indexMax)

· setStreamVolumeIndex(AudioSystem::stream_type stream, int index)

· getStreamVolumeIndex(AudioSystem::stream_type stream)

AudioService.java中定義了每一種音頻流的最大音量級別:

1. /** @hide Maximum volume index values for audio streams */

2. private int[] MAX_STREAM_VOLUME = new int[] { 

3.         5,  // STREAM_VOICE_CALL

4.         7,  // STREAM_SYSTEM

5.         7,  // STREAM_RING

6.         15, // STREAM_MUSIC

7.         7,  // STREAM_ALARM

8.         7,  // STREAM_NOTIFICATION

9.         15, // STREAM_BLUETOOTH_SCO

10.         7,  // STREAM_SYSTEM_ENFORCED

11.         15, // STREAM_DTMF

12.         15  // STREAM_TTS

13.     }; 

由此可見,電話鈴聲可以有7個級別的音量,而音樂則可以有15個音量級別,java的代碼通過jni,最後調用AudioPolicyManager的initStreamVolume(),把這個數組的內容傳入AudioPolicyManager中,這樣AudioPolicyManager也就記住了每一個音頻流的音量級別。應用程序可以調用setStreamVolumeIndex設置各個音頻流的音量級別,setStreamVolumeIndex會把這個整數的音量級別轉化爲適合人耳的對數級別,然後通過AudioPolicyService的AudioCommandThread,最終會將設置應用到AudioFlinger的相應的Track中。

音頻策略管理

我想首先要搞清楚stream_type,device,strategy三者之間的關係:

· AudioSystem::stream_type  音頻流的類型,一共有10種類型

· AudioSystem::audio_devices  音頻輸入輸出設備,每一個bit代表一種設備,見前面的說明

· AudioPolicyManager::routing_strategy 音頻路由策略,可以有4種策略

clip_image016

getStrategy(stream_type)根據stream type,返回對應的routing strategy值,getDeviceForStrategy()則是根據routing strategy,返回可用的device。Android把10種stream type歸納爲4種路由策略,然後根據路由策略決定具體的輸出設備。

成員變量mOutputs

1. KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs;   // list of output descriptors

這是AudioPolocyManager用管理輸出的鍵值對向量(數組),通常AudioPolocyManager會打開3個輸出句柄(audio_io_handle_t),關於audio_io_handle_t,請參考另一編博客:http://blog.csdn.net/DroidPhone/archive/2010/10/14/5941344.aspx,它實際上就是AudioFlinger中某個PlaybackTread的ID。這3個句柄分別是:

· mHardwareOutput            // hardware output handler

· mA2dpOutput                   // A2DP output handler

· mDuplicatedOutput          // duplicated output handler: outputs to hardware and A2DP

可以通過startOutput()把某一個stream type放入到相應的輸出中。

popCount()

這個函數主要用來計算device變量中有多少個非0位(計算32位數種1的個數),例如該函數返回2,代表同時有兩個device要處理。之所以特別介紹它,是因爲這個函數的實現很有意思:

1. uint32_t AudioSystem::popCount(uint32_t u) 

2. { 

3.     u = ((u&0x55555555) + ((u>>1)&0x55555555)); 

4.     u = ((u&0x33333333) + ((u>>2)&0x33333333)); 

5.     u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); 

6.     u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); 

7.     u = ( u&0x0000ffff) + (u>>16); 

8. return u; 

9. } 

不知道各位看懂了麼?

AudioCommandThread

這是AudioPolicyService中的一個線程,主要用於處理音頻設置相關的命令。包括:

· START_TONE

· STOP_TONE

· SET_VOLUME

· SET_PARAMETERS

· SET_VOICE_VOLUME

每種命令的參數有相應的包裝:

· class ToneData

· class VolumeData

· class ParametersData

· class VoiceVolumeData

    START_TONE/STOP_TONE:播放電話系統中常用的特殊音調,例如:TONE_DTMF_0,TONE_SUP_BUSY等等。

    SET_VOLUME:最終會調用AudioFlinger進行音量設置

    SET_VOICE_VOLUME:最終會調用AudioFlinger進行電話音量設置

    SET_PARAMETERS:通過一個KeyValuePairs形式的字符串進行參數設置,KeyValuePairs的格式可以這樣:

·  "sampling_rate=44100"

· "channels=2"

· "sampling_rate=44100;channels=2"     // 組合形式

這些KeyValuePairs可以通過AudioPolicyService的成員函數setParameters()傳入。

發佈了35 篇原創文章 · 獲贊 13 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章