【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务

简介

前面的AwesomePlayerNuPlayer最终解码都会到达OMX框架。Android用OMX来做编解码,Android向上抽象了一层OMXCodec,提供给上层播放器AwesomePlayer使用。同时有一个IOMX接口,在ACodec中可以通过IOMX调用OMX组件。播放器中音视频解码器mVideo/AudioSource都是OMXCodec的实例。OMXCodec::Create是解码器初始化的入口。OMXCodec通过IOMX依赖Binder机制后的OMX服务,OMX服务才是其在Android中的实现

OMX与StageFright框架层级的关系

【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第1张图片

StageFright共有两路到达OMX框架。一路是通过NuPlayer到达ACodec类,然后调用OMX IL Core中的接口。另一路是通过StagefrightPlayer到AwesomePlayer,再到达OMXCodec类,然后调用OMX组件接口进行数据传输
【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第2张图片

在AwesomePlayer中,音视频数据会到OMXCodec中寻找对应的解码器进行解码。IOMX和OMX组件通过Binder通信,中间还涉及OMXClient。OMX中的OMXNodeIstance负责创建并维护不同的实例,这些实例是根据上面的需求创建的,以Node作为唯一标识。这样播放器中的每一个OMXCodec在OMX服务器端都有了自己对应的OMXNodeInstance实例。OMXMaster维护底层软硬件解码库,根据OMXNodeInstance中想要的解码器来创建解码实体组件

OMX的初始化流程

AwesomePlayer获得OMX服务的流程

1.在AwesomePlayer初始化的时候会调用AwesomePlayer::onPrepareAsyncEvent
2.继而调用AwesomePlayer::initVideoDecoder以及AwesomePlayer::initAudioDecoder
3.然后开始正式进入OMX以及硬件解码器的初始化工作

在Android中的组件都是在提供服务,有服务器端,有客户端,大部分是C/S模型,在AwesomePlayer中需要和OMXClient通信,代码中有一OMXClient mClient,位于OMXClient.cpp中
【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第3张图片

OMXClient有一个IOMX变量mOMX,它负责与OMX服务进行Binder通信。在AwesomePlayer的构造函数中会调用
在这里插入图片描述

OMXClient::connect函数是通过Binder机制获得MediaPlayerService的,然后通过MediaPlayerService来创建OMX的实例。这样OMXClient就获得了OMX的入口,接下来可以通过Binder机制获得OMX提供的服务。也就是说OMXClient是Android中OMX的入口

在创建音视频解码mVideoSourcemAudioSource的时候,会把OMXClient中的spmOMX的实例传给mVideoSource、mAudioSource,来共享使用这个OMX的入口。一个AwesomePlayer对应一个IOMX变量,AwesomePlayer中的音视频解码器共用这个IOMX变量来获得OMX服务,下图是AwesomePlayer创建OMX的过程,以initVideoDecoder为例(截取部分)
在这里插入图片描述【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第4张图片
在这里插入图片描述

先看一下Create函数

【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第5张图片

每个AwesomePlayer实例只有一个OMX服务的入口,但AwesomePlayer不一定只有一种解码器。音频、视频都有对应的解码器,部分场景下还有多路音频或多路视频,此时OMX需要建立不同的解码器组件来应对AwesomePlayer中不同的解码需求

OMX中OMXMasterOMXNodeInstance是两个很重要的成员。OMX通过它们来创建和维护不同的OMX解码器组件,为AwesomePlayer中不同的解码需求提供服务。OMXNodeInstance负责创建并维护不同的实例,这些实例根据实际的解码需求创建,以node_id作为唯一标识

这样解码器组件中每个OMXCodec在OMX服务器端都有了自己对应的OMXNodeInstance实例。AwesomePlayer就可以根据这个OMXNodeInstance来操作相应的解码器

OMXMaster维护底层软硬件解码库,用于管理解码器组件,根据OMXNodeInstance中想要的解码器来创建解码实体组件,所以我们要追踪下OMXMaster和OMXNodeInstance

OMX构造函数中会进行初始化操作
在这里插入图片描述

OMXMaster.cpp中的代码如下
【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第6张图片

到这里就明白了AwesomePlayer是如何利用具体硬件平台上的硬件解码器的

针对不同的文件格式,如何选择具体的解码器组件呢?回到Create中的allocateNode函数
【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第7张图片

以上代码实例化了OMXNodeInstance,接下来会调用OMXNodeInstance的makeComponentInstance函数

【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第8张图片

这样就实现了根据文件编码格式对具体解码器的连接,然后来了解一下OMXCodec如何注册和初始化OMX所需要的回调函数

OMX服务主要完成3个任务:NodeInstance列表的管理、NodeInstance的操作、事件的处理

OMX中NodeInstance列表的管理

在我们创建ComponentInstance(OMX组件实例)后,需要对它里面的NodeInstance列表进行管理

1.OMX对解码器组件Component的使用是通过OMXNodeInstance来实现的。OMXNodeInstance自身的动作包括NodeInstance的生成(allocateNode)和删除(freeNode)。其实就是对mDispatchers和mNodeIDToInstance进行添加和删除

  1. mNodeIDToInstance就是一个key为node_id,value为NodeInstance的键值对列表。而mDispatchers就是一个key为node_id,value为OMX::CallbackDispatcher的键值对列表。并且每个NodeInstance都拥有一个OMX::CallbackDispatcher

3.CallbackDispatcher的主要作用是在解码器组件Component发出回调动作后,将message分发给对应的OMXCodec客户端

OMX中NodeInstance节点的操作

每个OMXNodeInstance中都有Node结点,若你需要给这些Node节点分配一些Buffer,下面看看对Node节点的操作过程
【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第9张图片

当执行这些函数时,都是先通过findInstancemNodeIDToInstance列表中找到对应的NodeInstance,然后调用NodeInstance对应的方法

OMXCodec对具体的component函数的操作,是通过OMXNodeInstance来实现的,如fillBuffer、emptyBuffer等,它们都是通过OMX_Core.h中的宏定义间接调用OMX_Component.h的OMX_COMPONENTTYPE中的相应函数指针来完成的。OMX_Core.h和OMX_Componment.h都是OMX标准头文件

在OMXNodeInstance.cpp中有一段代码
在这里插入图片描述

它把三个OMXNodeInstance类的静态方法注册给了kCallbacks。kCallbacks实际就是struct OMX_COMPONENTTYPEstruct OMX_CALLBACKTYPE的具体实现,而这两者就是在OMX_Core.h和OMX_Component.h中定义的

然后查看OMX.cpp中的allocateNode函数
【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第10张图片

事件处理函数传给了组件ComponentInstance,也就是传给了具体芯片平台相关的OMX IL层

当组件有事件发生时,就会调用OMXNodeInstance中这几个注册过的事件处理函数:OnEmptyBufferDone、OnFillBufferDone、OnEvent。而这几个函数又会调用OMX中对应的OnEmptyBufferDone、OnFillBufferDone、OnEvent。这几个函数都采用相同的方式,即根据node_id找到CallbackDispatcher,并把事件信息传递出去,也就是findDispatcher(node)->post(msg)。进一步地,需要了解CallbackDispatcher的实现机制。它的内部开启了一个线程,使用了信号量机制

findDispatcher(node)->post(msg)是一个异步操作,只把消息传递过去,不会等待事件处理完毕就返回,那CallbackDispatcher是怎么处理接收到的信息的?查看以下代码

【学习】从零开始的Android音视频开发(10)——OMX:Codec部分中AwesomePlayer到OMX服务_第11张图片

这样事件最终还是跨Binder传到OMXCodec里面,交给OMXCodecObserver了。一旦有关于回调的过程,再从OMX服务器端发送到OMX客户端。我们又知道AwesomePlayer类中持有OMX客户端,所以这些从OMX组件通知上来的雄安锡就可以到达AwesomePlayer中。这样就完成了AwesomePlayer和OMX组件之间的通信

总结AwesomePlayer到OMX服务过程

1.在AwesomePlayer初始化过程中,通过initVideo/AudioDecoder函数来创建音视频解码器mVideoSource/mAudioSource

2.在mVideoSource中通过mVideoTrack来解复用媒体文件,从中获取文件编码格式,继而得到需要的解码器类型,通过类型调用omx->allocateNode创建OMX node实例,与编码格式对应。以后都是通过Node实例来操作实际的硬件解码器的

3.初始化MediaPlayerService对象的时候会创建OMX对象,OMX对象的构造函数会创建mMaster,mMaster负责获得与管理硬件平台的硬件解码器组件库

4.在omx->allocateNode中通过mMaster->makeComponentInstance来创建真正对应的解码器组件。这个解码器组件将完成之后实质的解码操作

5.在创建mMaster->makeComponentInstance的过程中,通过上面mVideoTrack传递过来的解码器类型名,找到相对应的解码器的库,然后实例化

6.解码Component通过输入Port输出Port进行交互,通过和OMXCodec共享Bufer进行编解码

7.AwesomePlayer包含了mVideoSource,当初始化时指向OMXCodec的实际对象。OMXCodec使用了Binder机制,实现了对OMX服务的远程调用,其中IOMX作为接口类定义了OMX的大部分接口函数

8.当具体实现OMX时,OMXMaster类用于管理OMX的插件,OMXNodeInstance类代表OMX的具体实例,完成和Component的调用和交互

9.CallbackDispatcher用于调度处理回调函数传回的消息OMXNodeInstance和CallbackDispatcher一一对应,协同工作,完成不同实例的消息处理

10.OMXNodeInstance是OMX端的概念,是服务器端的概念。其服务端与OMX在一个进进程空间中

11.OMXObserver是OMXCodec端的概念,是客户端的概念。其客户端与OMXCodec在一个进程空间中。其BnBp方向和OMXOMXNodeInstance相反,主要用于反向通知onMessage消息

到此AwesomePlayer对OMX的初始化,以及如何关联到对应硬件平台上的HardWare解码器的过程结束

你可能感兴趣的:(Android音视频开发,学习,android,音视频)