入门级嵌入式板卡的视频工具FFmpeg移植与测试开发——米尔i.MX6UL/i.MX6ULL开发板

描述

 

本篇测评由电子发烧友的优秀测评者“ALSET”提供。
 

嵌入式

电子发烧友网发布了一款产品:米尔 MYD-Y6ULX-V2 开发板,基于 NXP i.MX6UL/i.MX6ULL处理器,该开发板被米尔称之为经典王牌产品。也是是一款嵌入式Linux入门级的开发板。本次测试目标是在此开发板上进行视频工具FFmpeg的移植与测试开发,测试FFmpeg在此开发板上的视频应用效果。

嵌入式

 

01.

 

FFmpeg是什么

    FFmpeg是一套开源的音视频代库,具有非常强大的功能包括视频采集功能、视频格式转换、视频抓图、给视频加水印等。

FFmpeg视频采集功能非常强大,不仅可以采集视频采集卡或USB摄像头的图像,还可以进行屏幕录制,同时还支持以RTP方式将视频流传送给支持RTSP/RTMP 的流媒体服务器,支持直播应用。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,具有高可移植性和编解码质量。它几乎实现了所有当下常见的数据封装格式、多媒体传输协议以及音视频编解码器。

很多Linux桌面环境中的开源播放器VLC、MPlayer,Windows下的KMPlayer、暴风影音以及Android下几乎全部第三方播放器都是基于FFmpeg的,可见它的应用非常广泛,也是嵌入式系统中音视频使用最多的代码库。

FFmpeg工程的几个主要目录libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;libavcodec:用于各种类型声音/图像编解码;libavutil:包含一些公共的工具函数;libswscale:用于视频场景比例缩放、色彩映射转换;libpostproc:用于后期效果处理;ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;ffsever:一个 HTTP 多媒体即时广播串流服务器;ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示。嵌入式

02.

 

FFmpeg移植编译

1. 首先切换为交叉编译环境嵌入式
2.从github 上拉取ffmpeg源码,考虑ffmpeg版本比较多,最新的版本增加了许多的扩展功能,同时也增加了对硬件资源的需求。考虑该开发板为ARMv7的初级开发版,因此这里不使用最新的版本,而是使用一个两年前的版本。使用 ffmpeg-4.1.3版本。

嵌入式嵌入式
3. 写配置执行脚本

FFmpeg代码编译配置有非常多的选项,为了更好的进行配置,这里写一个脚本用来进行配置执行。

脚本内容如下:嵌入式
脚本文件名:config_for_mx6ull.sh放到ffmpeg工程根目录下这里特别注意,因为项目放到是主机的虚拟机外挂的共享目录里,共享目录的文件系统是fat32,它不支持软链接,因此需要设置链接命令为硬复制,即如下:--ln_s="cp-R"4. 执行配置执行脚本编写好文配置脚本后,然后可以直接执行,执行完毕,输出结果如下:嵌入式
5.开始多线程编译配置脚本执行完毕后,会产生makefile文件,然后就可以进行编译,执行make–j4嵌入式
编译需要大概30分钟左右,整体编译顺利,最后编译出结果:嵌入式
6.检查编译的库文件使用命令 file 检查编译输出的动态库文件和ffmpeg文件,可见是armv7执行文件。嵌入式
经过检查,目标文件已经编译成功。7. 编译FFmpeg附带的测试例子程序编译完ffmpeg 库后,就可以编译附带的例子程序,可以更好的定制开发ffmpeg. 先编译例子程序。例子带的makefile 文件是x86 平台的,需要修改一下才能编译开发板上的版本,进入目录 /mnt/hgfs/MYD-Y6ULX/Proj/ffmpeg.4.1.3/ffmpeg/doc/examples下,修改Makefile.example修改如下:嵌入式
修改完,就可以执行编译,命令如下:make-f Makefile.example嵌入式嵌入式
编译很顺利,一次通过。8. 瘦身动态库文件和ffmpeg执行文件编译完的库文件体积都比较大,像| libavcodecs.so 文件达40M+,所有文件一起传到开发板上,将非常占用开发板文件空间,因此可以对动态库和ffmepg执行文件进行瘦身一下。所谓瘦身,就是把执行文件带的gdb符号信息全部去掉,即可大大减少文件的体积。嵌入式
9.把编译瘦身后的文件传送到开发板然后就可以把编译好的库文件和执行文件全部传送到开发板上,执行命令:scplibavcodec/libavcodec.so.58  root@192.168.50.150:/home/rootscplibavformat/libavformat.so.58  root@192.168.50.150:/home/rootscplibavdevice/libavdevice.so.58  root@192.168.50.150:/home/rootscplibavfilter/libavfilter.so.7  root@192.168.50.150:/home/rootscplibavutil/libavutil.so.56  root@192.168.50.150:/home/rootscplibswresample/libswresample.so.3  root@192.168.50.150:/home/rootscplibswscale/libswscale.so.5  root@192.168.50.150:/home/root嵌入式
 

03.

 

测试FFmpeg程序

因此把FFmpeg的动态库和主程序都复制到板上的/home/root目录下了,因此执行FFmpeg时,会提示找不到依赖的运行库,因此在开发板环境下首先设置一个动态连接库的路径。
export LD_LIBRARY_PATH=/home/root

1.查看FFmpeg运行输出信息
 

然后执行ffmpeg –version ,看一下版本信息;
 

嵌入式

可见输出了FFmpeg版本库正确的信息。

2. 使用FFmpeg采集视频
 

执行命令:
./ffmpeg-f v4l2 -s 320*240 -r 10 -i /dev/video2 output.yuv
嵌入式


 

把从摄像头采集到的yuv数据,使用yuvview播放器上播放,播放如图:
 

嵌入式

显示采集视频成功。

3. 使用FFmpeg采集编码视频

板上执行命令:

./ffmpeg -f v4l2 -s 640*360 -r 10 -i /dev/video2output1.mp4
嵌入式嵌入式
 

传输到主机上,查看采集编码的视频内容。

嵌入式
 

可见,能够正确的采集并编码出视频文件了。

3. 使用FFmpeg采集编码并进行推送rtsp视频流

因为在编译ffmpeg时,没加入X264编码库,因此这里只能使用mpeg4编码器,因此执行命令如下:
./ffmpeg -t 10 -f video4linux2 -s176*144 -r 8 -i /dev/video2 -vcodec mpeg4 -f rtp rtp://192.168.50.171:554 >/ffmpeg.sdp

Rtsp推流测试,需要一个rtsp服务,推流软件先使用rtsp协议把流推送到rtsp服务上,然后播放器通过连接rtsp服务器进行拉流播放。Rtsp服务这里使用了 RtspServer 开源rtsp服务器,地址为:

https://github.com/PHZ76/RtspServer.git

在ubuntu主机上编译成rtsp服务程序,添加接收上行的流,再进行进入队列,并进行分发即可。操作为启动rtsp服务后,等待ffmpeg推流,在ffmpeg推流后,即可通过VLC 进行播放测试。
 

嵌入式嵌入式

04.

 

测试FFmpeg例子程序

再测试一下基于FFmpeg库的例子程序,这里测试一下example下的几个例程,把例程copy到板子上:
 

嵌入式
 

然后进入开发板执行一个视频合成例程 muxing,这个例程是调用ffmpeg库,来生成一段机器合成视频文件。
 

嵌入式
 

看一下输出的视频文件:
 

嵌入式

05.

 

开发FFmpeg应用程序

也可以基于FFmpeg库进行视频应用的开发,这里从网上下载一段FFmpeg的应用程序内容,来测试一下基于上面编译的FFmpeg库,来开发应用程序。

代码如下:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

int main(int argc, char **argv){ uint8_t *src_data[4], *dst_data[4]; int src_linesize[4], dst_linesize[4]; int src_w = 320, src_h = 240, dst_w, dst_h; enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P, dst_pix_fmt = AV_PIX_FMT_RGB24; const char *dst_size = NULL; const char *dst_filename = NULL; FILE *dst_file; int dst_bufsize; struct SwsContext *sws_ctx; int i, ret;
if (argc != 3) { fprintf(stderr, "Usage: %s output_file output_size\n" "API example program to show how to scale an image with libswscale.\n" "This program generates a series of pictures, rescales them to the given " "output_size and saves them to an output file named output_file\n." "\n", argv[0]); exit(1); } dst_filename = argv[1]; dst_size = argv[2];
if (av_parse_video_size(&dst_w, &dst_h, dst_size) < 0) { fprintf(stderr, "Invalid size '%s', must be in the form WxH or a valid size abbreviation\n", dst_size); exit(1); }
dst_file = fopen(dst_filename, "wb"); if (!dst_file) { fprintf(stderr, "Could not open destination file %s\n", dst_filename); exit(1); }
/* create scaling context */ sws_ctx = sws_getContext(src_w, src_h, src_pix_fmt, dst_w, dst_h, dst_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL); if (!sws_ctx) { fprintf(stderr, "Impossible to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", av_get_pix_fmt_name(src_pix_fmt), src_w, src_h, av_get_pix_fmt_name(dst_pix_fmt), dst_w, dst_h); ret = AVERROR(EINVAL); goto end; }
/* allocate source and destination image buffers */ if ((ret = av_image_alloc(src_data, src_linesize, src_w, src_h, src_pix_fmt, 16)) < 0) { fprintf(stderr, "Could not allocate source image\n"); goto end; }
/* buffer is going to be written to rawvideo file, no alignment */ if ((ret = av_image_alloc(dst_data, dst_linesize, dst_w, dst_h, dst_pix_fmt, 1)) < 0) { fprintf(stderr, "Could not allocate destination image\n"); goto end; } dst_bufsize = ret;
for (i = 0; i < 100; i++) { /* generate synthetic video */ fill_yuv_image(src_data, src_linesize, src_w, src_h, i);
/* convert to destination format */ sws_scale(sws_ctx, (const uint8_t * const*)src_data, src_linesize, 0, src_h, dst_data, dst_linesize);
/* write scaled image to file */ fwrite(dst_data[0], 1, dst_bufsize, dst_file); }
fprintf(stderr, "Scaling succeeded. Play the output file with the command:\n" "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", av_get_pix_fmt_name(dst_pix_fmt), dst_w, dst_h, dst_filename);
end: fclose(dst_file); av_freep(&src_data[0]); av_freep(&dst_data[0]); sws_freeContext(sws_ctx); return ret < 0;}

编译后,板上运行:嵌入式
 

成功地输出 rgb 格式的原始视频文件,传输到PC上,使用yuvplayer 播放,可以看到生成的内容:

嵌入式

06.

 

Y6ULX板上FFmpeg移植测试开发总结

    通过从这次在MYD-Y6ULL开发板上进行FFmpeg的移植编译与应用开发测试过程看,FFmpeg在该开发板上移植非常容易,而且基本上代码不做任何改动,能够一次编译通过。也说明了该开发板的C/C++的开发工具与库支持比较完善,对这样复杂的工程也有很好的支持。    这也大大方便了试用该方案进行音视频开发的可能性,能够利用开源社区大量的音视频应用资源,开发出多种嵌入式的音视频应用。

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分