android audio buffer 分析

我們知道,播放audio的時候,音頻數據是從AT傳送到AF的,然後AF中的audiomixer來讀取PCM數據做mix
下面對這個流程做大體分析。
首先,分析一下AT和AF之間傳遞數據使用的內存是在哪裏分配的。
AT章節裏面,我們分析AudioTrack::createTrack_l函數時,有這麼一段代碼:
  1. sp<IAudioTrack> track = audioFlinger->createTrack(streamType, //調用AF接口來在AF裏面創建Track實例
  2. sampleRate,
  3. format == AUDIO_FORMAT_PCM_8_BIT ?
  4. AUDIO_FORMAT_PCM_16_BIT : format,
  5. mChannelMask,
  6. frameCount,
  7. &trackFlags,
  8. sharedBuffer,
  9. output,
  10. tid,
  11. &mSessionId,
  12. mName,
  13. mClientUid,
  14. &status);
  15. sp<IMemory> iMem = track->getCblk();
  16. audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
AT章節裏,我們交代過,   cblk實際上就是指向了audio buffer.
我們是不是可以這麼猜想:audioFlinger->createTrack中就完成了audio buffer的分配?
那我們看一下這個函數。
  1. sp<IAudioTrack> AudioFlinger::createTrack(...)
  2. {
  3. Mutex::Autolock _l(mLock);
  4. PlaybackThread *thread = checkPlaybackThread_l(output);//通過前面講過的獲得的output,來獲得playbackThread
  5. client = registerPid_l(pid);//根據AT所在的進程PID, 來爲每個AT所在進程創建一個client
  6. track = thread->createTrack_l(client, streamType, sampleRate, format,
  7. channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus); //在這個playbackThread裏面創建Track
  8. trackHandle = new TrackHandle(track); //提供給AT的handle,是IAudioTrack形式的
  9. }
這個函數裏面最重要的一步就是track = thread->createTrack_l, 爲每個AT在AF裏面找到對應的PlaybackThread, 然後在playbackThread裏面創建了Track.
繼續往裏跟蹤。
  1. AudioFlinger::PlaybackThread::createTrack_l
  2. {
  3. track = new Track(this, client, streamType, sampleRate, format,
  4. channelMask, frameCount, sharedBuffer, sessionId, uid, *flags); //實例化一個Track
  5. }
Track這個類,實際上繼承了TrackBase這個類。
在TrackBase的構造函數AudioFlinger::ThreadBase::TrackBase::TrackBase裏面,我們可以發現:
  1. size_t size = sizeof(audio_track_cblk_t);
  2. size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
  3. if (sharedBuffer == 0) {
  4. size += bufferSize;
  5. }
  6. mCblkMemory = client->heap()->allocate(size);
看到了嗎?這裏就是申請audio buffer的地方,並且申請的長度是 sizeof(audio_track_cblk_t)+bufferSize
這裏實際上就是在需要的size基礎上,多申請一塊內存,用來存放 audio_track_cblk_t 這個頭信息。
audio_track_cblk_t 是個啥呢?
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中可寫的空閒空間的大小,只是加鎖和不加鎖的區別。playback 的場景中,主要 用於AT來write
framesReady()用於獲取FIFO中可讀取的空間大小。playback場景中,主要用於AF來read

我們可以用下面這個圖來解釋這塊內存的結構:



也就是說, TrackBase裏面申請了一塊內存,內存的前部是一個audio_track_cblk_t信息,用於指導讀寫位置的同步工作。內存的後部纔是真正的緩衝區。

然後我們回到client->heap()->allocate(size)看一下具體是從哪裏分配的內存
這裏的client就是client = registerPid_l(pid)創建的。
其構造函數裏面:
  1. AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid)
  2. : RefBase(),
  3. mAudioFlinger(audioFlinger),
  4. mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")),
  5. mPid(pid),
  6. mTimedTrackCount(0)
  7. {}
可以看到,這裏申請了1M bytes的空間。
我們知道,AF中最多支持32個Track,
在AudioFlinger::PlaybackThread::createTrack_l函數裏,
可以看到,不管AT來自什麼進程空間,都會創建Track,
也就是說,這32個Track可以來自不同的進程空間的不同AT,也可以來自同一進程空間同一AT的,也可以來自同一進程空間的不同AT,
我們分析registerPid_l函數就會發現,每個進程空間都會只有一個client,一個client只會申請1M的空間,
也就是說,每個進程空間不管有多少AT ,都會共用這1M的空間。具體每個AT使用多少,由client->heap()->allocate(size)來實報實銷。
而不同的進程空間之間,則是各自獨立的1M空間,互不干擾。

繼續往下跟蹤:
  1. MemoryDealer::MemoryDealer(size_t size, const char* name)
  2. : mHeap(new MemoryHeapBase(size, 0, name)),
  3. mAllocator(new SimpleBestFitAllocator(size))
  4. {
  5. }
  6. MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
  7. {
  8. int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
  9. }
看到了嗎?這1M的空間是使用了android的ashmem匿名共享內存機制來分配的。
這也是爲什麼AT和AF處於兩個不同的進程空間,但是卻能夠共享傳遞數據了。

現在我們知道了AT和AF之間是用的共享內存了,那麼具體運行起來的時候,是什麼樣子的呢?

我們從AT的write函數來分析
  1. ssize_t AudioTrack::write(const void* buffer, size_t userSize)
  2. {
  3. while (userSize >= mFrameSize) {
  4. audioBuffer.frameCount = userSize / mFrameSize;
  5. status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); //獲取可用buffer
  6. memcpy(audioBuffer.i8, buffer, toWrite); //寫入AF
  7. releaseBuffer(&audioBuffer); //釋放
  8. }
  9. return written;
  10. }
對其簡化後,可以知道AT通過obtainBuffer來獲取可用空間。然後進行memcpy。最後釋放這段空間。
這樣音頻數據就寫入了AF中的共享內存了。

下面分析一下 obtainBuffer
  1. status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
  2. {
  3. uint32_t framesReq = audioBuffer->frameCount;
  4. size_t framesAvail = mProxy->framesAvailable();
  5. if (framesAvail == 0) {
  6. while (framesAvail == 0) {
  7. if (cblk->user < cblk->loopEnd) {
  8. cblk->lock.unlock();
  9. result = mAudioTrack->start();
  10. cblk->lock.lock();
  11. }
  12. }
  13. }
  14. if (framesReq > framesAvail) {
  15. framesReq = framesAvail;
  16. }
  17. uint32_t u = cblk->user;
  18. uint32_t bufferEnd = cblk->userBase + mFrameCount;
  19. if (framesReq > bufferEnd - u) {
  20. framesReq = bufferEnd - u;
  21. }
  22. audioBuffer->frameCount = framesReq;
  23. audioBuffer->size = framesReq * mFrameSizeAF;
  24. audioBuffer->raw = mProxy->buffer(u);
  25. active = mActive;
  26. return active ? status_t(NO_ERROR) : status_t(STOPPED);
  27. }
可以看到,這個函數最重要的工作就是確定了framesReq這個變量的值 ,以及通過buffer函數確定了真正的buffer的起始地址

首先,通過調用framesAvailable函數,獲得了整個buffer裏面,可以用來寫入的空閒空間大小。
  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; // 實際上就是 frameCount - (u-s)
  8. } else {
  9. return frameCount + u - s;
  10. }
  11. }
可以知道,u-s 實際上就是buffer中,還沒有被讀取完的數據,frameCount-(u-s)實際上就是兩側可以使用的空間
這樣,obtainBuffer中,framesAvailable_l之後,framesReq就初步預存了可用空間的值,然後又比較了framesReq和bufferEnd-u的值。
那麼,bufferEnd-u又是什麼呢?
在代碼中,我們可以看到,bufferEnd=userBase+mFrameCount
那麼我們的疑問就成了userBase+mFrameCount-u了
mFrameCount沒有什麼神祕的,就是整個buffer的大小而已。
那麼整個公式具體代表什麼意思呢?

前面我們介紹了一下audio_track_cblk_t中的幾個數據變量的含義,
重點就是    user,userBase,server,serverBase 這四個變量。
需要注意的是,這四個變量的值並不是讀寫位置相對於buffer的起點,物理上的offset值,而是一個經過映射的虛擬值。
隨着讀寫的進行,user,userBase,server,serverBase這四個值在不斷的累加,
超過了buffer的實際的size之後,四個值會在一個虛擬的空間裏繼續累加,從而形成了一個環的組織形式。
其中,userBase,serverBase的累加單元就是buffer的實際的size frameCount.
user,userBase的累加單元,就是實際的讀寫的數量。


這個映射的關係,可以從下面圖中表示出來

假設某一時刻的映射關係如下,我們有一段數據準備寫入。



看完這個圖,是不是明白了?

總結一下,實際上,這段buffer是按照環的形式來讀寫的,隨着讀寫的繼續,user和server都是在不斷向後移動。
由於數據寫入的時候,採用了memcpy, 而memcpy只能向一個方向單向拷貝,不可能扭回頭去再繼續拷貝,所以這裏每次memcpy的操作,只能是針對user 指向的位置向後的那段空間。
而後面空間的終點,就是這段內存物理上的重點。映射過來,就是bufferEnd了。
那麼這段空間的可用大小,就是bufferEnd-user了。也就是上圖中橙色區域。
所以前面講的,比較framesReq和bufferEnd-u的值,實際上就是爲了獲取到memcpy可以執行的size的大小。

現在obtainBuffer中,我們知道了memcpy可以執行的size大小,那麼可以執行的目的地址怎麼確定呢?
這就是mProxy->buffer(u)起到的作用了。
  1. void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const
  2. {
  3. return (int8_t *)buffers + (offset - userBase) * frameSize;
  4. }
代碼中,buffers指向的是實際data buffer的指針,(offset-userBase)實際上就是(user-userBase),也就是user在實際data buffer中相對data buffer頭部的偏移量,當然,這個偏移量是以幀爲單位的。
再乘上framesize,就得出了byte偏移量了,這樣,buffer函數就可以得到user在實際data buffer中的位置了。
memcpy的目的地址也就知道了。
這樣,obtainBuffer函數執行完畢後,就可以知道要寫入的buffer的地址和可寫入數據的大小了。

前面我們提到,user,userBase,server,serverBase這四個值在不斷的累加,那麼具體的累加操作是在哪裏執行的呢?
這就是stepUser和stepServer函數的作用了。(這兩個函數在releaseBuffer函數裏調用,也就是說內存複製完畢之後,就會被調用了)
  1. uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut)
  2. {
  3. uint32_t u = user;
  4. u += stepCount;
  5. if (isOut) {
  6. if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
  7. bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
  8. }
  9. } else if (u > server) {
  10. u = server;
  11. }
  12. if (u >= frameCount) {
  13. if (u - frameCount >= userBase ) {
  14. userBase += frameCount;
  15. }
  16. } else if (u >= userBase + frameCount) {
  17. userBase += frameCount;
  18. }
  19. user = u;
  20. return u;
  21. }
  1. bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut)
  2. {
  3. uint32_t s = server;
  4. bool flushed = (s == user);
  5. s += stepCount;
  6. if (isOut) {
  7. if (flushed) {
  8. s = user;
  9. }
  10. }
  11. if (s >= loopEnd) {
  12. s = loopStart;
  13. if (--loopCount == 0) {
  14. loopEnd = UINT_MAX;
  15. loopStart = UINT_MAX;
  16. }
  17. }
  18. if (s >= frameCount) {
  19. if (s - frameCount >= serverBase ) {
  20. serverBase += frameCount;
  21. }
  22. } else if (s >= serverBase + frameCount) {
  23. serverBase += frameCount;
  24. }
  25. server = s;
  26. return true;
  27. }
從這兩個函數中可以看到,對user,server累加了實際的讀寫數值,對userBase,serverBase累加了buffer的實際長度frameCount.
在stepUser的函數執行完畢後,整個buffer的映射關係如下:


這樣,在下一次obtainBuffer操作之後,經過framesAvaliable函數計算出來的可用空間就是圖中綠色區域。
發現沒有?可用空間從buffer尾的橙色區域,轉移到了buffer頭的綠色區域,是不是實現了一個環的操作?
綠色和橙色之間的區域,就是AF尚未讀取的數據區。

前面分析了AT的寫操作。下面分析一下AF的讀操作。
首先要解決的問題是,AF什麼時候去讀呢?
前面的AF章節中,我們介紹過playbackThread,沒錯,就是在AudioFlinger::PlaybackThread::threadLoop裏面,循環的去讀取的!
另外,我們重溫一下obtainBuffer函數
  1. status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
  2. {
  3. if (framesAvail == 0) {
  4. while (framesAvail == 0) {
  5. if (cblk->user < cblk->loopEnd) {
  6. cblk->lock.unlock();
  7. result = mAudioTrack->start();
  8. cblk->lock.lock();
  9. }
  10. }
  11. }
  12. ........
  13. }
看見了嗎?當發現framesAvalil爲0,即沒有空間可以寫的時候,說明AF那端停了,或者壓根沒啓動,所以這裏會循環等待,並且會觸發mAudioTrack->start函數
經過binder調用,最後會調用到AF中的 AudioFlinger::PlaybackThread::Track::start, 這裏實際上是把track添加到了playbackthread中,並且激活了track的狀態。
然後AF就可以開始讀了。
在obtainBuffer函數中,你會發現有很多lock,unlock的操作。這裏主要是爲了保護audio_track_cblk_t這個臨界區中的變量。因爲audio_track_cblk_t位於共享內存,可以被AF和AT兩個進程訪問,所以一定要加鎖同步。
AF章節中,我們講過,AudioFlinger::PlaybackThread::threadLoop進行audioMixer操作,
在audioMixer中,我們以process__genericNoResampling爲例分析
  1. void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
  2. {
  3. uint32_t enabledTracks = state->enabledTracks;
  4. uint32_t e0 = enabledTracks;
  5. while (e0) {
  6. const int i = 31 - __builtin_clz(e0);
  7. e0 &= ~(1<<i);
  8. track_t& t = state->tracks[i];
  9. t.buffer.frameCount = state->frameCount;
  10. t.bufferProvider->getNextBuffer(&t.buffer, pts); //獲取databuffer
  11. t.frameCount = t.buffer.frameCount;
  12. t.in = t.buffer.raw;
  13. if (t.in == NULL)
  14. enabledTracks &= ~(1<<i);
  15. }
  16. ......
  17. e0 = enabledTracks;
  18. while (e0) {
  19. const int i = 31 - __builtin_clz(e0);
  20. e0 &= ~(1<<i);
  21. track_t& t = state->tracks[i];
  22. t.bufferProvider->releaseBuffer(&t.buffer); //釋放buffer,當中調用了stepServer函數
  23. }
可以看到實際上是getNextBuffer這個函數在從buffer中取數據
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
  1. AudioBufferProvider::Buffer* buffer, int64_t pts)
  2. {
  3. audio_track_cblk_t* cblk = this->cblk();
  4. uint32_t framesReady;
  5. uint32_t framesReq = buffer->frameCount;
  6. framesReady = mServerProxy->framesReady();
  7. if (CC_LIKELY(framesReady)) {
  8. uint32_t s = cblk->server;
  9. uint32_t bufferEnd = cblk->serverBase + mFrameCount;
  10. bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
  11. if (framesReq > framesReady) {
  12. framesReq = framesReady;
  13. }
  14. if (framesReq > bufferEnd - s) {
  15. framesReq = bufferEnd - s;
  16. }
  17. buffer->raw = getBuffer(s, framesReq);
  18. buffer->frameCount = framesReq;
  19. return NO_ERROR;
  20. }
  21. getNextBuffer_exit:
  22. buffer->raw = NULL;
  23. buffer->frameCount = 0;
  24. return NOT_ENOUGH_DATA;
  25. }
這裏實際上是和obtainBuffer類似的操作。只不過從寫buffer,變成了讀buffer,從framesAvailable變成了framesReady , 這裏就不多講了。
在buffer使用完畢之後,t.bufferProvider->releaseBuffer(&t.buffer)被調用,當中調用了stepServer函數,用來更新server,serverBase.

至此,AT與AF之間如何傳遞數據,以及數據是怎麼共享的,兩端如何同步的,就分析完了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章