<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                * * * * * **注意**:Java空間的分析包括JNI這一層,因為它們二者的關系最為緊密。 * * * * * 1. AudioTrack的構造 回顧一下用例中調用AudioTrack構造函數的代碼: ~~~ AudioTrack trackplayer = new AudioTrack( AudioManager.STREAM_MUSIC, 8000,AudioFormat.CHANNEL_CONFIGURATION_ STEREO,    AudioFormat.ENCODING_PCM_16BIT,bufsize, AudioTrack.MODE_STREAM); ~~~ AudioTrack構造函數的實現在AudioTrack.java中。來看這個函數: **AudioTrack.java** ~~~ public AudioTrack(int streamType, intsampleRateInHz, int channelConfig, intaudioFormat,int bufferSizeInBytes, int mode) throws IllegalArgumentException { mState= STATE_UNINITIALIZED; //檢查參數是否合法 audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat,mode); //bufferSizeInBytes是通過getMinBufferSize得到的,所以下面的檢查肯定能通過 audioBuffSizeCheck(bufferSizeInBytes); /* 調用native層的native_setup,構造一個WeakReference傳進去。 不了解Java WeakReference讀者可以上網查一下,很簡單 */ int initResult = native_setup(new WeakReference<AudioTrack>(this), mStreamType,//這個值是AudioManager.STREAM_MUSIC mSampleRate, //這個值是8000 mChannels, //這個值是2 mAudioFormat,//這個值是AudioFormat.ENCODING_PCM_16BIT mNativeBufferSizeInBytes,//這個值等于bufferSizeInBytes mDataLoadMode);//DataLoadMode是MODE_STREAM .... } ~~~ OK,native_setup對應的JNI層函數是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; //進行一些信息查詢 AudioSystem::getOutputFrameCount(&afFrameCount, streamType); AudioSystem::getOutputSamplingRate(&afSampleRate, streamType); AudioSystem::isOutputChannel(channels); //popCount用于統計一個整數中有多少位為1,有很多經典的算法 int nbChannels = AudioSystem::popCount(channels); //Java層的值和JNI層的值轉換 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; //計算以幀為單位的緩沖大小 intframeCount = buffSizeInBytes / (nbChannels * bytesPerSample); //① AudioTrackJniStorage對象,它保存了一些信息,后面將詳細分析 AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); ...... //②創建Native層的AudioTrack對象 AudioTrack* lpTrack = new AudioTrack(); if(memoryMode == javaAudioTrackFields.MODE_STREAM) { //③STREAM模式 lpTrack->set( atStreamType,//指定流類型 sampleRateInHertz, format,// 采樣點的精度,一般為PCM16或者PCM8 channels, frameCount, 0,// flags audioCallback, //該回調函數定義在android_media_AudioTrack.cpp中 &(lpJniStorage->mCallbackData), 0, 0,// 共享內存,STREAM模式下為空。實際使用的共享內存由AF創建 true);//內部線程可以調用JNI函數,還記得“zygote偷梁換柱”那一節嗎? } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) { //如果是static模式,需要先創建共享內存 lpJniStorage->allocSharedMem(buffSizeInBytes); lpTrack->set( atStreamType,// stream type sampleRateInHertz, format,// word length, PCM channels, frameCount, 0,// flags audioCallback, &(lpJniStorage->mCallbackData), 0, lpJniStorage->mMemBase, //STATIC模式下,需要傳遞該共享內存 true); } ...... /* 把JNI層中new出來的AudioTrack對象指針保存到Java對象的一個變量中, 這樣就把JNI層的AudioTrack對象和Java層的AudioTrack對象關聯起來了, 這是Android的常用技法。 */ env->SetIntField(thiz,javaAudioTrackFields.nativeTrackInJavaObj, (int)lpTrack); // lpJniStorage對象指針也保存到Java對象中 env->SetIntField(thiz, javaAudioTrackFields.jniData,(int)lpJniStorage); } ~~~ 上邊的代碼列出了三個要點,這一節僅分析AudioTrackJniStorage這個類,其余的作為Native AudioTrack部分放在后面進行分析。 2. AudioTrackJniStorage分析 AudioTrackJniStorage是一個輔助類,其中有一些有關共享內存方面的較重要的知識,這里先簡單介紹一下。 (1) 共享內存介紹 共享內存,作為進程間數據傳遞的一種手段,在AudioTrack和AudioFlinger中被大量使用。先簡單了解一下有關共享內存的知識: - 每個進程的內存空間是4GB,這個4GB是由指針長度決定的,如果指針長度為32位,那么地址的最大編號就是0xFFFFFFFF,為4GB。 - 上面說的內存空間是進程的虛擬地址空間。換言之,在應用程序中使用的指針其實是指向虛擬空間地址的。那么,如何通過這個虛地址找到存儲在真實物理內存中的數據呢? 上面的問題,引出了內存映射的概念。內存映射讓虛擬空間中的內存地址和真實物理內存地址之間建立了一種對應關系。也就是說,進程中操作的0x12345678這塊內存的地址,在經過OS內存管理機制的轉換后,它實際對應的物理地址可能會是0x87654321。當然,這一切對進程來說都是透明的,這些活都由操作系統悄悄地完成了。這和我們的共享內存會有什么關系嗎? 當然有,共享內存和內存映射有著重要關系。來看圖7-1“共享內存示意圖”: :-: ![](http://img.blog.csdn.net/20150802160407345?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-1 共享內存示意圖 圖7-1提出了一個關鍵性問題,即真實內存中0x87654321標志的這塊內存頁(OS的內存管理機制將物理內存分成了一個個的內存頁,一塊內存頁的大小一般是4KB)現在已經映射到了進程A中。可它能同時映射到進程B中嗎?如果能,那么在進程A中,對這塊內存頁所寫的數據在進程B中就能看見了,這豈不就做到了內存在兩個進程間共享嗎? 事實確實如此,否則我們的生活就不會像現在這么美好了。這個機制是由操作系統提供和實現的,原理很簡單,實現起來卻很復雜,這里就不深究了。 如何創建和共享內存呢?不同系統會有不同的方法。Linux平臺的一般做法是: - 進程A創建并打開一個文件,得到一個文件描述符fd。 - 通過mmap調用將fd映射成內存映射文件。在mmap調用中指定特定參數表示要創建進程間共享內存。 - 進程B打開同一個文件,也得到一個文件描述符,這樣A和B就打開了同一個文件。 - 進程B也要用mmap調用指定參數表示想使用共享內存,并傳遞打開的fd。這樣A和B就通過打開同一個文件并構造內存映射,實現了進程間內存共享。 * * * * * **注意**,這個文件也可以是設備文件。一般來說,mmap函數的具體工作由參數中的那個文件描述符所對應的驅動或內核模塊來完成。 * * * * * 除上述一般方法外,Linux還有System V的共享內存創建方法,這里就不再介紹了。總之,AT和AF之間的數據傳遞,就是通過共享內存方式來完成的。這種方式對于跨進程的大數據量傳輸來說,是非常高效的。 (2) MemoryHeapBase和MemoryBase類介紹 AudioTrackJniStorage用到了Android對共享內存機制的封裝類。所以我們有必要先看看AudioTrackJniStorage的內容。 **android_media_AudioTrack.cpp::AudioTrackJniStorage相關** ~~~ /下面這個結構就是保存一些變量,沒有什么特別的作用 struct audiotrack_callback_cookie { jclass audioTrack_class; jobject audioTrack_ref; }; class AudioTrackJniStorage { public: sp<MemoryHeapBase> mMemHeap;//這兩個Memory很重要 sp<MemoryBase> mMemBase; audiotrack_callback_cookie mCallbackData; int mStreamType; boolallocSharedMem(int sizeInBytes) { /* 注意關于MemoryHeapBase和MemoryBase的用法。 先new一個MemoryHeapBase,再以它為參數new一個MemoryBase */ //① MemoryHeapBase mMemHeap = new MemoryHeapBase(sizeInBytes, 0,"AudioTrack Heap Base"); //②MemoryBase mMemBase= new MemoryBase(mMemHeap, 0, sizeInBytes); return true; } }; ~~~ * * * * * **注意**代碼中所標識①和②的地方,它們很好地展示了這兩個Memory類的用法。在介紹它們之前,先來看圖7-2中與這兩個Memory有關的家譜。 * * * * * :-: ![](http://img.blog.csdn.net/20150802160313974?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-2 MemoryHeapBase和MemoryBase的家譜 MemoryHeapBase是一個基于Binder通信的類,根據前面的Binder知識,BpMemoryHeapBase由客戶端使用,而MemoryHeapBase完成BnMemoryHeapBase的業務工作。 從MemoryHeapBase開始分析。它的使用方法是: ~~~ mMemHeap = new MemoryHeapBase(sizeInBytes, 0,"AudioTrack Heap Base"); ~~~ 它的代碼在MemoryHeapBase.cpp中。 **MemoryHeapBase.cpp** ~~~ /* MemoryHeapBase有兩個構造函數,我們用的是第一個。 size表示共享內存大小,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();//獲取系統中的內存頁大小,一般為4KB size =((size + pagesize-1) & ~(pagesize-1)); /* 創建共享內存,ashmem_create_region函數由libcutils提供。 在真實設備上將打開/dev/ashmem設備得到一個文件描述符,在模擬器上則創建一個tmp文件 */ int fd= ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); //下面這個函數將通過mmap方式得到內存地址,這是Linux的標準做法,有興趣的讀者可以看看 mapfd(fd,size); } ~~~ MemoryHeapBase構造完后,得到了以下結果: - mBase變量指向共享內存的起始位置。 - mSize是所要求分配的內存大小。 - mFd是ashmem_create_region返回的文件描述符。 另外,MemoryHeapBase提供了以下幾個函數,可以獲取共享內存的大小和位置。由于這些函數都很簡單,僅把它們的作用描述一下即可。 ~~~ MemoryHeapBase::getBaseID() //返回mFd,如果為負數,表明剛才創建共享內存失敗了 MemoryHeapBase::getBase() //共享內存起始地址 MemoryHeapBase::getSize() //返回mSize,表示內存大小 ~~~ MemoryHeapBase確實比較簡單,它通過ashmem_create_region得到一個文件描述符。 >[info]**說明**:Android系統通過ashmem創建共享內存的原理,和Linux系統中通過打開文件創建共享內存的原理類似,但ashmem設備驅動在這方面做了較大的改進,例如增加了引用計數、延時分配物理內存的機制(即真正使用的時候才去分配內存)等。這些內容,感興趣的讀者還可以自行對其研究。 那么,MemoryBase是何物?它又有什么作用? MemoryBase也是一個基于Binder通信的類,它比起MemoryHeapBase就更顯簡單了,看起來更像是一個輔助類。它的聲明在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對象 constsp<IMemoryHeap>& getHeap() const { return mHeap;} }; //MemoryBase的構造函數 MemoryBase::MemoryBase(constsp<IMemoryHeap>& heap,ssize_t offset, size_t size) :mSize(size), mOffset(offset), mHeap(heap) { } ~~~ MemoryHeapBase和MemoryBase都夠簡單吧?總結起來不過是: - 分配了一塊共享內存,這樣兩個進程可以共享這塊內存。 - 基于Binder通信,這樣使用這兩個類的進程就可以交互了。 這兩個類在后續的講解中會頻繁碰到,但不必對它們做深入分析,只需把它當成普通的共享內存看待即可。 >[warning] **提醒**:這兩個類沒有提供同步對象來保護這塊共享內存,所以后續在使用這塊內存時,必然需要一個跨進程的同步對象來保護它。這一點,是我在AT中第一次見到它們時想到的,不知道你是否注意過這個問題。 3. play和write的分析 還記得用例中的③和④關鍵代碼行嗎? ~~~ //③ 開始播放 trackplayer.play() ; //④ 調用write寫數據 trackplayer.write(bytes_pkg, 0,bytes_pkg.length) ;//往track中寫數據 ~~~ 現在就來分析它們。我們要直接轉向JNI層來進行分析。相信你,現在已有能力從Java層直接跳轉至JNI層了。 (1) play的分析 先看看play函數對應的JNI層函數,它是android_media_AudioTrack_start。 **android_media_AudioTrack.cpp** ~~~ static void android_media_AudioTrack_start(JNIEnv *env,jobject thiz) { /* 從Java的AudioTrack對象中獲取對應Native層的AudioTrack對象指針。 從int類型直接轉換成指針,不過要是以后ARM平臺支持64位指針了,代碼就得大修改了。 */ AudioTrack *lpTrack = (AudioTrack *)env->GetIntField( thiz,javaAudioTrackFields.nativeTrackInJavaObj); lpTrack->start(); //很簡單的調用 } ~~~ play函數太簡單了,至于它調用的start,等到Native層進行AudioTrack分析時,我們再去觀察。 (2) write的分析 Java層的write函數有兩個: - 一個是用來寫PCM16數據的,它對應的一個采樣點的數據量是兩個字節。 - 另外一個用來寫PCM8數據的,它對應的一個采樣點的數據量是一個字節。 我們的用例中采用的是PCM16數據。它對應的JNI層函數是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); } ~~~ 無論PCM16還是PCM8數據,最終都會調用writeToTrack函數。 **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模式,調用write函數寫數據 written = pTrack->write(data + offsetInBytes, sizeInBytes); } else{ if (audioFormat == javaAudioTrackFields.PCM16){ if ((size_t)sizeInBytes > pTrack->sharedBuffer()->size()) { sizeInBytes = pTrack->sharedBuffer()->size(); } //在STATIC模式下,直接把數據memcpy到共享內存,記住在這種模式下要先調用write //后調用play memcpy(pTrack->sharedBuffer()->pointer(), data+ offsetInBytes, sizeInBytes); written = sizeInBytes; }else if (audioFormat == javaAudioTrackFields.PCM8) { //如果是PCM8數據,則先轉換成PCM16數據再拷貝 ...... } returnwritten; } ~~~ 看上去,play和write這兩個函數還真是比較簡單,須知,大部分工作還都是由Native的AudioTrack來完成的。繼續Java層的分析。 4. release的分析 當數據都write完后,需要調用stop停止播放,或者直接調用release來釋放相關資源。由于release和stop有一定的相關性,這里只分析release調用。 **android_media_AudioTrack.cpp** ~~~ static voidandroid_media_AudioTrack_native_release(JNIEnv *env, jobject thiz) { //調用android_media_AudioTrack_native_finalize真正釋放資源 android_media_AudioTrack_native_finalize(env, thiz); //之前保存在Java對象中的指針變量此時都要設置為零 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();//調用stop delete lpTrack; //調用AudioTrack的析構函數 } ...... } ~~~ 掃尾工作也很簡單,沒什么需要特別注意的。 至此,在Java空間的分析工作就完成了。但在進入Native空間的分析之前,要總結一下Java空間使用Native的AudioTrack的流程,只有這樣,在進行Native空間分析時才能有章可循。 5. AudioTrack(Java空間)的分析總結 AudioTrack在JNI層使用了Native的AudioTrack對象,總結一下調用Native對象的流程: - new一個AudioTrack,使用無參的構造函數。 - 調用set函數,把Java層的參數傳進去,另外還設置了一個audiocallback回調函數。 - 調用了AudioTrack的start函數。 - 調用AudioTrack的write函數。 - 工作完畢后,調用stop。 - 最后就是Native對象的delete。 * * * * * **說明**:為什么要總結流程呢? 第一:控制了流程,就把握了系統工作的命脈,這一點至關重要。 第二:有些功能的實現縱跨Java/Native層,橫跨兩個進程,這中間有很多封裝、很多的特殊處理,但是其基本流程是不變的。通過精簡流程,我們才能把注意力集中在關鍵點上。 * * * * *
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看