Android 系统(4.4 KitKat)使用OffloadThread播放音乐的数据流程分析

《原创作品,禁止转载》


Google在Android4.4音频系统中新增了使用OffloadThread来播放音乐的流程,此文仅记录自己对Offload的一些分析与理解,方便以后查阅,如文中有误谨请网友提出,谢谢。

1. OffloadThread的继承关系如图:

                                                                                    Android 系统(4.4 KitKat)使用OffloadThread播放音乐的数据流程分析_第1张图片

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函数执行相关参数初始化工作,其中:

if ((mOutput->flags &AUDIO_OUTPUT_FLAG_NON_BLOCKING) &&

           (mOutput->stream->set_callback != NULL)) { //上述在HAL层adev_open_output_stream函数中已对set_callback赋值

       if (mOutput->stream->set_callback(mOutput->stream,AudioFlinger::PlaybackThread::asyncCallback,this) == 0) {

//执行HAL层out_set_callback函数,将PlaybackThread::asyncCallback注册到out->offload_callback中,

//此后HAL层即可通过此回调接口调用PlaybackThread的asyncCallback函数;

           mUseAsyncWrite = true;

           mCallbackThread = new AudioFlinger::AsyncCallbackThread(this); 

//在Native层也为offload创建一个单独的线程,

//offload真心高大上,涉及到4个线程的交互,后面会具体分析;

        }

}


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()) { //(1)
                if (exitPending()) {
                    break;
                }
                releaseWakeLock_l();
                ALOGE("wait async completion");
                mWaitWorkCV.wait(mLock);
                ALOGE("async completion/wake");
                acquireWakeLock_l();
                standbyTime = systemTime() + standbyDelay;
                sleepTime = 0;
                continue;
            }
	    ……
        }

        if (mBytesRemaining == 0) { //(2)
            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;
			……
        }
	……
        if (!waitingAsyncCallback()) { //(3)
            // 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();
                }
	 }
                mStandby = false;
         } else {
                usleep(sleepTime);
         }
      }//end while (!exitPending())
	……
}

上述循环中,涉及到写数据至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 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) { 
	// AsyncCallbackThread  mWriteAckSequence的第1位是否为1?
	// AsyncCallbackThread 线程刚建立时mWriteAckSequence为0,此后在PlaybackThread的threadLoop_write
	//函数中由mCallbackThread->setWriteBlocked(mWriteAckSequence)语句,将AsyncCallbackThread 
	//mWriteAckSequence的值置为PlaybackThread线程mWriteAckSequence的两倍,故其第1位肯定为1

        mWriteAckSequence |= 1; //将AsyncCallbackThread的mWriteAckSequence第0位置为1,这里是不是将前面遗留的AAAAAAA_我是遗留的问题很好的解释了呢!
        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的放音流程。













你可能感兴趣的:(Android,Native)