android MediaCodec ACodec

MediaCodec


MediaCodec类可用于访问Android底层的媒体编解码器,例如,编码/解码组件。它是Android为多媒体支持提供的底层接口的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)。
从广义上讲,一个编解码器通过处理输入数据来产生输出数据。它通过异步方式处理数据,并且使用了一组输入输出buffers。在简单层面,请求(或接收)到一个空的输入buffer,向里面填满数据并将它传递给编解码器处理。这个编解码器将使用完这些数据并向所有空的输出buffer中的一个填充这些数据。最终,请求(或接受)到一个填充了数据的buffer,可以使用其中的数据内容,并且在使用完后将其释放回编解码器。


1、 Data Types
编解码器处理三种类型的数据:压缩数据,原始音频数据,原始视频数据。上述三种数据都可以通过ByteBuffers进行处理,但需要为原始视频数据提供一个Surface来提高编解码性能。
压缩缓存(Compressed Buffers):输入缓冲区(解码器)和输出缓冲区(编码器)包含压缩数据格式的类型。
原始音频缓存(Raw Audio Buffers):原始音频缓冲区包含整个PCM音频帧数据。
原始视频缓存(Raw Video Buffers):ByteBuffer模式视频缓冲区根据他们的颜色格式布局。视频编解码器可能支持三种类型的色彩格式:1)、native raw video format:被COLOR_FormatSurface标记,其可与输入或输出Surface一起使用;2)、flexible YUV buffers(如COLOR_FormatYUV420Flexible),可以与输入/输出Surface一起使用, ByteBuffer模式下可以通过调用getInput/OutputImage(int)方法进行使用;3)、通常只在ByteBuffer模式下被支持。由供应商指定,可以使用 getInput/OutputImage(int)方法。


2、States
在编解码器的生命周期内有三种理论状态:停止态-Stopped、执行态-Executing、释放态-Released。停止状态(Stopped)包括了三种子状态:未初始化(Uninitialized)、配置(Configured)、错误(Error)。执行状态(Executing)在概念上会经历三种子状态:刷新(Flushed)、运行(Running)、流结束(End-of-Stream)。
                                             
使用工厂方法之一创建一个编解码器的时候,是处于Uninitialized状态。首先,需要通过configure(…)方法配置它,以此进入Configured 状态。然后,通过调用start()方法转入Executing 状态。在这个状态下可以通过上述buffer队列操作过程数据。
调用start()方法后立即进入Flushed状态,此时编解码器拥有所有的缓存。一旦第一个输入缓存被移出队列,编解码器就转入运行子状态,这种状态占据了编解码器的大部分生命周期。当将一个带有end-of-stream marker标记的输入缓存入队列时,编解码器将转入流结束子状态。可在Executing状态的任何时候通过调用flush()。
调用stop()方法返回编解码器的Uninitialized 状态,因此这个编解码器需要再次configured 。当使用完编解码器后,必须调用release()方法释放其资源。


3、Data Processing
每一个编解码器包含一组输入和输出缓存,这些缓存在API调用中通过buffer-ID进行引用。当成功调用start()方法后客户端将不会拥有输入或输出buffers。在同步模式下,通过调用dequeueInput/OutputBuffer(…) 方法从编解码器获得一个输入或输出buffer;在异步模式下,可以通过MediaCodec.Callback.onInput/OutputBufferAvailable(…)的回调方法自动地获得可用的buffers。
在获得一个输入buffe后,填充数据,利用queueInputBuffer/queueSecureInputBuffer方法将其提交给编解码器,不要提交多个具有相同时间戳的输入bufers(除非它是也被同样标记的codec-specific data,因为codec-specific data缓冲的时间戳无意义)。
a. Asynchronous Processing using Buffers
    从Android 5.0开始,首选的方法是调用configure之前通过设置回调异步地处理数据。异步模式稍微改变了状态转换方式,因为必须在调用flush()方法后再调用start()方法才能使编解码器的状态转换为Running子状态并开始接收输入buffers。同样,初始调用start方法将编解码器的状态直接变化为Running 子状态并通过回调方法开始传递可用的输入buufers。
b. Synchronous Processing using Buffers
从Android 5.0开始,即使在同步模式下使用编解码器,也应该通过getInput/OutputBuffer(int) 和/或 getInput/OutputImage(int) 方法检索输入和输出buffers。






ACodec


1、ACodec消息机制:
ACodec有一个BaseState和派生出来的其他State,如 UninitializedState, LoadedToIdleState, ExecutingState等。当有消息过来时,如果派生类有重写的方法,则会调到重写的方法,如果没有,则会调到BaseState的
ACodec继承自AHierarchicalStateMachine类,该类用于将收到的消息传递给哪个state。
ACodec收到的消息分两种,一种是MediaCodec传过来的,对应onMessageReceived方法;另一种是OMX Component传过来的,对应onOMXMessage方法。而onOMXMessage里面又分了4种情况来调用不同的方法。(EVENT、EMPTY_BUFFER_DONE、FILL_BUFFER_DONE和FRAME_RENDERED)


2、MediaCodec与ACodec的通知:
OMX的组件解码之后,ACodec::BaseState:: onOMXFillBufferDone (…)会被回调,去取得解码后的数据。然后会在onOMXFillBufferDone中调用notify通知MediaCodec,发给MediaCodec的消息形如notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
MediaCodec收到ACodec发的消息之后会updateBuffers(kPortIndexOutput, msg) 进行更新,同时调用onOutputBufferAvailable()中通知NuPlayer::Decoder有可用的output buffer。


3、ACodec有三种端口模式状态,其会根据当前处于哪个状态来决定buffer如何处理:
KEEP_BUFFERS:当ACodec处于BaseState或者收到OnInputBufferFilled消息但是buffer里面没有填充有效的数据时,ACodec握有的buffer不会送到OMX 组件;
RESUBMIT_BUFFERS:当ACodec处于ExecutingState或者处于OutputPortSettingChangedState但是当前是input口的buffer时,ACodec将握有的buffer送给OMX 组件;
FREE_BUFFERS:当ACodec处于OutputPortSettingChangedState并且当前是output口的buffer时,ACodec将握有的buffer free。


4、stagefright类的调用关系:
OMXNodeInstance负责创建并维护不同的实例,这些实例以node作为唯一标识。这样播放器中每个ACodec在OMX服务端都对应有了自己的OMXNodeInstance实例。
OMXMaster用来维护底层软硬件解码库,根据OMXNodeInstance中想要的解码器来创建解码实体组件。
OMXPluginBase负责加载组件库,创建组件实例,由OMXMaster管理。Android原生提供的组件都是由SoftOMXPlugin类来管理,这个类就是继承自OMXPluginBase。(Android源码提供了一些软件解码和编码的组件,它们被抽象为SoftOMXComponent)
OMXClient是客户端用来与OMX IL进行通信的。
内部结构CallbackDispatcher作用是用来接收回调函数的消息
OMXNodeInstance + CallbackDispatcher = 合作完成不同实例的消息处理任务


5、ACodec同OMXNodeInstance的消息传递:
ACodec将CodecObserver observer对象通过omx->allocateNode()传递到OMXNodeInstance。
OMXNodeInstance将kCallbacks(OnEvent,OnEmptyBufferDone,OnFillBufferDone)传递给OMX Component
当OMX Component有消息notify上来时,OMXNodeInstance最先收到,然后调用OMX.cpp。将消息在OMX.cpp里面将OMX Component thread转换到CallbackDispatcher线程中处理。CallbackDispatcher又将消息反调到OMXNodeInstance. 最后调用CodecObserver 的onMessage()回到ACodec中


6、 ACodec与OMX组件的关系
ACodec ,CodecObserver和OMXNodeInstance是一一对应的,简单的可以理解它们3个构成了OpenMAX IL的一个Component,每一个node就是一个codec在OMX服务端的标识。当然还有CallbackDispatcher,用于处理codec过来的消息,通过它的post/loop/dispatch来发起接收,从OMX.cpp发送消息,最终通过OMXNodeInstance::onMessage -> CodecObserver::onMessage -> ACodec::onMessage一路往上,当然消息的来源是因为我们有向codec注册OMXNodeInstance::kCallbacks。
而在OMXPluginBase创建组件实例的时候,需要传递一个callback给组件,这个callback用于接收组件的消息,它的实现是在OMXNodeInstance.cpp中。而kcallbacks是OMXNodeInstance的静态成员变量,它内部的三个函数指针分别指向了OMXNodeInstance的三个静态方法,也即是这三个方法与组件进行着消息传递
 

对于NuPlayer来说,它并不直接接触解码组件,而是通过创建ACodec来和组件交互。ACode内部有一个id,这个id对应于一个OMXNodeInstance。OMX对象中会对产生的每一个OMXNodeInstance分配一个唯一的node_id。每一个OMXNodeInstance内部又保存着组件实例的指针【OMX_HANDLETYPE mHandle;】,通过这个指针就可以和组件进行交互。交互的流程为:ACodec → OMX → OMXNodeInstance → COMPONENT。
 
                                           NuPlayer和libstagefrighthw的大致关系



7、 组件的加载
OMXMaster的makeComponentInstance方法根据组件的名字,找到对应的OMXPluginBase对象。然后调用OMXPluginBase的makeComponentInstance方法获得OMX_COMPONENTTYPE对象的指针,并将OMX_COMPONENTTYPE对象的指针保存在形参中返回。
OMXMaster内部维护着一个map表:KeyedVector<String8, OMXPluginBase *> mPluginByComponentName; key为组件的名字,value为OMXPluginBase的指针,一个OMXPluginBase管理着很多的组件。 


8、组件的管理
对组件的管理可以总结为:通过OMXMaster加载libstagefrighthw.so库文件,创建OMXPluginBase【即创建继承此类的组件对象】,通过这个类来管理组件。
Android源码提供了一些软件解码和编码的组件,它们被抽象为SoftOMXComponent。OMXPluginBase扮演者组件的管理者。它负责加载组件库,创建组件实例。而OMXMaster则管理着OMXPluginBase,Android原生提供的组件都是由SoftOMXPlugin类来管理,这个类就是继承自OMXPluginBase。
对于厂商来说,如果要实现自己的组件管理模块,需要通过继承实现OMXPluginBase,并将之编译为libstagefrighthw.so。在OMXMaster中会加载这个库文件,然后调用其createOMXPlugin方法获得一个OMXPluginBase指针,然后将其加入OMXPluginBase列表以及与组件名相关的map 【mPluginByComponentName】中,后续都会通过OMXPluginBase来管理组件。
 
发布了7 篇原创文章 · 获赞 7 · 访问量 1万+
展开阅读全文

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

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

分享到微信朋友圈

×

扫一扫,手机浏览