Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3514358
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: LINUX

2016-03-12 14:45:43

  • Content

  1. 架构概述
  2. 代码详解
  3. 运作流程图


1. 架构概述

Stagefright中,AwesomePlayer只负责完成Video的处理,而Audio部分则交由AudioPlayer完成。

整个Android Mediaframework 的运作部分可分为三层,即libmedia, libmediaplayerservice, libstagefright三大模块。AudioPlayerAwesomePlayer同在stagefright模块,但AudioPlayer运作却联系到了mediaplayerservice层以及media层,如图所示。其中,mediaplayerservice模块中的AudioOutput作为AudioSinkAudioPlayer相接,而其本身又与media模块中的AudioTrack交互;AudioTrackAudioThread线程触发的对象,主要完成对Audiobuffer的处理。AudioSystem则作为工具对象被AudioTrack所使用,例如获取audiosamplingrate, framecount等,另一个重要的作用就是AudioSystem通过Binder机制直接与AudioFingerService进行交互,并将AudioFingerClient交给AudioTrack使用。

2. 代码详解

当需要播放一段影音多媒体时,从上层调入到AwesomePlayer中的函数主要有三个,分别是setDataSource(), prepareAsync(), play()。而AudioPlayer是在play()函数之中建立的,AudioThread也是在这之后才开始的,因此前两个函数就不再展开了。这里要详说的是执行play()函数后的一系列关于audio的流程。 

play()函数的实体是play_l(),主要完成两个工作。一是新建AudioPlayer,二是启动AudioPlayer

点击(此处)折叠或打开

  1. status_t AwesomePlayer::play_l() {
  2.     ...
  3.     mAudioPlayer = new AudioPlayer(mAudioSink, this);
  4.     mAudioPlayer->start(); // inside of startAudioPlayer_l()
  5.     ...
  6. }

在这里可以注意到新建的同时设了两个参数给AudioPlayer,其中mAudioSink是在MediaPlayerService层执行SetDataSource()时初始化的,mAudioSink的实质对象其实就是位于该层的AudioOutput

点击(此处)折叠或打开

  1. status_t MediaPlayerService::Client::setDataSource(...)
  2. {
  3.     ...
  4.     mAudioOutput = new AudioOutput(mAudioSessionId);                 
  5.     static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
  6.     ...
  7. }

创建AudioPlayer第二个参数this就是把AwesomePlayer指针本身设给AudioPlayer作为观察者对象(mObserver), 以此获取AudioPlayer之后的通知,例如mObserver->postAudioEOS()

当创建完AudioPlayer之后便调用其start()函数AudioPlayer最核心的工作就从这里开始。

点击(此处)折叠或打开

  1. status_t AudioPlayer::start(...)
  2. {
  3.   mSource->read(...);         ------- (1)

   ...

  1.   if (mAudioSink.get() != NULL)
  2.   {
  3.     mAudioSink->open(..., &AudioPlayer::AudioSinkCallback, ...);   ------- (2)
  4.     mAudioSink->start();      ------- (3)
  5.   }
  6.   else
  7.   {
  8.     mAudioTrack = new AudioTrack(..., &AudioPlayer::AudioCallback, ...);
  9.     mAudioTrack->start();
  10.   }
  11. }
(1)调用的是OMXCodec::read(),读取audio decoder数据,与video是一样的,这里就不详说了。

(2)调用了位于libmediaservice模块的AudioOutput对象的AudioOutput::open(),意为开启AudioSink,其实是做了启动audio线程的前期工作,我们来具体看下。

点击(此处)折叠或打开

  1. status_t MediaPlayerService::AudioOutput::open( ..., AudioCallback cb, ... )
  2. {
  3.     mCallback = cb/*R2*/

  4.     ...
  5.     
  6.    AudioTrack *t;
  7.    if (mCallback != NULL) {
  8. t = new AudioTrack(..., CallbackWrapper,...);
  9.    }

  10.    ...

  11.    mTrack = t;
  1. }
open函数里的形参cb其实就是AudioPlayer::AudioSinkCallback函数的指针,并把这个值设给mCallback,作为后续的回调函数使用。随后创建AudioTrack,并赋给mTrack。创建的同时会将自身的CallbackWrapper函数传给AudioTrack作为回调。

下面我们来看下AudioTrack创建的过程,这里涉及到一些关键的地方。

点击(此处)折叠或打开

  1. AudioTrack::AudioTrack(..., callback_t cbf,...)
  2. {
  3.     mStatus = set( ...cbf,... );
  4. }

点击(此处)折叠或打开

  1. status_t AudioTrack::set(..., callback_t cbf,...)
  2. {
  3.     ...

  4.     status_t status = createTrack_l();

  5.     ...
  6.     
  7.     if (cbf != 0) 
  8.     {
  9.         mAudioTrackThread = new AudioTrackThread(...);
  10.         ...
  11.     }

  12.     ...

      mCbf = cbf;  /*R1*/
  1. }
AudioTrack构造函数调用了自身的set()主要完成了两个工作,createTrack和创建AudioTrackThread。另外,成员回调函数mCbf最后得到的值其实就是AudioOutput::CallbackWrapper()。

点击(此处)折叠或打开

  1. status_t AudioTrack::createTrack_l(...)
  2. {
  3.     const sp& audioFlinger = AudioSystem::get_audio_flinger();
     
     ...

     sp track = audioFlinger->createTrack();

     ...

     mAudioTrack = track;
  1. }
在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线程是如何启动和运作的。

点击(此处)折叠或打开

  1. void MediaPlayerService::AudioOutput::start()
  2. {
  3.      if (mTrack) {

  4.          ...

  5.          mTrack->start();
  6.      }
  7. }

点击(此处)折叠或打开

  1. void AudioTrack::start()
  2. {
  3.      sp<AudioTrackThread> t = mAudioTrackThread;

  4.      ...

  5.      t->run(...);

  6.      ...

  7.      status = mAudioTrack->start();
  8. }
这边其实就两个工作:mAudioTrackThread->run() 和 mAudioTrack->start()。我们回顾下AudioOutput::open()时做的工作便可知道:前者是开启AudioTrackThread线程,从而驱动AudioTrack工作(其流程下面会做详细介绍)。后者则是开启AudioFlinger那侧的track,进行audio buffer的协同处理(属于AudioFlingerService进程范围不再深入)。

我们来看下 AudioTrackThread 具体运作流程。运行mAudioTrackThread->run()对应启动的是AudioTrackThread 父类的Thread::run()。

点击(此处)折叠或打开

  1. status_t Thread::run(...)
  2. {
  3.      ...

  4.      if (mCanCallJava) {
  5.          res = createThreadEtc(_threadLoop,
  6.                  this, name, priority, stack, &mThread);
  7.      } else {
  8.          res = androidCreateRawThreadEtc(_threadLoop,
  9.                  this, name, priority, stack, &mThread);
  10.      }

  11.      ...
  12. }
这里无论是走哪个if分支都会启动一个Thread::_threadLoop()函数线程。

点击(此处)折叠或打开

  1. int Thread::_threadLoop(void* user)
  2. {
      ...

  1.      bool first = true;
  2.      
  3.      do {         
  4.         bool result;
  5.         if (first) {
  6.             first = false;
  7.             self->mStatus = self->readyToRun();
  8.            
  9.             ...

  10.         }else{

  11.             result = self->threadLoop();
  12.         }         

      }while(strong != 0);

      return 0;
  1. }
正常情况下在这个函数体内会循环运行自身的threadLoop(),也就是子类的AudioTrackThread::threadLoop()。

点击(此处)折叠或打开

  1. bool AudioTrack::AudioTrackThread::threadLoop()
  2. {
  3.     return mReceiver.processAudioBuffer(this);
  4. }
mReceiver就是AudioTrack对象本身,是AudioTrackThread随同AudioTrack一起创建的时候赋的值。所以这里函数循环的实体便是AudioTrack::processAudioBuffer()。

点击(此处)折叠或打开

  1. bool AudioTrack::processAudioBuffer(...)
  2. {
     ...

  1.     do{
         status_t err = obtainBuffer(&audioBuffer, waitCount);

         ...

         mCbf(..., &audioBuffer);

         ...

         releaseBuffer(&audioBuffer);
     }
     while(frames);
  1. }
这里最关键的就是调用了mCbf,这个回调函数的值可以参照前文代码中“/*R1*/”注释的内容。其实就是调用了AudioOutput::CallbackWrapper()。

点击(此处)折叠或打开

  1. void MediaPlayerService::AudioOutput::CallbackWrapper()
  2. {
  3.     ...

  4.     size_t actualSize = (*me->mCallback)(...);

  5.     ...
  6. }
函数内部又调用了AudioOutput自身的mCallback成员回调函数(参照上文代码/*R2*/),其实就是AudioPlayer::AudioSinkCallback()。

点击(此处)折叠或打开

  1. size_t AudioPlayer::AudioSinkCallback()
  2. {
  3.     ...

     return me->fillBuffer(buffer, size);
  1. }

 size_t AudioPlayer::fillBuffer(...) 
 {
     ...

     mSource->read(&mInputBuffer, ...);

     ...

     memcpy(data, mInputBuffer->data(), ...);

 }

通过fillBuffer()调用了OMXCodec::read(),随即将codec处理完的数据(mInputBuffer)复制到data(之后AudioOutput应该会去取用data),这样就完成了整个audio线程的运作。

3.运作流程图


阅读(1248) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~