[FFmpeg-devel] [PATCH] lavfi/overlay: add dynamic x/y expression evaluation support

Stefano Sabatini stefasab at gmail.com
Fri Apr 5 01:14:42 CEST 2013


On date Thursday 2013-04-04 10:40:30 +0200, Clément Bœsch encoded:
> On Thu, Apr 04, 2013 at 12:23:45AM +0200, Stefano Sabatini wrote:
> > On date Tuesday 2013-02-19 20:10:50 +0100, Stefano Sabatini encoded:
> > > TODO: bump micro
> > > ---
> > >  doc/filters.texi         |   59 ++++++++++++++++++++--------
> > >  libavfilter/vf_overlay.c |   98 +++++++++++++++++++++++++++++++---------------
> > >  2 files changed, 109 insertions(+), 48 deletions(-)
> > 
> > Updated, with the possibility to disable per-frame evaluation.
> > -- 
> > FFmpeg = Fundamental and Fast Murdering Picky Enlightening Gorilla
> 
> > From b90270220a0d813819860a12ffcc20f16ef785ac Mon Sep 17 00:00:00 2001
> > From: Stefano Sabatini <stefasab at gmail.com>
> > Date: Tue, 19 Feb 2013 20:10:02 +0100
> > Subject: [PATCH] lavfi/overlay: add dynamic expression evaluation support
> > 
> > Add support for dynamic x, y, enable expression evaluation.
> > 
> > Also add support for an evaluation mode which allows to disable per-frame
> > evaluation, so that there is no speedloss in case the expression does not
> > depend on frame variables.
> > 
> > TODO: bump micro
> > ---
> >  doc/filters.texi         |   83 ++++++++++++++++++++++------
> >  libavfilter/vf_overlay.c |  137 ++++++++++++++++++++++++++++++++++------------
> >  2 files changed, 170 insertions(+), 50 deletions(-)
> > 
> [...]
> > diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c
> > index 0dcd0b8..23afe5a 100644
> > --- a/libavfilter/vf_overlay.c
> > +++ b/libavfilter/vf_overlay.c
> > @@ -47,6 +47,13 @@ static const char *const var_names[] = {
> >      "main_h",    "H", ///< height of the main    video
> >      "overlay_w", "w", ///< width  of the overlay video
> >      "overlay_h", "h", ///< height of the overlay video
> > +    "hsub",
> > +    "vsub",
> 
> I would prefer something similar to SW/SH like in geq; that's easier for
> users to deal with IMO.
> > +    "x",
> > +    "y",
> > +    "n",            ///< number of frame
> > +    "pos",          ///< position in the file
> > +    "t",            ///< timestamp expressed in seconds
> >      NULL
> >  };
> >  
> > @@ -55,6 +62,13 @@ enum var_name {
> >      VAR_MAIN_H,    VAR_MH,
> >      VAR_OVERLAY_W, VAR_OW,
> >      VAR_OVERLAY_H, VAR_OH,
> > +    VAR_HSUB,
> > +    VAR_VSUB,
> > +    VAR_X,
> > +    VAR_Y,
> > +    VAR_N,
> > +    VAR_POS,
> > +    VAR_T,
> >      VAR_VARS_NB
> >  };
> >  
> > @@ -73,6 +87,7 @@ enum var_name {
> >  typedef struct {
> >      const AVClass *class;
> >      int x, y;                   ///< position of overlayed picture
> > +    int enable;                 ///< tells if blending is enabled
> >  
> >      int allow_packed_rgb;
> >      uint8_t frame_requested;
> > @@ -84,6 +99,7 @@ typedef struct {
> >      uint8_t overlay_rgba_map[4];
> >      uint8_t overlay_has_alpha;
> >      enum OverlayFormat { OVERLAY_FORMAT_YUV420, OVERLAY_FORMAT_YUV444, OVERLAY_FORMAT_RGB, OVERLAY_FORMAT_NB} format;
> > +    enum EvalMode { EVAL_MODE_INIT, EVAL_MODE_FRAME, EVAL_MODE_NB } eval_mode;
> >  
> >      AVFrame *overpicref;
> >      struct FFBufQueue queue_main;
> > @@ -94,7 +110,9 @@ typedef struct {
> >      int hsub, vsub;             ///< chroma subsampling values
> >      int shortest;               ///< terminate stream when the shortest input terminates
> >  
> > -    char *x_expr, *y_expr;
> > +    double var_values[VAR_VARS_NB];
> > +    char *x_expr, *y_expr, *enable_expr;
> > +    AVExpr *x_pexpr, *y_pexpr, *enable_pexpr;
> >  } OverlayContext;
> >  
> >  #define OFFSET(x) offsetof(OverlayContext, x)
> > @@ -103,6 +121,12 @@ typedef struct {
> >  static const AVOption overlay_options[] = {
> >      { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS },
> >      { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS },
> > +    { "enable", "set expression which enables overlay", OFFSET(enable_expr), AV_OPT_TYPE_STRING, {.str = "1"}, .flags = FLAGS },
> > +
> > +    { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" },
> > +    { "init",  "eval expressions during init", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT},  .flags = FLAGS, .unit = "eval" },
> > +    { "frame", "eval expressions per-frame",   0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
> > +
> 
> nit+++: it's nice to have an indent of the values to better see where they
> start/end.

Kept this way for internal consistency, feel free to do it in a
subsequent patch.

> 
> Also, I wonder if we will have an "auto" mode with av_eval_is_const soon
> :)

Yes that's the plan, even if I'm not sure it will useful only for a
very limited use case (no variables in the expression, even the ones
which are constant).

> 
> [...]
> >  static int config_input_overlay(AVFilterLink *inlink)
> >  {
> >      AVFilterContext *ctx  = inlink->dst;
> >      OverlayContext  *over = inlink->dst->priv;
> >      char *expr;
> > -    double var_values[VAR_VARS_NB], res;
> >      int ret;
> >      const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
> >  
> > @@ -230,53 +263,62 @@ static int config_input_overlay(AVFilterLink *inlink)
> >  
> >      /* Finish the configuration by evaluating the expressions
> >         now when both inputs are configured. */
> > -    var_values[VAR_MAIN_W   ] = var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
> > -    var_values[VAR_MAIN_H   ] = var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
> > -    var_values[VAR_OVERLAY_W] = var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
> > -    var_values[VAR_OVERLAY_H] = var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
> > -
> > -    if ((ret = av_expr_parse_and_eval(&res, (expr = over->x_expr), var_names, var_values,
> > -                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
> > +    over->var_values[VAR_MAIN_W   ] = over->var_values[VAR_MW] = ctx->inputs[MAIN   ]->w;
> > +    over->var_values[VAR_MAIN_H   ] = over->var_values[VAR_MH] = ctx->inputs[MAIN   ]->h;
> > +    over->var_values[VAR_OVERLAY_W] = over->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w;
> > +    over->var_values[VAR_OVERLAY_H] = over->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h;
> > +    over->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
> > +    over->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
> > +    over->var_values[VAR_X]     = NAN;
> > +    over->var_values[VAR_Y]     = NAN;
> > +    over->var_values[VAR_N]     = 0;
> > +    over->var_values[VAR_T]     = NAN;
> > +    over->var_values[VAR_POS]   = NAN;
> > +
> > +    expr = over->x_expr;
> > +    if ((ret = av_expr_parse(&over->x_pexpr, expr, var_names,
> > +                             NULL, NULL, NULL, NULL, 0, ctx)) < 0)
> >          goto fail;
> > -    over->x = res;
> > -    if ((ret = av_expr_parse_and_eval(&res, (expr = over->y_expr), var_names, var_values,
> > -                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)))
> > +    expr = over->y_expr;
> > +    if ((ret = av_expr_parse(&over->y_pexpr, expr, var_names,
> > +                             NULL, NULL, NULL, NULL, 0, ctx)) < 0)
> >          goto fail;
> > -    over->y = res;
> > -    /* x may depend on y */
> > -    if ((ret = av_expr_parse_and_eval(&res, (expr = over->x_expr), var_names, var_values,
> > -                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
> > +    expr = over->enable_expr;
> > +    if ((ret = av_expr_parse(&over->enable_pexpr, expr, var_names,
> > +                             NULL, NULL, NULL, NULL, 0, ctx)) < 0)
> >          goto fail;
> > -    over->x = res;
> >  
> >      over->overlay_is_packed_rgb =
> >          ff_fill_rgba_map(over->overlay_rgba_map, inlink->format) >= 0;
> >      over->overlay_has_alpha = ff_fmt_is_in(inlink->format, alpha_pix_fmts);
> >  
> > +    if (over->eval_mode == EVAL_MODE_INIT) {
> > +        double enable = av_expr_eval(over->enable_pexpr, over->var_values, NULL);
> > +        over->var_values[VAR_X] = av_expr_eval(over->x_pexpr, over->var_values, NULL);
> > +        over->var_values[VAR_Y] = av_expr_eval(over->y_pexpr, over->var_values, NULL);
> > +        over->var_values[VAR_X] = av_expr_eval(over->x_pexpr, over->var_values, NULL);
> > +
> > +        over->x = normalize_xy(over->var_values[VAR_X], over->hsub);
> > +        over->y = normalize_xy(over->var_values[VAR_Y], over->vsub);
> > +        over->enable = isnan(enable) ? 0 : enable != 0;
> > +
> > +        av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d enable:%f enablei:%d\n",
> > +               over->var_values[VAR_X], over->x,
> > +               over->var_values[VAR_Y], over->y,
> > +               enable, over->enable);
> > +    }
> > +
> >      av_log(ctx, AV_LOG_VERBOSE,
> > -           "main w:%d h:%d fmt:%s overlay x:%d y:%d w:%d h:%d fmt:%s\n",
> > +           "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s\n",
> >             ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h,
> >             av_get_pix_fmt_name(ctx->inputs[MAIN]->format),
> > -           over->x, over->y,
> >             ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h,
> >             av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format));
> > -
> > -    if (over->x < 0 || over->y < 0 ||
> > -        over->x + var_values[VAR_OVERLAY_W] > var_values[VAR_MAIN_W] ||
> > -        over->y + var_values[VAR_OVERLAY_H] > var_values[VAR_MAIN_H]) {
> > -        av_log(ctx, AV_LOG_WARNING,
> > -               "Overlay area with coordinates x1:%d y1:%d x2:%d y2:%d "
> > -               "is not completely contained within the output with size %dx%d\n",
> > -               over->x, over->y,
> > -               (int)(over->x + var_values[VAR_OVERLAY_W]),
> > -               (int)(over->y + var_values[VAR_OVERLAY_H]),
> > -               (int)var_values[VAR_MAIN_W], (int)var_values[VAR_MAIN_H]);
> > -    }
> >      return 0;
> >  
> >  fail:
> >      av_log(NULL, AV_LOG_ERROR,
> > -           "Error when evaluating the expression '%s'\n", expr);
> > +           "Error when parsing the expression '%s'\n", expr);
> 
> Note: I wonder if that's necessary; eval functions should print such
> error.

Kept since it is needed to show which expression failed. Will be
removed in a pending patch.

> 
> >      return ret;
> >  }
> >  
> > @@ -495,6 +537,7 @@ static void blend_image(AVFilterContext *ctx,
> >  static int try_filter_frame(AVFilterContext *ctx, AVFrame *mainpic)
> >  {
> >      OverlayContext *over = ctx->priv;
> > +    AVFilterLink *inlink = ctx->inputs[0];
> >      AVFrame *next_overpic;
> >      int ret;
> >  
> > @@ -526,8 +569,34 @@ static int try_filter_frame(AVFilterContext *ctx, AVFrame *mainpic)
> >                  av_ts2str(over->overpicref->pts), av_ts2timestr(over->overpicref->pts, &ctx->inputs[OVERLAY]->time_base));
> >      av_dlog(ctx, "\n");
> >  
> > -    if (over->overpicref)
> > -        blend_image(ctx, mainpic, over->overpicref, over->x, over->y);
> > +    if (over->overpicref) {
> > +        if (over->eval_mode == EVAL_MODE_FRAME) {
> > +            int64_t pos = av_frame_get_pkt_pos(mainpic);
> > +            double enable;
> > +
> > +            over->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
> > +                NAN : mainpic->pts * av_q2d(inlink->time_base);
> > +            over->var_values[VAR_POS] = pos == -1 ? NAN : pos;
> > +            over->var_values[VAR_X] = av_expr_eval(over->x_pexpr, over->var_values, NULL);
> > +            over->var_values[VAR_Y] = av_expr_eval(over->y_pexpr, over->var_values, NULL);
> > +            over->var_values[VAR_X] = av_expr_eval(over->x_pexpr, over->var_values, NULL);
> > +            enable = av_expr_eval(over->enable_pexpr, over->var_values, NULL);
> > +
> > +            over->x = normalize_xy(over->var_values[VAR_X], over->hsub);
> > +            over->y = normalize_xy(over->var_values[VAR_Y], over->vsub);
> > +            over->enable = isnan(enable) ? 0 : enable != 0;
> > +
> > +            av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d enable:%f\n",
> > +                   over->var_values[VAR_N], over->var_values[VAR_T], over->var_values[VAR_POS],
> > +                   over->var_values[VAR_X], over->x,
> > +                   over->var_values[VAR_Y], over->y,
> > +                   enable);
> > +        }
> > +        if (over->enable)
> > +            blend_image(ctx, mainpic, over->overpicref, over->x, over->y);
> > +
> > +        over->var_values[VAR_N] += 1.0;
> > +    }
> 
> Can't this be factorized a little with the code in the init eval?

Done.
-- 
FFmpeg = Friendly and Fiendish Minimalistic Practical Earthshaking God
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0003-lavfi-overlay-add-dynamic-expression-evaluation-supp.patch
Type: text/x-diff
Size: 14491 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130405/a886ee2e/attachment.bin>


More information about the ffmpeg-devel mailing list