PR #24978 opened by Kilian (lusoris)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24978
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24978.patch
avfilter/vf_libvmaf: add max pool method, reject unknown ones
`pool=max` is not accepted by vf_libvmaf, although libvmaf has had
`VMAF_POOL_METHOD_MAX` since 2.0.0 (the minimum version configure requires).
Worse, any unrecognized `pool` string silently falls back to `mean`, so
`pool=max` (or a typo like `pool=Mean`) prints the mean score with no hint.
This PR adds `max` and makes an unknown value fail at init with
`AVERROR(EINVAL)` and a message listing the valid values. The check runs for
both `libvmaf` and `libvmaf_cuda`. The option is documented in
`doc/filters.texi`.
Behaviour change: a script that passes an invalid `pool` value used to run and
report the mean. It now fails to start. I think that is the right trade, since
the old result was wrong without saying so, but it is the one thing to weigh in
review.
Test (CPU, libvmaf 3.2.0, 50 frames):
ffmpeg -f lavfi -i "testsrc2=s=320x240:d=2,noise=alls=20:allf=t" \
-f lavfi -i testsrc2=s=320x240:d=2 \
-filter_complex
"[0:v][1:v]libvmaf=pool=max:log_fmt=json:log_path=v.json" -f null -
Per-frame scores in `v.json`: mean 85.746642, max 87.417440.
| pool | before | after |
| --- | --- | --- |
| max | 85.746642 (the mean) | 87.417440 (the max) |
| mean | 85.746642 | 85.746642 |
| min | 84.101864 | 84.101864 |
| harmonic_mean | 85.741703 | 85.741703 |
| bogus | 85.746642 | error, exit 234 (EINVAL) |
Not tested: the `libvmaf_cuda` hunk (three lines, same as the `libvmaf` one)
was not compiled here, as I have no CUDA libvmaf build.
Related: #24973 touches the error paths of the same file; the two are
independent, the only nearby hunk is `init_cuda()`.
>From 791fe64a8b8edcd012000f8bae2d82bdbc60d865 Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Fri, 9 Oct 2026 19:58:34 +0200
Subject: [PATCH] avfilter/vf_libvmaf: add max pool method, reject unknown ones
libvmaf has supported VMAF_POOL_METHOD_MAX since 2.0.0, which is the
minimum version required by configure, but the pool option only
accepted min, mean and harmonic_mean.
Add "max". Also stop mapping any unrecognized pool string to mean
silently: pool=max, or a typo such as pool=Mean, used to print the
mean score without any hint. Validate the option at init time, for
both libvmaf and libvmaf_cuda, and fail with AVERROR(EINVAL) and a
message listing the valid values.
Tested with two 2 s 320x240 testsrc2 inputs, one with noise (50
frames, libvmaf 3.2.0). Before, pool=max printed 85.746642, the mean
of the per-frame scores in the json log. After, it prints 87.417440,
the maximum of those scores. mean, min and harmonic_mean print the
same values as before, and pool=bogus now exits with EINVAL.
Signed-off-by: lusoris <[email protected]>
---
doc/filters.texi | 3 ++-
libavfilter/vf_libvmaf.c | 51 +++++++++++++++++++++++++++++-----------
2 files changed, 39 insertions(+), 15 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index 406ead7198..8a1aa0f8bf 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -17835,7 +17835,8 @@ Set the format of the log file (xml, json, csv, or sub).
@item pool
Set the pool method to be used for computing vmaf.
-Options are @code{min}, @code{harmonic_mean} or @code{mean} (default).
+Options are @code{min}, @code{max}, @code{harmonic_mean} or @code{mean}
(default).
+An unknown value makes the filter fail to initialize.
@item n_threads
Set number of threads to be used when initializing libvmaf.
diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c
index 3ab67d3dba..7d08a62440 100644
--- a/libavfilter/vf_libvmaf.c
+++ b/libavfilter/vf_libvmaf.c
@@ -426,6 +426,35 @@ static enum VmafLogLevel log_level_map(int log_level)
}
}
+static enum VmafPoolingMethod pool_method_map(const char *pool_method)
+{
+ if (!pool_method)
+ return VMAF_POOL_METHOD_MEAN;
+ if (!strcmp(pool_method, "min"))
+ return VMAF_POOL_METHOD_MIN;
+ if (!strcmp(pool_method, "max"))
+ return VMAF_POOL_METHOD_MAX;
+ if (!strcmp(pool_method, "mean"))
+ return VMAF_POOL_METHOD_MEAN;
+ if (!strcmp(pool_method, "harmonic_mean"))
+ return VMAF_POOL_METHOD_HARMONIC_MEAN;
+
+ return VMAF_POOL_METHOD_UNKNOWN;
+}
+
+static av_cold int check_pool_method(AVFilterContext *ctx)
+{
+ LIBVMAFContext *s = ctx->priv;
+
+ if (pool_method_map(s->pool) == VMAF_POOL_METHOD_UNKNOWN) {
+ av_log(ctx, AV_LOG_ERROR, "Unknown pool method '%s', "
+ "must be one of min, max, mean, harmonic_mean.\n", s->pool);
+ return AVERROR(EINVAL);
+ }
+
+ return 0;
+}
+
static av_cold int init(AVFilterContext *ctx)
{
LIBVMAFContext *s = ctx->priv;
@@ -437,6 +466,10 @@ static av_cold int init(AVFilterContext *ctx)
.n_threads = s->n_threads,
};
+ err = check_pool_method(ctx);
+ if (err < 0)
+ return err;
+
err = vmaf_init(&s->vmaf, cfg);
if (err)
return AVERROR(EINVAL);
@@ -537,20 +570,6 @@ static enum VmafOutputFormat log_fmt_map(const char
*log_fmt)
return VMAF_OUTPUT_FORMAT_XML;
}
-static enum VmafPoolingMethod pool_method_map(const char *pool_method)
-{
- if (pool_method) {
- if (!strcmp(pool_method, "min"))
- return VMAF_POOL_METHOD_MIN;
- if (!strcmp(pool_method, "mean"))
- return VMAF_POOL_METHOD_MEAN;
- if (!strcmp(pool_method, "harmonic_mean"))
- return VMAF_POOL_METHOD_HARMONIC_MEAN;
- }
-
- return VMAF_POOL_METHOD_MEAN;
-}
-
static av_cold void uninit(AVFilterContext *ctx)
{
LIBVMAFContext *s = ctx->priv;
@@ -799,6 +818,10 @@ static int do_vmaf_cuda(FFFrameSync* fs)
static av_cold int init_cuda(AVFilterContext *ctx)
{
LIBVMAFContext *s = ctx->priv;
+ int err = check_pool_method(ctx);
+ if (err < 0)
+ return err;
+
s->fs.on_event = do_vmaf_cuda;
return 0;
}
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]