[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