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

Paul B Mahol onemda at gmail.com
Wed Feb 20 15:58:51 CET 2013


On 2/19/13, Stefano Sabatini <stefasab at gmail.com> wrote:
> TODO: bump micro
> ---
>  doc/filters.texi         |   59 ++++++++++++++++++++--------
>  libavfilter/vf_overlay.c |   98
> +++++++++++++++++++++++++++++++---------------
>  2 files changed, 109 insertions(+), 48 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 7b45ba4..5dd7404 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -3854,33 +3854,53 @@ arguments are interpreted according to the syntax
> @var{x}:@var{y}.
>  A description of the accepted options follows.
>
>  @table @option
> - at item x, y
> + at item x
> + at item y
>  Set the expression for the x and y coordinates of the overlayed video
> -on the main video. Default value is 0.
> +on the main video. These values are evaluated for each new output
> +frame. Default value is "0" for both expressions. In case the
> +expression is invalid, it is set to a huge value (meaning that the
> +overlay will not be displayed within the output visible area).
> +
> + at item rgb
> +If set to 1, force the filter to accept inputs in the RGB
> +color space. Default value is 0.
> +
> + at item shortest
> +If set to 1, force the output to terminate when the shortest input
> +terminates. Default value is 0.
> + at end table

Huh, I saw this in some other patch.

>
>  The @var{x} and @var{y} expressions can contain the following
>  parameters:
>  @table @option
> - at item main_w, main_h
> + at item main_w, W
> + at item main_h, H
>  main input width and height
>
> - at item W, H
> -same as @var{main_w} and @var{main_h}
> -
> - at item overlay_w, overlay_h
> + at item overlay_w, w
> + at item overlay_h, h
>  overlay input width and height
>
> - at item w, h
> -same as @var{overlay_w} and @var{overlay_h}
> - at end table
> + at item x
> + at item y
> +the computed values for @var{x} and @var{y}. They are evaluated for
> +each new frame.
>
> - at item rgb
> -If set to 1, force the filter to accept inputs in the RGB
> -color space. Default value is 0.
> + at item hsub
> + at item vsub
> +horizontal and vertical chroma subsample values of the output
> +format. For example for the pixel format "yuv422p" @var{hsub} is 2 and
> + at var{vsub} is 1.
>
> - at item shortest
> -If set to 1, force the output to terminate when the shortest input
> -terminates. Default value is 0.
> + at item n
> +the number of input frame, starting from 0
> +
> + at item pos
> +the position in the file of the input frame, NAN if unknown

In what unit? bytes?

> +
> + at item t
> +timestamp expressed in seconds, NAN if the input timestamp is unknown
>  @end table
>
>  Be aware that frames are taken from each input video in timestamp
> @@ -3941,6 +3961,13 @@ ffplay input.avi -vf 'split[b], pad=iw*2[src],
> [b]deshake, [src]overlay=w'
>  @end example
>
>  @item
> +Make a sliding overlay appearing from the left to the right top part of
> the
> +screen starting since time 2:
> + at example
> +overlay=x='if(gte(t,2), -w+(t-2)*20, NAN)':y=0
> + at end example
> +
> + at item
>  Create a side by side video, combining left and right videos side to side:
>  @example
>  ffmpeg -i left.avi -i right.avi -filter_complex "
> diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c
> index 607c02d..166c376 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",
> +    "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
>  };
>
> @@ -93,7 +107,9 @@ typedef struct {
>      int hsub, vsub;             ///< chroma subsampling values
>      int shortest;               ///< terminate stream when the shortest
> input terminates
>
> +    double var_values[VAR_VARS_NB];
>      char *x_expr, *y_expr;
> +    AVExpr *x_pexpr, *y_pexpr;
>  } OverlayContext;
>
>  #define OFFSET(x) offsetof(OverlayContext, x)
> @@ -126,6 +142,9 @@ static av_cold void uninit(AVFilterContext *ctx)
>
>      av_opt_free(over);
>
> +    av_expr_free(over->x_pexpr); over->x_pexpr = NULL;
> +    av_expr_free(over->y_pexpr); over->y_pexpr = NULL;
> +
>      avfilter_unref_bufferp(&over->overpicref);
>      ff_bufqueue_discard_all(&over->queue_main);
>      ff_bufqueue_discard_all(&over->queue_over);
> @@ -194,7 +213,6 @@ 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);
>
> @@ -202,48 +220,37 @@ 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)
> -        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)))
> +    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->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->y_expr;
> +    if ((ret = av_expr_parse(&over->y_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);
>
>      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:
> @@ -450,6 +457,7 @@ static int try_filter_frame(AVFilterContext *ctx,
> AVFilterBufferRef *mainpic)
>  {
>      OverlayContext *over = ctx->priv;
>      AVFilterLink *outlink = ctx->outputs[0];
> +    AVFilterLink *inlink = ctx->inputs[0];
>      AVFilterBufferRef *next_overpic;
>      int ret;
>
> @@ -481,8 +489,34 @@ static int try_filter_frame(AVFilterContext *ctx,
> AVFilterBufferRef *mainpic)
>                  av_ts2str(over->overpicref->pts),
> av_ts2timestr(over->overpicref->pts, &outlink->time_base));
>      av_dlog(ctx, "\n");
>
> -    if (over->overpicref)
> +    if (over->overpicref) {
> +        over->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ?
> +            NAN : mainpic->pts * av_q2d(inlink->time_base);
> +        over->var_values[VAR_POS] = mainpic->pos == -1 ? NAN :
> mainpic->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);
> +
> +        if (isnan(over->var_values[VAR_X])) {
> +            over->x = INT_MAX;
> +        } else {
> +            over->x = over->var_values[VAR_X];
> +            over->x &= ~((1 << over->hsub) - 1);
> +        }
> +        if (isnan(over->var_values[VAR_Y])) {
> +            over->y = INT_MAX;
> +        } else {
> +            over->y = over->var_values[VAR_Y];
> +            over->y &= ~((1 << over->vsub) - 1);
> +        }
> +        av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f
> yi:%d\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);
> +
>          blend_image(ctx, mainpic, over->overpicref, over->x, over->y);
> +        over->var_values[VAR_N] += 1.0;
> +    }
>      ret = ff_filter_frame(ctx->outputs[0], mainpic);
>      av_assert1(ret != AVERROR(EAGAIN));
>      over->frame_requested = 0;
> --
> 1.7.9.5

I see nothing obvioulsy wrong, so lgtm.


More information about the ffmpeg-devel mailing list