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的事件驱动来完成数据的读取、解码操作。