Android 系統(4.4 KitKat)使用OffloadThread播放音樂的數據流程分析

《原創作品,禁止轉載》


Google在Android4.4音頻系統中新增了使用OffloadThread來播放音樂的流程,此文僅記錄自己對Offload的一些分析與理解,方便以後查閱,如文中有誤謹請網友提出,謝謝。

1. OffloadThread的繼承關係如圖:

                                                                                    

2.Native AudioTrack在向AudioFlinger創建Track時,首先調用AudioSystem::getOutput()確定要在哪種放音線程中創建Track,此過程最終調用的是AudioFlinger中的getOutput函數:

/frameworks/av/services/audioflinger/AudioFlinger.cpp AudioFlinger::getOutput:

outHwDev = findSuitableHwDev_l(module,*pDevices);

audio_hw_device_t *hwDevHal =outHwDev->hwDevice();

audio_io_handle_t id = nextUniqueId();

……

status_t status = hwDevHal->open_output_stream(hwDevHal,id,*pDevices, (audio_output_flags_t)flags, &config,&outStream);

if (status == NO_ERROR && outStream!= NULL) {

                   //與HAL層交互的對象

       AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);

 

       if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {

//參數flag爲AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD

//創建OffloadThread

           thread = new OffloadThread(this, output, id, *pDevices);

       } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||

           (config.format != AUDIO_FORMAT_PCM_16_BIT) ||

           (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {

           thread = new DirectOutputThread(this, output, id, *pDevices);

       } else {

                            //創建MiexrThread,一般情況下使用的thread

           thread = new MixerThread(this, output, id, *pDevices);

       }

}

mPlaybackThreads.add(id, thread); //添加到AudioFlinger維護的mPlaybackThreads中

return id;

//end AudioFlinger::getOutput



此後Native AudioTrack會根據上述的audio_io_handle_t(用於找到mPlaybackThreads中的thread)繼續調用AudioFlinger::createTrack函數創建Track並添加到thread的mTracks中。


上述過程比較關鍵的地方:hwDevHal->open_output_stream(hwDevHal,id, *pDevices,(audio_output_flags_t)flags,&config,&outStream); 此open_output_stream函數最終調用的是/hardware/qcom/audio/hal/audio_hw.c(以Googlenexus5-hammerhead的源碼爲例)中的adev_open_output_stream函數,標準函數指針定義在/hardware/libhardware/include/hardware/audio.h中。在adev_open_output_stream函數中:

if (out->flags &AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {

out->stream.set_callback = out_set_callback;

       if (flags &AUDIO_OUTPUT_FLAG_NON_BLOCKING)

           out->non_blocking = 1;

 

       out->send_new_metadata = 1;

       create_offload_callback_thread(out); //HAL層爲offload單獨創建了一個線程,用於執行回調函數,後面會繼續分析

}


3.OffloadThread對象的建立過程:

根據OffloadThread的繼承關係,newOffloadThread()時,先從父類開始:PlaybackThread-> DirectOutputThread-> OffloadThread;

注意在PlaybackThread構造函數中,會調用readOutputParameters函數執行相關參數初始化工作,其中:

<p></p><p>if ((mOutput->flags &AUDIO_OUTPUT_FLAG_NON_BLOCKING) &&</p><p>           (mOutput->stream->set_callback != NULL)) { //上述在HAL層adev_open_output_stream函數中已對set_callback賦值</p><p>       if (mOutput->stream->set_callback(mOutput->stream,AudioFlinger::PlaybackThread::asyncCallback,this) == 0) {</p><p><span style="white-space:pre">	</span>   //執行HAL層out_set_callback函數,將PlaybackThread::asyncCallback註冊到out->offload_callback中,</p><p><span style="white-space:pre">	</span>   //此後HAL層即可通過此回調接口調用PlaybackThread的asyncCallback函數;</p><p>           mUseAsyncWrite = true;</p><p>           mCallbackThread = new AudioFlinger::AsyncCallbackThread(this); </p><p><span style="white-space:pre">	</span>   //在Native層也爲offload創建一個單獨的線程,</p><p><span style="white-space:pre">	</span>   //offload真心高大上,涉及到4個線程的交互,後面會具體分析;</p><p>        }</p><p>}</p>


OffloadThread對象創建過程基本上就這樣,之後執行PlaybackThread的onFirstRef(){run(mName,ANDROID_PRIORITY_URGENT_AUDIO);}開始線程循環。


4.接着分析OffloadThread與HAL層交互傳遞數據的過程。

PlaybackThread線程循環thread_Loop中:

bool AudioFlinger::PlaybackThread::threadLoop()
{
    ……
    while (!exitPending())
    {
	……
        { // scope for mLock

            Mutex::Autolock _l(mLock);
            ……
            if (mSignalPending) {//Offload下不成立
                // A signal was raised while we were unlocked
                mSignalPending = false;
            } else if (waitingAsyncCallback_l()) { //<span style="color:#ff0000;">(1)</span>
                if (exitPending()) {
                    break;
                }
                releaseWakeLock_l();
                ALOGE("wait async completion");
                mWaitWorkCV.wait(mLock);
                ALOGE("async completion/wake");
                acquireWakeLock_l();
                standbyTime = systemTime() + standbyDelay;
                sleepTime = 0;
                continue;
            }
<span style="white-space:pre">	</span>    ……
        }

        if (mBytesRemaining == 0) { //<span style="color:#ff0000;">(2)</span>
            mCurrentWriteLength = 0;
            if (mMixerStatus == MIXER_TRACKS_READY) {
                // threadLoop_mix() sets mCurrentWriteLength
                threadLoop_mix();
            } else if ((mMixerStatus != MIXER_DRAIN_TRACK)
                        && (mMixerStatus != MIXER_DRAIN_ALL)) {
                // threadLoop_sleepTime sets sleepTime to 0 if data
                // must be written to HAL
                threadLoop_sleepTime();
                if (sleepTime == 0) {
                    mCurrentWriteLength = mixBufferSize;
                }
            }
            mBytesRemaining = mCurrentWriteLength;
			……
        }
<span style="white-space:pre">	</span>……
        if (!waitingAsyncCallback()) { //<span style="color:#ff0000;">(3)</span>
            // sleepTime == 0 means we must write to audio hardware
            if (sleepTime == 0) {
                if (mBytesRemaining) {
                    ssize_t ret = threadLoop_write();
                    if (ret < 0) {
                        mBytesRemaining = 0;
                    } else {
                        mBytesWritten += ret;
                        mBytesRemaining -= ret;
                    }
                } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
                        (mMixerStatus == MIXER_DRAIN_ALL)) {
                    threadLoop_drain();
                }
<span style="white-space:pre">	</span> }
                mStandby = false;
         } else {
                usleep(sleepTime);
         }
      }//end while (!exitPending())
<span style="white-space:pre">	</span>……
}

上述循環中,涉及到寫數據至HAL層主要有上述標紅的3點(注意在Offload情形下mUseAsyncWrite爲true):

(1)處調用OffloadThread::waitingAsyncCallback_l(),當mWriteAckSequence的第0位(化爲2進制)爲1或mDrainSequence第0位爲1時,函數返回true,否則返回false。此處僅分析mWriteAckSequence,即mWriteAckSequence爲第0位爲1時,執行條件內語句,即執行mWaitWorkCV.wait(mLock)進入等待,被喚醒後continue重新開始循環。

(2)處當mBytesRemaining爲0時,執行DirectOutputThread::threadLoop_mix(),此處執行的不是混音操作,僅將數據準備好(每次準備mFrameCount Byte數據,對於nexus5-hammerhead 此mFrameCount = 8192,注意mFrameCount代表的意思是幀數,一幀爲32bit = 4Byte,但是Offload並不是每次向HAL層寫mFrameCount*4 = 32768Byte的數據而是隻寫mFrameCountByte,與MixerThread不一樣),等待寫到HAL(Offload傳遞到HAL層的音頻數據是未解碼的數據)。

(3) 處調用OffloadThread::waitingAsyncCallback_l(),當mWriteAckSequence的第0位(化爲2進制)爲0時,執行PlaybackThread::threadLoop_write()。


在PlaybackThread::threadLoop_write()函數中,主要做3件事:

a.將PlaybackThread的mWriteAckSequence加2,再將其第0位置1,並調用mCallbackThread->setWriteBlocked(mWriteAckSequence)函數,將AsyncCallbackThread的mWriteAckSequence設置爲PlaybackThread mWriteAckSequence的2倍:

void AsyncCallbackThread::setWriteBlocked(uint32_t sequence)
{
    Mutex::Autolock _l(mLock);
    // bit 0 is cleared
    mWriteAckSequence = sequence << 1; //因形參即PlaybackThread 的mWriteAckSequence其第0位爲1,
    //則乘2後則AsyncCallbackThread的mWriteAckSequence的第0位爲0。
}


b.執行mOutput->stream->write(mOutput->stream,mMixBuffer + offset, mBytesRemaining),向Hal層寫數據,HAL層返回寫入的數據量,此處mBytesRemaining要麼全部寫入,要麼都不寫入。OffloadThread向HAL寫數據時,其數據最終是通過/external/tinycompress/compress.c中的compress_write寫入Kernel的一個緩衝區,當緩衝區填滿時則寫入爲0,緩衝區未滿時則寫入mBytesRemaining的數據量。

c.如果b中數據成功寫入,則將PlaybackThread的mWriteAckSequence第0位置0(mWriteAckSequence &= ~1),並在此執行mCallbackThread->setWriteBlocked(mWriteAckSequence)函數,將AsyncCallbackThread的mWriteAckSequence設置爲PlaybackThreadmWriteAckSequence的2倍,繼而開始下一輪循環;如果b中數據未成功寫入,則PlaybackThread::threadLoop_write()函數結束,開始下一輪循環。(注意此處只分析了寫數據的過程,其他過程讀者可參照源碼自行分析)。

在上述步驟c中,當數據成功寫入HAL層並進入下一輪循環時,因PlaybackThread的mWriteAckSequence的第0位爲0,則在(1)處的判斷中不成立,即不會進入線程等待,而在(2)處的判斷成立,即每次將數據寫入HAL層後,立即又將數據準備好。並且在立即執行(3)處的語句,即執行PlaybackThread::threadLoop_write()嘗試再寫入數據,而此時Kernel的緩衝區數據還沒被消耗,無法寫入數據,則在(3)中僅將PlaybackThread mWriteAckSequence加2後將其第0位置1,則又進入下一輪循環,此時PlaybackThreadmWriteAckSequence的第0位爲1,則在(1)處的判斷成立,線程進入等待狀態;

在上述步驟c中,當數據未成功寫入HAL層時,此時PlaybackThreadmWriteAckSequence的第0位爲1,下一輪循環時則線程進入等待狀態。


那麼PlaybackThread進入mWaitWorkCV.wait(mLock)等待狀態後,何時被喚醒呢?來看看Native層的AsyncCallbackThread線程與HAL層的offload_callback_thread都幹了啥吧!

先來看看AsyncCallbackThread線程,還記得它是在何處被創建的了嗎?在PlaybackThread構造函數中的調用的readOutputParameters函數裏,它被創建之後執行AsyncCallbackThread::onFirstRef()進入線程循環(注意AsyncCallbackThread中的mWriteAckSequence初始值爲0,但是在PlaybackThread的threadLoop_write裏會調用mCallbackThread->setWriteBlocked(mWriteAckSequence)函數,將其值置爲偶數,第0位爲0)。它的AsyncCallbackThread::threadLoop:

bool AudioFlinger::AsyncCallbackThread::threadLoop()
{
    while (!exitPending()) {
        uint32_t writeAckSequence;
        uint32_t drainSequence;
        {
            Mutex::Autolock _l(mLock);
            mWaitWorkCV.wait(mLock);
            if (exitPending()) {
                break;
            }
            ALOGV("AsyncCallbackThread mWriteAckSequence %d mDrainSequence %d",
                  mWriteAckSequence, mDrainSequence);
            writeAckSequence = mWriteAckSequence;
            mWriteAckSequence &= ~1;
            drainSequence = mDrainSequence;
            mDrainSequence &= ~1;
        }
        {
            sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
            if (playbackThread != 0) {
                if (writeAckSequence & 1) {
                    playbackThread->resetWriteBlocked(writeAckSequence >> 1);
                }
                if (drainSequence & 1) {
                    playbackThread->resetDraining(drainSequence >> 1);
                }
            }
        }
    }
    return false;
}

好傢伙剛進入循環就mWaitWorkCV.wait(mLock)進入等待了(注意此處的mLock是AsyncCallbackThread的mLock,與PlaybackThread的mLock不是同一個),估計是這貨比較懶,必須等別人叫醒它才肯幹活,而且幹一次就又進入等待狀態了。那麼它每次都幹什麼活呢?每次被喚醒後,先把mWriteAckSequence保存到副本writeAckSequence中,並將mWriteAckSequence的第0位置0,接着如果副本writeAckSequence即原來mWriteAckSequence的第0位爲1,則調用playbackThread->resetWriteBlocked(writeAckSequence>> 1)函數(注意傳入的參數是writeAckSequence/2的值,可能爲奇數也可能爲偶數,即第0位既可能爲1也可能爲0),然而此處writeAckSequence的第0位是否爲1呢?PlaybackThread的threadLoop_write裏調用mCallbackThread->setWriteBlocked(mWriteAckSequence)時是把AsyncCallbackThread的mWriteAckSequence置爲偶數(乘2)了的!!先放在這裏(AAAAAAA_我是遺留的問題),等會自有揭曉。


繼續分析AsyncCallbackThread的線程循環,它調用的playbackThread->resetWriteBlocked(writeAckSequence>> 1)做了什麼呢?

void PlaybackThread::resetWriteBlocked(uint32_t sequence)
{
    Mutex::Autolock _l(mLock);
    // reject out of sequence requests
    if ((mWriteAckSequence & 1) && (sequence == mWriteAckSequence)) {
        mWriteAckSequence &= ~1;
        mWaitWorkCV.signal();
    }
}


在PlaybackThread的resetWriteBlocked函數中,若PlaybackThread的mWriteAckSequence第0位爲1且等於入參,則將PlaybackThread的mWriteAckSequence第0位置爲0,並喚醒PlaybackThread。想一想執行此函數時PlaybackThread的mWriteAckSequence第0位是否爲1?在前面的分析中,PlaybackThread向HAL層成功寫入一次數據後,有接着嘗試再此寫數據,但是Kernel的緩衝已經滿了,則寫入失敗並且進入mWaitWorkCV.wait(mLock)等待,當時它的mWriteAckSequence第0爲就是1,那麼是否等於入參sequence呢?答案是肯定的。執行此函數後,PlaybackThread被喚醒,並且重新開始線程循環(continue語句),而且剛纔的函數中已將mWriteAckSequence第0位置0,則跳過前述標紅的(1)的代碼,按需執行(2)處的代碼,並且執行(3)處的代碼,向HAL層寫數據。

那麼問題來了,是誰喚醒AsyncCallbackThread線程的?再來看HAL層的offload_callback_thread線程,它是在NativeAudioTrack調用AudioFlinger的getOutput函數中,調用hwDevHal->open_output_stream(hwDevHal,id, *pDevices,(audio_output_flags_t)flags,&config,&outStream)時,在HAL層的adev_open_output_stream函數中,由create_offload_callback_thread(out)這條語句創建的(忘記的可會頭看一下文章開始第2點的分析)。來看看create_offload_callback_thread的代碼:

/hardware/qcom/audio/hal/audio_hw.c

static int create_offload_callback_thread(struct stream_out *out)
{
    pthread_cond_init(&out->offload_cond, (const pthread_condattr_t *) NULL);
    list_init(&out->offload_cmd_list);
    pthread_create(&out->offload_thread, (const pthread_attr_t *) NULL,offload_thread_loop, out);
    return 0;
}

pthread_create(…,…,…,…)函數創建了一個線程,創建後將線程賦值給out->offload_thread,並且指定線程循環體爲offload_thread_loop,此函數的相關說明可Google或百度。線程創建後進入循環體offload_thread_loop,源碼如下:

static void *offload_thread_loop(void *context)
{
   	……
    pthread_mutex_lock(&out->lock);
    for (;;) {
        struct offload_cmd *cmd = NULL;
        stream_callback_event_t event;
        bool send_callback = false;
		……
        if (list_empty(&out->offload_cmd_list)) { //查看請求隊列是否爲空
            pthread_cond_wait(&out->offload_cond, &out->lock); //爲空則進入等待,線程剛建立時肯定爲空,則進入等待狀態
            continue; //被喚醒後重新下一輪循環
        }

        item = list_head(&out->offload_cmd_list);
        cmd = node_to_item(item, struct offload_cmd, node);
        list_remove(item);
		……
        out->offload_thread_blocked = true;
        pthread_mutex_unlock(&out->lock);
        send_callback = false;
        switch(cmd->cmd) {
        case OFFLOAD_CMD_WAIT_FOR_BUFFER:
            compress_wait(out->compr, -1);
            send_callback = true;
            event = STREAM_CBK_EVENT_WRITE_READY;
            break;
        case OFFLOAD_CMD_PARTIAL_DRAIN:
           	……
        case OFFLOAD_CMD_DRAIN:
            ……
        default:
           	……
        }
        pthread_mutex_lock(&out->lock);
        out->offload_thread_blocked = false;
        pthread_cond_signal(&out->cond);
        if (send_callback) {
            out->offload_callback(event, NULL, out->offload_cookie);
        }
        free(cmd);
    }
    ……
    pthread_mutex_unlock(&out->lock);
    return NULL;
}

原來offload_callback_thread主要就是查看out->offload_cmd_list中是否有調用請求,有則取出該隊列中的第一個並執行,否則進入等待狀態。線程剛建立時out->offload_cmd_list肯定爲空,則進入等待狀態,那是誰想其中添加調用命令並將線程喚醒的呢?原來在PlaybackThread向HAL層寫數據時,在HAL層的out_write函數中:

static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, size_t bytes)
{
   	……
    pthread_mutex_lock(&out->lock);
    if (out->standby) {
        out->standby = false;
        pthread_mutex_lock(&adev->lock);
        ret = start_output_stream(out);
        pthread_mutex_unlock(&adev->lock);
        ……
    }

    if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
		……
        ret = compress_write(out->compr, buffer, bytes);
        if (ret >= 0 && ret < (ssize_t)bytes) {
            send_offload_cmd_l(out, OFFLOAD_CMD_WAIT_FOR_BUFFER); //向out->offload_cmd_list添加調用請求
        }
        ……
        pthread_mutex_unlock(&out->lock);
        return ret;
    } else {
       ……
}
……
    return bytes;
}

由源碼可知當kernel數據緩衝區已滿時,根據前述分析可知數據無法寫入,則compress_write(out->compr, buffer, bytes)返回值爲0,則會調用send_offload_cmd_l(out,OFFLOAD_CMD_WAIT_FOR_BUFFER),向out->offload_cmd_list添加調用請求,在send_offload_cmd_l函數中:

static int send_offload_cmd_l(struct stream_out* out, int command)
{
    struct offload_cmd *cmd = (struct offload_cmd *)calloc(1, sizeof(struct offload_cmd));
    cmd->cmd = command;
    list_add_tail(&out->offload_cmd_list, &cmd->node); //將調用請求添加到out->offload_cmd_list鏈表中
    pthread_cond_signal(&out->offload_cond); //喚醒offload_callback_thread線程
    return 0;
}

由上述源碼可知當PlaybackThread向HAL層寫數據失敗時,HAL層會進入線程等待,同時,HAL層的向out->offload_cmd_list鏈表添加一個寫入數據OFFLOAD_CMD_WAIT_FOR_BUFFER的請求,並喚醒offload_callback_thread執行回調函數out->offload_callback(event,NULL, out->offload_cookie),最終回調的是PlaybackThread的asyncCallback函數(詳見文章開始第3點,由mOutput->stream->set_callback(mOutput->stream,AudioFlinger::PlaybackThread::asyncCallback,this)將PlaybackThread的asyncCallback函數註冊成HAL層的回調函數)。



接下來再看PlaybackThread::asyncCallback:

int PlaybackThread::asyncCallback(stream_callback_event_t event,void *param,void *cookie)
{
    AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie;
    switch (event) {
    case STREAM_CBK_EVENT_WRITE_READY:
        me->writeCallback(); //調用PlaybackThread的writeCallback函數
        break;
    case STREAM_CBK_EVENT_DRAIN_READY:
        ……
    default:
        ……
    }
    return 0;
}

接着看PlaybackThread::writeCallback:

void AudioFlinger::PlaybackThread::writeCallback()
{
    mCallbackThread->resetWriteBlocked();
}

調用的是Native層的AsyncCallbackThread線程的resetWriteBlocked函數,繼續看AsyncCallbackThread:: resetWriteBlocked:

void AsyncCallbackThread::resetWriteBlocked()
{
    Mutex::Autolock _l(mLock);
    // ignore unexpected callbacks
if (mWriteAckSequence & 2) { 
<span style="white-space:pre">	</span>// AsyncCallbackThread  mWriteAckSequence的第1位是否爲1?
<span style="white-space:pre">	</span>// AsyncCallbackThread 線程剛建立時mWriteAckSequence爲0,此後在PlaybackThread的threadLoop_write
<span style="white-space:pre">	</span>//函數中由mCallbackThread->setWriteBlocked(mWriteAckSequence)語句,將AsyncCallbackThread 
<span style="white-space:pre">	</span>//mWriteAckSequence的值置爲PlaybackThread線程mWriteAckSequence的兩倍,故其第1位肯定爲1

        mWriteAckSequence |= 1; //將AsyncCallbackThread的mWriteAckSequence第0位置爲1,這裏是不是將前面遺留的<span style="color:#ff0000;">AAAAAAA_我是遺留的問題</span>很好的解釋了呢!
        mWaitWorkCV.signal(); //喚醒AsyncCallbackThread線程循環
    }
}

這樣由HAL層線程向Kernel寫入數據失敗->添加調用請求到out->offload_cmd_list->喚醒offload_callback_thread執行回調->PlaybackThreadasyncCallback-> PlaybackThread writeCallback-> AsyncCallbackThread resetWriteBlocked->PlaybackThread resetWriteBlocked->喚醒PlaybackThread 線程循環。

總結:經過上述的分析,可以發現OffloadThread的執行流程大致可歸納如下:PlaybackThread第一次向HAL寫數據,成功寫入後立即嘗試第二次寫數據,但Kernel緩衝區寫滿,第二次寫入失敗,從而使PlaybackThread線程循環進入等待狀態;同時,因爲第二次寫入失敗,HAL線程就需要在適當時機將PlaybackThread線程喚醒,故其向out->offload_cmd_list添加了WAIT_FOR_BUFFER 的請求並喚醒offload_callback_thread讓其發起回調;最後經過PlaybackThreadasyncCallback-> PlaybackThread writeCallback-> AsyncCallbackThread resetWriteBlocked->PlaybackThread resetWriteBlocked->重新喚醒PlaybackThread 線程循環,在PlaybackThread線程循環中向HAL層寫數據。如此周而復始,直至音樂播放完畢。就這樣,通過PlaybackThread、AsyncCallbackThread、HAL、offload_callback_thread四個線程循環調用,實現OffloadThread的放音流程。













發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章