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

Reply via email to