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

Showing the changes to libavfilter (2 of 4 files). Show all

Unified · Split

2 files
  1. libavfilter/version.h +1 −1
  2. libavfilter/vf_libvmaf.c +514 −168
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