[FFmpeg-devel] [PATCH] avfilter/vf_perspective: Add support for commands

Simon Binder oss at simonbinder.eu
Thu Mar 31 23:54:42 EEST 2022


Store expressions to avoid parsing them for each change.
Support a command re-assigning all corners for the perspective.

Signed-off-by: Simon Binder <oss at simonbinder.eu>
---
  doc/filters.texi             |  13 ++++
  libavfilter/vf_perspective.c | 114 ++++++++++++++++++++++++++++-------
  2 files changed, 106 insertions(+), 21 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 1d56d24819..4fe4dc04d9 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -17201,6 +17201,19 @@ evaluate expressions for each incoming frame
  Default value is @samp{init}.
  @end table
  + at subsection Commands
+
+This filter supports the following command:
+
+ at table @option
+ at item dims
+Updates all eight coordinate values from one colon-separated parameter.
+ at example
+0.0 [enter] perspective dims 0:0:300:0:0:300:300:300;
+5.0 [enter] perspective dims 0:0:300:50:0:300:300:350;
+ at end example
+ at end table
+
  @section phase
   Delay interlaced video by one field time so that the field order changes.
diff --git a/libavfilter/vf_perspective.c b/libavfilter/vf_perspective.c
index da720dcb54..e81234c7d6 100644
--- a/libavfilter/vf_perspective.c
+++ b/libavfilter/vf_perspective.c
@@ -19,13 +19,13 @@
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
   */
  +#include <libavutil/avstring.h>
  #include "libavutil/avassert.h"
  #include "libavutil/eval.h"
  #include "libavutil/imgutils.h"
  #include "libavutil/pixdesc.h"
  #include "libavutil/opt.h"
  #include "avfilter.h"
-#include "formats.h"
  #include "internal.h"
  #include "video.h"
  @@ -36,9 +36,13 @@
  #define LINEAR 0
  #define CUBIC  1
  +static const char *const var_names[] = {   "W",   "H",   "in",
"on",        NULL };
+enum                                   { VAR_W, VAR_H, VAR_IN, VAR_ON,
VAR_VARS_NB };
+
  typedef struct PerspectiveContext {
      const AVClass *class;
      char *expr_str[4][2];
+    AVExpr* expr[4][2];
      double ref[4][2];
      int32_t (*pv)[2];
      int32_t coeff[SUB_PIXELS][4];
@@ -49,6 +53,7 @@ typedef struct PerspectiveContext {
      int nb_planes;
      int sense;
      int eval_mode;
+    double var_values[VAR_VARS_NB];
       int (*perspective)(AVFilterContext *ctx,
                         void *arg, int job, int nb_jobs);
@@ -117,34 +122,31 @@ static inline double get_coeff(double d)
      return coeff;
  }
  -static const char *const var_names[] = {   "W",   "H",   "in",
"on",        NULL };
-enum                                   { VAR_W, VAR_H, VAR_IN, VAR_ON,
VAR_VARS_NB };
-
-static int calc_persp_luts(AVFilterContext *ctx, AVFilterLink *inlink)
+static void set_variables_for_link(AVFilterContext *ctx, AVFilterLink
*inlink)
  {
      PerspectiveContext *s = ctx->priv;
      AVFilterLink *outlink = ctx->outputs[0];
+
+    s->var_values[VAR_W] = inlink->w;
+    s->var_values[VAR_H] = inlink->h;
+    s->var_values[VAR_IN] = inlink->frame_count_out + 1;
+    s->var_values[VAR_ON] = outlink->frame_count_in + 1;
+}
+
+static int calc_persp_luts(AVFilterContext *ctx)
+{
+    PerspectiveContext *s = ctx->priv;
      double (*ref)[2]      = s->ref;
  -    double values[VAR_VARS_NB] = { [VAR_W] = inlink->w, [VAR_H] =
inlink->h,
-                                   [VAR_IN] = inlink->frame_count_out + 1,
-                                   [VAR_ON] = outlink->frame_count_in +
1 };
-    const int h = values[VAR_H];
-    const int w = values[VAR_W];
+    const int h = s->var_values[VAR_H];
+    const int w = s->var_values[VAR_W];
      double x0, x1, x2, x3, x4, x5, x6, x7, x8, q;
      double t0, t1, t2, t3;
-    int x, y, i, j, ret;
+    int x, y, i, j;
       for (i = 0; i < 4; i++) {
          for (j = 0; j < 2; j++) {
-            if (!s->expr_str[i][j])
-                return AVERROR(EINVAL);
-            ret = av_expr_parse_and_eval(&s->ref[i][j], s->expr_str[i][j],
-                                         var_names, &values[0],
-                                         NULL, NULL, NULL, NULL,
-                                         0, 0, ctx);
-            if (ret < 0)
-                return ret;
+            s->ref[i][j] = av_expr_eval(s->expr[i][j], s->var_values, s);
          }
      }
  @@ -238,8 +240,27 @@ static int config_input(AVFilterLink *inlink)
      if (!s->pv)
          return AVERROR(ENOMEM);
  +    // Parse coordinate expressions
+    for (i = 0; i < 4; i++) {
+        for (j = 0; j < 2; j++) {
+            av_expr_free(s->expr[i][j]);
+            s->expr[i][j] = NULL;
+
+            if (!s->expr_str[i][j])
+                return AVERROR(EINVAL);
+
+            if ((ret = av_expr_parse(&s->expr[i][j], s->expr_str[i][j],
var_names,
+                                     NULL, NULL, NULL, NULL, 0, ctx)) <
0) {
+                av_log(ctx, AV_LOG_ERROR,
+                       "Error occurred parsing coordinate '%s'\n",
s->expr_str[i][j]);
+                return ret;
+            }
+        }
+    }
+
      if (s->eval_mode == EVAL_MODE_INIT) {
-        if ((ret = calc_persp_luts(ctx, inlink)) < 0) {
+        set_variables_for_link(ctx, inlink);
+        if ((ret = calc_persp_luts(ctx)) < 0) {
              return ret;
          }
      }
@@ -437,6 +458,49 @@ static av_cold int init(AVFilterContext *ctx)
      return 0;
  }
  +static int process_command(AVFilterContext *ctx, const char *cmd,
const char *arg,
+                           char *res, int res_len, int flags)
+{
+    PerspectiveContext *s = ctx->priv;
+    int ret;
+    char *token;
+    AVExpr *old;
+
+    if (!strcmp(cmd, "dims")) {
+        // Expect arg to be an 8-token argument containing the new
coordinates
+        for (int i = 0; i < 4; i++) {
+            for (int j = 0; j < 2; j++) {
+                if (!*arg) {
+                    av_log(ctx, AV_LOG_ERROR,"Expected 8 expressions
for the new coordinates\n");
+                    return AVERROR(EINVAL);
+                }
+
+                token = av_get_token(&arg, ":");
+                if (!token)
+                    return AVERROR(ENOMEM);
+                arg++;
+
+                old = s->expr[i][j];
+
+                if ((ret = av_expr_parse(&s->expr[i][j], token, var_names,
+                                         NULL, NULL, NULL, NULL, 0,
ctx)) < 0) {
+                    av_log(ctx, AV_LOG_ERROR,
+                           "Error occurred parsing coordinate '%s'\n",
token);
+                    av_free(token);
+                    return ret;
+                }
+                av_free(token);
+                av_expr_free(old);
+            }
+        }
+
+        ret = calc_persp_luts(ctx);
+    } else
+        ret = AVERROR(ENOSYS);
+
+    return ret;
+}
+
  static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
  {
      AVFilterContext *ctx = inlink->dst;
@@ -454,7 +518,8 @@ static int filter_frame(AVFilterLink *inlink,
AVFrame *frame)
      av_frame_copy_props(out, frame);
       if (s->eval_mode == EVAL_MODE_FRAME) {
-        if ((ret = calc_persp_luts(ctx, inlink)) < 0) {
+        set_variables_for_link(ctx, inlink);
+        if ((ret = calc_persp_luts(ctx)) < 0) {
              av_frame_free(&out);
              return ret;
          }
@@ -484,6 +549,12 @@ static av_cold void uninit(AVFilterContext *ctx)
      PerspectiveContext *s = ctx->priv;
       av_freep(&s->pv);
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 2; j++) {
+            av_expr_free(s->expr[i][j]);
+            s->expr[i][j] = NULL;
+        }
+    }
  }
   static const AVFilterPad perspective_inputs[] = {
@@ -508,6 +579,7 @@ const AVFilter ff_vf_perspective = {
      .priv_size     = sizeof(PerspectiveContext),
      .init          = init,
      .uninit        = uninit,
+    .process_command = &process_command,
      FILTER_INPUTS(perspective_inputs),
      FILTER_OUTPUTS(perspective_outputs),
      FILTER_PIXFMTS_ARRAY(pix_fmts),
--
2.35.1




More information about the ffmpeg-devel mailing list