转载自http://bbs.9ria.com/forum.php?mod=viewthread&tid=213240&highlight=AUDIOFLINGER
1.PlaybackThread的循环主体当一个PlaybackThread进入主循环后(threadLoop),音频事务就正式开启了。仔细观察的话,我们会发现这个循环中会不断地调用以“threadLoop_”开头的若干接口,比如threadLoop_mix、threadLoop_sleepTime、threadLoop_standby等等。以这样的前缀开头,是因为这些函数都是在threadLoop这个主体里被调用的,可以说代表了这个PlaybackThread所需要完成的各个操作步骤。从上一小节可以了解到,当程序执行到PlaybackThread:
Step1@ PlaybackThread::threadLoop, 循环的条件是!exitPending()为true。这个函数属于Thread类,它主要通过判断内部变量mExitPending的值来得出是否要结束线程。变量mExitPending在Thread初始化时为fasle,如果后面有人通过requestExit()、requestExitAndWait等等来请求退出,这个值就会改变,从而使得Playback Thread结束循环。Step2@ PlaybackThread::threadLoop, 处理config事件。当有配置改变的事件发生时,可以通过sendConfigEvent来通知PlaybackThread。这个函数将把事件添加到mConfigEvents全局变量中,以供processConfigEvents进行处理。配置事件包括如下几种:
Step3@ PlaybackThread::threadLoop,判断当前是否符合Standby的条件,如果是的话就调用threadLoop_st andby。这个函数最终还是通过HAL层的接口来实现,如下:mOutput->stream->common.standby(&mOutput->stream->common);Step4@ PlaybackThread::threadLoop, 进行数据准备,prepareTracks_l这个函数非常长,我们先用伪代码的形式整理一下它所做的工作,如下所示:
现在我们针对上面的步骤来做“填空”。Step1@ MixerThread::prepareTracks_l, mActiveTracks的数据类型是SortedVector,用于记录当前活跃的Track。它会随着新的AudioTrack的加入而扩大,也会在必要的情况下(AudioTrack工作结束、或者出错等等)remove相应的Track。Step2&3@MixerThread::prepareTracks_l, 循环的条件就是要逐个处理该PlaybackThread中包含的Track。假如当前是一个FastTrack,我们还要做一些其它准备,这里就暂时不去涉及具体细节了。 Step4@ MixerThread::prepareTracks_l, 这一步是准备工作中最重要的,那就是缓冲数据。在学习代码细节前,我们先来了解数据传输时容易出现的underrun情况。什么是BufferUnderrun呢?当两个设备或进程间形成“生产者-消费者”关系时,如果生产的速度不及消费者消耗的速度,就会出现Underrun。以音频回放为例,此时用户听到的声音就可能是断断续续的,或者是重复播放当前buffer中的数据(取决于具体的实现)。如何避免这种异常的发生?这也是Step4所要解决的问题,以下分为几个小步骤来看AudioFlinger是如何做到的。 Step4.1,取得数据块audio_track_cblk_t*cblk = track->cblk()关于audio_track_cblk_t的更多描述,可以参见后面数据流小节。Step4.2 计算正确回放音频所需的最少帧数,初始值为1。
当track->sharedBuffer()为0时,说明AudioTrack不是STATIC模式的,否则数据就是一次性传送的,可以参见AudioTrack小节的描述。全局变量mSampleRate 是通过mOutput->stream->common.get_sample_rate获得的,它是由HAL提供的,代表的是设备的Sampling rate。 如果两者一致的话,就采用mNormalFrameCount,这个值在readOutputParameters函数中进行初始化。如果两者不一致的话,就要预留多余的量做rounding(+1)和interpolation(+1)。另外,还需要考虑未释放的空间大小,也就是getUnreleasedFrames得到的。得出的minFrames必需小于数据块的总大小,因而最后有个ASSERT。通常情况下frameCount分配的是一个buffer的两倍,可以参见AudioTrack小节的例子。 Step4.3 数据是否准备就绪了? 上一步我们计算出了数据的最小帧值,即minFrames,接下来就该判断目前的情况是否符合这一指标了,代码如下所示:
数据未准备就绪,对于音量的设置还有很多细节,大家有兴趣的可以深入研究下。在得到vl、vr和va的值后,还需要把它们应用到AudioMixer中去,不过在prepareTracks_l中还只是调用mAudioMixer->setParameter设置了这些参数,真正的实现是在threadLoop_mix中,我们后面会讲到这个函数。 Step5@ MixerThread::prepareTracks_l, 通过对每个Track执行上述的处理后,最后要返回一个结果,这通常取决于: ①是否有activetrack ②active track的数据是否已经准备就绪 返回的最终值将影响到threadLoop的下一步操作。完成了prepareTracks_l的分析,我们再回到前面的thread Loop。Step5@ PlaybackThread::threadLoop, 如果上一步的数据准备工作已经完成(即返回值是MIXER_T RAC KS_READY),就开始进行真正的混音操作,即threadLoop_mix,否则会休眠一定的时间——如此循环往复直到退出循环体。
这样就进入AudioMixer的处理了,我们放在下一小节做统一分析。假如数据还没有准备就绪,那么AudioFlin ger将调用threadLoop_sleepTime来计算需要休眠多长时间(变量sleepTime),并在threadLoop主循环的末尾(在remove track之前)执行usleep进入休眠。Step6@ PlaybackThread::threadLoop, 将数据写到HAL中,从而逐步写入到硬件设备中。
分为两种情况: 如果是采用了NBAIO(Non-blocking AudioI/O),即mNormalSink不为空,则通过它写入HA否则使用普通的AudioStreamOut(即mOutput变量)将数据输出Step7@ PlaybackThread::threadLoop, 移除tracksToRemove中指示的Tracks。是否移除一个Track是在prepareTracks_l中判断中,可以概括为以下几种情况:对于Fast Trac,如果它的状态(mState)是STOPPING_2、PAUSED、TERMINATED、STOPPED、FLUSHED,或者状态是ACTIVE但underun的次数超过限额(mRetryCount),则会被加入tracksToRemove列表中 当前的Track数据未准备就绪的情况下,且是STATICTRACK或者已经停止/暂停,也会被加入tracksToRemove列表中在tracksToRemove列表中的Track,与其相关的output将收到stop请求(由AudioSystem::stopOutput发起)关于AudioFlinger中与AudioTrack、AudioPolicyService有交互的部分,我们还将在后续小节进行阐述。 |
Android音频系统之AudioFlinger(二)
最新推荐文章于 2024-05-20 14:08:31 发布