鍍金池/ 教程/ Android/ 第7章 ?深入理解Audio系統(tǒng)
第4章 ?深入理解 Zygote
第10章 深入理解MediaScanner
第3章 ?深入理解init
第8章 ?深入理解Surface系統(tǒng)
第5章 深入理解常見類
第7章 ?深入理解Audio系統(tǒng)
第一章 ?閱讀前的準(zhǔn)備工作
<span>第6章 深入理解Binder</span>
第9章 ?深入理解Vold和Rild
第2章? 深入理解JNI

第7章 ?深入理解Audio系統(tǒng)

本章主要內(nèi)容

·? 詳細(xì)分析AudioTrack。

·? 詳細(xì)分析AudioFlinger。

·? 詳細(xì)分析AudioPolicyService。

本章涉及的源代碼文件名及位置

下面是本章分析的源碼文件名及其位置。

·? AudioTrack.java

framework/base/media/java/com/android/media/AudioTrack.java

·? android_media_track.cpp

framework/base/core/jni/android_media_track.cpp

·? MemoryHeapBase

framework/base/libs/binder/MemoryHeapBase.cpp

·? MemoryBase.h

framework/base/include/binder/MemoryBase.h

·? AudioTrack.cpp

framework/base/libmedia/AudioTrack.cpp

·? audio_track_cblk_t聲明

framework/base/include/private/media/AudioTrackShared.h

·? audio_track_cblk_t定義

framework/base/media/libmedia/AudioTrack.cpp

·? Main_MediaServer.cpp

framework/base/media/mediaserver/Main_MediaServer.cpp

·? AudioFlinger.cpp

framework/base/libs/audioFlinger/AudioFlinger.cpp

·? AudioHardwareInterface.h

hardware/libhardware_legacy/include/hardware_legacy/AudioHardwareInterface.h

·? AudioMixer.cpp

framework/base/libs/audioflinger/AudioMixer.cpp

·? AudioSystem.h

framework/base/include/media/AudioSystem.h

·? AudioSystem.cpp

framework/base/media/libmedia/AudioSystem.cpp

·? AudioPolicyInterface.h

hardware/libhardware_legacy/include/hardware_legacy

·? AudioPolicyManagerBase.cpp

framework/base/libs/audioflinger/AudioPolicyManagerBase.cpp

·? AudioService.java

framework/base/media/java/com/android/media/AudioService.java

·? Android_media_AudioSystem.cpp

framework/base/core/Jni/Android_media_AudioSystem.cpp

7.1 ?綜述

Audio系統(tǒng)是Android平臺(tái)的重要組成部分,它主要包括三方面的內(nèi)容:

·? AudioRcorder和AudioTrack:這兩個(gè)類屬于Audio系統(tǒng)對(duì)外提供的API類,通過它們可以完成Android平臺(tái)上音頻數(shù)據(jù)的采集和輸出任務(wù)。

·? AudioFlinger:它是Audio系統(tǒng)的工作引擎,管理著系統(tǒng)中的輸入輸出音頻流,并承擔(dān)音頻數(shù)據(jù)的混音,以及讀寫Audio硬件以實(shí)現(xiàn)數(shù)據(jù)的輸入輸出等工作。

·? AudioPolicyService,它是Audio系統(tǒng)的策略控制中心,具有掌管系統(tǒng)中聲音設(shè)備的選擇和切換、音量控制等功能。

Android的Audio系統(tǒng)是我們分析的第一個(gè)具有相當(dāng)難度的復(fù)雜系統(tǒng)。對(duì)于這種系統(tǒng),我采取的學(xué)習(xí)方法是,以一個(gè)常見用例為核心,沿著重要函數(shù)調(diào)用的步驟逐步進(jìn)行深入分析。中途若出現(xiàn)所需但并不熟悉的知識(shí),則以此為契機(jī),及時(shí)學(xué)習(xí)、思考、研究,當(dāng)不熟悉的知識(shí)逐漸被自己了解掌握時(shí),該系統(tǒng)的真面目也隨之清晰了。

下面是破解Audio系統(tǒng)的戰(zhàn)略步驟:

·? 首先,從API類的AudioTrack開始,從Java層到Native層一步步了解其工作原理。其中AudioTrack和AudioFlinger有較多的交互工作,但在這一步中,我們暫時(shí)只集中關(guān)注AudioTrack的流程。

·? 提煉上一步中AudioTrack和AudioFlinger的交互流程,以這個(gè)交互流程作為分析AudioFlinger的突破口。

·? 在前面兩個(gè)步驟中還會(huì)有一些剩余的“抵抗分子”,我們將在AudioPolicyService的破解過程中把它們徹底消滅掉。另外,在分析AudioPolicyService時(shí),還會(huì)通過一個(gè)耳機(jī)插入事件的處理實(shí)例來幫助分析AudioPolicyService的工作流程。

·? 最后,在本章的拓展部分,我們會(huì)介紹一下AudioFlinger中DuplicatingThread的工作原理。

說明:在下文中AudioTrack被簡(jiǎn)寫為AT,AudioFlinger被簡(jiǎn)寫為AF,AudioPolicyService被簡(jiǎn)寫為AP。

讓我們整裝上陣,開始代碼的征程吧!

?

7.2 ?AudioTrack的破解

AudioTrack屬于Audio系統(tǒng)對(duì)外提供的API類,所以它在Java層和Native層均有對(duì)應(yīng)類,先從Java層的用例開始。

7.2.1 用例介紹

這個(gè)用例很簡(jiǎn)單,但其中會(huì)有一些重要概念,應(yīng)注意理解。

注意:要了解AudioTrack Java API的具體信息,需要仔細(xì)閱讀Android API中的相關(guān)文檔。閱讀API文檔,是一個(gè)能快速掌握相關(guān)知識(shí)的好方法。

[-->AudioTrackAPI使用例子(Java層)]

//① 根據(jù)音頻數(shù)據(jù)的特性來確定所要分配的緩沖區(qū)的最小size

int bufsize =

???? ?????AudioTrack.getMinBufferSize(8000,//采樣率:每秒8K個(gè)點(diǎn)?????????????????????

   ?????AudioFormat.CHANNEL_CONFIGURATION_STEREO,//聲道數(shù):雙聲道???????????

??????? AudioFormat.ENCODING_PCM_16BIT//采樣精度:一個(gè)采樣點(diǎn)16比特,相當(dāng)于2個(gè)字節(jié)

);

?

//② 創(chuàng)建AudioTrack

AudioTrack trackplayer = new AudioTrack(

???????????????? AudioManager.STREAM_MUSIC,//音頻流類型

???????????????? 8000,AudioFormat.CHANNEL_CONFIGURATION_ STEREO,

  ??????????????? ?AudioFormat.ENCODING_PCM_16BIT, bufsize,

???????????????? AudioTrack.MODE_STREAM//數(shù)據(jù)加載模式);

??//③ 開始播放

trackplayer.play() ;

?

......

//④ 調(diào)用write寫數(shù)據(jù)

trackplayer.write(bytes_pkg, 0,bytes_pkg.length) ;//往track中寫數(shù)據(jù)

......

?//⑤ 停止播放和釋放資源

trackplayer.stop();//停止播放

trackplayer.release();//釋放底層資源

上面的用例引入了兩個(gè)新的概念,一個(gè)是數(shù)據(jù)加載模式,另一個(gè)是音頻流類型。下面進(jìn)行詳細(xì)介紹。

1. AudioTrack的數(shù)據(jù)加載模式

AudioTrack有兩種數(shù)據(jù)加載模式:MODE_STREAM和MODE_STATIC,它們對(duì)應(yīng)著兩種完全不同的使用場(chǎng)景。

·? MODE_STREAM:在這種模式下,通過write一次次把音頻數(shù)據(jù)寫到AudioTrack中。這和平時(shí)通過write系統(tǒng)調(diào)用往文件中寫數(shù)據(jù)類似,但這種工作方式每次都需要把數(shù)據(jù)從用戶提供的Buffer中拷貝到AudioTrack內(nèi)部的Buffer中,這在一定程度上會(huì)使引入延時(shí)。為解決這一問題,AudioTrack就引入了第二種模式。

·? MODE_STATIC:這種模式下,在play之前只需要把所有數(shù)據(jù)通過一次write調(diào)用傳遞到AudioTrack中的內(nèi)部緩沖區(qū),后續(xù)就不必再傳遞數(shù)據(jù)了。這種模式適用于像鈴聲這種內(nèi)存占用量較小,延時(shí)要求較高的文件。但它也有一個(gè)缺點(diǎn),就是一次write的數(shù)據(jù)不能太多,否則系統(tǒng)無法分配足夠的內(nèi)存來存儲(chǔ)全部數(shù)據(jù)。

這兩種模式中以MODE_STREAM模式相對(duì)常見和復(fù)雜,我們的分析將以它為主。

注意:如果采用STATIC模式,須先調(diào)用write寫數(shù)據(jù),然后再調(diào)用play。

2. 音頻流的類型

在AudioTrack構(gòu)造函數(shù)中,會(huì)接觸到AudioManager.STREAM_MUSIC這個(gè)參數(shù)。它的含義與Android系統(tǒng)對(duì)音頻流的管理和分類有關(guān)。

Android將系統(tǒng)的聲音分為好幾種流類型,下面是幾個(gè)常見的:

·? STREAM_ALARM:警告聲

·? STREAM_MUSIC:音樂聲,例如music等

·? STREAM_RING:鈴聲

·? STREAM_SYSTEM:系統(tǒng)聲音,例如低電提示音,鎖屏音等

·? STREAM_VOCIE_CALL:通話聲

注意:上面這些類型的劃分和音頻數(shù)據(jù)本身并沒有關(guān)系。例如MUSIC和RING類型都可以是某首MP3歌曲。另外,聲音流類型的選擇沒有固定的標(biāo)準(zhǔn),例如,鈴聲預(yù)覽中的鈴聲可以設(shè)置為MUSIC類型。

音頻流類型的劃分和Audio系統(tǒng)對(duì)音頻的管理策略有關(guān)。其具體作用,在以后的分析中再做詳細(xì)介紹。在目前的用例中,把它當(dāng)做一個(gè)普通數(shù)值即可。

3. Buffer分配和Frame的概念

在用例中碰到的第一個(gè)重要函數(shù)就是getMinBufferSize。這個(gè)函數(shù)對(duì)于確定應(yīng)用層分配多大的數(shù)據(jù)Buffer具有重要指導(dǎo)意義。先回顧一下它的調(diào)用方式:

[-->AudioTrackAPI使用例子(Java層)]

//注意這些參數(shù)的值。想象我們正在一步步的Trace,這些參數(shù)都會(huì)派上用場(chǎng)

AudioTrack.getMinBufferSize(8000,//每秒8K個(gè)點(diǎn)??????????????????????????????

  ? ????AudioFormat.CHANNEL_CONFIGURATION_STEREO,//雙聲道??????????????????

??????? AudioFormat.ENCODING_PCM_16BIT);

?

來看這個(gè)函數(shù)的實(shí)現(xiàn):

[-->AudioTrack.java]

static public int getMinBufferSize(intsampleRateInHz, int channelConfig,??

????????????????????????????????? intaudioFormat) {

???????int channelCount = 0;

???????switch(channelConfig) {

???????case AudioFormat.CHANNEL_OUT_MONO:

??????? caseAudioFormat.CHANNEL_CONFIGURATION_MONO:

???????????channelCount = 1;

???????????break;

???????case AudioFormat.CHANNEL_OUT_STEREO:

???????case AudioFormat.CHANNEL_CONFIGURATION_STEREO:

???????????channelCount = 2;//目前最多支持雙聲道

???????????break;

??????? default:

???? ??????return AudioTrack.ERROR_BAD_VALUE;

??????? }

??? ????//目前只支持PCM8和PCM16精度的音頻數(shù)據(jù)???

??????? if((audioFormat != AudioFormat.ENCODING_PCM_16BIT)

???????????&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {

???????????return AudioTrack.ERROR_BAD_VALUE;

??????? }

????? //對(duì)采樣頻率也有要求,太低或太高都不行。

??????? if( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) )

????????????return AudioTrack.ERROR_BAD_VALUE;

??????

?????? /*

??????? 調(diào)用Native函數(shù),先想想為什么,如果是簡(jiǎn)單計(jì)算,那么Java層做不到嗎?

??????? 原來,還需要確認(rèn)硬件是否支持這些參數(shù),當(dāng)然得進(jìn)入Native層查詢了

??????? */

???????int size = native_get_min_buff_size(sampleRateInHz, ????????

???????????????? channelCount,audioFormat);???????????????????????

??????? if((size == -1) || (size == 0)) {

? ???????????return AudioTrack.ERROR;

??????? }

???????else {

???????????return size;

????? ??}

}

Native的函數(shù)將查詢Audio系統(tǒng)中音頻輸出硬件HAL對(duì)象的一些信息,并確認(rèn)它們是否支持這些采樣率和采樣精度。

說明:HAL對(duì)象的具體實(shí)現(xiàn)和硬件廠商有關(guān)系,如果沒有特殊說明,我們則把硬件和HAL作為一種東西討論。

來看Native的native_get_min_buff_size函數(shù)。它在android_media_track.cpp中。

[-->android_media_track.cpp]

/*

注意我們傳入的參數(shù)是:

sampleRateInHertz = 8000,nbChannels = 2

audioFormat = AudioFormat.ENCODING_PCM_16BIT

*/

static jintandroid_media_AudioTrack_get_min_buff_size(????????????????????

???????????????? JNIEnv*env,? jobject thiz,

???????????????? jintsampleRateInHertz, jint nbChannels, jint audioFormat)

{

??? intafSamplingRate;

??? intafFrameCount;

? ??uint32_t afLatency;

???? /*

??????? 下面這些調(diào)用涉及了AudioSystem,這個(gè)和AudioPolicy有關(guān)系。這里僅把它們看成是

信息查詢即可

??? */

?? //查詢采樣率,一般返回的是所支持的最高采樣率,例如44100

??? if(AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {

???????return -1;

}

???? //① 查詢硬件內(nèi)部緩沖的大小,以Frame為單位。什么是Frame?

??? if(AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {

???????return -1;

??? }

??? //查詢硬件的延時(shí)時(shí)間

??? if(AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {

???????return -1;

}

......

這里有必要插入內(nèi)容,因?yàn)榇a中出現(xiàn)了音頻系統(tǒng)中的一個(gè)重要概念:Frame(幀)。

說明:Frame是一個(gè)單位,經(jīng)多方查尋,最終在ALSA的wiki中找到了對(duì)它的解釋。Frame直觀上用來描述數(shù)據(jù)量的多少,例如,一幀等于多少字節(jié)。1單位的Frame等于1個(gè)采樣點(diǎn)的字節(jié)數(shù)×聲道數(shù)(比如PCM16,雙聲道的1個(gè)Frame等于2×2=4字節(jié))。

我們知道,1個(gè)采樣點(diǎn)只針對(duì)一個(gè)聲道,而實(shí)際上可能會(huì)有一或多個(gè)聲道。由于不能用一個(gè)獨(dú)立的單位來表示全部聲道一次采樣的數(shù)據(jù)量,也就引出了Frame的概念。Frame的大小,就是一個(gè)采樣點(diǎn)的字節(jié)數(shù)×聲道數(shù)。另外,在目前的聲卡驅(qū)動(dòng)程序中,其內(nèi)部緩沖區(qū)也是采用Frame作為單位來分配和管理的。

? OK,繼續(xù)native_get_min_buff_size函數(shù)。

?? ......

?? // minBufCount表示緩沖區(qū)的最少個(gè)數(shù),它以Frame作為單位

???uint32_t minBufCount = afLatency / ((1000 *afFrameCount)/afSamplingRate);

??? if(minBufCount < 2) minBufCount = 2;//至少要兩個(gè)緩沖

?

?? //計(jì)算最小幀個(gè)數(shù)

?? uint32_tminFrameCount =???????????????

???????? (afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;

? //下面根據(jù)最小的FrameCount計(jì)算最小的緩沖大小???

?? intminBuffSize = minFrameCount //計(jì)算方法完全符合我們前面關(guān)于Frame的介紹

???????????* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)

???????????* nbChannels;

?

??? returnminBuffSize;

}

getMinBufSize會(huì)綜合考慮硬件的情況(諸如是否支持采樣率,硬件本身的延遲情況等)后,得出一個(gè)最小緩沖區(qū)的大小。一般我們分配的緩沖大小會(huì)是它的整數(shù)倍。

好了,介紹完一些基本概念后,開始要分析AudioTrack了。

7.2.2 ?AudioTrack(Java空間)的分析

注意:Java空間的分析包括JNI這一層,因?yàn)樗鼈兌叩年P(guān)系最為緊密。

1. AudioTrack的構(gòu)造

回顧一下用例中調(diào)用AudioTrack構(gòu)造函數(shù)的代碼:

AudioTrack trackplayer = new AudioTrack(

???????????????? AudioManager.STREAM_MUSIC,

???????????????? 8000,AudioFormat.CHANNEL_CONFIGURATION_ STEREO,

  ??????????????? ?AudioFormat.ENCODING_PCM_16BIT,bufsize,

???????????????? AudioTrack.MODE_STREAM);

AudioTrack構(gòu)造函數(shù)的實(shí)現(xiàn)在AudioTrack.java中。來看這個(gè)函數(shù):

[-->AudioTrack.java]

public AudioTrack(int streamType, intsampleRateInHz, int channelConfig,

???????????????? intaudioFormat,int bufferSizeInBytes, int mode)

?? ????????????? ?throws IllegalArgumentException {

???????

??????? mState= STATE_UNINITIALIZED;

??????? //檢查參數(shù)是否合法

???????audioParamCheck(streamType, sampleRateInHz, channelConfig,

???????????????????????? audioFormat,mode);

?? ????//bufferSizeInBytes是通過getMinBufferSize得到的,所以下面的檢查肯定能通過

???????audioBuffSizeCheck(bufferSizeInBytes);

?

??????? /*

?????????? 調(diào)用native層的native_setup,構(gòu)造一個(gè)WeakReference傳進(jìn)去。

???? ??????不了解Java WeakReference讀者可以上網(wǎng)查一下,很簡(jiǎn)單

?????? */

???????int initResult = native_setup(new WeakReference<AudioTrack>(this),

???????? mStreamType,//這個(gè)值是AudioManager.STREAM_MUSIC???

???????? mSampleRate, //這個(gè)值是8000???????

??????? mChannels,???//這個(gè)值是2

??????? mAudioFormat,//這個(gè)值是AudioFormat.ENCODING_PCM_16BIT??????

??????? ?mNativeBufferSizeInBytes,//這個(gè)值等于bufferSizeInBytes??????????????

??????? mDataLoadMode);//DataLoadMode是MODE_STREAM???????

???????? ....

}

OK,native_setup對(duì)應(yīng)的JNI層函數(shù)是android_media_AudioTrack_native_setup。一起來看:

[-->android_media_AudioTrack.cpp]

static int

android_media_AudioTrack_native_setup(JNIEnv*env, jobject thiz,???

???????????????????????? jobjectweak_this,jint streamType, ???????

???????????????????????? jintsampleRateInHertz, jint channels,???

???????????????????????? jintaudioFormat, jint buffSizeInBytes,??

???????????????????????? jintmemoryMode)?????????????????

{

??? intafSampleRate;

??? intafFrameCount;

?? ?//進(jìn)行一些信息查詢

??AudioSystem::getOutputFrameCount(&afFrameCount, streamType);

??AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);

??AudioSystem::isOutputChannel(channels);

?? //popCount用于統(tǒng)計(jì)一個(gè)整數(shù)中有多少位為1,有很多經(jīng)典的算法

int nbChannels = AudioSystem::popCount(channels);

??? //Java層的值和JNI層的值轉(zhuǎn)換

??? if(streamType == javaAudioTrackFields.STREAM_MUSIC)

?????? ??atStreamType = AudioSystem::MUSIC;

???

?? intbytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;

?? intformat = audioFormat == javaAudioTrackFields.PCM16 ?

????????????????? AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;

???

??? //計(jì)算以幀為單位的緩沖大小

??? intframeCount = buffSizeInBytes / (nbChannels * bytesPerSample);

?

???? //① AudioTrackJniStorage對(duì)象,它保存了一些信息,后面將詳細(xì)分析

???AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();

??? ??......

???? //②創(chuàng)建Native層的AudioTrack對(duì)象

???AudioTrack* lpTrack = new AudioTrack();

?????? if(memoryMode == javaAudioTrackFields.MODE_STREAM) {

? ?????//③STREAM模式

??????lpTrack->set(

???????????atStreamType,//指定流類型

???????????sampleRateInHertz,

???????????format,// 采樣點(diǎn)的精度,一般為PCM16或者PCM8

???????????channels,

???????????frameCount,

???????????0,// flags

???????????audioCallback, //該回調(diào)函數(shù)定義在android_media_AudioTrack.cpp中???

??????? &(lpJniStorage->mCallbackData),

???????????0,

???????????0,// 共享內(nèi)存,STREAM模式下為空。實(shí)際使用的共享內(nèi)存由AF創(chuàng)建

???????????true);//內(nèi)部線程可以調(diào)用JNI函數(shù),還記得“zygote偷梁換柱”那一節(jié)嗎?

?????????} else if (memoryMode == javaAudioTrackFields.MODE_STATIC) {

??????? ??//如果是static模式,需要先創(chuàng)建共享內(nèi)存

?????????lpJniStorage->allocSharedMem(buffSizeInBytes);

?????????lpTrack->set(

???????????atStreamType,// stream type

???????????sampleRateInHertz,

???????????format,// word length, PCM

???????????channels,

???????????frameCount,

???????????0,// flags

???????????audioCallback,

??????? ? &(lpJniStorage->mCallbackData),

???????????0,

???????????lpJniStorage->mMemBase, //STATIC模式下,需要傳遞該共享內(nèi)存

???????????true);

??? }

?

??? ......

??? /*

????? 把JNI層中new出來的AudioTrack對(duì)象指針保存到Java對(duì)象的一個(gè)變量中,

????? 這樣就把JNI層的AudioTrack對(duì)象和Java層的AudioTrack對(duì)象關(guān)聯(lián)起來了,

???? 這是Android的常用技法。? ??????????????????????

?? */???

???? env->SetIntField(thiz,javaAudioTrackFields.nativeTrackInJavaObj,

???????????????? ??? (int)lpTrack);

?? // lpJniStorage對(duì)象指針也保存到Java對(duì)象中

???env->SetIntField(thiz, javaAudioTrackFields.jniData,(int)lpJniStorage);

? }

上邊的代碼列出了三個(gè)要點(diǎn),這一節(jié)僅分析AudioTrackJniStorage這個(gè)類,其余的作為Native AudioTrack部分放在后面進(jìn)行分析。

2. AudioTrackJniStorage分析

AudioTrackJniStorage是一個(gè)輔助類,其中有一些有關(guān)共享內(nèi)存方面的較重要的知識(shí),這里先簡(jiǎn)單介紹一下。

(1) 共享內(nèi)存介紹

共享內(nèi)存,作為進(jìn)程間數(shù)據(jù)傳遞的一種手段,在AudioTrack和AudioFlinger中被大量使用。先簡(jiǎn)單了解一下有關(guān)共享內(nèi)存的知識(shí):

·? 每個(gè)進(jìn)程的內(nèi)存空間是4GB,這個(gè)4GB是由指針長度決定的,如果指針長度為32位,那么地址的最大編號(hào)就是0xFFFFFFFF,為4GB。

·? 上面說的內(nèi)存空間是進(jìn)程的虛擬地址空間。換言之,在應(yīng)用程序中使用的指針其實(shí)是指向虛擬空間地址的。那么,如何通過這個(gè)虛地址找到存儲(chǔ)在真實(shí)物理內(nèi)存中的數(shù)據(jù)呢?

上面的問題,引出了內(nèi)存映射的概念。內(nèi)存映射讓虛擬空間中的內(nèi)存地址和真實(shí)物理內(nèi)存地址之間建立了一種對(duì)應(yīng)關(guān)系。也就是說,進(jìn)程中操作的0x12345678這塊內(nèi)存的地址,在經(jīng)過OS內(nèi)存管理機(jī)制的轉(zhuǎn)換后,它實(shí)際對(duì)應(yīng)的物理地址可能會(huì)是0x87654321。當(dāng)然,這一切對(duì)進(jìn)程來說都是透明的,這些活都由操作系統(tǒng)悄悄地完成了。這和我們的共享內(nèi)存會(huì)有什么關(guān)系嗎?

當(dāng)然有,共享內(nèi)存和內(nèi)存映射有著重要關(guān)系。來看圖7-1“共享內(nèi)存示意圖”:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter7/image001.png" alt="image" />

圖7-1 ?共享內(nèi)存示意圖

圖7-1提出了一個(gè)關(guān)鍵性問題,即真實(shí)內(nèi)存中0x87654321標(biāo)志的這塊內(nèi)存頁(OS的內(nèi)存管理機(jī)制將物理內(nèi)存分成了一個(gè)個(gè)的內(nèi)存頁,一塊內(nèi)存頁的大小一般是4KB)現(xiàn)在已經(jīng)映射到了進(jìn)程A中??伤芡瑫r(shí)映射到進(jìn)程B中嗎?如果能,那么在進(jìn)程A中,對(duì)這塊內(nèi)存頁所寫的數(shù)據(jù)在進(jìn)程B中就能看見了,這豈不就做到了內(nèi)存在兩個(gè)進(jìn)程間共享嗎?

事實(shí)確實(shí)如此,否則我們的生活就不會(huì)像現(xiàn)在這么美好了。這個(gè)機(jī)制是由操作系統(tǒng)提供和實(shí)現(xiàn)的,原理很簡(jiǎn)單,實(shí)現(xiàn)起來卻很復(fù)雜,這里就不深究了。

如何創(chuàng)建和共享內(nèi)存呢?不同系統(tǒng)會(huì)有不同的方法。Linux平臺(tái)的一般做法是:

·? 進(jìn)程A創(chuàng)建并打開一個(gè)文件,得到一個(gè)文件描述符fd。

·? 通過mmap調(diào)用將fd映射成內(nèi)存映射文件。在mmap調(diào)用中指定特定參數(shù)表示要?jiǎng)?chuàng)建進(jìn)程間共享內(nèi)存。

·? 進(jìn)程B打開同一個(gè)文件,也得到一個(gè)文件描述符,這樣A和B就打開了同一個(gè)文件。

·? 進(jìn)程B也要用mmap調(diào)用指定參數(shù)表示想使用共享內(nèi)存,并傳遞打開的fd。這樣A和B就通過打開同一個(gè)文件并構(gòu)造內(nèi)存映射,實(shí)現(xiàn)了進(jìn)程間內(nèi)存共享。

注意,這個(gè)文件也可以是設(shè)備文件。一般來說,mmap函數(shù)的具體工作由參數(shù)中的那個(gè)文件描述符所對(duì)應(yīng)的驅(qū)動(dòng)或內(nèi)核模塊來完成。

除上述一般方法外,Linux還有System V的共享內(nèi)存創(chuàng)建方法,這里就不再介紹了。總之,AT和AF之間的數(shù)據(jù)傳遞,就是通過共享內(nèi)存方式來完成的。這種方式對(duì)于跨進(jìn)程的大數(shù)據(jù)量傳輸來說,是非常高效的。

(2) MemoryHeapBase和MemoryBase類介紹

AudioTrackJniStorage用到了Android對(duì)共享內(nèi)存機(jī)制的封裝類。所以我們有必要先看看AudioTrackJniStorage的內(nèi)容。

[-->android_media_AudioTrack.cpp::AudioTrackJniStorage相關(guān)]

//下面這個(gè)結(jié)構(gòu)就是保存一些變量,沒有什么特別的作用

struct audiotrack_callback_cookie {

???jclass????? audioTrack_class;

???jobject???? audioTrack_ref;

?};

class AudioTrackJniStorage {

???public:

???????sp<MemoryHeapBase>????mMemHeap;//這兩個(gè)Memory很重要

???????sp<MemoryBase>?????????mMemBase;

?

???????audiotrack_callback_cookie mCallbackData;

???????int???????????????????????mStreamType;

?

????? boolallocSharedMem(int sizeInBytes) {

???? /*?

???? ?注意關(guān)于MemoryHeapBase和MemoryBase的用法。???

????? 先new一個(gè)MemoryHeapBase,再以它為參數(shù)new一個(gè)MemoryBase????????

??? */??

?? //① MemoryHeapBase

?? ?mMemHeap = new MemoryHeapBase(sizeInBytes, 0,"AudioTrack Heap Base");

? ?//②MemoryBase

??? mMemBase= new MemoryBase(mMemHeap, 0, sizeInBytes);

??

?? ?return true;

?? }

};

注意代碼中所標(biāo)識(shí)的地方,它們很好地展示了這兩個(gè)Memory類的用法。在介紹它們之前,先來看圖7-2中與這兩個(gè)Memory有關(guān)的家譜。

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter7/image002.png" alt="image" />

圖7-2 ?MemoryHeapBase和MemoryBase的家譜

MemoryHeapBase是一個(gè)基于Binder通信的類,根據(jù)前面的Binder知識(shí),BpMemoryHeapBase由客戶端使用,而MemoryHeapBase完成BnMemoryHeapBase的業(yè)務(wù)工作。

從MemoryHeapBase開始分析。它的使用方法是:

mMemHeap = new MemoryHeapBase(sizeInBytes, 0,"AudioTrack Heap Base");

它的代碼在MemoryHeapBase.cpp中。

[-->MemoryHeapBase.cpp]

/*

?? MemoryHeapBase有兩個(gè)構(gòu)造函數(shù),我們用的是第一個(gè)。

? size表示共享內(nèi)存大小,flags為0,name為"AudioTrackHeap Base"

*/

MemoryHeapBase::MemoryHeapBase(size_t size,uint32_t flags,char const * name)

??? :mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),

?????mDevice(0), mNeedUnmap(false)

{

??? constsize_t pagesize = getpagesize();//獲取系統(tǒng)中的內(nèi)存頁大小,一般為4KB

??? size =((size + pagesize-1) & ~(pagesize-1));

?? /*???

???? 創(chuàng)建共享內(nèi)存,ashmem_create_region函數(shù)由libcutils提供。

??? ?在真實(shí)設(shè)備上將打開/dev/ashmem設(shè)備得到一個(gè)文件描述符,在模擬器上則創(chuàng)建一個(gè)tmp文件????

? */

?? int fd= ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);

? //下面這個(gè)函數(shù)將通過mmap方式得到內(nèi)存地址,這是Linux的標(biāo)準(zhǔn)做法,有興趣的讀者可以看看

?? mapfd(fd,size);

}

MemoryHeapBase構(gòu)造完后,得到了以下結(jié)果:

·? mBase變量指向共享內(nèi)存的起始位置。

·? mSize是所要求分配的內(nèi)存大小。

·? mFd是ashmem_create_region返回的文件描述符。

另外,MemoryHeapBase提供了以下幾個(gè)函數(shù),可以獲取共享內(nèi)存的大小和位置。由于這些函數(shù)都很簡(jiǎn)單,僅把它們的作用描述一下即可。

MemoryHeapBase::getBaseID() //返回mFd,如果為負(fù)數(shù),表明剛才創(chuàng)建共享內(nèi)存失敗了

MemoryHeapBase::getBase()? //共享內(nèi)存起始地址

MemoryHeapBase::getSize() //返回mSize,表示內(nèi)存大小

MemoryHeapBase確實(shí)比較簡(jiǎn)單,它通過ashmem_create_region得到一個(gè)文件描述符。

說明:Android系統(tǒng)通過ashmem創(chuàng)建共享內(nèi)存的原理,和Linux系統(tǒng)中通過打開文件創(chuàng)建共享內(nèi)存的原理類似,但ashmem設(shè)備驅(qū)動(dòng)在這方面做了較大的改進(jìn),例如增加了引用計(jì)數(shù)、延時(shí)分配物理內(nèi)存的機(jī)制(即真正使用的時(shí)候才去分配內(nèi)存)等。這些內(nèi)容,感興趣的讀者還可以自行對(duì)其研究。

那么,MemoryBase是何物?它又有什么作用?

MemoryBase也是一個(gè)基于Binder通信的類,它比起MemoryHeapBase就更顯簡(jiǎn)單了,看起來更像是一個(gè)輔助類。它的聲明在MemoryBase.h中。一起來看:

[-->MemoryBase.h::MemoryBase聲明]

class MemoryBase : public BnMemory

{

public:

???MemoryBase(const sp<IMemoryHeap>& heap,ssize_t offset, size_tsize);

???virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size)const;

?

protected:

??? size_tgetSize() const { return mSize; }//返回大小

??? ssize_tgetOffset() const { return mOffset;}//返回偏移量

??? //返回MemoryHeapBase對(duì)象?????

??? constsp<IMemoryHeap>& getHeap() const { return mHeap;}

};

//MemoryBase的構(gòu)造函數(shù)

MemoryBase::MemoryBase(constsp<IMemoryHeap>& heap,ssize_t offset, size_t size)

??? :mSize(size), mOffset(offset), mHeap(heap)

{

}

MemoryHeapBase和MemoryBase都?jí)蚝?jiǎn)單吧?總結(jié)起來不過是:

·? 分配了一塊共享內(nèi)存,這樣兩個(gè)進(jìn)程可以共享這塊內(nèi)存。

·? 基于Binder通信,這樣使用這兩個(gè)類的進(jìn)程就可以交互了。

這兩個(gè)類在后續(xù)的講解中會(huì)頻繁碰到,但不必對(duì)它們做深入分析,只需把它當(dāng)成普通的共享內(nèi)存看待即可。

提醒:這兩個(gè)類沒有提供同步對(duì)象來保護(hù)這塊共享內(nèi)存,所以后續(xù)在使用這塊內(nèi)存時(shí),必然需要一個(gè)跨進(jìn)程的同步對(duì)象來保護(hù)它。這一點(diǎn),是我在AT中第一次見到它們時(shí)想到的,不知道你是否注意過這個(gè)問題。

?

3. play和write的分析

還記得用例中的③和④關(guān)鍵代碼行嗎?

//③ 開始播放

trackplayer.play() ;

//④ 調(diào)用write寫數(shù)據(jù)

trackplayer.write(bytes_pkg, 0,bytes_pkg.length) ;//往track中寫數(shù)據(jù)

現(xiàn)在就來分析它們。我們要直接轉(zhuǎn)向JNI層來進(jìn)行分析。相信你,現(xiàn)在已有能力從Java層直接跳轉(zhuǎn)至JNI層了。

(1) play的分析

先看看play函數(shù)對(duì)應(yīng)的JNI層函數(shù),它是android_media_AudioTrack_start。

[-->android_media_AudioTrack.cpp]

static void

android_media_AudioTrack_start(JNIEnv *env,jobject thiz)

{

/*

? 從Java的AudioTrack對(duì)象中獲取對(duì)應(yīng)Native層的AudioTrack對(duì)象指針。

?從int類型直接轉(zhuǎn)換成指針,不過要是以后ARM平臺(tái)支持64位指針了,代碼就得大修改了。

*/

???AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(

??????? thiz,javaAudioTrackFields.nativeTrackInJavaObj);

???lpTrack->start(); //很簡(jiǎn)單的調(diào)用

}

play函數(shù)太簡(jiǎn)單了,至于它調(diào)用的start,等到Native層進(jìn)行AudioTrack分析時(shí),我們?cè)偃ビ^察。

(2) write的分析

Java層的write函數(shù)有兩個(gè):

·? 一個(gè)是用來寫PCM16數(shù)據(jù)的,它對(duì)應(yīng)的一個(gè)采樣點(diǎn)的數(shù)據(jù)量是兩個(gè)字節(jié)。

·? 另外一個(gè)用來寫PCM8數(shù)據(jù)的,它對(duì)應(yīng)的一個(gè)采樣點(diǎn)的數(shù)據(jù)量是一個(gè)字節(jié)。

我們的用例中采用的是PCM16數(shù)據(jù)。它對(duì)應(yīng)的JNI層函數(shù)是android_media_AudioTrack_native_write_short,一起來看:

[-->android_media_AudioTrack.cpp]

static jint android_media_AudioTrack_native_write_short(

???????????????? JNIEnv*env,? jobject thiz,

???????????????? jshortArrayjavaAudioData,jint offsetInShorts,

???????????????? jintsizeInShorts,jint javaAudioFormat) {

?

??????? return(android_media_AudioTrack_native_write(

???????????????? env,thiz,(jbyteArray)javaAudioData,offsetInShorts*2,

???????????????? sizeInShorts*2,javaAudioFormat)/ 2);

}

無論P(yáng)CM16還是PCM8數(shù)據(jù),最終都會(huì)調(diào)用writeToTrack函數(shù)。

[-->android_media_AudioTrack.cpp]

jint writeToTrack(AudioTrack* pTrack, jintaudioFormat,

???????????????? jbyte*data,jint offsetInBytes, jint sizeInBytes) {

????

?? ssize_t written = 0;

? /*

???? 如果是STATIC模式,sharedBuffer()返回不為空

???? 如果是STREAM模式,sharedBuffer()返回空

? */

?? ???if (pTrack->sharedBuffer() == 0) {

??? ?????//我們的用例是STREAM模式,調(diào)用write函數(shù)寫數(shù)據(jù)

???????written = pTrack->write(data + offsetInBytes, sizeInBytes);

??? } else{

?????? ?if (audioFormat == javaAudioTrackFields.PCM16){

???????????if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) {

???????????????sizeInBytes = pTrack->sharedBuffer()->size();

???????????}

????????//在STATIC模式下,直接把數(shù)據(jù)memcpy到共享內(nèi)存,記住在這種模式下要先調(diào)用write

??????? //后調(diào)用play

??????????memcpy(pTrack->sharedBuffer()->pointer(),

???????????????????????? data+ offsetInBytes, sizeInBytes);

???????????written = sizeInBytes;

??????? }else if (audioFormat == javaAudioTrackFields.PCM8) {

??????????//如果是PCM8數(shù)據(jù),則先轉(zhuǎn)換成PCM16數(shù)據(jù)再拷貝

???????????......

??? }

??? returnwritten;

}

看上去,play和write這兩個(gè)函數(shù)還真是比較簡(jiǎn)單,須知,大部分工作還都是由Native的AudioTrack來完成的。繼續(xù)Java層的分析。

4. release的分析

當(dāng)數(shù)據(jù)都write完后,需要調(diào)用stop停止播放,或者直接調(diào)用release來釋放相關(guān)資源。由于release和stop有一定的相關(guān)性,這里只分析release調(diào)用。

[-->android_media_AudioTrack.cpp]

static voidandroid_media_AudioTrack_native_release(JNIEnv *env,? jobject thiz) {

??????

??? //調(diào)用android_media_AudioTrack_native_finalize真正釋放資源

???android_media_AudioTrack_native_finalize(env, thiz);

??? //之前保存在Java對(duì)象中的指針變量此時(shí)都要設(shè)置為零

???env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);

???env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);

}

[-->android_media_AudioTrack.cpp]

static voidandroid_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz) {

???AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(

?????? ?????????????????? ?thiz, javaAudioTrackFields.nativeTrackInJavaObj);

??? if(lpTrack) {

???????lpTrack->stop();//調(diào)用stop

???????delete lpTrack; //調(diào)用AudioTrack的析構(gòu)函數(shù)

}

......

}

掃尾工作也很簡(jiǎn)單,沒什么需要特別注意的。

至此,在Java空間的分析工作就完成了。但在進(jìn)入Native空間的分析之前,要總結(jié)一下Java空間使用Native的AudioTrack的流程,只有這樣,在進(jìn)行Native空間分析時(shí)才能有章可循。

5. AudioTrack(Java空間)的分析總結(jié)

AudioTrack在JNI層使用了Native的AudioTrack對(duì)象,總結(jié)一下調(diào)用Native對(duì)象的流程:

·? new一個(gè)AudioTrack,使用無參的構(gòu)造函數(shù)。

·? 調(diào)用set函數(shù),把Java層的參數(shù)傳進(jìn)去,另外還設(shè)置了一個(gè)audiocallback回調(diào)函數(shù)。

·? 調(diào)用了AudioTrack的start函數(shù)。

·? 調(diào)用AudioTrack的write函數(shù)。

·? 工作完畢后,調(diào)用stop。

·? 最后就是Native對(duì)象的delete。

說明:為什么要總結(jié)流程呢?

第一:控制了流程,就把握了系統(tǒng)工作的命脈,這一點(diǎn)至關(guān)重要。

第二:有些功能的實(shí)現(xiàn)縱跨Java/Native層,橫跨兩個(gè)進(jìn)程,這中間有很多封裝、很多的特殊處理,但是其基本流程是不變的。通過精簡(jiǎn)流程,我們才能把注意力集中在關(guān)鍵點(diǎn)上。

?

7.2.3 ?AudioTrack(Native空間)的分析

1. new AudioTrack和set分析

Native的AudioTrack代碼在AudioTrack.cpp中。這一節(jié),分析它的構(gòu)造函數(shù)和set調(diào)用。

[-->AudioTrack.cpp]

AudioTrack::AudioTrack()//我們使用無參構(gòu)造函數(shù)

??? :mStatus(NO_INIT)

{

? //把狀態(tài)初始化成NO_INIT。Android的很多類都采用了這種狀態(tài)控制

}

再看看set調(diào)用,這個(gè)函數(shù)有很多內(nèi)容。

[-->AudioTrack.cpp]

/*?

?? 還記得我們傳入的參數(shù)嗎?

?streamType=STREAM_MUSIC,sampleRate=8000,format=PCM_16

?channels=2,frameCount由計(jì)算得來,可以假設(shè)一個(gè)值,例如1024,不影響分析。

? flags=0,cbf=audiocallback, user為cbf的參數(shù),notificationFrames=0

? 因?yàn)槭橇髂J剑詓haredBuffer=0。threadCanCallJava 為true

*/

status_t AudioTrack::set(int streamType,uint32_t sampleRate,int format,

???????int channels,int frameCount,uint32_t flags,callback_t cbf,void* user,

???????int notificationFrames,const sp<IMemory>& sharedBuffer,

??????? boolthreadCanCallJava)

{

? ??//前面有一些判斷,都是和AudioSystem有關(guān)的,以后再分析

??? ......

? /*

???? audio_io_handle_t是一個(gè)int類型,通過typedef定義,這個(gè)值的來歷非常復(fù)雜,

涉及AudioFlinger和AudioPolicyService, 后邊的分析試將其解釋清楚。

這個(gè)值主要被AudioFlinger使用,用來表示內(nèi)部的工作線程索引號(hào)。AudioFlinger會(huì)根據(jù)

情況創(chuàng)建幾個(gè)工作線程,下面的AudioSystem::getOutput會(huì)根據(jù)流類型等其他參數(shù)最終選

取一個(gè)合適的工作線程,并返回它在AF中的索引號(hào)。

而AudioTrack一般使用混音線程(Mixer Thread)

?*/

??? audio_io_handle_toutput = AudioSystem::getOutput(

???????????????????????? (AudioSystem::stream_type)streamType,

????????????????????????? sampleRate,format, channels,

???????????????????????? (AudioSystem::output_flags)flags);

? ?//調(diào)用creatTrack

???status_t status = createTrack(streamType, sampleRate, format,channelCount,

????????????????????????????????? frameCount,flags, sharedBuffer, output);

? ??

?

//cbf是JNI層傳入的回調(diào)函數(shù)audioCallback,如果用戶設(shè)置了回調(diào)函數(shù),則啟動(dòng)一個(gè)線程

? if (cbf!= 0) {

???????mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

?? }

?? returnNO_ERROR;

}

再看createTrack函數(shù):

[-->AudioTrack.cpp]

status_t AudioTrack::createTrack(intstreamType,uint32_t sampleRate,

???????int format,int channelCount,int frameCount, uint32_t flags,

???????const sp<IMemory>& sharedBuffer, audio_io_handle_t output)

{

?? status_tstatus;

?

? /*

??? 得到AudioFlinger的Binder代理端BpAudioFlinger。

??? 關(guān)于這部分內(nèi)容,我們已經(jīng)很熟悉了,以后的講解會(huì)跨過Binder,直接分析Bn端的實(shí)現(xiàn)

? */

? constsp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();

??

?/*

向AudioFinger發(fā)送createTrack請(qǐng)求。注意其中的幾個(gè)參數(shù),

在STREAM模式下sharedBuffer為空

??? output為AudioSystem::getOutput得到一個(gè)值,代表AF中的線程索引號(hào)

該函數(shù)返回IAudioTrack(實(shí)際類型是BpAudioTrack)對(duì)象,后續(xù)AF和AT的交互就是

圍繞IAudioTrack進(jìn)行的

*/

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

???? ???streamType,sampleRate,format,channelCount,frameCount,

????????((uint16_t)flags) << 16,sharedBuffer,output,&status);

?

?? /*

???? 在STREAM模式下,沒有在AT端創(chuàng)建共享內(nèi)存,但前面提到了AT和AF的數(shù)據(jù)交互是

通過共享內(nèi)存完成的,這塊共享內(nèi)存最終由AF的createTrack創(chuàng)建。我們以后分析AF時(shí)

再做介紹。下面這個(gè)調(diào)用會(huì)取出AF創(chuàng)建的共享內(nèi)存

*/

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

???mAudioTrack.clear();//sp的clear

??? mAudioTrack= track;

???mCblkMemory.clear();

??? mCblkMemory= cblk;//cblk是control block的簡(jiǎn)寫

? /*

??? IMemory的pointer在此處將返回共享內(nèi)存的首地址,類型為void*,

??? static_cast直接把這個(gè)void*類型轉(zhuǎn)成audio_track_cblk_t,表明這塊內(nèi)存的首部中存在

???? audio_track_cblk_t這個(gè)對(duì)象

? */

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

???mCblk->out = 1;//out為1表示輸出,out為0表示輸入

???mFrameCount = mCblk->frameCount;

??? if(sharedBuffer == 0) {

?????? //buffers指向數(shù)據(jù)空間,它的起始位置是共享內(nèi)存的首部加上audio_track_cblk_t的大小

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

? } else {

??? ???//STATIC模式下的處理

?????? ?mCblk->buffers =sharedBuffer->pointer();

?? ?????mCblk->stepUser(mFrameCount);//更新數(shù)據(jù)位置,后面要分析stepUser的作用

??? }

??? returnNO_ERROR;

}

(1)IAudioTrack和AT、AF的關(guān)系

上面的createTrack函數(shù)中突然冒出來一個(gè)新面孔,叫IAudioTrack。關(guān)于它和AT及AF的關(guān)系,我們用圖7-3來表示:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter7/image003.png" alt="image" />

圖7-3 ?IAudioTrack和AT、AF的關(guān)系

從圖7-3中可以發(fā)現(xiàn):

·? IAudioTrack是聯(lián)系A(chǔ)T和AF的關(guān)鍵紐帶。

至于IAudioTrack在AF端到底是什么,在分析AF時(shí)會(huì)有詳細(xì)解釋。

(2)共享內(nèi)存及其Control Block

通過前面的代碼分析,我們發(fā)現(xiàn)IAudioTrack中有一塊共享內(nèi)存,其頭部是一個(gè)audio_track_cblk_t(簡(jiǎn)稱CB)對(duì)象,在該對(duì)象之后才是數(shù)據(jù)緩沖。這個(gè)CB對(duì)象有什么作用呢?

還記得前面提到的那個(gè)深層次思考的問題嗎?即MemoryHeapBase和MemoryBase都沒有提供同步對(duì)象,那么,AT和AF作為典型的數(shù)據(jù)生產(chǎn)者和消費(fèi)者,如何正確協(xié)調(diào)二者生產(chǎn)和消費(fèi)的步調(diào)呢?

Android為順應(yīng)民意,便創(chuàng)造出了這個(gè)CB對(duì)象,其主要目的就是協(xié)調(diào)和管理AT和AF二者數(shù)據(jù)生產(chǎn)和消費(fèi)的步伐。先來看CB都管理些什么內(nèi)容。它的聲明在AudioTrackShared.h中,而定義卻在AudioTrack.cpp中。

[-->AudioTrackShared.h::audio_track_cblk_t聲明]

struct audio_track_cblk_t

{

?

?????? Mutex?????? lock;

?????? Condition?? cv;//這是兩個(gè)同步變量,初始化的時(shí)候會(huì)設(shè)置為支持跨進(jìn)程共享

? /*

? ?一塊數(shù)據(jù)緩沖同時(shí)被生產(chǎn)者和消費(fèi)者使用,最重要的就是維護(hù)它的讀寫位置了。

? ?下面定義的這些變量就和讀寫的位置有關(guān),雖然它們的名字并不是那么直觀。

?? 另外,這里提一個(gè)擴(kuò)展問題,讀者可以思考一下:

??volatile支持跨進(jìn)程嗎?要回答這個(gè)問題需要理解volatile、CPU Cache機(jī)制和共享內(nèi)存的本質(zhì)

?*/

???volatile??? uint32_t??? user;???//當(dāng)前寫位置(即生產(chǎn)者已經(jīng)寫到什么位置了)

??? volatile??? uint32_t???server;? //當(dāng)前讀位置

?/*

??? userBase和serverBase要和user及server結(jié)合起來用。

??? CB巧妙地通過上面幾個(gè)變量把一塊線性緩沖當(dāng)做環(huán)形緩沖來使用,以后將單獨(dú)分析這個(gè)問題

? */

???????????????uint32_t??? userBase;? //

???????????????uint32_t??? serverBase;

???void*?????? buffers; //指向數(shù)據(jù)緩沖的首地址

???uint32_t??? frameCount;//數(shù)據(jù)緩沖的總大小,以Frame為單位