前面讲了OMX服务主要完成三个任务:NodeInstance列表的管理、NodeInstance的操作、事件的处理。最后这个事件处理就是放大要看的内容。要一步一步进行编解码操作,事件传递进行通信是必不可少的环节
OMXCodec
与OMX callback
事件的处理时序图
从时序图来看,首先要建立一个OMXCodecObserver
类,该类是OMXCodec的内部类,在create
函数中被创建,并把对应的OMXCodec加入自己的观察范围内,在Create中
其次初始化它的callback事件和事件的派发处理函数。那么OMX主要的callback事件有哪些呢?我们可以查看OMXNodeInstance.cpp的kCallbacks
函数
callback在allocateNode
定义
即每个Component对应一组回调事件,这些回调事件由哪些函数返回呢?查看OMX_Core.h
有了callback事件,如何dispatch?我们在allocateNode函数中已经定义好dispatch
的函数了
如何从OMX中分发事件到OMXCodec
有了Observer
、Callback Event
、CallbackDispatcher
,那么一个时间如何从OMX分发到OMXCodec?
以下以EmptyBuffer流程来具体看
首先查看mVideoSource->read,实际上就是调用OMXCodec::read函数
接下来看看drainInputBuffer
的实现代码(截取部分)
在OMXCodec::fillOutputBuffer(BufferInfo *info)
中会调用下图的函数
fillOutputBuffer作用是把输出通道中的输出缓冲区逐个传递给输出Buffer。在OMXCodec中的fillOutputBuffer函数调用mOMX->fillBuffer
发送消息给OMX,相当于把缓冲区传递给OMX。以上是第一次执行OMXCodec::read时的操作,当整个编解码流程运行起来之后,会面临一个输入输出缓冲区更新的问题
缓冲区更新过程
输入缓冲区更新过程为,如果一个输入串冲去数据被读取完,OMX就触发事件omx_message::EMPTY_BUFFER_DONE
通知上层,在这个事件处理流程中,会根据发送来的bufferid找到对应的输入缓冲区,然后把这个缓冲区传递给drainInputBuffer,继续往下执行,在OMXCodec::on_mseeage
中有如下代码
输出缓冲区更新过程如下:解码完毕后,OMX组件触发omx_message::FILL_BUFFER_DONE
,输出缓冲区会被传出交给上层使用(传递给SurfaceFlinger
来显示),使用完后需要把这个缓冲区重新交给OMX。上层使用完输出缓冲区后会调用MediaBuffer::release
销毁缓冲区,在这个接口中会给输出缓冲区的引用计数减一。然后调用signalBufferReturned
,实际对应OMXCodec::signalBufferReturned
接口。最后,下一层调用fillOutputBuffer函数,把这个缓冲区重新交给OMX
综上所述,这样就通过第一次调用drainInputBuffer触发了OMX,然后依靠OMX的事件驱动来完成数据的读取、解码操作
以输出缓冲区为例。OMX Component解码完一帧之后,会调用ppCallbacks->FillBufferDone,也就是调用了之前初始化好的OMX::OnFillBufferDone
然后查看post函数
接下来把这消息分发出去
消息回调
在消息回调中,主要通过onMessage
函数处理消息,然后回调给上面调用OMX接口的类
通过调用OMXCodecObserver把消息通过onMessage
函数传递给OMXCodec
在OMXCodec::on_message中的case omx_message::FILL_BUFFER_DONE
虽然在OMX框架中输入/输出端口上的Buffer的生产和消费是异步的,但还是用过一个Condition mBufferFilled
来做了同步,通过信号量机制达到同步的目的。本质上是一个生产者/消费者模型。
Android中的OMX适配层是OMX IL层之上的封装层,在Android系统中被StageFright调用,也可以被其他部分调用