首页 > 代码库 > MediaPlayer本地播放流程解析(三)

MediaPlayer本地播放流程解析(三)

[cpp] view plaincopy
  1. status_t AwesomePlayer::play() {  
  2.     ……  
  3.     return play_l();  
  4. }  
  5.   
  6. status_t AwesomePlayer::play_l() {  
  7.     ……  
  8.     if (mAudioSource != NULL) {  
  9.         if (mAudioPlayer == NULL) {  
  10.             createAudioPlayer_l();  
  11.         }  
  12.   
  13.         if (mVideoSource == NULL) {  
  14.             status_t err = startAudioPlayer_l(  
  15.                     false /* sendErrorNotification */);  
  16.             ……;  
  17.         }  
  18.     }  
  19.   
  20.     if (mVideoSource != NULL) {  
  21.         // Kick off video playback  
  22.         postVideoEvent_l();  
  23.   
  24.         if (mAudioSource != NULL && mVideoSource != NULL) {  
  25.             postVideoLagEvent_l();  
  26.         }  
  27.     }  
  28.     ……  
  29. }  

如果是播放AVI、MP4等视频文件,则m_AudioSource和mVideoSource都不为null,如果是播放MP3文件,则m_AudioSource不为null而mVideoSource为null。现在按照播放视频文件的流程来分析。

先看createAudioPlayer_1();

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void AwesomePlayer::createAudioPlayer_l()  
  2. {  
  3.     ……  
  4.     mAudioPlayer = new AudioPlayer(mAudioSink, flags, this);  
  5.     mAudioPlayer->setSource(mAudioSource);  
  6.     ……  
  7. }  

可以看到,创建了audioPlayer,且把mAudioSource传给了创建的mAudioPlayer。

接着看postVideoEvent_l

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {  
  2.     ATRACE_CALL();  
  3.   
  4.     if (mVideoEventPending) {  
  5.         return;  
  6.     }  
  7.   
  8.     mVideoEventPending = true;  
  9.     mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);  
  10. }  

有关TimedEventQueue的介绍,在上一篇TimedEventQueue分析。mVideoEvent在AwesomePlayer的构造函数中定义,如下:

mVideoEvent = new AwesomeEvent(this,&AwesomePlayer::onVideoEvent);

所以,mQueue.postEventWithDelay()后,将会执行到onVideoEvent

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void AwesomePlayer::onVideoEvent() {  
  2.     ……  
  3.     if (!mVideoBuffer) {  
  4.         ……  
  5.         for (;;) {  
  6.             status_t err = mVideoSource->read(&mVideoBuffer, &options);  
  7.             ……  
  8.             if (mVideoBuffer->range_length() == 0) {  
  9.                 mVideoBuffer->release();  
  10.                 mVideoBuffer = NULL;  
  11.                 continue;  
  12.             }  
  13.             break;  
  14.         }  
  15.   
  16.         {  
  17.             Mutex::Autolock autoLock(mStatsLock);  
  18.             ++mStats.mNumVideoFramesDecoded;  
  19.         }  
  20.     }  
  21.   
  22.     ……  
  23.     if (mAudioPlayer != NULL && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {  
  24.         status_t err = startAudioPlayer_l();  
  25.     }  
  26.   
  27.     if ((mFlags & TEXTPLAYER_INITIALIZED)  
  28.             && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {  
  29.         mTextDriver->start();  
  30.         modifyFlags(TEXT_RUNNING, SET);  
  31. }  
  32. ……  
  33.   
  34.     if ((mNativeWindow != NULL)  
  35.             && (mVideoRendererIsPreview || mVideoRenderer == NULL)) {  
  36.         mVideoRendererIsPreview = false;  
  37.   
  38.         initRenderer_l();  
  39.     }  
  40.   
  41.     if (mVideoRenderer != NULL) {  
  42.         mSinceLastDropped++;  
  43.         mVideoRenderer->render(mVideoBuffer);  
  44.         if (!mVideoRenderingStarted) {  
  45.             mVideoRenderingStarted = true;  
  46.             notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);  
  47.         }  
  48.   
  49.         if (mFlags & PLAYING) {  
  50.             notifyIfMediaStarted_l();  
  51.         }  
  52.     }  
  53.     ……  
  54.     postVideoEvent_l();  
  55. }  

接下来分步分析

step1 : status_t err= mVideoSource->read(&mVideoBuffer, &options); 这个方法的作用就是从数据源读取数据,然后将数据送给解码器解码,最后解码器将解码好的数据输出,并通过输出参数buffer传出,为后面的render 所用。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t OMXCodec::read(  
  2.         MediaBuffer **buffer, const ReadOptions *options) {  
  3.     ……  
  4.     if (mInitialBufferSubmit) { // only run once,之后通过解码器调用之前设置的回调函数来调用drainInputBuffers来获取原始视频数据  
  5.         mInitialBufferSubmit = false;  
  6.         ……  
  7.         drainInputBuffers(); // 通过videoTrack读取数据后将数据送给解码器处理  
  8.   
  9.         if (mState == EXECUTING) {  
  10.             // Otherwise mState == RECONFIGURING and this code will trigger  
  11.             // after the output port is reenabled.  
  12.             fillOutputBuffers(); // 向解码器请求输出数据  
  13.         }  
  14.     }  
  15. ……  
  16. // 等待解码器输出解码完成  
  17.     while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {  
  18.         if ((err = waitForBufferFilled_l()) != OK) {  
  19.             return err;  
  20.         }  
  21.     }  
  22.     ……  
  23.     *buffer = info->mMediaBuffer; // 输出数据给AwesomePlayer  
  24.     return OK;  
  25. }  

drainInputBuffers()和fillOutputBuffers()这两个函数后面再分析,先接着看onVideoEvent里面的step2 : status_t err = startAudioPlayer_l();

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t AwesomePlayer::startAudioPlayer_l(bool sendErrorNotification) {  
  2.     ……  
  3.     err = mAudioPlayer->start(  
  4.                 true /* sourceAlreadyStarted */);  
  5.     ……  
  6.     return err;  
  7. }  
  8. status_t AudioPlayer::start(bool sourceAlreadyStarted) {  
  9. ……  
  10. mAudioTrack = new AudioTrack(AUDIO_STREAM_MUSIC,  
  11. mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audioMask,  
  12. 0, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, 0);  
  13. ……  
  14. // start之后,mAudioTrack开始播放并通过回调函数AudioCallback来获取音频数据  
  15. mAudioTrack->start();  
  16. ……  
  17. return OK;  
  18. }  
  19. void AudioPlayer::AudioCallback(int event, void *info) {  
  20.     switch (event) {  
  21.     case AudioTrack::EVENT_MORE_DATA:  
  22.         {  
  23.         AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;  
  24.         // fillBuffer通过mSource->read(&mInputBuffer, &options)读取数据,mSource为audioTrack  
  25.         size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size);  
  26.         buffer->size = numBytesWritten;  
  27.         }  
  28.         break;  
  29.   
  30.     case AudioTrack::EVENT_STREAM_END:  
  31.         mReachedEOS = true;  
  32.         notifyAudioEOS();  
  33.         break;  
  34.     }  
  35. }  

Step3 : initRenderer_l() 创建render

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void AwesomePlayer::initRenderer_l() {  
  2.     ……  
  3.     if (USE_SURFACE_ALLOC  
  4.             && !strncmp(component, "OMX.", 4)  
  5.             && strncmp(component, "OMX.google.", 11)) {  
  6.         // Hardware decoders avoid the CPU color conversion by decoding  
  7.         // directly to ANativeBuffers, so we must use a renderer that  
  8.         // just pushes those buffers to the ANativeWindow.  
  9.         // 创建硬件加速渲染的render  
  10.         mVideoRenderer =  
  11.             new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);  
  12.     } else {  
  13.         // Other decoders are instantiated locally and as a consequence  
  14.         // allocate their buffers in local address space.  This renderer  
  15.         // then performs a color conversion and copy to get the data  
  16.         // into the ANativeBuffer.  
  17.         // 创建软件渲染的render  
  18.         mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, meta);  
  19.     }  
  20. }  

Step4 : mVideoRenderer->render(mVideoBuffer);

就是将mVideoBuffer的内容输出到屏幕

到现在为止,onVideoEvent流程分析完成,接下来详细分析drainInputBuffers和fillOutputBuffers,这两个函数都是通过OMX和解码器通讯的函数。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. bool OMXCodec::drainInputBuffer(BufferInfo *info) {  
  2. ……  
  3. // mSource为mVideoTrack,通过read方法读取原始视频数据  
  4. err = mSource->read(&srcBuffer);  
  5. ……  
  6. // 通过OMX的emptyBuffer调用解码库解码数据  
  7. err = mOMX->emptyBuffer(  
  8.             mNode, info->mBuffer, 0, offset,  
  9.             flags, timestampUs);  
  10.   
  11.     info->mStatus = OWNED_BY_COMPONENT;  
  12.   
  13.     return true;  
  14. }  
  15. void OMXCodec::fillOutputBuffer(BufferInfo *info) {  
  16.     // 通过调用OMX的fillBuffer来获取解码后的帧数据  
  17.     status_t err = mOMX->fillBuffer(mNode, info->mBuffer);  
  18.   
  19.     info->mStatus = OWNED_BY_COMPONENT;  
  20. }  

貌似很简单,其实调用解码器的这个流程还是蛮复杂的,下面以emptyBuffer为例来梳理一下:

首先,mOMX为OMX的Bp端,其Bn端的实现在OMX.cpp里面

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t OMX::emptyBuffer(  
  2.         node_id node,  
  3.         buffer_id buffer,  
  4.         OMX_U32 range_offset, OMX_U32 range_length,  
  5.         OMX_U32 flags, OMX_TICKS timestamp) {  
  6.     return findInstance(node)->emptyBuffer(  
  7.             buffer, range_offset, range_length, flags, timestamp);  
  8. }  

再看findInstance(node),node是prepare时调用OMX的allocateNode方法得到的,findInstance将会根据这个node值返回之前创建的OMXNodeInstance对象,其emptyBuffer定义如下:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t OMXNodeInstance::emptyBuffer(  
  2.         OMX::buffer_id buffer,  
  3.         OMX_U32 rangeOffset, OMX_U32 rangeLength,  
  4.         OMX_U32 flags, OMX_TICKS timestamp) {  
  5.     Mutex::Autolock autoLock(mLock);  
  6.   
  7.     OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;  
  8.     header->nFilledLen = rangeLength;  
  9.     header->nOffset = rangeOffset;  
  10.     header->nFlags = flags;  
  11.     header->nTimeStamp = timestamp;  
  12.   
  13.     BufferMeta *buffer_meta =  
  14.         static_cast<BufferMeta *>(header->pAppPrivate);  
  15.     buffer_meta->CopyToOMX(header);  
  16.   
  17.     OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);  
  18.   
  19.     return StatusFromOMXError(err);  
  20. }  
  21.   
  22. #define OMX_EmptyThisBuffer(                                \  
  23.         hComponent,                                         \  
  24.         pBuffer)                                            \  
  25.     ((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer(      \  
  26.         hComponent,                                         \  
  27.         pBuffer)                        /* Macro End */  

从代码中,我们可以看出实际上是调用了mHandle的EmptyThisBuffer方法,mHandle是什么呢?在prepare过程中,我们调用了OMX的allocateNode方法,在这个方法里面,我们通过调用OMXNodeInstance的setHandle方法设置了其值。再回顾一下其实现:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. status_t OMX::allocateNode(  
  2.         const char *name, const sp<IOMXObserver> &observer, node_id *node) {  
  3.     Mutex::Autolock autoLock(mLock);  
  4.   
  5.     *node = 0;  
  6.   
  7.     // 创建instance  
  8.     OMXNodeInstance *instance = new OMXNodeInstance(this, observer);  
  9.   
  10.     // 创建解码器组件,并将其值赋值给handle  
  11.     OMX_COMPONENTTYPE *handle;  
  12.     OMX_ERRORTYPE err = mMaster->makeComponentInstance(  
  13.             name, &OMXNodeInstance::kCallbacks,  
  14.             instance, &handle);  
  15.   
  16.     if (err != OMX_ErrorNone) {  
  17.         ALOGV("FAILED to allocate omx component ‘%s‘", name);  
  18.   
  19.         instance->onGetHandleFailed();  
  20.   
  21.         return UNKNOWN_ERROR;  
  22.     }  
  23.   
  24.     *node = makeNodeID(instance);  
  25.     mDispatchers.add(*node, new CallbackDispatcher(instance));  
  26.   
  27.     instance->setHandle(*node, handle);  
  28.   
  29.     mLiveNodes.add(observer->asBinder(), instance);  
  30.     observer->asBinder()->linkToDeath(this);  
  31.   
  32.     return OK;  
  33. }  
  34.   
  35. void OMXNodeInstance::setHandle(OMX::node_id node_id, OMX_HANDLETYPE handle) {  
  36.     CHECK(mHandle == NULL);  
  37.     mNodeID = node_id;  
  38.     mHandle = handle;  
  39. }  

OK,至此整个发送数据到解码器的流程就跑通了,但是前面也说了,在OMXCodec的read方法中,drainInputBuffers方法只会执行一次,之后要再往解码器送数据该怎么弄呢?就要靠回调了。一起来看一下这个回调的流程是怎么弄的吧?

在OMX的allocateNode方法中

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. OMX_COMPONENTTYPE *handle;  
  2. OMX_ERRORTYPE err = mMaster->makeComponentInstance(  
  3.         name, &OMXNodeInstance::kCallbacks,  
  4.         instance, &handle);  

创建解码器时,就注册了回调函数

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {  
  2.     &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone  
  3. };  

当解码器将原始数据全部解码完成后,就会调用OMXNodeInstance 的OnEmptyBufferDone方法

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(  
  2.         OMX_IN OMX_HANDLETYPE hComponent,  
  3.         OMX_IN OMX_PTR pAppData,  
  4.         OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {  
  5.     OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);  
  6.     if (instance->mDying) {  
  7.         return OMX_ErrorNone;  
  8.     }  
  9.     return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);  
  10. }  

Instance->owner()实际上就是OMX,再看OMX的OnEmptyBufferDone

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. OMX_ERRORTYPE OMX::OnEmptyBufferDone(  
  2.         node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {  
  3.     ALOGV("OnEmptyBufferDone buffer=%p", pBuffer);  
  4.   
  5.     omx_message msg;  
  6.     msg.type = omx_message::EMPTY_BUFFER_DONE;  
  7.     msg.node = node;  
  8.     msg.u.buffer_data.buffer = pBuffer;  
  9.   
  10.     findDispatcher(node)->post(msg);  
  11.   
  12.     return OMX_ErrorNone;  
  13. }  

findDispatcher返回CallbackDispatcher对象,是OMX的内部类,也是在OMX的allocate方法中创建的,CallbackDispatcher在创建时启动了一个线程,不断的获取消息,然后调用dispatch方法执行之,看dispatch方法:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {  
  2.     if (mOwner == NULL) {  
  3.         ALOGV("Would have dispatched a message to a node that‘s already gone.");  
  4.         return;  
  5.     }  
  6.     mOwner->onMessage(msg);  
  7. }  

mOwner为OMXNodeInstance对象,所以看OMXNodeInstance的onMessage方法。

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void OMXNodeInstance::onMessage(const omx_message &msg) {  
  2.     ……  
  3.     mObserver->onMessage(msg);  
  4. }  

mObserver为OMXCodecObserver对象,是在prepare时在OMXCodec::Create中创建,下面看其onMessage方法实现

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. virtual void onMessage(const omx_message &msg) {  
  2.     sp<OMXCodec> codec = mTarget.promote();  
  3.   
  4.     if (codec.get() != NULL) {  
  5.         Mutex::Autolock autoLock(codec->mLock);  
  6.         codec->on_message(msg);  
  7.         codec.clear();  
  8.     }  
  9. }  

codec的值为OMXCodec对象,看其on_message的实现

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
    1. void OMXCodec::on_message(const omx_message &msg) {  
    2.     ……  
    3. switch (msg.type) {  
    4.     ……  
    5.         case omx_message::EMPTY_BUFFER_DONE:  
    6.         {  
    7.             ……  
    8.             if (mPortStatus[kPortIndexInput] == DISABLING) {  
    9.                 ……  
    10.             } else if (mState != ERROR  
    11.                     && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {  
    12.                 CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED);  
    13.   
    14.                 if (mFlags & kUseSecureInputBuffers) {  
    15.                     drainAnyInputBuffer();  
    16.                 } else {  
    17.                     // 看,数据用完后又开始要新的数据了  
    18.                     drainInputBuffer(&buffers->editItemAt(i));  
    19.                 }  
    20.             break;  
    21.         }  
    22.   
    23.         case omx_message::FILL_BUFFER_DONE:  
    24.         {  
    25.             // 可以看到,FILL_BUFFER_DONE的回调流程和EMPTY_BUFFER_DONE基本一样  
    26.             // 这里做的主要的事情就是通知AwesomePlayer解码库有解码好的帧输出,可以播放了  
    27.             ……  
    28.         }  
    29.     }  

MediaPlayer本地播放流程解析(三)