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

Stefano Sabatini stefasab at gmail.com
Tue Feb 19 20:10:50 CET 2013


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
 
 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
+
+ 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



More information about the ffmpeg-devel mailing list