第一章 解码器模块简介
1.1、背景
usb摄像头采集图像(格式为jpeg)通过LCD显示的过程中,存在cpu占有率很高的现象,特别是在高分辨率图像的格式下(最低640*480开始)cpu占有率基本维持100%,而且是在最优cpu占有率方案下成立的。在致力于ffmpeg软件解码的时候偶然发现了板上的vdec设备,于是查看了d4的芯片手册,发现了集成在板上的硬件解码器(video decoder),并在源码中发现了vdec的驱动源码。本文档旨在使用硬件解码器减少摄像头画面显示中的cpu占有率,并测试其有效性和优化性能。
1.2、简介
Sama5d44比sama5d3x系列多了NEON(SMID)指令集和720p硬件解码器,此类芯片更适用于做图像/数字信号处理等相关的多媒体设备。硬件解码器类型为Hantro g1 decoder,集成在处理器内部。开发板为MYD-JA5D4X,其定制的内核3.18.0-linux4sam_5.0-alpha7中已静态链接了硬件解码器的驱动,所以只需要用应用程序调用就可以使用硬件解码器。硬件解码器的应用程序源码库由其公司提供,g1相关的解码器基本上都是集成在gstreamer中,解码器则以插件的形式集成在gstreamer上。Gstreamer是以plugin的方式来加载硬件解码器的,也就是需要加载解码器的相关库。由以下三种方式生成gstreamer插件(可能还有更好的方式,这些都是自己查阅了很多资料得出的):
1)、自定义插件:通过gst-template创建,此方法需要自己从头到尾编写解码器的代码。实际上官方有给出plugin源码,所以不选用此类方法。
2)、buildroot:buildroot是用来生成文件系统的一款很功能十分强大的开源包,谁用谁知道。在buildroot的开源包中有gstremaer两个版本,分别是gstreamer(0.10)和gstreamer1(1.x)。gst-0.10基本功能完善且很成熟,但是现在已经不再维护了,所以现在使用的gstreamer一般都是1.x版本的。巧的是硬件解码器的源码只在buildroot中的gstreamer0-10中存在,对应的包是gst-plugin-x170-1.0,而在1.x版本中并没有解码器相关的包。
其实在gstreamer1版本上也有对应的解码器插件,后面会讲到。
第二章主要讲的是解码器的gstreamer-0.10版本,包含了一些实用的基本使用方法和调试方法以及介绍。第三章主要讲解码器的gstreamer-1.x版本,为本文重点,不再叙述理论,所以第二章的法同样适用在第三章
第二章 gstreamer-0.10版本解码器
我们将使用buildroot来编译解码器。buildroot是一个功能十分强大的开源包,专门用于生成文件系统,可以编译出自己的交叉编译工具也可以使用自己的交叉编译工具,还提供了丰富的资源包,详情见根目录下的package。它的目录结构和内核很相像,都可以使用make menuconfig去配置,但是它的源码并不集成在源码包中,而是通过地址去链接下载,然后编译,安装。最常用的目录有output(编译输出),其下的target为目标平台输出目录,也就是对应的开发板的目录结构;其下的build存放解压好的包源码;其下的host存放交叉编译工具和环境。还有dl目录存放的就是下载的源码包。之前打算手动下载源码包的,下载不下来才选择了buildroot。
环境:ubuntu-16.01
源码版本:buildroot 2019.02.2
交叉编译工具:arm-linux-gnueabihf-gcc(4.7.3)
源码地址:https://buildroot.org/download.html
开发板型号:MYD-JA4D4X(米尔科技)
2.1、buildroot使用自己的交叉编译工具
1)、下载buildroot源码包(我的版本是Buildroot 2019.02.2)
2)、进入源码根目录,make menuconfig
图2.1.1 Buildroot根目录结构
3)、选择target options,配置如下:(仅针对sama5d44板子哦)
图2.1.2 平台设置
4)、退出,选择Toolchain,配置如下:
图2.1.3 编译工具设置
注意:① Toolchain path为你自己的交叉编译工具路径
② External toolchain kernel headers series如果你选错了的话,在编译的时候会提醒你类似set 3.2.x,expect 4.2.x的错误,你只需要重新选择它认定的版本就可以了。
2.2、配置gstreamer和x170
1)、进入buildroot源码根目录,make menuconfig
图2.2.1 包配置
2)、进入target packages--->Audio and video applications,配置gstreamer0.10如下:
图2.2.2 使能gstreamer 0.10和相关插件
注意:gstreamer0.10下的base/good/bad/ugly提供了丰富的多媒体处理插件,稳定高效。在使用gstreamer0.10中,如果需要什么插件去这四个成熟的插件中添加配置。
3)、进入源码根目录,gedit package/gstreamer/Config.in,添加以下内容:
source "package/gstreamer/gst-plugin-x170/Config.in"
source "package/on2-8170-libs/Config.in"
4)、gedit package/gstreamer/gst-plugin-x170/Config.in,添加如下所示:
depends on BR2_TOOLCHAIN_USES_GLIBC # on2-8170-libs
depends on BR2_LINUX_KERNEL # on2-8170-libs
select BR2_PACKAGE_ON2_8170_LIBS
5)、gedit package/gstreamer/gst-plugin-x170/gst-plugin-x170.mk,修改如下所示:
GST_PLUGIN_X170_DEPENDENCIES = gstreamer libglib2 on2-8170-libs
6)、make
7)、查看output/target/usr/lib/gstreamer-0.10目录,是否生成了libgstx170.so。如果没有生成成功,则手动添加配置,修改.config文件,添加如下:
BR2_PACKAGE_GST_PLUGIN_X170=y
BR2_PACKAGE_ON2_8170_LIBS=y
8)、make,再次查看output/target/usr/lib/gstreamer-0.10目录是否生成libgstx170.so
2.3、gstreamer环境搭建
在output/target/usr/lib下存放了gstreamer和x170运行时运要调用的库,其体积较大,放在开发板上不合适,所以建议采用NFS服务使用。详情见“SAMA5D27__sqlite3数据库.docx->第二章->NFS的搭建和配置。”
接下来是在开发板上部署gstreamer和x170:
1)、挂载文件系统
mount –t nfs –o nolock 172.16.54.122:/home/southgnss/work /mnt/nfs
2)、将编译好的库文件放到共享文件下
mkdir /home/southgnss/work/0.10
cp ./output/target/usr/lib /home/southgnss/work/0.10/ -r
cp ./output/target/usr/bin/gst-inspect-0.10 /home/southgnss/work/0.10/
cp ./output/target/usr/bin/gst-launch-0.10 /home/southgnss/work/0.10/
3)、配置动态库环境
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/nfs/0.10/lib
注:在MYD-JA5D4X的板上是有gstreamer1版本的,为了避免冲突,建议将/usr/lib中相关gstreamer库不要放在LD_LIBRARY_PATH路径中。
4、配置插件搜索环境
export GST_PLUGIN_PATH=/mnt/nfs/0.10/lib/gstreamer-0.10
说明:gatreamer是根据GST_PLUGIN_PATH去搜索插件库的,所以这个宏必须被正确配置。否则gstreamer找不到任何插件。还有相关的宏GST_PLUGIN_SCANNER(扫描路径,与GST_PLUGIN_PATH类似,不过这个一般为用户自己的插件库路径)。这些非常重要啊,我在这上面曾经纠结了好久!!!
2.4、gst-inspect-0.10使用
gst-inspect-0.10的作用在于检查插件是否可用,就如上面所说的可能环境没搭建好都可能导致检查不到插件;它还有一个作用是能检查插件的属性,这个就很重要了,当你在使用gst-launch-0.10时,插件相关的属性就非常重要了。所以可以把gst-inspect理解成辅助gst-launch的命令。
1)、查看x170硬件解码插件是否可用
gst-inspect-0.10 x170
图2.4.1 x170插件简介
注:若插件无法搜索到或者没有这个插件,会出现No such element or plugin ‘x170’;若搜索到此插件,会显示该插件的全部信息。
2)、属性特性
①带sink标志的为该插件的输入,其下为输入格式
②带src标志的为该插件的输出,其下为输出格式
③Properties为该插件的属性,可设置
2.5、新增插件与调试方法
gst-launch-0.10工具是gstreamer的发起者,是真正运行程序的可执行文件。Gstreamer是通过插件进行据流通的,每个插件有输入输出特性,根据这些特性将一个或者多个插件相接,形成了一条流水线,比如我们本文想做的摄像头画面显示,那么它的流水线就是从摄像头到LCD,对应的就是v4l2src->x170->lcd。那我们就需要v4l2src、x170和lcd的插件,x170插件我们已经编译好了,那么v4l2src和lcd的插件怎么弄?
2.5.1、创建v4l2src和fbdev插件
1)、> Target packages > Audio and video applications > gst-plugins-bad,选中fbdev
图2.5.1 使能fbdev
2)、> Target packages > Audio and video applications > gst-plugins-good,选中v4l2
图2.5.2 使能v4l2src
3)、先删除源码包,重新make
rm –rf ./output/build/gst-plugins-bad
rm –rf ./output/build/gst-plugins-good
make
注:这样buildroot才会重新编译
4)、将编译好的v4l2src和fbdev插件库放到plugin搜索路径上
cd output/target/usr/lib/gstreamer-0.10
cp libgstvideo4linux2.so /home/southgnss/work/0.10/lib/gstreamer-0.10
cp libgstfbdevsink.so /home/southgnss/work/0.10/lib/gstreamer-0.10
2.5.2、两种调试方法
1)、通过debug-level来调试
Gstreamer拥有超级大的日志文件,用来统计调试信息,平常的gst-launch只会打印error和warning,因为其日志量太多影响程序运行效率,但通常可以使用这些信息来调试程序。--gst-debug-level值与打印消息等级表如下:
表一 level与调试等级
Gst-debug-level值 | 调试等级 |
1 | error |
2 | error +warning |
3 | error+warning+FIXM |
4 | error +warning+info |
5 | error +warning+info+debug |
调试小技巧:非gstreamer原本的插件调试等级都比较高,想要打印出源码中的调试信息,那么程序执行时间就会很长也会产生很多的垃圾信息。如果你觉得那些地方可能出问题了,可以将调试等级降低。比如说将源码中的GST_DEBUG_OBJECT改为GST_WARNING_OBJECT,这样调试信息少,便于调试。
2)、生成链接图
①运行gst-launch前添加:
GST_DEBUG_DUMP_DOT_DIR=$PWD
图2.5.2.1 生成状态dot
②利用dot命令生成png图片,如:
dot –Tpng 0.00.02.596378272-gst-launch.error.dot –o xxx.png
图2.5.2.2 png图片显示链接状态
这样可以查看各个插件在各个状态之前的链接情况,图片还会显示当前插件的输入输出属性和格式,可以根据这些信息再去配置gst-launch。
2.6、使用硬件解码器
从上面几节我们知道要使用d44的硬件解码器,我们就要学习怎么使用gst-launch工具。在最开始使用的时候可能会出现很多很多的错误,这是不可避免的。而且这些错误并不是一定是环境配置问题,比如说动态库没有链接,原板的库存在导致编译的库无法加载等等错误,这都是一些比较痛苦的过程。以下是我总结出来的gst-launch用法,给出一个例子:
./gst-launch-0.10 --gst-debug-level=2 v4l2src ! video/x-raw-yuv, width=640, height=480 ! ffmpegcolorspace ! video/x-raw-rgb,width=640,height=480 ! fbdevsink
1)、--gst-debug-level=2这个上面说过了,调试等级为2,只会打印错误和警告。
2)、gst-launch-0.10后面跟第一个插件,不用!分开。
3)、插件Properties这类属性与插件在同两个!号之间,并且用空格分开。如何获取Properties信息,请看2.4节第一点。
4)、src和sink属性紧跟在插件后面,与插件用!分开;属性下的各详细值用逗号分开,如上面v4l2src ! video/x-raw-yuv, width=640, height=480。
5)、当报不能创建两个插件之间的链接的错误时,请用gst-inspect 插件名 来确定插件属性是否设置正确。
最终使用x170:
./gst-launch-0.10 -v --gst-debug-level=2 v4l2src ! image/jpeg,width=640,height=480 ! x170 codec=AUTO output=RGB32 ! fbdevsink
第三章 gstreamer1版本解码器
第二章提到MYD-JA5D44上面已经有了gstreamer1所需要的库和工具了,现在只需要添加上硬件解码器gst1的库就可以使用了。在gstreamer-0.10版本中,解码器对应的两个库是on2-8170-libs(解码器官方库)和gst-plugin-x170(非官方);在gstreamer-1.x中,解码器对应的两个库为g1_decoder(官方源码)和gst1-hantro-g1-gstreamer1.0-plugins-hantro_1.5(gst源码),需要自己交叉编译。本文还要涉及到gstreamer1.x版本和其扩展插件的编译,与0.10版本相同,我们可以采用Buildroot来编译gstreamer1。
3.1、交叉编译gstreamer1.x
工具:buildroot 2019.02.2
环境:ubuntu16.04
交叉编译工具:arm-linux-gnueabihf
3.1.1、buildroot配置
步骤一:
路径:> Target packages > Audio and video applications
介绍:配置gstreamer 1.x
如图:
图3.1.1.1 使能gstreamer 1.x
步骤二:
路径:> Target packages > Audio and video applications > gst1-plugins-good
介绍:配置v4l2
如图:
图3.1.1.1 使能v4l2src
3.1.2、搭建gst环境
1)、将编译好的gstreamer 1.x相关拷贝到共享目录下
cp ./output/target/usr/lib/libgst* /home/southgnss/work/1.0/lib cp ./output/target/usr/bin/gst-inspect-1.0 ./output/target/usr/bin/gst-launch-1.0 /home/southgnss/work/1.0 |
2)、开发板上挂载文件系统
mount –t nfs –o nolock 172.16.54.122:/home/southgnss/work /mnt/nfs |
3)、配置gstreamer 1.x运行环境
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/nfs/1.0/lib export GST_PLUGIN_PATH=/mnt/nfs/1.0/lib/gstreamer-1.0 |
3.2、交叉编译g1_decoder
G1_decoder是官方的硬件解码器源码,我们需要编译它。
1)、获取g1_decoder源码
git https://github.com/linux4sam/g1_decoder.git |
2)、进入g1_decoder源码根目录
cd g1_decoder |
3)、得到common,自动配置生成configure
cp common/* ./common ./autogen.sh |
4)、配置
./configure --prefix=/usr/local/x170_decoder/gst1/install \ host=arm-linux-gnueabihf \ G1_CFLAGS=-I/usr/local/x170_decoder/gst1/g1decoder/include/g1decoder \ G1_LIBS=-L/usr/local/x170_decoder/gst1/g1decoder/lib \ CC=/usr/local/arm/4.7.3/bin/arm-linux-gnueabihf-gcc |
5)、编译安装
make make install |
3.3、交叉编译g1插件
源码地址:https://github.com/linux4sam/gst1-hantro-g1/releases/tag/gstreamer1.0-plugins-hantro_1.5
交叉编译工具:arm-linux-gnueabihf-gcc(4.7.3)
环境:ubuntu 16.04
1)、进入解压目录
cd gst1-hantro-g1-gstreamer1.0-plugins-hantro_1.5 |
2)、配置
./configure --prefix=/usr/local/x170_decoder/gst1/install \ --host=arm-linux-gnueabihf \ G1_CFLAGS=-I/usr/local/x170_decoder/gst1/g1decoder/include \ G1_LIBS=-L/usr/local/x170_decoder/gst1/g1decoder/lib \ CC=/usr/local/arm/4.7.3/bin/arm-linux-gnueabihf-gcc \ LDFLAGS=-L/usr/local/x170_decoder/gst1/g1decoder/lib \ -ldecx170h \ -ldecx170m \ -ldecx170p \ -ldecx170vp8 \ -ldwlx170 \ -lx170j \ GLIB_CFLAGS=-I/usr/local/x170_decoder/buildroot-2019.02.2/buildroot-2019.02.2/output/host/usr/include/glib-2.0 \ GLIB_LIBS=-L/usr/local/x170_decoder/buildroot-2019.02.2/buildroot-2019.02.2/output/target/usr/lib\ PKG_CONFIG_PATH=/usr/local/x170_decoder/buildroot-2019.02.2/buildroot-2019.02.2/output/host/armeb-buildroot-linux-gnueabihf/sysroot/usr/lib/pkgconfig \ GST_CFLAGS=-I/usr/local/x170_decoder/install/include/gstreamer-1.0 \ GST_LIBS=-L/usr/local/x170_decoder/install/lib \ CPPFLAGS=-I/usr/local/x170_decoder/buildroot-2019.02.2/buildroot-2019.02.2/output/host/usr/include/glib-2.0 \ -I/usr/local/x170_decoder/buildroot-2019.02.2/buildroot-2019.02.2/output/host/armeb-buildroot-linux-gnueabihf/sysroot/usr/lib/glib-2.0/include \ -I/usr/local/x170_decoder/buildroot-2019.02.2/buildroot-2019.02.2/output/host/armeb-buildroot-linux-gnueabihf/sysroot/usr/include/gstreamer-1.0/ |
3)、编译安装
make make install |
4)、将编译好的库放到插件搜索路径去
cp /usr/local/x170_decoder/gst1/install/lib/* /home/southgnss/work/1.0/lib cp /usr/local/x170_decoder/gst1/install/lib/gstreamer-1.0/* /home/southgnss/work/1.0/lib/gstreamer-1.0 |
5)、解码jpg图片
./gst-launch-1.0 --gst-debug-level=4 filesrc location=x170.jpg ! jpegparse ! g1jpegdec use-drm=false ! imagefreeze ! video/x-raw,format=BGRx,width=800, height=480 ! g1fbdevsink |
6)、解码摄像头
./gst-launch-1.0 v4l2src do-timestamp=true ! image/jpeg,width=640,height=480,framerate=30/1 ! g1jpegdec use-drm=false ! video/x-raw,format=BGRx,width=800,height=480,framerate=30/1 ! g1fbdevsink max-lateness=-1 async=false enable-last-sample=false |