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
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