[FFmpeg-devel] [PATCH] lavfi/overlay: add dynamic x/y expression evaluation support
Clément Bœsch
ubitux at gmail.com
Thu Apr 4 10:40:30 CEST 2013
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.
Also, I wonder if we will have an "auto" mode with av_eval_is_const soon
:)
[...]
> 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.
> 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?
--
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130404/d762fd27/attachment.asc>
More information about the ffmpeg-devel
mailing list