7.5 拓展思考

7.5.1 DuplicatingThread破解

DuplicatingThread需要与蓝牙结合起来使用,它的存在与Audio硬件结构息息相关。读者可参考图7-12“智能手机硬件架构图”来理解。当一份数据同时需要发送给DSP和蓝牙A2DP设备时,DuplicatingThread就派上用场了。在分析DuplicatingThread前,还是应该了解一下它的来龙去脉。

1.DuplicatingThread的来历

DuplicatingThread和蓝牙的A2DP设备有关系。可先假设有一个蓝牙立体声耳机已经连接上了,接着从setDeviceConnectionState开始分析,代码如下所示:


[—>AudioPolicyManagerBase.cpp]

status_t AudioPolicyManagerBase:setDeviceConnectionState(

AudioSystem:audio_devices device,

AudioSystem:device_connection_state state,

const char*device_address)

{

……

switch(state)

{

case AudioSystem:DEVICE_STATE_AVAILABLE:

mAvailableOutputDevices|=device;

ifdef WITH_A2DP

if(AudioSystem:isA2dpDevice(device)){

//专门处理A2DP设备的连接。

status_t status=handleA2dpConnection(device,device_address);

}

endif

……

}


对于A2DP设备,有专门的函数handleA2dpConnection处理,代码如下所示:


[—>AudioPolicyManagerBase.cpp]

status_t AudioPolicyManagerBase:handleA2dpConnection(

AudioSystem:audio_devices device,

const char*device_address)

{

AudioOutputDescriptor*outputDesc=new AudioOutputDescriptor();

outputDesc->mDevice=device;

//先为mA2dpOutput创建一个MixerThread,这个和mHardwareOutput一样。

mA2dpOutput=mpClientInterface->openOutput(&outputDesc->mDevice,

&outputDesc->mSamplingRate,

&outputDesc->mFormat,

&outputDesc->mChannels,

&outputDesc->mLatency,

outputDesc->mFlags);

if(mA2dpOutput){

/*

a2dpUsedForSonification永远返回true,表示属于SONIFCATION策略的音频流声音需要同时从蓝牙和DSP中传出。至于SONIFCATION策略的音频流类型可查看前面关于getStrategy的分析,来电铃声、短信通知等属于这一类。

*/

if(a2dpUsedForSonification()){

/*

创建一个DuplicateOutput,注意它的参数,第一个是蓝牙MixerThread,

第二个是DSP MixerThread。

*/

mDuplicatedOutput=mpClientInterface->openDuplicateOutput(

mA2dpOutput,mHardwareOutput);

}

if(mDuplicatedOutput!=0||

!a2dpUsedForSonification()){

if(a2dpUsedForSonification()){

//创建一个AudioOutputDescriptor对象。

AudioOutputDescriptor*dupOutputDesc=newAudioOutputDescriptor();

dupOutputDesc->mOutput1=mOutputs.valueFor(mHardwareOutput);

dupOutputDesc->mOutput2=mOutputs.valueFor(mA2dpOutput);

……

//保存mDuplicatedOutput和dupOutputDesc键值对。

addOutput(mDuplicatedOutput,dupOutputDesc);

……

}

}

}

……

}


这里最重要的函数是openDuplicateOutput。它和openOutput一样,最终的处理都是在AF中。去那里看看,代码如下所示:


[—>AudioFlinger.cpp]

int AudioFlinger:openDuplicateOutput(int output1,int output2)

{

Mutex:Autolock_l(mLock);

//output1对应蓝牙的MixerThread。

MixerThread*thread1=checkMixerThread_l(output1);

//output2对应DSP的MixerThread。

MixerThread*thread2=checkMixerThread_l(output2);

//①创建DuplicatingThread,注意它第二个参数使用的是代表蓝牙的MixerThread。

DuplicatingThread*thread=new DuplicatingThread(this,

thread1,++mNextThreadId);

//②加入代表DSP的MixerThread。

thread->addOutputTrack(thread2);

mPlaybackThreads.add(mNextThreadId,thread);

return mNextThreadId;//返回DuplicatingThread的索引。

}


从现在起,MixerThread要简写为MT,而DuplicatingThread则简写为DT。这里面有两个重要的函数调用,一起来看。

2.DuplicatingThread和OutputTrack

先看DT的构造函数,代码如下所示:


[—>AudioFlinger.cpp]

AudioFlinger:DuplicatingThread:DuplicatingThread(const sp<AudioFlinger>&

audioFlinger,AudioFlinger:MixerThread*mainThread,int id)

:MixerThread(audioFlinger,mainThread->getOutput(),id),

mWaitTimeMs(UINT_MAX)

{

//DT是MT的派生类,所以先要完成基类的构造,还记得MT的构造吗?它会创建一个AudioMixer对象。

mType=PlaybackThread:DUPLICATING;

//把代表DSP的MT加入进来,咱们看看。

addOutputTrack(mainThread);

}

[—>AudioFlinger.cpp]

void AudioFlinger:DuplicatingThread:addOutputTrack(MixerThread*thread)

{

int frameCount=(3mFrameCountmSampleRate)/thread->sampleRate();

//构造一个OutputTrack,它的第一个参数是MT。

OutputTrackoutputTrack=new OutputTrack((ThreadBase)thread,

this,mSampleRate,mFormat,

mChannelCount,frameCount);

if(outputTrack->cblk()!=NULL){

thread->setStreamVolume(AudioSystem:NUM_STREAM_TYPES,1.0f);

//把这个outputTrack加入到mOutputTracks数组保存。

mOutputTracks.add(outputTrack);

updateWaitTime();

}

}


此时,当下面两句代码执行完:


DuplicatingThread*thread=new DuplicatingThread(this,thread1,++mNextThreadId);

thread->addOutputTrack(thread2);


DT则分别构造了两个OutputTrack,一个对应蓝牙的MT,另一个对应DSP的MT。现在来看OutputTrack为何方神圣,代码如下所示:


[—>AudioFlinger.cpp]

AudioFlinger:PlaybackThread:OutputTrack:OutputTrack(

const wp<ThreadBase>&thread,DuplicatingThread*sourceThread,

uint32_t sampleRate,int format,int channelCount,int frameCount)

:Track(thread,NULL,AudioSystem:NUM_STREAM_TYPES,sampleRate,

format,channelCount,frameCount,NULL),//最后这个参数为NULL。

mActive(false),mSourceThread(sourceThread)

{

/*

OutputTrack从Track派生,所以需要先调用基类的构造,还记得Track构造函数

中的事情吗?它会创建一块内存,至于是不是共享内存,由Track构造函数的最后一个参数决定。

如果该值为NULL,表示没有客户端参与,则会在本进程内创建一块内存,这块内存的结构如图7-4所示,前面为CB对象,后面为数据缓冲。

*/

//下面的这个thread对象为MT。

PlaybackThreadplaybackThread=(PlaybackThread)thread.unsafe_get();

if(mCblk!=NULL){

mCblk->out=1;//表示DT将往MT中写数据。

//和前面所分析的AT、AF中的处理何其相似!

mCblk->buffers=(char*)mCblk+sizeof(audio_track_cblk_t);

mCblk->volume[0]=mCblk->volume[1]=0x1000;

mOutBuffer.frameCount=0;

//把这个Track加到MT的Track中。

playbackThread->mTracks.add(this);

}

}


明白了吗?图7-16表示的是openDuplicateOutput的结果:

7.5 拓展思考 - 图1

图 7-16 openDuplicateOutput的结果示意图

图7-16说明(以蓝牙MT为例):

蓝牙MT的Track中有一个成员为OutputTrack0。

DT的mOutputTracks也有一个成员指向OutputTrack0。这就好像DT是MT的客户端一样,它和前面分析的AT是AF的客户端类似。

灰色部分代表数据传递用的缓冲。

3.DT的客户端AT

DT是从MT中派生的,根据AP和AT的交互流程可知,当AT创建的流类型对应策略为SONIFACATION时,它会从AP中得到代表DT的线程索引号。由于DT没有重载createTrack_l,所以这个过程也会创建一个Track对象(和MT创建Track对象一样)。此时的结果,将导致图7-16变成图7-17。

图7-17把DT的工作方式表达得非常清晰了。一个DT配合两个OutputTrack中的进程内缓冲,把来自AT的数据原封不动地发给蓝牙MT和DSP MT,这简直就是个数据中继器。不过俗话说得好,道理虽简单,实现却复杂。来看DT是如何完成这一复杂而艰巨的任务的吧。

4.DT的线程函数

DT的线程函数代码如下所示:

7.5 拓展思考 - 图2

图 7-17 有AT的DT全景图


[—>AudioFlinger.cpp]

bool AudioFlinger:DuplicatingThread:threadLoop()

{

int16_t*curBuf=mMixBuffer;

Vector<sp<Track>>tracksToRemove;

uint32_t mixerStatus=MIXER_IDLE;

nsecs_t standbyTime=systemTime();

size_t mixBufferSize=mFrameCount*mFrameSize;

SortedVector<sp<OutputTrack>>outputTracks;

while(!exitPending())

{

processConfigEvents();

mixerStatus=MIXER_IDLE;

{

……

//处理配置请求,和MT处理一样。

const SortedVector<wp<Track>>&activeTracks=mActiveTracks;

for(size_t i=0;i<mOutputTracks.size();i++){

outputTracks.add(mOutputTracks[i]);

}

//如果AT的Track停止了,则需要停止和MT共享的OutputTrack。

if UNLIKELY((!activeTracks.size()&&systemTime()>standbyTime)

||mSuspended){

if(!mStandby){

for(size_t i=0;i<outputTracks.size();i++){

outputTracks[i]->stop();

}

mStandby=true;

mBytesWritten=0;

}

……

//DT从MT派生,天然具有混音的功能,所以这部分功能和MT一致。

mixerStatus=prepareTracks_l(activeTracks,&tracksToRemove);

}

if(LIKELY(mixerStatus==MIXER_TRACKS_READY)){

//outputsReady将检查OutputTracks对应的MT状态。

if(outputsReady(outputTracks)){

mAudioMixer->process(curBuf);//使用AudioMixer对象混音。

}else{

memset(curBuf,0,mixBufferSize);

}

sleepTime=0;

writeFrames=mFrameCount;

}

……

if(sleepTime==0){

standbyTime=systemTime()+kStandbyTimeInNsecs;

for(size_t i=0;i<outputTracks.size();i++){

//将混音后的数据写到outputTrack中。

outputTracks[i]->write(curBuf,writeFrames);

}

mStandby=false;

mBytesWritten+=mixBufferSize;

}else{

usleep(sleepTime);

}

tracksToRemove.clear();

outputTracks.clear();

}

return false;

}


现在,来自远端进程AT的数据已得到了混音,这一份混音后的数据还将通过调用OutputTrack的write完成DT到其他两个MT的传输。注意,这里除了AT使用的Track外,还有DT和两个MT共享的OutputTrack。AT调用的start,将导致DT的Track加入到活跃数组中,但另外两个OutputTrack还没调用start。这些操作又是在哪里做的呢?来看write函数,代码如下所示:


[—>AudioFlinger.cpp]

bool AudioFlinger:PlaybackThread:OutputTrack:write(int16_t*data,

uint32_t frames)

{

//注意,此处的OutputTrack是DT和MT共享的。

Buffer*pInBuffer;

Buffer inBuffer;

uint32_t channels=mCblk->channels;

bool outputBufferFull=false;

inBuffer.frameCount=frames;

inBuffer.i16=data;

uint32_t waitTimeLeftMs=mSourceThread->waitTimeMs();

if(!mActive&&frames!=0){

//如果此Track没有活跃,则调用start激活。

start();

……

}

/*

现在,AF中的数据传递有三个线程:一个DT,两个MT。MT作为DT的二级消费者,可能由于某种原因来不及消费数据,所以DT中提供了一个缓冲队列mBufferQueue,把MT来不及消费的数据保存在这个缓冲队列中。注意这个缓冲队列容纳的临时缓冲个数是有限制的,其限制值由kMaxOverFlowBuffers控制,初始化为10个。

*/

while(waitTimeLeftMs){

//先消耗保存在缓冲队列的数据。

if(mBufferQueue.size()){

pInBuffer=mBufferQueue.itemAt(0);

}else{

pInBuffer=&inBuffer;

}

……

//获取可写缓冲,下面这句代码是否和AT中对应的代码很相似?

if(obtainBuffer(&mOutBuffer,waitTimeLeftMs)==

(status_t)AudioTrack:NO_MORE_BUFFERS){

……

break;

}

uint32_t outFrames=pInBuffer->frameCount>mOutBuffer.frameCount?

mOutBuffer.frameCount:pInBuffer->frameCount;

//将数据拷贝到DT和MT共享的那块缓冲中去。

memcpy(mOutBuffer.raw,pInBuffer->raw,

outFrameschannelssizeof(int16_t));

//更新写位置

mCblk->stepUser(outFrames);

pInBuffer->frameCount-=outFrames;

pInBuffer->i16+=outFrames*channels;

mOutBuffer.frameCount-=outFrames;

mOutBuffer.i16+=outFrames*channels;

……

}//while结束

if(inBuffer.frameCount){

sp<ThreadBase>thread=mThread.promote();

if(thread!=0&&!thread->standby()){

if(mBufferQueue.size()<kMaxOverFlowBuffers){

pInBuffer=new Buffer;

pInBuffer->mBuffer=new int16_t[inBuffer.frameCount*channels];

pInBuffer->frameCount=inBuffer.frameCount;

pInBuffer->i16=pInBuffer->mBuffer;

//拷贝旧数据到新的临时缓冲。

memcpy(pInBuffer->raw,inBuffer.raw,

inBuffer.frameCountchannelssizeof(int16_t));

//保存这个临时缓冲。

mBufferQueue.add(pInBuffer);

}

}

}

//如果数据全部写完

if(pInBuffer->frameCount==0){

if(mBufferQueue.size()){

mBufferQueue.removeAt(0);

delete[]pInBuffer->mBuffer;

delete pInBuffer;//释放缓冲队列对应的数据缓冲。

}else{

break;

}

}

}

……

return outputBufferFull;

}


数据就这样通过DT的帮助,从AT传输到蓝牙的MT和DSP的MT中了。这种方式的数据传输比直接使用MT传输要缓慢。

到这里,对DT的讲解就告一段落了。本人觉得,DT的实现是AF代码中最美妙的地方,多学习这些优秀代码,有助于提高学习者的水平。

说明 DT还有别的一些细节本书中没有涉及,读者可以结合自己的情况进行分析和理解。