diff --git a/doc/filters.texi b/doc/filters.texi index 6e616b26b4..10f1f0498d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2886,11 +2886,11 @@ This filter accepts the following options: @table @option @item x @item y -Specify the top left corner coordinates of the box. Default to 0. +The expressions which specify the top left corner coordinates of the box. Default to 0. @item width, w @item height, h -Specify the width and height of the box, if 0 they are interpreted as +The expressions which specify the width and height of the box, if 0 they are interpreted as the input width and height. Default to 0. @item color, c @@ -2900,7 +2900,44 @@ value @code{invert} is used, the box edge color is the same as the video with inverted luma. @item thickness, t -Set the thickness of the box edge. Default value is @code{3}. +The expression which sets the thickness of the box edge. Default value is @code{3}. + +See below for the list of accepted constants. +@end table + +The parameters for @var{x}, @var{y}, @var{w} and @var{h} and @var{t} are expressions containing the +following constants: + +@table @option +@item dar +The input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar}. + +@item hsub +@item vsub +horizontal and vertical chroma subsample values. For example for the +pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. + +@item in_h, ih +@item in_w, iw +The input width and height. + +@item sar +The input sample aspect ratio. + +@item x +@item y +The x and y offset coordinates where the box is drawn. + +@item w +@item h +The width and height of the drawn box. + +@item t +The thickness of the drawn box. + +These constants allow the @var{x}, @var{y}, @var{w}, @var{h} and @var{t} expressions to refer to +each other, so you may for example specify @code(y=x/dar} or @code(h=w/dar). + @end table @subsection Examples @@ -2928,6 +2965,12 @@ Fill the box with pink color: @example drawbox=x=10:y=10:w=100:h=100:color=pink@@0.5:t=max @end example + +@item +Draw a 2-pixel red 2.40:1 mask: +@example +drawbox=x=-t:y=0.5*(ih-iw/2.4)-t:w=iw+t*2:h=iw/2.4+t*2:t=2:c=red +@end example @end itemize @section drawgrid @@ -2939,11 +2982,11 @@ This filter accepts the following options: @table @option @item x @item y -Specify the coordinates of some point of grid intersection (meant to configure offset). Both default to 0. +The expressions which specify the coordinates of some point of grid intersection (meant to configure offset). Both default to 0. @item width, w @item height, h -Specify the width and height of the grid cell, if 0 they are interpreted as the +The expressions which specify the width and height of the grid cell, if 0 they are interpreted as the input width and height, respectively, minus @code{thickness}, so image gets framed. Default to 0. @@ -2956,7 +2999,44 @@ Note that you can append opacity value (in range of 0.0 - 1.0) to color name after @@ sign. @item thickness, t -Set the thickness of the grid line. Default value is @code{1}. +The expression which sets the thickness of the grid line. Default value is @code{1}. + +See below for the list of accepted constants. +@end table + +The parameters for @var{x}, @var{y}, @var{w} and @var{h} and @var{t} are expressions containing the +following constants: + +@table @option +@item dar +The input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar}. + +@item hsub +@item vsub +horizontal and vertical chroma subsample values. For example for the +pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. + +@item in_h, ih +@item in_w, iw +The input grid cell width and height. + +@item sar +The input sample aspect ratio. + +@item x +@item y +The x and y coordinates of some point of grid intersection (meant to configure offset). + +@item w +@item h +The width and height of the drawn cell. + +@item t +The thickness of the drawn cell. + +These constants allow the @var{x}, @var{y}, @var{w}, @var{h} and @var{t} expressions to refer to +each other, so you may for example specify @code(y=x/dar} or @code(h=w/dar). + @end table @subsection Examples @@ -2967,6 +3047,12 @@ Draw a grid with cell 100x100 pixels, thickness 2 pixels, with color red and an @example drawgrid=width=100:height=100:thickness=2:color=red@@0.5 @end example + +@item +Draw a white 3x3 grid with an opacity of 50%: +@example +drawgrid=w=iw/3:h=ih/3:t=2:c=white@@0.5 +@end example @end itemize @anchor{drawtext} diff --git a/libavfilter/vf_drawbox.c b/libavfilter/vf_drawbox.c index 014876168d..6bcaf75202 100644 --- a/libavfilter/vf_drawbox.c +++ b/libavfilter/vf_drawbox.c @@ -28,6 +28,7 @@ #include "libavutil/colorspace.h" #include "libavutil/common.h" #include "libavutil/opt.h" +#include "libavutil/eval.h" #include "libavutil/pixdesc.h" #include "libavutil/parseutils.h" #include "avfilter.h" @@ -35,18 +36,51 @@ #include "internal.h" #include "video.h" +static const char *const var_names[] = { + "dar", + "hsub", "vsub", + "in_h", "ih", ///< height of the input video + "in_w", "iw", ///< width of the input video + "sar", + "x", + "y", + "h", ///< height of the rendered box + "w", ///< width of the rendered box + "t", + NULL +}; + enum { Y, U, V, A }; +enum var_name { + VAR_DAR, + VAR_HSUB, VAR_VSUB, + VAR_IN_H, VAR_IH, + VAR_IN_W, VAR_IW, + VAR_SAR, + VAR_X, + VAR_Y, + VAR_H, + VAR_W, + VAR_T, + VARS_NB +}; + typedef struct { const AVClass *class; - int x, y, w_opt, h_opt, w, h; + int x, y, w, h; int thickness; char *color_str; unsigned char yuv_color[4]; int invert_color; ///< invert luma color int vsub, hsub; ///< chroma subsampling + char *x_expr, *y_expr; ///< expression for x and y + char *w_expr, *h_expr; ///< expression for width and height + char *t_expr; ///< expression for thickness } DrawBoxContext; +static const int NUM_EXPR_EVALS = 5; + static av_cold int init(AVFilterContext *ctx) { DrawBoxContext *s = ctx->priv; @@ -83,20 +117,82 @@ static int query_formats(AVFilterContext *ctx) static int config_input(AVFilterLink *inlink) { - DrawBoxContext *s = inlink->dst->priv; + AVFilterContext *ctx = inlink->dst; + DrawBoxContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + double var_values[VARS_NB], res; + char *expr; + int ret; s->hsub = desc->log2_chroma_w; s->vsub = desc->log2_chroma_h; - s->w = (s->w_opt > 0) ? s->w_opt : inlink->w; - s->h = (s->h_opt > 0) ? s->h_opt : inlink->h; + var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h; + var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w; + var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; + var_values[VAR_DAR] = (double)inlink->w / inlink->h * var_values[VAR_SAR]; + var_values[VAR_HSUB] = s->hsub; + var_values[VAR_VSUB] = s->vsub; + var_values[VAR_X] = NAN; + var_values[VAR_Y] = NAN; + var_values[VAR_H] = NAN; + var_values[VAR_W] = NAN; + var_values[VAR_T] = NAN; - av_log(inlink->dst, AV_LOG_VERBOSE, "x:%d y:%d w:%d h:%d color:0x%02X%02X%02X%02X\n", + for (int i = 0; i <= NUM_EXPR_EVALS; i++) { + /* evaluate expressions, fail on last iteration */ + if ((ret = av_expr_parse_and_eval(&res, (expr = s->x_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0 && i == NUM_EXPR_EVALS) + goto fail; + s->x = var_values[VAR_X] = res; + + if ((ret = av_expr_parse_and_eval(&res, (expr = s->y_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0 && i == NUM_EXPR_EVALS) + goto fail; + s->y = var_values[VAR_Y] = res; + + if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0 && i == NUM_EXPR_EVALS) + goto fail; + s->w = var_values[VAR_W] = res; + + if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0 && i == NUM_EXPR_EVALS) + goto fail; + s->h = var_values[VAR_H] = res; + + if ((ret = av_expr_parse_and_eval(&res, (expr = s->t_expr), + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0 && i == NUM_EXPR_EVALS) + goto fail; + s->thickness = var_values[VAR_T] = res; + } + + /* if w or h are zero, use the input w/h */ + s->w = (s->w > 0) ? s->w : inlink->w; + s->h = (s->h > 0) ? s->h : inlink->h; + + /* sanity check width and height */ + if (s->w < 0 || s->h < 0) { + av_log(ctx, AV_LOG_ERROR, "Size values less than 0 are not acceptable.\n"); + return AVERROR(EINVAL); + } + + av_log(ctx, AV_LOG_VERBOSE, "x:%d y:%d w:%d h:%d color:0x%02X%02X%02X%02X\n", s->x, s->y, s->w, s->h, s->yuv_color[Y], s->yuv_color[U], s->yuv_color[V], s->yuv_color[A]); return 0; + +fail: + av_log(NULL, AV_LOG_ERROR, + "Error when evaluating the expression '%s'.\n", + expr); + return ret; } static int filter_frame(AVFilterLink *inlink, AVFrame *frame) @@ -140,16 +236,16 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) #if CONFIG_DRAWBOX_FILTER static const AVOption drawbox_options[] = { - { "x", "set horizontal position of the left box edge", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "y", "set vertical position of the top box edge", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "width", "set width of the box", OFFSET(w_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "w", "set width of the box", OFFSET(w_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "height", "set height of the box", OFFSET(h_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "h", "set height of the box", OFFSET(h_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, + { "x", "set horizontal position of the left box edge", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "y", "set vertical position of the top box edge", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "width", "set width of the box", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "w", "set width of the box", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "height", "set height of the box", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "h", "set height of the box", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, { "color", "set color of the box", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, { "c", "set color of the box", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "thickness", "set the box thickness", OFFSET(thickness), AV_OPT_TYPE_INT, { .i64 = 3 }, 0, INT_MAX, FLAGS }, - { "t", "set the box thickness", OFFSET(thickness), AV_OPT_TYPE_INT, { .i64 = 3 }, 0, INT_MAX, FLAGS }, + { "thickness", "set the box thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, { .str="3" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "t", "set the box thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, { .str="3" }, CHAR_MIN, CHAR_MAX, FLAGS }, { NULL } }; @@ -248,16 +344,16 @@ static int drawgrid_filter_frame(AVFilterLink *inlink, AVFrame *frame) } static const AVOption drawgrid_options[] = { - { "x", "set horizontal offset", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "y", "set vertical offset", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "width", "set width of grid cell", OFFSET(w_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "w", "set width of grid cell", OFFSET(w_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "height", "set height of grid cell", OFFSET(h_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "h", "set height of grid cell", OFFSET(h_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, + { "x", "set horizontal offset", OFFSET(x_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "y", "set vertical offset", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "width", "set width of grid cell", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "w", "set width of grid cell", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "height", "set height of grid cell", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, + { "h", "set height of grid cell", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str="0" }, CHAR_MIN, CHAR_MAX, FLAGS }, { "color", "set color of the grid", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, { "c", "set color of the grid", OFFSET(color_str), AV_OPT_TYPE_STRING, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, - { "thickness", "set grid line thickness", OFFSET(thickness), AV_OPT_TYPE_INT, {.i64=1}, 0, INT_MAX, FLAGS }, - { "t", "set grid line thickness", OFFSET(thickness), AV_OPT_TYPE_INT, {.i64=1}, 0, INT_MAX, FLAGS }, + { "thickness", "set grid line thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX, FLAGS }, + { "t", "set grid line thickness", OFFSET(t_expr), AV_OPT_TYPE_STRING, {.str="1"}, CHAR_MIN, CHAR_MAX, FLAGS }, { NULL } };