<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國際加速解決方案。 廣告
                DuplicatingThread需要與藍牙結合起來使用,它的存在與Audio硬件結構息息相關。讀者可參考圖7-12“智能手機硬件架構圖”來理解。當一份數據同時需要發送給DSP和藍牙A2DP設備時,DuplicatingThread就派上用場了。在分析DuplicatingThread前,還是應該了解一下它的來龍去脈。 1. DuplicatingThread的來歷 DuplicatingThread和藍牙的A2DP設備有關系。可先假設有一個藍牙立體聲耳機已經連接上了,接著從setDeviceConnectionState開始分析,代碼如下所示: **AudioPolicyManagerBase.cpp** ~~~ status_t AudioPolicyManagerBase::setDeviceConnectionState( AudioSystem::audio_devicesdevice, AudioSystem::device_connection_state state, const char *device_address) { ...... switch (state) { case AudioSystem::DEVICE_STATE_AVAILABLE: mAvailableOutputDevices |= device; #ifdef WITH_A2DP if (AudioSystem::isA2dpDevice(device)) { //專門處理A2DP設備的連接 status_t status = handleA2dpConnection(device, device_address); } #endif ...... ~~~ 對于A2DP設備,有專門的函數handleA2dpConnection處理,代碼如下所示: **AudioPolicyManagerBase.cpp** ~~~ status_tAudioPolicyManagerBase::handleA2dpConnection( AudioSystem::audio_devicesdevice, const char*device_address) { AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); outputDesc->mDevice= device; //先為mA2dpOutput創建一個MixerThread,這個和mHardwareOutput一樣 mA2dpOutput =mpClientInterface->openOutput(&outputDesc->mDevice, &outputDesc->mSamplingRate, &outputDesc->mFormat, &outputDesc->mChannels, &outputDesc->mLatency, outputDesc->mFlags); if (mA2dpOutput) { /* a2dpUsedForSonification永遠返回true,表示屬于SONIFCATION策略的音頻流聲音需要 同時從藍牙和DSP中傳出。屬于SONIFCATION策略的音頻流類型可查看前面關于getStrategy的 分析,來電鈴聲、短信通知等屬于這一類 */ if(a2dpUsedForSonification()) { /* 創建一個DuplicateOutput,注意它的參數,第一個是藍牙MixerThread 第二個是DSPMixerThread */ mDuplicatedOutput = mpClientInterface->openDuplicateOutput( mA2dpOutput, mHardwareOutput); } if(mDuplicatedOutput != 0 || !a2dpUsedForSonification()) { if (a2dpUsedForSonification()) { //創建一個AudioOutputDescriptor對象 AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(); dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput); dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput); ...... //保存mDuplicatedOutput和dupOutputDesc鍵值對 addOutput(mDuplicatedOutput, dupOutputDesc); ...... } } } ...... ~~~ 這里,最重要的函數是openDuplicateOutput。它和openOutput一樣,最終的處理都是在AF中。去那里看看,代碼如下所示: **AudioFlinger.cpp** ~~~ int AudioFlinger::openDuplicateOutput(intoutput1, int output2) { Mutex::Autolock_l(mLock); //output1對應藍牙的MixerThread MixerThread*thread1 = checkMixerThread_l(output1); //output2對應DSP的MixerThread MixerThread *thread2 = checkMixerThread_l(output2); //①創建DuplicatingThread,注意它第二個參數使用的,是代表藍牙的MixerThread DuplicatingThread *thread = new DuplicatingThread(this, thread1,++mNextThreadId); //②加入代表DSP的MixerThread thread->addOutputTrack(thread2); mPlaybackThreads.add(mNextThreadId, thread); returnmNextThreadId;//返回DuplicatingThread的索引 } ~~~ 從現在起,MixerThread要簡寫為MT,而DuplicatingThread則簡寫為DT。 OK,這里面有兩個重要的函數調用,一起來看。 2. DuplicatingThread和OutputTrack 先看DT的構造函數,代碼如下所示: **AudioFlinger.cpp** ~~~ AudioFlinger::DuplicatingThread::DuplicatingThread(constsp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread*mainThread,int id) : MixerThread(audioFlinger,mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX) { //DT是MT的派生類,所以先要完成基類的構造,還記得MT的構造嗎?它會創建一個AudioMixer對象 mType =PlaybackThread::DUPLICATING; //把代表DSP的MT加入進來,咱們看看 addOutputTrack(mainThread); } ~~~ **AudioFlinger.cpp** ~~~ voidAudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) { intframeCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); //構造一個OutputTrack,它的第一個參數是MT OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, this, mSampleRate, mFormat, mChannelCount,frameCount); if(outputTrack->cblk() != NULL) { thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f); //把這個outputTrack加入到mOutputTracks數組保存 mOutputTracks.add(outputTrack); updateWaitTime(); } } ~~~ 此時,當下面兩句代碼執行完: ~~~ DuplicatingThread *thread = newDuplicatingThread(this, thread1,++mNextThreadId); thread->addOutputTrack(thread2); ~~~ DT分別構造了兩個OutputTrack,一個對應藍牙的MT,一個對應DSP的MT。現在來看OutputTrack為何方神圣,代碼如下所示: **AudioFlinger.cpp** ~~~ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( const wp<ThreadBase>& thread, DuplicatingThread*sourceThread, uint32_t sampleRate, int format,int channelCount,int frameCount) :Track(thread,NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),//最后這個參數為NULL mActive(false),mSourceThread(sourceThread) { /* OutputTrack從Track派生,所以需要先調用基類的構造,還記得Track構造函數 中的事情嗎?它會創建一塊內存,至于是不是共享內存,由Track構造函數的最后一個參數決定。 如果該值為NULL,表示沒有客戶端參與,則會在本進程內創建一塊內存,這塊內存的結構如 圖7-4所示,前邊為CB對象,后邊為數據緩沖 */ //下面的這個thread對象為MT PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); if(mCblk != NULL) { mCblk->out = 1;//表示DT將往MT中寫數據 //和前面所分析的AT、AF中的處理何其相似! mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->volume[0] = mCblk->volume[1] = 0x1000; mOutBuffer.frameCount = 0; //把這個Track加到MT的Track中 playbackThread->mTracks.add(this); } ~~~ 明白了嗎?圖7-16表示的是openDuplicateOutput的結果: :-: ![](http://img.blog.csdn.net/20150802161026784?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-16 openDuplicateOutput的結果示意圖 圖7-16說明(以藍牙MT為例): - 藍牙MT的Track中有一個成員為OutputTrack0。 - DT的mOutputTracks也有一個成員指向OutputTrack0。這就好像DT是MT的客戶端一樣,它和前面分析的AT是AF的客戶端類似。 - 紅色部分代表數據傳遞用的緩沖。 3. DT的客戶端AT DT是從MT中派生的,根據AP和AT的交互流程,當AT創建的流類型對應策略為SONIFACATION時,它會從AP中得到代表DT的線程索引號。由于DT沒有重載createTrack_l,所以這個過程也會創建一個Track對象(和MT創建Track對象一樣)。此時的結果,將導致圖7-16變成圖7-17。 :-: ![](http://img.blog.csdn.net/20150802160934318?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-17 有AT的DT全景圖 圖7-17把DT的工作方式表達得非常清晰了。一個DT配合兩個OutputTrack中的進程內緩沖,把來自AT的數據原封不動地發給藍牙MT和DSP MT,這簡直就是個數據中繼器!。不過俗話說得好,道理雖簡單,實現卻復雜。來看DT是如何完成這一復雜而艱巨的任務的吧。 4. DT的線程函數 DT的線程函數代碼如下所示: **AudioFlinger.cpp** ~~~ boolAudioFlinger::DuplicatingThread::threadLoop() { int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; uint32_t mixerStatus = MIXER_IDLE; nsecs_t standbyTime = systemTime(); size_tmixBufferSize = mFrameCount*mFrameSize; SortedVector< sp<OutputTrack> > outputTracks; while(!exitPending()) { processConfigEvents(); mixerStatus = MIXER_IDLE; { ...... //處理配置請求,和MT處理一樣 const SortedVector< wp<Track> >& activeTracks =mActiveTracks; for (size_t i = 0; i < mOutputTracks.size(); i++) { outputTracks.add(mOutputTracks[i]); } //如果AT的Track停止了,則需要停止和MT共享的OutputTrack ifUNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || mSuspended) { if (!mStandby) { for (size_t i = 0; i <outputTracks.size(); i++) { outputTracks[i]->stop(); } mStandby = true; mBytesWritten = 0; } ...... //DT從MT派生,天然具有混音的功能,所以這部分功能和MT一致 mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); } if(LIKELY(mixerStatus == MIXER_TRACKS_READY)) { //outputsReady將檢查OutputTracks對應的MT狀態 if (outputsReady(outputTracks)) { mAudioMixer->process(curBuf);//使用AudioMixer對象混音 } else { memset(curBuf, 0, mixBufferSize); } sleepTime = 0; writeFrames = mFrameCount; } ...... if (sleepTime == 0) { standbyTime = systemTime() +kStandbyTimeInNsecs; for (size_t i = 0; i < outputTracks.size(); i++) { //將混音后的數據寫到outputTrack中 outputTracks[i]->write(curBuf, writeFrames); } mStandby = false; mBytesWritten += mixBufferSize; }else { usleep(sleepTime); } tracksToRemove.clear(); outputTracks.clear(); } returnfalse; } ~~~ 現在,來自遠端進程AT的數據已得到了混音,這一份混音后的數據還將通過調用OutputTrack的write完成DT到其他兩個MT的傳輸。注意,這里除了AT使用的Track外,還有DT和兩個MT共享的OutputTrack。AT調用的start,將導致DT的Track加入到活躍數組中,但另外兩個OutputTrack還沒調用start。這些操作又是在哪里做的呢?來看write函數: **AudioFlinger.cpp** ~~~ boolAudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames) { //注意,此處的OutputTrack是DT和MT共享的 Buffer *pInBuffer; BufferinBuffer; uint32_t channels = mCblk->channels; booloutputBufferFull = false; inBuffer.frameCount = frames; inBuffer.i16 = data; uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs(); if(!mActive && frames != 0) { //如果此Track沒有活躍,則調用start激活 start(); ...... } /* 現在,AF中的數據傳遞有三個線程:一個DT,兩個MT。MT作為DT的二級消費者, 可能由于某種原因來不及消費數據,所以DT中提供了一個緩沖隊列mBufferQueue, 把MT來不及消費的數據保存在這個緩沖隊列中。注意這個緩沖隊列容納的臨時緩沖 個數是有限制的,其限制值由kMaxOverFlowBuffers控制,初始化為10個 */ while(waitTimeLeftMs) { //先消耗保存在緩沖隊列的數據 if(mBufferQueue.size()) { pInBuffer = mBufferQueue.itemAt(0); }else { pInBuffer = &inBuffer; } ...... //獲取可寫緩沖,下面這句代碼是否和AT中對應的代碼很相似? if(obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS){ ...... break; } uint32_toutFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount: pInBuffer->frameCount; //將數據拷貝到DT和MT共享的那塊緩沖中去 memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); //更新寫位置 mCblk->stepUser(outFrames); pInBuffer->frameCount-= outFrames; pInBuffer->i16 += outFrames * channels; mOutBuffer.frameCount -= outFrames; mOutBuffer.i16 += outFrames * channels; ...... }//while 結束 if(inBuffer.frameCount) { sp<ThreadBase> thread = mThread.promote(); if(thread != 0 && !thread->standby()) { if (mBufferQueue.size() < kMaxOverFlowBuffers) { pInBuffer = new Buffer; pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; pInBuffer->frameCount = inBuffer.frameCount; pInBuffer->i16 = pInBuffer->mBuffer; //拷貝舊數據到新的臨時緩沖 memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount *channels * sizeof(int16_t)); //保存這個臨時緩沖 mBufferQueue.add(pInBuffer); } } } //如果數據全部寫完 if(pInBuffer->frameCount == 0) { if (mBufferQueue.size()) { mBufferQueue.removeAt(0); delete [] pInBuffer->mBuffer; delete pInBuffer;//釋放緩沖隊列對應的數據緩沖 } else { break; } } } ...... return outputBufferFull; } ~~~ 數據就這樣從AT通過DT的幫助,傳輸到藍牙的MT和DSP的MT中了。這種方式繼數據傳輸比直接使用MT傳輸要緩慢。 到這里,對DT的講解就告一段落了。本人覺得,DT的實現是AF代碼中最美妙的地方,多學習這些優秀代碼,有助于提高學習者的水平。 >[info] **說明**:DT還有別的一些細節本書中沒有涉及,讀者可以結合自己的情況進行分析和理解。
                  <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>

                              哎呀哎呀视频在线观看