转自 http://blog.chinaunix.net/uid-7318785-id-3323948.html
Content
架构概述
代码详解
运作流程图
1. 架构概述
在Stagefright中,AwesomePlayer只负责完成Video的处理,而Audio部分则交由AudioPlayer完成。
整个Android Mediaframework 的运作部分可分为三层,即libmedia, libmediaplayerservice, libstagefright三大模块。AudioPlayer与AwesomePlayer同在stagefright模块,但AudioPlayer运作却联系到了mediaplayerservice层以及media层,如图所示。其中,mediaplayerservice模块中的AudioOutput作为AudioSink与AudioPlayer相接,而其本身又与media模块中的AudioTrack交互;AudioTrack是AudioThread线程触发的对象,主要完成对Audiobuffer的处理。AudioSystem则作为工具对象被AudioTrack所使用,例如获取audio的samplingrate, framecount等,另一个重要的作用就是AudioSystem通过Binder机制直接与AudioFingerService进行交互,并将AudioFingerClient交给AudioTrack使用。
2. 代码详解
当需要播放一段影音多媒体时,从上层调入到AwesomePlayer中的函数主要有三个,分别是setDataSource(), prepareAsync(), play()。而AudioPlayer是在play()函数之中建立的,AudioThread也是在这之后才开始的,因此前两个函数就不再展开了。这里要详说的是执行play()函数后的一系列关于audio的流程。
play()函数的实体是play_l(),主要完成两个工作。一是新建AudioPlayer,二是启动AudioPlayer。
点击(此处)折叠或打开
status_t AwesomePlayer::play_l() {
...
mAudioPlayer = new AudioPlayer(mAudioSink, this);
mAudioPlayer->start(); // inside of startAudioPlayer_l()
...
}
在这里可以注意到新建的同时设了两个参数给AudioPlayer,其中mAudioSink是在MediaPlayerService层执行SetDataSource()时初始化的,mAudioSink的实质对象其实就是位于该层的AudioOutput。
点击(此处)折叠或打开
status_t MediaPlayerService::Client::setDataSource(...)
{
...
mAudioOutput = new AudioOutput(mAudioSessionId);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
...
}
创建AudioPlayer第二个参数this就是把AwesomePlayer指针本身设给AudioPlayer作为观察者对象(mObserver), 以此获取AudioPlayer之后的通知,例如mObserver->postAudioEOS()。
当创建完AudioPlayer之后便调用其start()函数,AudioPlayer最核心的工作就从这里开始。
点击(此处)折叠或打开
status_t AudioPlayer::start(...)
{
mSource->read(...); ------- (1)
...
if (mAudioSink.get() != NULL)
{
mAudioSink->open(..., &AudioPlayer::AudioSinkCallback, ...); ------- (2)
mAudioSink->start(); ------- (3)
}
else
{
mAudioTrack = new AudioTrack(..., &AudioPlayer::AudioCallback, ...);
mAudioTrack->start();
}
}
(1)调用的是OMXCodec::read(),读取audio decoder数据,与video是一样的,这里就不详说了。
(2)调用了位于libmediaservice模块的AudioOutput对象的AudioOutput::open(),意为开启AudioSink,其实是做了启动audio线程的前期工作,我们来具体看下。
点击(此处)折叠或打开
status_t MediaPlayerService::AudioOutput::open( ..., AudioCallback cb, ... )
{
mCallback = cb; /*R2*/
...
AudioTrack *t;
if (mCallback != NULL) {
t = new AudioTrack(..., CallbackWrapper,...);
}
...
mTrack = t;
}
open函数里的形参cb其实就是AudioPlayer::AudioSinkCallback函数的指针,并把这个值设给mCallback,作为后续的回调函数使用。随后创建AudioTrack,并赋给mTrack。创建的同时会将自身的CallbackWrapper函数传给AudioTrack作为回调。
下面我们来看下AudioTrack创建的过程,这里涉及到一些关键的地方。
点击(此处)折叠或打开
AudioTrack::AudioTrack(..., callback_t cbf,...)
{
mStatus = set( ..., cbf,... );
}
点击(此处)折叠或打开
status_t AudioTrack::set(..., callback_t cbf,...)
{
...
status_t status = createTrack_l();
...
if (cbf != 0)
{
mAudioTrackThread = new AudioTrackThread(...);
...
}
...
mCbf = cbf; /*R1*/
}
AudioTrack构造函数调用了自身的set()主要完成了两个工作,createTrack和创建AudioTrackThread。另外,成员回调函数mCbf最后得到的值其实就是AudioOutput::CallbackWrapper()。
点击(此处)折叠或打开
status_t AudioTrack::createTrack_l(...)
{
const sp& audioFlinger = AudioSystem::get_audio_flinger();
...
sp track = audioFlinger->createTrack();
...
mAudioTrack = track;
}
在createTrack过程中,先由AudioSystem获取一个AudioFlinger的client对象,然后通过binder机制在AudioFlinger实体中执行createTrack,track当然也是在 AudioFlinger 内部创建的,由于是跨进程,具体过程不再追溯。
随后就是创建AudioTrackThread,该对象是用来驱动AudioTrack的,看到后面就会清楚。
至此,AudioOutput::open()的工作就结束了。到底做了哪些工作呢?归结起来就是两点。一是设置各层对象的回调函数;二是创建AudioTrack。而在AudioTrack的创建中其实就是创建了两个对象,一个位于AudioFlinger中的track,拿到的是该track的指针,另一个便是AudioTrackThread,用来对AudioTrack做线程驱动。如此一来,启动AudioTrack前的准备工作就算做好了。
(3)调用了AudioOutput::start(),我们下面就来看audio线程是如何启动和运作的。
点击(此处)折叠或打开
void MediaPlayerService::AudioOutput::start()
{
if (mTrack) {
...
mTrack->start();
}
}
点击(此处)折叠或打开
void AudioTrack::start()
{
sp<AudioTrackThread> t = mAudioTrackThread;
...
t->run(...);
...
status = mAudioTrack->start();
}
这边其实就两个工作:mAudioTrackThread->run() 和 mAudioTrack->start()。我们回顾下AudioOutput::open()时做的工作便可知道:前者是开启AudioTrackThread线程,从而驱动AudioTrack工作(其流程下面会做详细介绍)。后者则是开启AudioFlinger那侧的track,进行audio buffer的协同处理(属于AudioFlingerService进程范围不再深入)。
我们来看下 AudioTrackThread 具体运作流程。运行mAudioTrackThread->run()对应启动的是AudioTrackThread 父类的Thread::run()。
点击(此处)折叠或打开
status_t Thread::run(...)
{
...
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
...
}
这里无论是走哪个if分支都会启动一个Thread::_threadLoop()函数线程。
点击(此处)折叠或打开
int Thread::_threadLoop(void* user)
{
...
bool first = true;
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
...
}else{
result = self->threadLoop();
}
}while(strong != 0);
return 0;
}
正常情况下在这个函数体内会循环运行自身的threadLoop(),也就是子类的AudioTrackThread::threadLoop()。
点击(此处)折叠或打开
bool AudioTrack::AudioTrackThread::threadLoop()
{
return mReceiver.processAudioBuffer(this);
}
mReceiver就是AudioTrack对象本身,是AudioTrackThread随同AudioTrack一起创建的时候赋的值。所以这里函数循环的实体便是AudioTrack::processAudioBuffer()。
点击(此处)折叠或打开
bool AudioTrack::processAudioBuffer(...)
{
...
do{
status_t err = obtainBuffer(&audioBuffer, waitCount);
...
mCbf(..., &audioBuffer);
...
releaseBuffer(&audioBuffer);
}
while(frames);
}
这里最关键的就是调用了mCbf,这个回调函数的值可以参照前文代码中“/*R1*/”注释的内容。其实就是调用了AudioOutput::CallbackWrapper()。
点击(此处)折叠或打开
void MediaPlayerService::AudioOutput::CallbackWrapper()
{
...
size_t actualSize = (*me->mCallback)(...);
...
}
函数内部又调用了AudioOutput自身的mCallback成员回调函数(参照上文代码/*R2*/),其实就是AudioPlayer::AudioSinkCallback()。
点击(此处)折叠或打开
size_t AudioPlayer::AudioSinkCallback()
{
...
return me->fillBuffer(buffer, size);
}
size_t AudioPlayer::fillBuffer(...)
{
...
mSource->read(&mInputBuffer, ...);
...
memcpy(data, mInputBuffer->data(), ...);
}
通过fillBuffer()调用了OMXCodec::read(),随即将codec处理完的数据(mInputBuffer)复制到data(之后AudioOutput应该会去取用data),这样就完成了整个audio线程的运作。
3.运作流程图