FATE (FFmpeg Automated Testing Environment) 是FFmpeg社区开发的自动化测试框架,支持对FFmpeg API的测试,也支持对FFmpeg内部函数的测试。对FFmpeg video filter的测试,就属于对API的测试。之前为了为一个video filter增加fate测试,大概研究了一下,感觉FFmpeg官方主页的资料还可以更详细,在酝酿写一篇介绍的时候,看到"大师兄悟空"在微信公共号“流媒体技术”发布了《如何编写FFmpeg自动化测试用例》(一、二),就一时懒得动笔。

趁最近几天假期,想想还是记录一下为佳,除了分享外,还可以自用备查。我的记录角度是在Linux环境下如何一步步的探索出FFmpeg video filter FATE的测试过程,从而理解如何为一个video filter增加fate测试。可能这样更实用,一个新的任务,缺少足够资料,都可以用这样类似的方法去探索。成文顺序虽然大致是按探索过程来的,但是,为了阅读逻辑性,有些地方会直接介绍后来才理解到的知识点。

先上一个图,列一下接下来会用到的主要文件以及目录。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tGRrAXw6-1587431743857)(https://graph.baidu.com/resource/2227cb590ec9d4d2b732201581001970.png)]

  • 预备步骤1

得到FFmpeg源代码并且编译:

#演示目的,所以,所有内容都放在/tmp/目录下
$ mkdir -p /tmp/ffmpeg_work
$ cd /tmp/ffmpeg_work$ git clone https://git.ffmpeg.org/ffmpeg.git#新建编译目录,以和源码目录分离
$ mkdir build
$ cd build#configure参数有很多,这里简单起见,使用默认值
$ ../ffmpeg/configure$ make -j8#可以看到编译出了ffmpeg和ffplay等
#其中带_g后缀的文件名表示symbol没有被strip。
$ ls
config.asm  doc      ffmpeg    ffplay    ffprobe
fftools     libavdevice  libavformat  libswresample
Makefile  tests config.h    ffbuild  ffmpeg_g
ffplay_g  ffprobe_g  libavcodec  libavfilter
libavutil  libswscale     src.
  • 预备步骤2

在FATE中有很多测试,有些测试是自包含的,有些测试还需要额外的资料,在FFmpeg中一般称之为SAMPLES,可以用下面的方法获取SAMPLES。

$ make fate-rsync SAMPLES=fate-suite/
#命令输出如下,表示用rsync方法来同步资料
rsync -vrltLW --timeout=60 --contimeout=60 rsync://fate-suite.ffmpeg.org/fate-suite/ fate-suite/

不幸的是,由于网络原因,这条命令很难成功执行。所以,只能假设这些资料都已经在你的系统的/tmp/ffmpeg_work/fate-suite/目录下面了。需要提醒的是,这些资料可能会经常更新(一般来说,只会增加内容,不会修改文件),如果你要跑完整FATE的话,需要时时更新。

  • 运行FATE

对于自包含不需要SAMPLES的测试,只需要运行下面的命令。

$ make fate
GEN     tests/pixfmts.mak
# 下面的输出提示也表明后续测试不需要SAMPLES
warning: only a subset of the fate tests will be run because SAMPLES is not specified
HOSTCC  tests/base64.o
HOSTLD  tests/base64
HOSTCC  tests/tiny_psnr.o
HOSTLD  tests/tiny_psnr
HOSTCC  tests/tiny_ssim.o
HOSTLD  tests/tiny_ssim
HOSTCC  tests/audiomatch.o
HOSTLD  tests/audiomatch
CC      tests/checkasm/aacpsdsp.o
CC      tests/checkasm/af_afir.o
CC      tests/checkasm/alacdsp.o
...
TEST    checkasm-aacpsdsp
TEST    checkasm-af_afir
TEST    checkasm-alacdsp
...

我为DNN模块加的FATE测试也属于这种类型。

对于需要SAMPLES的测试,则可以用下面的命令来指定SAMPLES对应的目录。实际上,指定SAMPLES还有其他的几种方法,这里只是选择了其中的一种方法。在指定SAMPLES后,make fate就会跑所有的测试用例,包括自包含的测试。

$ make fate SAMPLES=../fate-suite/
...
TEST    filter-scale500
...

有些FFmpeg video filter的fate测试属于这种类型,需要依赖SAMPLES的存在。而有些video filter的fate测试所需要的数据都已经在FFmpeg源代码目录树中了,就不需要依赖SAMPLES了。

在make fate的时候,首先会编译测试用例(见命令行输出的CC等字样),然后就会执行测试(见命令行输出的TEST字样)。根据命令行输出我们可以看到有很多个子测试被执行。

  • 单跑一个fate测试

上述make fate的方法会跑多个测试,为了理解如何增加一个filter测试,我们应该先缩小到一个测试的情况。基于对makefile的了解,我们知道只要为make指定单独的一个测试作为target即可,那具体应该是什么呢,大胆猜测,就从上一步骤的命令行输出中选择字符串filter-scale500,试着执行make filter-scale500,发现并不正确。那怎么办呢,到FFmpeg源代码目录的tests目录下,用grep搜索字符串,最终在filter-video.mak文件中找到了正确的字符串是fate-filter-scale500。如下所示。

$ make filter-scale500
make: *** No rule to make target 'filter-scale500'.  Stop.$ cd ../ffmpeg/tests/
$ grep -rn filter-scale500 ./
./fate/filter-video.mak:478:FATE_FILTER_VSYNTH-$(CONFIG_SCALE_FILTER) += fate-filter-scale500
./fate/filter-video.mak:479:fate-filter-scale500: CMD = video_filter "scale=w=500:h=500"$ cd -
/tmp/ffmpeg_work/build
$ make fate-filter-scale500
TEST    filter-scale500

为什么选择filter-scale500作为尝试呢,首先,这是video filter,而我要加的也是video filter的测试;其次,scale具体是干什么的,容易望文生义,比较好理解;最后,有了500为后缀后,字符串比较唯一,不容易搜索到其他容易混淆的地方去。

  • 深入到一个fate测试中

对于make来说,使用V=1可以输出更多的信息,如下所示。

$ V=1 make fate-filter-scale500
TEST    filter-scale500
/tmp/ffmpeg_work/ffmpeg/tests/fate-run.sh fate-filter-scale500 "" "" "/tmp/ffmpeg_work/build" 'video_filter "scale=w=500:h=500"' '' '' '' '1' '' '' '' '' '' '' '' '' '' ''/tmp/ffmpeg_work/build/ffmpeg -nostdin -nostats -cpuflags all -flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact -threads 1 -f image2 -vcodec pgmyuv -hwaccel none -threads 1 -thread_type frame+slice -i /tmp/ffmpeg_work/build/tests/vsynth1/%02d.pgm -flags +bitexact -sws_flags +accurate_rnd+bitexact -fflags +bitexact -flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact -threads 1 -dct fastint -vf scale=w=500:h=500 -vcodec rawvideo -frames:v 5 -f nut md5:

从上述信息可以看出来,makefile中调用了fate-run.sh,而这个脚本执行时候的参数是哪里来的,看一下前面找到的filter-video.mak文件,发现匹配在一起了。

#filter-video.mak部分内容摘录
FATE_FILTER_VSYNTH-$(CONFIG_SCALE_FILTER) += fate-filter-scale500
fate-filter-scale500: CMD = video_filter "scale=w=500:h=500"

所以,如果要为video filter加一个fate测试,就是要在filter-video.mak增加相应的内容。浏览这个文件,可以看到CMD = ...的开头,除了video_filter外,还有诸如framecrc、framemd5和ffmpeg等,都是作为参数被传入fate-run.sh脚本中,具体含义将在后面介绍。

  • 探索fate-run.sh

为了看到脚本fate-run.sh中每条指令的执行过程,我们用bash -x来跑这个脚本,如下所示。

# bash -x 会打印出shell脚本中每一条指令的执行情况
$ bash -x /tmp/ffmpeg_work/ffmpeg/tests/fate-run.sh fate-filter-scale500 "" "" "/tmp/ffmpeg_work/build" 'video_filter "scale=w=500:h=500"' '' '' '' '1' '' '' '' '' '' '' '' '' '' ''
...

根据上述命令行输出,一行一行的对应过去,最终会找到下面的关键脚本:

eval $command >"$outfile" 2>$errfile

还是从上述命令行输出可以看到,eval这行脚本展开后为:

eval video_filter '"scale=w=500:h=500"'

于是,eval执行这条命令,也就是来自filter-video.mak文件中的CMD的内容。而在脚本文件fate-run.sh中,已经定义了多个函数,诸如video_filter、framecrc、framemd5和ffmpeg等,相应的命令就对应着这些内部函数。

  • 最终执行的命令

用eval启用的命令,被shell直接当做一条命令处理,无法看到内部的展开情况,(也可能是我没有找到正确的方法)。作为一个快速方法,我们直接修改脚本中的eval行如下所示。

#eval $command >"$outfile" 2>$errfile
video_filter "scale=w=500:h=500"
#为了避免和后面的混淆,所以加了exit
exit

再用bash -x来执行这个脚本,如下所示。

$ bash -x /tmp/ffmpeg_work/ffmpeg/tests/fate-run.sh fate-filter-scale500 "" "" "/tmp/ffmpeg_work/build" 'video_filter "scale=w=500:h=500"' '' '' '' '1' '' '' '' '' '' '' '' '' '' ''
...
+ /tmp/ffmpeg_work/build/ffmpeg -nostdin -nostats -cpuflags all -flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact -threads 1 -f image2 -vcodec pgmyuv -hwaccel none -threads 1 -thread_type frame+slice -i /tmp/ffmpeg_work/build/tests/vsynth1/%02d.pgm -flags +bitexact -sws_flags +accurate_rnd+bitexact -fflags +bitexact -flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact -threads 1 -dct fastint -vf scale=w=500:h=500 -vcodec rawvideo -frames:v 5 -f nut md5:
...
e7d6f07710a707e4e5583aee54a8f5ff
+ exit

上述命令行输出很长,大部分篇幅是用来设置最终的/tmp/ffmpeg_work/build/ffmpeg的命令行参数,最后得到的命令行参数如上所示(两行省略号之间的那一行)。回顾一下,其实,我们前面在执行V=1 make fate-filter-scale500看到的最后一行输出,和这里看到的是相同的。而倒数第二行的e7d6…这一串字符则是ffmpeg的输出结果。我们也可以直接在命令行执行ffmpeg程序,得到相同的结果,如下所示。

$ /tmp/ffmpeg_work/build/ffmpeg -nostdin -nostats -cpuflags all -flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact -threads 1 -f image2 -vcodec pgmyuv -hwaccel none -threads 1 -thread_type frame+slice -i /tmp/ffmpeg_work/build/tests/vsynth1/%02d.pgm -flags +bitexact -sws_flags +accurate_rnd+bitexact -fflags +bitexact -flags +bitexact -idct simple -sws_flags +accurate_rnd+bitexact -fflags +bitexact -threads 1 -dct fastint -vf scale=w=500:h=500 -vcodec rawvideo -frames:v 5 -f nut md5:
...
e7d6f07710a707e4e5583aee54a8f5ff
  • ffmpeg的几个参数

上述ffmpeg命令行参数的最后是-f nut md5:,表示将结果做md5的输出,输出一个字符串。如果在filter-video.mak 中,CMD是用framecrc开始的,那么,最终得到的ffmpeg命令行参数大致如下所示,其中最后的-f framecrc -表示对每个输入的图片做处理后的最终结果是一个crc值。在命令行执行此命令,得到的输出是多行字符串,分别对应着每一个输入,如下所示,一共处理了50个输入图片。

$ /tmp/ffmpeg_work/build/ffmpeg -nostdin -nostats -cpuflags all -c:v pgmyuv -hwaccel none -threads 1 -thread_type frame+slice -i /tmp/ffmpeg_work/build/tests/vsynth1/%02d.pgm -vf format=rgb24,perms=random,colorchannelmixer=.31415927:.4:.31415927:0:.27182818:.8:.27182818:0:.2:.6:.2:0 -flags +bitexact -sws_flags +accurate_rnd+bitexact -bitexact -f framecrc -
...
0,          0,          0,        1,   304128, 0x42900c13
0,          1,          1,        1,   304128, 0xfb0439bc
0,          2,          2,        1,   304128, 0x967b9f0d
0,          3,          3,        1,   304128, 0xc2c92489
0,          4,          4,        1,   304128, 0x024499b1
0,          5,          5,        1,   304128, 0x66144785
0,          6,          6,        1,   304128, 0x0e505bcd
0,          7,          7,        1,   304128, 0xc8b26ed2
0,          8,          8,        1,   304128, 0x14b5717b
0,          9,          9,        1,   304128, 0x2ba3144a
0,         10,         10,        1,   304128, 0x1185992b
0,         11,         11,        1,   304128, 0xd55b289a
0,         12,         12,        1,   304128, 0x59f2f3be
0,         13,         13,        1,   304128, 0xfe4d6adf
0,         14,         14,        1,   304128, 0x630806cc
0,         15,         15,        1,   304128, 0x2deb2f19
0,         16,         16,        1,   304128, 0xfbffa923
0,         17,         17,        1,   304128, 0xb7770d46
0,         18,         18,        1,   304128, 0xda09bd0e
0,         19,         19,        1,   304128, 0x17a422d2
0,         20,         20,        1,   304128, 0xbb6172f5
0,         21,         21,        1,   304128, 0xcf639456
0,         22,         22,        1,   304128, 0xdb0ae1ac
0,         23,         23,        1,   304128, 0x850d6a68
0,         24,         24,        1,   304128, 0xdc8409fb
0,         25,         25,        1,   304128, 0x26216c51
0,         26,         26,        1,   304128, 0x1d0004de
0,         27,         27,        1,   304128, 0xed019a70
0,         28,         28,        1,   304128, 0xb1abd985
0,         29,         29,        1,   304128, 0xec1c14b2
0,         30,         30,        1,   304128, 0x046db068
0,         31,         31,        1,   304128, 0xa4fb1029
0,         32,         32,        1,   304128, 0x49e05e61
0,         33,         33,        1,   304128, 0x7668d6d1
0,         34,         34,        1,   304128, 0x6dd0ce9d
0,         35,         35,        1,   304128, 0x87983f5e
0,         36,         36,        1,   304128, 0xb98278cf
0,         37,         37,        1,   304128, 0x55186244
0,         38,         38,        1,   304128, 0x3135e7ea
0,         39,         39,        1,   304128, 0xdbf59a2c
0,         40,         40,        1,   304128, 0x944cdc92
0,         41,         41,        1,   304128, 0x5849dfe8
0,         42,         42,        1,   304128, 0xaf9075ba
0,         43,         43,        1,   304128, 0xb4f01118
0,         44,         44,        1,   304128, 0x4dfb711f
0,         45,         45,        1,   304128, 0xb558e732
0,         46,         46,        1,   304128, 0xb23a171e
0,         47,         47,        1,   304128, 0xb5c68065
0,         48,         48,        1,   304128, 0xcf1b122e
0,         49,         49,        1,   304128, 0x1e2d38e5

还有一些情况,在filter-video.mak 中,CMD是直接用ffmpeg开头,一般来说,最后得到的ffmpeg参数最后会是-f rawvideo -,表示最后输出的不是crc也不是md5,而是rawvideo的内容。

  • 测试的最后判断

回到fate-run.sh,再看一下eval行。

eval $command >"$outfile" 2>$errfile

不管是md5输出、crc输出还是rawvideo输出,都会被重定向到outfile文件中。然后,outfile文件中。然后,outfile文件中。然后,outfile文件内容和参考文件内容比较,得出测试是pass还是fail的结论。

继续用前面的bash -x的方法来执行fate-run.sh,继续看eval之后的命令行输出,我们可以看到md5和crc的参考文件的名字和所在的目录。一般来说,参考文件被保存在目录/tmp/ffmpeg_work/ffmpeg/tests/ref/fate/下。而对于rawvideo输出,情况略有不同。

有了crc和md5后,为什么还要rawvideo?目前我的理解是当处理过程中涉及到了float运算,或者frame的格式就是float相关的,那么,由于不同架构CPU的输出结果在浮点数操作上会略有不同,而FFmpeg是跨平台支持诸如X86、ARM、PowerPC等多种CPU的,我们就无法用crc或者md5这种bit-exact的方法进行比较了,只能采用基于rawvideo的方式进行比较,并且允许存在一定的偏差。在FATE中,可以用oneoff的方法进行rawvideo的比较,在filter-video.mak文件中差不多可以这样写:

FATE_FILTER_SAMPLES-yes += fate-filter-abc
fate-filter-abc: CMD = ffmpeg -i $(SRC) -frames:v 1 -vf ... -f rawvideo -
fate-filter-abc: CMP = oneoff#指出参考文件所在的目录和文件名,
#需要将此参考文件发到samples-request@ffmpeg.org并申请放在相应目录下
fate-filter-abc: REF = $(SAMPLES)/filter-reference/....raw#假如frame格式是float,这里就要指出这是f32的比较
fate-filter-abc: CMP_UNIT=f32#在比较时允许存在的偏差
fate-filter-abc: FUZZ = 1

在fate-run.sh中,oneoff最终会调用tiny_psnr,如下所示。具体不再展开,大致意思就是根据filter-video.mak文件中的参数设置,对当前测试得到的rawvideo文件和参考文件做个比较,判断两者是否相同。

do_tiny_psnr(){psnr=$(tests/tiny_psnr${HOSTEXECSUF} "$1" "$2" $cmp_unit $cmp_shift 0) || return 1val=$(expr "$psnr" : ".*$3: *\([0-9.]*\)")size1=$(expr "$psnr" : '.*bytes: *\([0-9]*\)')size2=$(expr "$psnr" : '.*bytes:[ 0-9]*/ *\([0-9]*\)')val_cmp=$(compare $val $cmp_target $fuzz)size_cmp=$(compare $size1 $size2 $size_tolerance)if [ "$val_cmp" != 0 ] || [ "$size_cmp" != 0 ]; thenecho "$psnr"if [ "$val_cmp" != 0 ]; thenecho "$3: |$val - $cmp_target| >= $fuzz"fiif [ "$size_cmp" != 0 ]; thenecho "size: |$size1 - $size2| >= $size_tolerance"fireturn 1fi
}oneoff(){do_tiny_psnr "$1" "$2" MAXDIFF
}

经过上述探索过程后,知其然亦知其所以然,再来写一个video filter的fate测试,就得心应手了。

以上内容是本人业余时间兴趣之作,限于水平,差错难免,仅代表个人观点,和本人任职公司无关。

本文首发于微信公众号:那遁去的一

FFmpeg video filter FATE测试过程介绍相关推荐

  1. ffmpeg 之video filter 大全(待整理)

    截止到19年11月13日,ffmpeg官网上显示,ffmepg目前已有的video filter是233个. 本文以ffmepg4.1为例,介绍各个video filter的用途和用法.每个filte ...

  2. FFmpeg filter的使用介绍

    1. 参考 [1] ffmpeg.org/libavfilter documentation [2] ffmpeg.org/Filters Documentation [3] 北雨南萍/FFmpeg- ...

  3. FFmpeg filter语法使用介绍

    目录 参考 FFmpeg filter简介 filter的使用方法 1. 参考 [1] ffmpeg.org/libavfilter documentation [2] ffmpeg.org/Filt ...

  4. ffmpeg的filter分析

    原文网址(转载请注明出处): (http://blog.csdn.net/newchenxf/article/details/51364105) 目录 目录 什么是ffmpeg filter 如何使用 ...

  5. 使用ffmpeg的filter处理yuv数据包括split filter(分流)、crop filter(裁剪)、vflip filter(垂直向上的翻转)、overlay filter(合成)

    使用ffmpeg的filter处理yuv数据包括split filter(分流).crop filter(裁剪).vflip filter(垂直向上的翻转).overlay filter(合成) #i ...

  6. FFmpeg的视频format滤镜介绍

    视频的 format 滤镜是一个非常常用的滤镜,用来转换图像的格式,例如可以把 AV_PIX_FMT_YUV420P 转成 AV_PIX_FMT_RGB24. 我们可以用以下命令查询 format 滤 ...

  7. 使用ffmpeg 的 filter 给图片添加水印

    使用ffmpeg 的 filter 给图片添加水印. main.c #include <stdio.h>#include <libavfilter/avfilter.h> #i ...

  8. java过滤器应用实例_Servlet过滤器Filter的简单介绍(附示例)

    本篇文章给大家带来的内容是关于Servlet过滤器Filter的简单介绍(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 特点 1)Filter是依赖于Servlet容器,属 ...

  9. Filter 过滤器的介绍及使用

    Filter过滤器 MySql的语法看这篇就够了,传送地址:MySql必知必会 JSP的使用看这篇就够了,传送地址:JSP必知必会 文章目录 Filter过滤器 一.Filter过滤器的介绍 二.Fi ...

最新文章

  1. 2022-2028年中国无溶剂聚氨酯复膜胶行业市场调查研究及发展前景规划报告
  2. 【算法学习笔记】43.动态规划 逆向思维 SJTU OJ 1012 增长率问题
  3. 点击切换属性html,jQuery_$方法、属性、点击切换
  4. inotify-tools使用方法介绍
  5. [黑马程序员二]:C#面向对象基础
  6. SQL中Group By的使用,以及一些特殊使用方法
  7. eigen 编译_头条 | 使用eigen实现四元数、欧拉角、旋转矩阵、旋转向量间的转换...
  8. pict 手册_zbrush中文手册.pdf
  9. PHP define()的用法
  10. 线段树(Segment Tree)
  11. 如何在Visual Studio 2012中撤消“范围到此”?
  12. 台式计算机华硕电源,美声大师+智能电源 华硕台式电脑M51AC
  13. springboot+web文件上传和下载
  14. 洛谷——P1507 NASA的食物计划
  15. 联发科想办法与大陆合作是生存的唯一出路
  16. 腾讯android 热更新,Android 腾讯 Bugly 热更新
  17. 一文搞懂蓝绿部署和金丝雀发布
  18. 分号与逗号的区别及举例_顿号、逗号、分号的区别
  19. SQL--打折日期交叉问题
  20. yolov3原理+训练损失

热门文章

  1. 根据不同按钮显现不同的表格(表格为多个)
  2. OpenCV中的图像处理 —— 图像梯度+Canny边缘检测+图像金字塔
  3. Spring boot 写一个接口
  4. NSS LitCTF部分wp
  5. Avi与Kubernetes集成
  6. css拖动音乐进度条,用css3绘制酷狗音乐闪烁的进度条
  7. (七)JavaScript 流程控制语句
  8. 认证服务器管理系统,微服务系统之认证管理详解
  9. 7个方法快速提升你的技术领导力!
  10. [含论文+开题报告+答辩PPT+源码等]SSM校园食堂点餐系统订餐就餐餐厅(已降重)