Browse code

lavfi: add tblend filter

Stefano Sabatini authored on 2014/12/04 20:27:53
Showing 6 changed files
... ...
@@ -8,6 +8,7 @@ version <next>:
8 8
 - RIFX format for *.wav files
9 9
 - RTP/mpegts muxer
10 10
 - non continuous cache protocol support
11
+- tblend filter
11 12
 
12 13
 
13 14
 version 2.5:
... ...
@@ -2634,13 +2634,17 @@ The threshold below which a pixel value is considered black; it defaults to
2634 2634
 
2635 2635
 @end table
2636 2636
 
2637
-@section blend
2637
+@section blend, tblend
2638 2638
 
2639 2639
 Blend two video frames into each other.
2640 2640
 
2641
-It takes two input streams and outputs one stream, the first input is the
2642
-"top" layer and second input is "bottom" layer.
2643
-Output terminates when shortest input terminates.
2641
+The @code{blend} filter takes two input streams and outputs one
2642
+stream, the first input is the "top" layer and second input is
2643
+"bottom" layer.  Output terminates when shortest input terminates.
2644
+
2645
+The @code{tblend} (time blend) filter takes two consecutive frames
2646
+from one single stream, and outputs the result obtained by blending
2647
+the new frame on top of the old frame.
2644 2648
 
2645 2649
 A description of the accepted options follows.
2646 2650
 
... ...
@@ -2730,11 +2734,13 @@ Value of pixel component at current location for second video frame (bottom laye
2730 2730
 @end table
2731 2731
 
2732 2732
 @item shortest
2733
-Force termination when the shortest input terminates. Default is @code{0}.
2733
+Force termination when the shortest input terminates. Default is
2734
+@code{0}. This option is only defined for the @code{blend} filter.
2735
+
2734 2736
 @item repeatlast
2735 2737
 Continue applying the last bottom frame after the end of the stream. A value of
2736 2738
 @code{0} disable the filter after the last frame of the bottom layer is reached.
2737
-Default is @code{1}.
2739
+Default is @code{1}. This option is only defined for the @code{blend} filter.
2738 2740
 @end table
2739 2741
 
2740 2742
 @subsection Examples
... ...
@@ -2769,6 +2775,12 @@ Apply uncover up-left effect:
2769 2769
 @example
2770 2770
 blend=all_expr='if(gte(T*SH*40+Y,H)*gte((T*40*SW+X)*W/H,W),A,B)'
2771 2771
 @end example
2772
+
2773
+@item
2774
+Display differences between the current and the previous frame:
2775
+@example
2776
+tblend=all_mode=difference128
2777
+@end example
2772 2778
 @end itemize
2773 2779
 
2774 2780
 @section boxblur
... ...
@@ -188,6 +188,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               += vf_stereo3d.o
188 188
 OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
189 189
 OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
190 190
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
191
+OBJS-$(CONFIG_TBLEND_FILTER)                 += vf_blend.o
191 192
 OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
192 193
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
193 194
 OBJS-$(CONFIG_TILE_FILTER)                   += vf_tile.o
... ...
@@ -203,6 +203,7 @@ void avfilter_register_all(void)
203 203
     REGISTER_FILTER(SUBTITLES,      subtitles,      vf);
204 204
     REGISTER_FILTER(SUPER2XSAI,     super2xsai,     vf);
205 205
     REGISTER_FILTER(SWAPUV,         swapuv,         vf);
206
+    REGISTER_FILTER(TBLEND,         tblend,         vf);
206 207
     REGISTER_FILTER(TELECINE,       telecine,       vf);
207 208
     REGISTER_FILTER(THUMBNAIL,      thumbnail,      vf);
208 209
     REGISTER_FILTER(TILE,           tile,           vf);
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR  5
33
-#define LIBAVFILTER_VERSION_MINOR  5
34
-#define LIBAVFILTER_VERSION_MICRO 102
33
+#define LIBAVFILTER_VERSION_MINOR  6
34
+#define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
37 37
                                                LIBAVFILTER_VERSION_MINOR, \
... ...
@@ -96,52 +96,57 @@ typedef struct {
96 96
     double all_opacity;
97 97
 
98 98
     FilterParams params[4];
99
+    int tblend;
100
+    AVFrame *prev_frame;        /* only used with tblend */
99 101
 } BlendContext;
100 102
 
103
+#define COMMON_OPTIONS \
104
+    { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
105
+    { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
106
+    { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
107
+    { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},\
108
+    { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, "mode"},\
109
+    { "addition",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION},   0, 0, FLAGS, "mode" },\
110
+    { "and",        "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND},        0, 0, FLAGS, "mode" },\
111
+    { "average",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE},    0, 0, FLAGS, "mode" },\
112
+    { "burn",       "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN},       0, 0, FLAGS, "mode" },\
113
+    { "darken",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN},     0, 0, FLAGS, "mode" },\
114
+    { "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, "mode" },\
115
+    { "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE128}, 0, 0, FLAGS, "mode" },\
116
+    { "divide",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE},     0, 0, FLAGS, "mode" },\
117
+    { "dodge",      "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE},      0, 0, FLAGS, "mode" },\
118
+    { "exclusion",  "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION},  0, 0, FLAGS, "mode" },\
119
+    { "hardlight",  "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT},  0, 0, FLAGS, "mode" },\
120
+    { "lighten",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN},    0, 0, FLAGS, "mode" },\
121
+    { "multiply",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY},   0, 0, FLAGS, "mode" },\
122
+    { "negation",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION},   0, 0, FLAGS, "mode" },\
123
+    { "normal",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL},     0, 0, FLAGS, "mode" },\
124
+    { "or",         "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR},         0, 0, FLAGS, "mode" },\
125
+    { "overlay",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY},    0, 0, FLAGS, "mode" },\
126
+    { "phoenix",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX},    0, 0, FLAGS, "mode" },\
127
+    { "pinlight",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT},   0, 0, FLAGS, "mode" },\
128
+    { "reflect",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT},    0, 0, FLAGS, "mode" },\
129
+    { "screen",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN},     0, 0, FLAGS, "mode" },\
130
+    { "softlight",  "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT},  0, 0, FLAGS, "mode" },\
131
+    { "subtract",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT},   0, 0, FLAGS, "mode" },\
132
+    { "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, "mode" },\
133
+    { "xor",        "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR},        0, 0, FLAGS, "mode" },\
134
+    { "c0_expr",  "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
135
+    { "c1_expr",  "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
136
+    { "c2_expr",  "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
137
+    { "c3_expr",  "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
138
+    { "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },\
139
+    { "c0_opacity",  "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
140
+    { "c1_opacity",  "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
141
+    { "c2_opacity",  "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
142
+    { "c3_opacity",  "set color component #3 opacity", OFFSET(params[3].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },\
143
+    { "all_opacity", "set opacity for all color components", OFFSET(all_opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS}
144
+
101 145
 #define OFFSET(x) offsetof(BlendContext, x)
102 146
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
103 147
 
104 148
 static const AVOption blend_options[] = {
105
-    { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
106
-    { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
107
-    { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
108
-    { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode"},
109
-    { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, "mode"},
110
-    { "addition",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION},   0, 0, FLAGS, "mode" },
111
-    { "and",        "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND},        0, 0, FLAGS, "mode" },
112
-    { "average",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE},    0, 0, FLAGS, "mode" },
113
-    { "burn",       "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN},       0, 0, FLAGS, "mode" },
114
-    { "darken",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN},     0, 0, FLAGS, "mode" },
115
-    { "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, "mode" },
116
-    { "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE128}, 0, 0, FLAGS, "mode" },
117
-    { "divide",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE},     0, 0, FLAGS, "mode" },
118
-    { "dodge",      "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE},      0, 0, FLAGS, "mode" },
119
-    { "exclusion",  "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION},  0, 0, FLAGS, "mode" },
120
-    { "hardlight",  "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT},  0, 0, FLAGS, "mode" },
121
-    { "lighten",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN},    0, 0, FLAGS, "mode" },
122
-    { "multiply",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY},   0, 0, FLAGS, "mode" },
123
-    { "negation",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION},   0, 0, FLAGS, "mode" },
124
-    { "normal",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL},     0, 0, FLAGS, "mode" },
125
-    { "or",         "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR},         0, 0, FLAGS, "mode" },
126
-    { "overlay",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY},    0, 0, FLAGS, "mode" },
127
-    { "phoenix",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX},    0, 0, FLAGS, "mode" },
128
-    { "pinlight",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT},   0, 0, FLAGS, "mode" },
129
-    { "reflect",    "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT},    0, 0, FLAGS, "mode" },
130
-    { "screen",     "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN},     0, 0, FLAGS, "mode" },
131
-    { "softlight",  "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT},  0, 0, FLAGS, "mode" },
132
-    { "subtract",   "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT},   0, 0, FLAGS, "mode" },
133
-    { "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, "mode" },
134
-    { "xor",        "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR},        0, 0, FLAGS, "mode" },
135
-    { "c0_expr",  "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
136
-    { "c1_expr",  "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
137
-    { "c2_expr",  "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
138
-    { "c3_expr",  "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
139
-    { "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
140
-    { "c0_opacity",  "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
141
-    { "c1_opacity",  "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
142
-    { "c2_opacity",  "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
143
-    { "c3_opacity",  "set color component #3 opacity", OFFSET(params[3].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
144
-    { "all_opacity", "set opacity for all color components", OFFSET(all_opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS},
149
+    COMMON_OPTIONS,
145 150
     { "shortest",    "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
146 151
     { "repeatlast",  "repeat last bottom frame", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
147 152
     { NULL }
... ...
@@ -288,7 +293,8 @@ static AVFrame *blend_frame(AVFilterContext *ctx, AVFrame *top_buf,
288 288
         ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outh, ctx->graph->nb_threads));
289 289
     }
290 290
 
291
-    av_frame_free(&top_buf);
291
+    if (!b->tblend)
292
+        av_frame_free(&top_buf);
292 293
 
293 294
     return dst_buf;
294 295
 }
... ...
@@ -298,6 +304,8 @@ static av_cold int init(AVFilterContext *ctx)
298 298
     BlendContext *b = ctx->priv;
299 299
     int ret, plane;
300 300
 
301
+    b->tblend = !strcmp(ctx->filter->name, "tblend");
302
+
301 303
     for (plane = 0; plane < FF_ARRAY_ELEMS(b->params); plane++) {
302 304
         FilterParams *param = &b->params[plane];
303 305
 
... ...
@@ -416,6 +424,8 @@ static av_cold void uninit(AVFilterContext *ctx)
416 416
     int i;
417 417
 
418 418
     ff_dualinput_uninit(&b->dinput);
419
+    av_freep(&b->prev_frame);
420
+
419 421
     for (i = 0; i < FF_ARRAY_ELEMS(b->params); i++)
420 422
         av_expr_free(b->params[i].e);
421 423
 }
... ...
@@ -467,3 +477,71 @@ AVFilter ff_vf_blend = {
467 467
     .priv_class    = &blend_class,
468 468
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
469 469
 };
470
+
471
+static int tblend_config_output(AVFilterLink *outlink)
472
+{
473
+    AVFilterContext *ctx = outlink->src;
474
+    AVFilterLink *inlink = ctx->inputs[0];
475
+    BlendContext *b = ctx->priv;
476
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
477
+
478
+    b->hsub = pix_desc->log2_chroma_w;
479
+    b->vsub = pix_desc->log2_chroma_h;
480
+    b->nb_planes = av_pix_fmt_count_planes(inlink->format);
481
+    outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
482
+
483
+    return 0;
484
+}
485
+
486
+static int tblend_filter_frame(AVFilterLink *inlink, AVFrame *frame)
487
+{
488
+    BlendContext *b = inlink->dst->priv;
489
+    AVFilterLink *outlink = inlink->dst->outputs[0];
490
+
491
+    if (b->prev_frame) {
492
+        AVFrame *out = blend_frame(inlink->dst, frame, b->prev_frame);
493
+        av_frame_free(&b->prev_frame);
494
+        b->prev_frame = frame;
495
+        return ff_filter_frame(outlink, out);
496
+    }
497
+    b->prev_frame = frame;
498
+    return 0;
499
+}
500
+
501
+static const AVOption tblend_options[] = {
502
+    COMMON_OPTIONS,
503
+    { NULL }
504
+};
505
+
506
+AVFILTER_DEFINE_CLASS(tblend);
507
+
508
+static const AVFilterPad tblend_inputs[] = {
509
+    {
510
+        .name          = "default",
511
+        .type          = AVMEDIA_TYPE_VIDEO,
512
+        .filter_frame  = tblend_filter_frame,
513
+    },
514
+    { NULL }
515
+};
516
+
517
+static const AVFilterPad tblend_outputs[] = {
518
+    {
519
+        .name          = "default",
520
+        .type          = AVMEDIA_TYPE_VIDEO,
521
+        .config_props  = tblend_config_output,
522
+    },
523
+    { NULL }
524
+};
525
+
526
+AVFilter ff_vf_tblend = {
527
+    .name          = "tblend",
528
+    .description   = NULL_IF_CONFIG_SMALL("Blend successive frames."),
529
+    .priv_size     = sizeof(BlendContext),
530
+    .priv_class    = &blend_class,
531
+    .query_formats = query_formats,
532
+    .init          = init,
533
+    .uninit        = uninit,
534
+    .inputs        = tblend_inputs,
535
+    .outputs       = tblend_outputs,
536
+    .flags         = AVFILTER_FLAG_SLICE_THREADS,
537
+};