<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                1. new AudioTrack和set分析 Native的AudioTrack代碼在AudioTrack.cpp中。這一節,分析它的構造函數和set調用。 **AudioTrack.cpp** ~~~ AudioTrack::AudioTrack()//我們使用無參構造函數 :mStatus(NO_INIT) { //把狀態初始化成NO_INIT。Android的很多類都采用了這種狀態控制 } ~~~ 再看看set調用,這個函數有很多內容。 **AudioTrack.cpp** ~~~ /* 還記得我們傳入的參數嗎? streamType=STREAM_MUSIC,sampleRate=8000,format=PCM_16 channels=2,frameCount由計算得來,可以假設一個值,例如1024,不影響分析。 flags=0,cbf=audiocallback, user為cbf的參數,notificationFrames=0 因為是流模式,所以sharedBuffer=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有關的,以后再分析 ...... /* audio_io_handle_t是一個int類型,通過typedef定義,這個值的來歷非常復雜, 涉及AudioFlinger和AudioPolicyService, 后邊的分析試將其解釋清楚。 這個值主要被AudioFlinger使用,用來表示內部的工作線程索引號。AudioFlinger會根據 情況創建幾個工作線程,下面的AudioSystem::getOutput會根據流類型等其他參數最終選 取一個合適的工作線程,并返回它在AF中的索引號。 而AudioTrack一般使用混音線程(Mixer Thread) */ audio_io_handle_toutput = AudioSystem::getOutput( (AudioSystem::stream_type)streamType, sampleRate,format, channels, (AudioSystem::output_flags)flags); //調用creatTrack status_t status = createTrack(streamType, sampleRate, format,channelCount, frameCount,flags, sharedBuffer, output); //cbf是JNI層傳入的回調函數audioCallback,如果用戶設置了回調函數,則啟動一個線程 if (cbf!= 0) { mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava); } returnNO_ERROR; } ~~~ 再看createTrack函數: **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。 關于這部分內容,我們已經很熟悉了,以后的講解會跨過Binder,直接分析Bn端的實現 */ constsp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger(); /* 向AudioFinger發送createTrack請求。注意其中的幾個參數, 在STREAM模式下sharedBuffer為空 output為AudioSystem::getOutput得到一個值,代表AF中的線程索引號 該函數返回IAudioTrack(實際類型是BpAudioTrack)對象,后續AF和AT的交互就是 圍繞IAudioTrack進行的 */ sp<IAudioTrack> track = audioFlinger->createTrack(getpid(), streamType,sampleRate,format,channelCount,frameCount, ((uint16_t)flags) << 16,sharedBuffer,output,&status); /* 在STREAM模式下,沒有在AT端創建共享內存,但前面提到了AT和AF的數據交互是 通過共享內存完成的,這塊共享內存最終由AF的createTrack創建。我們以后分析AF時 再做介紹。下面這個調用會取出AF創建的共享內存 */ sp<IMemory> cblk = track->getCblk(); mAudioTrack.clear();//sp的clear mAudioTrack= track; mCblkMemory.clear(); mCblkMemory= cblk;//cblk是control block的簡寫 /* IMemory的pointer在此處將返回共享內存的首地址,類型為void*, static_cast直接把這個void*類型轉成audio_track_cblk_t,表明這塊內存的首部中存在 audio_track_cblk_t這個對象 */ mCblk= static_cast<audio_track_cblk_t*>(cblk->pointer()); mCblk->out = 1;//out為1表示輸出,out為0表示輸入 mFrameCount = mCblk->frameCount; if(sharedBuffer == 0) { //buffers指向數據空間,它的起始位置是共享內存的首部加上audio_track_cblk_t的大小 mCblk->buffers= (char*)mCblk + sizeof(audio_track_cblk_t); } else { //STATIC模式下的處理 mCblk->buffers =sharedBuffer->pointer(); mCblk->stepUser(mFrameCount);//更新數據位置,后面要分析stepUser的作用 } returnNO_ERROR; } ~~~ (1)IAudioTrack和AT、AF的關系 上面的createTrack函數中突然冒出來一個新面孔,叫IAudioTrack。關于它和AT及AF的關系,我們用圖7-3來表示: :-: ![](http://img.blog.csdn.net/20150802160437437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-3 IAudioTrack和AT、AF的關系 從圖7-3中可以發現: - IAudioTrack是聯系AT和AF的關鍵紐帶。 至于IAudioTrack在AF端到底是什么,在分析AF時會有詳細解釋。 (2)共享內存及其Control Block 通過前面的代碼分析,我們發現IAudioTrack中有一塊共享內存,其頭部是一個audio_track_cblk_t(簡稱CB)對象,在該對象之后才是數據緩沖。這個CB對象有什么作用呢? 還記得前面提到的那個深層次思考的問題嗎?即MemoryHeapBase和MemoryBase都沒有提供同步對象,那么,AT和AF作為典型的數據生產者和消費者,如何正確協調二者生產和消費的步調呢? Android為順應民意,便創造出了這個CB對象,其主要目的就是協調和管理AT和AF二者數據生產和消費的步伐。先來看CB都管理些什么內容。它的聲明在AudioTrackShared.h中,而定義卻在AudioTrack.cpp中。 **AudioTrackShared.h::audio_track_cblk_t聲明** ~~~ struct audio_track_cblk_t { Mutex lock; Condition cv;//這是兩個同步變量,初始化的時候會設置為支持跨進程共享 /* 一塊數據緩沖同時被生產者和消費者使用,最重要的就是維護它的讀寫位置了。 下面定義的這些變量就和讀寫的位置有關,雖然它們的名字并不是那么直觀。 另外,這里提一個擴展問題,讀者可以思考一下: volatile支持跨進程嗎?要回答這個問題需要理解volatile、CPU Cache機制和共享內存的本質 */ volatile uint32_t user; //當前寫位置(即生產者已經寫到什么位置了) volatile uint32_t server; //當前讀位置 /* userBase和serverBase要和user及server結合起來用。 CB巧妙地通過上面幾個變量把一塊線性緩沖當做環形緩沖來使用,以后將單獨分析這個問題 */ uint32_t userBase; // uint32_t serverBase; void* buffers; //指向數據緩沖的首地址 uint32_t frameCount;//數據緩沖的總大小,以Frame為單位 uint32_t loopStart; //設置打點播放(即設置播放的起點和終點) uint32_t loopEnd; int loopCount;//循環播放的次數 volatile union { uint16_t volume[2]; uint32_t volumeLR; }; //和音量有關系,可以不管它 uint32_t sampleRate;//采樣率 uint32_t frameSize;//一單位Frame的數據大小 uint8_t channels;//聲道數 uint8_t flowControlFlag;//控制標志,見下文分析 uint8_t out; // AudioTrack為1,AudioRecord為0 uint8_t forceReady; uint16_t bufferTimeoutMs; uint16_t waitTimeMs; //下面這幾個函數很重要,后續會詳細介紹它們 uint32_t stepUser(uint32_tframeCount);//更新寫位置 bool stepServer(uint32_tframeCount);//更新讀位置 void* buffer(uint32_toffset) const;//返回可寫空間起始位置 uint32_t framesAvailable();//還剩多少空間可寫 uint32_t framesAvailable_l(); uint32_t framesReady();//是否有可讀數據 } ~~~ 關于CB對象,這里要專門講解一下其中flowControlFlag的意思: - 對于音頻輸出來說,flowControlFlag對應著underrun狀態,underrun狀態是指生產者提供數據的速度跟不上消費者使用數據的速度。這里的消費者指的是音頻輸出設備。由于音頻輸出設備采用環形緩沖方式管理,當生產者沒有及時提供新數據時,輸出設備就會循環使用緩沖中的數據,這樣就會聽到一段重復的聲音。這種現象一般被稱作“machinegun”。對于這種情況,一般的處理方法是暫停輸出,等數據準備好后再恢復輸出。 - 對于音頻輸入來說,flowControlFlag對于著overrun狀態,它的意思和underrun一樣,只是這里的生產者變成了音頻輸入設備,而消費者變成了Audio系統的AudioRecord。 * * * * * **說明**:目前這個參數并不直接和音頻輸入輸出設備的狀態有關系。它在AT和AF中的作用必須結合具體情況,才能分析。 * * * * * 圖7-4表示CB對象和它所駐留的共享內存間的關系: :-: ![](http://img.blog.csdn.net/20150802160453692?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-4 共享內存和CB的關系 * * * * * **注意**:CB實際是按照環形緩沖來處理數據讀寫的,所以user和server的真實作用還需要結合userBase和serverBase。圖7-4只是一個示意圖。 * * * * * 另外,關于CB,還有一個神秘的問題。先看下面這行代碼: ~~~ mCblk =static_cast<audio_track_cblk_t*>(cblk->pointer()); ~~~ 這看起來很簡單,但仔細琢磨會發現其中有一個很難解釋的問題: - cblk->pointer返回的是共享內存的首地址,怎么把audio_track_cblk_t對象塞到這塊內存中呢? 這個問題將通過對AudioFlinger的分析,得到答案。 * * * * * **說明**:關于audio_track_cblk_t的使用方式,后文會有詳細分析。 * * * * * (3)數據的Push or Pull 在JNI層的代碼中可以發現,在構造AudioTrack時,傳入了一個回調函數audioCallback。由于它的存在,導致了Native的AudioTrack還將創建另一個線程AudioTrackThread。它有什么用呢? 這個線程與外界數據的輸入方式有關系,AudioTrack支持兩種數據輸入方式: - Push方式:用戶主動調用write寫數據,這相當于數據被push到AudioTrack。MediaPlayerService一般使用這種這方式提供數據。 - Pull方式:AudioTrackThread將利用這個回調函數,以EVENT_MORE_DATA為參數主動從用戶那pull數據。ToneGenerator使用這種方式為AudioTrack提供數據。 這兩種方式都可以使用,不過回調函數除了EVENT_MORE_DATA外,還能表達其他許多意圖,這是通過回調函數的第一個參數來表明的。一起來看: **AudioTrack.h::event_type** ~~~ enum event_type { EVENT_MORE_DATA = 0, //表示AudioTrack需要更多數據 EVENT_UNDERRUN = 1,//這是Audio的一個術語,表示Audio硬件處于低負荷狀態 //AT可以設置打點播放,即設置播放的起點和終點,LOOP_END表示已經到達播放終點 EVENT_LOOP_END= 2, /* 數據使用警戒通知。該值可通過setMarkerPosition ()設置。 當數據使用超過這個值時,AT會且僅通知一次,有點像WaterMarker。 這里所說的數據使用,是針對消費者AF消費的數據量而言的 */ EVENT_MARKER = 3, /* 數據使用進度通知。進度通知值由setPositionUpdatePeriod()設置, 例如每使用500幀通知一次 */ EVENT_NEW_POS = 4, EVENT_BUFFER_END = 5 //數據全部被消耗 }; ~~~ 請看AudioTrackThread的線程函數threadLoop。 **AudioTrack.cpp** ~~~ bool AudioTrack::AudioTrackThread::threadLoop() { //mReceiver就是創建該線程的AudioTrack returnmReceiver.processAudioBuffer(this); } ~~~ **AudioTrack.cpp** ~~~ bool AudioTrack::processAudioBuffer(constsp<AudioTrackThread>& thread) { BufferaudioBuffer; uint32_t frames; size_twrittenSize; //處理underun的情況 if(mActive && (mCblk->framesReady() == 0)) { if(mCblk->flowControlFlag == 0) { mCbf(EVENT_UNDERRUN, mUserData, 0);//under run 通知 if (mCblk->server == mCblk->frameCount) { /* server是讀位置,frameCount是buffer中的數據總和 當讀位置等于數據總和時,表示數據都已經使用完了 */ mCbf(EVENT_BUFFER_END, mUserData, 0); } mCblk->flowControlFlag = 1; if (mSharedBuffer != 0) return false; } } // 循環播放通知 while(mLoopCount > mCblk->loopCount) { int loopCount = -1; mLoopCount--; if(mLoopCount >= 0) loopCount = mLoopCount; //一次循環播放完畢,loopCount表示還剩多少次 mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount); } if(!mMarkerReached && (mMarkerPosition > 0)) { if(mCblk->server >= mMarkerPosition) { //如果數據使用超過警戒值,則通知用戶 mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition); //只通知一次,因為該值被設為true mMarkerReached = true; } } if(mUpdatePeriod > 0) { while (mCblk->server >= mNewPosition) { /* 進度通知,但它不是以時間為基準,而是以幀數為基準的。 例如設置每500幀通知一次,假設消費者一次就讀了1500幀,那么這個循環會連續通知3次 */ mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition); mNewPosition += mUpdatePeriod; } } if(mSharedBuffer != 0) { frames = 0; } else{ frames = mRemainingFrames; } do { audioBuffer.frameCount = frames; //得到一塊可寫的緩沖 status_t err = obtainBuffer(&audioBuffer, 1); ...... //從用戶那pull數據 mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); writtenSize = audioBuffer.size; ...... if(writtenSize > reqSize) writtenSize = reqSize; //PCM8數據轉PCM16 ....... audioBuffer.size = writtenSize; audioBuffer.frameCount = writtenSize/mCblk->frameSize; frames -= audioBuffer.frameCount; releaseBuffer(&audioBuffer);//寫完畢,釋放這塊緩沖 } while(frames);  ...... returntrue; } ~~~ 關于obtainBuffer和releaseBuffer,后面再分析。這里有一個問題值得思考: - 用例會調用write函數寫數據,AudioTrackThread的回調函數也讓我們提供數據。難道我們同時在使用Push和Pull模式? 這太奇怪了!來查看這個回調函數的實現,了解一下究竟是怎么回事。該回調函數是通過set調用傳入的,對應的函數是audioCallback。 **android_media_AudioTrack.cpp** ~~~ static void audioCallback(int event, void* user,void *info) { if(event == AudioTrack::EVENT_MORE_DATA) { //很好,沒有提供數據,也就是說,雖然AudioTrackThread通知了EVENT_MORE_DATA, //但是我們并沒有提供數據給它 AudioTrack::Buffer* pBuff = (AudioTrack::Buffer*)info; pBuff->size = 0; } ...... ~~~ 懸著的心終于放下來了,還是老老實實地看Push模式下的數據輸入吧。 2. write輸入數據 write函數涉及Audio系統中最重要的關于數據如何傳輸的問題,在分析它的時候,不妨先思考一下它會怎么做。回顧一下我們已了解的信息: - 有一塊共享內存。 - 有一個控制結構,里邊有一些支持跨進程的同步變量。 有了這些東西,write的工作方式就非常簡單了: - 通過共享內存傳遞數據。 - 通過控制結構協調生產者和消費者的步調。 >[info] **重點強調**:帶著問題和思考來分析代碼相當于“智取”,它比一上來就直接扎入源碼的“強攻”要高明得多。希望我們能掌握這種思路和方法。 好了,現在開始分析write,看看它的實現是不是如所想的那樣。 **AudioTrack.cpp** ~~~ ssize_t AudioTrack::write(const void* buffer,size_t userSize) { if(mSharedBuffer != 0) return INVALID_OPERATION; if(ssize_t(userSize) < 0) { returnBAD_VALUE; } ssize_t written = 0; constint8_t *src = (const int8_t *)buffer; BufferaudioBuffer; // Buffer是一個輔助性的結構 do { //以幀為單位 audioBuffer.frameCount = userSize/frameSize(); //obtainBuffer從共享內存中得到一塊空閑的數據塊 status_terr = obtainBuffer(&audioBuffer, -1); ...... size_t toWrite; if(mFormat == AudioSystem::PCM_8_BIT && !(mFlags &AudioSystem::OUTPUT_FLAG_DIRECT)) { //PCM8數據轉PCM16 }else { //空閑數據緩沖的大小是audioBuffer.size。 //地址在audioBuffer.i8中,數據傳遞通過memcpy完成 toWrite = audioBuffer.size; memcpy(audioBuffer.i8, src, toWrite); src += toWrite; } userSize -= toWrite; written += toWrite; //releaseBuffer更新寫位置,同時會觸發消費者 releaseBuffer(&audioBuffer); }while (userSize); returnwritten; } ~~~ 通過write函數,會發現數據的傳遞其實是很簡單的memcpy,但消費者和生產者的協調,則是通過obtainBuffer與releaseBuffer來完成的。現在來看這兩個函數。 3. obtainBuffer和releaseBuffer 這兩個函數展示了做為生產者的AT和CB對象的交互方法。先簡單看看,然后把它們之間交互的流程記錄下來,以后在CB對象的單獨分析部分,我們再來做詳細介紹。 **AudioTrack.cpp** ~~~ status_t AudioTrack::obtainBuffer(Buffer*audioBuffer, int32_t waitCount) { intactive; status_t result; audio_track_cblk_t* cblk = mCblk; ...... //①調用framesAvailable,得到當前可寫的空間大小 uint32_t framesAvail = cblk->framesAvailable(); if(framesAvail == 0) { ...... //如果沒有可寫空間,則要等待一段時間 result= cblk->cv.waitRelative(cblk->lock,milliseconds(waitTimeMs)); ...... } cblk->waitTimeMs = 0; if(framesReq > framesAvail) { framesReq = framesAvail; } //user為可寫空間起始地址 uint32_t u = cblk->user; uint32_tbufferEnd = cblk->userBase + cblk->frameCount; if (u+ framesReq > bufferEnd) { framesReq = bufferEnd - u; } ...... //②調用buffer,得到可寫空間的首地址 audioBuffer->raw = (int8_t *)cblk->buffer(u); active= mActive; returnactive ? status_t(NO_ERROR) : status_t(STOPPED); } ~~~ obtainBuffer的功能,就是從CB管理的數據緩沖中得到一塊可寫空間,而releaseBuffer,則是在使用完這塊空間后更新寫指針的位置。 **AudioTrack.cpp** ~~~ void AudioTrack::releaseBuffer(Buffer*audioBuffer) { audio_track_cblk_t* cblk = mCblk; cblk->stepUser(audioBuffer->frameCount);// ③調用stepUser更新寫位置 } ~~~ obtainBuffer和releaseBuffer與CB交互,一共會有三個函數調用,如下所示: - framesAvailable判斷是否有可寫空間。 - buffer得到寫空間起始地址。 - stepUser更新寫位置。 請記住這些流程,以后在分析CB時會發現它們有重要作用。 4. delete AudioTrack 到這里,AudioTrack的使命就進入倒計時階段了。來看在它生命的最后還會做一些什么工作。 **AudioTrack.cpp** ~~~ AudioTrack::~AudioTrack() { if(mStatus == NO_ERROR) { stop();//調用stop if(mAudioTrackThread != 0) { //通知AudioTrackThread退出 mAudioTrackThread->requestExitAndWait(); mAudioTrackThread.clear(); } mAudioTrack.clear(); //將殘留在IPCThreadState 發送緩沖區的信息發送出去 IPCThreadState::self()->flushCommands(); } } ~~~ 如果不調用stop,析構函數也會先調用stop,這個做法很周到。 **AudioTrack.cpp** ~~~ void AudioTrack::stop() { sp<AudioTrackThread> t = mAudioTrackThread; if (t!= 0) { t->mLock.lock(); } if(android_atomic_and(~1, &mActive) == 1) { mCblk->cv.signal(); /* mAudioTrack是IAudioTrack類型,其stop的最終處理在AudioFlinger端 */ mAudioTrack->stop(); //清空循環播放設置 setLoop(0, 0, 0); mMarkerReached = false; if (mSharedBuffer != 0) { flush(); } if(t != 0) { t->requestExit();//請求退出AudioTrackThread }else { setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); } } if (t!= 0) { t->mLock.unlock(); } } ~~~ stop的工作比較簡單,就是調用IAudioTrack的stop,并且還要求退出回調線程。要重點關注IAudioTrack的stop函數,這個將做為AT和AF交互流程中的一個步驟來分析。
                  <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>

                              哎呀哎呀视频在线观看