深入剖析 Stagefright AudioPlayer

转自 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.运作流程图


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值