From b44db61f98e1ea43e195008e9d9276c5892614cf Mon Sep 17 00:00:00 2001 From: Martchus Date: Tue, 28 Jun 2022 00:22:05 +0200 Subject: [PATCH] Sync mingw-w64-ffmpeg with AUR package and update to 5.0.1 --- ffmpeg/mingw-w64/PKGBUILD | 205 ++-- ...av_stream_get_first_dts-for-chromium.patch | 44 + ffmpeg/mingw-w64/configure.patch | 17 + ffmpeg/mingw-w64/ffmpeg-vmaf2.x.patch | 955 ++++++++++++++++++ ffmpeg/mingw-w64/vmaf-model-path.patch | 13 + ffmpeg/mingw-w64/windres.patch | 13 + 6 files changed, 1181 insertions(+), 66 deletions(-) create mode 100644 ffmpeg/mingw-w64/add-av_stream_get_first_dts-for-chromium.patch create mode 100644 ffmpeg/mingw-w64/configure.patch create mode 100644 ffmpeg/mingw-w64/ffmpeg-vmaf2.x.patch create mode 100644 ffmpeg/mingw-w64/vmaf-model-path.patch create mode 100644 ffmpeg/mingw-w64/windres.patch diff --git a/ffmpeg/mingw-w64/PKGBUILD b/ffmpeg/mingw-w64/PKGBUILD index e45cd5c8..e2a3ea28 100644 --- a/ffmpeg/mingw-w64/PKGBUILD +++ b/ffmpeg/mingw-w64/PKGBUILD @@ -1,79 +1,148 @@ -# Maintainer: Karl-Felix Glatzer +# Maintainer : Karl-Felix Glatzer # Contributor: Martchus -# This version includes the programs and supports libfdk-aac in contrast to the -# version found in the AUR. - -_name=ffmpeg pkgname=mingw-w64-ffmpeg -pkgver=4.0 +pkgver=5.0.1 pkgrel=1 epoch=1 -pkgdesc='Complete solution to record, convert and stream audio and video (mingw-w64)' +pkgdesc="Complete solution to record, convert and stream audio and video (mingw-w64)" arch=('any') -url='https://ffmpeg.org/' -license=('GPL3' 'custom:libfdk-aac') -depends=('mingw-w64-crt' 'mingw-w64-bzip2' 'mingw-w64-fontconfig' 'mingw-w64-fribidi' 'mingw-w64-gmp' 'mingw-w64-gnutls' - 'mingw-w64-gsm' 'mingw-w64-lame' 'mingw-w64-libass' 'mingw-w64-dcadec' 'mingw-w64-libbluray' 'mingw-w64-libmodplug' - 'mingw-w64-libsoxr' 'mingw-w64-libtheora' 'mingw-w64-vid.stab' 'mingw-w64-libwebp' 'mingw-w64-libvorbis' - 'mingw-w64-libvpx' 'mingw-w64-libxml2' 'mingw-w64-opencore-amr' 'mingw-w64-openjpeg2' 'mingw-w64-opus' 'mingw-w64-libssh' - 'mingw-w64-schroedinger' 'mingw-w64-libfdk-aac' 'mingw-w64-sdl2' 'mingw-w64-speex' 'mingw-w64-x264' 'mingw-w64-xvidcore' - 'mingw-w64-zlib' 'mingw-w64-x265') +url="https://ffmpeg.org/" +license=('GPL3') +depends=( + 'mingw-w64-aom' + 'mingw-w64-crt' + 'mingw-w64-bzip2' + 'mingw-w64-fontconfig' + 'mingw-w64-fribidi' + 'mingw-w64-gmp' + 'mingw-w64-gnutls' + 'mingw-w64-gsm' + 'mingw-w64-lame' + 'mingw-w64-libass' + 'mingw-w64-libbluray' + 'mingw-w64-dav1d' + 'mingw-w64-libmodplug' + 'mingw-w64-libsoxr' + 'mingw-w64-libtheora' + 'mingw-w64-vid.stab' + 'mingw-w64-libwebp' + 'mingw-w64-libxml2' + 'mingw-w64-libvorbis' + 'mingw-w64-libvpx' + 'mingw-w64-opencore-amr' + 'mingw-w64-openjpeg2' + 'mingw-w64-opus' + 'mingw-w64-rav1e' + 'mingw-w64-librsvg' + 'mingw-w64-libssh' + 'mingw-w64-sdl2' + 'mingw-w64-speex' + 'mingw-w64-srt' + 'mingw-w64-x264' + 'mingw-w64-xvidcore' + 'mingw-w64-zimg' + 'mingw-w64-zlib' + 'mingw-w64-x265' +) +# TODO: Add vmaf dependency +#'mingw-w64-vmaf' +#'mingw-w64-svt-av1' (only 64 bit support) options=(!strip !buildflags staticlibs) -makedepends=('mingw-w64-gcc' 'mingw-w64-pkg-config' 'yasm') -source=(https://ffmpeg.org/releases/ffmpeg-${pkgver}.tar.xz{,.asc}) -validpgpkeys=('FCF986EA15E6E293A5644F10B4322F04D67658D8') # ffmpeg-devel -sha256sums=('ed945daf40b124e77a685893cc025d086f638bc703183460aff49508edb3a43f' - 'SKIP') -_architectures='i686-w64-mingw32 x86_64-w64-mingw32' +makedepends=('mingw-w64-amf-headers' 'mingw-w64-avisynthplus' 'mingw-w64-gcc' 'mingw-w64-pkg-config' 'git' 'yasm') +_tag=9687cae2b468e09e35df4cea92cc2e6a0e6c93b3 +#source=("git+https://git.ffmpeg.org/ffmpeg.git#tag=n${pkgver}" +source=(git+https://git.ffmpeg.org/ffmpeg.git#tag=${_tag} + ffmpeg-vmaf2.x.patch + add-av_stream_get_first_dts-for-chromium.patch + configure.patch) +b2sums=('SKIP' + '65039aac811bfd143359e32720cd6ca64124f1789c1b624bd28a5bd75b37362b2a3b6b402203c4e9d137fb1d00895114f3789df40f8381091d38c98e7876cc8a' + '3f2ee7606500fa9444380d138959cd2bccfbba7d34629a17f4f6288c6bde29e931bbe922a7c25d861f057ddd4ba0b095bbd675c1930754746d5dd476b3ccbc13' + '600ce3b6c87378f6d0827ba837484c859a84595f63f6ffdc8d6f5d989ebab4b661b3d15810bdd1192b983119e131fec7421f18fb0ed642b965554d2f9e5efc64') + +_architectures="i686-w64-mingw32 x86_64-w64-mingw32" + +prepare() { + cd ffmpeg + + patch -Np1 -i "${srcdir}/configure.patch" + +# TODO: Add vmaf dependency +# patch -Np1 -i "${srcdir}"/ffmpeg-vmaf2.x.patch # vmaf 2.x support + + patch -Np1 -i "${srcdir}"/add-av_stream_get_first_dts-for-chromium.patch # https://crbug.com/1251779 +} + +pkgver() { + cd ffmpeg + + git describe --tags | sed 's/^n//' +} build() { for _arch in ${_architectures}; do mkdir -p "${srcdir}"/build-${_arch} && cd "${srcdir}"/build-${_arch} - "${srcdir}"/${_name}-${pkgver}/configure \ - --prefix="/usr/${_arch}" \ - --enable-cross-compile \ - --cross-prefix="${_arch}-" \ - --target-os=mingw32 \ - --arch=${_arch%%-*} \ - --disable-debug \ - --enable-static \ - --disable-stripping \ - --enable-avisynth \ - --enable-fontconfig \ - --enable-gmp \ - --enable-gnutls \ - --enable-gpl \ - --enable-libass \ - --enable-libbluray \ - --enable-libfreetype \ - --enable-libfribidi \ - --enable-libgsm \ - --enable-libmodplug \ - --enable-libmp3lame \ - --enable-libopencore_amrnb \ - --enable-libopencore_amrwb \ - --enable-libopenjpeg \ - --enable-libopus \ - --enable-libsoxr \ - --enable-libspeex \ - --enable-libssh \ - --enable-libtheora \ - --enable-libvidstab \ - --enable-libvorbis \ - --enable-libvpx \ - --enable-libwebp \ - --enable-libxml2 \ - --enable-libx264 \ - --enable-libx265 \ - --enable-libxvid \ - --enable-zlib \ - --enable-shared \ - --enable-version3 \ - --enable-libfdk_aac \ - --enable-nonfree \ - --disable-doc \ - --x86asmexe=yasm + + # avoid multiple definitions error + export LDFLAGS="$LDFLAGS -Wl,--allow-multiple-definition" + + "${srcdir}"/ffmpeg/configure \ + --prefix="/usr/${_arch}" \ + --enable-cross-compile \ + --cross-prefix="${_arch}-" \ + --target-os=mingw32 \ + --arch=${_arch%%-*} \ + --disable-debug \ + --enable-static \ + --disable-stripping \ + --enable-amf \ + --enable-fontconfig \ + --enable-gmp \ + --enable-gnutls \ + --enable-gpl \ + --enable-avisynth \ + --enable-lto \ + --enable-libaom \ + --enable-libass \ + --enable-libbluray \ + --enable-libdav1d \ + --enable-libfreetype \ + --enable-libfribidi \ + --enable-libgsm \ + --enable-libmodplug \ + --enable-libmp3lame \ + --enable-libopencore_amrnb \ + --enable-libopencore_amrwb \ + --enable-libopenjpeg \ + --enable-libopus \ + --enable-librav1e \ + --enable-librsvg \ + --enable-libsoxr \ + --enable-libspeex \ + --enable-libsrt \ + --enable-libssh \ + --enable-libtheora \ + --enable-libvidstab \ + --enable-libvorbis \ + --enable-libvpx \ + --enable-libwebp \ + --enable-libx264 \ + --enable-libx265 \ + --enable-libxml2 \ + --enable-libxvid \ + --enable-libzimg \ + --enable-zlib \ + --enable-shared \ + --enable-version3 \ + --disable-doc \ + --x86asmexe=yasm + +# TODO: Add vmaf dependency +# --enable-libvmaf \ +# (only 64 bit support) +# --enable-libsvtav1 \ + make done } @@ -82,10 +151,14 @@ package() { for _arch in ${_architectures}; do cd "${srcdir}"/build-${_arch} make DESTDIR="$pkgdir" install - ${_arch}-strip --strip-all "${pkgdir}"/usr/${_arch}/bin/*.exe + + ${_arch}-strip -s "${pkgdir}"/usr/${_arch}/bin/*.exe ${_arch}-strip --strip-unneeded "${pkgdir}"/usr/${_arch}/bin/*.dll ${_arch}-strip -g "${pkgdir}"/usr/${_arch}/lib/*.a + mv "${pkgdir}"/usr/${_arch}/bin/*.lib "${pkgdir}"/usr/${_arch}/lib/ + + rm -rf "${pkgdir}"/usr/${_arch}/share done } diff --git a/ffmpeg/mingw-w64/add-av_stream_get_first_dts-for-chromium.patch b/ffmpeg/mingw-w64/add-av_stream_get_first_dts-for-chromium.patch new file mode 100644 index 00000000..fb558980 --- /dev/null +++ b/ffmpeg/mingw-w64/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" +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/mingw-w64/configure.patch b/ffmpeg/mingw-w64/configure.patch new file mode 100644 index 00000000..33fa63d9 --- /dev/null +++ b/ffmpeg/mingw-w64/configure.patch @@ -0,0 +1,17 @@ +diff -ruN ffmpeg-4.0/configure patched/configure +--- ffmpeg-4.0/configure 2018-04-20 12:02:55.000000000 +0200 ++++ patched/configure 2018-05-13 10:55:23.199869611 +0200 +@@ -5101,10 +5101,10 @@ + elif enabled aarch64; then + LIBTARGET="arm64" + fi +- if enabled shared; then ++ #if enabled shared; then + # Cannot build both shared and static libs when using dllimport. +- disable static +- fi ++ # disable static ++ #fi + enabled shared && ! enabled small && test_cmd $windres --version && enable gnu_windres + enabled x86_32 && check_ldflags -Wl,--large-address-aware + shlibdir_default="$bindir_default" diff --git a/ffmpeg/mingw-w64/ffmpeg-vmaf2.x.patch b/ffmpeg/mingw-w64/ffmpeg-vmaf2.x.patch new file mode 100644 index 00000000..52ed69ff --- /dev/null +++ b/ffmpeg/mingw-w64/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 + #include ++ + #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/ffmpeg/mingw-w64/vmaf-model-path.patch b/ffmpeg/mingw-w64/vmaf-model-path.patch new file mode 100644 index 00000000..abd65cdf --- /dev/null +++ b/ffmpeg/mingw-w64/vmaf-model-path.patch @@ -0,0 +1,13 @@ +diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c +index 249e50c720..9b791e19b1 100644 +--- a/libavfilter/vf_libvmaf.c ++++ b/libavfilter/vf_libvmaf.c +@@ -72,7 +72,7 @@ typedef struct LIBVMAFContext { + #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}, ++ {"model_path", "Set the model to be used for computing vmaf.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/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 (xml or json).", 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}, diff --git a/ffmpeg/mingw-w64/windres.patch b/ffmpeg/mingw-w64/windres.patch new file mode 100644 index 00000000..0e040f03 --- /dev/null +++ b/ffmpeg/mingw-w64/windres.patch @@ -0,0 +1,13 @@ +diff --git a/ffbuild/common.mak b/ffbuild/common.mak +index e070b6b5e2..ab536e157d 100644 +--- a/ffbuild/common.mak ++++ b/ffbuild/common.mak +@@ -90,7 +90,7 @@ COMPILE_MSA = $(call COMPILE,CC,MSAFLAGS) + -$(if $(ASMSTRIPFLAGS), $(STRIP) $(ASMSTRIPFLAGS) $@) + + %.o: %.rc +- $(WINDRES) $(IFLAGS) --preprocessor "$(DEPWINDRES) -E -xc-header -DRC_INVOKED $(CC_DEPFLAGS)" -o $@ $< ++ $(WINDRES) $(IFLAGS) -o $@ $< + + %.i: %.c + $(CC) $(CCFLAGS) $(CC_E) $<