《原創作品,禁止轉載》
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>
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的放音流程。