vlc源码分析(六) 调用OpenMAX硬解码H.265
H.265(HEVC)编码格式能够在得到相同编码质量视频的前提下,使用相当于H.264(AVC)一半的存储容量,虽然H.265的算法复杂度比H.264高一个数量级,但是硬件水平在不断提高,因此H.265使用场合逐渐多了起来。好多硬件厂商芯片内部实现了H.265的硬解码。最近调试了vlc-android调用OpenMAX硬解码H.265的部分,使用的硬件平台是ZX-2000,系统是Android5.1。
OpenMAX的官方网站:https://www.khronos.org/openmax/
官方文档整理:https://github.com/jiayayao/DataSheet/tree/master/encode-decode/openmax
官方介绍:The OpenMAX Working Group has been formed by the Khronos Group, to define a set of standard, open Application Programming Interfaces (APIs) for multimedia applications. The goal of this open standard is to reduce the cost and complexity of porting multimedia software to new processors and architectures.
可见OpenMAX的核心价值在于抽象了各个硬件平台和框架,减少了多媒体应用软件移植的复杂度。OpenMAX实际上分成三个层次,自上而下分别是,OpenMAX AL(应用层),OpenMAX IL(集成层)和OpenMAX DL(开发层)。在实际的应用中,OpenMAX的三个层次中使用较多的是集成层OpenMAX IL。三个层次的内容如下所示。
第一层:OpenMAX AL(Appliction Layer,应用层),在应用程序和多媒体中间件之间提供了一个标准化接口,多媒体中间件提供服务以实现被期待的API功能;
第二层:OpenMAX IL(Integration Layer,集成层),作为音频、视频和图像编解码器能与多媒体编解码器交互,并以统一的行为支持组件(例如,资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是透明的底层接口应用于嵌入式、移动设备。它提供了应用程序和媒体框架,透明的。编解码器供应商必须写私有的或者封闭的接口,集成进移动设备。IL的主要目的是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题;
第三层:OpenMAX DL(Development Layer,开发层),定义了一个API,它是音频、视频和图像功能的集合。供应商能够在一个新的处理器上实现并优化,然后编解码供应商使用它来编写更广泛的编解码器功能。它包括音频信号的处理功能,如FFT和filter,图像原始处理,如颜色空间转换、视频原始处理,以实现例如MPEG-4、H.264、MP3、AAC和JPEG等编解码器的优化。
OpenMAX对硬解码的事务进行了抽象,提供了一系列的.h文件,各个厂家分别根据头文件实现OpenMAX的标准。在vlc-android工程中,通过调用mediacodec.c文件,查询Android系统的MediaCodec是否支持硬解码,如果支持,则创建对应的硬解码器。mediacodec.c文件的作用是封装Android系统的Mediacodec框架,同时OpenMAX及其实现会作为对应的解码器注册到MediaCodec框架中。当mediacodec_ndk.c文件中调用Start函数创建解码器时:
p_sys->p_codec = syms.AMediaCodec.createCodecByName(api->psz_name);
该语句实际上调用的是MediaCodec框架的AMediaCodec_createCodecByName函数(Android5.1源码:NdkMediaCodec.cpp). Android系统的MediaCodec与OpenMAX的关系如下图:
对H265编码格式的视频来说,创建的解码器名称是:OMX.ZX.video.decoder.hevc,也就是说实际的硬解码走的是ZX-2000对OpenMAX的实现的H265部分,硬解码时打印的日志大部分都是以“S3OMX”等开头也证明了这一点”。从OpenMAX的AL, IL, DL三个层次的理解上来看,mediacodec.c文件及该文件之上的文件封装了OpenMAX,使得OpenMAX在vlc中得以利用,应该属于AL层;ZX-2000的BSP开发部门实现了OpenMAX标准(代码以“S3OMX”开头),BSP中对OpenMAX的实现应该属于IL层;DL层应该是芯片内部硬解码部分的驱动实现。所以对于应用层的开发人员来说,应该了解AL和IL层。
一、OpenMAX IL
OpenMAX IL层标准提供了几个头文件:
OMX_Types.h:OpenMAX Il的数据类型定义
OMX_Core.h:OpenMAX IL核心的API
OMX_Component.h:OpenMAX IL 组件相关的 API
OMX_Audio.h:音频相关的常量和数据结构
OMX_IVCommon.h:图像和视频公共的常量和数据结构
OMX_Image.h:图像相关的常量和数据结构
OMX_Video.h:视频相关的常量和数据结构
OMX_Other.h:其他数据结构(包括A/V 同步)
OMX_Index.h:OpenMAX IL定义的数据结构索引
OMX_ContentPipe.h:内容的管道定义
各个硬件厂商需要根据这些头文件实现OpenMAX标准。比如以Android5.1源码为例,TI的OMAP芯片实现OpenMAX的代码就在文件android5.1\hardware\ti\omap4xxx\ domx\omx_core\src\OMX_Core.c中,其他厂商如果要实现硬解码,也需要按照类似的做法,将自身对于OpenMAX的实现添加到Android系统BSP中。
Android5.1源码的android5.1\hardware\ti\omap4xxx\domx\test\sample_proxy\test_sample_ proxy.c文件是TI的OMAP芯片OpenMAX部分的简单测试程序。该测试程序仅仅是简单的初始化Component,处理缓冲区,最后通过改变Component状态释放Component。如果需要适配OpenMAX到新的芯片,可以参考OMAP的实现。
二、OpenMAX AL
mediacode.c和avcodec.c作用是类似的,mediacodec.c为OpenMAX在VLC下的使用提供了封装,avcodec.c为FFmpeg在VLC下的使用提供了封装。提供封装时,一般需要提供几个函数,如OpenDecoder,CloseDecoder,DecodeBlock等,这些函数会被上层调用。
当获取到的RTSP流是H.265编码格式后,mediacodec_ndk.c创建解码器“OMX.ZX.video.decoder.hevc”,创建解码器时,会将mediacodec_ndk.c实现的一系列函数赋值给mc_api结构体,包括start(), stop(), configure(), clean()等。解码时解码线程DecodeThread会调用DecodeBlock函数解码数据块,但是与avcodec(软解)的直接解码不同,mediacodec的解码线程是将需要解码的数据块(block_t类型)通过API经mediacodec_ndk.c送入硬解码芯片队列中,另外开启一个新的线程OutThread来从硬解码芯片的图像队列中取出图像的index,赋值一些时间戳信息,再推入图像fifo中,交给渲染线程处理。
注意,硬解码时推入图像fifo的picture_t只是带有硬件缓冲区的index,最后渲染线程只是根据index将该缓冲区释放(modules/video_out/android/display.c);而软解码时,FFmepg会将带有解码后图像数据的picture_t交给opengl(modules/video_output/opengl/display.c)去渲染。个人的理解是,硬解码时,解码后的图像就直接在GPU里边,GPU在恰当的时机直接把图像交给显示的framebuffer就可以了,无需再取出来再交给opengl渲染;软解码时,解码后的图像在系统内存中,需要交给opengl去渲染。
解码线程堆栈如下:
硬解码模块自行创建的输出线程堆栈如下:
注意,在将图像推入图像fifo之前,会先调用API从硬解码芯片的图像队列中取出图像,调用mediacodec_ndk.c文件的DequeOutput函数。
参考资料:
《Android系统级深入开发—移植与调试》中OpenMAX章节
vlc源码分析(六) 调用OpenMAX硬解码H.265的更多相关文章
- vlc源码分析(三) 调用live555接收RTSP数据
首先了解RTSP/RTP/RTCP相关概念,尤其是了解RTP协议:RTP与RTCP协议介绍(转载). vlc使用模块加载机制调用live555,调用live555的文件是live555.cpp. 一. ...
- vlc源码分析(七) 调试学习HLS协议
HTTP Live Streaming(HLS)是苹果公司提出来的流媒体传输协议.与RTP协议不同的是,HLS可以穿透某些允许HTTP协议通过的防火墙. 一.HLS播放模式 (1) 点播模式(Vide ...
- Dubbo 源码分析 - 服务调用过程
注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...
- VLC源码分析知识总结
1. 关于#和## 1.1).在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号. 比如在早 ...
- ABP源码分析六:依赖注入的实现
ABP的依赖注入的实现有一个本质两个途径:1.本质上是依赖于Castle这个老牌依赖注入的框架.2.一种实现途径是通过实现IConventionalDependencyRegistrar的实例定义注入 ...
- Duilib源码分析(六)整体流程
在<Duilib源码分析(一)整体框架>.<Duilib源码分析(二)控件构造器—CDialogBuilder>以及<Duilib源码分析(三)XML解析器—CMarku ...
- motan源码分析六:客户端与服务器的通信层分析
本章将分析motan的序列化和底层通信相关部分的代码. 1.在上一章中,有一个getrefers的操作,来获取所有服务器的引用,每个服务器的引用都是由DefaultRpcReferer来创建的 pub ...
- 5.源码分析---SOFARPC调用服务
我们这一次来接着上一篇文章<4. 源码分析---SOFARPC服务端暴露>讲一下服务暴露之后被客户端调用之后服务端是怎么返回数据的. 示例我们还是和上篇文章一样使用一样的bolt协议来讲: ...
- Vue.js 源码分析(六) 基础篇 计算属性 computed 属性详解
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护,比如: <div id="example">{{ messag ...
随机推荐
- 面向对象,继承,浏览器,上传文件, ajax
'use strict'; //父类 class Student2{ constructor(name){ this.name = name || 'tom'; } hello(){ console. ...
- 使用匿名函数给setInterval()传递参数
在使用JScript的时候,我们有时需要间隔的执行一个方法,比如用来产生网页UI动画特效啥的.这是我们常常会使用方法setInterval或setTimeout,但是由于这两个方法是由脚本宿主模拟出来 ...
- 关于 PHPMailer 邮件发送类的使用心得(含多文件上传)
This is important for send mail PHPMailer 核心文件 class.phpmailer.php class.phpmaileroauth.php class.ph ...
- Cloudera Manager5及CDH5在线(cloudera-manager-installer.bin)安装详细文档
问题导读:1.Cloudera Manager5如何使用cloudera-manager-installer.bin安装?2.Cloudera Manager5安装被中断该如何继续安装?还是重新安装? ...
- Array inversion case
package basic.java; import java.util.Scanner; /* * 需求: * (1)键盘录入5个int类型的数据存储数组arr中 * (2)定义方法将arr数组中的 ...
- Appium+java移动端项目测试问题整理
一.每次打开APP都要重新安装.充值账号密码 解决:打开appium,设备,Use Browser ,勾选“No Reset” 二.一个页面包含相同文字,打开页面路径错误 问题描述:APP处于[ ...
- linux 服务器 keras 深度学习环境搭建
感慨: 程序跑不起来,都是环境问题. 1. 安装Anaconda https://blog.csdn.net/gdkyxy2013/article/details/79463859 2. 在 Anac ...
- mysql中int、bigint、smallint 和 tinyint的区别与长度的含义【转】
最近使用mysql数据库的时候遇到了多种数字的类型,主要有int,bigint,smallint和tinyint.其中比较迷惑的是int和smallint的差别.今天就在网上仔细找了找,找到如下内容, ...
- jQuery 插件封装的方法
方式1.$.fn.xxx ==>针对元素添加方法: ;(function ($) { $.fn.myPlugin = function () { //你自己的插件代码 }; })(jQuer ...
- laravel入门-01
创建laravel应用 laravel new app_name 使用 PHP 内置 web server 驱动我们的网站 cd xxx/public php -S localhost:port 查看 ...