先从Java层AudioTrack类说起
一 AudioTrack Java类变化说明
二 AudioTrack JNI层变化说明
这一层包括JNI层和AudioTrack本身
typedef enum {
AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes
AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track
// to one output stream: no software mixer
AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of
// the device. It is unique and must be
// present. It is opened by default and
// receives routing, audio mode and volume
// controls related to voice calls.
AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", 《==什么叫fast track?太难理解了!目前,java层的audiotrack只会使用第一个标志。
// defined elsewhere
AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 // use deep audio buffers 《==deep buffer是个什么玩意?这个马赛克是不是太大了点?现在完全看不清楚啊??!
} audio_output_flags_t;
OK,上面有好几个马赛克,平常看看日本大片的时候也就撸过去了,但分析Audio可不行。把去马赛克的希望寄托在下一步AudioFlinger的分析上吧!
三 AudioFlinger变化说明
我们将根据AF工作的主要流程来介绍下变化情况:
3.1 AF创建和onFirstRef
恩,没什么太大变化。有三个点:
3.2 openOutput函数
openOutput 函数比较关键,其中会见到以前的老朋友MixerThread,AudioStreamOutput等。整个流程包括加载Audio相关的硬件so。这部 分工作在4.0的时候就有了,谈不上太多的变化。但物是人非,老朋友已经发生巨大变化了。先来看MixerThread家族。
图1 PlaybackThread家族
图1稍加解释:
ThreadBase的内部类TrackBase从ExtendedAudioBufferProvider派生,这个应该是新增加的。TrackBase嘛,大家把它理解成一个Buffer Container就好了。
ThreadBase 的内部类PMDeathRecipient用来监听PowerManagerService的死亡消息。这个设计有点搞,因为PMS运行在 SystemService中,只有SS挂了,PMS才会挂。而SS挂了,mediaserver也会被init.rc的规则给弄死,所以 AudioFlinger也会死。既然大家都一起死,速度很快。故,设置这个PMDeathRecipient有何大的意义呢?
再来看ThreadBase的一个重要子类PlaybackThread,这个类应该是做过大整容了。
其定义了一个枚举mixer_state,用来反映当前混音工作的状态,有MIXER_IDLE,MIXER_READY和MIXER_ENABLED
定义了几个虚函数,需要子类实现,包括threadLoop_mix,prepareTracks_l等。这几个函数的抽象工作做得还是可以。但变化之大让人防不胜防啊。
Track类增加了从VolumeProvider派生,这个VP是用来控制音量的。根据前面的介绍,在JB中,音量管理比以前来得细致
新增定义了TimedTrack。这个类的作用和前面提到的rtp aah有关。等同学们学完本篇,即可开展相应研究,打响歼灭战!
接下来看图2。
图2 MixerThread和它的弟兄们
图2,简单介绍一下:
MixerThread从PlaybackThread派生,这个关系至始至终不会变化,相信以后也不会。
MT最大的变化是其中几个重要的成员变量。大家肯定认识其中的AudioMixer,它是用来混音的。
新 增一个Soaker对象(由编译宏控制),它是一个线程。这个单词的前缀soak在webster词典(相信经历过,那些年,我们一起GRE的日子 的人知道什么是webster)中最贴切的一条解释是to cause to pay an exorbitant amount。还是不很明白是干嘛的?再一看代码。原来,soaker就是一个专职玩弄CPU的线程。它的工作就是不断得做运算,拉高CPU使用率。它的 存在应该是为了测试新AF框架在多核CPU上的效率等等等的问题。所以,低端智能机们,你们不要玩JB了。
另外一条证明低端智能机不能随便玩JB的铁证就是:我们看到MT中新增了一个FastMixer,它也是一个线程。明白了?在JB中,多核智能机上,混音工作可以放到FastMixer所在的线程来做,当然速度,效率会高了。
FastMixer 工作流程比较复杂,又牵扯到多线程同步。所以,这里定义了一个FastMixerStateQueue,它由typedef StateQueue
FasetMixerState类似状态机,有一个enum Command,用来控制状态的。FastMixerState中含有一个八元组的FastTracks数组。FastTrack是用来完成FastMixer的一个功能类。
每个FastTrack都有一个mBufferProvider,该成员类型为SourceAudioBufferProvider。
以上的内容已经比较复杂了,下面来介绍下MixerThread对象创建中碰到的其他内容:
#p#
3.3 MixerThread创建
通 过图1和图2,应该对AF的几个主要成员有了认识。可惜啊,上面MixerThread中还有一个mOutputSink成员,没看到吧?它就和我们前面 提到的NBAIO(Non-block Audio I/O )有重大关系。NBAIO的存在,是为了想实现非阻塞的音频输入输出操作。下面是这个类的注释:
NBAIO注释:
// This header file has the abstract interfaces only. Concrete implementation classes are declared
// elsewhere. Implementations _should_ be non-blocking for all methods, especially read() and
// write(), but this is not enforced. In general, implementations do not need to be multi-thread
// safe, and any exceptions are noted in the particular implementation.
NBAIO只是定义了一个接口,需要去实现具体的实现类。当然,它要求read/write函数是非阻塞的,真实实现到底是不是阻塞,由实现者去控制。
个人感觉这部分框架还没有完全成熟,但NBIO的引入,需要同学们小心,相对而言,难度也比较大。下面我们通过图3来看看NBAIO的一些内容。
图3 NBAIO相关内容
图3解释如下:
NBAIO_Sink对应output端点,其定义了write和writeVia函数,writeVia函数需要传递一个回调函数via,其内部将调用这个via函数获取数据。类似数据的推/拉两种模式。
NBAIO_Source对应input端点,其定义了read和readVia函数。意义同NBAIO_Sink。
定 义一个MonoPipe和MonoPipeReader。Pipe即管道,MonoPipe和LINUX中的IPC通信Pipe没毛关系,只不过借用了这 个管道概念和思路。MonoPipe即只支持单个读者的Pipe(AF中,它是MonoPipeReader)。这两个Pipe,代表了Audio的 Output和Input端点。
MT中由mOutputSink指向 AudioStreamOutSink,此类用NBAIO_Sink派生,用于普通的mixer的输出。mPipeSink指向MonoPipe,本意是 用于FastMixer的。另外,还有一个变量mNormalSink,它将根据FastMixer的情况,指向mPipeSink,或者是 mOutputSink。这段控制的逻辑如下:
switch (kUseFastMixer) { //kUseFastMixer用于控制FastMixer的使用情况,一共4种:
case FastMixer_Never: //永远不使用FastMixer,这个选项用于调试,即关闭FastMixer的情况
case FastMixer_Dynamic: //根据情况,动态使用。根据注释,这个功能似乎还没有完全实现好
mNormalSink = mOutputSink;
break;
case FastMixer_Always: //永远使用FastMixer,调试用
mNormalSink = mPipeSink;
break;
case FastMixer_Static://静态。默认就是这个。但具体是否使用mPipeSink,将收到initFastMixer的控制
mNormalSink = initFastMixer ? mPipeSink : mOutputSink;
break;
}
由上所述,kUseFastMixer默认是FastMixer_Static,但mNormalSink是否指向mPipeSink,还由initFastMixer控制。这个变量本身又有mFrameCount和
mNormalFrameCount的大小决定,只有mFrameCount小于mNormalFrameCount时,initFastMixer才为真。晕了....这两个frameCount由PlaybackThread的
readOutputParameters得到。请同学们自己研究这段代码吧,就是一些简单的计算。想要搞明白的话,最好带着参数进去,把值都算出来。
好了,MixerThread的创建就分析到此,最好还是把这段代码多研究研究。了解几个兄弟对象是做什么的....
3.4 createTrack和start说明
createTrack 中最大的变化就是新增了对MediaSyncEvent同步机制的处理。MediaSyncEvent的目的很简单,其Java API的解释如下:startRecording(MediaSyncEvent) is used to start capture only when the playback on a particular audio session is complete. The audio session ID is retrieved from a player (e.g MediaPlayer, AudioTrack or ToneGenerator) by use of the getAudioSessionId() method. 简单点讲,就是必须等上一个player工作完毕了,才能开始下一个播放或者录制。这个机制解决了Android长久以来的声音经常混着出来的问题(目前 一个恶心但却实效的方法就是加一个sleep,以错开多个player不同步的问题。)。注意,iPhone上就没有这个问题。
另外,这个机制的潜在好处就是解放了做AudioPolicy AudioRoute工作的同学们,似乎(个人感觉是可以解决这个问题的)可以不用再去琢磨到底sleep多少时间,在哪加sleep的问题了
在AF中,MediaSyncEvent机制的代表是SyncEvent。大家自己看看就好。
start函数的变化不大,其中加了对SyncEvent的处理。
另外,createTrack中还涉及到FastMixer和TimedTrack处理。核心在PlaybackThread的createTrack_l和Track构造函数中。尤其是和FastMixer的关系。
根据图2,FM(FastMixer简写)内部用得数据结构是FastTrack,而MT用得是Track,所以这里存在一一对应的关系。FM的FastTrack是保存在数组中的,所以
使用FM的Track将通过mFastIndex来指向这个FastTrack。
现在搞清楚FastTrack和Track之间的关系即可,后续的数据流动还需要详细讨论
下面来看看MixerThread的工作流程。这部分是重头戏!
3.5 MixerThread的工作流程
这部分难的还是在FastMixer的工作原理上。不过这里提前和大家说:目前这个功能还没有做完,代码里边一堆的FIXME...。但屌丝们不要happy太早了,
估计马上、很快、必须得下个版本就好了。现在看看这个不成熟的东西,可以缓解以后看到成熟的东西的心理压力。
MT是一个线程,其工作内容主要在threadLoop中完成,而这个函数是由其基类PlaybackThread定义的,大体变化如下:
PlaybackThread的threadLoop定义了整个音频处理的大体流程,具体的细节通过几个虚函数(如prepareTracks_l,threadLoop_mix,threadLoop_write)交给子类去实现了
MT 变化大的首先是prepareTracks_l,首先处理的是FastMix类型的Track,判断标准是该Track是否设置了TRACK_FAST标 志(爽了,目前JB中还没有哪个地方使用了这个标志)。这部分判断比较复杂。首先FastMixer维护了一个状态机,另外,这个FastMixer运行 在自己的线程里,所以线程同步是必须的。这里采用的是状态来控制FastMixer的工作流程。由于涉及到多线程,所以音频的 underrun,overrun状态(不知道是什么吗?看前面提到的参考书!)也是一个需要处理的棘手问题。另外,一个MT是带一个 AudioMixer对象,这个对象将完成数据的混音,下变换等等超难度,数字音频处理等方面的工作。也就是说,对于混音来说,前期的prepare工作 还是由MT线程来完成,因为这样可以做到统一管理(有些Track并不需要使用FastMixer。但仔细一想,谁都希望处理越快越好,在多核CPU上, 将混音工作交给多个线程处理是充分利用CPU资源的典范,这应该是未来Android演化的趋势。所以,我估计这个JB还没完全长大....)。对 FastMixer感兴趣的屌丝们,请务必认真研究prepareTracks_l函数。
MT 下一个重要函数就是threadLoop_mix了,由于存在一个TimedTrack类,那么AudioMixer的process函数就带上了一个时 间戳,PTS,presentation timestamp。从编解码角度来说,还有一个DTS,Decode timestamp。这里要闲扯下PTS和DTS的区别了。DTS是解码时间,但编码的时候由于有可能会根据未来帧来编码当前帧。所以,解码的时候会先解 未来帧,然后解出当前帧,但是。你播放的时候可不能先播未来帧。只能老老实实得按播放顺序来先播当前帧,然后播未来帧(尽管先解出来的是未来帧)。关于 PTS/DTS,请屌丝们研究下IBP相关的知识吧。回到MT,这个PTS是从硬件hal对象取的,应该是HAL内部维护的时间戳。这个时间戳原则上会比 较准确。
混音完了,再做特效处理(和以前的版本差不多),然后调用 threadLoop_write。MT的threadLoop_write函数的输出端点就是前面那个坑爹的mNormalSink,如果不为空,就调 用它的write函数。想着是调用NBAIO_Sink的非阻塞的write函数。根据图2的分析,它有可能是那个MonoPipe,也有可能就是 AudioStreamOutputSink,这个sink节点用得就是以前的AudioStreamOutput。而MonoPipe的write其内 部就是一个buffer。并没有和真实的AUDIO HAL Output挂上关系。这.....咋整??(大胆假设,小心求证。只能是FastMixer把这个buffer取出来,然后再写到真实的Audio HAL中了。因为在MixerThread构造函数中,曾经为FastTrack保存过mOutputSink,这个就是用来和 AudioStreamOutput联系的)
另外,DulicatingThread,DirectOuptutThread没有太大变化。
四 FastMixer工作原理简单说明
我以前想得是:混音工作由FastMixer线程和MixerThread线程共同完成,但输出工作依然在MixerThread做。从上面MonoPipe的分析来看,这个判断可能不准。
既 有可能是输出工作也交给FastMixer来做,而MixerThread仅做一部分混音工作,然后把数据通过MonoPipe传给FastMixer线 程。FastMixer线程将自己的FastTrack的混音结果和MT的混音结果再做一次混音,然后再由FastMixer输出。
FM定义在FastMixer.cpp中,核心就是一个ThreadLoop。由于AF所有Track的预备工作由MT线程来做,所以FM的threadLoop基本上就是根据状态来做对应处理。
这 里的同步使用了LINUX中很底层的futex(Fast Userspace Mutex)。晕,futex是POSIX Mutex的实现基础。不知道写这段代码的人为何不直接用Mutex(估计还是嫌效率的问题,但是 妈的,用了Mutex效率能差多少?代码是写给人看的,太B4我们了...)。玩多线程玩到这种地步,佩服啊!不懂多线程编程的屌丝们,请仔细研究 Posix MultiThread Programming吧
FastMixer内部还使用了一个AudioMixer,用于它的混音
然后再write出去.....
这里是FM的简单说明,详细内容,没有拿个真机给我,我也没法整啊....欢迎乐善好施的兄弟们刷个4.1的机器,然后借给我研究下...
(这玩意,个人感觉也不是太难。东西嘛,耐不住琢磨,总能搞透的)。兄弟们今天知道FM和MT的大体工作流程就可以了。
五 其他变化
其他变化包括:
六 总结
我记得在研究2.2 AF的时候,AudioFlinger才3k多行,而JB已经有9K多行了。还没算其他的辅助类。从整体上看,JB变化趋势为:
还有其他的东西.....今天先到这了。
网站题目:AndroidAudio系统变化说明
文章地址:http://www.shufengxianlan.com/qtweb/news17/312717.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联