Browse code

avfilter: add tlut2 filter

Paul B Mahol authored on 2017/08/04 17:29:12
Showing 6 changed files
... ...
@@ -30,6 +30,7 @@ version <next>:
30 30
 - libvmaf video filter
31 31
 - Dolby E decoder and SMPTE 337M demuxer
32 32
 - unpremultiply video filter
33
+- tlut2 video filter
33 34
 
34 35
 version 3.3:
35 36
 - CrystalHD decoder moved to new decode API
... ...
@@ -9946,9 +9946,13 @@ lutyuv=u='(val-maxval/2)*2+maxval/2':v='(val-maxval/2)*2+maxval/2'
9946 9946
 @end example
9947 9947
 @end itemize
9948 9948
 
9949
-@section lut2
9949
+@section lut2, tlut2
9950 9950
 
9951
-Compute and apply a lookup table from two video inputs.
9951
+The @code{lut2} filter takes two input streams and outputs one
9952
+stream.
9953
+
9954
+The @code{tlut2} (time lut2) filter takes two consecutive frames
9955
+from one single stream.
9952 9956
 
9953 9957
 This filter accepts the following parameters:
9954 9958
 @table @option
... ...
@@ -312,6 +312,7 @@ OBJS-$(CONFIG_THRESHOLD_FILTER)              += vf_threshold.o framesync2.o
312 312
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
313 313
 OBJS-$(CONFIG_TILE_FILTER)                   += vf_tile.o
314 314
 OBJS-$(CONFIG_TINTERLACE_FILTER)             += vf_tinterlace.o
315
+OBJS-$(CONFIG_TLUT2_FILTER)                  += vf_lut2.o framesync2.o
315 316
 OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o
316 317
 OBJS-$(CONFIG_TRIM_FILTER)                   += trim.o
317 318
 OBJS-$(CONFIG_UNPREMULTIPLY_FILTER)          += vf_premultiply.o framesync2.o
... ...
@@ -323,6 +323,7 @@ static void register_all(void)
323 323
     REGISTER_FILTER(THUMBNAIL,      thumbnail,      vf);
324 324
     REGISTER_FILTER(TILE,           tile,           vf);
325 325
     REGISTER_FILTER(TINTERLACE,     tinterlace,     vf);
326
+    REGISTER_FILTER(TLUT2,          tlut2,          vf);
326 327
     REGISTER_FILTER(TRANSPOSE,      transpose,      vf);
327 328
     REGISTER_FILTER(TRIM,           trim,           vf);
328 329
     REGISTER_FILTER(UNPREMULTIPLY,  unpremultiply,  vf);
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   6
33
-#define LIBAVFILTER_VERSION_MINOR  96
34
-#define LIBAVFILTER_VERSION_MICRO 101
33
+#define LIBAVFILTER_VERSION_MINOR  97
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, \
... ...
@@ -61,6 +61,8 @@ typedef struct LUT2Context {
61 61
     int width[4], height[4];
62 62
     int nb_planes;
63 63
     int depth, depthx, depthy;
64
+    int tlut2;
65
+    AVFrame *prev_frame;        /* only used with tlut2 */
64 66
 
65 67
     void (*lut2)(struct LUT2Context *s, AVFrame *dst, AVFrame *srcx, AVFrame *srcy);
66 68
 
... ...
@@ -70,7 +72,7 @@ typedef struct LUT2Context {
70 70
 #define OFFSET(x) offsetof(LUT2Context, x)
71 71
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
72 72
 
73
-static const AVOption lut2_options[] = {
73
+static const AVOption options[] = {
74 74
     { "c0", "set component #0 expression", OFFSET(comp_expr_str[0]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = FLAGS },
75 75
     { "c1", "set component #1 expression", OFFSET(comp_expr_str[1]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = FLAGS },
76 76
     { "c2", "set component #2 expression", OFFSET(comp_expr_str[2]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = FLAGS },
... ...
@@ -83,6 +85,8 @@ static av_cold void uninit(AVFilterContext *ctx)
83 83
     LUT2Context *s = ctx->priv;
84 84
     int i;
85 85
 
86
+    av_frame_free(&s->prev_frame);
87
+
86 88
     for (i = 0; i < 4; i++) {
87 89
         av_expr_free(s->comp_expr[i]);
88 90
         s->comp_expr[i] = NULL;
... ...
@@ -133,6 +137,11 @@ static int config_inputx(AVFilterLink *inlink)
133 133
     s->depthx = desc->comp[0].depth;
134 134
     s->var_values[VAR_BITDEPTHX] = s->depthx;
135 135
 
136
+    if (s->tlut2) {
137
+        s->depthy = desc->comp[0].depth;
138
+        s->var_values[VAR_BITDEPTHY] = s->depthy;
139
+    }
140
+
136 141
     return 0;
137 142
 }
138 143
 
... ...
@@ -232,13 +241,64 @@ static int config_output(AVFilterLink *outlink)
232 232
 {
233 233
     AVFilterContext *ctx = outlink->src;
234 234
     LUT2Context *s = ctx->priv;
235
-    AVFilterLink *srcx = ctx->inputs[0];
236
-    AVFilterLink *srcy = ctx->inputs[1];
237
-    FFFrameSyncIn *in;
238 235
     int p, ret;
239 236
 
240 237
     s->depth = s->depthx + s->depthy;
241 238
 
239
+    s->lut2 = s->depth > 16 ? lut2_16bit : lut2_8bit;
240
+
241
+    for (p = 0; p < s->nb_planes; p++) {
242
+        s->lut[p] = av_malloc_array(1 << s->depth, sizeof(uint16_t));
243
+        if (!s->lut[p])
244
+            return AVERROR(ENOMEM);
245
+    }
246
+
247
+    for (p = 0; p < s->nb_planes; p++) {
248
+        double res;
249
+        int x, y;
250
+
251
+        /* create the parsed expression */
252
+        av_expr_free(s->comp_expr[p]);
253
+        s->comp_expr[p] = NULL;
254
+        ret = av_expr_parse(&s->comp_expr[p], s->comp_expr_str[p],
255
+                            var_names, NULL, NULL, NULL, NULL, 0, ctx);
256
+        if (ret < 0) {
257
+            av_log(ctx, AV_LOG_ERROR,
258
+                   "Error when parsing the expression '%s' for the component %d.\n",
259
+                   s->comp_expr_str[p], p);
260
+            return AVERROR(EINVAL);
261
+        }
262
+
263
+        /* compute the lut */
264
+        for (y = 0; y < (1 << s->depthx); y++) {
265
+            s->var_values[VAR_Y] = y;
266
+            for (x = 0; x < (1 << s->depthx); x++) {
267
+                s->var_values[VAR_X] = x;
268
+                res = av_expr_eval(s->comp_expr[p], s->var_values, s);
269
+                if (isnan(res)) {
270
+                    av_log(ctx, AV_LOG_ERROR,
271
+                           "Error when evaluating the expression '%s' for the values %d and %d for the component %d.\n",
272
+                           s->comp_expr_str[p], x, y, p);
273
+                    return AVERROR(EINVAL);
274
+                }
275
+
276
+                s->lut[p][(y << s->depthx) + x] = res;
277
+            }
278
+        }
279
+    }
280
+
281
+    return 0;
282
+}
283
+
284
+static int lut2_config_output(AVFilterLink *outlink)
285
+{
286
+    AVFilterContext *ctx = outlink->src;
287
+    LUT2Context *s = ctx->priv;
288
+    AVFilterLink *srcx = ctx->inputs[0];
289
+    AVFilterLink *srcy = ctx->inputs[1];
290
+    FFFrameSyncIn *in;
291
+    int ret;
292
+
242 293
     if (srcx->format != srcy->format) {
243 294
         av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
244 295
         return AVERROR(EINVAL);
... ...
@@ -281,47 +341,8 @@ static int config_output(AVFilterLink *outlink)
281 281
     s->fs.opaque   = s;
282 282
     s->fs.on_event = process_frame;
283 283
 
284
-    s->lut2 = s->depth > 16 ? lut2_16bit : lut2_8bit;
285
-
286
-    for (p = 0; p < s->nb_planes; p++) {
287
-        s->lut[p] = av_malloc_array(1 << s->depth, sizeof(uint16_t));
288
-        if (!s->lut[p])
289
-            return AVERROR(ENOMEM);
290
-    }
291
-
292
-    for (p = 0; p < s->nb_planes; p++) {
293
-        double res;
294
-        int x, y;
295
-
296
-        /* create the parsed expression */
297
-        av_expr_free(s->comp_expr[p]);
298
-        s->comp_expr[p] = NULL;
299
-        ret = av_expr_parse(&s->comp_expr[p], s->comp_expr_str[p],
300
-                            var_names, NULL, NULL, NULL, NULL, 0, ctx);
301
-        if (ret < 0) {
302
-            av_log(ctx, AV_LOG_ERROR,
303
-                   "Error when parsing the expression '%s' for the component %d.\n",
304
-                   s->comp_expr_str[p], p);
305
-            return AVERROR(EINVAL);
306
-        }
307
-
308
-        /* compute the lut */
309
-        for (y = 0; y < (1 << s->depthx); y++) {
310
-            s->var_values[VAR_Y] = y;
311
-            for (x = 0; x < (1 << s->depthx); x++) {
312
-                s->var_values[VAR_X] = x;
313
-                res = av_expr_eval(s->comp_expr[p], s->var_values, s);
314
-                if (isnan(res)) {
315
-                    av_log(ctx, AV_LOG_ERROR,
316
-                           "Error when evaluating the expression '%s' for the values %d and %d for the component %d.\n",
317
-                           s->comp_expr_str[p], x, y, p);
318
-                    return AVERROR(EINVAL);
319
-                }
320
-
321
-                s->lut[p][(y << s->depthx) + x] = res;
322
-            }
323
-        }
324
-    }
284
+    if ((ret = config_output(outlink)) < 0)
285
+        return ret;
325 286
 
326 287
     return ff_framesync2_configure(&s->fs);
327 288
 }
... ...
@@ -350,11 +371,13 @@ static const AVFilterPad outputs[] = {
350 350
     {
351 351
         .name          = "default",
352 352
         .type          = AVMEDIA_TYPE_VIDEO,
353
-        .config_props  = config_output,
353
+        .config_props  = lut2_config_output,
354 354
     },
355 355
     { NULL }
356 356
 };
357 357
 
358
+#define lut2_options options
359
+
358 360
 AVFILTER_DEFINE_CLASS(lut2);
359 361
 
360 362
 AVFilter ff_vf_lut2 = {
... ...
@@ -369,3 +392,73 @@ AVFilter ff_vf_lut2 = {
369 369
     .outputs       = outputs,
370 370
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
371 371
 };
372
+
373
+#if CONFIG_TLUT2_FILTER
374
+
375
+static av_cold int init(AVFilterContext *ctx)
376
+{
377
+    LUT2Context *s = ctx->priv;
378
+
379
+    s->tlut2 = !strcmp(ctx->filter->name, "tlut2");
380
+
381
+    return 0;
382
+}
383
+
384
+static int tlut2_filter_frame(AVFilterLink *inlink, AVFrame *frame)
385
+{
386
+    LUT2Context *s = inlink->dst->priv;
387
+    AVFilterLink *outlink = inlink->dst->outputs[0];
388
+
389
+    if (s->prev_frame) {
390
+        AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
391
+        if (!out) {
392
+            av_frame_free(&s->prev_frame);
393
+            s->prev_frame = frame;
394
+            return AVERROR(ENOMEM);
395
+        }
396
+        av_frame_copy_props(out, frame);
397
+        s->lut2(s, out, frame, s->prev_frame);
398
+        av_frame_free(&s->prev_frame);
399
+        s->prev_frame = frame;
400
+        return ff_filter_frame(outlink, out);
401
+    }
402
+    s->prev_frame = frame;
403
+    return 0;
404
+}
405
+
406
+#define tlut2_options options
407
+
408
+AVFILTER_DEFINE_CLASS(tlut2);
409
+
410
+static const AVFilterPad tlut2_inputs[] = {
411
+    {
412
+        .name          = "default",
413
+        .type          = AVMEDIA_TYPE_VIDEO,
414
+        .filter_frame  = tlut2_filter_frame,
415
+        .config_props  = config_inputx,
416
+    },
417
+    { NULL }
418
+};
419
+
420
+static const AVFilterPad tlut2_outputs[] = {
421
+    {
422
+        .name          = "default",
423
+        .type          = AVMEDIA_TYPE_VIDEO,
424
+        .config_props  = config_output,
425
+    },
426
+    { NULL }
427
+};
428
+
429
+AVFilter ff_vf_tlut2 = {
430
+    .name          = "tlut2",
431
+    .description   = NULL_IF_CONFIG_SMALL("Compute and apply a lookup table from two successive frames."),
432
+    .priv_size     = sizeof(LUT2Context),
433
+    .priv_class    = &tlut2_class,
434
+    .query_formats = query_formats,
435
+    .init          = init,
436
+    .uninit        = uninit,
437
+    .inputs        = tlut2_inputs,
438
+    .outputs       = tlut2_outputs,
439
+};
440
+
441
+#endif