lavfi: add tblend filter
This commit is contained in:
parent
05e74ac2f3
commit
d4fd3f24e8
@ -8,6 +8,7 @@ version <next>:
|
||||
- RIFX format for *.wav files
|
||||
- RTP/mpegts muxer
|
||||
- non continuous cache protocol support
|
||||
- tblend filter
|
||||
|
||||
|
||||
version 2.5:
|
||||
|
@ -2634,13 +2634,17 @@ The threshold below which a pixel value is considered black; it defaults to
|
||||
|
||||
@end table
|
||||
|
||||
@section blend
|
||||
@section blend, tblend
|
||||
|
||||
Blend two video frames into each other.
|
||||
|
||||
It takes two input streams and outputs one stream, the first input is the
|
||||
"top" layer and second input is "bottom" layer.
|
||||
Output terminates when shortest input terminates.
|
||||
The @code{blend} filter takes two input streams and outputs one
|
||||
stream, the first input is the "top" layer and second input is
|
||||
"bottom" layer. Output terminates when shortest input terminates.
|
||||
|
||||
The @code{tblend} (time blend) filter takes two consecutive frames
|
||||
from one single stream, and outputs the result obtained by blending
|
||||
the new frame on top of the old frame.
|
||||
|
||||
A description of the accepted options follows.
|
||||
|
||||
@ -2730,11 +2734,13 @@ Value of pixel component at current location for second video frame (bottom laye
|
||||
@end table
|
||||
|
||||
@item shortest
|
||||
Force termination when the shortest input terminates. Default is @code{0}.
|
||||
Force termination when the shortest input terminates. Default is
|
||||
@code{0}. This option is only defined for the @code{blend} filter.
|
||||
|
||||
@item repeatlast
|
||||
Continue applying the last bottom frame after the end of the stream. A value of
|
||||
@code{0} disable the filter after the last frame of the bottom layer is reached.
|
||||
Default is @code{1}.
|
||||
Default is @code{1}. This option is only defined for the @code{blend} filter.
|
||||
@end table
|
||||
|
||||
@subsection Examples
|
||||
@ -2769,6 +2775,12 @@ Apply uncover up-left effect:
|
||||
@example
|
||||
blend=all_expr='if(gte(T*SH*40+Y,H)*gte((T*40*SW+X)*W/H,W),A,B)'
|
||||
@end example
|
||||
|
||||
@item
|
||||
Display differences between the current and the previous frame:
|
||||
@example
|
||||
tblend=all_mode=difference128
|
||||
@end example
|
||||
@end itemize
|
||||
|
||||
@section boxblur
|
||||
|
@ -188,6 +188,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER) += vf_stereo3d.o
|
||||
OBJS-$(CONFIG_SUBTITLES_FILTER) += vf_subtitles.o
|
||||
OBJS-$(CONFIG_SUPER2XSAI_FILTER) += vf_super2xsai.o
|
||||
OBJS-$(CONFIG_SWAPUV_FILTER) += vf_swapuv.o
|
||||
OBJS-$(CONFIG_TBLEND_FILTER) += vf_blend.o
|
||||
OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o
|
||||
OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o
|
||||
OBJS-$(CONFIG_TILE_FILTER) += vf_tile.o
|
||||
|
@ -203,6 +203,7 @@ void avfilter_register_all(void)
|
||||
REGISTER_FILTER(SUBTITLES, subtitles, vf);
|
||||
REGISTER_FILTER(SUPER2XSAI, super2xsai, vf);
|
||||
REGISTER_FILTER(SWAPUV, swapuv, vf);
|
||||
REGISTER_FILTER(TBLEND, tblend, vf);
|
||||
REGISTER_FILTER(TELECINE, telecine, vf);
|
||||
REGISTER_FILTER(THUMBNAIL, thumbnail, vf);
|
||||
REGISTER_FILTER(TILE, tile, vf);
|
||||
|
@ -30,8 +30,8 @@
|
||||
#include "libavutil/version.h"
|
||||
|
||||
#define LIBAVFILTER_VERSION_MAJOR 5
|
||||
#define LIBAVFILTER_VERSION_MINOR 5
|
||||
#define LIBAVFILTER_VERSION_MICRO 102
|
||||
#define LIBAVFILTER_VERSION_MINOR 6
|
||||
#define LIBAVFILTER_VERSION_MICRO 100
|
||||
|
||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||
LIBAVFILTER_VERSION_MINOR, \
|
||||
|
@ -96,52 +96,57 @@ typedef struct {
|
||||
double all_opacity;
|
||||
|
||||
FilterParams params[4];
|
||||
int tblend;
|
||||
AVFrame *prev_frame; /* only used with tblend */
|
||||
} BlendContext;
|
||||
|
||||
#define COMMON_OPTIONS \
|
||||
{ "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
|
||||
{ "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
|
||||
{ "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
|
||||
{ "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
|
||||
{ "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, "mode"},\
|
||||
{ "addition", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION}, 0, 0, FLAGS, "mode" },\
|
||||
{ "and", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND}, 0, 0, FLAGS, "mode" },\
|
||||
{ "average", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE}, 0, 0, FLAGS, "mode" },\
|
||||
{ "burn", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN}, 0, 0, FLAGS, "mode" },\
|
||||
{ "darken", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN}, 0, 0, FLAGS, "mode" },\
|
||||
{ "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, "mode" },\
|
||||
{ "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE128}, 0, 0, FLAGS, "mode" },\
|
||||
{ "divide", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE}, 0, 0, FLAGS, "mode" },\
|
||||
{ "dodge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE}, 0, 0, FLAGS, "mode" },\
|
||||
{ "exclusion", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION}, 0, 0, FLAGS, "mode" },\
|
||||
{ "hardlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT}, 0, 0, FLAGS, "mode" },\
|
||||
{ "lighten", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN}, 0, 0, FLAGS, "mode" },\
|
||||
{ "multiply", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY}, 0, 0, FLAGS, "mode" },\
|
||||
{ "negation", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION}, 0, 0, FLAGS, "mode" },\
|
||||
{ "normal", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL}, 0, 0, FLAGS, "mode" },\
|
||||
{ "or", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR}, 0, 0, FLAGS, "mode" },\
|
||||
{ "overlay", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY}, 0, 0, FLAGS, "mode" },\
|
||||
{ "phoenix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX}, 0, 0, FLAGS, "mode" },\
|
||||
{ "pinlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT}, 0, 0, FLAGS, "mode" },\
|
||||
{ "reflect", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT}, 0, 0, FLAGS, "mode" },\
|
||||
{ "screen", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN}, 0, 0, FLAGS, "mode" },\
|
||||
{ "softlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT}, 0, 0, FLAGS, "mode" },\
|
||||
{ "subtract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT}, 0, 0, FLAGS, "mode" },\
|
||||
{ "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, "mode" },\
|
||||
{ "xor", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR}, 0, 0, FLAGS, "mode" },\
|
||||
{ "c0_expr", "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
|
||||
{ "c1_expr", "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
|
||||
{ "c2_expr", "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
|
||||
{ "c3_expr", "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
|
||||
{ "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
|
||||
{ "c0_opacity", "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
|
||||
{ "c1_opacity", "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
|
||||
{ "c2_opacity", "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
|
||||
{ "c3_opacity", "set color component #3 opacity", OFFSET(params[3].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
|
||||
{ "all_opacity", "set opacity for all color components", OFFSET(all_opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}
|
||||
|
||||
#define OFFSET(x) offsetof(BlendContext, x)
|
||||
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
|
||||
|
||||
static const AVOption blend_options[] = {
|
||||
{ "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
|
||||
{ "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
|
||||
{ "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
|
||||
{ "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
|
||||
{ "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, "mode"},
|
||||
{ "addition", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION}, 0, 0, FLAGS, "mode" },
|
||||
{ "and", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND}, 0, 0, FLAGS, "mode" },
|
||||
{ "average", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE}, 0, 0, FLAGS, "mode" },
|
||||
{ "burn", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN}, 0, 0, FLAGS, "mode" },
|
||||
{ "darken", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN}, 0, 0, FLAGS, "mode" },
|
||||
{ "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, "mode" },
|
||||
{ "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE128}, 0, 0, FLAGS, "mode" },
|
||||
{ "divide", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE}, 0, 0, FLAGS, "mode" },
|
||||
{ "dodge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE}, 0, 0, FLAGS, "mode" },
|
||||
{ "exclusion", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION}, 0, 0, FLAGS, "mode" },
|
||||
{ "hardlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT}, 0, 0, FLAGS, "mode" },
|
||||
{ "lighten", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN}, 0, 0, FLAGS, "mode" },
|
||||
{ "multiply", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY}, 0, 0, FLAGS, "mode" },
|
||||
{ "negation", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION}, 0, 0, FLAGS, "mode" },
|
||||
{ "normal", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL}, 0, 0, FLAGS, "mode" },
|
||||
{ "or", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR}, 0, 0, FLAGS, "mode" },
|
||||
{ "overlay", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY}, 0, 0, FLAGS, "mode" },
|
||||
{ "phoenix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX}, 0, 0, FLAGS, "mode" },
|
||||
{ "pinlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT}, 0, 0, FLAGS, "mode" },
|
||||
{ "reflect", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT}, 0, 0, FLAGS, "mode" },
|
||||
{ "screen", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN}, 0, 0, FLAGS, "mode" },
|
||||
{ "softlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT}, 0, 0, FLAGS, "mode" },
|
||||
{ "subtract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT}, 0, 0, FLAGS, "mode" },
|
||||
{ "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, "mode" },
|
||||
{ "xor", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR}, 0, 0, FLAGS, "mode" },
|
||||
{ "c0_expr", "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
|
||||
{ "c1_expr", "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
|
||||
{ "c2_expr", "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
|
||||
{ "c3_expr", "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
|
||||
{ "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
|
||||
{ "c0_opacity", "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
|
||||
{ "c1_opacity", "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
|
||||
{ "c2_opacity", "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
|
||||
{ "c3_opacity", "set color component #3 opacity", OFFSET(params[3].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
|
||||
{ "all_opacity", "set opacity for all color components", OFFSET(all_opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
|
||||
COMMON_OPTIONS,
|
||||
{ "shortest", "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
|
||||
{ "repeatlast", "repeat last bottom frame", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
|
||||
{ NULL }
|
||||
@ -288,7 +293,8 @@ static AVFrame *blend_frame(AVFilterContext *ctx, AVFrame *top_buf,
|
||||
ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outh, ctx->graph->nb_threads));
|
||||
}
|
||||
|
||||
av_frame_free(&top_buf);
|
||||
if (!b->tblend)
|
||||
av_frame_free(&top_buf);
|
||||
|
||||
return dst_buf;
|
||||
}
|
||||
@ -298,6 +304,8 @@ static av_cold int init(AVFilterContext *ctx)
|
||||
BlendContext *b = ctx->priv;
|
||||
int ret, plane;
|
||||
|
||||
b->tblend = !strcmp(ctx->filter->name, "tblend");
|
||||
|
||||
for (plane = 0; plane < FF_ARRAY_ELEMS(b->params); plane++) {
|
||||
FilterParams *param = &b->params[plane];
|
||||
|
||||
@ -416,6 +424,8 @@ static av_cold void uninit(AVFilterContext *ctx)
|
||||
int i;
|
||||
|
||||
ff_dualinput_uninit(&b->dinput);
|
||||
av_freep(&b->prev_frame);
|
||||
|
||||
for (i = 0; i < FF_ARRAY_ELEMS(b->params); i++)
|
||||
av_expr_free(b->params[i].e);
|
||||
}
|
||||
@ -467,3 +477,71 @@ AVFilter ff_vf_blend = {
|
||||
.priv_class = &blend_class,
|
||||
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
|
||||
};
|
||||
|
||||
static int tblend_config_output(AVFilterLink *outlink)
|
||||
{
|
||||
AVFilterContext *ctx = outlink->src;
|
||||
AVFilterLink *inlink = ctx->inputs[0];
|
||||
BlendContext *b = ctx->priv;
|
||||
const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
|
||||
|
||||
b->hsub = pix_desc->log2_chroma_w;
|
||||
b->vsub = pix_desc->log2_chroma_h;
|
||||
b->nb_planes = av_pix_fmt_count_planes(inlink->format);
|
||||
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tblend_filter_frame(AVFilterLink *inlink, AVFrame *frame)
|
||||
{
|
||||
BlendContext *b = inlink->dst->priv;
|
||||
AVFilterLink *outlink = inlink->dst->outputs[0];
|
||||
|
||||
if (b->prev_frame) {
|
||||
AVFrame *out = blend_frame(inlink->dst, frame, b->prev_frame);
|
||||
av_frame_free(&b->prev_frame);
|
||||
b->prev_frame = frame;
|
||||
return ff_filter_frame(outlink, out);
|
||||
}
|
||||
b->prev_frame = frame;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const AVOption tblend_options[] = {
|
||||
COMMON_OPTIONS,
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
AVFILTER_DEFINE_CLASS(tblend);
|
||||
|
||||
static const AVFilterPad tblend_inputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_VIDEO,
|
||||
.filter_frame = tblend_filter_frame,
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static const AVFilterPad tblend_outputs[] = {
|
||||
{
|
||||
.name = "default",
|
||||
.type = AVMEDIA_TYPE_VIDEO,
|
||||
.config_props = tblend_config_output,
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
AVFilter ff_vf_tblend = {
|
||||
.name = "tblend",
|
||||
.description = NULL_IF_CONFIG_SMALL("Blend successive frames."),
|
||||
.priv_size = sizeof(BlendContext),
|
||||
.priv_class = &blend_class,
|
||||
.query_formats = query_formats,
|
||||
.init = init,
|
||||
.uninit = uninit,
|
||||
.inputs = tblend_inputs,
|
||||
.outputs = tblend_outputs,
|
||||
.flags = AVFILTER_FLAG_SLICE_THREADS,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user