Android4.2.2的Stagefright中编解码器数据流的维护

来源:互联网 发布:那英精选.知英情歌 编辑:程序博客网 时间:2024/06/08 06:46

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:gzzaigcn2012@gmail.com

Android源码版本Version:4.2.2; 硬件平台 全志A31

 

前沿:

在前面的博文中,基本提到的是stagefright相关的控制流,具体分析了android架构中的MediaExtractor、AwesomePlayer、StagefrightPlayer、OMXCodec等的创建,底层OMXNodinstance实例的创建。分析了OMX最底层插件库、编解码器组件的架构以及如何创建属于我们自己的OMX Plugin。

分析源码架构的另一个关键是数据流的分析,从这里开始,我们将对stagefright中的编解码缓存区进行分析:

1.

回到OMXCodec的创建过程的源码:

[cpp] view plain copy
  1. status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {  
  2. .......  
  3.     mVideoSource = OMXCodec::Create(  
  4.             mClient.interface(), mVideoTrack->getFormat(),//提取视频流的格式, mClient:BpOMX;mVideoTrack->getFormat()  
  5.             false// createEncoder,不创建编码器false  
  6.             mVideoTrack,  
  7.             NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);//创建一个解码器mVideoSource  
  8.   
  9.     if (mVideoSource != NULL) {  
  10.         int64_t durationUs;  
  11.         if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {  
  12.             Mutex::Autolock autoLock(mMiscStateLock);  
  13.             if (mDurationUs < 0 || durationUs > mDurationUs) {  
  14.                 mDurationUs = durationUs;  
  15.             }  
  16.         }  
  17.   
  18.         status_t err = mVideoSource->start();//启动解码器OMXCodec,完成解码器的init初始化操作  
  19. .............  
  20. }  

在Android4.2.2下Stagefright多媒体架构中的A31的OMX插件和Codec组件 博文我们对于OMXCodec::create已经做了详细的分析,这里来关注mVideoSource->start的相关功能,即OMXCodec::start的处理:

[cpp] view plain copy
  1. status_t OMXCodec::start(MetaData *meta) {  
  2.     Mutex::Autolock autoLock(mLock);  
  3. ........  
  4.     return init();//进行初始化操作  
  5. }  

这里调用init()的过程,将会进行buffer的申请操作,为后续的流操作打下基础:

[cpp] view plain copy
  1. status_t OMXCodec::init() {  
  2.     // mLock is held.  
  3. .........  
  4.     err = allocateBuffers();//缓存区的分配  
  5.     if (err != (status_t)OK) {  
  6.         return err;  
  7.     }  
  8.   
  9.     if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {  
  10.         err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);  
  11.         CHECK_EQ(err, (status_t)OK);  
  12.   
  13.         setState(LOADED_TO_IDLE);  
  14.     }  
  15. ............  
  16. }  

我们来看allocateBuffers的实现

 

2.关注allocateBuffersOnPort的实现

[html] view plain copy
  1. status_t OMXCodec::allocateBuffers() {  
  2.     status_t err = allocateBuffersOnPort(kPortIndexInput);//输入缓存input口分配  
  3.   
  4.     if (err != OK) {  
  5.         return err;  
  6.     }  
  7.   
  8.     return allocateBuffersOnPort(kPortIndexOutput);//输出缓存input口分配  
  9. }  

这里分别将对输入和输出口进行Buffer的申请与分配,对于解码器,需要输入口来存储待解码的数据源,需要将解码后的数据源存储到输出口,而这也符合硬件的实现逻辑。以输入缓存区分配为例展开分析:

[cpp] view plain copy
  1. status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {  
  2. .......  
  3.     OMX_PARAM_PORTDEFINITIONTYPE def;  
  4.     InitOMXParams(&def);  
  5.     def.nPortIndex = portIndex;//输入口  
  6.   
  7.     err = mOMX->getParameter(  
  8.             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));//获取输入口参数到def  
  9. ..........  
  10.                 err = mOMX->allocateBuffer(  
  11.                         mNode, portIndex, def.nBufferSize, &buffer,  
  12.                         &info.mData);  
  13. ........  
  14.       info.mBuffer = buffer;//获取对应的buffer_id,有保存有底层的buffer的相关信息  
  15.         info.mStatus = OWNED_BY_US;  
  16.         info.mMem = mem;  
  17.         info.mMediaBuffer = NULL;  
  18.  ...........  
  19.         mPortBuffers[portIndex].push(info);//把当前的buffer恢复到mPortBuffers[2]中去  

上述过程主要分为:

step1:先是获取底层解码器组件的当前的参数熟悉,一般这些参数都在建立OMX_Codec时完成的初始配置,前一博文中已经提到过。

step2:进行allocateBuffer的处理,这个函数的调用最终交给底层的OMX组件来完成,相关的实现将集成到A31的底层OMX编解码组件的处理流中进行分析。

step3:完成对分配好的buffer信息info,维护在mPortBuffers[0]这个端口中。

上述过程完成了输入与输出的Buffer分配,为后续解码操作buffer打下了基础。

 

3.mediaplay启动播放器

通过start的API调用,进入MediaplayerService::Client,再依次经过stagefrightplayer,AwesomePlayer。触发play的videoevent的发生.

[html] view plain copy
  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. }  

根据前一博文的分析可知,该事件对应的处理函数为AwesomePlayer::onVideoEvent(),该部分代码量较大,提取核心内容read的处理进行分析:

[html] view plain copy
  1. status_t err = mVideoSource->read(&mVideoBuffer, &options);//循环读数据实际的OMX_CODEC::read,读取到mVideoBuffer  

read的核心是获取可以用于render的视频数据,这表明了read函数主要完成了从视频源读取元数据,并调用解码器完成解码生成可送显的数据。

 

4. read函数的实现

可以想象read函数的应该是一个比较复杂的过程,我们从OMX_Codec的read函数入手来分析:

[html] view plain copy
  1. status_t OMXCodec::read(  
  2.         MediaBuffer **buffer, const ReadOptions *options) {  
  3.     status_t err = OK;  
  4.     *buffer = NULL;  
  5.   
  6.     Mutex::Autolock autoLock(mLock);  
  7.   
  8.         drainInputBuffers();//buffer,填充数据源  
  9.   
  10.         if (mState == EXECUTING) {  
  11.             // Otherwise mState == RECONFIGURING and this code will trigger  
  12.             // after the output port is reenabled.  
  13.             fillOutputBuffers();  
  14.         }  
  15.     }  
  16.   
  17. ...........  
  18. }  

read的核心逻辑总结为drainInputBuffers()和fillOutputBuffers(),我们对其依次进行深入的分析

 

5. drainInputBuffers()读取待解码的视频数据源到解码器的Inport

这里贴出其较为复杂的处理过程代码,主要分为以下3个部分进行分析:

(1)

[cpp] view plain copy
  1. bool OMXCodec::drainInputBuffer(BufferInfo *info) {<pre code_snippet_id="360665" snippet_file_name="blog_20140523_9_9848139" class="html" name="code">   if (mCodecSpecificDataIndex < mCodecSpecificData.size()) {  
  2.         CHECK(!(mFlags & kUseSecureInputBuffers));  
  3.   
  4.         const CodecSpecificData *specific =  
  5.             mCodecSpecificData[mCodecSpecificDataIndex];  
  6.   
  7.         size_t size = specific->mSize;  
  8.   
  9.         if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME)  
  10.                 && !(mQuirks & kWantsNALFragments)) {  
  11.             static const uint8_t kNALStartCode[4] =  
  12.                     { 0x00, 0x00, 0x00, 0x01 };  
  13.   
  14.             CHECK(info->mSize >= specific->mSize + 4);  
  15.   
  16.             size += 4;  
  17.   
  18.             memcpy(info->mData, kNALStartCode, 4);  
  19.             memcpy((uint8_t *)info->mData + 4,  
  20.                    specific->mData, specific->mSize);  
  21.         } else {  
  22.             CHECK(info->mSize >= specific->mSize);  
  23.             memcpy(info->mData, specific->mData, specific->mSize);//copy前面的数据字段  
  24.         }  
  25.   
  26.         mNoMoreOutputData = false;  
  27.   
  28.         CODEC_LOGV("calling emptyBuffer with codec specific data");  
  29.   
  30.         status_t err = mOMX->emptyBuffer(  
  31.                 mNode, info->mBuffer, 0, size,  
  32.                 OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,  
  33.                 0);//处理buffer  
  34.         CHECK_EQ(err, (status_t)OK);  
  35.   
  36.         info->mStatus = OWNED_BY_COMPONENT;  
  37.   
  38.         ++mCodecSpecificDataIndex;  
  39.         return true;  
  40.     }</pre>...............(1)<br>  

这部分的内容主要是提取一部分解码器字段,填充到info->mData的存储空间中去。这部分主要基于视频源的格式,如mp4等在创建OXMCodec病configureCodec时就完成了这个mCodecSpecificData字段的添加,应该些解码需要的特殊字段吧。是否需要要看其视频源的格式。获取完这个字段信息后就是正式读取视频源的数据了。

 

(2)

[html] view plain copy
  1. for (;;) {  
  2.       MediaBuffer *srcBuffer;  
  3.       if (mSeekTimeUs >= 0) {  
  4.           if (mLeftOverBuffer) {  
  5.               mLeftOverBuffer->release();  
  6.               mLeftOverBuffer = NULL;  
  7.           }  
  8.   
  9.           MediaSource::ReadOptions options;  
  10.           options.setSeekTo(mSeekTimeUs, mSeekMode);  
  11.   
  12.           mSeekTimeUs = -1;  
  13.           mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC;  
  14.           mBufferFilled.signal();  
  15.   
  16.           err = mSource->read(&srcBuffer, &options);//读取视频源中的真实数据这里是MPEG4Source的read  
  17.   
  18.           if (err == OK) {  
  19.               int64_t targetTimeUs;  
  20.               if (srcBuffer->meta_data()->findInt64(  
  21.                           kKeyTargetTime, &targetTimeUs)  
  22.                       && targetTimeUs >= 0) {  
  23.                   CODEC_LOGV("targetTimeUs = %lld us", targetTimeUs);  
  24.                   mTargetTimeUs = targetTimeUs;  
  25.               } else {  
  26.                   mTargetTimeUs = -1;  
  27.               }  
  28.           }  
  29.       } else if (mLeftOverBuffer) {  
  30.           srcBuffer = mLeftOverBuffer;  
  31.           mLeftOverBuffer = NULL;  
  32.   
  33.           err = OK;  
  34.       } else {  
  35.           err = mSource->read(&srcBuffer);  
  36.       }  
  37.   
  38.       if (err != OK) {  
  39.           signalEOS = true;  
  40.           mFinalStatus = err;  
  41.           mSignalledEOS = true;  
  42.           mBufferFilled.signal();  
  43.           break;  
  44.       }  
  45.   
  46.       if (mFlags & kUseSecureInputBuffers) {  
  47.           info = findInputBufferByDataPointer(srcBuffer->data());  
  48.           CHECK(info != NULL);  
  49.       }  
  50.   
  51.       size_t remainingBytes = info->mSize - offset;//buffer中剩余的可以存储视频数据的空间  
  52.   
  53.       if (srcBuffer->range_length() > remainingBytes) {//当前读取的数据已经达到解码的数据量  
  54.           if (offset == 0) {  
  55.               CODEC_LOGE(  
  56.                    "Codec's input buffers are too small to accomodate "  
  57.                    "buffer read from source (info->mSize = %d, srcLength = %d)",  
  58.                    info->mSize, srcBuffer->range_length());  
  59.   
  60.               srcBuffer->release();  
  61.               srcBuffer = NULL;  
  62.   
  63.               setState(ERROR);  
  64.               return false;  
  65.           }  
  66.   
  67.           mLeftOverBuffer = srcBuffer;//把没读取的buffer记录下来  
  68.           break;  
  69.       }  
  70.   
  71.       bool releaseBuffer = true;  
  72.       if (mFlags & kStoreMetaDataInVideoBuffers) {  
  73.               releaseBuffer = false;  
  74.               info->mMediaBuffer = srcBuffer;  
  75.       }  
  76.   
  77.       if (mFlags & kUseSecureInputBuffers) {  
  78.               // Data in "info" is already provided at this time.  
  79.   
  80.               releaseBuffer = false;  
  81.   
  82.               CHECK(info->mMediaBuffer == NULL);  
  83.               info->mMediaBuffer = srcBuffer;  
  84.       } else {  
  85.           CHECK(srcBuffer->data() != NULL) ;  
  86.           memcpy((uint8_t *)info->mData + offset,  
  87.                   (const uint8_t *)srcBuffer->data()  
  88.                       + srcBuffer->range_offset(),  
  89.                   srcBuffer->range_length());//copy数据源数据到输入缓存,数据容量srcBuffer->range_length()  
  90.       }  
  91.   
  92.       int64_t lastBufferTimeUs;  
  93.       CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));  
  94.       CHECK(lastBufferTimeUs >= 0);  
  95.       if (mIsEncoder && mIsVideo) {  
  96.           mDecodingTimeList.push_back(lastBufferTimeUs);  
  97.       }  
  98.   
  99.       if (offset == 0) {  
  100.           timestampUs = lastBufferTimeUs;  
  101.       }  
  102.   
  103.       offset += srcBuffer->range_length();//增加偏移量  
  104.   
  105.       if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mMIME)) {  
  106.           CHECK(!(mQuirks & kSupportsMultipleFramesPerInputBuffer));  
  107.           CHECK_GE(info->mSize, offset + sizeof(int32_t));  
  108.   
  109.           int32_t numPageSamples;  
  110.           if (!srcBuffer->meta_data()->findInt32(  
  111.                       kKeyValidSamples, &numPageSamples)) {  
  112.               numPageSamples = -1;  
  113.           }  
  114.   
  115.           memcpy((uint8_t *)info->mData + offset,  
  116.                  &numPageSamples,  
  117.                  sizeof(numPageSamples));  
  118.   
  119.           offset += sizeof(numPageSamples);  
  120.       }  
  121.   
  122.       if (releaseBuffer) {  
  123.           srcBuffer->release();  
  124.           srcBuffer = NULL;  
  125.       }  
  126.   
  127.       ++n;  
  128.   
  129.       if (!(mQuirks & kSupportsMultipleFramesPerInputBuffer)) {  
  130.           break;  
  131.       }  
  132.   
  133.       int64_t coalescedDurationUs = lastBufferTimeUs - timestampUs;  
  134.   
  135.       if (coalescedDurationUs > 250000ll) {  
  136.           // Don't coalesce more than 250ms worth of encoded data at once.  
  137.           break;  
  138.       }  
  139.   }...........  

该部分是提取视频源数据的关键,主要通过 err = mSource->read(&srcBuffer, &options)来完成,mSource是在创建编解码器传入的,实际是一个对应于视频源格式的一个解析器MediaExtractor。比如在建立MP4的解析器MPEG4Extractor,通过新建一个new MPEG4Source。故最终这里调用的是MPEG4Source的read成员函数,其实际也维护着整个待解码的原始视频流。

我们可以看大在read函数后,会将待解码的数据流以for循环依次读入到底层的buffer空间中,只有当满足当前读取的原始数据片段比底层的input口的buffer剩余空间小srcBuffer->range_length() > remainingBytes,那就可以继续读取,否则直接break后,去进行下一步操作。或者如果一次待解码的数据时张是大于250ms也直接跳出。

这处理体现了处理的高效性。最终视频原始数据存储在info->mData的底层输入空间中。

 

(3)

[html] view plain copy
  1. err = mOMX->emptyBuffer(  
  2.         mNode, info->mBuffer, 0, offset,  
  3.         flags, timestampUs);  

触发底层的解码器组件进行处理。这部分留在后续对A31的底层编解码API操作时进行分析。

6.fillOutputBuffers对输出buffer口的填充,即实现解码过程:

[cpp] view plain copy
  1. void OMXCodec::fillOutputBuffers() {  
  2.     CHECK_EQ((int)mState, (int)EXECUTING);  
  3. ...........  
  4.      Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];输出端口  
  5.     for (size_t i = 0; i < buffers->size(); ++i) {  
  6.         BufferInfo *info = &buffers->editItemAt(i);  
  7.         if (info->mStatus == OWNED_BY_US) {  
  8.             fillOutputBuffer(&buffers->editItemAt(i));  
  9.         }  
  10.     }  
  11. }  
[cpp] view plain copy
  1. void OMXCodec::fillOutputBuffer(BufferInfo *info) {  
  2.     CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);  
  3.   
  4.     if (mNoMoreOutputData) {  
  5.         CODEC_LOGV("There is no more output data available, not "  
  6.              "calling fillOutputBuffer");  
  7.         return;  
  8.     }  
  9.   
  10.     CODEC_LOGV("Calling fillBuffer on buffer %p", info->mBuffer);  
  11.     status_t err = mOMX->fillBuffer(mNode, info->mBuffer);  
  12.   
  13.     if (err != OK) {  
  14.         CODEC_LOGE("fillBuffer failed w/ error 0x%08x", err);  
  15.   
  16.         setState(ERROR);  
  17.         return;  
  18.     }  
  19.   
  20.     info->mStatus = OWNED_BY_COMPONENT;  
  21. }  

从上面的代码看来,fillOutputBuffer的实现比drainInputBuffers简单了很多。但相同的是,两者最终都讲控制权交给底层的解码器来完成。

 

7.等待解码数据被fill到outbuffer中,OMXCodecObserver完成回调处理

等待解码完成的这部分内容在read函数中通过以下函数来实现:

[html] view plain copy
  1. while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {  
  2.     if ((err = waitForBufferFilled_l()) != OK) {//进入等待buffer被填充  
  3.         return err;  
  4.     }  
  5. }  

上述表明,只要mFilledBuffers为空则进入等待填充pthread_cond_timedwait。而这个线程被唤醒是通过底层的组件回调来完成的,回调函数的注册哎底层编解码器Node完成的,实际最终的回调是交给OMXCodecObserver来完成的:

[html] view plain copy
  1. struct OMXCodecObserver : public BnOMXObserver {  
  2.     OMXCodecObserver() {  
  3.     }  
  4.   
  5.     void setCodec(const sp<OMXCodec> &target) {  
  6.         mTarget = target;  
  7.     }  
  8.   
  9.     // from IOMXObserver  
  10.     virtual void onMessage(const omx_message &msg) {  
  11.         sp<OMXCodec> codec = mTarget.promote();  
  12.   
  13.         if (codec.get() != NULL) {  
  14.             Mutex::Autolock autoLock(codec->mLock);  
  15.             codec->on_message(msg);//OMX_Codec的on_message处理  
  16.             codec.clear();  
  17.         }  
  18.     }  

最终可以看到是由OMX_Codec->on_message来进行消息的处理,这部分的内容主要包括EMPTY_BUFFER_DONE和FILL_BUFFER_DONE两个message处理,对FILL_BUFFER_DONE完成后的消息回调进行分析:

[cpp] view plain copy
  1. void OMXCodec::on_message(const omx_message &msg) {  
  2.     if (mState == ERROR) {  
  3.         /* 
  4.          * only drop EVENT messages, EBD and FBD are still 
  5.          * processed for bookkeeping purposes 
  6.          */  
  7.         if (msg.type == omx_message::EVENT) {  
  8.             ALOGW("Dropping OMX EVENT message - we're in ERROR state.");  
  9.             return;  
  10.         }  
  11.     }  
  12.   
  13.     switch (msg.type) {                                                                                                                                         case omx_message::FILL_BUFFER_DONE://底层回调callback告知当前                        ..............  
  14.                 mFilledBuffers.push_back(i);//当前的输出buffer信息维护在mFilledBuffers  
  15.                 mBufferFilled.signal();//发出信息用于渲染  

可以看到这里对read线程进行了唤醒。

8.提取一个可用的解码后的数据帧

[cpp] view plain copy
  1. size_t index = *mFilledBuffers.begin();  
  2. mFilledBuffers.erase(mFilledBuffers.begin());  
  3.   
  4. BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);//从获取解码后的视频源  
  5. CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);  
  6. info->mStatus = OWNED_BY_CLIENT;  
  7.   
  8. info->mMediaBuffer->add_ref();//  
  9. if (mSkipCutBuffer != NULL) {  
  10.     mSkipCutBuffer->submit(info->mMediaBuffer);  
  11. }  
  12. *buffer = info->mMediaBuffer;  

获得了线程唤醒后的buffer,从这里获取到输出端口对应的Bufferinfo,作为最终的BufferInfo信息返回给read函数

9

经过5、6、7、8的处理过程,read最终返回可用于显示的mVideoBuffer,接下去就是如何送显的过程了。可以看到下面的代码,将会创建一个渲染器mVideoRenderer来完成这个解码后视频源的显示:

[html] view plain copy
  1.           <p>    if ((mNativeWindow != NULL)  
  2.             && (mVideoRendererIsPreview || mVideoRenderer == NULL)) {//首次创建渲染器  
  3.         mVideoRendererIsPreview = false;</p><p>        initRenderer_l();//初始化渲染器,新建一个AwesomeLocalRenderer  
  4.     }</p><p>    if (mVideoRenderer != NULL) {  
  5.         mSinceLastDropped++;  
  6.         mVideoRenderer->render(mVideoBuffer);//启动渲染,即显示当前buffer  
  7.         if (!mVideoRenderingStarted) {  
  8.             mVideoRenderingStarted = true;  
  9.             notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);  
  10.         }</p><p>    }</p>  
[html] view plain copy
  1. void AwesomePlayer::initRenderer_l() {  
  2.     ATRACE_CALL();  
  3.   
  4.     if (mNativeWindow == NULL) {  
  5.         return;  
  6.     }  
  7.   
  8.     sp<MetaData> meta = mVideoSource->getFormat();  
  9.   
  10.     int32_t format;  
  11.     const char *component;  
  12.     int32_t decodedWidth, decodedHeight;  
  13.     CHECK(meta->findInt32(kKeyColorFormat, &format));  
  14.     CHECK(meta->findCString(kKeyDecoderComponent, &component));  
  15.     CHECK(meta->findInt32(kKeyWidth, &decodedWidth));  
  16.     CHECK(meta->findInt32(kKeyHeight, &decodedHeight));  
  17.   
  18.     int32_t rotationDegrees;  
  19.     if (!mVideoTrack->getFormat()->findInt32(  
  20.                 kKeyRotation, &rotationDegrees)) {  
  21.         rotationDegrees = 0;  
  22.     }  
  23.   
  24.     mVideoRenderer.clear();  
  25.   
  26.     // Must ensure that mVideoRenderer's destructor is actually executed  
  27.     // before creating a new one.  
  28.     IPCThreadState::self()->flushCommands();  
  29.   
  30.     // Even if set scaling mode fails, we will continue anyway  
  31.     setVideoScalingMode_l(mVideoScalingMode);  
  32.     if (USE_SURFACE_ALLOC  
  33.             && !strncmp(component, "OMX.", 4)  
  34.             && strncmp(component, "OMX.google.", 11)  
  35.             && strcmp(component, "OMX.Nvidia.mpeg2v.decode")) {//使用硬件渲染器,除去上述的解码器  
  36.         // Hardware decoders avoid the CPU color conversion by decoding  
  37.         // directly to ANativeBuffers, so we must use a renderer that  
  38.         // just pushes those buffers to the ANativeWindow.  
  39.         mVideoRenderer =  
  40.             new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);//一般是使用硬件渲染机制  
  41.     } else {  
  42.         // Other decoders are instantiated locally and as a consequence  
  43.         // allocate their buffers in local address space.  This renderer  
  44.         // then performs a color conversion and copy to get the data  
  45.         // into the ANativeBuffer.  
  46.         mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, meta);  
  47.     }  
  48. }  

可以看到这里有2个渲染器的创建分支,OMX和OMX.google说明底层的解码器用的是软解码,那么他渲染器也使用所谓的本地渲染器实际是软渲染器。故这里我们使用的是AwesomeNativeWindowRenderer渲染器,其结构如下所述:

[html] view plain copy
  1. struct AwesomeNativeWindowRenderer : public AwesomeRenderer {  
  2.     AwesomeNativeWindowRenderer(  
  3.             const sp<ANativeWindow> &nativeWindow,  
  4.             int32_t rotationDegrees)  
  5.         : mNativeWindow(nativeWindow) {  
  6.         applyRotation(rotationDegrees);  
  7.     }  
  8.   
  9.     virtual void render(MediaBuffer *buffer) {  
  10.         ATRACE_CALL();  
  11.         int64_t timeUs;  
  12.         CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));  
  13.         native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000);  
  14.         status_t err = mNativeWindow->queueBuffer(  
  15.                 mNativeWindow.get(), buffer->graphicBuffer().get(), -1);//直接使用queuebuffer进行渲染显示  
  16.         if (err != 0) {  
  17.             ALOGE("queueBuffer failed with error %s (%d)", strerror(-err),  
  18.                     -err);  
  19.             return;  
  20.         }  
  21.   
  22.         sp<MetaData> metaData = buffer->meta_data();  
  23.         metaData->setInt32(kKeyRendered, 1);  
  24.     }  

不是很复杂,只是实现了AwesomeRenderer渲染接口render。最终调用这个函数来实现对buffer的显示。这里看到很熟悉的queueBuffer,大家可以回看我的博文Android4.2.2 SurfaceFlinger之图形渲染queueBuffer实现和VSYNC的存在感 ,这是通过应用程序的本地窗口mNativeWindow(因为播放器videoview继承了sufaceview,surfaceview类会创建一个本地的surface,其继承了本地窗口类)将当前buffer提交给SurfaceFlinger服务进行显示,具体内容不在展开。

至此我们完成了stagefright下的编解码的数据流的相关操作,程序上复杂主要体现在emptybuffer和fillbuffer为主。当然由于能力有限,在很多细节上也没有进行很详细的分析,也希望大家多交流,多学习。

阅读全文
0 0
原创粉丝点击