OpenMAX

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

libstagefright openmax编解码数据流向分析

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

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中的这样一段代码:

1
2
3
4
5
  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方法中的代码:

1
2
3
4
5
[java]  view plain copy
  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呢?看以下代码:

1
2
3
4
5
6
7
8
  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时传入的。

1
2
3
4
5
  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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TensorFlow OpenMax是一个用于图像分类任务的开源工具,可以用于处理和分析深度学习模型的输出概率分布。 OpenMax是一种开源的概率校准框架,旨在提高深度学习模型的不确定性估计。概率校准是指通过调整分类器的概率输出,使其更符合实际概率分布。传统的深度学习模型往往对于属于不同类别的样本,输出的概率很接近1或者0,忽略了模型的不确定性。而OpenMax通过引入Open Set Recognition的概念,对未知样本进行特殊处理,提供更加准确的概率估计。 TensorFlow OpenMax基于TensorFlow框架,提供了一套接口和工具,可以方便地集成到现有的深度学习模型中。通过调用OpenMax接口,可以获取深度学习模型的概率输出,然后使用OpenMax工具对这些概率进行校准。校准的过程包括计算置信度分布和外部期望分布,并进一步计算OpenMax概率。 使用TensorFlow OpenMax可以提高深度学习模型在图像分类任务中的表现。通过校准概率输出,可以更准确地估计模型的不确定性,并能够对未知样本进行更好的处理。这对于一些对模型的鲁棒性要求较高的应用场景非常有用。 总而言之,TensorFlow OpenMax是一个用于图像分类任务的开源工具,可以用于处理和校准深度学习模型的概率输出。它能够提高模型的不确定性估计,并能够对未知样本进行更好的处理,提高模型的鲁棒性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值