On date Wednesday 2014-12-10 19:03:10 +0100, Clément Bœsch encoded: > Oups, sorry about the delay, I forgot that one. > > On Thu, Dec 04, 2014 at 12:36:43PM +0100, Stefano Sabatini wrote: [...] > > From c805460ceb04ec2da3f607e69067f4cc710c0613 Mon Sep 17 00:00:00 2001 > > From: Stefano Sabatini <stefa...@gmail.com> > > Date: Thu, 4 Dec 2014 12:27:53 +0100 > > Subject: [PATCH] lavfi: add tblend filter > > > > --- > > libavfilter/Makefile | 1 + > > libavfilter/allfilters.c | 1 + > > libavfilter/vf_blend.c | 73 > > +++++++++++++++++++++++++++++++++++++++++++++++- > > 3 files changed, 74 insertions(+), 1 deletion(-) > > > > diff --git a/libavfilter/Makefile b/libavfilter/Makefile > > index d41a52e..d1be7e3 100644 > > --- a/libavfilter/Makefile > > +++ b/libavfilter/Makefile > > @@ -187,6 +187,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_TDIFF_FILTER) += vf_tdiff.o > > OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o > > OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o > > diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c > > index f13f82d..e8914cc 100644 > > --- a/libavfilter/allfilters.c > > +++ b/libavfilter/allfilters.c > > @@ -202,6 +202,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(TDIFF, tdiff, vf); > > REGISTER_FILTER(TELECINE, telecine, vf); > > REGISTER_FILTER(THUMBNAIL, thumbnail, vf); > > diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c > > index 8bf19ff..3c1572c 100644 > > --- a/libavfilter/vf_blend.c > > +++ b/libavfilter/vf_blend.c > > @@ -95,6 +95,8 @@ typedef struct { > > double all_opacity; > > > > FilterParams params[4]; > > + int tblend; > > + AVFrame *prev_frame; /* only used with tblend */ > > } BlendContext; > > > > #define OFFSET(x) offsetof(BlendContext, x) > > @@ -285,7 +287,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; > > } > > @@ -295,6 +298,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]; > > > > @@ -412,6 +417,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); > > } > > @@ -463,3 +470,67 @@ 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; > > Do you need that? (it's single input)
>From internal.h: /** * Frame requests may need to loop in order to be fulfilled. * A filter must set this flags on an output link if it may return 0 in * request_frame() without filtering a frame. */ FF_LINK_FLAG_REQUEST_LOOP = 1, I need to set this, since the filter caches the first frame, and will output only when the second frame arrives. Indeed if I comment the line I get: Assertion !link->frame_requested || link->flags & FF_LINK_FLAG_REQUEST_LOOP failed at libavfilter/avfilter.c:368 > > > + > > + 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; > > +} > > + > > +#define tblend_options blend_options > > +AVFILTER_DEFINE_CLASS(tblend); > > + > > You probably want to sort out the shortest/repeatlast options Good point, should be fixed. > > +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_SUPPORT_TIMELINE_INTERNAL | > > AVFILTER_FLAG_SLICE_THREADS, > > Does the timeline works as expected? Like, is it caching the previous > frame even when skipping segment? I disabled it, since I have no use case for it at the moment. > > +}; > > -- > > 1.8.3.2 > > > > > From 65455601f502dd148bcd140f8d70eef104b1bdbe Mon Sep 17 00:00:00 2001 > > From: Stefano Sabatini <stefa...@gmail.com> > > Date: Thu, 4 Dec 2014 12:34:30 +0100 > > Subject: [PATCH] lavfi/blend: add difference128 mode > > > > --- > > libavfilter/vf_blend.c | 4 ++++ > > 1 file changed, 4 insertions(+) > > > > diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c > > index 3c1572c..e73ea35 100644 > > --- a/libavfilter/vf_blend.c > > +++ b/libavfilter/vf_blend.c > > @@ -41,6 +41,7 @@ enum BlendMode { > > BLEND_BURN, > > BLEND_DARKEN, > > BLEND_DIFFERENCE, > > + BLEND_DIFFERENCE128, > > BLEND_DIVIDE, > > BLEND_DODGE, > > BLEND_EXCLUSION, > > @@ -114,6 +115,7 @@ static const AVOption blend_options[] = { > > { "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" }, > > @@ -192,6 +194,7 @@ DEFINE_BLEND(subtract, FFMAX(0, A - B)) > > DEFINE_BLEND(multiply, MULTIPLY(1, A, B)) > > DEFINE_BLEND(negation, 255 - FFABS(255 - A - B)) > > DEFINE_BLEND(difference, FFABS(A - B)) > > +DEFINE_BLEND(difference128, av_clip_uint8(128 + A - B)) > > DEFINE_BLEND(screen, SCREEN(1, A, B)) > > DEFINE_BLEND(overlay, (A < 128) ? MULTIPLY(2, A, B) : SCREEN(2, A, B)) > > DEFINE_BLEND(hardlight, (B < 128) ? MULTIPLY(2, B, A) : SCREEN(2, B, A)) > > @@ -315,6 +318,7 @@ static av_cold int init(AVFilterContext *ctx) > > case BLEND_BURN: param->blend = blend_burn; break; > > case BLEND_DARKEN: param->blend = blend_darken; break; > > case BLEND_DIFFERENCE: param->blend = blend_difference; break; > > + case BLEND_DIFFERENCE128: param->blend = blend_difference128; > > break; > > case BLEND_DIVIDE: param->blend = blend_divide; break; > > case BLEND_DODGE: param->blend = blend_dodge; break; > > case BLEND_EXCLUSION: param->blend = blend_exclusion; break; > > I'm not a maintainer of blend but it looks OK (assuming previous comments > are honored). > > Would be nice to complete doc but I suppose you won't forget them. Check new patch. -- FFmpeg = Faithful and Foolish Mortal Pitiless Easy Guru
>From 3310ce2542545e45204337b25651cdf624f1e9b8 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini <stefa...@gmail.com> Date: Thu, 4 Dec 2014 12:27:53 +0100 Subject: [PATCH] lavfi: add tblend filter TODO: update changelog, bump minor --- doc/filters.texi | 24 +++++-- libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_blend.c | 160 +++++++++++++++++++++++++++++++++++------------ 4 files changed, 139 insertions(+), 47 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 44095b4..72b6850 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -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 consecutives 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 @code{blend}. + @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 @code{blend}. @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 diff --git a/libavfilter/Makefile b/libavfilter/Makefile index ed7f49f..22e12b9 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -189,6 +189,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_TDIFF_FILTER) += vf_tdiff.o OBJS-$(CONFIG_TELECINE_FILTER) += vf_telecine.o OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 99680b9..8584a2b 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -204,6 +204,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(TDIFF, tdiff, vf); REGISTER_FILTER(TELECINE, telecine, vf); REGISTER_FILTER(THUMBNAIL, thumbnail, vf); diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c index 1b3ba95..6dbf149 100644 --- a/libavfilter/vf_blend.c +++ b/libavfilter/vf_blend.c @@ -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, +}; -- 1.8.3.2
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel