文件播放的实质就是通过对文件识别,文件解析,文件解码后输出数据流。android在2.3以上版本中都使用stagefright作为播放引擎实现播放。在这里值得关注的是stagefright以OMX插件完成主要的文件解码任务,从而完成播放工作。
OMX中的三层结构中重点关注的是OMX_IL,
向上,OMX_AL这层的接口是随时可以变化的,
向下,OMX_DL这个是供应商根据下层硬件的接口设计,硬件更新的话这层也是很容易变化的,
相对不变的是OMX_IL,这个框架,这样的话在这层使用OMX标准供Clients调用是很合理的。也是基于此,在实现播放时是调用的OMX_IL层的标准接口实现的。
其实,在文件录制中也使用的是OMX插件,有一点是录制编码的格式支持远不及播放解码支持的格式多。
1. 既然使用的其实是OMX_IL,首先应该认识OMX_IL的结构:
第一层:OMX_IL——Core(核心内容)
第二层:组件component:主要是4个组件,这4个组件分别对用完成各自的功能,但是完成具体的播放或者录制任务时是协同工作的。
第三层:LCML:各个组件是通过调用这层的接口 完成真正分工作。
第四层:DSPBridge:连接下层驱动。
第五层:DSPBridge的驱动。
这里的使用是OMX_IL是作为第三方的插件供我们使用,它会以.so供程序使用。包括在使用component时也是以.so的形式出现。
2. 开启动态库的一个常用函数:
void* dlsym(void* handle, char* symbol); // 打开对应库,找到对应的函数名。
3. 所有组件都是使用统一的接口标准:
(1) OMX_Decder.c: 用于解码
(2) OMX_Dec_CompThread.c:解码的线程循环
(3) OMX_Dec_Util.c:相关解码工具,功能调用LCML实现解码功能
这个对组件的操作对外接口函数就是标准的使用两个:在(1)中,函数为:OMX_ComponentInit(); (2)中调用****Dec_StartThread()这个函数内部调用的是nRet = pthread_create(参数1,参数2,参数3,参数4);
4. OMX的核心Core设计完成的工作是将和component相关的属性跟操作都放在一个结构体中,这样就很方便了,clients直接就调用的是这个结构体中的函数指针,就可以和components交互,clients使用命令操作组件这样就正确流向了。
5. stagefright类的调用关系是:OMXNodeInstance代表OMX的具体实例,完成components的调用,OMXMaster是管理OMX插件。在工作过程中内部类CallbackDispatcher作用是用来接收回调函数的消息。OMXNodeInstance + CallbackDispatcher = 合作才完成不同实例的消息处理任务。
。。。。。
看了将近1个多月的stagefright和OMX IL,感觉对框架有了一点感觉,趁感觉还在,记录下来和大家分享,由于本人也是刚开始看多媒体框架,有认识不当的地方还请务必指正.
由 于stagefright和openmax运行在两个不同的进程上,所以他们之间的通讯要经过openBinder进行处理,对openBinder这一 块还没有了解,所以恕stagefright和openmax之间的通信不能做分析,还有就是本小结不考虑音频这一块,假设视频为MP4封装的AVC编码 文件.
最早看的是opencore,但opencore兼顾了多平台的移植性而过于复杂,后来就改看stagefright了,stagefright使 用的OMX IL为opencore中的代码........所以我蛮期待google什么时候能重写一个OMX IL框架来全面替代opencore
先简单的看一下stagefright是怎么工作的, stagefright使用event来进行驱动,event调度器和event运行在同一个线程中,
播放器向队列插入event来驱动整个解码流程,event调度器的工作抽象流程如下:
1. 检测队列是否为空,为空则等待event的插入
2. 获取队列中的第一个event
3. 计算event所要求的delay time后进行延时操作
4. 将event从队列中剔除后执行该event
event调度器通过不断循环这样的过程来进行调度,在具体代码中还会根据特殊情况进而改变调度过程,目前event事件有如下几种:
1. onVideoEvent
2. onStreamDone
3. onBufferingUpdate
4. onCheckAudioStatus
5. onPrepareAsyncEvent
stagefright的播放器类为AwesomePlayer,这个类主要有以下几个成员(排除Audio部分):
1. mVideoSource(解码视频)
2. mVideoTrack(从多媒体文件中读取视频数据)
3. mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
4. mISurface(重绘图层)
5. mQueue(event事件队列)
stagefright运行时的抽象流程如下:
![](http://blogimg.chinaunix.net/blog/upfile2/101207143435.png)
1) 设置mUri为xxxx.MP4的绝对路径
2) 启动mQueue,这会创建一个线程来运行threadEntry,并命名为TimedEventQueue,这个线程就是event调度器
3) 打开mUri所指定的文件,xxxx.MP4文件的头部为(....ftypisom....),则会选择MPEG4Extractor来作为分离器
4) 使用MPEG4Extractor对MP4进行音视频轨道的分离,并返回MPEG4Source类型的视频轨道给mVideoTrack
5) 根据mVideoTrack中的编码类型来选择解码器,avc的编码类型会选择AVCDecoder (假设不使用OMX),并返回给mVideoSource,并设置mVideoSource中的mSource为mVideoTrack
6) 插入onVideoEvent到Queue中,开始解码播放
7) 通过mVideoSource对象来读取解析好的视频buffer
8) 如果解析好的buffer还没到AV时间戳同步的时刻,则推迟到下一轮操作
9) mVideoRenderer为空,则进行初始化,如果不使用OMX会将mVideoRenderer设置为AwesomeLocalRenderer
10) 通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式并发给display模块进行图像绘制
11) 将onVideoEvent重新插入event调度器来循环
OMX IL作为底层解码部件的集合层,为上层多媒体框架提供了统一的接口,在Android2.2的stagefright中, stagefright使用的是opencore中的OMX IL实现,使用该OMX IL框架需要将mVideoSource设置为OMXCodec类,OMX IL层的对外接口主要有以下几种:
1) stagefright使用OMX_MasterInit初始化OMX框架,加载component
2) stagefright使用OMX_MasterGetHandle匹配OMX中的component,匹配成功则返回OMX_HANDLETYPE用于OMXCodec与component之间进行通信
3) OMXCodec使用OMX_SendCommand来设置component的状态,操作component的port
4) OMXCodec使用EventHandler通知OMXCodec的Command执行结果
5) OMXCodec使用OMX_GetParameter和OMX_SetParameter来获取和设置component的属性参数
6) OMXCodec使用OMX_UseBuffer设置compoment使用的buffer为OMXCodec分配的buffer
7) OMXCodec使用OMX_EmptyThisBuffer传递未解码的buffer给component,用于解码
8) OMXCodec使用OMX_FillThisBuffer传递空的bffer给component用于存储解码后的帧
9) compoment使用EmptyBufferDone通知OMXCodec已完成inputport buffer的读取
10) compoment使用FillBufferDone通知OMXCodec已完成outputport buffer的填充
初始化流程如下:
![](http://blogimg.chinaunix.net/blog/upfile2/101207143543.png)
![](http://blogimg.chinaunix.net/blog/upfile2/101207143629.png)
OMXCodec使用 OMX_EmptyThisBuffer传递 未解码的buffer给component,component收到该命令后会读取input port buffer中的数据,将其组装成帧进行解码,读取buffer中的数据完成后会调用EmptyBufferDone通知OMXCodec
compoment 使用EmptyBufferDone 通知OMXCodec已完成inputport buffer的读取, OMXCodec收到该命令后会通过mVideoTrack读取新的视频buffer到input port的buffer中,并调用OMX_EmptyThisBuffer通知component
OMXCodec使用OMX_FillThisBuffer 传递空的bffer给component用于存储解码后的帧,component收到该命令后将解码好的帧数据复制到该buffer上,然后调用FillBufferDone通知OMXCodec
compoment 使用FillBufferDone 通知OMXCodec已完成outputport buffer的填充, OMXCodec收到该命令后将解码好的帧传递给mISurface进行图像绘制,绘制完毕后使用OMX_FillThisBuffer通知 component有空的buffer可填充
抽象图如下:
![](http://blogimg.chinaunix.net/blog/upfile2/101207143658.png)
1) 使用AssemblePartialFrames将input port的buffer组装成帧
2) 将帧传递给AvcDecoder_OMX进行解码后输出到output port的buffer中
如下图:
![](http://blogimg.chinaunix.net/blog/upfile2/101207143810.png)
组合完成后将tmp_buffer_2和output port的buffer交给AvcDecoder_OMX进行解码, AvcDecoder_OMX将解码后的帧数据拷贝到output port buffer中.