[FFmpeg-devel] [PATCH] avfilter/vf_cropdetect: add ability to change limit/reset at runtime

Jeffrey CHAPUIS ashyni1987 at gmail.com
Wed Dec 28 17:37:30 EET 2022


> You need a custom function that will keep old values around and 
> realloc the buffers for bboxes using the new reset_count value passed 
> as a runtime command, but one that will not call config_input() like 
> you did the first time as that one does a lot more than what you need.
>
> It should also call init() to reset frame_nb, a value you of course 
> also need to preserve for the fallback scenario, and allocate the new 
> buffers but only replace them in the filter context if all four 
> allocations succeeded, as doing av_realloc() could potentially not let 
> you fallback to continue the process with the old values if required.

Here is my last attempt to do it myself, i don't want to waste 
everyone's time, i barely understand what i code, without proper 
knowledge in c/c++ and ffmpeg project.

diff --git a/libavfilter/vf_cropdetect.c b/libavfilter/vf_cropdetect.c
index 7e985fb27..f4d2f1379 100644
--- a/libavfilter/vf_cropdetect.c
+++ b/libavfilter/vf_cropdetect.c
@@ -422,26 +422,65 @@ static int filter_frame(AVFilterLink *inlink, 
AVFrame *frame)
          SET_META("lavfi.cropdetect.h",  h);
          SET_META("lavfi.cropdetect.x",  x);
          SET_META("lavfi.cropdetect.y",  y);
+        SET_META("lavfi.cropdetect.pts", frame->pts);
+        SET_META("lavfi.cropdetect.limit", limit);
+        SET_META("lavfi.cropdetect.reset", s->reset_count);

          av_log(ctx, AV_LOG_INFO,
-               "x1:%d x2:%d y1:%d y2:%d w:%d h:%d x:%d y:%d 
pts:%"PRId64" t:%f crop=%d:%d:%d:%d\n",
+               "x1:%d x2:%d y1:%d y2:%d w:%d h:%d x:%d y:%d 
pts:%"PRId64" t:%f limit:%d crop=%d:%d:%d:%d\n",
                 s->x1, s->x2, s->y1, s->y2, w, h, x, y, frame->pts,
                 frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * 
av_q2d(inlink->time_base),
-               w, h, x, y);
+               limit, w, h, x, y);
      }

      return ff_filter_frame(inlink->dst->outputs[0], frame);
  }

+static int process_command(AVFilterContext *ctx, const char *cmd, const 
char *args,
+                           char *res, int res_len, int flags)
+{
+    CropDetectContext *s = ctx->priv;
+    AVFilterLink *inlink = ctx->inputs[0];
+    int old_limit = s->limit;
+    int old_reset_count = s->reset_count;
+    int old_frame_nb = s->frame_nb;
+    int ret;
+
+    if ((ret = ff_filter_process_command(ctx, cmd, args, res, res_len, 
flags)) < 0)
+        return ret;
+
+    init;
+
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    const int bufsize = inlink->w * inlink->h;
+    av_image_fill_max_pixsteps(s->max_pixsteps, NULL, desc);
+    if (s->limit < 1.0)
+        s->limit *= (1 << desc->comp[0].depth) - 1;
+
+    s->window_size = FFMAX(s->reset_count, 15);
+    if ((ret = (av_realloc(s->filterbuf, bufsize * s->max_pixsteps[0])
+        || av_realloc(s->bboxes[0], s->window_size * sizeof(*s->bboxes[0]))
+        || av_realloc(s->bboxes[1], s->window_size * sizeof(*s->bboxes[1]))
+        || av_realloc(s->bboxes[2], s->window_size * sizeof(*s->bboxes[2]))
+        || av_realloc(s->bboxes[3], s->window_size * 
sizeof(*s->bboxes[3])))) < 0) {
+        s->limit = old_limit;
+        s->reset_count = old_reset_count;
+        s->frame_nb = old_frame_nb;
+    }
+
+    return ret;
+}
+
  #define OFFSET(x) offsetof(CropDetectContext, x)
  #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM | 
AV_OPT_FLAG_RUNTIME_PARAM

  static const AVOption cropdetect_options[] = {
-    { "limit", "Threshold below which the pixel is considered black", 
OFFSET(limit),       AV_OPT_TYPE_FLOAT, { .dbl = 24.0/255 }, 0, 65535, 
FLAGS },
+    { "limit", "Threshold below which the pixel is considered black", 
OFFSET(limit),       AV_OPT_TYPE_FLOAT, { .dbl = 24.0/255 }, 0, 65535, 
TFLAGS },
      { "round", "Value by which the width/height should be divisible", 
OFFSET(round),       AV_OPT_TYPE_INT, { .i64 = 16 }, 0, INT_MAX, FLAGS },
-    { "reset", "Recalculate the crop area after this many frames",    
OFFSET(reset_count), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
+    { "reset", "Recalculate the crop area after this many frames",    
OFFSET(reset_count), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, TFLAGS },
      { "skip",  "Number of initial frames to skip",                    
OFFSET(skip),        AV_OPT_TYPE_INT, { .i64 = 2 },  0, INT_MAX, FLAGS },
-    { "reset_count", "Recalculate the crop area after this many 
frames",OFFSET(reset_count),AV_OPT_TYPE_INT,{ .i64 = 0 },  0, INT_MAX, 
FLAGS },
+    { "reset_count", "Recalculate the crop area after this many 
frames",OFFSET(reset_count),AV_OPT_TYPE_INT,{ .i64 = 0 },  0, INT_MAX, 
TFLAGS },
      { "max_outliers", "Threshold count of outliers",                  
OFFSET(max_outliers),AV_OPT_TYPE_INT, { .i64 = 0 },  0, INT_MAX, FLAGS },
      { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, 
{.i64=MODE_BLACK}, 0, MODE_NB-1, FLAGS, "mode" },
          { "black",    "detect black pixels surrounding the video",     
0, AV_OPT_TYPE_CONST, {.i64=MODE_BLACK},    INT_MIN, INT_MAX, FLAGS, 
"mode" },
@@ -481,4 +520,5 @@ const AVFilter ff_vf_cropdetect = {
      FILTER_OUTPUTS(avfilter_vf_cropdetect_outputs),
      FILTER_PIXFMTS_ARRAY(pix_fmts),
      .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | 
AVFILTER_FLAG_METADATA_ONLY,
+    .process_command = process_command,
  };


> Why are you adding frame pts as metadata? Or the detection parameters? 
> Those are not detected, so have no business being there in metadata or 
> log.
>
> Regards,
> Marton 
It make sense for my lua script, https://github.com/Ashyni/mpv-scripts.
For pts, it's because mpv doesn't provide the frame/pts/pts_time data 
alongside others tag field in the metadata, may be an issue should be 
open for mpv to change that.
As for the parameters, It's added as a reference to be sure when i look 
at the metadata that the result come from the new limit and not the 
previous one, same for reset.
It's the only way i can be sure of the result without waiting an 
arbitrary time between every change to limit/reset.

In the end, the goal is to analyzed data ahead with something like :

ffmpeg -i <input> -filter_complex 
'split[a1][b1];[b1]setpts=PTS-2/TB,cropdetect at cd1=reset=1[b2];[b2][a1]overlay,setpts=PTS-2/TB,cropdetect at cd2=reset=1' 
-f null -

with mpv : mp.set_property_native("lavfi-complex", 
"[vid1]split[a_1][b_1];[b_1]setpts=PTS-2/TB,cropdetect at cd1=reset=1[b_2];[b_2][a_1]overlay,setpts=PTS-2/TB,cropdetect at cd2=reset=1[vo]")

but that another topic, and mpv have some issue anyway with vf-command 
and graph/lavfi-complex.

Reminder, that was an attempt for https://trac.ffmpeg.org/ticket/9851, 
if someone wants to take over.
Thanks for your time everyone.



More information about the ffmpeg-devel mailing list