summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authortytan6522022-02-17 22:09:35 +0100
committertytan6522022-02-17 22:09:35 +0100
commit9f3405a88a364da632096f46aa93ac6e044bbdd6 (patch)
tree4cc5a6b226db0c503a2dea0a967a7f19b5515def
parenta661803ec22c24f3177b03334576fb1ea1cb719f (diff)
downloadaur-9f3405a88a364da632096f46aa93ac6e044bbdd6.tar.gz
build: Update to version 5.0
-rw-r--r--.SRCINFO14
-rw-r--r--PKGBUILD155
-rw-r--r--add-av_stream_get_first_dts-for-chromium.patch44
-rw-r--r--ffmpeg-vmaf2.x.patch955
-rw-r--r--rist.patch77
5 files changed, 1191 insertions, 54 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 886bf5f1772e..3041b8b9ad3d 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = ffmpeg-obs
pkgdesc = Complete solution to record, convert and stream audio and video with fixes for OBS Studio. And various options in the PKGBUILD
- pkgver = 4.4.1
- pkgrel = 7
+ pkgver = 5.0
+ pkgrel = 1
url = https://ffmpeg.org/
arch = i686
arch = x86_64
@@ -84,11 +84,15 @@ pkgbase = ffmpeg-obs
provides = libswresample.so
provides = libswscale.so
conflicts = ffmpeg
- source = ffmpeg::git+https://git.ffmpeg.org/ffmpeg.git#tag=7e0d640edf6c3eee1816b105c2f7498c4f948e74
+ source = ffmpeg::git+https://git.ffmpeg.org/ffmpeg.git#tag=390d6853d0ef408007feb39c0040682c81c02751
source = obs-deps::git+https://github.com/obsproject/obs-deps.git#tag=15072cd42722d87c6b3ed1636b22e98c08575f20
- source = vmaf-model-path.patch
+ source = ffmpeg-vmaf2.x.patch
+ source = add-av_stream_get_first_dts-for-chromium.patch
+ source = rist.patch
sha256sums = SKIP
sha256sums = SKIP
- sha256sums = 8dff51f84a5f7460f8893f0514812f5d2bd668c3276ef7ab7713c99b71d7bd8d
+ sha256sums = 42bd572442a4eba4e9559953c72f9e980b78286ab288ac01e659be39d8bc7298
+ sha256sums = 91973c465f01446a999f278f0c2a3763304994dba1ac35de0e4c72f12f39409e
+ sha256sums = 5d6a9f381c528fa2b46c88cf138dfe11150ee1708e2126ec9677796305c3bc7e
pkgname = ffmpeg-obs
diff --git a/PKGBUILD b/PKGBUILD
index 9d98b7f5614e..6f10e23c54d5 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -59,8 +59,15 @@ fi
DISTRIB_ID=`lsb_release --id | cut -f2 -d$'\t'`
pkgname=ffmpeg-obs
-pkgver=4.4.1
-pkgrel=7
+# Manjaro still on 4.4.1
+# Don't touch them, they will be removed once Manjaro provide FFmpeg 5
+if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ pkgver=4.4.1
+ pkgrel=7
+else
+ pkgver=5.0
+ pkgrel=1
+fi
pkgdesc='Complete solution to record, convert and stream audio and video with fixes for OBS Studio. And various options in the PKGBUILD'
arch=('i686' 'x86_64' 'aarch64')
url=https://ffmpeg.org/
@@ -68,9 +75,10 @@ license=(GPL3)
_aomver=3
_libristver=0.2.6
_libvpxsover=7
-_libx264ver=163
+_libx264ver=164
_libx265ver=199
_srtver=1.4.3
+_vmafver=2
depends=(
alsa-lib
bzip2
@@ -165,25 +173,36 @@ provides=(
libswscale.so
)
conflicts=(ffmpeg)
-_tag=7e0d640edf6c3eee1816b105c2f7498c4f948e74
+# Manjaro still on 4.4.1
+if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ _tag=7e0d640edf6c3eee1816b105c2f7498c4f948e74
+else
+ _tag=390d6853d0ef408007feb39c0040682c81c02751
+fi
_deps_tag=15072cd42722d87c6b3ed1636b22e98c08575f20
source=(
"ffmpeg::git+https://git.ffmpeg.org/ffmpeg.git#tag=${_tag}"
"obs-deps::git+https://github.com/obsproject/obs-deps.git#tag=${_deps_tag}"
- "vmaf-model-path.patch"
+ "ffmpeg-vmaf2.x.patch"
+ "add-av_stream_get_first_dts-for-chromium.patch"
+ "rist.patch"
)
sha256sums=(
'SKIP'
'SKIP'
- '8dff51f84a5f7460f8893f0514812f5d2bd668c3276ef7ab7713c99b71d7bd8d'
+ '42bd572442a4eba4e9559953c72f9e980b78286ab288ac01e659be39d8bc7298'
+ '91973c465f01446a999f278f0c2a3763304994dba1ac35de0e4c72f12f39409e'
+ '5d6a9f381c528fa2b46c88cf138dfe11150ee1708e2126ec9677796305c3bc7e'
)
if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
source+=(
+ "vmaf-model-path.patch"
"$pkgname.hook"
"$pkgname.sh"
)
sha256sums+=(
+ '8dff51f84a5f7460f8893f0514812f5d2bd668c3276ef7ab7713c99b71d7bd8d'
"39820f085e4366cfa24b7bf632d331d3bfa6e9f62f47df55892901218636a2fc"
"195ad5f134f02666d330342d04561c12a10e0522b3ace80cd36531d4092e1e4d"
)
@@ -293,7 +312,11 @@ fi
## Add upstream feature for i686 and x86_64 build
if [[ $CARCH == "i686" || $CARCH == "x86_64" ]]; then
_args+=(--enable-libvmaf)
- depends+=(vmaf)
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ depends+=(vmaf)
+ else
+ depends+=("vmaf>=$_vmafver")
+ fi
fi
## Add args for aarch64 build
@@ -339,14 +362,20 @@ fi
if [[ $FFMPEG_OBS_NDI == 'ON' ]]; then
_nonfree_enabled=ON
depends+=('ndi-sdk')
+ # Manjaro still on 4.4.1
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ source+=("Revert-lavd-Remove-libndi_newtek.patch::https://framagit.org/tytan652/ffmpeg-ndi-patch/-/raw/master/master_Revert-lavd-Remove-libndi_newtek.patch?inline=false")
+ sha256sums+=('5a37c295d01ae02f02b366c3ba4867d27bc3ac29c1420472fbe8e4d7bca3bd4c')
+ else
+ source+=("Revert-lavd-Remove-libndi_newtek.patch::https://framagit.org/tytan652/ffmpeg-ndi-patch/-/raw/master/4.4_Revert-lavd-Remove-libndi_newtek.patch?inline=false")
+ sha256sums+=('a5701faa71ac69c94dc0230b401203d135e48c45980926432f1190ef3218009b')
+ fi
source+=(
- "Revert-lavd-Remove-libndi_newtek.patch::https://framagit.org/tytan652/ffmpeg-ndi-patch/-/raw/master/Revert-lavd-Remove-libndi_newtek.patch?inline=false"
"libndi_newtek_common.h::https://framagit.org/tytan652/ffmpeg-ndi-patch/-/raw/master/libavdevice/libndi_newtek_common.h?inline=false"
"libndi_newtek_dec.c::https://framagit.org/tytan652/ffmpeg-ndi-patch/-/raw/master/libavdevice/libndi_newtek_dec.c?inline=false"
"libndi_newtek_enc.c::https://framagit.org/tytan652/ffmpeg-ndi-patch/-/raw/master/libavdevice/libndi_newtek_enc.c?inline=false"
)
sha256sums+=(
- '5a37c295d01ae02f02b366c3ba4867d27bc3ac29c1420472fbe8e4d7bca3bd4c'
'462e984a7cb3d0af17b0ea0eb2a010aee2f79a3e77c2055fdfd760163dd75fa4'
'3c6dea7583d79911e9ea198c35b1b56830b85eea84e49d63c2d5c03af5210eca'
'83cc714edc8d1c37ffabd2ee17960d6ed91a1d019bd43d01383f84eea28e4fbb'
@@ -357,17 +386,23 @@ fi
if [[ $FFMPEG_OBS_SVT == 'ON' ]]; then
depends+=(svt-vp9 svt-hevc)
- _svt_hevc_ver='33ca9aa8a2a2d28022d3fc03704e99ce01828376'
- _svt_vp9_ver='abd5c59c06d686eae57ef4e6f899c601f791d055'
+ _svt_hevc_ver='111eef187fd7b91ad27573421c7238ef787e164f'
+ _svt_vp9_ver='308ef4464568a824f1f84c4491cb08ab4f535f6c'
+ # Manjaro still on 4.4.1
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ source+=("019-ffmpeg-add-svt-hevc-g${_svt_hevc_ver:0:7}.patch"::"https://raw.githubusercontent.com/OpenVisualCloud/SVT-HEVC/${_svt_hevc_ver}/ffmpeg_plugin/n4.4-0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch")
+ sha256sums+=('e737f8f0104cabb7f81755af8684bf68d03e0d262edfeae7cf65c0a32923ee96')
+ else
+ source+=("020-ffmpeg-add-svt-hevc-g${_svt_hevc_ver:0:7}.patch"::"https://raw.githubusercontent.com/OpenVisualCloud/SVT-HEVC/${_svt_hevc_ver}/ffmpeg_plugin/master-0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch")
+ sha256sums+=('efd01f96c1f48ea599881dfc836d20ba18c758a3588d616115546912aebeb77f')
+ fi
source+=(
- "020-ffmpeg-add-svt-hevc-g${_svt_hevc_ver:0:7}.patch"::"https://raw.githubusercontent.com/OpenVisualCloud/SVT-HEVC/${_svt_hevc_ver}/ffmpeg_plugin/0001-lavc-svt_hevc-add-libsvt-hevc-encoder-wrapper.patch"
"030-ffmpeg-add-svt-hevc-docs-g${_svt_hevc_ver:0:7}.patch"::"https://raw.githubusercontent.com/OpenVisualCloud/SVT-HEVC/${_svt_hevc_ver}/ffmpeg_plugin/0002-doc-Add-libsvt_hevc-encoder-docs.patch"
"040-ffmpeg-add-svt-vp9-g${_svt_vp9_ver:0:7}.patch"::"https://raw.githubusercontent.com/OpenVisualCloud/SVT-VP9/${_svt_vp9_ver}/ffmpeg_plugin/master-0001-Add-ability-for-ffmpeg-to-run-svt-vp9.patch"
)
sha256sums+=(
- '740dc9838aa47daa9f9b107178e53e384344f4c6f90865bd7e3af189257da544'
- '1499e419dda72b1604dc5e3959668f3843292ff56bfba78734e31510ba576de0'
- 'b7d722dfce20b73e9d5c73d55ffe041bbdc92a3c4a5c5d766b6b3040671b4052'
+ '837cac5a64234f34d136d18c8f7dc14203cdea11406fdb310cef2f62504d9e0c'
+ '9565b3eed177ce5d109876f2a56f3781a2c7fae41e32601bf6ec805ea199d21b'
)
_args+=(--enable-libsvtvp9 --enable-libsvthevc)
provides+=(ffmpeg-svt-av1)
@@ -436,31 +471,43 @@ prepare() {
### Arch Linux changes
- ## Change default vmaf model path to Arch defaults
- patch -Np1 -i "${srcdir}"/vmaf-model-path.patch
+ # Manjaro still on 4.4.1
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ ## Change default vmaf model path to Arch defaults
+ patch -Np1 -i "${srcdir}"/vmaf-model-path.patch
+ else
+ ## https://crbug.com/1251779
+ patch -Np1 -i "${srcdir}"/add-av_stream_get_first_dts-for-chromium.patch
+ ## Add libvmaf 2 compatibility based on
+ # avfilter/vf_libvmaf: update filter for libvmaf v2.0.0
+ # https://github.com/FFmpeg/FFmpeg/commit/3d29724c008d8f27fecf85757152789b074e8ef9
+ patch -Np1 -i "${srcdir}"/ffmpeg-vmaf2.x.patch
+ fi
### OBS changes
- ## To fix crashes when VAAPI settings are wrong
- # avcodec/vaapi_encode: Fix segfault upon closing uninitialized encoder
- # https://github.com/FFmpeg/FFmpeg/commit/d1b47f3bfcc625ca1cae210fc198dcbd54381a88
- git cherry-pick -n d1b47f3bfcc625ca1cae210fc198dcbd54381a88
-
- ## Cherry-pick for FFmpeg from obs FFmpeg repository
- ## This was hastly made before 27.2 beta 4 release
- # avcodec/libsvtav1: properly enforce CQP mode when set in wrapper
- # https://github.com/FFmpeg/FFmpeg/commit/c5f314309067dc85c165b975f53975c38e196258
- git cherry-pick -n c5f314309067dc85c165b975f53975c38e196258
- # avcodec/libsvtav1: Fix value range for rc mode
- # https://github.com/FFmpeg/FFmpeg/commit/0463f5d6d56db6cc01bc88a0d77488f4ef23dfdc
- git cherry-pick -n 0463f5d6d56db6cc01bc88a0d77488f4ef23dfdc
- # avcodec/libsvtav1: make coded GOP type configurable
- # https://github.com/FFmpeg/FFmpeg/commit/64e2fb3f9d89e5ad552f48e2d5beb9be7a91572a
- git cherry-pick -n 64e2fb3f9d89e5ad552f48e2d5beb9be7a91572a
- # avcodec/libsvtav1: Fix duplicate definition of caps_internal
- # https://github.com/FFmpeg/FFmpeg/commit/04b89e8ae33ba74e5cb5b3b770613fa599f9cb36
- git cherry-pick -n 04b89e8ae33ba74e5cb5b3b770613fa599f9cb36
-
+ # Manjaro still on 4.4.1
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ ## To fix crashes when VAAPI settings are wrong
+ # avcodec/vaapi_encode: Fix segfault upon closing uninitialized encoder
+ # https://github.com/FFmpeg/FFmpeg/commit/d1b47f3bfcc625ca1cae210fc198dcbd54381a88
+ git cherry-pick -n d1b47f3bfcc625ca1cae210fc198dcbd54381a88
+
+ ## Cherry-pick for FFmpeg from obs FFmpeg repository
+ ## This was hastly made before 27.2 beta 4 release
+ # avcodec/libsvtav1: properly enforce CQP mode when set in wrapper
+ # https://github.com/FFmpeg/FFmpeg/commit/c5f314309067dc85c165b975f53975c38e196258
+ git cherry-pick -n c5f314309067dc85c165b975f53975c38e196258
+ # avcodec/libsvtav1: Fix value range for rc mode
+ # https://github.com/FFmpeg/FFmpeg/commit/0463f5d6d56db6cc01bc88a0d77488f4ef23dfdc
+ git cherry-pick -n 0463f5d6d56db6cc01bc88a0d77488f4ef23dfdc
+ # avcodec/libsvtav1: make coded GOP type configurable
+ # https://github.com/FFmpeg/FFmpeg/commit/64e2fb3f9d89e5ad552f48e2d5beb9be7a91572a
+ git cherry-pick -n 64e2fb3f9d89e5ad552f48e2d5beb9be7a91572a
+ # avcodec/libsvtav1: Fix duplicate definition of caps_internal
+ # https://github.com/FFmpeg/FFmpeg/commit/04b89e8ae33ba74e5cb5b3b770613fa599f9cb36
+ git cherry-pick -n 04b89e8ae33ba74e5cb5b3b770613fa599f9cb36
+ fi
## Patch for FFmpeg from obs-deps repository
# This patch applies:
@@ -472,17 +519,22 @@ prepare() {
# https://patchwork.ffmpeg.org/project/ffmpeg/patch/20210913021204.22138-1-lq@chinaffmpeg.org/
patch -Np1 -i "${srcdir}"/obs-deps/CI/patches/FFmpeg-9010.patch
- ## This patch applies:
- # - avformat/librist: replace deprecated functions
- # https://github.com/FFmpeg/FFmpeg/commit/5274f2f7f8c5e40d18b84055fbb232752bd24f2f
- # - avformat/librist: correctly initialize logging_settings
- # https://github.com/FFmpeg/FFmpeg/commit/9b15f43cf8c7976fba115da686a990377f7b5ab9
- # - libRIST: allow setting fifo size and fail on overflow
- # https://patchwork.ffmpeg.org/project/ffmpeg/patch/20211117141929.1164334-2-gijs@peskens.net/
- patch -Np1 -i "${srcdir}"/obs-deps/CI/patches/FFmpeg-4.4.1-librist.patch
-
- ## This patch applies a fix on AOM encoder
- patch -Np1 -i "${srcdir}"/obs-deps/CI/patches/FFmpeg-4.4.1-libaomenc.patch
+ # Manjaro still on 4.4.1 and librist patch need to be replaced
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ ## This patch applies:
+ # - avformat/librist: replace deprecated functions
+ # https://github.com/FFmpeg/FFmpeg/commit/5274f2f7f8c5e40d18b84055fbb232752bd24f2f
+ # - avformat/librist: correctly initialize logging_settings
+ # https://github.com/FFmpeg/FFmpeg/commit/9b15f43cf8c7976fba115da686a990377f7b5ab9
+ # - libRIST: allow setting fifo size and fail on overflow
+ # https://patchwork.ffmpeg.org/project/ffmpeg/patch/20211117141929.1164334-2-gijs@peskens.net/
+ patch -Np1 -i "${srcdir}"/obs-deps/CI/patches/FFmpeg-4.4.1-librist.patch
+
+ ## This patch applies a fix on AOM encoder
+ patch -Np1 -i "${srcdir}"/obs-deps/CI/patches/FFmpeg-4.4.1-libaomenc.patch
+ else
+ patch -Np1 -i "${srcdir}"/rist.patch
+ fi
# Fix "error: unknown type name ‘bool’" made by the patch because stdbool.h is only added through librist from version 0.2.7
sed -i '49 a #if FF_LIBRIST_VERSION < FF_LIBRIST_VERSION_42\n#include <stdbool.h>\n#endif' libavformat/librist.c
@@ -502,7 +554,12 @@ prepare() {
if [[ $FFMPEG_OBS_SVT == 'ON' ]]; then
rm -f "libavcodec/"libsvt_{hevc,vp9}.c
sed -E -n 's/general.texi/general_contents.texi/g' "${srcdir}/030-ffmpeg-add-svt-hevc-docs-g${_svt_hevc_ver:0:7}.patch" > "030-ffmpeg-add-svt-hevc-docs-g${_svt_hevc_ver:0:7}.patch"
- patch -Np1 -i "${srcdir}/020-ffmpeg-add-svt-hevc-g${_svt_hevc_ver:0:7}.patch"
+ # Manjaro still on 4.4.1
+ if [[ $DISTRIB_ID == 'ManjaroLinux' ]]; then
+ patch -Np1 -i "${srcdir}/019-ffmpeg-add-svt-hevc-g${_svt_hevc_ver:0:7}.patch"
+ else
+ patch -Np1 -i "${srcdir}/020-ffmpeg-add-svt-hevc-g${_svt_hevc_ver:0:7}.patch"
+ fi
patch -Np1 -i "030-ffmpeg-add-svt-hevc-docs-g${_svt_hevc_ver:0:7}.patch"
patch -Np1 -i "${srcdir}/040-ffmpeg-add-svt-vp9-g${_svt_vp9_ver:0:7}.patch"
fi
diff --git a/add-av_stream_get_first_dts-for-chromium.patch b/add-av_stream_get_first_dts-for-chromium.patch
new file mode 100644
index 000000000000..fb5589800d3d
--- /dev/null
+++ b/add-av_stream_get_first_dts-for-chromium.patch
@@ -0,0 +1,44 @@
+From 95aab0fd83619408995720ce53d7a74790580220 Mon Sep 17 00:00:00 2001
+From: "liberato@chromium.org" <liberato@chromium.org>
+Date: Wed, 7 Jul 2021 19:01:22 -0700
+Subject: [PATCH] Add av_stream_get_first_dts for Chromium
+
+[foutrelis: adjust for new FFStream struct replacing AVStreamInternal]
+---
+ libavformat/avformat.h | 4 ++++
+ libavformat/utils.c | 7 +++++++
+ 2 files changed, 11 insertions(+)
+
+diff --git a/libavformat/avformat.h b/libavformat/avformat.h
+index cd7b0d941c..b4a6dce885 100644
+--- a/libavformat/avformat.h
++++ b/libavformat/avformat.h
+@@ -1010,6 +1010,10 @@ struct AVCodecParserContext *av_stream_get_parser(const AVStream *s);
+ */
+ int64_t av_stream_get_end_pts(const AVStream *st);
+
++// Chromium: We use the internal field first_dts vvv
++int64_t av_stream_get_first_dts(const AVStream *st);
++// Chromium: We use the internal field first_dts ^^^
++
+ #define AV_PROGRAM_RUNNING 1
+
+ /**
+diff --git a/libavformat/utils.c b/libavformat/utils.c
+index de7580c32d..0ef0fe530e 100644
+--- a/libavformat/utils.c
++++ b/libavformat/utils.c
+@@ -121,6 +121,13 @@ int64_t av_stream_get_end_pts(const AVStream *st)
+ return AV_NOPTS_VALUE;
+ }
+
++// Chromium: We use the internal field first_dts vvv
++int64_t av_stream_get_first_dts(const AVStream *st)
++{
++ return cffstream(st)->first_dts;
++}
++// Chromium: We use the internal field first_dts ^^^
++
+ struct AVCodecParserContext *av_stream_get_parser(const AVStream *st)
+ {
+ return st->internal->parser;
diff --git a/ffmpeg-vmaf2.x.patch b/ffmpeg-vmaf2.x.patch
new file mode 100644
index 000000000000..52ed69fffc4e
--- /dev/null
+++ b/ffmpeg-vmaf2.x.patch
@@ -0,0 +1,955 @@
+diff --git a/configure b/configure
+index 94f513288a..493493b4c5 100755
+--- a/configure
++++ b/configure
+@@ -3751,7 +3751,7 @@ vaguedenoiser_filter_deps="gpl"
+ vflip_vulkan_filter_deps="vulkan spirv_compiler"
+ vidstabdetect_filter_deps="libvidstab"
+ vidstabtransform_filter_deps="libvidstab"
+-libvmaf_filter_deps="libvmaf pthreads"
++libvmaf_filter_deps="libvmaf"
+ zmq_filter_deps="libzmq"
+ zoompan_filter_deps="swscale"
+ zscale_filter_deps="libzimg const_nan"
+@@ -6626,7 +6626,7 @@ enabled libtwolame && require libtwolame twolame.h twolame_init -ltwolame
+ enabled libuavs3d && require_pkg_config libuavs3d "uavs3d >= 1.1.41" uavs3d.h uavs3d_decode
+ enabled libv4l2 && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl
+ enabled libvidstab && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit
+-enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 1.5.2" libvmaf.h compute_vmaf
++enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 2.0.0" libvmaf.h vmaf_init
+ enabled libvo_amrwbenc && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc
+ enabled libvorbis && require_pkg_config libvorbis vorbis vorbis/codec.h vorbis_info_init &&
+ require_pkg_config libvorbisenc vorbisenc vorbis/vorbisenc.h vorbis_encode_init
+diff --git a/doc/filters.texi b/doc/filters.texi
+index 248c09caf8..9a890d1555 100644
+--- a/doc/filters.texi
++++ b/doc/filters.texi
+@@ -14666,68 +14666,60 @@ ffmpeg -i input.mov -vf lensfun=make=Canon:model="Canon EOS 100D":lens_model="Ca
+
+ @section libvmaf
+
+-Obtain the VMAF (Video Multi-Method Assessment Fusion)
+-score between two input videos.
++Calulate the VMAF (Video Multi-Method Assessment Fusion) score for a
++reference/distorted pair of input videos.
+
+-The first input is the encoded video, and the second input is the reference video.
++The first input is the distorted video, and the second input is the reference video.
+
+ The obtained VMAF score is printed through the logging system.
+
+ It requires Netflix's vmaf library (libvmaf) as a pre-requisite.
+ After installing the library it can be enabled using:
+ @code{./configure --enable-libvmaf}.
+-If no model path is specified it uses the default model: @code{vmaf_v0.6.1.pkl}.
+
+ The filter has following options:
+
+ @table @option
+-@item model_path
+-Set the model path which is to be used for SVM.
+-Default value: @code{"/usr/local/share/model/vmaf_v0.6.1.pkl"}
+-
+-@item log_path
+-Set the file path to be used to store logs.
++@item model
++A `|` delimited list of vmaf models. Each model can be configured with a number of parameters.
++Default value: @code{"version=vmaf_v0.6.1"}
+
+-@item log_fmt
+-Set the format of the log file (csv, json or xml).
++@item model_path
++Deprecated, use model='path=...'.
+
+ @item enable_transform
+-This option can enable/disable the @code{score_transform} applied to the final predicted VMAF score,
+-if you have specified score_transform option in the input parameter file passed to @code{run_vmaf_training.py}
+-Default value: @code{false}
++Deprecated, use model='enable_transform=true'.
+
+ @item phone_model
+-Invokes the phone model which will generate VMAF scores higher than in the
+-regular model, which is more suitable for laptop, TV, etc. viewing conditions.
+-Default value: @code{false}
++Deprecated, use model='enable_transform=true'.
++
++@item enable_conf_interval
++Deprecated, use model='enable_conf_interval=true'.
++
++@item feature
++A `|` delimited list of features. Each feature can be configured with a number of parameters.
+
+ @item psnr
+-Enables computing psnr along with vmaf.
+-Default value: @code{false}
++Deprecated, use feature='name=psnr'.
+
+ @item ssim
+-Enables computing ssim along with vmaf.
+-Default value: @code{false}
++Deprecated, use feature='name=ssim'.
+
+ @item ms_ssim
+-Enables computing ms_ssim along with vmaf.
+-Default value: @code{false}
++Deprecated, use feature='name=ms_ssim'.
+
+-@item pool
+-Set the pool method to be used for computing vmaf.
+-Options are @code{min}, @code{harmonic_mean} or @code{mean} (default).
++@item log_path
++Set the file path to be used to store log files.
++
++@item log_fmt
++Set the format of the log file (xml, json, csv, or sub).
+
+ @item n_threads
+-Set number of threads to be used when computing vmaf.
+-Default value: @code{0}, which makes use of all available logical processors.
++Set number of threads to be used when initializing libvmaf.
++Default value: @code{0}, no threads.
+
+ @item n_subsample
+-Set interval for frame subsampling used when computing vmaf.
+-Default value: @code{1}
+-
+-@item enable_conf_interval
+-Enables confidence interval.
+-Default value: @code{false}
++Set frame subsampling interval to be used.
+ @end table
+
+ This filter also supports the @ref{framesync} options.
+@@ -14735,23 +14727,31 @@ This filter also supports the @ref{framesync} options.
+ @subsection Examples
+ @itemize
+ @item
+-On the below examples the input file @file{main.mpg} being processed is
+-compared with the reference file @file{ref.mpg}.
++In the examples below, a distorted video @file{distorted.mpg} is
++compared with a reference file @file{reference.mpg}.
+
++@item
++Basic usage:
++@example
++ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf=log_path=output.xml -f null -
++@end example
++
++@item
++Example with multiple models:
+ @example
+-ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf -f null -
++ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf='model=version=vmaf_v0.6.1\\:name=vmaf|version=vmaf_v0.6.1neg\\:name=vmaf_neg' -f null -
+ @end example
+
+ @item
+-Example with options:
++Example with multiple addtional features:
+ @example
+-ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf="psnr=1:log_fmt=json" -f null -
++ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf='feature=name=psnr|name=ciede' -f null -
+ @end example
+
+ @item
+ Example with options and different containers:
+ @example
+-ffmpeg -i main.mpg -i ref.mkv -lavfi "[0:v]settb=AVTB,setpts=PTS-STARTPTS[main];[1:v]settb=AVTB,setpts=PTS-STARTPTS[ref];[main][ref]libvmaf=psnr=1:log_fmt=json" -f null -
++ffmpeg -i distorted.mpg -i reference.mkv -lavfi "[0:v]settb=AVTB,setpts=PTS-STARTPTS[main];[1:v]settb=AVTB,setpts=PTS-STARTPTS[ref];[main][ref]libvmaf=log_fmt=json:log_path=output.json" -f null -
+ @end example
+ @end itemize
+
+diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c
+index 5d492126eb..eee1c280ef 100644
+--- a/libavfilter/vf_libvmaf.c
++++ b/libavfilter/vf_libvmaf.c
+@@ -24,8 +24,8 @@
+ * Calculate the VMAF between two input videos.
+ */
+
+-#include <pthread.h>
+ #include <libvmaf.h>
++
+ #include "libavutil/avstring.h"
+ #include "libavutil/opt.h"
+ #include "libavutil/pixdesc.h"
+@@ -39,23 +39,9 @@
+ typedef struct LIBVMAFContext {
+ const AVClass *class;
+ FFFrameSync fs;
+- const AVPixFmtDescriptor *desc;
+- int width;
+- int height;
+- double vmaf_score;
+- int vmaf_thread_created;
+- pthread_t vmaf_thread;
+- pthread_mutex_t lock;
+- pthread_cond_t cond;
+- int eof;
+- AVFrame *gmain;
+- AVFrame *gref;
+- int frame_set;
+ char *model_path;
+ char *log_path;
+ char *log_fmt;
+- int disable_clip;
+- int disable_avx;
+ int enable_transform;
+ int phone_model;
+ int psnr;
+@@ -65,184 +51,487 @@ typedef struct LIBVMAFContext {
+ int n_threads;
+ int n_subsample;
+ int enable_conf_interval;
+- int error;
++ char *model_cfg;
++ char *feature_cfg;
++ VmafContext *vmaf;
++ VmafModel **model;
++ unsigned model_cnt;
++ unsigned frame_cnt;
++ unsigned bpc;
+ } LIBVMAFContext;
+
+ #define OFFSET(x) offsetof(LIBVMAFContext, x)
+ #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+ static const AVOption libvmaf_options[] = {
+- {"model_path", "Set the model to be used for computing vmaf.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/local/share/model/vmaf_v0.6.1.pkl"}, 0, 1, FLAGS},
+- {"log_path", "Set the file path to be used to store logs.", OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+- {"log_fmt", "Set the format of the log (csv, json or xml).", OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+- {"enable_transform", "Enables transform for computing vmaf.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+- {"phone_model", "Invokes the phone model that will generate higher VMAF scores.", OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+- {"psnr", "Enables computing psnr along with vmaf.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+- {"ssim", "Enables computing ssim along with vmaf.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
+- {"ms_ssim", "Enables computing ms-ssim along with vmaf.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
++ {"model_path", "use model='path=...'.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
++ {"log_path", "Set the file path to be used to write log.", OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
++ {"log_fmt", "Set the format of the log (csv, json, xml, or sub).", OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str="xml"}, 0, 1, FLAGS},
++ {"enable_transform", "use model='enable_transform=true'.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
++ {"phone_model", "use model='enable_transform=true'.", OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
++ {"psnr", "use feature='name=psnr'.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
++ {"ssim", "use feature='name=ssim'.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
++ {"ms_ssim", "use feature='name=ms_ssim'.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
+ {"pool", "Set the pool method to be used for computing vmaf.", OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+ {"n_threads", "Set number of threads to be used when computing vmaf.", OFFSET(n_threads), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT_MAX, FLAGS},
+ {"n_subsample", "Set interval for frame subsampling used when computing vmaf.", OFFSET(n_subsample), AV_OPT_TYPE_INT, {.i64=1}, 1, UINT_MAX, FLAGS},
+- {"enable_conf_interval", "Enables confidence interval.", OFFSET(enable_conf_interval), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
++ {"enable_conf_interval", "model='enable_conf_interval=true'.", OFFSET(enable_conf_interval), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
++ {"model", "Set the model to be used for computing vmaf.", OFFSET(model_cfg), AV_OPT_TYPE_STRING, {.str="version=vmaf_v0.6.1"}, 0, 1, FLAGS},
++ {"feature", "Set the feature to be used for computing vmaf.", OFFSET(feature_cfg), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
+ { NULL }
+ };
+
+ FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs);
+
+-#define read_frame_fn(type, bits) \
+- static int read_frame_##bits##bit(float *ref_data, float *main_data, \
+- float *temp_data, int stride, void *ctx) \
+-{ \
+- LIBVMAFContext *s = (LIBVMAFContext *) ctx; \
+- int ret; \
+- \
+- pthread_mutex_lock(&s->lock); \
+- \
+- while (!s->frame_set && !s->eof) { \
+- pthread_cond_wait(&s->cond, &s->lock); \
+- } \
+- \
+- if (s->frame_set) { \
+- int ref_stride = s->gref->linesize[0]; \
+- int main_stride = s->gmain->linesize[0]; \
+- \
+- const type *ref_ptr = (const type *) s->gref->data[0]; \
+- const type *main_ptr = (const type *) s->gmain->data[0]; \
+- \
+- float *ptr = ref_data; \
+- float factor = 1.f / (1 << (bits - 8)); \
+- \
+- int h = s->height; \
+- int w = s->width; \
+- \
+- int i,j; \
+- \
+- for (i = 0; i < h; i++) { \
+- for ( j = 0; j < w; j++) { \
+- ptr[j] = ref_ptr[j] * factor; \
+- } \
+- ref_ptr += ref_stride / sizeof(*ref_ptr); \
+- ptr += stride / sizeof(*ptr); \
+- } \
+- \
+- ptr = main_data; \
+- \
+- for (i = 0; i < h; i++) { \
+- for (j = 0; j < w; j++) { \
+- ptr[j] = main_ptr[j] * factor; \
+- } \
+- main_ptr += main_stride / sizeof(*main_ptr); \
+- ptr += stride / sizeof(*ptr); \
+- } \
+- } \
+- \
+- ret = !s->frame_set; \
+- \
+- av_frame_unref(s->gref); \
+- av_frame_unref(s->gmain); \
+- s->frame_set = 0; \
+- \
+- pthread_cond_signal(&s->cond); \
+- pthread_mutex_unlock(&s->lock); \
+- \
+- if (ret) { \
+- return 2; \
+- } \
+- \
+- return 0; \
++static enum VmafPixelFormat pix_fmt_map(enum AVPixelFormat av_pix_fmt)
++{
++ switch (av_pix_fmt) {
++ case AV_PIX_FMT_YUV420P:
++ case AV_PIX_FMT_YUV420P10LE:
++ case AV_PIX_FMT_YUV420P12LE:
++ case AV_PIX_FMT_YUV420P16LE:
++ return VMAF_PIX_FMT_YUV420P;
++ case AV_PIX_FMT_YUV422P:
++ case AV_PIX_FMT_YUV422P10LE:
++ case AV_PIX_FMT_YUV422P12LE:
++ case AV_PIX_FMT_YUV422P16LE:
++ return VMAF_PIX_FMT_YUV422P;
++ case AV_PIX_FMT_YUV444P:
++ case AV_PIX_FMT_YUV444P10LE:
++ case AV_PIX_FMT_YUV444P12LE:
++ case AV_PIX_FMT_YUV444P16LE:
++ return VMAF_PIX_FMT_YUV444P;
++ default:
++ return VMAF_PIX_FMT_UNKNOWN;
++ }
+ }
+
+-read_frame_fn(uint8_t, 8);
+-read_frame_fn(uint16_t, 10);
++static int copy_picture_data(AVFrame *src, VmafPicture *dst, unsigned bpc)
++{
++ int err = vmaf_picture_alloc(dst, pix_fmt_map(src->format), bpc,
++ src->width, src->height);
++ if (err)
++ return AVERROR(ENOMEM);
++
++ for (unsigned i = 0; i < 3; i++) {
++ uint8_t *src_data = src->data[i];
++ uint8_t *dst_data = dst->data[i];
++ for (unsigned j = 0; j < dst->h[i]; j++) {
++ memcpy(dst_data, src_data, sizeof(*dst_data) * dst->w[i]);
++ src_data += src->linesize[i];
++ dst_data += dst->stride[i];
++ }
++ }
++
++ return 0;
++}
+
+-static void compute_vmaf_score(LIBVMAFContext *s)
++static int do_vmaf(FFFrameSync *fs)
+ {
+- int (*read_frame)(float *ref_data, float *main_data, float *temp_data,
+- int stride, void *ctx);
+- char *format;
++ AVFilterContext *ctx = fs->parent;
++ LIBVMAFContext *s = ctx->priv;
++ VmafPicture pic_ref, pic_dist;
++ AVFrame *ref, *dist;
++ int err = 0;
+
+- if (s->desc->comp[0].depth <= 8) {
+- read_frame = read_frame_8bit;
+- } else {
+- read_frame = read_frame_10bit;
++ int ret = ff_framesync_dualinput_get(fs, &dist, &ref);
++ if (ret < 0)
++ return ret;
++ if (ctx->is_disabled || !ref)
++ return ff_filter_frame(ctx->outputs[0], dist);
++
++ err = copy_picture_data(ref, &pic_ref, s->bpc);
++ if (err) {
++ av_log(s, AV_LOG_ERROR, "problem during vmaf_picture_alloc.\n");
++ return AVERROR(ENOMEM);
++ }
++
++ err = copy_picture_data(dist, &pic_dist, s->bpc);
++ if (err) {
++ av_log(s, AV_LOG_ERROR, "problem during vmaf_picture_alloc.\n");
++ vmaf_picture_unref(&pic_ref);
++ return AVERROR(ENOMEM);
+ }
+
+- format = (char *) s->desc->name;
++ err = vmaf_read_pictures(s->vmaf, &pic_ref, &pic_dist, s->frame_cnt++);
++ if (err) {
++ av_log(s, AV_LOG_ERROR, "problem during vmaf_read_pictures.\n");
++ return AVERROR(EINVAL);
++ }
+
+- s->error = compute_vmaf(&s->vmaf_score, format, s->width, s->height,
+- read_frame, s, s->model_path, s->log_path,
+- s->log_fmt, 0, 0, s->enable_transform,
+- s->phone_model, s->psnr, s->ssim,
+- s->ms_ssim, s->pool,
+- s->n_threads, s->n_subsample, s->enable_conf_interval);
++ return ff_filter_frame(ctx->outputs[0], dist);
+ }
+
+-static void *call_vmaf(void *ctx)
++
++static AVDictionary **delimited_dict_parse(char *str, unsigned *cnt)
+ {
+- LIBVMAFContext *s = (LIBVMAFContext *) ctx;
+- compute_vmaf_score(s);
+- if (!s->error) {
+- av_log(ctx, AV_LOG_INFO, "VMAF score: %f\n",s->vmaf_score);
+- } else {
+- pthread_mutex_lock(&s->lock);
+- pthread_cond_signal(&s->cond);
+- pthread_mutex_unlock(&s->lock);
++ AVDictionary **dict = NULL;
++ char *str_copy = NULL;
++ char *saveptr = NULL;
++ unsigned cnt2;
++ int err = 0;
++
++ if (!str)
++ return NULL;
++
++ cnt2 = 1;
++ for (char *p = str; *p; p++) {
++ if (*p == '|')
++ cnt2++;
++ }
++
++ dict = av_calloc(cnt2, sizeof(*dict));
++ if (!dict)
++ goto fail;
++
++ str_copy = av_strdup(str);
++ if (!str_copy)
++ goto fail;
++
++ *cnt = 0;
++ for (unsigned i = 0; i < cnt2; i++) {
++ char *s = av_strtok(i == 0 ? str_copy : NULL, "|", &saveptr);
++ if (!s)
++ continue;
++ err = av_dict_parse_string(&dict[(*cnt)++], s, "=", ":", 0);
++ if (err)
++ goto fail;
++ }
++
++ av_free(str_copy);
++ return dict;
++
++fail:
++ if (dict) {
++ for (unsigned i = 0; i < *cnt; i++) {
++ if (dict[i])
++ av_dict_free(&dict[i]);
++ }
++ av_free(dict);
+ }
+- pthread_exit(NULL);
++
++ av_free(str_copy);
++ *cnt = 0;
+ return NULL;
+ }
+
+-static int do_vmaf(FFFrameSync *fs)
++static int parse_features(AVFilterContext *ctx)
+ {
+- AVFilterContext *ctx = fs->parent;
+ LIBVMAFContext *s = ctx->priv;
+- AVFrame *master, *ref;
+- int ret;
++ AVDictionary **dict = NULL;
++ unsigned dict_cnt;
++ int err = 0;
+
+- ret = ff_framesync_dualinput_get(fs, &master, &ref);
+- if (ret < 0)
+- return ret;
+- if (!ref)
+- return ff_filter_frame(ctx->outputs[0], master);
++ if (!s->feature_cfg)
++ return 0;
++
++ dict = delimited_dict_parse(s->feature_cfg, &dict_cnt);
++ if (!dict) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not parse feature config: %s\n", s->feature_cfg);
++ return AVERROR(EINVAL);
++ }
+
+- pthread_mutex_lock(&s->lock);
++ for (unsigned i = 0; i < dict_cnt; i++) {
++ char *feature_name = NULL;
++ VmafFeatureDictionary *feature_opts_dict = NULL;
++ AVDictionaryEntry *e = NULL;
++
++ while (e = av_dict_get(dict[i], "", e, AV_DICT_IGNORE_SUFFIX)) {
++ if (av_stristr(e->key, "name")) {
++ feature_name = e->value;
++ continue;
++ }
++
++ err = vmaf_feature_dictionary_set(&feature_opts_dict, e->key,
++ e->value);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not set feature option: %s.%s=%s\n",
++ feature_name, e->key, e->value);
++ goto exit;
++ }
++ }
++
++ err = vmaf_use_feature(s->vmaf, feature_name, feature_opts_dict);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem during vmaf_use_feature: %s\n", feature_name);
++ goto exit;
++ }
++ }
+
+- while (s->frame_set && !s->error) {
+- pthread_cond_wait(&s->cond, &s->lock);
++exit:
++ for (unsigned i = 0; i < dict_cnt; i++) {
++ if (dict[i])
++ av_dict_free(&dict[i]);
+ }
++ av_free(dict);
++ return err;
++}
++
++static int parse_models(AVFilterContext *ctx)
++{
++ LIBVMAFContext *s = ctx->priv;
++ AVDictionary **dict;
++ unsigned dict_cnt;
++ int err = 0;
++
++ if (!s->model_cfg) return 0;
+
+- if (s->error) {
++ dict_cnt = 0;
++ dict = delimited_dict_parse(s->model_cfg, &dict_cnt);
++ if (!dict) {
+ av_log(ctx, AV_LOG_ERROR,
+- "libvmaf encountered an error, check log for details\n");
+- pthread_mutex_unlock(&s->lock);
++ "could not parse model config: %s\n", s->model_cfg);
+ return AVERROR(EINVAL);
+ }
+
+- av_frame_ref(s->gref, ref);
+- av_frame_ref(s->gmain, master);
++ s->model_cnt = dict_cnt;
++ s->model = av_calloc(s->model_cnt, sizeof(*s->model));
++ if (!s->model)
++ return AVERROR(ENOMEM);
++
++ for (unsigned i = 0; i < dict_cnt; i++) {
++ VmafModelConfig model_cfg = { 0 };
++ AVDictionaryEntry *e = NULL;
++ char *version = NULL;
++ char *path = NULL;
++
++ while (e = av_dict_get(dict[i], "", e, AV_DICT_IGNORE_SUFFIX)) {
++ if (av_stristr(e->key, "disable_clip")) {
++ model_cfg.flags |= av_stristr(e->value, "true") ?
++ VMAF_MODEL_FLAG_DISABLE_CLIP : 0;
++ continue;
++ }
++
++ if (av_stristr(e->key, "enable_transform")) {
++ model_cfg.flags |= av_stristr(e->value, "true") ?
++ VMAF_MODEL_FLAG_ENABLE_TRANSFORM : 0;
++ continue;
++ }
++
++ if (av_stristr(e->key, "name")) {
++ model_cfg.name = e->value;
++ continue;
++ }
++
++ if (av_stristr(e->key, "version")) {
++ version = e->value;
++ continue;
++ }
++
++ if (av_stristr(e->key, "path")) {
++ path = e->value;
++ continue;
++ }
++ }
++
++ if (version) {
++ err = vmaf_model_load(&s->model[i], &model_cfg, version);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not load libvmaf model with version: %s\n",
++ version);
++ goto exit;
++ }
++ }
++
++ if (path && !s->model[i]) {
++ err = vmaf_model_load_from_path(&s->model[i], &model_cfg, path);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not load libvmaf model with path: %s\n",
++ path);
++ goto exit;
++ }
++ }
++
++ if (!s->model[i]) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not load libvmaf model with config: %s\n",
++ s->model_cfg);
++ goto exit;
++ }
++
++ while (e = av_dict_get(dict[i], "", e, AV_DICT_IGNORE_SUFFIX)) {
++ VmafFeatureDictionary *feature_opts_dict = NULL;
++ char *feature_opt = NULL;
++
++ char *feature_name = av_strtok(e->key, ".", &feature_opt);
++ if (!feature_opt)
++ continue;
++
++ err = vmaf_feature_dictionary_set(&feature_opts_dict,
++ feature_opt, e->value);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not set feature option: %s.%s=%s\n",
++ feature_name, feature_opt, e->value);
++ err = AVERROR(EINVAL);
++ goto exit;
++ }
++
++ err = vmaf_model_feature_overload(s->model[i], feature_name,
++ feature_opts_dict);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "could not overload feature: %s\n", feature_name);
++ err = AVERROR(EINVAL);
++ goto exit;
++ }
++ }
++ }
++
++ for (unsigned i = 0; i < s->model_cnt; i++) {
++ err = vmaf_use_features_from_model(s->vmaf, s->model[i]);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem during vmaf_use_features_from_model\n");
++ err = AVERROR(EINVAL);
++ goto exit;
++ }
++ }
++
++exit:
++ for (unsigned i = 0; i < dict_cnt; i++) {
++ if (dict[i])
++ av_dict_free(&dict[i]);
++ }
++ av_free(dict);
++ return err;
++}
++
++static enum VmafLogLevel log_level_map(int log_level)
++{
++ switch (log_level) {
++ case AV_LOG_QUIET:
++ return VMAF_LOG_LEVEL_NONE;
++ case AV_LOG_ERROR:
++ return VMAF_LOG_LEVEL_ERROR;
++ case AV_LOG_WARNING:
++ return VMAF_LOG_LEVEL_WARNING;
++ case AV_LOG_INFO:
++ return VMAF_LOG_LEVEL_INFO;
++ case AV_LOG_DEBUG:
++ return VMAF_LOG_LEVEL_DEBUG;
++ default:
++ return VMAF_LOG_LEVEL_INFO;
++ }
++}
++
++static int parse_deprecated_options(AVFilterContext *ctx)
++{
++ LIBVMAFContext *s = ctx->priv;
++ VmafModel *model = NULL;
++ VmafModelCollection *model_collection = NULL;
++ enum VmafModelFlags flags = VMAF_MODEL_FLAGS_DEFAULT;
++ int err = 0;
++
++ VmafModelConfig model_cfg = {
++ .name = "vmaf",
++ .flags = flags,
++ };
++
++ if (s->enable_transform || s->phone_model)
++ flags |= VMAF_MODEL_FLAG_ENABLE_TRANSFORM;
++
++ if (!s->model_path)
++ goto extra_metrics_only;
++
++ if (s->enable_conf_interval) {
++ err = vmaf_model_collection_load_from_path(&model, &model_collection,
++ &model_cfg, s->model_path);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading model file: %s\n", s->model_path);
++ goto exit;
++ }
++
++ err = vmaf_use_features_from_model_collection(s->vmaf, model_collection);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading feature extractors from model file: %s\n",
++ s->model_path);
++ goto exit;
++ }
++ } else {
++ err = vmaf_model_load_from_path(&model, &model_cfg, s->model_path);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading model file: %s\n", s->model_path);
++ goto exit;
++ }
++ err = vmaf_use_features_from_model(s->vmaf, model);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading feature extractors from model file: %s\n",
++ s->model_path);
++ goto exit;
++ }
++ }
++
++extra_metrics_only:
++ if (s->psnr) {
++ VmafFeatureDictionary *d = NULL;
++ vmaf_feature_dictionary_set(&d, "enable_chroma", "false");
++
++ err = vmaf_use_feature(s->vmaf, "psnr", d);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading feature extractor: psnr\n");
++ goto exit;
++ }
++ }
+
+- s->frame_set = 1;
++ if (s->ssim) {
++ err = vmaf_use_feature(s->vmaf, "float_ssim", NULL);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading feature extractor: ssim\n");
++ goto exit;
++ }
++ }
+
+- pthread_cond_signal(&s->cond);
+- pthread_mutex_unlock(&s->lock);
++ if (s->ms_ssim) {
++ err = vmaf_use_feature(s->vmaf, "float_ms_ssim", NULL);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem loading feature extractor: ms_ssim\n");
++ goto exit;
++ }
++ }
+
+- return ff_filter_frame(ctx->outputs[0], master);
++exit:
++ return err;
+ }
+
+ static av_cold int init(AVFilterContext *ctx)
+ {
+ LIBVMAFContext *s = ctx->priv;
++ int err = 0;
+
+- s->gref = av_frame_alloc();
+- s->gmain = av_frame_alloc();
+- if (!s->gref || !s->gmain)
+- return AVERROR(ENOMEM);
++ VmafConfiguration cfg = {
++ .log_level = log_level_map(av_log_get_level()),
++ .n_subsample = s->n_subsample,
++ .n_threads = s->n_threads,
++ };
++
++ err = vmaf_init(&s->vmaf, cfg);
++ if (err)
++ return AVERROR(EINVAL);
++
++ err = parse_deprecated_options(ctx);
++ if (err)
++ return err;
+
+- s->error = 0;
++ err = parse_models(ctx);
++ if (err)
++ return err;
+
+- s->vmaf_thread_created = 0;
+- pthread_mutex_init(&s->lock, NULL);
+- pthread_cond_init (&s->cond, NULL);
++ err = parse_features(ctx);
++ if (err)
++ return err;
+
+ s->fs.on_event = do_vmaf;
+ return 0;
+@@ -256,26 +545,31 @@ static const enum AVPixelFormat pix_fmts[] = {
+
+ static int config_input_ref(AVFilterLink *inlink)
+ {
+- AVFilterContext *ctx = inlink->dst;
++ AVFilterContext *ctx = inlink->dst;
+ LIBVMAFContext *s = ctx->priv;
+- int th;
++ const AVPixFmtDescriptor *desc;
++ int err = 0;
+
+- if (ctx->inputs[0]->w != ctx->inputs[1]->w ||
+- ctx->inputs[0]->h != ctx->inputs[1]->h) {
+- av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n");
+- return AVERROR(EINVAL);
++ if (ctx->inputs[0]->w != ctx->inputs[1]->w) {
++ av_log(ctx, AV_LOG_ERROR, "input width must match.\n");
++ err |= AVERROR(EINVAL);
+ }
+
+- s->desc = av_pix_fmt_desc_get(inlink->format);
+- s->width = ctx->inputs[0]->w;
+- s->height = ctx->inputs[0]->h;
++ if (ctx->inputs[0]->h != ctx->inputs[1]->h) {
++ av_log(ctx, AV_LOG_ERROR, "input height must match.\n");
++ err |= AVERROR(EINVAL);
++ }
+
+- th = pthread_create(&s->vmaf_thread, NULL, call_vmaf, (void *) s);
+- if (th) {
+- av_log(ctx, AV_LOG_ERROR, "Thread creation failed.\n");
+- return AVERROR(EINVAL);
++ if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
++ av_log(ctx, AV_LOG_ERROR, "input pix_fmt must match.\n");
++ err |= AVERROR(EINVAL);
+ }
+- s->vmaf_thread_created = 1;
++
++ if (err)
++ return err;
++
++ desc = av_pix_fmt_desc_get(inlink->format);
++ s->bpc = desc->comp[0].depth;
+
+ return 0;
+ }
+@@ -307,28 +601,80 @@ static int activate(AVFilterContext *ctx)
+ return ff_framesync_activate(&s->fs);
+ }
+
++static enum VmafOutputFormat log_fmt_map(const char *log_fmt)
++{
++ if (log_fmt) {
++ if (av_stristr(log_fmt, "xml"))
++ return VMAF_OUTPUT_FORMAT_XML;
++ if (av_stristr(log_fmt, "json"))
++ return VMAF_OUTPUT_FORMAT_JSON;
++ if (av_stristr(log_fmt, "csv"))
++ return VMAF_OUTPUT_FORMAT_CSV;
++ if (av_stristr(log_fmt, "sub"))
++ return VMAF_OUTPUT_FORMAT_SUB;
++ }
++
++ return VMAF_OUTPUT_FORMAT_XML;
++}
++
++static enum VmafPoolingMethod pool_method_map(const char *pool_method)
++{
++ if (pool_method) {
++ if (av_stristr(pool_method, "min"))
++ return VMAF_POOL_METHOD_MIN;
++ if (av_stristr(pool_method, "mean"))
++ return VMAF_POOL_METHOD_MEAN;
++ if (av_stristr(pool_method, "harmonic_mean"))
++ return VMAF_POOL_METHOD_HARMONIC_MEAN;
++ }
++
++ return VMAF_POOL_METHOD_MEAN;
++}
++
+ static av_cold void uninit(AVFilterContext *ctx)
+ {
+ LIBVMAFContext *s = ctx->priv;
++ int err = 0;
+
+ ff_framesync_uninit(&s->fs);
+
+- pthread_mutex_lock(&s->lock);
+- s->eof = 1;
+- pthread_cond_signal(&s->cond);
+- pthread_mutex_unlock(&s->lock);
++ if (!s->frame_cnt)
++ goto clean_up;
+
+- if (s->vmaf_thread_created)
+- {
+- pthread_join(s->vmaf_thread, NULL);
+- s->vmaf_thread_created = 0;
++ err = vmaf_read_pictures(s->vmaf, NULL, NULL, 0);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem flushing libvmaf context.\n");
+ }
+
+- av_frame_free(&s->gref);
+- av_frame_free(&s->gmain);
++ for (unsigned i = 0; i < s->model_cnt; i++) {
++ double vmaf_score;
++ err = vmaf_score_pooled(s->vmaf, s->model[i], pool_method_map(s->pool),
++ &vmaf_score, 0, s->frame_cnt - 1);
++ if (err) {
++ av_log(ctx, AV_LOG_ERROR,
++ "problem getting pooled vmaf score.\n");
++ }
++
++ av_log(ctx, AV_LOG_INFO, "VMAF score: %f\n", vmaf_score);
++ }
++
++ if (s->vmaf) {
++ if (s->log_path && !err)
++ vmaf_write_output(s->vmaf, s->log_path, log_fmt_map(s->log_fmt));
++ }
++
++clean_up:
++ if (s->model) {
++ for (unsigned i = 0; i < s->model_cnt; i++) {
++ if (s->model[i])
++ vmaf_model_destroy(s->model[i]);
++ }
++ av_free(s->model);
++ }
+
+- pthread_mutex_destroy(&s->lock);
+- pthread_cond_destroy(&s->cond);
++ if (s->vmaf)
++ vmaf_close(s->vmaf);
+ }
+
+ static const AVFilterPad libvmaf_inputs[] = {
+--
+2.20.1
+
diff --git a/rist.patch b/rist.patch
new file mode 100644
index 000000000000..3145ffbe5a59
--- /dev/null
+++ b/rist.patch
@@ -0,0 +1,77 @@
+diff --git a/libavformat/librist.c b/libavformat/librist.c
+index 378b635ea7..6f68868f3c 100644
+--- a/libavformat/librist.c
++++ b/libavformat/librist.c
+@@ -43,6 +43,9 @@
+ ((patch) + ((minor)* 0x100) + ((major) *0x10000))
+ #define FF_LIBRIST_VERSION FF_LIBRIST_MAKE_VERSION(LIBRIST_API_VERSION_MAJOR, LIBRIST_API_VERSION_MINOR, LIBRIST_API_VERSION_PATCH)
+ #define FF_LIBRIST_VERSION_41 FF_LIBRIST_MAKE_VERSION(4, 1, 0)
++#define FF_LIBRIST_VERSION_42 FF_LIBRIST_MAKE_VERSION(4, 2, 0)
++
++#define FF_LIBRIST_FIFO_DEFAULT_SHIFT 13
+
+ typedef struct RISTContext {
+ const AVClass *class;
+@@ -52,6 +55,8 @@ typedef struct RISTContext {
+ int packet_size;
+ int log_level;
+ int encryption;
++ int fifo_shift;
++ bool overrun_nonfatal;
+ char *secret;
+
+ struct rist_logging_settings logging_settings;
+@@ -70,6 +75,8 @@ static const AVOption librist_options[] = {
+ { "main", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN}, 0, 0, .flags = D|E, "profile" },
+ { "advanced", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E, "profile" },
+ { "buffer_size", "set buffer_size in ms", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64=0}, 0, 30000, .flags = D|E },
++ { "fifo_size", "Set libRIST fifo buffer size, applied as: buffer_size=2^fifo_size", OFFSET(fifo_shift), AV_OPT_TYPE_INT, {.i64=FF_LIBRIST_FIFO_DEFAULT_SHIFT}, 10, 63, .flags = D|E },
++ { "overrun_nonfatal", "survive in case of libRIST receiving circular buffer overrun", OFFSET(overrun_nonfatal), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D },
+ { "pkt_size", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64=1316}, 1, MAX_PAYLOAD_SIZE, .flags = D|E },
+ { "log_level", "set loglevel", OFFSET(log_level), AV_OPT_TYPE_INT, {.i64=RIST_LOG_INFO}, -1, INT_MAX, .flags = D|E },
+ { "secret", "set encryption secret",OFFSET(secret), AV_OPT_TYPE_STRING,{.str=NULL}, 0, 0, .flags = D|E },
+@@ -161,6 +168,19 @@ static int librist_open(URLContext *h, const char *uri, int flags)
+ if (ret < 0)
+ goto err;
+
++ //Prior to 4.2.0 there was a bug in libRIST which made this call always fail.
++#if FF_LIBRIST_VERSION >= FF_LIBRIST_VERSION_42
++ if (flags & AVIO_FLAG_READ) {
++ ret = rist_receiver_set_output_fifo_size(s->ctx, 2 << s->fifo_shift);
++ if (ret != 0)
++ goto err;
++ }
++#else
++ if (s->fifo_buffer_size != FF_LIBRIST_FIFO_DEFAULT) {
++ av_log(h, AV_LOG_ERROR, "libRIST prior to 0.2.7 has a bug which fails setting the fifo buffer size");
++ }
++#endif
++
+ if (((s->encryption == 128 || s->encryption == 256) && !s->secret) ||
+ ((peer_config->key_size == 128 || peer_config->key_size == 256) && !peer_config->secret[0])) {
+ av_log(h, AV_LOG_ERROR, "secret is mandatory if encryption is enabled\n");
+@@ -223,8 +243,24 @@ static int librist_read(URLContext *h, uint8_t *buf, int size)
+ return AVERROR_EXTERNAL;
+ }
+
++#if FF_LIBRIST_VERSION >= FF_LIBRIST_VERSION_42
++ if (data_block->flags & RIST_DATA_FLAGS_OVERFLOW == RIST_DATA_FLAGS_OVERFLOW) {
++ if (!s->overrun_nonfatal) {
++ av_log(h, AV_LOG_ERROR, "Fifo buffer overrun. "
++ "To avoid, increase fifo_size URL option. "
++ "To survive in such case, use overrun_nonfatal option\n");
++ size = AVERROR(EIO);
++ goto out_free;
++ } else {
++ av_log(h, AV_LOG_WARNING, "Fifo buffer buffer overrun. "
++ "Surviving due to overrun_nonfatal option\n");
++ }
++ }
++#endif
++
+ size = data_block->payload_len;
+ memcpy(buf, data_block->payload, size);
++out_free:
+ #if FF_LIBRIST_VERSION < FF_LIBRIST_VERSION_41
+ rist_receiver_data_block_free((struct rist_data_block**)&data_block);
+ #else