android ACodec MediaCodec NuPlayer flow

MediaCodec/ACodec流程

i. 初始化(从java层开始):
1、 java层中调用MediaCodec.createEncoder/DecoderByType(…)或MediaCodec.createByCodecName(…)创建编码器或者解码器
2、 根据不同的参数new MediaCodec,然后其中会调用native_setup(…)
3、 Native层android_media_MediaCode.cpp调用android_media_MediaCodec_native_setup(…),然后其中会new JMediaCodec
4、 在JMediaCodec的构建中会根据输入数据或输出数据是否为MIME类型调用MediaCodec::CreateByType(是)或MediaCodec::CreateByComponentName(否)。
5、 然后会new MediaCodec,接着调用MediaCodec::init(…),在init中先后进行如下操作:
首先调用MediaCodec::GetCodecBase(…)来根据情况,如果是要建编码器或解码器名字以”omx.”开通则new ACodec,构造ACodec过程中将其状态转换为UninitializedState,如果解码器名字以” android.filter.”开通则new MediaFilter
接着对于解码器调用MediaCodecList::getInstance()来获取MediaCodecList实例,然后去调用类IMediaCodecList中的findCodecByName(…)获取codec的索引,然后就能得到解码器支持的MIME格式以判断其是否为视频解码器。
若为video codec,则需要为其创建一个专门的ALooper,名为mCodecLooper;若为audio则与MediaCodec使用同一个ALooper,名为mLooper。
○1然后New AMessage,名为kWhatInit,再其对应的消息处理中调用ACodec::initiateAllocateComponent(…);○2而initiateAllocateComponent中又产生了新的kWhatAllocateComponent消息,在该消息处理中会去调用ACodec::UninitializedState::onAllocateComponent(…);
○3在onAllocateComponent中,首先调用OMXClient::connect(),通过binder机制获得MediaPlayerService和MediaCodecService,然后通过MediaPlayerService和MediaCodecService来创建OMX的实例【这样OMXClient就获得了OMX的入口,就可以通过binder机制来获得OMX提供的服务,也就是说OMXClient是android中openmax的入口】;其次会调用MediaCodecList::findMatchingCodecs(…) 获取codec在MediaCodecList中的索引;随后new CodecObserver用于消息的传递;接着调用IOMX中的allocateNode(…) 函数,随后调用OMX::allocateNode(…),在其中会new OMXNodeInstance创建实例,注册OMXNodeInstance::kCallbacks回调并调用OMXMaster::makeComponentInstance(…)【实际会调用到组件继承自OMXPluginBase的相关方法】来创建解码实体组件【生成OMX_COMPONENTTYPE对象】;然后向MediaCodec发送kWhatComponentAllocated消息,在消息处理中将MediaCodec状态设为INITIALIZED;再者将ACodec状态设为LoadedState。


ii. 初始化(从NuPlayer开始)


1、 NuPlayer::start()时产生一个kWhatStart,在消息处理中如果是暂停后的开始就调用NuPlayer::onResume()【只需mSource->resume()和mRenderer->resume()】;否则调用NuPlayer::onStart()
2、 在函数NuPlayer::onStart()中会先后进行如下操作:(设置Source状态、初始化Render、初始化解码器,并将三者关联起来)
若mSource【source类对象】没有开始,则mSource->start(),来启动音视频包读取;
创建Render和并为其创建一个名为mRendererLooper的ALooper,然后使用setRenderer方法将Render与解码器关联起来
调用NuPlayer::postScanSources(),然后其中生成kWhatScanSources消息,在消息处理中会根据情况调用NuPlayer::instantiateDecoder(…)初始化video或者audio解码器【需要在audio解码器之前实例化video解码器,因为成功初始化视频解码器可能会改变音频解码器的深度缓冲模式】。在instantiateDecoder中又会:
○1根据情况用new Decoder(…)创建音视频解码器(NuPlayer::Decoder),为其创建名为mCodecLooper的ALooper【其父类NuPlayer::DecoderBase的构造中则会创建mDecoderLooper】。
○2对该解码器进行init()操作,调用NuPlayer::DecoderBase::init()为mDecoderLooper注册handler【init()和configure()都是NuPlayerDecoder继承自NuPlayer::DecoderBase的方法】
○3对该解码器进行configure(format)操作,调用NuPlayer::DecoderBase::configure(…)产生一个kWhatConfigure消息,然后消息处理中调用NuPlayer::Decoder::onConfigure(…),在onConfigure中,首先会调用MediaCodec::CreateByType(…)或者MediaCodec::CreateByComponentName(…)根据情况创建MediaCodec,接着调用MediaCodec::init(…),之后过程就同上述ⅰ中5一致;随后调用MediaCodec::configure(…)对MediaCodec进行配置使其转入Configured状态;然后又调用MediaCodec::start()使MediaCodec转入Executing状态。
P.S. 1: MediaCodec::configure(…)的调用:
产生kWhatConfigure消息,在消息处理中调用ACodec::initiateConfigureComponent(…)又产生消息kWhatConfigureComponent,然后该消息处理中又调用了ACodec::LoadedState::onConfigureComponent(…)。然后在其中又会先调用ACodec::configureCodec(…),在configureCodec中会对IOMX进行一系列的设置以及配置操作,通过Binder通信就对OMXNodeInstance进行相应的设置和配置操作,最终就对OMX组件进行了相应的设置和配置。然后向MediaCodec发送kWhatComponentConfigured消息,在消息处理中将MediaCodec状态设为CONFIGURED;
P.S. 2: MediaCodec::start()的调用:
产生kWhatStart消息,消息处理中先将MediaCodec状态设为STARTING,然后调用ACodec::initiateStart()产生kWhatStart消息,在其消息处理中又调用ACodec::LoadedState::onStart(),然后在其中首先向IOMX发送状态转换命令,经过OMXNodeInstance最终对将OMX组件状态转换成Idle(转换完成时OMX会发送OMX_EventCmdComplete事件),接着对ACodec进行changeState至LoadedToIdleState。而在changeState过程中会调用ACodec::LoadedToIdleState::stateEntered() =>  ACodec::LoadedToIdleState::allocateBuffers() => ACodec::allocateBuffersOnPort(…),其中会为OMX组件端口分配缓冲,并向MediaCodec发送消息kWhatBuffersAllocated,消息处理中将MediaCodec状态设为STARTED而若allocateBuffers失败则由IOMX经OMXNodeInstance将OMX组件转换回Loaded状态,同时把ACodec状态转换回LoadedState。


iii. 数据处理1(emptyBuffer)


1、 MediaCodec::start()之后ACodec是在LoadedToIdleState状态,此时若ACodec::LoadedToIdleState::onOMXEvent(…)接收到组件转换至Idle状态后的OMX_EventCmdComplete事件,会向IOMX发送状态转换命令,经过OMXNodeInstance最终对将OMX组件状态转换成Executing状态(这里OMX会发送OMX_EventCmdComplete事件),然后ACodec进行changeState至IdleToExecutingState。
2、 此时ACodec::IdleToExecutingState::onOMXEvent(…)检测到上面的OMX_EventCmdComplete事件后,会首先调用函数ACodec::ExecutingState::resume(),然后对ACodec进行changeState至ExecutingState。
3、 在函数ACodec::ExecutingState::resume()中会调用ACodec::BaseState::postFillThisBuffer(…),然后其中会先向MediaCodec发送kWhatFillThisBuffer消息,消息处理中在满足相应的条件下就会去调用函数MediaCodec::onInputBufferAvailable()来通知NuPlayer::Decoder有可用的inputbuffer;然后再生成kWhatInputBufferFilled消息,消息处理中调用ACodec::BaseState::onInputBufferFilled(…)。
【产生两个消息,一个向上(MediaCodec)处理,一个向下(OMX)处理】
P.S. 1:MediaCodec::onInputBufferAvailable()的调用:
其中会先调用函数MediaCodec::dequeuePortBuffer(…)获取buffer的索引,然后将一个新消息发送给NuPlayer::Decoder,并设置消息的callbackID为CB_INPUT_AVAILABLE,同时设置index,接着NuPlayer::Decoder接收到该CB_INPUT_AVAILABLE消息,在消息处理中调用NuPlayer::Decoder::handleAnInputBuffer(…),其会:
○1先通过MediaCodec::getInputBuffer(…) -> MediaCodec::getBufferAndFormat(…)获取该buffer
○2然后调用NuPlayer::Decoder::onInputBufferFetched(…)执行内存拷贝将buffer拷贝到编解码器,然后又调用了MediaCodec::queueInputBuffer(…)将buffer提交给解码器,其会产生消息kWhatQueueInputBuffer,消息处理中调用MediaCodec::onQueueInputBuffer(…)
○3之后调用函数NuPlayer::DecoderBase::onRequestInputBuffers(),处理是否需要更多的数据。其中会调用NuPlayer::Decoder::doRequestBuffers,若返回true则需要更多的数据,则会产生新消息kWhatRequestInputBuffers,消息处理中又将调用onRequestInputBuffers。(实际获取更多缓冲的操作在下面ACodec部分完成)
P.S. 2:ACodec::BaseState::onInputBufferFilled(…)的调用:
因为当前ACodec在ExecutingState,所以PortMode为RESUBMIT_BUFFERS,故会调用IOMX的emptyBuffer(…)方法,经过进程间通信调用到OMX::emptyBuffer(…),并最终调用OMXNodeInstance::emptyBuffer(…),其中又会调用到函数OMXNodeInstance::emptyBuffer_l(…),其则会调用OMX_EmptyThisBuffer宏对OMX组件进行相关的操作(根据需要选择相应的软解组件或者硬解组件)。对于软解组件SoftOMXComponent
○1其的构造函数的初始化列表中有mComponent->EmptyThisBuffer = EmptyThisBufferWrapper;故实际会调用其EmptyThisBufferWrapper(…)函数,而其中调用SoftOMXComponent的虚函数emptyThisBuffer。
○2所以调用子类的emptyThisBuffer即SimpleSoftOMXComponent::emptyThisBuffer(…)产生kWhatEmptyThisBuffer消息,消息处理中实际的解码器就要调用onQueueFilled(…)函数【实际组件继承自SimpleSoftOMXComponent】
○3接着会调用SoftOMXComponent::notifyEmptyBufferDone(…)使用OMX的回调机制,闭环发送消息到OMX客户端ACodec。
○4调用到OMXNodeInstance::OnEmptyBufferDone(…),其又会调用OMX::OnEmptyBufferDone(…),然后在其中会发送omx_message::EMPTY_BUFFER_DONE消息,ACodec中收到该消息【CodecObserver中先收到,但只设置消息】调用ACodec::BaseState::onOMXEmptyBufferDone(…)
○5在onOMXEmptyBufferDone中获取PortMode,为RESUBMIT_BUFFERS则ACodec::BaseState::postFillThisBuffer(…)被调用,从而又从3中的postFillThisBuffer开始循环执行相关操作以处理更多的输入缓冲。



iv. 数据处理2(fillBuffer)


1、在ⅲ中OMX发送OMX_EventCmdComplete事件后会调用到ACodec::ExecutingState::resume()函数,在resume()中调用ACodec::BaseState::postFillThisBuffer(…)前会先调用函数ACodec::ExecutingState::submitOutputBuffers(),即在获取输入数据前会先把输出端的数据提交出去。
2、在submitOutputBuffers()中调用ACodec::ExecutingState::submitRegularOutputBuffers(),其中又会调用到IOMX的fillBuffer (…)方法,经过进程间通信调用到OMX:: fillBuffer (…),并最终调用OMXNodeInstance:: fillBuffer (…),其中又会调用到OMX_FillThisBuffer宏对OMX组件进行相关的操作(同样根据需要选择相应的软解组件或者硬解组件)。对于软解组件SoftOMXComponent:(下面的操作与emptyBuffer时类似)
○1在其构造函数的初始化列表中有mComponent->FillThisBuffer = FillThisBufferWrapper;所以实际会调用到其FillThisBufferWrapper (…)函数
○2然后调用SimpleSoftOMXComponent::fillThisBuffer(…)产生kWhatFillThisBuffer消息,消息处理中实际的组件就要调用onQueueFilled(…)函数【实际组件继承自SimpleSoftOMXComponent】
○3接着会调用SoftOMXComponent::notifyFillBufferDone(…)使用OMX的回调机制,闭环发送消息到OMX客户端ACodec。
○4之后调用到OMXNodeInstance:: OnFillBufferDone (…)函数,其又会调用OMX:: OnFillBufferDone (…),然后在其中会发送omx_message:: FILL_BUFFER_DONE消息,ACodec中收到该消息【CodecObserver中先收到,但只设置消息】调用ACodec::BaseState:: onOMXFillBufferDone (…)
○5在onOMXFillBufferDone中获取PortMode,为RESUBMIT_BUFFERS则首先如果需要继续调用到IOMX的fillBuffer (…)填充输出缓冲重复做相关操作,接着ACodec又会生成一个kWhatOutputBufferDrained消息存在reply中,作为kWhatDrainThisBuffer消息的返回消息【notify->setMessage("reply", reply);】,然后向MediaCodec发送消息kWhatDrainThisBuffer,消息处理中调用函数MediaCodec::onOutputBufferAvailable()通知NuPlayer::Decoder有可用的output buffer,其中会设置消息的callbackID为CB_OUTPUT_AVAILABLE,同时设置index,接着NuPlayer::Decoder接收到该CB_OUTPUT_AVAILABLE消息,在消息处理中调用NuPlayer::Decoder::handleAnOutputBuffer(…),在其中会进行如下处理:
a. 先通过MediaCodec:: getOutputBuffer (…) -> MediaCodec::getBufferAndFormat(…)获取该buffer的信息
b. 若Renderer非空则会调用NuPlayer::Renderer::queueBuffer(…)进行Renderer的相关处理同时消耗产生的kWhatRenderBuffer消息。queueBuffer()会产生kWhatQueueBuffer消息,消息处理中会调用函数NuPlayer::Renderer::onQueueBuffer(…) –> NuPlayer::Renderer::postDrainVideoQueue() 【另外有audio的相关处理】,其中产生kWhatDrainVideoQueue消息,消息处理中调用先NuPlayer::Renderer::onDrainVideoQueue()在VideoQueue中取相关数据,再调用NuPlayer::Renderer::postDrainVideoQueue()循环取video数据,接着还会发送kWhatRenderBuffer消息。
c. 在kWhatRenderBuffer消息处理中会调用NuPlayer::Decoder::onRenderBuffer(…),在其中根据情况调用函数MediaCodec::renderOutputBufferAndRelease(..)渲染并释放,或者调用MediaCodec::releaseOutputBuffer(…)不渲染直接释放,两中情况都会产生kWhatReleaseOutputBuffer消息,该消息处理中调用函数MediaCodec::onReleaseOutputBuffer(…),其中判断若SoftRenderer非空则进行软件渲染,不然就会通过○5中的reply让ACodec去硬件渲染,在kWhatOutputBufferDrained消息处理就会中调用到函数ACodec::BaseState::onOutputBufferDrained(…)进行真正的硬件渲染。
发布了7 篇原创文章 · 获赞 7 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览