《原创作品,禁止转载》
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函数执行相关参数初始化工作,其中: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个线程的交互,后面会具体分析;
}
}
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的放音流程。