Android音頻系統之AudioFlinger(三)


http://blog.csdn.net/xuesen_lin/article/details/8805091

1.1.1 PlaybackThread的循環主體

當一個PlaybackThread進入主循環后(threadLoop),音頻事務就正式開啟了。仔細觀察的話,我們會發現這個循環中會不斷地調用以“threadLoop_”開頭的若干接口,比如threadLoop_mix、threadLoop_sleepTime、threadLoop_standby等等。以這樣的前綴開頭,是因為這些函數都是在threadLoop這個主體里被調用的,可以說代表了這個PlaybackThread所需要完成的各個操作步驟。

從上一小節可以了解到,當程序執行到PlaybackThread::onFirstRef時會去真正啟動一個線程承載運行threadLoop,接下來我們具體看下這個循環體的處理流程。

bool AudioFlinger::PlaybackThread::threadLoop()

{ …        

    while (!exitPending())/*Step1.*/

    {   …    

        processConfigEvents();/*Step 2. */

        { /*把這段代碼框起來的目的是限制自動鎖變量_l的生命期,

           從而靈活地實現了自動鎖的控制范圍*/

          Mutex::Autolock  _l(mLock);

                                 …

          /*Step 3. Standby判斷*/

          if(CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime)|| mSuspended> 0)) {                       

                if (!mStandby){

                   threadLoop_standby();//調用設備的

                    mStandby =true;

                   mBytesWritten = 0;

                }

                                                 …

           }

                                  /*Step 4.*/

            mMixerStatus =prepareTracks_l(&tracksToRemove);

                                   …

        }

                                /*Step5.*/

        if(CC_LIKELY(mMixerStatus == MIXER_TRACKS_READY)) {

            threadLoop_mix();

        } else {

           threadLoop_sleepTime();

        }

                   …

        /*Step 6.*/

        if (sleepTime == 0) {

           threadLoop_write(); //不需要休眠,有數據要寫

                                   …

            mStandby = false;

        } else {

            usleep(sleepTime);//進入休眠,時間長短是sleepTime

        }

                    /*Step 7.*/

        threadLoop_removeTracks(tracksToRemove);//移除相關Track

       tracksToRemove.clear();…

    }//while (!exitPending())結束

                …

    releaseWakeLock();

    return false;

}

Step1@ PlaybackThread::threadLoop, 循環的條件是!exitPending()為true。這個函數屬於Thread類,它主要通過判斷內部變量mExitPending的值來得出是否要結束線程。變量mExitPending在Thread初始化時為fasle,如果后面有人通過requestExit()、requestExitAndWait等等來請求退出,這個值就會改變,從而使得PlaybackThread結束循環。

Step2@ PlaybackThread::threadLoop, 處理config事件。當有配置改變的事件發生時,可以通過sendConfigEvent來通知PlaybackThread。這個函數將把事件添加到mConfigEvents全局變量中,以供processConfigEvents進行處理。配置事件包括如下幾種:

    enum io_config_event {

        OUTPUT_OPENED,//Output打開

        OUTPUT_CLOSED,//Output關閉

        OUTPUT_CONFIG_CHANGED,//Output配置改變

        INPUT_OPENED, //Input打開

        INPUT_CLOSED, //Input關閉

        INPUT_CONFIG_CHANGED,//Input配置改變

        STREAM_CONFIG_CHANGED,//Stream配置改變

        NUM_CONFIG_EVENTS

    };

Step3@ PlaybackThread::threadLoop,判斷當前是否符合Standby的條件,如果是的話就調用threadLoop_standby。這個函數最終還是通過HAL層的接口來實現,如下:

mOutput->stream->common.standby(&mOutput->stream->common);

Step4@ PlaybackThread::threadLoop, 進行數據准備,prepareTracks_l這個函數非常長,我們先用偽代碼的形式整理一下它所做的工作,如下所示:

AudioFlinger::PlaybackThread::mixer_stateAudioFlinger::MixerThread::prepareTracks_l(…)

{

                /*Step 1. 當前活躍的Track數量*/

    size_t  count = mActiveTracks.size();

 

    /*Step 2. 循環處理每個Track,這是函數的核心*/

                for (size_t i=0; i<count ; i++) {

        Track* track =mActiveTracks[i];//偽代碼沒有考慮強指針

 

        /*Step 3. FastTrack下的處理*/

        if(track is FastTrack)

        {

                                                //dosomething;

        }

        /*Step 4. 准備數據,分為以下幾個小部來完成*/

                                audio_track_cblk_t*cblk = track->cblk(); //Step 4.1 數據塊准備

 

        /*Step 4.2 要回放音頻前,至少需要准備多少幀數據?*/

        uint32_t  minFrames = 1;//初始化

          //具體計算minFrames…

   

        /*Step 4.3 如果數據已經准備完畢*/

          //調整音量

          //其它參數設置

    }//for循環結束

   

     /*Step 5. 后續判斷*/

       //返回結果,指明當前狀態是否已經ready

}

現在我們針對上面的步驟來做“填空”。

Step1@ MixerThread::prepareTracks_l, mActiveTracks的數據類型是SortedVector,用於記錄當前活躍的Track。它會隨着新的AudioTrack的加入而擴大,也會在必要的情況下(AudioTrack工作結束、或者出錯等等)remove相應的Track。

Step2&3@MixerThread::prepareTracks_l, 循環的條件就是要逐個處理該PlaybackThread中包含的Track。假如當前是一個FastTrack,我們還要做一些其它准備,這里就暫時不去涉及具體細節了。

Step4@ MixerThread::prepareTracks_l, 這一步是准備工作中最重要的,那就是緩沖數據。在學習代碼細節前,我們先來了解數據傳輸時容易出現的underrun情況。

什么是BufferUnderrun呢?

當兩個設備或進程間形成“生產者-消費者”關系時,如果生產的速度不及消費者消耗的速度,就會出現Underrun。以音頻回放為例,此時用戶聽到的聲音就可能是斷斷續續的,或者是重復播放當前buffer中的數據(取決於具體的實現)。

如何避免這種異常的發生?這也是Step4所要解決的問題,以下分為幾個小步驟來看AudioFlinger是如何做到的。

Ø  Step4.1,取得數據塊

  audio_track_cblk_t*cblk = track->cblk();

  關於audio_track_cblk_t的更多描述,可以參見后面數據流小節。

Ø  Step4.2 計算正確回放音頻所需的最少幀數,初始值為1。

                                uint32_tminFrames = 1;

                                if((track->sharedBuffer() == 0) && !track->isStopped() &&!track->isPausing() &&

               (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {

            if(t->sampleRate() == (int)mSampleRate) {

                minFrames = mNormalFrameCount;

            } else {

                minFrames =(mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;               

                minFrames +=mAudioMixer->getUnreleasedFrames(track->name());                

               ALOG_ASSERT(minFrames <= cblk->frameCount);

            }

         }

當track->sharedBuffer()為0時,說明AudioTrack不是STATIC模式的,否則數據就是一次性傳送的,可以參見AudioTrack小節的描述。全局變量mSampleRate 是通過mOutput->stream->common.get_sample_rate獲得的,它是由HAL提供的,代表的是設備的Sampling rate。

如果兩者一致的話,就采用mNormalFrameCount,這個值在readOutputParameters函數中進行初始化。如果兩者不一致的話,就要預留多余的量做rounding(+1)和interpolation(+1)。另外,還需要考慮未釋放的空間大小,也就是getUnreleasedFrames得到的。得出的minFrames必需小於數據塊的總大小,因而最后有個ASSERT。通常情況下frameCount分配的是一個buffer的兩倍,可以參見AudioTrack小節的例子。

Ø  Step4.3 數據是否准備就緒了?

上一步我們計算出了數據的最小幀值,即minFrames,接下來就該判斷目前的情況是否符合這一指標了,代碼如下所示:

                    if ((track->framesReady() >=minFrames) && track->isReady() &&!track->isPaused()&& !track->isTerminated())

        {//數據准備就緒,並處於ready狀態

            mixedTracks++; //需要mix的Track數量增加1

            …

            /*計算音量值*/

            uint32_t vl, vr,va; //三個變量分別表示左、右聲道、Aux level音量

            if(track->isMuted() || track->isPausing()||mStreamTypes[track->streamType()].mute) {

                vl = vr = va =0; //當靜音時,變量直接賦0

                if (track->isPausing()) {

                   track->setPaused();

                }

            } else {               

                /*這里獲得的是針對每個stream類型設置的音量值,也就是后面“音量調節”小節里最

                  后執行到的地方,在這里就起到作用了*/

                                                  float typeVolume =mStreamTypes[track->streamType()].volume;               

                float v =masterVolume * typeVolume; //主音量和類型音量的乘積

                uint32_t  vlr = cblk->getVolumeLR(); //這里得到的vlr必須經過驗證是否在合理范圍內

                vl = vlr &0xFFFF; //vlr的高低位分別表示vr和vl

                vr = vlr>> 16;

                if (vl >MAX_GAIN_INT) { //對vl進行合理值判斷

                   ALOGV("Track left volume out of range: %04X", vl);

                    vl =MAX_GAIN_INT;

                }

                if (vr >MAX_GAIN_INT) {//對vr進行合理值判斷

                   ALOGV("Track right volume out of range: %04X", vr);

                    vr =MAX_GAIN_INT;

                }

                // now applythe master volume and stream type volume

                vl =(uint32_t)(v * vl) << 12;

                vr =(uint32_t)(v * vr) << 12;

                uint16_tsendLevel = cblk->getSendLevel_U4_12();

                // send levelcomes from shared memory and so may be corrupt

                if (sendLevel> MAX_GAIN_INT) {

                    ALOGV("Track send level out of range:%04X", sendLevel);

                    sendLevel= MAX_GAIN_INT;

                }

                va =(uint32_t)(v * sendLevel);

            } …          

           mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void*)vl);

           mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void*)vr);

           mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void*)va);

            …           

        } else {//數據未准備就緒,略過。。。

  對於音量的設置還有很多細節,大家有興趣的可以深入研究下。在得到vl、vr和va的值后,還需要把它們應用到AudioMixer中去,不過在prepareTracks_l中還只是調用mAudioMixer->setParameter設置了這些參數,真正的實現是在threadLoop_mix中,我們后面會講到這個函數。

Step5@ MixerThread::prepareTracks_l, 通過對每個Track執行上述的處理后,最后要返回一個結果,這通常取決於:

①是否有activetrack

②active track的數據是否已經准備就緒

返回的最終值將影響到threadLoop的下一步操作。

 

完成了prepareTracks_l的分析,我們再回到前面的threadLoop。

Step5@ PlaybackThread::threadLoop, 如果上一步的數據准備工作已經完成(即返回值是MIXER_TRACKS_READY),就開始進行真正的混音操作,即threadLoop_mix,否則會休眠一定的時間——如此循環往復直到退出循環體。

void AudioFlinger::MixerThread::threadLoop_mix()

{

    int64_t pts;

                …

   mAudioMixer->process(pts);

    …

}

這樣就進入AudioMixer的處理了,我們放在下一小節做統一分析。

假如數據還沒有准備就緒,那么AudioFlinger將調用threadLoop_sleepTime來計算需要休眠多長時間(變量sleepTime),並在threadLoop主循環的末尾(在remove track之前)執行usleep進入休眠。

Step6@ PlaybackThread::threadLoop, 將數據寫到HAL中,從而逐步寫入到硬件設備中。

void AudioFlinger::PlaybackThread::threadLoop_write()

{

    mLastWriteTime =systemTime();

    mInWrite = true;

    int bytesWritten;

    if (mNormalSink != 0) {

                   …

        ssize_t framesWritten= mNormalSink->write(mMixBuffer, count);

        …

    } else {

        bytesWritten =(int)mOutput->stream->write(mOutput->stream, mMixBuffer,mixBufferSize);

    }

    if (bytesWritten > 0)mBytesWritten += mixBufferSize;

    mNumWrites++;

    mInWrite = false;

}

分為兩種情況:

Ø  如果是采用了NBAIO(Non-blocking AudioI/O),即mNormalSink不為空,則通過它寫入HAL

Ø  否則使用普通的AudioStreamOut(即mOutput變量)將數據輸出

 

Step7@ PlaybackThread::threadLoop, 移除tracksToRemove中指示的Tracks。是否移除一個Track是在prepareTracks_l中判斷中,可以概括為以下幾種情況:

Ø  對於Fast Track,如果它的狀態(mState)是STOPPING_2、PAUSED、TERMINATED、STOPPED、FLUSHED,或者狀態是ACTIVE但underrun的次數超過限額(mRetryCount),則會被加入tracksToRemove列表中

Ø  當前的Track數據未准備就緒的情況下,且是STATICTRACK或者已經停止/暫停,也會被加入tracksToRemove列表中

在tracksToRemove列表中的Track,與其相關的output將收到stop請求(由AudioSystem::stopOutput發起)。

關於AudioFlinger中與AudioTrack、AudioPolicyService有交互的部分,我們還將在后續小節進行闡述。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM