前面的AwesomePlayer
和NuPlayer
最终解码都会到达OMX
框架。Android用OMX来做编解码,Android向上抽象了一层OMXCodec
,提供给上层播放器AwesomePlayer使用。同时有一个IOMX
接口,在ACodec
中可以通过IOMX调用OMX组件。播放器中音视频解码器mVideo/AudioSource
都是OMXCodec的实例。OMXCodec::Create
是解码器初始化的入口。OMXCodec通过IOMX依赖Binder
机制后的OMX服务,OMX服务才是其在Android中的实现
StageFright共有两路到达OMX框架。一路是通过NuPlayer到达ACodec
类,然后调用OMX IL Core
中的接口。另一路是通过StagefrightPlayer到AwesomePlayer,再到达OMXCodec
类,然后调用OMX组件接口进行数据传输
在AwesomePlayer中,音视频数据会到OMXCodec
中寻找对应的解码器进行解码。IOMX和OMX组件通过Binder
通信,中间还涉及OMXClient
。OMX中的OMXNodeIstance
负责创建并维护不同的实例,这些实例是根据上面的需求创建的,以Node作为唯一标识。这样播放器中的每一个OMXCodec在OMX服务器端都有了自己对应的OMXNodeInstance实例。OMXMaster
维护底层软硬件解码库,根据OMXNodeInstance中想要的解码器来创建解码实体组件
1.在AwesomePlayer初始化的时候会调用AwesomePlayer::onPrepareAsyncEvent
2.继而调用AwesomePlayer::initVideoDecoder
以及AwesomePlayer::initAudioDecoder
3.然后开始正式进入OMX
以及硬件解码器的初始化工作
在Android中的组件都是在提供服务,有服务器端,有客户端,大部分是C/S模型,在AwesomePlayer中需要和OMXClient
通信,代码中有一OMXClient mClient
,位于OMXClient.cpp中
OMXClient有一个IOMX变量mOMX
,它负责与OMX服务进行Binder通信。在AwesomePlayer的构造函数中会调用
OMXClient::connect
函数是通过Binder机制获得MediaPlayerService
的,然后通过MediaPlayerService来创建OMX的实例。这样OMXClient就获得了OMX的入口,接下来可以通过Binder机制获得OMX提供的服务。也就是说OMXClient是Android中OMX的入口
在创建音视频解码mVideoSource
、mAudioSource
的时候,会把OMXClient中的sp
的实例传给mVideoSource、mAudioSource,来共享使用这个OMX的入口。一个AwesomePlayer对应一个IOMX变量,AwesomePlayer中的音视频解码器共用这个IOMX变量来获得OMX服务,下图是AwesomePlayer创建OMX的过程,以initVideoDecoder为例(截取部分)
先看一下Create函数
每个AwesomePlayer实例只有一个OMX服务的入口,但AwesomePlayer不一定只有一种解码器。音频、视频都有对应的解码器,部分场景下还有多路音频或多路视频,此时OMX需要建立不同的解码器组件来应对AwesomePlayer中不同的解码需求
OMX中OMXMaster
和OMXNodeInstance
是两个很重要的成员。OMX通过它们来创建和维护不同的OMX解码器组件,为AwesomePlayer中不同的解码需求提供服务。OMXNodeInstance负责创建并维护不同的实例,这些实例根据实际的解码需求创建,以node_id
作为唯一标识
这样解码器组件中每个OMXCodec
在OMX服务器端都有了自己对应的OMXNodeInstance
实例。AwesomePlayer就可以根据这个OMXNodeInstance来操作相应的解码器
OMXMaster
维护底层软硬件解码库,用于管理解码器组件,根据OMXNodeInstance中想要的解码器来创建解码实体组件,所以我们要追踪下OMXMaster和OMXNodeInstance
到这里就明白了AwesomePlayer是如何利用具体硬件平台上的硬件解码器的
针对不同的文件格式,如何选择具体的解码器组件呢?回到Create中的allocateNode
函数
以上代码实例化了OMXNodeInstance
,接下来会调用OMXNodeInstance的makeComponentInstance
函数
这样就实现了根据文件编码格式对具体解码器的连接,然后来了解一下OMXCodec如何注册和初始化OMX所需要的回调函数
OMX服务主要完成3个任务:NodeInstance列表的管理、NodeInstance的操作、事件的处理
在我们创建ComponentInstance(OMX组件实例)后,需要对它里面的NodeInstance列表进行管理
1.OMX对解码器组件Component的使用是通过OMXNodeInstance来实现的。OMXNodeInstance自身的动作包括NodeInstance的生成(allocateNode)和删除(freeNode)。其实就是对mDispatchers和mNodeIDToInstance进行添加和删除
mNodeIDToInstance
就是一个key为node_id,value为NodeInstance
的键值对列表。而mDispatchers
就是一个key为node_id,value为OMX::CallbackDispatcher
的键值对列表。并且每个NodeInstance都拥有一个OMX::CallbackDispatcher3.CallbackDispatcher的主要作用是在解码器组件Component发出回调动作后,将message分发给对应的OMXCodec客户端
每个OMXNodeInstance中都有Node结点,若你需要给这些Node节点分配一些Buffer
,下面看看对Node节点的操作过程
当执行这些函数时,都是先通过findInstance
在mNodeIDToInstance
列表中找到对应的NodeInstance
,然后调用NodeInstance对应的方法
OMXCodec对具体的component函数的操作,是通过OMXNodeInstance
来实现的,如fillBuffer、emptyBuffer
等,它们都是通过OMX_Core.h中的宏定义间接调用OMX_Component.h的OMX_COMPONENTTYPE中的相应函数指针来完成的。OMX_Core.h和OMX_Componment.h都是OMX标准头文件
它把三个OMXNodeInstance类的静态方法注册给了kCallbacks
。kCallbacks实际就是struct OMX_COMPONENTTYPE
和struct OMX_CALLBACKTYPE
的具体实现,而这两者就是在OMX_Core.h和OMX_Component.h中定义的
事件处理函数传给了组件ComponentInstance
,也就是传给了具体芯片平台相关的OMX IL层
当组件有事件发生时,就会调用OMXNodeInstance
中这几个注册过的事件处理函数:OnEmptyBufferDone、OnFillBufferDone、OnEvent
。而这几个函数又会调用OMX
中对应的OnEmptyBufferDone、OnFillBufferDone、OnEvent
。这几个函数都采用相同的方式,即根据node_id找到CallbackDispatcher,并把事件信息传递出去,也就是findDispatcher(node)->post(msg)
。进一步地,需要了解CallbackDispatcher的实现机制。它的内部开启了一个线程,使用了信号量机制
findDispatcher(node)->post(msg)是一个异步操作,只把消息传递过去,不会等待事件处理完毕就返回,那CallbackDispatcher是怎么处理接收到的信息的?查看以下代码
这样事件最终还是跨Binder传到OMXCodec
里面,交给OMXCodecObserver
了。一旦有关于回调的过程,再从OMX服务器端发送到OMX客户端。我们又知道AwesomePlayer类中持有OMX客户端,所以这些从OMX组件通知上来的雄安锡就可以到达AwesomePlayer中。这样就完成了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在一个进程空间中。其Bn
、Bp
方向和OMX
、OMXNodeInstance
相反,主要用于反向通知onMessage消息
到此AwesomePlayer对OMX的初始化,以及如何关联到对应硬件平台上的HardWare解码器的过程结束