History

avfilter/vf_libvmaf: update filter for libvmaf v2.0.0

Kyle Swanson committed 4 years ago

Commit 3d297241 parent 06a54a44 files · +558 −212Browse files

Unified · Split

4 files
  1. configure +2 −2
  2. doc/filters.texi +41 −41
  3. libavfilter/version.h +1 −1
  4. libavfilter/vf_libvmaf.c +514 −168
configure+2−2
@@ -3751,7 +3751,7 @@ vaguedenoiser_filter_deps="gpl" · 3750 unmodified lines
3751vflip_vulkan_filter_deps="vulkan spirv_compiler"3751vflip_vulkan_filter_deps="vulkan spirv_compiler"
3752vidstabdetect_filter_deps="libvidstab"3752vidstabdetect_filter_deps="libvidstab"
3753vidstabtransform_filter_deps="libvidstab"3753vidstabtransform_filter_deps="libvidstab"
3754libvmaf_filter_deps="libvmaf pthreads"3754libvmaf_filter_deps="libvmaf"
3755zmq_filter_deps="libzmq"3755zmq_filter_deps="libzmq"
3756zoompan_filter_deps="swscale"3756zoompan_filter_deps="swscale"
3757zscale_filter_deps="libzimg const_nan"3757zscale_filter_deps="libzimg const_nan"
@@ -6626,7 +6626,7 @@ enabled libtwolame && require libtwolame twolame.h twolame_init -ltwolame · 2868 unmodified lines
6626enabled libuavs3d && require_pkg_config libuavs3d "uavs3d >= 1.1.41" uavs3d.h uavs3d_decode6626enabled libuavs3d && require_pkg_config libuavs3d "uavs3d >= 1.1.41" uavs3d.h uavs3d_decode
6627enabled libv4l2 && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl6627enabled libv4l2 && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl
6628enabled libvidstab && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit6628enabled libvidstab && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit
6629enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 1.5.2" libvmaf.h compute_vmaf6629enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 2.0.0" libvmaf.h vmaf_init
6630enabled libvo_amrwbenc && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc6630enabled libvo_amrwbenc && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc
6631enabled libvorbis && require_pkg_config libvorbis vorbis vorbis/codec.h vorbis_info_init &&6631enabled libvorbis && require_pkg_config libvorbis vorbis vorbis/codec.h vorbis_info_init &&
6632 require_pkg_config libvorbisenc vorbisenc vorbis/vorbisenc.h vorbis_encode_init6632 require_pkg_config libvorbisenc vorbisenc vorbis/vorbisenc.h vorbis_encode_init
doc/filters.texi+41−41
@@ -14666,68 +14666,60 @@ ffmpeg -i input.mov -vf lensfun=make=Canon:model="Canon EOS 100D":lens_model="Ca · 14665 unmodified lines
14666 14666
14667@section libvmaf14667@section libvmaf
14668 14668
14669Obtain the VMAF (Video Multi-Method Assessment Fusion)14669Calulate the VMAF (Video Multi-Method Assessment Fusion) score for a
14670score between two input videos.14670reference/distorted pair of input videos.
14671 14671
14672The first input is the encoded video, and the second input is the reference video.14672The first input is the distorted video, and the second input is the reference video.
14673 14673
14674The obtained VMAF score is printed through the logging system.14674The obtained VMAF score is printed through the logging system.
14675 14675
14676It requires Netflix's vmaf library (libvmaf) as a pre-requisite.14676It requires Netflix's vmaf library (libvmaf) as a pre-requisite.
14677After installing the library it can be enabled using:14677After installing the library it can be enabled using:
14678@code{./configure --enable-libvmaf}.14678@code{./configure --enable-libvmaf}.
14679If no model path is specified it uses the default model: @code{vmaf_v0.6.1.pkl}.
14680 14679
14681The filter has following options:14680The filter has following options:
14682 14681
14683@table @option14682@table @option
14684@item model_path14683@item model
14685Set the model path which is to be used for SVM.14684A `|` delimited list of vmaf models. Each model can be configured with a number of parameters.
14686Default value: @code{"/usr/local/share/model/vmaf_v0.6.1.pkl"}14685Default value: @code{"version=vmaf_v0.6.1"}
14687
14688@item log_path
14689Set the file path to be used to store logs.
14690 14686
14691@item log_fmt14687@item model_path
14692Set the format of the log file (csv, json or xml).14688Deprecated, use model='path=...'.
14693 14689
14694@item enable_transform14690@item enable_transform
14695This option can enable/disable the @code{score_transform} applied to the final predicted VMAF score,14691Deprecated, use model='enable_transform=true'.
14696if you have specified score_transform option in the input parameter file passed to @code{run_vmaf_training.py}
14697Default value: @code{false}
14698 14692
14699@item phone_model14693@item phone_model
14700Invokes the phone model which will generate VMAF scores higher than in the14694Deprecated, use model='enable_transform=true'.
14701regular model, which is more suitable for laptop, TV, etc. viewing conditions.14695
14702Default value: @code{false}14696@item enable_conf_interval
14697Deprecated, use model='enable_conf_interval=true'.
14698
14699@item feature
14700A `|` delimited list of features. Each feature can be configured with a number of parameters.
14703 14701
14704@item psnr14702@item psnr
14705Enables computing psnr along with vmaf.14703Deprecated, use feature='name=psnr'.
14706Default value: @code{false}
14707 14704
14708@item ssim14705@item ssim
14709Enables computing ssim along with vmaf.14706Deprecated, use feature='name=ssim'.
14710Default value: @code{false}
14711 14707
14712@item ms_ssim14708@item ms_ssim
14713Enables computing ms_ssim along with vmaf.14709Deprecated, use feature='name=ms_ssim'.
14714Default value: @code{false}
14715 14710
14716@item pool14711@item log_path
14717Set the pool method to be used for computing vmaf.14712Set the file path to be used to store log files.
14718Options are @code{min}, @code{harmonic_mean} or @code{mean} (default).14713
14714@item log_fmt
14715Set the format of the log file (xml, json, csv, or sub).
14719 14716
14720@item n_threads14717@item n_threads
14721Set number of threads to be used when computing vmaf.14718Set number of threads to be used when initializing libvmaf.
14722Default value: @code{0}, which makes use of all available logical processors.14719Default value: @code{0}, no threads.
14723 14720
14724@item n_subsample14721@item n_subsample
14725Set interval for frame subsampling used when computing vmaf.14722Set frame subsampling interval to be used.
14726Default value: @code{1}
14727
14728@item enable_conf_interval
14729Enables confidence interval.
14730Default value: @code{false}
14731@end table14723@end table
14732 14724
14733This filter also supports the @ref{framesync} options.14725This filter also supports the @ref{framesync} options.
@@ -14735,23 +14727,31 @@ This filter also supports the @ref{framesync} options. · 1 unmodified line
14735@subsection Examples14727@subsection Examples
14736@itemize14728@itemize
14737@item14729@item
14738On the below examples the input file @file{main.mpg} being processed is14730In the examples below, a distorted video @file{distorted.mpg} is
14739compared with the reference file @file{ref.mpg}.14731compared with a reference file @file{reference.mpg}.
14740 14732
14733@item
14734Basic usage:
14735@example
14736ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf=log_path=output.xml -f null -
14737@end example
14738
14739@item
14740Example with multiple models:
14741@example14741@example
14742ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf -f null -14742ffmpeg -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 -
14743@end example14743@end example
14744 14744
14745@item14745@item
14746Example with options:14746Example with multiple addtional features:
14747@example14747@example
14748ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf="psnr=1:log_fmt=json" -f null -14748ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf='feature=name=psnr|name=ciede' -f null -
14749@end example14749@end example
14750 14750
14751@item14751@item
14752Example with options and different containers:14752Example with options and different containers:
14753@example14753@example
14754ffmpeg -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 -14754ffmpeg -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 -
14755@end example14755@end example
14756@end itemize14756@end itemize
14757 14757
libavfilter/version.h+1−1
@@ -31,7 +31,7 @@ · 30 unmodified lines
31 31
32#define LIBAVFILTER_VERSION_MAJOR 832#define LIBAVFILTER_VERSION_MAJOR 8
33#define LIBAVFILTER_VERSION_MINOR 2633#define LIBAVFILTER_VERSION_MINOR 26
34#define LIBAVFILTER_VERSION_MICRO 10034#define LIBAVFILTER_VERSION_MICRO 101
35 35
36 36
37#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \37#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
libavfilter/vf_libvmaf.c+514−168
@@ -24,8 +24,8 @@ · 23 unmodified lines
24 * Calculate the VMAF between two input videos.24 * Calculate the VMAF between two input videos.
25 */25 */
26 26
27#include <pthread.h>
28#include <libvmaf.h>27#include <libvmaf.h>
28
29#include "libavutil/avstring.h"29#include "libavutil/avstring.h"
30#include "libavutil/opt.h"30#include "libavutil/opt.h"
31#include "libavutil/pixdesc.h"31#include "libavutil/pixdesc.h"
@@ -39,23 +39,9 @@ · 7 unmodified lines
39typedef struct LIBVMAFContext {39typedef struct LIBVMAFContext {
40 const AVClass *class;40 const AVClass *class;
41 FFFrameSync fs;41 FFFrameSync fs;
42 const AVPixFmtDescriptor *desc;
43 int width;
44 int height;
45 double vmaf_score;
46 int vmaf_thread_created;
47 pthread_t vmaf_thread;
48 pthread_mutex_t lock;
49 pthread_cond_t cond;
50 int eof;
51 AVFrame *gmain;
52 AVFrame *gref;
53 int frame_set;
54 char *model_path;42 char *model_path;
55 char *log_path;43 char *log_path;
56 char *log_fmt;44 char *log_fmt;
57 int disable_clip;
58 int disable_avx;
59 int enable_transform;45 int enable_transform;
60 int phone_model;46 int phone_model;
61 int psnr;47 int psnr;
@@ -65,184 +51,487 @@ typedef struct LIBVMAFContext { · 3 unmodified lines
65 int n_threads;51 int n_threads;
66 int n_subsample;52 int n_subsample;
67 int enable_conf_interval;53 int enable_conf_interval;
68 int error;54 char *model_cfg;
55 char *feature_cfg;
56 VmafContext *vmaf;
57 VmafModel **model;
58 unsigned model_cnt;
59 unsigned frame_cnt;
60 unsigned bpc;
69} LIBVMAFContext;61} LIBVMAFContext;
70 62
71#define OFFSET(x) offsetof(LIBVMAFContext, x)63#define OFFSET(x) offsetof(LIBVMAFContext, x)
72#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM64#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
73 65
74static const AVOption libvmaf_options[] = {66static const AVOption libvmaf_options[] = {
75 {"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},67 {"model_path", "use model='path=...'.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
76 {"log_path", "Set the file path to be used to store logs.", OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},68 {"log_path", "Set the file path to be used to write log.", OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
77 {"log_fmt", "Set the format of the log (csv, json or xml).", OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},69 {"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},
78 {"enable_transform", "Enables transform for computing vmaf.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},70 {"enable_transform", "use model='enable_transform=true'.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
79 {"phone_model", "Invokes the phone model that will generate higher VMAF scores.", OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},71 {"phone_model", "use model='enable_transform=true'.", OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
80 {"psnr", "Enables computing psnr along with vmaf.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},72 {"psnr", "use feature='name=psnr'.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
81 {"ssim", "Enables computing ssim along with vmaf.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},73 {"ssim", "use feature='name=ssim'.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
82 {"ms_ssim", "Enables computing ms-ssim along with vmaf.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},74 {"ms_ssim", "use feature='name=ms_ssim'.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
83 {"pool", "Set the pool method to be used for computing vmaf.", OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},75 {"pool", "Set the pool method to be used for computing vmaf.", OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
84 {"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},76 {"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},
85 {"n_subsample", "Set interval for frame subsampling used when computing vmaf.", OFFSET(n_subsample), AV_OPT_TYPE_INT, {.i64=1}, 1, UINT_MAX, FLAGS},77 {"n_subsample", "Set interval for frame subsampling used when computing vmaf.", OFFSET(n_subsample), AV_OPT_TYPE_INT, {.i64=1}, 1, UINT_MAX, FLAGS},
86 {"enable_conf_interval", "Enables confidence interval.", OFFSET(enable_conf_interval), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},78 {"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},
79 {"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},
80 {"feature", "Set the feature to be used for computing vmaf.", OFFSET(feature_cfg), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
87 { NULL }81 { NULL }
88};82};
89 83
90FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs);84FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs);
91 85
92#define read_frame_fn(type, bits) \86static enum VmafPixelFormat pix_fmt_map(enum AVPixelFormat av_pix_fmt)
93 static int read_frame_##bits##bit(float *ref_data, float *main_data, \87{
94 float *temp_data, int stride, void *ctx) \88 switch (av_pix_fmt) {
95{ \89 case AV_PIX_FMT_YUV420P:
96 LIBVMAFContext *s = (LIBVMAFContext *) ctx; \90 case AV_PIX_FMT_YUV420P10LE:
97 int ret; \91 case AV_PIX_FMT_YUV420P12LE:
98 \92 case AV_PIX_FMT_YUV420P16LE:
99 pthread_mutex_lock(&s->lock); \93 return VMAF_PIX_FMT_YUV420P;
100 \94 case AV_PIX_FMT_YUV422P:
101 while (!s->frame_set && !s->eof) { \95 case AV_PIX_FMT_YUV422P10LE:
102 pthread_cond_wait(&s->cond, &s->lock); \96 case AV_PIX_FMT_YUV422P12LE:
103 } \97 case AV_PIX_FMT_YUV422P16LE:
104 \98 return VMAF_PIX_FMT_YUV422P;
105 if (s->frame_set) { \99 case AV_PIX_FMT_YUV444P:
106 int ref_stride = s->gref->linesize[0]; \100 case AV_PIX_FMT_YUV444P10LE:
107 int main_stride = s->gmain->linesize[0]; \101 case AV_PIX_FMT_YUV444P12LE:
108 \102 case AV_PIX_FMT_YUV444P16LE:
109 const type *ref_ptr = (const type *) s->gref->data[0]; \103 return VMAF_PIX_FMT_YUV444P;
110 const type *main_ptr = (const type *) s->gmain->data[0]; \104 default:
111 \105 return VMAF_PIX_FMT_UNKNOWN;
112 float *ptr = ref_data; \106 }
113 float factor = 1.f / (1 << (bits - 8)); \
114 \
115 int h = s->height; \
116 int w = s->width; \
117 \
118 int i,j; \
119 \
120 for (i = 0; i < h; i++) { \
121 for ( j = 0; j < w; j++) { \
122 ptr[j] = ref_ptr[j] * factor; \
123 } \
124 ref_ptr += ref_stride / sizeof(*ref_ptr); \
125 ptr += stride / sizeof(*ptr); \
126 } \
127 \
128 ptr = main_data; \
129 \
130 for (i = 0; i < h; i++) { \
131 for (j = 0; j < w; j++) { \
132 ptr[j] = main_ptr[j] * factor; \
133 } \
134 main_ptr += main_stride / sizeof(*main_ptr); \
135 ptr += stride / sizeof(*ptr); \
136 } \
137 } \
138 \
139 ret = !s->frame_set; \
140 \
141 av_frame_unref(s->gref); \
142 av_frame_unref(s->gmain); \
143 s->frame_set = 0; \
144 \
145 pthread_cond_signal(&s->cond); \
146 pthread_mutex_unlock(&s->lock); \
147 \
148 if (ret) { \
149 return 2; \
150 } \
151 \
152 return 0; \
153}107}
154 108
155read_frame_fn(uint8_t, 8);109static int copy_picture_data(AVFrame *src, VmafPicture *dst, unsigned bpc)
156read_frame_fn(uint16_t, 10);110{
111 int err = vmaf_picture_alloc(dst, pix_fmt_map(src->format), bpc,
112 src->width, src->height);
113 if (err)
114 return AVERROR(ENOMEM);
115
116 for (unsigned i = 0; i < 3; i++) {
117 uint8_t *src_data = src->data[i];
118 uint8_t *dst_data = dst->data[i];
119 for (unsigned j = 0; j < dst->h[i]; j++) {
120 memcpy(dst_data, src_data, sizeof(*dst_data) * dst->w[i]);
121 src_data += src->linesize[i];
122 dst_data += dst->stride[i];
123 }
124 }
125
126 return 0;
127}
157 128
158static void compute_vmaf_score(LIBVMAFContext *s)129static int do_vmaf(FFFrameSync *fs)
159{130{
160 int (*read_frame)(float *ref_data, float *main_data, float *temp_data,131 AVFilterContext *ctx = fs->parent;
161 int stride, void *ctx);132 LIBVMAFContext *s = ctx->priv;
162 char *format;133 VmafPicture pic_ref, pic_dist;
134 AVFrame *ref, *dist;
135 int err = 0;
163 136
164 if (s->desc->comp[0].depth <= 8) {137 int ret = ff_framesync_dualinput_get(fs, &dist, &ref);
165 read_frame = read_frame_8bit;138 if (ret < 0)
166 } else {139 return ret;
167 read_frame = read_frame_10bit;140 if (ctx->is_disabled || !ref)
141 return ff_filter_frame(ctx->outputs[0], dist);
142
143 err = copy_picture_data(ref, &pic_ref, s->bpc);
144 if (err) {
145 av_log(s, AV_LOG_ERROR, "problem during vmaf_picture_alloc.\n");
146 return AVERROR(ENOMEM);
147 }
148
149 err = copy_picture_data(dist, &pic_dist, s->bpc);
150 if (err) {
151 av_log(s, AV_LOG_ERROR, "problem during vmaf_picture_alloc.\n");
152 vmaf_picture_unref(&pic_ref);
153 return AVERROR(ENOMEM);
168 }154 }
169 155
170 format = (char *) s->desc->name;156 err = vmaf_read_pictures(s->vmaf, &pic_ref, &pic_dist, s->frame_cnt++);
157 if (err) {
158 av_log(s, AV_LOG_ERROR, "problem during vmaf_read_pictures.\n");
159 return AVERROR(EINVAL);
160 }
171 161
172 s->error = compute_vmaf(&s->vmaf_score, format, s->width, s->height,162 return ff_filter_frame(ctx->outputs[0], dist);
173 read_frame, s, s->model_path, s->log_path,
174 s->log_fmt, 0, 0, s->enable_transform,
175 s->phone_model, s->psnr, s->ssim,
176 s->ms_ssim, s->pool,
177 s->n_threads, s->n_subsample, s->enable_conf_interval);
178}163}
179 164
180static void *call_vmaf(void *ctx)165
166static AVDictionary **delimited_dict_parse(char *str, unsigned *cnt)
181{167{
182 LIBVMAFContext *s = (LIBVMAFContext *) ctx;168 AVDictionary **dict = NULL;
183 compute_vmaf_score(s);169 char *str_copy = NULL;
184 if (!s->error) {170 char *saveptr = NULL;
185 av_log(ctx, AV_LOG_INFO, "VMAF score: %f\n",s->vmaf_score);171 unsigned cnt2;
186 } else {172 int err = 0;
187 pthread_mutex_lock(&s->lock);173
188 pthread_cond_signal(&s->cond);174 if (!str)
189 pthread_mutex_unlock(&s->lock);175 return NULL;
176
177 cnt2 = 1;
178 for (char *p = str; *p; p++) {
179 if (*p == '|')
180 cnt2++;
181 }
182
183 dict = av_calloc(cnt2, sizeof(*dict));
184 if (!dict)
185 goto fail;
186
187 str_copy = av_strdup(str);
188 if (!str_copy)
189 goto fail;
190
191 *cnt = 0;
192 for (unsigned i = 0; i < cnt2; i++) {
193 char *s = av_strtok(i == 0 ? str_copy : NULL, "|", &saveptr);
194 if (!s)
195 continue;
196 err = av_dict_parse_string(&dict[(*cnt)++], s, "=", ":", 0);
197 if (err)
198 goto fail;
199 }
200
201 av_free(str_copy);
202 return dict;
203
204fail:
205 if (dict) {
206 for (unsigned i = 0; i < *cnt; i++) {
207 if (dict[i])
208 av_dict_free(&dict[i]);
209 }
210 av_free(dict);
190 }211 }
191 pthread_exit(NULL);212
213 av_free(str_copy);
214 *cnt = 0;
192 return NULL;215 return NULL;
193}216}
194 217
195static int do_vmaf(FFFrameSync *fs)218static int parse_features(AVFilterContext *ctx)
196{219{
197 AVFilterContext *ctx = fs->parent;
198 LIBVMAFContext *s = ctx->priv;220 LIBVMAFContext *s = ctx->priv;
199 AVFrame *master, *ref;221 AVDictionary **dict = NULL;
200 int ret;222 unsigned dict_cnt;
223 int err = 0;
201 224
202 ret = ff_framesync_dualinput_get(fs, &master, &ref);225 if (!s->feature_cfg)
203 if (ret < 0)226 return 0;
204 return ret;227
205 if (!ref)228 dict = delimited_dict_parse(s->feature_cfg, &dict_cnt);
206 return ff_filter_frame(ctx->outputs[0], master);229 if (!dict) {
230 av_log(ctx, AV_LOG_ERROR,
231 "could not parse feature config: %s\n", s->feature_cfg);
232 return AVERROR(EINVAL);
233 }
207 234
208 pthread_mutex_lock(&s->lock);235 for (unsigned i = 0; i < dict_cnt; i++) {
236 char *feature_name = NULL;
237 VmafFeatureDictionary *feature_opts_dict = NULL;
238 AVDictionaryEntry *e = NULL;
239
240 while (e = av_dict_get(dict[i], "", e, AV_DICT_IGNORE_SUFFIX)) {
241 if (av_stristr(e->key, "name")) {
242 feature_name = e->value;
243 continue;
244 }
245
246 err = vmaf_feature_dictionary_set(&feature_opts_dict, e->key,
247 e->value);
248 if (err) {
249 av_log(ctx, AV_LOG_ERROR,
250 "could not set feature option: %s.%s=%s\n",
251 feature_name, e->key, e->value);
252 goto exit;
253 }
254 }
255
256 err = vmaf_use_feature(s->vmaf, feature_name, feature_opts_dict);
257 if (err) {
258 av_log(ctx, AV_LOG_ERROR,
259 "problem during vmaf_use_feature: %s\n", feature_name);
260 goto exit;
261 }
262 }
209 263
210 while (s->frame_set && !s->error) {264exit:
211 pthread_cond_wait(&s->cond, &s->lock);265 for (unsigned i = 0; i < dict_cnt; i++) {
266 if (dict[i])
267 av_dict_free(&dict[i]);
212 }268 }
269 av_free(dict);
270 return err;
271}
272
273static int parse_models(AVFilterContext *ctx)
274{
275 LIBVMAFContext *s = ctx->priv;
276 AVDictionary **dict;
277 unsigned dict_cnt;
278 int err = 0;
279
280 if (!s->model_cfg) return 0;
213 281
214 if (s->error) {282 dict_cnt = 0;
283 dict = delimited_dict_parse(s->model_cfg, &dict_cnt);
284 if (!dict) {
215 av_log(ctx, AV_LOG_ERROR,285 av_log(ctx, AV_LOG_ERROR,
216 "libvmaf encountered an error, check log for details\n");286 "could not parse model config: %s\n", s->model_cfg);
217 pthread_mutex_unlock(&s->lock);
218 return AVERROR(EINVAL);287 return AVERROR(EINVAL);
219 }288 }
220 289
221 av_frame_ref(s->gref, ref);290 s->model_cnt = dict_cnt;
222 av_frame_ref(s->gmain, master);291 s->model = av_calloc(s->model_cnt, sizeof(*s->model));
292 if (!s->model)
293 return AVERROR(ENOMEM);
294
295 for (unsigned i = 0; i < dict_cnt; i++) {
296 VmafModelConfig model_cfg = { 0 };
297 AVDictionaryEntry *e = NULL;
298 char *version = NULL;
299 char *path = NULL;
300
301 while (e = av_dict_get(dict[i], "", e, AV_DICT_IGNORE_SUFFIX)) {
302 if (av_stristr(e->key, "disable_clip")) {
303 model_cfg.flags |= av_stristr(e->value, "true") ?
304 VMAF_MODEL_FLAG_DISABLE_CLIP : 0;
305 continue;
306 }
307
308 if (av_stristr(e->key, "enable_transform")) {
309 model_cfg.flags |= av_stristr(e->value, "true") ?
310 VMAF_MODEL_FLAG_ENABLE_TRANSFORM : 0;
311 continue;
312 }
313
314 if (av_stristr(e->key, "name")) {
315 model_cfg.name = e->value;
316 continue;
317 }
318
319 if (av_stristr(e->key, "version")) {
320 version = e->value;
321 continue;
322 }
323
324 if (av_stristr(e->key, "path")) {
325 path = e->value;
326 continue;
327 }
328 }
329
330 if (version) {
331 err = vmaf_model_load(&s->model[i], &model_cfg, version);
332 if (err) {
333 av_log(ctx, AV_LOG_ERROR,
334 "could not load libvmaf model with version: %s\n",
335 version);
336 goto exit;
337 }
338 }
339
340 if (path && !s->model[i]) {
341 err = vmaf_model_load_from_path(&s->model[i], &model_cfg, path);
342 if (err) {
343 av_log(ctx, AV_LOG_ERROR,
344 "could not load libvmaf model with path: %s\n",
345 path);
346 goto exit;
347 }
348 }
349
350 if (!s->model[i]) {
351 av_log(ctx, AV_LOG_ERROR,
352 "could not load libvmaf model with config: %s\n",
353 s->model_cfg);
354 goto exit;
355 }
356
357 while (e = av_dict_get(dict[i], "", e, AV_DICT_IGNORE_SUFFIX)) {
358 VmafFeatureDictionary *feature_opts_dict = NULL;
359 char *feature_opt = NULL;
360
361 char *feature_name = av_strtok(e->key, ".", &feature_opt);
362 if (!feature_opt)
363 continue;
364
365 err = vmaf_feature_dictionary_set(&feature_opts_dict,
366 feature_opt, e->value);
367 if (err) {
368 av_log(ctx, AV_LOG_ERROR,
369 "could not set feature option: %s.%s=%s\n",
370 feature_name, feature_opt, e->value);
371 err = AVERROR(EINVAL);
372 goto exit;
373 }
374
375 err = vmaf_model_feature_overload(s->model[i], feature_name,
376 feature_opts_dict);
377 if (err) {
378 av_log(ctx, AV_LOG_ERROR,
379 "could not overload feature: %s\n", feature_name);
380 err = AVERROR(EINVAL);
381 goto exit;
382 }
383 }
384 }
385
386 for (unsigned i = 0; i < s->model_cnt; i++) {
387 err = vmaf_use_features_from_model(s->vmaf, s->model[i]);
388 if (err) {
389 av_log(ctx, AV_LOG_ERROR,
390 "problem during vmaf_use_features_from_model\n");
391 err = AVERROR(EINVAL);
392 goto exit;
393 }
394 }
395
396exit:
397 for (unsigned i = 0; i < dict_cnt; i++) {
398 if (dict[i])
399 av_dict_free(&dict[i]);
400 }
401 av_free(dict);
402 return err;
403}
404
405static enum VmafLogLevel log_level_map(int log_level)
406{
407 switch (log_level) {
408 case AV_LOG_QUIET:
409 return VMAF_LOG_LEVEL_NONE;
410 case AV_LOG_ERROR:
411 return VMAF_LOG_LEVEL_ERROR;
412 case AV_LOG_WARNING:
413 return VMAF_LOG_LEVEL_WARNING;
414 case AV_LOG_INFO:
415 return VMAF_LOG_LEVEL_INFO;
416 case AV_LOG_DEBUG:
417 return VMAF_LOG_LEVEL_DEBUG;
418 default:
419 return VMAF_LOG_LEVEL_INFO;
420 }
421}
422
423static int parse_deprecated_options(AVFilterContext *ctx)
424{
425 LIBVMAFContext *s = ctx->priv;
426 VmafModel *model = NULL;
427 VmafModelCollection *model_collection = NULL;
428 enum VmafModelFlags flags = VMAF_MODEL_FLAGS_DEFAULT;
429 int err = 0;
430
431 VmafModelConfig model_cfg = {
432 .name = "vmaf",
433 .flags = flags,
434 };
435
436 if (s->enable_transform || s->phone_model)
437 flags |= VMAF_MODEL_FLAG_ENABLE_TRANSFORM;
438
439 if (!s->model_path)
440 goto extra_metrics_only;
441
442 if (s->enable_conf_interval) {
443 err = vmaf_model_collection_load_from_path(&model, &model_collection,
444 &model_cfg, s->model_path);
445 if (err) {
446 av_log(ctx, AV_LOG_ERROR,
447 "problem loading model file: %s\n", s->model_path);
448 goto exit;
449 }
450
451 err = vmaf_use_features_from_model_collection(s->vmaf, model_collection);
452 if (err) {
453 av_log(ctx, AV_LOG_ERROR,
454 "problem loading feature extractors from model file: %s\n",
455 s->model_path);
456 goto exit;
457 }
458 } else {
459 err = vmaf_model_load_from_path(&model, &model_cfg, s->model_path);
460 if (err) {
461 av_log(ctx, AV_LOG_ERROR,
462 "problem loading model file: %s\n", s->model_path);
463 goto exit;
464 }
465 err = vmaf_use_features_from_model(s->vmaf, model);
466 if (err) {
467 av_log(ctx, AV_LOG_ERROR,
468 "problem loading feature extractors from model file: %s\n",
469 s->model_path);
470 goto exit;
471 }
472 }
473
474extra_metrics_only:
475 if (s->psnr) {
476 VmafFeatureDictionary *d = NULL;
477 vmaf_feature_dictionary_set(&d, "enable_chroma", "false");
478
479 err = vmaf_use_feature(s->vmaf, "psnr", d);
480 if (err) {
481 av_log(ctx, AV_LOG_ERROR,
482 "problem loading feature extractor: psnr\n");
483 goto exit;
484 }
485 }
223 486
224 s->frame_set = 1;487 if (s->ssim) {
488 err = vmaf_use_feature(s->vmaf, "float_ssim", NULL);
489 if (err) {
490 av_log(ctx, AV_LOG_ERROR,
491 "problem loading feature extractor: ssim\n");
492 goto exit;
493 }
494 }
225 495
226 pthread_cond_signal(&s->cond);496 if (s->ms_ssim) {
227 pthread_mutex_unlock(&s->lock);497 err = vmaf_use_feature(s->vmaf, "float_ms_ssim", NULL);
498 if (err) {
499 av_log(ctx, AV_LOG_ERROR,
500 "problem loading feature extractor: ms_ssim\n");
501 goto exit;
502 }
503 }
228 504
229 return ff_filter_frame(ctx->outputs[0], master);505exit:
506 return err;
230}507}
231 508
232static av_cold int init(AVFilterContext *ctx)509static av_cold int init(AVFilterContext *ctx)
233{510{
234 LIBVMAFContext *s = ctx->priv;511 LIBVMAFContext *s = ctx->priv;
512 int err = 0;
235 513
236 s->gref = av_frame_alloc();514 VmafConfiguration cfg = {
237 s->gmain = av_frame_alloc();515 .log_level = log_level_map(av_log_get_level()),
238 if (!s->gref || !s->gmain)516 .n_subsample = s->n_subsample,
239 return AVERROR(ENOMEM);517 .n_threads = s->n_threads,
518 };
519
520 err = vmaf_init(&s->vmaf, cfg);
521 if (err)
522 return AVERROR(EINVAL);
523
524 err = parse_deprecated_options(ctx);
525 if (err)
526 return err;
240 527
241 s->error = 0;528 err = parse_models(ctx);
529 if (err)
530 return err;
242 531
243 s->vmaf_thread_created = 0;532 err = parse_features(ctx);
244 pthread_mutex_init(&s->lock, NULL);533 if (err)
245 pthread_cond_init (&s->cond, NULL);534 return err;
246 535
247 s->fs.on_event = do_vmaf;536 s->fs.on_event = do_vmaf;
248 return 0;537 return 0;
@@ -256,26 +545,31 @@ static const enum AVPixelFormat pix_fmts[] = { · 7 unmodified lines
256 545
257static int config_input_ref(AVFilterLink *inlink)546static int config_input_ref(AVFilterLink *inlink)
258{547{
259 AVFilterContext *ctx = inlink->dst;548 AVFilterContext *ctx = inlink->dst;
260 LIBVMAFContext *s = ctx->priv;549 LIBVMAFContext *s = ctx->priv;
261 int th;550 const AVPixFmtDescriptor *desc;
551 int err = 0;
262 552
263 if (ctx->inputs[0]->w != ctx->inputs[1]->w ||553 if (ctx->inputs[0]->w != ctx->inputs[1]->w) {
264 ctx->inputs[0]->h != ctx->inputs[1]->h) {554 av_log(ctx, AV_LOG_ERROR, "input width must match.\n");
265 av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n");555 err |= AVERROR(EINVAL);
266 return AVERROR(EINVAL);
267 }556 }
268 557
269 s->desc = av_pix_fmt_desc_get(inlink->format);558 if (ctx->inputs[0]->h != ctx->inputs[1]->h) {
270 s->width = ctx->inputs[0]->w;559 av_log(ctx, AV_LOG_ERROR, "input height must match.\n");
271 s->height = ctx->inputs[0]->h;560 err |= AVERROR(EINVAL);
561 }
272 562
273 th = pthread_create(&s->vmaf_thread, NULL, call_vmaf, (void *) s);563 if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
274 if (th) {564 av_log(ctx, AV_LOG_ERROR, "input pix_fmt must match.\n");
275 av_log(ctx, AV_LOG_ERROR, "Thread creation failed.\n");565 err |= AVERROR(EINVAL);
276 return AVERROR(EINVAL);
277 }566 }
278 s->vmaf_thread_created = 1;567
568 if (err)
569 return err;
570
571 desc = av_pix_fmt_desc_get(inlink->format);
572 s->bpc = desc->comp[0].depth;
279 573
280 return 0;574 return 0;
281}575}
@@ -307,28 +601,80 @@ static int activate(AVFilterContext *ctx) · 25 unmodified lines
307 return ff_framesync_activate(&s->fs);601 return ff_framesync_activate(&s->fs);
308}602}
309 603
604static enum VmafOutputFormat log_fmt_map(const char *log_fmt)
605{
606 if (log_fmt) {
607 if (av_stristr(log_fmt, "xml"))
608 return VMAF_OUTPUT_FORMAT_XML;
609 if (av_stristr(log_fmt, "json"))
610 return VMAF_OUTPUT_FORMAT_JSON;
611 if (av_stristr(log_fmt, "csv"))
612 return VMAF_OUTPUT_FORMAT_CSV;
613 if (av_stristr(log_fmt, "sub"))
614 return VMAF_OUTPUT_FORMAT_SUB;
615 }
616
617 return VMAF_OUTPUT_FORMAT_XML;
618}
619
620static enum VmafPoolingMethod pool_method_map(const char *pool_method)
621{
622 if (pool_method) {
623 if (av_stristr(pool_method, "min"))
624 return VMAF_POOL_METHOD_MIN;
625 if (av_stristr(pool_method, "mean"))
626 return VMAF_POOL_METHOD_MEAN;
627 if (av_stristr(pool_method, "harmonic_mean"))
628 return VMAF_POOL_METHOD_HARMONIC_MEAN;
629 }
630
631 return VMAF_POOL_METHOD_MEAN;
632}
633
310static av_cold void uninit(AVFilterContext *ctx)634static av_cold void uninit(AVFilterContext *ctx)
311{635{
312 LIBVMAFContext *s = ctx->priv;636 LIBVMAFContext *s = ctx->priv;
637 int err = 0;
313 638
314 ff_framesync_uninit(&s->fs);639 ff_framesync_uninit(&s->fs);
315 640
316 pthread_mutex_lock(&s->lock);641 if (!s->frame_cnt)
317 s->eof = 1;642 goto clean_up;
318 pthread_cond_signal(&s->cond);
319 pthread_mutex_unlock(&s->lock);
320 643
321 if (s->vmaf_thread_created)644 err = vmaf_read_pictures(s->vmaf, NULL, NULL, 0);
322 {645 if (err) {
323 pthread_join(s->vmaf_thread, NULL);646 av_log(ctx, AV_LOG_ERROR,
324 s->vmaf_thread_created = 0;647 "problem flushing libvmaf context.\n");
325 }648 }
326 649
327 av_frame_free(&s->gref);650 for (unsigned i = 0; i < s->model_cnt; i++) {
328 av_frame_free(&s->gmain);651 double vmaf_score;
652 err = vmaf_score_pooled(s->vmaf, s->model[i], pool_method_map(s->pool),
653 &vmaf_score, 0, s->frame_cnt - 1);
654 if (err) {
655 av_log(ctx, AV_LOG_ERROR,
656 "problem getting pooled vmaf score.\n");
657 }
658
659 av_log(ctx, AV_LOG_INFO, "VMAF score: %f\n", vmaf_score);
660 }
661
662 if (s->vmaf) {
663 if (s->log_path && !err)
664 vmaf_write_output(s->vmaf, s->log_path, log_fmt_map(s->log_fmt));
665 }
666
667clean_up:
668 if (s->model) {
669 for (unsigned i = 0; i < s->model_cnt; i++) {
670 if (s->model[i])
671 vmaf_model_destroy(s->model[i]);
672 }
673 av_free(s->model);
674 }
329 675
330 pthread_mutex_destroy(&s->lock);676 if (s->vmaf)
331 pthread_cond_destroy(&s->cond);677 vmaf_close(s->vmaf);
332}678}
333 679
334static const AVFilterPad libvmaf_inputs[] = {680static const AVFilterPad libvmaf_inputs[] = {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t