7.3.2 通过流程分析AudioFlinger
图7-5中说明的AT和AF交互流程,对于分析AF来说非常重要。先来回顾一下图7-5的流程:
AT调用createTrack,得到一个IAudioTrack对象。
AT调用IAudioTrack对象的start,表示准备写数据了。
AT通过write写数据,这个过程和audio_track_cblk_t有着密切关系。
最后AT调用IAudioTrack的stop或delete IAudioTrack结束工作。
至此,上面的每一步都很清楚了。根据Binder知识可知,AT调用的这些函数最终都会在AF端得到实现,所以可直接从AF端开始。
1.createTrack分析
按照前面的流程步骤来看,第一个被调用的函数会是createTrack,请注意在用例中传的参数。代码如下所示:
[—>AudioFlinger.cpp]
sp<IAudioTrack>AudioFlinger:createTrack(
pid_t pid,//AT的pid号
int streamType,//流类型,用例中是MUSIC类型
uint32_t sampleRate,//8000采样率
int format,//PCM_16类型
int channelCount,//2,双声道
int frameCount,//需要创建的缓冲大小,以帧为单位。
uint32_t flags,
const sp<IMemory>&sharedBuffer,//AT传入的共享buffer,这里为空。
int output,//这个值前面提到过,是AF中的工作线程索引号。
status_t*status)
{
sp<PlaybackThread:Track>track;
sp<TrackHandle>trackHandle;
sp<Client>client;
wp<Client>wclient;
status_t lStatus;
{
Mutex:Autolock_l(mLock);
//output代表索引号,这里根据索引号找到一个工作线程,它是一个PlaybackThread。
PlaybackThread*thread=checkPlaybackThread_l(output);
//看看这个进程是否已经是AF的Client,AF根据进程pid来标识不同的Client。
wclient=mClients.valueFor(pid);
if(wclient!=NULL){
}else{
//如果还没有这个Client信息,则创建一个,并加入到mClients中去。
client=new Client(this,pid);
mClients.add(pid,client);
}
//在找到的工作线程对象中创建一个Track,注意它的类型是Track。
track=thread->createTrack_l(client,streamType,sampleRate,format,
channelCount,frameCount,sharedBuffer,&lStatus);
}
/*
TrackHandle是Track对象的Proxy,它支持Binder通信,而Track不支持Binder。
TrackHandle所接收的请求最终会由Track处理,这是典型的Proxy模式。
*/
trackHandle=new TrackHandle(track);
return trackHandle;
}
这个函数相当复杂,主要原因之一,是其中出现了几个我们没接触过的类。我刚接触这个函数的时候,大脑也曾因为看到这些眼生的东西而“死机”!不过暂时先不用去理会它们,等了解了这个函数后,再回过头来收拾它们。先进入checkPlaybackThread_l看看。
(1)选择工作线程
checkPlaybackThread_l的代码如下所示:
[—>AudioFlinger.cpp]
AudioFlinger:PlaybackThread*
AudioFlinger:checkPlaybackThread_l(int output)const
{
PlaybackThread*thread=NULL;
//根据output的值找到对应的thread。
if(mPlaybackThreads.indexOfKey(output)>=0){
thread=(PlaybackThread*)mPlaybackThreads.valueFor(output).get();
}
return thread;
}
上面函数中传入的output,就是之前在分析AT时提到的工作线程索引号。看到这里,是否感觉有点困惑?困惑的原因可能有二:
目前的流程中尚没有见到创建线程的地方,但在这里确实能找到一个线程。
Output的含义到底是什么?为什么它会作为index来找线程呢?
关于这两个问题,待会儿再做解释。现在只需知道AudioFlinger会创建几个工作线程,AT会找到对应的工作线程即可。
(2)createTrack_l分析
找到工作线程后,会执行createTrack_l函数,请看这个函数的作用:
[—>AudioFlinger.cpp]
//Android的很多代码都采用了内部类的方式进行封装,看习惯就好了。
sp<AudioFlinger:PlaybackThread:Track>
AudioFlinger:PlaybackThread:createTrack_l(
const sp<AudioFlinger:Client>&client,int streamType,
uint32_t sampleRate,int format,int channelCount,int frameCount,
const sp<IMemory>&sharedBuffer,//注意这个参数,从AT中传入,为0。
status_t*status)
{
sp<Track>track;
status_t lStatus;
{
Mutex:Autolock_l(mLock);
//创建Track对象。
track=new Track(this,client,streamType,sampleRate,format,
channelCount,frameCount,sharedBuffer);
//将新创建的Track加入到内部数组mTracks中。
mTracks.add(track);
}
lStatus=NO_ERROR;
return track;
}
上面的函数调用传入的sharedBuffer为空,那共享内存又是在哪里创建的呢?可以注意到Track构造函数中关于sharedBuffer这个参数的类型是一个引用,莫非是构造函数创建的?
(3)Track创建共享内存和TrackHandle
在createTrack_l中,会new出来一个Track,请看它的代码:
[—>AudioFlinger.cpp]
AudioFlinger:PlaybackThread:Track:Track(const wp<ThreadBase>&thread,
const sp<Client>&client,int streamType,uint32_t sampleRate,
int format,int channelCount,int frameCount,
const sp<IMemory>&sharedBuffer)
:TrackBase(thread,client,sampleRate,format,channelCount,
frameCount,0,sharedBuffer),//sharedBuffer仍然为空。
mMute(false),mSharedBuffer(sharedBuffer),mName(-1)
{
//mCblk!=NULL?什么时候创建的呢?只能看基类TrackBase的构造函数了。
if(mCblk!=NULL){
mVolume[0]=1.0f;
mVolume[1]=1.0f;
mStreamType=streamType;
mCblk->frameSize=AudioSystem:isLinearPCM(format)?
channelCount*sizeof(int16_t):sizeof(int8_t);
}
}
对于这种重重继承,我们只能步步深入分析,一定要找到共享内存创建的地方,继续看代码:
[—>AudioFlinger.cpp]
AudioFlinger:ThreadBase:TrackBase:TrackBase(
const wp<ThreadBase>&thread,const sp<Client>&client,
uint32_t sampleRate,int format,int channelCount,int frameCount,
uint32_t flags,const sp<IMemory>&sharedBuffer)
:RefBase(),mThread(thread),mClient(client),mCblk(0),
mFrameCount(0),mState(IDLE),mClientTid(-1),mFormat(format),
mFlags(flags&~SYSTEM_FLAGS_MASK)
{
size_t size=sizeof(audio_track_cblk_t);//得到CB对象的大小。
//计算数据缓冲的大小。
size_t bufferSize=frameCountchannelCountsizeof(int16_t);
if(sharedBuffer==0){
//还记得图7-4吗?共享内存的最前面一部分是audio_track_cblk_t,后面才是数据空间。
size+=bufferSize;
}
//根据size创建一块共享内存。
mCblkMemory=client->heap()->allocate(size);
/*
pointer()返回共享内存的首地址,并强制转换void类型为audio_track_cblk_t类型。
其实把它强制转换成任何类型都可以,但是这块内存中会有CB对象吗?
*/
mCblk=static_cast<audio_track_cblk_t*>(mCblkMemory->pointer());
//①下面这句代码看起来很独特。什么意思?
new(mCblk)audio_track_cblk_t();
mCblk->frameCount=frameCount;
mCblk->sampleRate=sampleRate;
mCblk->channels=(uint8_t)channelCount;
if(sharedBuffer==0){
//清空数据区
mBuffer=(char*)mCblk+sizeof(audio_track_cblk_t);
memset(mBuffer,0,frameCountchannelCountsizeof(int16_t));
//flowControlFlag初始值为1。
mCblk->flowControlFlag=1;
}
……
}
这里需要重点讲解下面这句话的意思。
new(mCblk)audio_track_cblk_t();
注意它的用法,new后面的括号里是内存,紧接其后的是一个类的构造函数。
重点说明 这个语句就是C++语言中的placement new。其含义是在括号里指定的内存中创建一个对象。我们知道,普通的new只能在堆上创建对象,堆的地址由系统分配。这里采用placement new将使audio_track_cblk_t创建在共享内存上,它就自然而然地能被多个进程看见并使用了。关于placement new较详细的知识,还请读者自己搜索一下。
通过上面的分析可以知道:
Track创建了共享内存。
CB对象通过placement new方法创建于这块共享内存中。
AF的createTrack函数返回的是一个IAudioTrack类型的对象,可现在碰到的Track对象是IAudioTrack类型的吗?来看代码:
[—>AudioFlinger.cpp]
sp<IAudioTrack>AudioFlinger:createTrack(……)
{
sp<TrackHandle>trackHandle;
……
track=thread->createTrack_l(client,streamType,sampleRate,
format,channelCount,frameCount,sharedBuffer,&lStatus);
trackHandle=new TrackHandle(track);
return trackHandle;//①这个trackHandle对象竟然没有在AF中保存!
}
原来,createTrack返回的是TrackHandle对象,它以Track为参数构造。这二者之间又是什么关系呢?
Android在这里使用了Proxy模式,即TrackHandle是Track的代理,TrackHandle代理的内容是什么呢?分析TrackHandle的定义可以知道:
Track没有基于Binder通信,它不能接收来自远端进程的请求。
TrackHandle能基于Binder通信,它可以接收来自远端进程的请求,并且能调用Track对应的函数。这就是Proxy模式的意思。
讨论 Android为什么不直接让Track从IBinder派生,直接支持Binder通信呢?关于这个问题,在看到后面的Track家族图谱后,我们或许就明白了。
另外,注意代码中的注释①:
trackHandle被new出来后直接返回,而AF中并没有保存它,这岂不是成了令人闻之色变的野指针?
说明 关于这个问题的答案,请读者自己思考并回答。提示,可从Binder和RefBase入手。
分析完createTrack后,估计有些人会晕头转向的。确实,这个createTrack比较复杂。仅仅是对象类型就层出不穷。到底它有多少种对象,它们之间又有怎样的关系呢?下面就来解决这几个问题。
2.到底有多少种对象?
现在不妨把AudioFlinger中出现的对象总结一下,以了解它们的作用和相互之间的关系。
(1)AudioFlinger对象
作为Audio系统的核心引擎,首先要介绍AudioFlinger。它的继承关系很简单:
class AudioFlinger:public BnAudioFlinger,public IBinder:DeathRecipient
AudioFlinger的主要工作由其定义的许多内部类来完成,我们用图7-7来表示。图中大括号所指向的类为外部类,大括号所包含的为该外部类所定义的内部类。例如,DuplicatingThread、RecordThread和DirectOutputThread都包括在一个大括号中,这个大括号指向AudioFlinger,所以它们三个都是AudioFlinger的内部类,而AudioFlinger则是它们三个的外部类:
图 7-7 AF中的所有类
看,AF够复杂吧?要不是使用了Visual Studio的代码段折叠功能,我画这个图,也会颇费周折的。
(2)Client对象
Client是AudioFlinger对客户端的封装,凡是使用了AudioTrack和AudioRecord的进程,都被会当作是AF的Client,并且Client用它的进程pid作为标识。代码如下所示:
class Client:public RefBase{
public:
Client(const sp<AudioFlinger>&audioFlinger,pid_t pid);
virtual ~Client();
const sp<MemoryDealer>&heap()const;
pid_t pid()const{return mPid;}
sp<AudioFlinger>audioFlinger(){return mAudioFlinger;}
private:
Client(const Client&);
Client&operator=(const Client&);
sp<AudioFlinger>mAudioFlinger;
sp<MemoryDealer>mMemoryDealer;//内存分配器
pid_t
mPid;
};
Client对象比较简单,因此就不做过多的分析了。
注意 一个Client进程可以创建多个AudioTrack,这些AudioTrack都属于同一个Client。
(3)工作线程介绍
AudioFlinger中有几种不同类型的工作线程,它们之间的关系如图7-8所示:
图 7-8 AF中的工作线程家谱
下面来解释图7-8中各种类型工作线程的作用:
PlaybackThread:回放线程,用于音频输出。它有一个成员变量mOutput,为AudioStreamOutput*类型,这表明PlaybackThread直接和Audio音频输出设备建立了联系。
RecordThread:录音线程,用于音频输入,它的关系比较单纯。它有一个成员变量mInput,为AudioStreamInput*类型,这表明RecordThread直接和Audio音频输入设备建立了联系。
从PlaybackThread的派生关系上可看出,手机上的音频回放应该比较复杂,否则也不会派生出三个子类了。其中:
MixerThread:混音线程,它将来自多个源的音频数据混音后再输出。
DirectOutputThread:直接输出线程,它会选择一路音频流后将数据直接输出,由于没有混音的操作,这样可以减少很多延时。
DuplicatingThread:多路输出线程,它从MixerThread派生,意味着它也能够混音。它最终会把混音后的数据写到多个输出中,也就是一份数据会有多个接收者。这就是Duplicate的含义。目前在蓝牙A2DP设备输出中使用。
另外从图7-8中还可以看出:
PlaybackThread维护两个Track数组,一个是mActiveTracks,表示当前活跃的Track;另一个是mTracks,表示这个线程创建的所有Track。
DuplicatingThread还维护了一个mOutputTracks,表示多路输出的目的端。后面分析DuplicatingThread时再对此进行讲解。
说明 大部分常见音频输出使用的是MixerThread,后文会对此进行详细分析。另外,在拓展内容中,也将深入分析DuplicatingThread的实现。
(4)PlaybackThread和AudioStreamOutput
从图7-8中,可以发现PlaybackThread有一个AudioStreamOutput类型的对象,这个对象提供了音频数据的输出功能。可以用图7-9来表示音频数据的流动轨迹。该图以PlaybackThread最常用的子类MixerThread作为代表。
图 7-9 音频数据的流动轨迹
根据图7-9,就能明白MixerThread的大致工作流程了:
接收来自AT的数据。
对这些数据进行混音。
把混音的结果写到AudioStreamOut中,这样就完成了音频数据的输出。
(5)Track对象
前面所说的工作线程,其工作就是围绕Track展开的,图7-10展示了Track的家族:
图 7-10 Track家族
注意 这里把RecordTrack也统称为Track。
从图7-10中可看出,TrackHandle和RecordHandle是基于Binder通信的,它作为Proxy,用于接收请求并派发给对应的Track和RecordTrack。
说明 从图7-10也能看出,之所以不让Track继承Binder框架,是因为Track本身的继承关系和它所承担的工作已经很复杂了,如再让它掺合Binder,只会乱上添乱。
Track类作为工作线程的内部类来实现,其中:
TrackBase定义于ThreadBase中。
Track定义于PlaybackThread中,RecordTrack定义于RecordThread中。
OutputTrack定义于DuplicatingThread中。
根据前面的介绍可知,音频输出数据最后由Playback线程来处理,用例所对应的Playback线程实际上是一个MixerThread,那么它是如何工作的呢?一起来分析。
3.MixerThread分析
MixerThread是Audio系统中负担最重的一个工作线程。先来了解一下它的来历。
(1)MixerThread的来历
前面,在checkplaybackThread_l中,有一个地方一直没来得及解释。回顾一下它的代码:
[—>AudioFlinger.cpp]
AudioFlinger:PlaybackThread*
AudioFlinger:checkPlaybackThread_l(int output)const
{
PlaybackThread*thread=NULL;
//根据output的值找到对应的thread
if(mPlaybackThreads.indexOfKey(output)>=0){
thread=(PlaybackThread*)mPlaybackThreads.valueFor(output).get();
}
return thread;
}
上面这个函数的意思很明确:就是根据output值找到对应的回放线程。
但在前面的流程分析中,并没有见到创建线程的地方,那这个线程又是如何得来的?它又是何时、怎样创建的呢?
答案在AudioPolicyService中。提前看看AudioPolicyService,分析一下它为什么和这个线程有关系。
AudioPolicyService和AudioFlinger一样,都驻留在MediaServer中,直接看它的构造函数:
[—>AudioPolicyService.cpp]
AudioPolicyService:AudioPolicyService()
:BnAudioPolicyService(),mpPolicyManager(NULL)
{
char value[PROPERTY_VALUE_MAX];
//Tone音播放线程。
mTonePlaybackThread=new AudioCommandThread(String8(""));
//命令处理线程。
mAudioCommandThread=new AudioCommandThread(String8("ApmCommandThread"));
if(defined GENERIC_AUDIO)||(defined AUDIO_POLICY_TEST)
//这里属于Generic的情况,所以构造AudioPolicyManagerBase,注意构造函数的参数。
mpPolicyManager=new AudioPolicyManagerBase(this);
else
……
//创建和硬件厂商相关的AudioPolicyManager。
endif
……
}
看AudioPolicyManagerBase的构造函数,注意传给它的参数是this,即把AudioPolicyService对象传进去了。代码如下所示:
[—>AudioPolicyManagerBase.cpp]
AudioPolicyManagerBase:AudioPolicyManagerBase(
AudioPolicyClientInterface*clientInterface)
:mPhoneState(AudioSystem:MODE_NORMAL),mRingerMode(0),
mMusicStopTime(0),mLimitRingtoneVolume(false)
{
mpClientInterface=clientInterface;
//先把不相关的内容去掉。
……
/*
返回来调用mpClientInterface的openOutput,实际就是AudioPolicyService。
注意openOutput函数是在AP的创建过程中调用的。
*/
mHardwareOutput=mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
……
}
真是山不转水转!咱们还得回到AudioPolicyService中去看看:
[—>AudioPolicyService.cpp]
audio_io_handle_t AudioPolicyService:openOutput(uint32_t*pDevices,
uint32_t*pSamplingRate,
uint32_t*pFormat,
uint32_t*pChannels,
uint32_t*pLatencyMs,
AudioSystem:output_flags flags)
{
sp<IAudioFlinger>af=AudioSystem:get_audio_flinger();
//下面会调用AudioFlinger的openOutput,这个时候AF已经启动了。
return af->openOutput(pDevices,pSamplingRate,(uint32_t*)pFormat,
pChannels,pLatencyMs,flags);
}
真是曲折啊,又得到AF去看看:
[—>AudioFlinger.cpp]
int AudioFlinger:openOutput(
uint32_tpDevices,uint32_tpSamplingRate,uint32_t*pFormat,
uint32_tpChannels,uint32_tpLatencyMs,uint32_t flags)
{
……
Mutex:Autolock_l(mLock);
//创建Audio HAL的音频输出对象,和音频输出扯上了关系。
AudioStreamOutoutput=mAudioHardware->openOutputStream(pDevices,
(int*)&format,
&channels,
&samplingRate,
&status);
mHardwareStatus=AUDIO_HW_IDLE;
if(output!=0){
if((flags&AudioSystem:OUTPUT_FLAG_DIRECT)||
(format!=AudioSystem:PCM_16_BIT)||
(channels!=AudioSystem:CHANNEL_OUT_STEREO)){
//如果标志为OUTPUT_FLAG_DIRECT,则创建DirectOutputThread。
thread=new DirectOutputThread(this,output,++mNextThreadId);
}else{
//一般创建的都是MixerThread,注意代表AudioStreamOut对象的output也传进去了。
thread=new MixerThread(this,output,++mNextThreadId);
}
//把新创建的线程加入线程组mPlaybackThreads中保存,mNextThreadId是它的索引号。
mPlaybackThreads.add(mNextThreadId,thread);
……
return mNextThreadId;//返回该线程的索引号。
}
return 0;
}
明白了吗?是否感觉有点绕?可用一个简单的示意图来观察三者的交互流程,如图7-11所示:
图 7-11 MixerThread的曲折来历示意图
图7-11表明:
AF中工作线程的创建,受到了AudioPolicyService的控制。从AudioPolicyService的角度出发,这也是应该的,因为APS控制着整个音频系统,而AF只是管理音频的输入和输出。
另外,注意这个线程是在AP的创建过程中产生的。也就是说,AP一旦创建完Audio系统,就已经准备好要工作了。
关于AF和AP的恩恩怨怨,在后面APS的分析过程中再去探讨。目前,读者只需了解系统中第一个MixerThread的来历即可。下面来分析这个来之不易的MixerThread。
(2)MixerThread的构造和线程启动
[—>AudioFlinger.cpp]
AudioFlinger:MixerThread:MixerThread(
const sp<AudioFlinger>&audioFlinger,
AudioStreamOut*output,//AudioStreamOut为音频输出设备的HAL抽象。
int id)
:PlaybackThread(audioFlinger,output,id),mAudioMixer(0)
{
mType=PlaybackThread:MIXER;
//混音器对象,这个对象比较复杂,它完成多路音频数据的混合工作。
mAudioMixer=new AudioMixer(mFrameCount,mSampleRate);
}
再来看MixerThread的基类PlaybackThread的构造函数:
[—>AudioFlinger.cpp]
AudioFlinger:PlaybackThread:PlaybackThread(const sp<AudioFlinger>&
audioFlinger,AudioStreamOut*output,int id)
:ThreadBase(audioFlinger,id),
mMixBuffer(0),mSuspended(0),mBytesWritten(0),
mOutput(output),mLastWriteTime(0),mNumWrites(0),
mNumDelayedWrites(0),mInWrite(false)
{
//获取音频输出HAL对象的一些信息,包括硬件中音频缓冲区的大小(以帧为单位)。
readOutputParameters();
mMasterVolume=mAudioFlinger->masterVolume();
mMasterMute=mAudioFlinger->masterMute();
//设置不同类型音频流的音量及静音情况。
for(int stream=0;stream<AudioSystem:NUM_STREAM_TYPES;stream++)
{
mStreamTypes[stream].volume=
mAudioFlinger->streamVolumeInternal(stream);
mStreamTypes[stream].mute=mAudioFlinger->streamMute(stream);
}
//发送一个通知消息给监听者,这部分内容较简单,读者可自行研究。
sendConfigEvent(AudioSystem:OUTPUT_OPENED);
}
此时,线程对象已经创建完毕。根据对Thread的分析可知,应该调用它的run函数才能真正创建新线程。在首次创建sp时调用了run,这里利用了RefBase的onFirstRef函数。根据MixerThread的派生关系来看,该函数最终由父类PlaybackThread的onFirstRef实现:
[—>AudioFlinger.cpp]
void AudioFlinger:PlaybackThread:onFirstRef()
{
const size_t SIZE=256;
char buffer[SIZE];
snprintf(buffer,SIZE,“Playback Thread%p”,this);
//下面的run就真正创建了线程并开始执行threadLoop。
run(buffer,ANDROID_PRIORITY_URGENT_AUDIO);
}
好,线程已经run起来了。继续按流程分析,下一个轮到的调用函数是start。
4.start分析
AT调用的是IAudioTrack的start函数,由于TrackHandle的代理作用,这个函数的处理实际会由Track对象来完成。
[—>AudioFlinger.cpp]
status_t AudioFlinger:PlaybackThread:Track:start()
{
status_t status=NO_ERROR;
sp<ThreadBase>thread=mThread.promote();
//该Thread是用例中的MixerThread。
if(thread!=0){
Mutex:Autolock_l(thread->mLock);
int state=mState;
if(mState==PAUSED){
mState=TrackBase:RESUMING;
}else{
mState=TrackBase:ACTIVE;//设置Track的状态
}
PlaybackThreadplaybackThread=(PlaybackThread)thread.get();
//addTrack_l把这个track加入到mActiveTracks数组中。
playbackThread->addTrack_l(this);
return status;
}
看看这个addTrack_l函数,代码如下所示:
[—>AudioFlinger.cpp]
status_t AudioFlinger:PlaybackThread:addTrack_l(const sp<Track>&track)
{
status_t status=ALREADY_EXISTS;
//①mRetryCount:设置重试次数,kMaxTrackStartupRetries值为50。
track->mRetryCount=kMaxTrackStartupRetries;
if(mActiveTracks.indexOf(track)<0){
//②mFillingUpStatus:缓冲状态。
track->mFillingUpStatus=Track:FS_FILLING;
//原来是把调用start的这个track加入到活跃的Track数组中了。
mActiveTracks.add(track);
status=NO_ERROR;
}
//广播一个事件,一定会触发MixerThread线程,通知它有活跃数组加入,需要开工干活。
mWaitWorkCV.broadcast();
return status;
}
start函数把这个Track加入到活跃数组后,将触发一个同步事件,这个事件会让工作线程动起来。虽然这个函数很简单,但有两个关键点必须指出,这两个关键点其实指出了两个问题的处理办法:
mRetryCount表示重试次数,它针对的是这样一个问题:如果一个Track调用了start却没有write数据,该怎么办?如果MixerThread尝试了mRetryCount次后还没有可读数据,工作线程就会把该Track从激活队列中去掉了。
mFillingUpStatus能解决这样的问题:假设分配了1MB的数据缓冲,那么至少需要写多少数据的工作线程才会让Track觉得AT是真的需要它工作呢?难道AT写一个字节就需要工作线程兴师动众吗?其实,这个状态最初为Track:FS_FILLING,表示正在填充数据缓冲。在这种状态下,除非AT设置了强制读数据标志(CB对象中的forceReady变量),否则工作线程是不会读取该Track的数据的。该状态还有其他的值,读者可以自行研究。
说明 我们在介绍大流程的同时也把一些细节问题指出来,希望这些细节问题能激发读者深入研究的欲望。
Track加入工作线程的活跃数组后,又触发了一个同步事件,MixerThread是否真的动起来了呢?一起来看。
(1)MixerThread动起来
Thread类的线程工作都是在threadLoop中完成的,那么MixerThread的线程又会做什么呢?
[—>AudioFlinger.cpp]
bool AudioFlinger:MixerThread:threadLoop()
{
int16_t*curBuf=mMixBuffer;
Vector<sp<Track>>tracksToRemove;
uint32_t mixerStatus=MIXER_IDLE;
nsecs_t standbyTime=systemTime();
……
uint32_t sleepTime=idleSleepTime;
while(!exitPending())
{
//①处理一些请求和通知消息,如之前在构造函数中发出的OUTPUT_OPEN消息等。
processConfigEvents();
mixerStatus=MIXER_IDLE;
{//scope for mLock
Mutex:Autolock_l(mLock);
//检查配置参数,如有需要则重新设置内部参数值。
if(checkForNewParameters_l()){
mixBufferSize=mFrameCount*mFrameSize;
maxPeriod=seconds(mFrameCount)/mSampleRate*3;
……
}
//获得当前的已激活track数组。
const SortedVector<wp<Track>>&activeTracks=mActiveTracks;
……
/*
②prepareTracks_l将检查mActiveTracks数组,判断是否有AT的数据需要处理。
例如有些AudioTrack虽然调用了start,但是没有及时write数据,这时就无须进行混音工作。我们待会再分析prepareTracks_l函数。
*/
mixerStatus=prepareTracks_l(activeTracks,&tracksToRemove);
}
//MIXER_TRACKS_READY表示AT已经把数据准备好了。
if(LIKELY(mixerStatus==MIXER_TRACKS_READY)){
//③由混音对象进行混音工作,混音的结果放在curBuf中。
mAudioMixer->process(curBuf);
sleepTime=0;//等待时间设置为零,表示需要马上输出到Audio HAL中。
standbyTime=systemTime()+kStandbyTimeInNsecs;
}
……
if(sleepTime==0){
……
//④往Audio HAL的OutputStream中write混音后的数据,这是音频数据的最终归宿。
int bytesWritten=(int)mOutput->write(curBuf,mixBufferSize);
if(bytesWritten<0)mBytesWritten-=mixBufferSize;
……
mStandby=false;
}else{
usleep(sleepTime);
}
tracksToRemove.clear();
}
if(!mStandby){
mOutput->standby();
}
return false;
}
从上面的分析可以看出,MixerThread的线程函数大致工作流程是:
如果有通知信息或配置请求,则先完成这些工作。比如向监听者通知AF的一些信息,或者根据配置请求进行音量控制、声音设备切换等。
调用prepareTracks_l函数,检查活跃的Tracks是否有数据已准备好。
调用混音器对象mAudioMixer的process,并且传入一个存储结果数据的缓冲,混音后的结果就存储在这个缓冲中。
调用代表音频输出设备的AudioOutputStream对象的write,把结果数据写入设备。
其中,配置请求处理的工作将在AudioPolicyService的分析中以一个耳机插入处理实例进行讲解。这里主要分析代码中的②③两个步骤。
(2)prepareTracks_l和process分析
prepareTracks_l函数检查激活的Track数组,看看其中是否有数据等待使用,代码如下所示:
[—>AudioFlinger.cpp]
uint32_t AudioFlinger:MixerThread:prepareTracks_l(
const SortedVector<wp<Track>>&activeTracks,
Vector<sp<Track>>*tracksToRemove)
{
uint32_t mixerStatus=MIXER_IDLE;
//激活Track的个数。
size_t count=activeTracks.size();
float masterVolume=mMasterVolume;
bool masterMute=mMasterMute;
//依次查询这些Track的情况。
for(size_t i=0;i<count;i++){
sp<Track>t=activeTracks[i].promote();
if(t==0)continue;
Track*const track=t.get();
//怎么查?通过audio_track_cblk_t对象。
audio_track_cblk_t*cblk=track->cblk();
/*
一个混音器可支持32个Track,它内部有一个32元素的数组,name函数返回的就是Track在这个数组中的索引。混音器每次通过setActiveTrack设置一个活跃Track,后续所有操作都会针对当前设置的这个活跃Track。
*/
mAudioMixer->setActiveTrack(track->name());
//下面这个判断语句决定了什么情况下Track数据可用。
if(cblk->framesReady()&&(track->isReady()||track->isStopped())
&&!track->isPaused()&&!track->isTerminated())
{
……
/*
设置活跃Track的数据提供者为Track本身,因为Track从AudioBufferProvider派生。混音器工作时,需从Track得到待混音的数据,也就是AT写入的数据由混音器取出并消费。
*/
mAudioMixer->setBufferProvider(track);
//设置对应Track的混音标志。
mAudioMixer->enable(AudioMixer:MIXING);
……
//设置该Track的音量等信息,这在以后的混音操作中会使用。
mAudioMixer->setParameter(param,AudioMixer:VOLUME0,left);
mAudioMixer->setParameter(param,AudioMixer:VOLUME1,right);
mAudioMixer->setParameter(
AudioMixer:TRACK,
AudioMixer:FORMAT,track->format());
……
mixerStatus=MIXER_TRACKS_READY;
}else{//如果不满足上面的条件,则走else分支。
if(track->isStopped()){
track->reset();//reset会清零读写位置,表示没有可读数据。
}
//如果处于这三种状态之一,则加入移除队列。
if(track->isTerminated()||track->isStopped()
||track->isPaused()){
tracksToRemove->add(track);
mAudioMixer->disable(AudioMixer:MIXING);
}else{
//不处于上面三种状态时,表示暂时没有可读数据,则重试mRetryCount次。
if(—(track->mRetryCount)<=0){
tracksToRemove->add(track);
}else if(mixerStatus!=MIXER_TRACKS_READY){
mixerStatus=MIXER_TRACKS_ENABLED;
}
//禁止这个Track的混音。
mAudioMixer->disable(AudioMixer:MIXING);
……
}
}
}
//对那些被移除的Track做最后的处理。
……
return mixerStatus;
}
如果所有Track都准备就绪了,最重要的工作就是混音了。混音对象的process也就派上了用场。来看这个process函数,代码如下所示:
[—>AudioMixer.cpp]
void AudioMixer:process(void*output)
{
mState.hook(&mState,output);//hook?这是一个函数指针
}
hook是函数指针,它根据Track的个数和它的音频数据格式(采样率等)等情况,使用不同的处理函数。为了进一步了解混音器是如何工作的,需要先分析AudioMixer对象。
(3)AudioMixer对象分析
AudioMixer实现在AudioMixer.cpp中,先看构造函数:
[—>AudioMixer.cpp]
AudioMixer:AudioMixer(size_t frameCount,uint32_t sampleRate)
:mActiveTrack(0),mTrackNames(0),mSampleRate(sampleRate)
{
mState.enabledTracks=0;
mState.needsChanged=0;
mState.frameCount=frameCount;//这个值等于音频输出对象的缓冲大小。
mState.outputTemp=0;
mState.resampleTemp=0;
//hook初始化的时候为process__nop,这个函数什么都不会做。
mState.hook=process__nop;
track_t*t=mState.tracks;//track_t是和Track相对应的一个结构。
//最大支持32路混音,也很不错了。
for(int i=0;i<32;i++){
……
t->channelCount=2;
t->enabled=0;
t->format=16;
t->buffer.raw=0;
t->bufferProvider=0;//bufferProvider为这一路Track的数据提供者。
t->hook=0;//每一个Track也有一个hook函数。
……
}
int mActiveTrack;
uint32_t mTrackNames;
const uint32_t mSampleRate;
state_t mState
}
其中,mState是在AudioMixer类中定义的一个数据结构。
struct state_t{
uint32_t enabledTracks;
uint32_t needsChanged;
size_t frameCount;
mix_t hook;
int32_t*outputTemp;
int32_t*resampleTemp;
int32_t reserved[2];
/*
aligned表示32字节对齐,由于source insight不认识这个标志,导致
state_t不能被解析。在看代码时,可以注释掉后面的attribute,这样source insight就可以识别state_t结构了。
*/
trackt tracks[32];_attribute((aligned(32)));
};
AudioMixer为hook准备了多个实现函数,来看:
process__validate:根据Track的格式、数量等信息选择其他的处理函数。
process__nop:什么都不做。
process__genericNoResampling:普通无需重采样。
process__genericResampling:普通需重采样。
process__OneTrack16BitsStereoNoResampling:一路音频流,双声道,PCM16格式,无需重采样。
process__TwoTracks16BitsStereoNoResampling:两路音频流,双声道,PCM16格式,无需重采样。
hook最初的值为process__nop,这一定不会是混音中最终使用的处理函数,难道有动态赋值的地方?是的。一起来看——
(4)杀鸡不用宰牛刀——根据情况选择处理函数
在AF的prepare_l中,会为每一个准备好的Track使能混音标志:
mAudioMixer->setBufferProvider(track);
mAudioMixer->enable(AudioMixer:MIXING);//使能混音
请看enable的实现:
[—>AudioMixer.cpp]
status_t AudioMixer:enable(int name)
{
switch(name){
case MIXING:{
if(mState.tracks[mActiveTrack].enabled!=1){
mState.tracks[mActiveTrack].enabled=1;
//注意这个invalidateState调用。
invalidateState(1<<mActiveTrack);
}
}break;
default:
return NAME_NOT_FOUND;
}
return NO_ERROR;
}
[—>AudioMixer.cpp]
void AudioMixer:invalidateState(uint32_t mask)
{
if(mask){
mState.needsChanged|=mask;
mState.hook=process__validate;//将hook设置为process_validate。
}
}
process_validate会根据当前Track的情况选择不同的处理函数,所以不会出现杀鸡却用宰牛刀的情况。
[—>AudioMixer.cpp]
void AudioMixer:process__validate(state_tstate,voidoutput)
{
uint32_t changed=state->needsChanged;
state->needsChanged=0;
uint32_t enabled=0;
uint32_t disabled=0;
……
if(countActiveTracks){
if(resampling){
……
//如果需要重采样,则选择process__genericResampling。
state->hook=process__genericResampling;
}else{
……
state->hook=process__genericNoResampling;
if(all16BitsStereoNoResample&&!volumeRamp){
if(countActiveTracks==1){
//如果只有一个Track,则使用process__OneTrack16BitsStereoNoResampling。
state->hook=process__OneTrack16BitsStereoNoResampling;
}
}
}
}
state->hook(state,output);
……
}
假设用例运行时,系统只有这么一个Track,那么hook函数使用的就是process__OneTrack16BitsStereoNoResampling处理。process_XXX函数会涉及很多数字音频处理的专业知识,先不用去讨论它。数据缓冲的消费工作是在这个函数中完成的,因此应重点关注它是如何通过CB对象使用数据缓冲的。
说明 这个数据消费和之前破解AT的过程中所讲的数据生产是对应的,先来提炼AT和AF在生产和消费这两个环节上与CB交互的流程。
(5)怎么消费数据
在AudioTrack中,曾讲到数据的生产流程:
ObtainBuffer得到一块数据缓冲。
memcpy数据到该缓冲。
releaseBuffer释放这个缓冲。
那么作为消费者,AudioFlinger是怎么获得这些数据的呢?
[—>AudioMixer.cpp]
void AudioMixer:process__OneTrack16BitsStereoNoResampling(
state_tstate,voidoutput)
{
//找到被激活的Track,此时只能有一个Track,否则就不会选择这个process函数了。
const int i=31-__builtin_clz(state->enabledTracks);
const track_t&t=state->tracks[i];
AudioBufferProvider:Buffer&b(t.buffer);
……
while(numFrames){
b.frameCount=numFrames;
//BufferProvider就是Track对象,调用它的getNextBuffer获得可读数据缓冲。
t.bufferProvider->getNextBuffer(&b);
int16_t const*in=b.i16;
……
size_t outFrames=b.frameCount;
do{//数据处理,也即混音。
uint32_t rl=reinterpret_cast<uint32_t const>(in);
in+=2;
int32_t l=mulRL(1,rl,vrl)>>12;
int32_t r=mulRL(0,rl,vrl)>>12;
//把数据复制给out缓冲。
*out++=(r<<16)|(l&0xFFFF);
}while(—outFrames);
}
numFrames-=b.frameCount;
//调用Track的releaseBuffer释放缓冲。
t.bufferProvider->releaseBuffer(&b);
}
}
bufferProvider就是Track对象,总结一下它使用数据缓冲的调用流程:
调用Track的getNextBuffer,得到可读数据缓冲。
调用Track的releaseBuffer,释放数据缓冲。
现在来分析上面这两个函数:getNextBuffer和releaseBuffer。
(6)getNextBuffer和releaseBuffer分析
先看getNextBuffer。它从数据缓冲中得到一块可读空间,代码如下所示:
[—>AudioFlinger.cpp]
status_t AudioFlinger:PlaybackThread:Track:getNextBuffer(
AudioBufferProvider:Buffer*buffer)
{
audio_track_cblk_t*cblk=this->cblk();//通过CB对象完成uint32_t framesReady;
//frameCount为AudioOutput音频输出对象的缓冲区大小uint32_t framesReq=buffer->frameCount;
……
//根据CB的读写指针计算有多少帧数据可读。
framesReady=cblk->framesReady();
if(LIKELY(framesReady)){
uint32_t s=cblk->server;//当前读位置
//可读的最大位置,为当前读位置加上frameCount。
uint32_t bufferEnd=cblk->serverBase+cblk->frameCount;
//AT可以通过setLooping设置播放的起点和终点,如果有终点的话,需要以loopEnd
//作为数据缓冲的末尾。
bufferEnd=(cblk->loopEnd<bufferEnd)?cblk->loopEnd:bufferEnd;
if(framesReq>framesReady){
//如果要求的读取帧数大于可读帧数,则只能选择实际可读的帧数。
framesReq=framesReady;
}
//如果可读帧数的最后位置超过了AT设置的末端点,则需要重新计算可读帧数。
if(s+framesReq>bufferEnd){
framesReq=bufferEnd-s;
}
//根据读起始位置得到数据缓冲的起始地址,framesReq参数用来做内部检查,防止出错。
buffer->raw=getBuffer(s,framesReq);
if(buffer->raw==0)goto getNextBuffer_exit;
buffer->frameCount=framesReq;
return NO_ERROR;
}
getNextBuffer_exit:
buffer->raw=0;
buffer->frameCount=0;
return NOT_ENOUGH_DATA;
}
getNextBuffer非常简单,不过就是根据CB记录的读写位置等计算可读的缓冲位置。下面来看releaseBuffer的操作。代码如下所示:
[—>AudioFlinger.cpp]
void AudioFlinger:ThreadBase:TrackBase:releaseBuffer(
AudioBufferProvider:Buffer*buffer)
{
buffer->raw=0;
mFrameCount=buffer->frameCount;//frameCount为getNextBuffer中分配的可读帧数。
step();//调用step函数
buffer->frameCount=0;
}
[—>AudioFlinger.cpp]
bool AudioFlinger:ThreadBase:TrackBase:step(){
bool result;
audio_track_cblk_t*cblk=this->cblk();
//调用stepServer更新读位置。
result=cblk->stepServer(mFrameCount);
if(!result){
mFlags|=STEPSERVER_FAILED;
}
return result;
}
getNextBuffer和releaseBuffer这两个函数相对比较简单。把它和CB交互的流程总结一下,为后面进行CB对象的分析做铺垫:
getNextBuffer通过frameReady得到可读帧数。
getBuffer函数将根据可读帧数等信息得到可读空间的首地址。
releaseBuffer通过stepServer更新读位置。
5.stop分析
(1)TrackHandle和Track的回收
来自AT的stop请求最终会通过TrackHandle这个Proxy交给Track的stop处理。请直接看Track的stop,代码如下所示:
[—>AudioFlinger.cpp]
void AudioFlinger:PlaybackThread:Track:stop()
{
sp<ThreadBase>thread=mThread.promote();
if(thread!=0){
Mutex:Autolock_l(thread->mLock);
int state=mState;//保存旧的状态。
if(mState>STOPPED){
mState=STOPPED;//设置新状态为STOPPED。
PlaybackThreadplaybackThread=(PlaybackThread)thread.get();
if(playbackThread->mActiveTracks.indexOf(this)<0){
reset();//如果该线程的活跃数组中没有Track,则重置读写位置。
}
}
//和APS相关,我们不在这里讨论,它不直接影响AudioFlinger。
if(!isOutputTrack()&&(state==ACTIVE||state==RESUMING)){
thread->mLock.unlock();
AudioSystem:stopOutput(thread->id(),
(AudioSystem:stream_type)mStreamType);
thread->mLock.lock();
}
}
}
如果Track最初处于活跃数组中,那么这个stop函数无非是把mState设置为STOPPED了,但播放该怎么停止呢?请再回头看prepareTrack_l中的那个判断:
if(cblk->framesReady()&&(track->isReady()||track->isStopped())
&&!track->isPaused()&&!track->isTerminated())
假设AT写数据快,而AF消耗数据慢,那么上面这个判断语句在一定时间内是成立的,换言之,如果仅仅调用了stop,还是会听到声音,该怎么办?在一般情况下,AT端stop后会很快被delete,这将导致AF端的TrackHandle也被delete。
说明 在介绍Track和TrackHandle的一节中,曾在最后提到了那个野指针问题。相信读者这时候会知道那个问题的答案了,是吗?
看TrackHandle的析构函数,代码如下所示:
[—>AudioFlinger.cpp]
AudioFlinger:TrackHandle:~TrackHandle(){
mTrack->destroy();
}
[—>AudioFlinger.cpp]
void AudioFlinger:PlaybackThread:Track:destroy()
{
sp<Track>keep(this);
{
sp<ThreadBase>thread=mThread.promote();
if(thread!=0){
if(!isOutputTrack()){
//和AudioSystem相关,以后再分析
if(mState==ACTIVE||mState==RESUMING){
AudioSystem:stopOutput(thread->id(),
(AudioSystem:stream_type)mStreamType);
}
AudioSystem:releaseOutput(thread->id());
}
Mutex:Autolock_l(thread->mLock);
PlaybackThreadplaybackThread=(PlaybackThread)thread.get();
//调用回放线程对象的destroyTrack_l。
playbackThread->destroyTrack_l(this);
}
}
}
[—>AudioFlinger.cpp]
void AudioFlinger:PlaybackThread:destroyTrack_l(const sp<Track>&track)
{
track->mState=TrackBase:TERMINATED;//设置状态为TERMINATED。
if(mActiveTracks.indexOf(track)<0){
mTracks.remove(track);//如果不在mActiveTracks数组中,则把它从mTracks中去掉。
//由PlaybackThread的子类实现,一般就是做回收一些资源等工作。
deleteTrackName_l(track->name());
}
}
TrackHandle的delete最后会导致它所代理的Track对象也被删除,那么Client对象什么时候被回收呢?
(2)Client的回收
直接看TrackBase的析构,因为Track的析构会导致它的基类TrackBase析构函数被调用,代码如下所示:
[—>AudioFlinger.cpp]
AudioFlinger:ThreadBase:TrackBase:~TrackBase()
{
if(mCblk){
//placement new出来的对象需要显示调用的析构函数。
mCblk->~audio_track_cblk_t();
if(mClient==NULL){
delete mCblk;//先调用析构,再释放内存,这是placement new的用法。
}
}
mCblkMemory.clear();
if(mClient!=NULL){
Mutex:Autolock_l(mClient->audioFlinger()->mLock);
mClient.clear();//如果mClient的强弱引用计数都为0,则会导致该Client被delete。
}
}
资源回收的工作相对比较简单,这里就不做过多的讨论了,读者可自行分析研究。
说明 其实,要找到TrackHandle是什么时候被delete会更有难度。