diff mbox series

[FFmpeg-devel,2/2] FATE: add tests for v360/ssim360 filters

Message ID 20210809102919.387-2-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,1/2] lavfi: Add vf_ssim360 filter | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/PPC64_make warning Make failed
andriy/x86_make_fate fail Make fate failed

Commit Message

Anton Khirnov Aug. 9, 2021, 10:29 a.m. UTC
---
 libavfilter/Makefile                   |   3 +-
 tests/Makefile                         |   4 +-
 tests/fate-run.sh                      |   6 +
 tests/fate/filter-video.mak            |  14 ++
 tests/ref/fate/filter-spherical-barrel | 192 +++++++++++++++++++++++++
 tests/ref/fate/filter-spherical-c3x2   | 192 +++++++++++++++++++++++++
 tools/Makefile                         |   1 +
 tools/spherical_compare.c              | 148 +++++++++++++++++++
 8 files changed, 558 insertions(+), 2 deletions(-)
 create mode 100644 tests/ref/fate/filter-spherical-barrel
 create mode 100644 tests/ref/fate/filter-spherical-c3x2
 create mode 100644 tools/spherical_compare.c

Comments

Derek Buitenhuis Aug. 9, 2021, 1:43 p.m. UTC | #1
On 8/9/2021 11:29 AM, Anton Khirnov wrote:
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index b0348ccfa3..27dd0c4b47 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -559,7 +559,8 @@ SKIPHEADERS-$(CONFIG_VULKAN)                 += vulkan.h
>  
>  OBJS-$(CONFIG_LIBGLSLANG)                    += glslang.o
>  
> -TOOLS     = graph2dot
> +TOOLS     = graph2dot                                                   \
> +            spherical_compare

Is there a reason it needs a new tool rather than ffmpeg.c?

> +frame 0
> +lavfi.ssim360.Y=0.97
> +lavfi.ssim360.U=1.00
> +lavfi.ssim360.V=1.00
> +lavfi.ssim360.All=1.00
> +lavfi.ssim360.dB=25.19

Is it wise to do a non-fuzzy compare of floats?

- Derek
Anton Khirnov Aug. 30, 2021, 9:16 a.m. UTC | #2
Quoting Derek Buitenhuis (2021-08-09 15:43:02)
> On 8/9/2021 11:29 AM, Anton Khirnov wrote:
> > diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> > index b0348ccfa3..27dd0c4b47 100644
> > --- a/libavfilter/Makefile
> > +++ b/libavfilter/Makefile
> > @@ -559,7 +559,8 @@ SKIPHEADERS-$(CONFIG_VULKAN)                 += vulkan.h
> >  
> >  OBJS-$(CONFIG_LIBGLSLANG)                    += glslang.o
> >  
> > -TOOLS     = graph2dot
> > +TOOLS     = graph2dot                                                   \
> > +            spherical_compare
> 
> Is there a reason it needs a new tool rather than ffmpeg.c?

I do not believe every single testing-only feature needs to be stuffed
into ffmpeg.c. It's big and complicated enough already, while tests
should ideally be simple and test just the thing they are supposed to
test.

> 
> > +frame 0
> > +lavfi.ssim360.Y=0.97
> > +lavfi.ssim360.U=1.00
> > +lavfi.ssim360.V=1.00
> > +lavfi.ssim360.All=1.00
> > +lavfi.ssim360.dB=25.19
> 
> Is it wise to do a non-fuzzy compare of floats?

One could hope that truncating to two decimal places might not break,
but I suppose it's still possible. Guess I could change the tool to
parse the strings and compare them properly.
diff mbox series

Patch

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index b0348ccfa3..27dd0c4b47 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -559,7 +559,8 @@  SKIPHEADERS-$(CONFIG_VULKAN)                 += vulkan.h
 
 OBJS-$(CONFIG_LIBGLSLANG)                    += glslang.o
 
-TOOLS     = graph2dot
+TOOLS     = graph2dot                                                   \
+            spherical_compare
 TESTPROGS = drawutils filtfmts formats integral
 
 TOOLS-$(CONFIG_LIBZMQ) += zmqsend
diff --git a/tests/Makefile b/tests/Makefile
index e42e66d81b..aba31b59d0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -208,7 +208,8 @@  FATE_EXTERN-$(CONFIG_FFMPEG)  += $(FATE_SAMPLES_AVCONV) $(FATE_SAMPLES_FFMPEG)
 FATE_EXTERN-$(CONFIG_FFPROBE) += $(FATE_SAMPLES_FFPROBE)
 FATE_SAMPLES_FFMPEG_FFPROBE   += $(FATE_SAMPLES_FFMPEG_FFPROBE-yes)
 FATE_EXTERN-$(call ALLYES, FFMPEG FFPROBE) += $(FATE_SAMPLES_FFMPEG_FFPROBE)
-FATE_EXTERN                   += $(FATE_EXTERN-yes) $(FATE_SAMPLES_FASTSTART)
+FATE_EXTERN                   += $(FATE_EXTERN-yes) $(FATE_SAMPLES_FASTSTART) \
+                                 $(FATE_SAMPLES_SPHERICAL_COMPARE)
 
 FATE += $(FATE-yes)
 
@@ -222,6 +223,7 @@  $(FATE_FFPROBE) $(FATE_FFMPEG_FFPROBE) $(FATE_SAMPLES_FFPROBE) $(FATE_SAMPLES_FF
 $(FATE_SAMPLES_FASTSTART): tools/qt-faststart$(EXESUF)
 $(FATE_SAMPLES_DUMP_DATA): tools/venc_data_dump$(EXESUF)
 $(FATE_SAMPLES_SCALE_SLICE): tools/scale_slice_test$(EXESUF)
+$(FATE_SAMPLES_SPHERICAL_COMPARE): tools/spherical_compare$(EXESUF)
 
 ifdef SAMPLES
 FATE += $(FATE_EXTERN)
diff --git a/tests/fate-run.sh b/tests/fate-run.sh
index cd16f5fcff..afdbb4d2ec 100755
--- a/tests/fate-run.sh
+++ b/tests/fate-run.sh
@@ -507,6 +507,12 @@  venc_data(){
     run tools/venc_data_dump${EXECSUF} ${file} ${stream} ${frames} ${threads} ${thread_type}
 }
 
+spherical_compare(){
+    file=$1
+    filterchain=$2
+    run tools/spherical_compare${EXECSUF} ${file} ${filterchain}
+}
+
 null(){
     :
 }
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index 16f24a8422..a28c3f7aec 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -845,6 +845,20 @@  FATE_FILTER_SAMPLES-$(call ALLYES, MOV_DEMUXER H264_DECODER AAC_FIXED_DECODER PC
 fate-filter-meta-4560-rotate0: tests/data/file4560-override2rotate0.mov
 fate-filter-meta-4560-rotate0: CMD = framecrc -auto_conversion_filters -flags +bitexact -c:a aac_fixed -i $(TARGET_PATH)/tests/data/file4560-override2rotate0.mov
 
+SPHERICAL_DEPS = MATROSKA_DEMUXER VP8_DECODER V360_FILTER SSIM360_FILTER
+SPHERICAL_SAMPLE = $(TARGET_SAMPLES)/spherical/Worlds_First_Live_360_Rocket_Launch-_Orbital_ATK_CRS-7_cut.mkv
+
+FATE_FILTER_SPHERICAL += fate-filter-spherical-c3x2
+fate-filter-spherical-c3x2: CMD = spherical_compare $(SPHERICAL_SAMPLE) \
+	"split[in][ref];[in]v360=input=e:output=c3x2[main];[main][ref]ssim360=main_projection=c3x2:ref_projection=e"
+
+FATE_FILTER_SPHERICAL += fate-filter-spherical-barrel
+fate-filter-spherical-barrel: CMD = spherical_compare $(SPHERICAL_SAMPLE) \
+	"split[in][ref];[in]v360=input=e:output=barrel[main];[main][ref]ssim360=main_projection=barrel:ref_projection=e"
+
+FATE_SAMPLES_SPHERICAL_COMPARE-$(call ALLYES, $(SPHERICAL_DEPS)) += $(FATE_FILTER_SPHERICAL)
+FATE_SAMPLES_SPHERICAL_COMPARE += $(FATE_SAMPLES_SPHERICAL_COMPARE-yes)
+
 REFCMP_DEPS = FFMPEG LAVFI_INDEV TESTSRC2_FILTER AVGBLUR_FILTER METADATA_FILTER
 
 FATE_FILTER-$(call ALLYES, $(REFCMP_DEPS) PSNR_FILTER) += fate-filter-refcmp-psnr-rgb
diff --git a/tests/ref/fate/filter-spherical-barrel b/tests/ref/fate/filter-spherical-barrel
new file mode 100644
index 0000000000..a2ff075a26
--- /dev/null
+++ b/tests/ref/fate/filter-spherical-barrel
@@ -0,0 +1,192 @@ 
+frame 0
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=25.19
+frame 1
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=25.04
+frame 2
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.95
+frame 3
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.88
+frame 4
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.80
+frame 5
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.75
+frame 6
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.70
+frame 7
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.63
+frame 8
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.58
+frame 9
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.54
+frame 10
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.51
+frame 11
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.49
+frame 12
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.46
+frame 13
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.42
+frame 14
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.41
+frame 15
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.39
+frame 16
+lavfi.ssim360.Y=0.97
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.33
+frame 17
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.30
+frame 18
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.27
+frame 19
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.25
+frame 20
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.22
+frame 21
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.19
+frame 22
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.16
+frame 23
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.14
+frame 24
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.08
+frame 25
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.02
+frame 26
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=24.01
+frame 27
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=23.98
+frame 28
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=23.96
+frame 29
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=23.95
+frame 30
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=23.93
+frame 31
+lavfi.ssim360.Y=0.96
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=1.00
+lavfi.ssim360.dB=23.91
diff --git a/tests/ref/fate/filter-spherical-c3x2 b/tests/ref/fate/filter-spherical-c3x2
new file mode 100644
index 0000000000..7c3b269f40
--- /dev/null
+++ b/tests/ref/fate/filter-spherical-c3x2
@@ -0,0 +1,192 @@ 
+frame 0
+lavfi.ssim360.Y=0.84
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.99
+lavfi.ssim360.dB=18.61
+frame 1
+lavfi.ssim360.Y=0.84
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=18.21
+frame 2
+lavfi.ssim360.Y=0.84
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=18.03
+frame 3
+lavfi.ssim360.Y=0.83
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.90
+frame 4
+lavfi.ssim360.Y=0.83
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.72
+frame 5
+lavfi.ssim360.Y=0.83
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.62
+frame 6
+lavfi.ssim360.Y=0.83
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.52
+frame 7
+lavfi.ssim360.Y=0.83
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.36
+frame 8
+lavfi.ssim360.Y=0.83
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.32
+frame 9
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.21
+frame 10
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.16
+frame 11
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.09
+frame 12
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=1.00
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=17.04
+frame 13
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.96
+frame 14
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.95
+frame 15
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.88
+frame 16
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.81
+frame 17
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.77
+frame 18
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.69
+frame 19
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.68
+frame 20
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.65
+frame 21
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.59
+frame 22
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.56
+frame 23
+lavfi.ssim360.Y=0.82
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.53
+frame 24
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.51
+frame 25
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.47
+frame 26
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.45
+frame 27
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.40
+frame 28
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.34
+frame 29
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.31
+frame 30
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.24
+frame 31
+lavfi.ssim360.Y=0.81
+lavfi.ssim360.U=0.99
+lavfi.ssim360.V=1.00
+lavfi.ssim360.All=0.98
+lavfi.ssim360.dB=16.20
diff --git a/tools/Makefile b/tools/Makefile
index f4d1327b9f..3cd252d712 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -19,6 +19,7 @@  tools/target_io_dem_fuzzer.o: tools/target_dem_fuzzer.c
 
 tools/venc_data_dump$(EXESUF): tools/decode_simple.o
 tools/scale_slice_test$(EXESUF): tools/decode_simple.o
+tools/spherical_compare$(EXESUF): tools/decode_simple.o
 
 OUTDIRS += tools
 
diff --git a/tools/spherical_compare.c b/tools/spherical_compare.c
new file mode 100644
index 0000000000..bcd7858636
--- /dev/null
+++ b/tools/spherical_compare.c
@@ -0,0 +1,148 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "decode_simple.h"
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/common.h"
+#include "libavutil/dict.h"
+#include "libavutil/error.h"
+#include "libavutil/pixdesc.h"
+
+#include "libavformat/avformat.h"
+
+#include "libavcodec/avcodec.h"
+
+#include "libavfilter/avfilter.h"
+#include "libavfilter/buffersink.h"
+#include "libavfilter/buffersrc.h"
+
+typedef struct PrivData {
+    const char *filterchain;
+    AVFilterGraph   *fg;
+    AVFilterContext *src;
+    AVFilterContext *sink;
+    AVFrame         *frame;
+
+    uint64_t nb_frames;
+} PrivData;
+
+static int process_frame(DecodeContext *dc, AVFrame *frame)
+{
+    PrivData *pd = dc->opaque;
+    int ret;
+
+    if (!pd->fg) {
+        AVFilterInOut *inputs, *outputs;
+        char filterchain[1024];
+
+        if (!frame)
+            return 0;
+
+        snprintf(filterchain, sizeof(filterchain),
+                 "buffer@src=width=%d:height=%d:pix_fmt=%s:time_base=%d/%d,"
+                 "%s,buffersink@sink",
+                 frame->width, frame->height,
+                 av_get_pix_fmt_name(frame->format),
+                 dc->stream->time_base.num, dc->stream->time_base.den,
+                 pd->filterchain);
+
+        pd->fg = avfilter_graph_alloc();
+        if (!pd->fg)
+            return AVERROR(ENOMEM);
+
+        ret = avfilter_graph_parse2(pd->fg, filterchain, &inputs, &outputs);
+        if (ret < 0)
+            return ret;
+
+        av_assert0(!inputs && !outputs);
+
+        pd->src  = avfilter_graph_get_filter(pd->fg, "buffer@src");
+        pd->sink = avfilter_graph_get_filter(pd->fg, "buffersink@sink");
+        av_assert0(pd->src && pd->sink);
+
+        ret = avfilter_graph_config(pd->fg, pd->fg);
+        if (ret < 0)
+            return ret;
+
+        pd->frame = av_frame_alloc();
+        if (!pd->frame)
+            return AVERROR(ENOMEM);
+    }
+
+    ret = av_buffersrc_write_frame(pd->src, frame);
+    if (ret < 0)
+        return ret;
+
+    while (ret >= 0) {
+        AVDictionaryEntry *t = NULL;
+
+        ret = av_buffersink_get_frame(pd->sink, pd->frame);
+        if ((frame  && ret == AVERROR(EAGAIN)) ||
+            (!frame && ret == AVERROR_EOF))
+            return 0;
+        else if (ret < 0)
+            return ret;
+
+        fprintf(stdout, "frame %"PRIu64"\n", pd->nb_frames++);
+        while ((t = av_dict_get(pd->frame->metadata, "lavfi.ssim360", t, AV_DICT_IGNORE_SUFFIX)))
+            fprintf(stdout, "%s=%s\n", t->key, t->value);
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    PrivData      pd;
+    DecodeContext dc;
+
+    const char *filename, *fc;
+    int ret = 0;
+
+    if (argc <= 2) {
+        fprintf(stderr, "Usage: %s <input file> <filterchain>\n", argv[0]);
+        return 0;
+    }
+
+    filename = argv[1];
+    fc       = argv[2];
+
+    memset(&pd, 0, sizeof(pd));
+    pd.filterchain = fc;
+
+    ret = ds_open(&dc, filename, 0);
+    if (ret < 0)
+        goto finish;
+
+    dc.process_frame = process_frame;
+    dc.opaque        = &pd;
+
+    ret = ds_run(&dc);
+
+finish:
+    avfilter_graph_free(&pd.fg);
+    av_frame_free(&pd.frame);
+    ds_free(&dc);
+    return ret;
+}