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
37513751 vflip_vulkan_filter_deps="vulkan spirv_compiler"
37523752 vidstabdetect_filter_deps="libvidstab"
37533753 vidstabtransform_filter_deps="libvidstab"
3754−libvmaf_filter_deps="libvmaf pthreads"
3754+libvmaf_filter_deps="libvmaf"
37553755 zmq_filter_deps="libzmq"
37563756 zoompan_filter_deps="swscale"
37573757 zscale_filter_deps="libzimg const_nan"
@@ -6626,7 +6626,7 @@ enabled libtwolame && require libtwolame twolame.h twolame_init -ltwolame · 2868 unmodified lines
66266626 enabled libuavs3d && require_pkg_config libuavs3d "uavs3d >= 1.1.41" uavs3d.h uavs3d_decode
66276627 enabled libv4l2 && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl
66286628 enabled libvidstab && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit
6629−enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 1.5.2" libvmaf.h compute_vmaf
6629+enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 2.0.0" libvmaf.h vmaf_init
66306630 enabled libvo_amrwbenc && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc
66316631 enabled libvorbis && require_pkg_config libvorbis vorbis vorbis/codec.h vorbis_info_init &&
66326632 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
1466614666
1466714667 @section libvmaf
1466814668
14669−Obtain the VMAF (Video Multi-Method Assessment Fusion)
14670−score between two input videos.
14669+Calulate the VMAF (Video Multi-Method Assessment Fusion) score for a
14670+reference/distorted pair of input videos.
1467114671
14672−The first input is the encoded video, and the second input is the reference video.
14672+The first input is the distorted video, and the second input is the reference video.
1467314673
1467414674 The obtained VMAF score is printed through the logging system.
1467514675
1467614676 It requires Netflix's vmaf library (libvmaf) as a pre-requisite.
1467714677 After installing the library it can be enabled using:
1467814678 @code{./configure --enable-libvmaf}.
14679−If no model path is specified it uses the default model: @code{vmaf_v0.6.1.pkl}.
1468014679
1468114680 The filter has following options:
1468214681
1468314682 @table @option
14684−@item model_path
14685−Set the model path which is to be used for SVM.
14686−Default value: @code{"/usr/local/share/model/vmaf_v0.6.1.pkl"}
14687−
14688−@item log_path
14689−Set the file path to be used to store logs.
14683+@item model
14684+A `|` delimited list of vmaf models. Each model can be configured with a number of parameters.
14685+Default value: @code{"version=vmaf_v0.6.1"}
1469014686
14691−@item log_fmt
14692−Set the format of the log file (csv, json or xml).
14687+@item model_path
14688+Deprecated, use model='path=...'.
1469314689
1469414690 @item enable_transform
14695−This option can enable/disable the @code{score_transform} applied to the final predicted VMAF score,
14696−if you have specified score_transform option in the input parameter file passed to @code{run_vmaf_training.py}
14697−Default value: @code{false}
14691+Deprecated, use model='enable_transform=true'.
1469814692
1469914693 @item phone_model
14700−Invokes the phone model which will generate VMAF scores higher than in the
14701−regular model, which is more suitable for laptop, TV, etc. viewing conditions.
14702−Default value: @code{false}
14694+Deprecated, use model='enable_transform=true'.
14695+
14696+@item enable_conf_interval
14697+Deprecated, use model='enable_conf_interval=true'.
14698+
14699+@item feature
14700+A `|` delimited list of features. Each feature can be configured with a number of parameters.
1470314701
1470414702 @item psnr
14705−Enables computing psnr along with vmaf.
14706−Default value: @code{false}
14703+Deprecated, use feature='name=psnr'.
1470714704
1470814705 @item ssim
14709−Enables computing ssim along with vmaf.
14710−Default value: @code{false}
14706+Deprecated, use feature='name=ssim'.
1471114707
1471214708 @item ms_ssim
14713−Enables computing ms_ssim along with vmaf.
14714−Default value: @code{false}
14709+Deprecated, use feature='name=ms_ssim'.
1471514710
14716−@item pool
14717−Set the pool method to be used for computing vmaf.
14718−Options are @code{min}, @code{harmonic_mean} or @code{mean} (default).
14711+@item log_path
14712+Set the file path to be used to store log files.
14713+
14714+@item log_fmt
14715+Set the format of the log file (xml, json, csv, or sub).
1471914716
1472014717 @item n_threads
14721−Set number of threads to be used when computing vmaf.
14722−Default value: @code{0}, which makes use of all available logical processors.
14718+Set number of threads to be used when initializing libvmaf.
14719+Default value: @code{0}, no threads.
1472314720
1472414721 @item n_subsample
14725−Set interval for frame subsampling used when computing vmaf.
14726−Default value: @code{1}
14727−
14728−@item enable_conf_interval
14729−Enables confidence interval.
14730−Default value: @code{false}
14722+Set frame subsampling interval to be used.
1473114723 @end table
1473214724
1473314725 This filter also supports the @ref{framesync} options.
@@ -14735,23 +14727,31 @@ This filter also supports the @ref{framesync} options. · 1 unmodified line
1473514727 @subsection Examples
1473614728 @itemize
1473714729 @item
14738−On the below examples the input file @file{main.mpg} being processed is
14739−compared with the reference file @file{ref.mpg}.
14730+In the examples below, a distorted video @file{distorted.mpg} is
14731+compared with a reference file @file{reference.mpg}.
1474014732
14733+@item
14734+Basic usage:
14735+@example
14736+ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf=log_path=output.xml -f null -
14737+@end example
14738+
14739+@item
14740+Example with multiple models:
1474114741 @example
14742−ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf -f null -
14742+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 -
1474314743 @end example
1474414744
1474514745 @item
14746−Example with options:
14746+Example with multiple addtional features:
1474714747 @example
14748−ffmpeg -i main.mpg -i ref.mpg -lavfi libvmaf="psnr=1:log_fmt=json" -f null -
14748+ffmpeg -i distorted.mpg -i reference.mpg -lavfi libvmaf='feature=name=psnr|name=ciede' -f null -
1474914749 @end example
1475014750
1475114751 @item
1475214752 Example with options and different containers:
1475314753 @example
14754−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 -
14754+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 -
1475514755 @end example
1475614756 @end itemize
1475714757
libavfilter/version.h+1−1
@@ -31,7 +31,7 @@ · 30 unmodified lines
3131
3232 #define LIBAVFILTER_VERSION_MAJOR 8
3333 #define LIBAVFILTER_VERSION_MINOR 26
34−#define LIBAVFILTER_VERSION_MICRO 100
34+#define LIBAVFILTER_VERSION_MICRO 101
3535
3636
3737 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
libavfilter/vf_libvmaf.c+514−168
@@ -24,8 +24,8 @@ · 23 unmodified lines
2424 * Calculate the VMAF between two input videos.
2525 */
2626
27−#include <pthread.h>
2827 #include <libvmaf.h>
28+
2929 #include "libavutil/avstring.h"
3030 #include "libavutil/opt.h"
3131 #include "libavutil/pixdesc.h"
@@ -39,23 +39,9 @@ · 7 unmodified lines
3939 typedef struct LIBVMAFContext {
4040 const AVClass *class;
4141 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;
5442 char *model_path;
5543 char *log_path;
5644 char *log_fmt;
57− int disable_clip;
58− int disable_avx;
5945 int enable_transform;
6046 int phone_model;
6147 int psnr;
@@ -65,184 +51,487 @@ typedef struct LIBVMAFContext { · 3 unmodified lines
6551 int n_threads;
6652 int n_subsample;
6753 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;
6961 } LIBVMAFContext;
7062
7163 #define OFFSET(x) offsetof(LIBVMAFContext, x)
7264 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
7365
7466 static 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},
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},
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},
78− {"enable_transform", "Enables transform for computing vmaf.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
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},
80− {"psnr", "Enables computing psnr along with vmaf.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
81− {"ssim", "Enables computing ssim along with vmaf.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS},
82− {"ms_ssim", "Enables computing ms-ssim along with vmaf.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 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},
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},
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},
70+ {"enable_transform", "use model='enable_transform=true'.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
71+ {"phone_model", "use model='enable_transform=true'.", OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
72+ {"psnr", "use feature='name=psnr'.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
73+ {"ssim", "use feature='name=ssim'.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
74+ {"ms_ssim", "use feature='name=ms_ssim'.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED},
8375 {"pool", "Set the pool method to be used for computing vmaf.", OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS},
8476 {"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},
8577 {"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},
8781 { NULL }
8882 };
8983
9084 FRAMESYNC_DEFINE_CLASS(libvmaf, LIBVMAFContext, fs);
9185
92−#define read_frame_fn(type, bits) \
93− static int read_frame_##bits##bit(float *ref_data, float *main_data, \
94− float *temp_data, int stride, void *ctx) \
95−{ \
96− LIBVMAFContext *s = (LIBVMAFContext *) ctx; \
97− int ret; \
98− \
99− pthread_mutex_lock(&s->lock); \
100− \
101− while (!s->frame_set && !s->eof) { \
102− pthread_cond_wait(&s->cond, &s->lock); \
103− } \
104− \
105− if (s->frame_set) { \
106− int ref_stride = s->gref->linesize[0]; \
107− int main_stride = s->gmain->linesize[0]; \
108− \
109− const type *ref_ptr = (const type *) s->gref->data[0]; \
110− const type *main_ptr = (const type *) s->gmain->data[0]; \
111− \
112− float *ptr = ref_data; \
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; \
86+static enum VmafPixelFormat pix_fmt_map(enum AVPixelFormat av_pix_fmt)
87+{
88+ switch (av_pix_fmt) {
89+ case AV_PIX_FMT_YUV420P:
90+ case AV_PIX_FMT_YUV420P10LE:
91+ case AV_PIX_FMT_YUV420P12LE:
92+ case AV_PIX_FMT_YUV420P16LE:
93+ return VMAF_PIX_FMT_YUV420P;
94+ case AV_PIX_FMT_YUV422P:
95+ case AV_PIX_FMT_YUV422P10LE:
96+ case AV_PIX_FMT_YUV422P12LE:
97+ case AV_PIX_FMT_YUV422P16LE:
98+ return VMAF_PIX_FMT_YUV422P;
99+ case AV_PIX_FMT_YUV444P:
100+ case AV_PIX_FMT_YUV444P10LE:
101+ case AV_PIX_FMT_YUV444P12LE:
102+ case AV_PIX_FMT_YUV444P16LE:
103+ return VMAF_PIX_FMT_YUV444P;
104+ default:
105+ return VMAF_PIX_FMT_UNKNOWN;
106+ }
153107 }
154108
155−read_frame_fn(uint8_t, 8);
156−read_frame_fn(uint16_t, 10);
109+static int copy_picture_data(AVFrame *src, VmafPicture *dst, unsigned bpc)
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+}
157128
158−static void compute_vmaf_score(LIBVMAFContext *s)
129+static int do_vmaf(FFFrameSync *fs)
159130 {
160− int (*read_frame)(float *ref_data, float *main_data, float *temp_data,
161− int stride, void *ctx);
162− char *format;
131+ AVFilterContext *ctx = fs->parent;
132+ LIBVMAFContext *s = ctx->priv;
133+ VmafPicture pic_ref, pic_dist;
134+ AVFrame *ref, *dist;
135+ int err = 0;
163136
164− if (s->desc->comp[0].depth <= 8) {
165− read_frame = read_frame_8bit;
166− } else {
167− read_frame = read_frame_10bit;
137+ int ret = ff_framesync_dualinput_get(fs, &dist, &ref);
138+ if (ret < 0)
139+ return ret;
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);
168154 }
169155
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+ }
171161
172− s->error = compute_vmaf(&s->vmaf_score, format, s->width, s->height,
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);
162+ return ff_filter_frame(ctx->outputs[0], dist);
178163 }
179164
180−static void *call_vmaf(void *ctx)
165+
166+static AVDictionary **delimited_dict_parse(char *str, unsigned *cnt)
181167 {
182− LIBVMAFContext *s = (LIBVMAFContext *) ctx;
183− compute_vmaf_score(s);
184− if (!s->error) {
185− av_log(ctx, AV_LOG_INFO, "VMAF score: %f\n",s->vmaf_score);
186− } else {
187− pthread_mutex_lock(&s->lock);
188− pthread_cond_signal(&s->cond);
189− pthread_mutex_unlock(&s->lock);
168+ AVDictionary **dict = NULL;
169+ char *str_copy = NULL;
170+ char *saveptr = NULL;
171+ unsigned cnt2;
172+ int err = 0;
173+
174+ if (!str)
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+
204+fail:
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);
190211 }
191− pthread_exit(NULL);
212+
213+ av_free(str_copy);
214+ *cnt = 0;
192215 return NULL;
193216 }
194217
195−static int do_vmaf(FFFrameSync *fs)
218+static int parse_features(AVFilterContext *ctx)
196219 {
197− AVFilterContext *ctx = fs->parent;
198220 LIBVMAFContext *s = ctx->priv;
199− AVFrame *master, *ref;
200− int ret;
221+ AVDictionary **dict = NULL;
222+ unsigned dict_cnt;
223+ int err = 0;
201224
202− ret = ff_framesync_dualinput_get(fs, &master, &ref);
203− if (ret < 0)
204− return ret;
205− if (!ref)
206− return ff_filter_frame(ctx->outputs[0], master);
225+ if (!s->feature_cfg)
226+ return 0;
227+
228+ dict = delimited_dict_parse(s->feature_cfg, &dict_cnt);
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+ }
207234
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+ }
209263
210− while (s->frame_set && !s->error) {
211− pthread_cond_wait(&s->cond, &s->lock);
264+exit:
265+ for (unsigned i = 0; i < dict_cnt; i++) {
266+ if (dict[i])
267+ av_dict_free(&dict[i]);
212268 }
269+ av_free(dict);
270+ return err;
271+}
272+
273+static 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;
213281
214− if (s->error) {
282+ dict_cnt = 0;
283+ dict = delimited_dict_parse(s->model_cfg, &dict_cnt);
284+ if (!dict) {
215285 av_log(ctx, AV_LOG_ERROR,
216− "libvmaf encountered an error, check log for details\n");
217− pthread_mutex_unlock(&s->lock);
286+ "could not parse model config: %s\n", s->model_cfg);
218287 return AVERROR(EINVAL);
219288 }
220289
221− av_frame_ref(s->gref, ref);
222− av_frame_ref(s->gmain, master);
290+ s->model_cnt = dict_cnt;
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+
396+exit:
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+
405+static 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+
423+static 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+
474+extra_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+ }
223486
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+ }
225495
226− pthread_cond_signal(&s->cond);
227− pthread_mutex_unlock(&s->lock);
496+ if (s->ms_ssim) {
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+ }
228504
229− return ff_filter_frame(ctx->outputs[0], master);
505+exit:
506+ return err;
230507 }
231508
232509 static av_cold int init(AVFilterContext *ctx)
233510 {
234511 LIBVMAFContext *s = ctx->priv;
512+ int err = 0;
235513
236− s->gref = av_frame_alloc();
237− s->gmain = av_frame_alloc();
238− if (!s->gref || !s->gmain)
239− return AVERROR(ENOMEM);
514+ VmafConfiguration cfg = {
515+ .log_level = log_level_map(av_log_get_level()),
516+ .n_subsample = s->n_subsample,
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;
240527
241− s->error = 0;
528+ err = parse_models(ctx);
529+ if (err)
530+ return err;
242531
243− s->vmaf_thread_created = 0;
244− pthread_mutex_init(&s->lock, NULL);
245− pthread_cond_init (&s->cond, NULL);
532+ err = parse_features(ctx);
533+ if (err)
534+ return err;
246535
247536 s->fs.on_event = do_vmaf;
248537 return 0;
@@ -256,26 +545,31 @@ static const enum AVPixelFormat pix_fmts[] = { · 7 unmodified lines
256545
257546 static int config_input_ref(AVFilterLink *inlink)
258547 {
259− AVFilterContext *ctx = inlink->dst;
548+ AVFilterContext *ctx = inlink->dst;
260549 LIBVMAFContext *s = ctx->priv;
261− int th;
550+ const AVPixFmtDescriptor *desc;
551+ int err = 0;
262552
263− if (ctx->inputs[0]->w != ctx->inputs[1]->w ||
264− ctx->inputs[0]->h != ctx->inputs[1]->h) {
265− av_log(ctx, AV_LOG_ERROR, "Width and height of input videos must be same.\n");
266− return AVERROR(EINVAL);
553+ if (ctx->inputs[0]->w != ctx->inputs[1]->w) {
554+ av_log(ctx, AV_LOG_ERROR, "input width must match.\n");
555+ err |= AVERROR(EINVAL);
267556 }
268557
269− s->desc = av_pix_fmt_desc_get(inlink->format);
270− s->width = ctx->inputs[0]->w;
271− s->height = ctx->inputs[0]->h;
558+ if (ctx->inputs[0]->h != ctx->inputs[1]->h) {
559+ av_log(ctx, AV_LOG_ERROR, "input height must match.\n");
560+ err |= AVERROR(EINVAL);
561+ }
272562
273− th = pthread_create(&s->vmaf_thread, NULL, call_vmaf, (void *) s);
274− if (th) {
275− av_log(ctx, AV_LOG_ERROR, "Thread creation failed.\n");
276− return AVERROR(EINVAL);
563+ if (ctx->inputs[0]->format != ctx->inputs[1]->format) {
564+ av_log(ctx, AV_LOG_ERROR, "input pix_fmt must match.\n");
565+ err |= AVERROR(EINVAL);
277566 }
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;
279573
280574 return 0;
281575 }
@@ -307,28 +601,80 @@ static int activate(AVFilterContext *ctx) · 25 unmodified lines
307601 return ff_framesync_activate(&s->fs);
308602 }
309603
604+static 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+
620+static 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+
310634 static av_cold void uninit(AVFilterContext *ctx)
311635 {
312636 LIBVMAFContext *s = ctx->priv;
637+ int err = 0;
313638
314639 ff_framesync_uninit(&s->fs);
315640
316− pthread_mutex_lock(&s->lock);
317− s->eof = 1;
318− pthread_cond_signal(&s->cond);
319− pthread_mutex_unlock(&s->lock);
641+ if (!s->frame_cnt)
642+ goto clean_up;
320643
321− if (s->vmaf_thread_created)
322− {
323− pthread_join(s->vmaf_thread, NULL);
324− s->vmaf_thread_created = 0;
644+ err = vmaf_read_pictures(s->vmaf, NULL, NULL, 0);
645+ if (err) {
646+ av_log(ctx, AV_LOG_ERROR,
647+ "problem flushing libvmaf context.\n");
325648 }
326649
327− av_frame_free(&s->gref);
328− av_frame_free(&s->gmain);
650+ for (unsigned i = 0; i < s->model_cnt; i++) {
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+
667+clean_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+ }
329675
330− pthread_mutex_destroy(&s->lock);
331− pthread_cond_destroy(&s->cond);
676+ if (s->vmaf)
677+ vmaf_close(s->vmaf);
332678 }
333679
334680 static 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