OpenMAX

来源:互联网 发布:b2c建站 编辑:程序博客网 时间:2024/04/20 06:26

http://www.xuebuyuan.com/2117774.html

libstagefright openmax编解码数据流向分析

<iframe id="cproIframe_u1788635_2" width="336" height="280" src="http://pos.baidu.com/acom?adn=3&amp;at=231&amp;aurl=&amp;cad=1&amp;ccd=24&amp;cec=UTF-8&amp;cfv=16&amp;ch=0&amp;col=zh-cn&amp;conBW=0&amp;conOP=1&amp;cpa=1&amp;dai=2&amp;dis=0&amp;ltr=http%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DHczDPZW2S13LDVFRXz0LSat_Ev94g84FDaw4fhRFuHGudg2HUbdQC1nS6S3XpB5s%26ie%3Dutf-8%26f%3D8%26tn%3Dbaidu%26wd%3DopenMAX%2520emptyBuffer&amp;ltu=http%3A%2F%2Fwww.xuebuyuan.com%2F2117774.html&amp;lu_161=0&amp;lunum=6&amp;n=83099053_cpr&amp;pcs=1600x799&amp;pis=10000x10000&amp;ps=326x1026&amp;psr=1600x900&amp;pss=1600x346&amp;qn=3a92d6157ae8025f&amp;rad=&amp;rsi0=336&amp;rsi1=280&amp;rsi5=4&amp;rss0=%23FFFFFF&amp;rss1=%23FFFFFF&amp;rss2=%230000ff&amp;rss3=%23444444&amp;rss4=%23008000&amp;rss5=&amp;rss6=%23e10900&amp;rss7=&amp;scale=&amp;skin=tabcloud_skin_3&amp;stid=5&amp;td_id=1788635&amp;titFF=%E5%AE%8B%E4%BD%93&amp;titFS=12&amp;titTA=left&amp;tn=text_default_336_280&amp;tpr=1426224257228&amp;ts=1&amp;version=2.0&amp;xuanting=0&amp;dtm=BAIDU_DUP2_SETJSONADSLOT&amp;dc=2&amp;di=u1788635&amp;tt=1426224257205.176.214.215" align="center,center" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="margin: 0px; padding: 0px; border-width: 0px; background: transparent;"></iframe>

OMXCodec::read第一次调用时,mInitialBufferSubmit值为true,这个标志就是用来标识OMXCodec::read是否是第一次调用的。

if (mInitialBufferSubmit) { },中

1、把标志mInitialBufferSubmit置为false

2、调用drainInputBuffers,把输入通道中的所有输入缓存区,逐个传递给drainInputBuffer

3、在drainInputBuffer中会从调用mSource->read读取原始数据,填充到缓存区中,然后调用mOMX->emptyBuffer发消息给openmax

4、调用fillOutputBuffers把输出通道中的输出缓冲区,逐个传递给fillOutputBuffer。

5、在fillOutputBuffer中调用mOMX->fillBuffer发消息给openmax,相当于把缓冲区传递给openmax

6

    while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {

        if ((err = waitForBufferFilled_l()) != OK) {

            return err;

        }

}

等待输出缓冲区的数据,如果有数据就往下走,读取数据输出。

等OMXCodec::read后续调用时,就直接到上述第6步,等待输出缓冲区数据。

PS: 有两个点说明一下

1、mBufferFilled,是Condition类型。在上述第6步会等待这个信号,这个信号主要是在omx_message::FILL_BUFFER_DONE处理流程中发送,这个事件是openmax解码数据后把一个输出缓冲区填充满了就触发这个事件。

2、mFilledBuffers数据类型是List<size_t>,它存储的不是缓冲区地址而是输出缓冲区索引。在omx_message::FILL_BUFFER_DONE处理流程中有下面两行代

                mFilledBuffers.push_back(i);

                mBufferFilled.signal();

即把已经填充好的输出缓冲区索引保存到mFilledBuffers中,然后再发信号。

OMXCodec::read第6步检查到有数据可读了,就从mFilledBuffers读取头部的缓冲区索引,同时把这个索引从List中删除,然后根据索引找到缓冲区,再把缓冲区地址赋值给输出指针输出,这个缓存区的引用计数会加1,上层使用完会释放。

输入缓冲区更新:

如果一个输入缓冲区数据被读取完了,openmax会触发事件omx_message::EMPTY_BUFFER_DONE通知上层,在这个事件处理流程中,会根据发送来的bufferid找到对应的输入缓冲区,然后把这个缓冲区传递给drainInputBuffer,执行上面的第3步流程。

输出缓冲区更新:

输出缓冲区会被传出交给上层使用(传递个渲染器使用),使用完后需要把这个缓存区重新交给openmax,(个人推测openmax把一个缓存填充满后,就把这个缓冲区置为不可用,所以上层使用完后,需要重新发消息给openmax通知它这个缓冲区可以用了,输入缓冲区也是这样处理的),上层使用完输出缓冲区后会调用MediaBuffer::release进行销毁,在这个接口中会把输出缓冲区的引用计数减1,然后调用signalBufferReturned,实际对应OMXCodec::signalBufferReturned接口,再下一层调用fillOutputBuffer,把这个缓冲区重新交给openmax

总结:这样就是通过第一次调用drainInputBuffers触发openmax,然后后面依靠openmax的事件驱动来完成数据的读取、解码操作。


=====================================================================================

OMXCodec是一个MediaSource,完成数据的parse和decode。而OMXCodec则主要通过IOMX跨越OpenBinder机制操作OMX来实现。

重点介绍一下OMX。OMX主要完成三个任务,NodeInstance列表的管理,针对一个NodeInstance的操作以及事件的处理。

一、NodeInstance列表的管理。

这个主要包括NodeInstance的生成(allocateNode)和删除(freeNode)。其实就是对mDispatchers和 mNodeIDToInstance进行添加和删除。mNodeIDToInstance就是一个key为node_id,value为 NodeInstance的名值对列表。而mDispatchers就是一个key为node_id,value为 OMX::CallbackDispatcher的名值对列表。并且,一个NodeInstance都拥有一个 OMX::CallbackDispatcher。

二、NodeInstance节点的操作。

主要成员函数如下:

sendCommand

getParameter

setParameter

… …

fillBuffer

emptyBuffer

getExtensionIndex

这些方法执行时,都是先通过findInstance在mNodeIDToInstance列表中找到对应的NodeInstance,然后调用NodeInstance对应的方法。

三、事件处理

先看一下OMXNodeInstance.cpp中的这样一段代码:

12345
[c] view plaincopy
  1. OMX_CALLBACKTYPE OMXNodeInstance<span style="color:#339933">::</span><span style="color:#202020">kCallbacks</span> <span style="color:#339933">=</span> <span style="color:#009900">{</span>  
  2.    
  3.     <span style="color:#339933">&</span>OnEvent<span style="color:#339933">,</span> <span style="color:#339933">&</span>OnEmptyBufferDone<span style="color:#339933">,</span> <span style="color:#339933">&</span>OnFillBufferDone  
  4.    
  5. <span style="color:#009900">}</span><span style="color:#339933">;</span>  

它把三个OMXNodeInstance类的静态方法注册给了kCallbacks。而kCallbacks在哪里使用呢?看一下OMX.cpp中的allocateNode方法中的代码:

12345
[java] view plaincopy
  1. OMX_ERRORTYPE err <span style="color:#339933">=</span> mMaster<span style="color:#339933">-></span>makeComponentInstance<span style="color:#009900">(</span>  
  2.    
  3.             name, <span style="color:#339933">&</span>OMXNodeInstance<span style="color:#339933">::</span>kCallbacks,  
  4.    
  5.             instance, <span style="color:#339933">&</span>handle<span style="color:#009900">)</span><span style="color:#339933">;</span>  

事件处理函数传给了组件ComponentInstance。当组件有事件发生时,就会调用OMXNodeInstance中这几个注册过的事件处理函数,而这几个函数又会去调用OMX中对应的函数,也就是下面这三个:

OnEvent、OnEmptyBufferDone、OnFillBufferDone。

这几个方法都采用相同的路子:根据node_id找到CallbackDispatcher,并把事件信息post过去。具体点儿,就是调用findDispatcher(node)->post(msg)。

这里不得不提一下CallbackDispatcher的实现机制。它内部开启了一个线程,使用了信号量机制。可以看一下OMX::CallbackDispatcher的属性:Condition mQueueChanged;

可以看出findDispatcher(node)->post(msg)是一个异步操作,只把msg给POST过去,不会等待事件处理完毕就返回了。那么CallbackDispatcher是怎么处理接收到的msg呢?看以下代码:

12345678
[c] view plaincopy
  1. OMX<span style="color:#339933">::</span><span style="color:#202020">CallbackDispatcher</span><span style="color:#339933">::</span><span style="color:#202020">threadEntry</span><span style="color:#009900">(</span><span style="color:#009900">)</span>  
  2.    
  3.           dispatch<span style="color:#009900">(</span>msg<span style="color:#009900">)</span><span style="color:#339933">;</span>  
  4.    
  5.    
  6.      mOwner<span style="color:#339933">-></span>onMessage<span style="color:#009900">(</span>msg<span style="color:#009900">)</span><span style="color:#339933">;</span>  
  7.    
  8. mObserver<span style="color:#339933">-></span>onMessage<span style="color:#009900">(</span>msg<span style="color:#009900">)</span><span style="color:#339933">;</span>  

这个mObserver是哪来的?OMXCodec::Create中初始化IOMX时传入的。

12345
[c] view plaincopy
  1. sp<span style="color:#339933"><</span>OMXCodecObserver<span style="color:#339933">></span> observer <span style="color:#339933">=</span> new OMXCodecObserver<span style="color:#339933">;</span>  
  2.    
  3. ... ...  
  4.    
  5. <span style="color:#202020">omx</span><span style="color:#339933">-></span>allocateNode<span style="color:#009900">(</span>componentName<span style="color:#339933">,</span> observer<span style="color:#339933">,</span> <span style="color:#339933">&</span>node<span style="color:#009900">)</span><span style="color:#339933">;</span>  

这样算下来,事件最终还是跨越OpenBinder又传到了OMXCodec里面去,交给OMXCodecObserver了。

对节点的操作

NodeInstance的大部分方法的实现,如sendCommand等,都是通过OMX_Core.h中的宏定义间接调用 OMX_Component.h中的OMX_COMPONENTTYPE这个struct中的相应函数指针来完成。在这里提到的OMX_Core.h和 OMX_Component.h都是OpenMAX标准头文件。
原文链接:http://blog.csdn.net/a345017062/archive/2011/02/17/6190905.aspx


0 0
原创粉丝点击