mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-02-04 06:08:26 +02:00
lavfi/overlay: support timeline through the new system.
This commit is contained in:
parent
38853169ce
commit
15d2f26998
@ -4765,14 +4765,8 @@ on the main video. Default value is "0" for both expressions. In case
|
|||||||
the expression is invalid, it is set to a huge value (meaning that the
|
the expression is invalid, it is set to a huge value (meaning that the
|
||||||
overlay will not be displayed within the output visible area).
|
overlay will not be displayed within the output visible area).
|
||||||
|
|
||||||
@item enable
|
|
||||||
Set the expression which enables the overlay. If the evaluation is
|
|
||||||
different from 0, the overlay is displayed on top of the input
|
|
||||||
frame. By default it is "1".
|
|
||||||
|
|
||||||
@item eval
|
@item eval
|
||||||
Set when the expressions for @option{x}, @option{y}, and
|
Set when the expressions for @option{x}, and @option{y} are evaluated.
|
||||||
@option{enable} are evaluated.
|
|
||||||
|
|
||||||
It accepts the following values:
|
It accepts the following values:
|
||||||
@table @samp
|
@table @samp
|
||||||
@ -4818,8 +4812,8 @@ main input until the end of the stream. A value of 0 disables this
|
|||||||
behavior, which is enabled by default.
|
behavior, which is enabled by default.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
The @option{x}, @option{y}, and @option{enable} expressions can
|
The @option{x}, and @option{y} expressions can contain the following
|
||||||
contain the following parameters.
|
parameters.
|
||||||
|
|
||||||
@table @option
|
@table @option
|
||||||
@item main_w, W
|
@item main_w, W
|
||||||
@ -4870,8 +4864,7 @@ This filter supports the following commands:
|
|||||||
@table @option
|
@table @option
|
||||||
@item x
|
@item x
|
||||||
@item y
|
@item y
|
||||||
@item enable
|
Modify the x and y of the overlay input.
|
||||||
Modify the x/y and enable overlay of the overlay input.
|
|
||||||
The command accepts the same syntax of the corresponding option.
|
The command accepts the same syntax of the corresponding option.
|
||||||
|
|
||||||
If the specified expression is not valid, it is kept at its current
|
If the specified expression is not valid, it is kept at its current
|
||||||
|
@ -88,7 +88,7 @@ enum var_name {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
const AVClass *class;
|
const AVClass *class;
|
||||||
int x, y; ///< position of overlayed picture
|
int x, y; ///< position of overlayed picture
|
||||||
double enable; ///< tells if blending is enabled
|
int enable; ///< tells if blending is enabled
|
||||||
|
|
||||||
int allow_packed_rgb;
|
int allow_packed_rgb;
|
||||||
uint8_t frame_requested;
|
uint8_t frame_requested;
|
||||||
@ -114,8 +114,7 @@ typedef struct {
|
|||||||
|
|
||||||
double var_values[VAR_VARS_NB];
|
double var_values[VAR_VARS_NB];
|
||||||
char *x_expr, *y_expr;
|
char *x_expr, *y_expr;
|
||||||
char *enable_expr;
|
AVExpr *x_pexpr, *y_pexpr;
|
||||||
AVExpr *x_pexpr, *y_pexpr, *enable_pexpr;
|
|
||||||
} OverlayContext;
|
} OverlayContext;
|
||||||
|
|
||||||
static av_cold int init(AVFilterContext *ctx)
|
static av_cold int init(AVFilterContext *ctx)
|
||||||
@ -139,7 +138,6 @@ static av_cold void uninit(AVFilterContext *ctx)
|
|||||||
ff_bufqueue_discard_all(&over->queue_over);
|
ff_bufqueue_discard_all(&over->queue_over);
|
||||||
av_expr_free(over->x_pexpr); over->x_pexpr = NULL;
|
av_expr_free(over->x_pexpr); over->x_pexpr = NULL;
|
||||||
av_expr_free(over->y_pexpr); over->y_pexpr = NULL;
|
av_expr_free(over->y_pexpr); over->y_pexpr = NULL;
|
||||||
av_expr_free(over->enable_pexpr); over->enable_pexpr = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int normalize_xy(double d, int chroma_sub)
|
static inline int normalize_xy(double d, int chroma_sub)
|
||||||
@ -149,23 +147,17 @@ static inline int normalize_xy(double d, int chroma_sub)
|
|||||||
return (int)d & ~((1 << chroma_sub) - 1);
|
return (int)d & ~((1 << chroma_sub) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EvalTarget { EVAL_XY, EVAL_ENABLE, EVAL_ALL };
|
static void eval_expr(AVFilterContext *ctx)
|
||||||
|
|
||||||
static void eval_expr(AVFilterContext *ctx, enum EvalTarget eval_tgt)
|
|
||||||
{
|
{
|
||||||
OverlayContext *over = ctx->priv;
|
OverlayContext *over = ctx->priv;
|
||||||
|
|
||||||
if (eval_tgt == EVAL_XY || eval_tgt == EVAL_ALL) {
|
/* TODO: reindent */
|
||||||
over->var_values[VAR_X] = av_expr_eval(over->x_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_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->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->x = normalize_xy(over->var_values[VAR_X], over->hsub);
|
||||||
over->y = normalize_xy(over->var_values[VAR_Y], over->vsub);
|
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, const char *option, void *log_ctx)
|
static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx)
|
||||||
{
|
{
|
||||||
@ -198,8 +190,6 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
|
|||||||
ret = set_expr(&over->x_pexpr, args, cmd, ctx);
|
ret = set_expr(&over->x_pexpr, args, cmd, ctx);
|
||||||
else if (!strcmp(cmd, "y"))
|
else if (!strcmp(cmd, "y"))
|
||||||
ret = set_expr(&over->y_pexpr, args, cmd, ctx);
|
ret = set_expr(&over->y_pexpr, args, cmd, ctx);
|
||||||
else if (!strcmp(cmd, "enable"))
|
|
||||||
ret = set_expr(&over->enable_pexpr, args, cmd, ctx);
|
|
||||||
else
|
else
|
||||||
ret = AVERROR(ENOSYS);
|
ret = AVERROR(ENOSYS);
|
||||||
|
|
||||||
@ -207,11 +197,10 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (over->eval_mode == EVAL_MODE_INIT) {
|
if (over->eval_mode == EVAL_MODE_INIT) {
|
||||||
eval_expr(ctx, EVAL_ALL);
|
eval_expr(ctx);
|
||||||
av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d enable:%f\n",
|
av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
|
||||||
over->var_values[VAR_X], over->x,
|
over->var_values[VAR_X], over->x,
|
||||||
over->var_values[VAR_Y], over->y,
|
over->var_values[VAR_Y], over->y);
|
||||||
over->enable);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -320,8 +309,7 @@ static int config_input_overlay(AVFilterLink *inlink)
|
|||||||
over->var_values[VAR_POS] = NAN;
|
over->var_values[VAR_POS] = NAN;
|
||||||
|
|
||||||
if ((ret = set_expr(&over->x_pexpr, over->x_expr, "x", ctx)) < 0 ||
|
if ((ret = set_expr(&over->x_pexpr, over->x_expr, "x", ctx)) < 0 ||
|
||||||
(ret = set_expr(&over->y_pexpr, over->y_expr, "y", ctx)) < 0 ||
|
(ret = set_expr(&over->y_pexpr, over->y_expr, "y", ctx)) < 0)
|
||||||
(ret = set_expr(&over->enable_pexpr, over->enable_expr, "enable", ctx)) < 0)
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
over->overlay_is_packed_rgb =
|
over->overlay_is_packed_rgb =
|
||||||
@ -329,11 +317,10 @@ static int config_input_overlay(AVFilterLink *inlink)
|
|||||||
over->overlay_has_alpha = ff_fmt_is_in(inlink->format, alpha_pix_fmts);
|
over->overlay_has_alpha = ff_fmt_is_in(inlink->format, alpha_pix_fmts);
|
||||||
|
|
||||||
if (over->eval_mode == EVAL_MODE_INIT) {
|
if (over->eval_mode == EVAL_MODE_INIT) {
|
||||||
eval_expr(ctx, EVAL_ALL);
|
eval_expr(ctx);
|
||||||
av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d enable:%f\n",
|
av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n",
|
||||||
over->var_values[VAR_X], over->x,
|
over->var_values[VAR_X], over->x,
|
||||||
over->var_values[VAR_Y], over->y,
|
over->var_values[VAR_Y], over->y);
|
||||||
over->enable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
av_log(ctx, AV_LOG_VERBOSE,
|
av_log(ctx, AV_LOG_VERBOSE,
|
||||||
@ -605,12 +592,11 @@ static int try_filter_frame(AVFilterContext *ctx, AVFrame *mainpic)
|
|||||||
NAN : mainpic->pts * av_q2d(inlink->time_base);
|
NAN : mainpic->pts * av_q2d(inlink->time_base);
|
||||||
over->var_values[VAR_POS] = pos == -1 ? NAN : pos;
|
over->var_values[VAR_POS] = pos == -1 ? NAN : pos;
|
||||||
|
|
||||||
eval_expr(ctx, EVAL_ALL);
|
eval_expr(ctx);
|
||||||
av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d enable:%f\n",
|
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_N], over->var_values[VAR_T], over->var_values[VAR_POS],
|
||||||
over->var_values[VAR_X], over->x,
|
over->var_values[VAR_X], over->x,
|
||||||
over->var_values[VAR_Y], over->y,
|
over->var_values[VAR_Y], over->y);
|
||||||
over->enable);
|
|
||||||
}
|
}
|
||||||
if (over->enable)
|
if (over->enable)
|
||||||
blend_image(ctx, mainpic, over->overpicref, over->x, over->y);
|
blend_image(ctx, mainpic, over->overpicref, over->x, over->y);
|
||||||
@ -678,6 +664,20 @@ static int filter_frame_over(AVFilterLink *inlink, AVFrame *inpicref)
|
|||||||
return ret == AVERROR(EAGAIN) ? 0 : ret;
|
return ret == AVERROR(EAGAIN) ? 0 : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DEF_FILTER_FRAME(name, mode, enable_value) \
|
||||||
|
static int filter_frame_##name##_##mode(AVFilterLink *inlink, AVFrame *frame) \
|
||||||
|
{ \
|
||||||
|
AVFilterContext *ctx = inlink->dst; \
|
||||||
|
OverlayContext *over = ctx->priv; \
|
||||||
|
over->enable = enable_value; \
|
||||||
|
return filter_frame_##name(inlink, frame); \
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_FILTER_FRAME(main, enabled, 1);
|
||||||
|
DEF_FILTER_FRAME(main, disabled, 0);
|
||||||
|
DEF_FILTER_FRAME(over, enabled, 1);
|
||||||
|
DEF_FILTER_FRAME(over, disabled, 0);
|
||||||
|
|
||||||
static int request_frame(AVFilterLink *outlink)
|
static int request_frame(AVFilterLink *outlink)
|
||||||
{
|
{
|
||||||
AVFilterContext *ctx = outlink->src;
|
AVFilterContext *ctx = outlink->src;
|
||||||
@ -714,7 +714,6 @@ static int request_frame(AVFilterLink *outlink)
|
|||||||
static const AVOption overlay_options[] = {
|
static const AVOption overlay_options[] = {
|
||||||
{ "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS },
|
{ "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 },
|
{ "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" },
|
{ "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 once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" },
|
{ "init", "eval expressions once during initialization", 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" },
|
{ "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" },
|
||||||
@ -736,14 +735,16 @@ static const AVFilterPad avfilter_vf_overlay_inputs[] = {
|
|||||||
.type = AVMEDIA_TYPE_VIDEO,
|
.type = AVMEDIA_TYPE_VIDEO,
|
||||||
.get_video_buffer = ff_null_get_video_buffer,
|
.get_video_buffer = ff_null_get_video_buffer,
|
||||||
.config_props = config_input_main,
|
.config_props = config_input_main,
|
||||||
.filter_frame = filter_frame_main,
|
.filter_frame = filter_frame_main_enabled,
|
||||||
|
.passthrough_filter_frame = filter_frame_main_disabled,
|
||||||
.needs_writable = 1,
|
.needs_writable = 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "overlay",
|
.name = "overlay",
|
||||||
.type = AVMEDIA_TYPE_VIDEO,
|
.type = AVMEDIA_TYPE_VIDEO,
|
||||||
.config_props = config_input_overlay,
|
.config_props = config_input_overlay,
|
||||||
.filter_frame = filter_frame_over,
|
.filter_frame = filter_frame_over_enabled,
|
||||||
|
.passthrough_filter_frame = filter_frame_over_disabled,
|
||||||
},
|
},
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
@ -773,4 +774,5 @@ AVFilter avfilter_vf_overlay = {
|
|||||||
|
|
||||||
.inputs = avfilter_vf_overlay_inputs,
|
.inputs = avfilter_vf_overlay_inputs,
|
||||||
.outputs = avfilter_vf_overlay_outputs,
|
.outputs = avfilter_vf_overlay_outputs,
|
||||||
|
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user