diff --git a/doc/filters.texi b/doc/filters.texi index 215e4387a2..a3da5fbd0c 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -4279,7 +4279,8 @@ Set when the expressions for @option{x}, @option{y}, and It accepts the following values: @table @samp @item init -only evaluate expressions once during the filter initialization +only evaluate expressions once during the filter initialization or +when a command is processed @item frame evaluate expressions for each incoming frame @@ -4360,6 +4361,20 @@ the @var{movie} filter. You can chain together more overlays but you should test the efficiency of such approach. +@subsection Commands + +This filter supports the following command: +@table @option +@item x +Set the @option{x} option expression. + +@item y +Set the @option{y} option expression. + +@item enable +Set the @option{enable} option expression. +@end table + @subsection Examples @itemize diff --git a/libavfilter/version.h b/libavfilter/version.h index e2fec54961..171a9adb7b 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #define LIBAVFILTER_VERSION_MAJOR 3 #define LIBAVFILTER_VERSION_MINOR 50 -#define LIBAVFILTER_VERSION_MICRO 102 +#define LIBAVFILTER_VERSION_MICRO 103 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c index 57a0bcd268..c8362bed56 100644 --- a/libavfilter/vf_overlay.c +++ b/libavfilter/vf_overlay.c @@ -164,6 +164,74 @@ static av_cold void uninit(AVFilterContext *ctx) av_expr_free(over->enable_pexpr); over->enable_pexpr = NULL; } +static inline int normalize_xy(double d, int chroma_sub) +{ + if (isnan(d)) + return INT_MAX; + return (int)d & ~((1 << chroma_sub) - 1); +} + +enum EvalTarget { EVAL_XY, EVAL_ENABLE, EVAL_ALL }; + +static void eval_expr(AVFilterContext *ctx, enum EvalTarget eval_tgt) +{ + OverlayContext *over = ctx->priv; + + if (eval_tgt == EVAL_XY || eval_tgt == EVAL_ALL) { + 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); + } + if (eval_tgt == EVAL_ENABLE || eval_tgt == EVAL_ALL) { + over->enable = av_expr_eval(over->enable_pexpr, over->var_values, NULL); + } +} + +static int set_expr(AVExpr **pexpr, const char *expr, void *log_ctx) +{ + int ret; + + if (*pexpr) + av_expr_free(*pexpr); + *pexpr = NULL; + ret = av_expr_parse(pexpr, expr, var_names, + NULL, NULL, NULL, NULL, 0, log_ctx); + if (ret < 0) + av_log(log_ctx, AV_LOG_ERROR, + "Error when evaluating the expression '%s'\n", expr); + return ret; +} + +static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, + char *res, int res_len, int flags) +{ + OverlayContext *over = ctx->priv; + int ret; + + if (!strcmp(cmd, "x")) + ret = set_expr(&over->x_pexpr, args, ctx); + else if (!strcmp(cmd, "y")) + ret = set_expr(&over->y_pexpr, args, ctx); + else if (!strcmp(cmd, "enable")) + ret = set_expr(&over->enable_pexpr, args, ctx); + else + ret = AVERROR(ENOSYS); + + if (ret < 0) + return ret; + + if (over->eval_mode == EVAL_MODE_INIT) { + eval_expr(ctx, EVAL_ALL); + av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d enable:%f\n", + over->var_values[VAR_X], over->x, + over->var_values[VAR_Y], over->y, + over->enable); + } + return ret; +} + static int query_formats(AVFilterContext *ctx) { OverlayContext *over = ctx->priv; @@ -244,30 +312,10 @@ static int config_input_main(AVFilterLink *inlink) return 0; } -static inline int normalize_xy(double d, int chroma_sub) -{ - if (isnan(d)) - return INT_MAX; - return (int)d & ~((1 << chroma_sub) - 1); -} - -static void eval_expr(AVFilterContext *ctx) -{ - OverlayContext *over = ctx->priv; - - 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 = av_expr_eval(over->enable_pexpr, over->var_values, NULL); -} - static int config_input_overlay(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; OverlayContext *over = inlink->dst->priv; - char *expr; int ret; const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); @@ -287,25 +335,17 @@ static int config_input_overlay(AVFilterLink *inlink) 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; - expr = over->y_expr; - if ((ret = av_expr_parse(&over->y_pexpr, expr, var_names, - NULL, NULL, NULL, NULL, 0, ctx)) < 0) - goto fail; - expr = over->enable_expr; - if ((ret = av_expr_parse(&over->enable_pexpr, expr, var_names, - NULL, NULL, NULL, NULL, 0, ctx)) < 0) - goto fail; + if ((ret = set_expr(&over->x_pexpr, over->x_expr, ctx)) < 0 || + (ret = set_expr(&over->y_pexpr, over->y_expr, ctx)) < 0 || + (ret = set_expr(&over->enable_pexpr, over->enable_expr, ctx)) < 0) + return ret; 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) { - eval_expr(ctx); + eval_expr(ctx, EVAL_ALL); av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d enable:%f\n", over->var_values[VAR_X], over->x, over->var_values[VAR_Y], over->y, @@ -319,11 +359,6 @@ static int config_input_overlay(AVFilterLink *inlink) ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h, av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format)); return 0; - -fail: - av_log(NULL, AV_LOG_ERROR, - "Error when parsing the expression '%s'\n", expr); - return ret; } static int config_output(AVFilterLink *outlink) @@ -581,7 +616,7 @@ static int try_filter_frame(AVFilterContext *ctx, AVFrame *mainpic) NAN : mainpic->pts * av_q2d(inlink->time_base); over->var_values[VAR_POS] = pos == -1 ? NAN : pos; - eval_expr(ctx); + eval_expr(ctx, EVAL_ALL); 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, @@ -725,6 +760,7 @@ AVFilter avfilter_vf_overlay = { .priv_size = sizeof(OverlayContext), .query_formats = query_formats, + .process_command = process_command, .inputs = avfilter_vf_overlay_inputs, .outputs = avfilter_vf_overlay_outputs,