前面講解了AudioFlinger的工作方式,但AT和AF以及那個神秘的CB對象的工作原理,一直都還沒能講解。對于Audio系統來說,如果最終也解決不了這個,真會有當年岳飛在朱仙鎮被十二道金牌召回時一樣的悲憤心情。幸好我們沒遇到秦檜,那就奮力窮追猛打,去解決這個CB對象吧。
解決問題要有好的對策。還是從AT和AF兩端關于CB對象的調用流程開始分析,這一招可是屢試不爽啊!
1. AT端的流程
AT端作為數據的生產者,可稱它為寫者,它在CB對象中用user表示。它的調用流程是:
- 調用framesAvailable,看看是否有空余的可寫空間。
- 調用buffer,獲得寫空間起始地址。
- 調用stepUser,更新user的位置。
一起來分析一下,由于這幾個函數都相當簡單,力爭一氣呵成。
先調用framesAvailable,看看當前剩余多少可寫空間。假設是第一次進來,讀者還在那等待數據,這樣就不用考慮競爭等問題了,代碼如下所示:
**AudioTrack.cpp::audio_track_cblk_t的framesAvailable()及相關**
~~~
uint32_t audio_track_cblk_t::framesAvailable()
{
Mutex::Autolock _l(lock);
returnframesAvailable_l();//調用framesAvailable_l
}
int32_t audio_track_cblk_t::framesAvailable_l()
{
uint32_t u = this->user; //當前寫者位置,此時也為0
uint32_t s = this->server; //當前讀者位置,此時為0
if(out) { //對于音頻輸出,out為1
uint32_t limit = (s < loopStart) ? s : loopStart;
//由于不設置播放端點,所以loopStart是初始值INT_MAX, limit=0
return limit + frameCount - u;
//返回0+frameCount-0,也就是數據緩沖的全部大小。假設frameCount=1024幀
}
}
~~~
然后,調用buffer獲得起始位置,buffer返回一個地址。
**AudioTrack.cpp**
~~~
void* audio_track_cblk_t::buffer(uint32_toffset) const
{
//buffers是數據緩沖的起始位置,offset是計算出來的基于userBase的偏移。
//通過這種方式巧妙地把數據緩沖當做環形緩沖來處理
return(int8_t *)this->buffers + (offset - userBase) * this->frameSize;
}
~~~
當把數據寫到緩沖后,調用stepUser。
**AudioTrack.cpp**
~~~
uint32_t audio_track_cblk_t::stepUser(uint32_tframeCount)
{
/*
framecount,表示寫了多少幀,前面分配了1024幀,但寫的數據可以比這個少
假設這一次寫了512幀
*/
uint32_t u = this->user;//user位置還沒更新,此時u=0;
u +=frameCount;//u更新了,u=512
......
/*
userBase還是初始值0。可惜只寫了1024的一半,所以userBase加不了。
但這句話很重要,還記得前面的buffer調用嗎?取數據地址的時候用offset-userBase,
一旦user位置到達緩沖的尾部,則userBase也會更新,這樣offset-userBase的位置就會
回到緩沖的頭部,從頭到尾這么反復循環,不就是一個環形緩沖了嗎?非常巧妙!
*/
if (u>= userBase + this->frameCount) {
userBase += this->frameCount;
}
this->user = u;//喔,user位置也更新為512了,但是useBase還是0
returnu;
}
~~~
假設寫者這時因某種原因停止了寫數據,而讀者卻會被喚醒。
2 AF端的流程
AF端作為數據的消費者,它在CB中的表示是server,可稱它為讀者。讀者的使用流程是:
- 調用framesReady看是否有可讀數據。
- 獲得可讀數據的起始位置,這個和上面的buffer調用基本一樣,都是根據offset和serverBase來獲得可讀數據塊的首地址。
- 調用stepServer更新讀位置。
現在來分析framesReady和stepServer這兩個函數,framesReady的代碼如下所示:
**AudioTrack.cpp**
~~~
uint32_t audio_track_cblk_t::framesReady()
{
uint32_t u = this->user; //u為512
uint32_ts = this->server;//還沒讀呢,s為零
if(out) {
if(u < loopEnd) {
return u - s;//loopEnd也是INT_MAX,所以這里返回512,表示有512幀可讀了
}else {
Mutex::Autolock _l(lock);
if (loopCount >= 0) {
return (loopEnd - loopStart)*loopCount + u - s;
} else {
return UINT_MAX;
}
}
} else{
return s - u;
}
}
~~~
可讀數據地址的計算方法和前面的buffer調用一樣,都是通過server和serverBase來計算的。接著看stepServer,代碼如下所示:
**AudioTrack.cpp**
~~~
bool audio_track_cblk_t::stepServer(uint32_tframeCount)
{
status_t err;
err = lock.tryLock();
uint32_t s = this->server;
s +=frameCount; //讀了512幀了,所以s=512
......
//沒有設置循環播放,所以不走這個
if (s>= loopEnd) {
s =loopStart;
if (--loopCount == 0) {
loopEnd = UINT_MAX;
loopStart = UINT_MAX;
}
}
//和userBase一樣的處理
if (s>= serverBase + this->frameCount) {
serverBase += this->frameCount;
}
this->server = s; //server為512了
cv.signal(); //讀者讀完了,觸發一個同步信號,因為讀者可能在等待可寫的數據緩沖
lock.unlock();
returntrue;
}
~~~
3. 真的是環形緩沖?
滿足下面場景的緩沖可稱為環形緩沖(假設數據緩沖最大為1024幀):
- 寫者先寫1024幀,此后便無剩余空間可寫。
- 讀者讀了前面的512幀,那么這512幀的數據空間就空余出來了。
- 所以,寫者就可以重新利用這空余512幀的空間了。
關鍵是第三步,寫者是否跟蹤了讀者的位置,并充分利用了讀者已使用過的數據空間。所以得回頭看看寫者AT是否把這512幀利用了。
先看寫者寫完1024幀后的情況,stepUser中會有下面幾句話:
~~~
if (u >= userBase + this->frameCount) {
//u為1024,userBase為0,frameCount為1024
userBase += this->frameCount;//好,userBase也為1024了
}
~~~
此時userBase更新為1024幀。再看寫者獲取可寫空間的framesAvailable_l函數,按照以前的假設,應該返回512幀可寫空間,代碼如下所示:
**AudioTrack.cpp**
~~~
uint32_t audio_track_cblk_t::framesAvailable_l()
{
uint32_t u = this->user; //1024,寫者上一次寫完了整個1024幀空間
uint32_t s = this->server;//512,讀者當前讀到的位置
if(out) {
uint32_t limit = (s < loopStart) ? s : loopStart;
return limit + frameCount - u;//返回512
}
}
~~~
framesAvailable返回了512幀,但可寫空間的地址是否是從頭開始的呢?要是從其他地方開始的,情況就慘了。來看buffer中最后返回的可寫空間地址:
~~~
return (int8_t *)this->buffers + (offset -userBase) * this->frameSize;
//offset是外界傳入的基于userBase的一個偏移量,它的值是userBase+512,所以
//offset-userBase將得到從頭開始的那段數據空間。真的是一個環形緩沖。
~~~
從上面的分析中看出,CB對象通過userBase和user等幾個變量,將一段有限長度的線性緩沖變成了一段無限長的緩沖,這不正是環形緩沖的精髓嗎!
- 前言
- 第1章 閱讀前的準備工作
- 1.1 系統架構
- 1.1.1 Android系統架構
- 1.1.2 本書的架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.3 工具介紹
- 1.3.1 Source Insight介紹
- 1.3.2 Busybox的使用
- 1.4 本章小結
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 學習JNI的實例:MediaScanner
- 2.3 Java層的MediaScanner分析
- 2.3.1 加載JNI庫
- 2.3.2 Java的native函數和總結
- 2.4 JNI層MediaScanner的分析
- 2.4.1 注冊JNI函數
- 2.4.2 數據類型轉換
- 2.4.3 JNIEnv介紹
- 2.4.4 通過JNIEnv操作jobject
- 2.4.5 jstring介紹
- 2.4.6 JNI類型簽名介紹
- 2.4.7 垃圾回收
- 2.4.8 JNI中的異常處理
- 2.5 本章小結
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 屬性服務
- 3.3 本章小結
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 關于zygote的總結
- 4.3 SystemServer分析
- 4.3.1 SystemServer的誕生
- 4.3.2 SystemServer的重要使命
- 4.3.3 關于 SystemServer的總結
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService發送請求
- 4.4.2 有求必應之響應請求
- 4.4.3 關于zygote分裂的總結
- 4.5 拓展思考
- 4.5.1 虛擬機heapsize的限制
- 4.5.2 開機速度優化
- 4.5.3 Watchdog分析
- 4.6 本章小結
- 第5章 深入理解常見類
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初識影子對象
- 5.2.2 第二板斧--由弱生強
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 輕量級的引用計數控制類LightRefBase
- 5.2.5 題外話-三板斧的來歷
- 5.3 Thread類及常用同步類分析
- 5.3.1 一個變量引發的思考
- 5.3.2 常用同步類
- 5.4 Looper和Handler類分析
- 5.4.1 Looper類分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步關系
- 5.4.4 HandlerThread介紹
- 5.5 本章小結
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函數
- 6.2.2 獨一無二的ProcessState
- 6.2.3 時空穿越魔術-defaultServiceManager
- 6.2.4 注冊MediaPlayerService
- 6.2.5 秋風掃落葉-StartThread Pool和join Thread Pool分析
- 6.2.6 你徹底明白了嗎
- 6.3 服務總管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服務的注冊
- 6.3.3 ServiceManager存在的意義
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查詢ServiceManager
- 6.4.2 子承父業
- 6.5 拓展思考
- 6.5.1 Binder和線程的關系
- 6.5.2 有人情味的訃告
- 6.5.3 匿名Service
- 6.6 學以致用
- 6.6.1 純Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小結
- 第7章 深入理解Audio系統
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介紹
- 7.2.2 AudioTrack(Java空間)分析
- 7.2.3 AudioTrack(Native空間)分析
- 7.2.4 關于AudioTrack的總結
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的誕生
- 7.3.2 通過流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 關于AudioFlinger的總結
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的創建
- 7.4.2 重回AudioTrack
- 7.4.3 聲音路由切換實例分析
- 7.4.4 關于AudioPolicy的總結
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 題外話
- 7.6 本章小結
- 第8章 深入理解Surface系統
- 8.1 概述
- 8.2 一個Activity的顯示
- 8.2.1 Activity的創建
- 8.2.2 Activity的UI繪制
- 8.2.3 關于Activity的總結
- 8.3 初識Surface
- 8.3.1 和Surface有關的流程總結
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI層分析
- 8.3.4 Surface和畫圖
- 8.3.5 初識Surface小結
- 8.4 深入分析Surface
- 8.4.1 與Surface相關的基礎知識介紹
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface對象的創建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介紹
- 8.4.7 深入分析Surface的總結
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的誕生
- 8.5.2 SF工作線程分析
- 8.5.3 Transaction分析
- 8.5.4 關于SurfaceFlinger的總結
- 8.6 拓展思考
- 8.6.1 Surface系統的CB對象分析
- 8.6.2 ViewRoot的你問我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小結
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理與機制分析
- 9.2.1 Netlink和Uevent介紹
- 9.2.2 初識Vold
- 9.2.3 NetlinkManager模塊分析
- 9.2.4 VolumeManager模塊分析
- 9.2.5 CommandListener模塊分析
- 9.2.6 Vold實例分析
- 9.2.7 關于Vold的總結
- 9.3 Rild的原理與機制分析
- 9.3.1 初識Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 關于Rild main函數的總結
- 9.3.6 Rild實例分析
- 9.3.7 關于Rild的總結
- 9.4 拓展思考
- 9.4.1 嵌入式系統的存儲知識介紹
- 9.4.2 Rild和Phone的改進探討
- 9.5 本章小結
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模塊分析
- 10.2.2 MSS模塊分析
- 10.2.3 android.process.media媒體掃描工作的流程總結
- 10.3 MediaScanner分析
- 10.3.1 Java層分析
- 10.3.2 JNI層分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 關于MediaScanner的總結
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介紹
- 10.4.2 我問你答
- 10.5 本章小結