]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_lut2.c
avfilter: Constify all AVFilters
[ffmpeg] / libavfilter / vf_lut2.c
1 /*
2  * Copyright (c) 2016 Paul B Mahol
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include "libavutil/attributes.h"
22 #include "libavutil/common.h"
23 #include "libavutil/eval.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "avfilter.h"
27 #include "drawutils.h"
28 #include "formats.h"
29 #include "internal.h"
30 #include "video.h"
31 #include "framesync.h"
32
33 static const char *const var_names[] = {
34     "w",        ///< width of the input video
35     "h",        ///< height of the input video
36     "x",        ///< input value for the pixel from input #1
37     "y",        ///< input value for the pixel from input #2
38     "bdx",      ///< input #1 video bitdepth
39     "bdy",      ///< input #2 video bitdepth
40     NULL
41 };
42
43 enum var_name {
44     VAR_W,
45     VAR_H,
46     VAR_X,
47     VAR_Y,
48     VAR_BITDEPTHX,
49     VAR_BITDEPTHY,
50     VAR_VARS_NB
51 };
52
53 typedef struct LUT2Context {
54     const AVClass *class;
55     FFFrameSync fs;
56
57     int odepth;
58     char   *comp_expr_str[4];
59
60     AVExpr *comp_expr[4];
61     double var_values[VAR_VARS_NB];
62     uint16_t *lut[4];  ///< lookup table for each component
63     int width[4], height[4];
64     int widthx[4], heightx[4];
65     int widthy[4], heighty[4];
66     int nb_planesx;
67     int nb_planesy;
68     int nb_planes;
69     int depth, depthx, depthy;
70     int tlut2;
71     AVFrame *prev_frame;        /* only used with tlut2 */
72
73     int (*lut2)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
74 } LUT2Context;
75
76 typedef struct ThreadData {
77     AVFrame *out, *srcx, *srcy;
78 } ThreadData;
79
80 #define OFFSET(x) offsetof(LUT2Context, x)
81 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
82 #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
83
84 static const AVOption options[] = {
85     { "c0", "set component #0 expression", OFFSET(comp_expr_str[0]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
86     { "c1", "set component #1 expression", OFFSET(comp_expr_str[1]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
87     { "c2", "set component #2 expression", OFFSET(comp_expr_str[2]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
88     { "c3", "set component #3 expression", OFFSET(comp_expr_str[3]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
89     { "d",  "set output depth",            OFFSET(odepth),            AV_OPT_TYPE_INT,    { .i64 =  0  }, 0, 16, .flags = FLAGS },
90     { NULL }
91 };
92
93 static av_cold void uninit(AVFilterContext *ctx)
94 {
95     LUT2Context *s = ctx->priv;
96     int i;
97
98     ff_framesync_uninit(&s->fs);
99     av_frame_free(&s->prev_frame);
100
101     for (i = 0; i < 4; i++) {
102         av_expr_free(s->comp_expr[i]);
103         s->comp_expr[i] = NULL;
104         av_freep(&s->comp_expr_str[i]);
105         av_freep(&s->lut[i]);
106     }
107 }
108
109 #define BIT8_FMTS \
110     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, \
111     AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, \
112     AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, \
113     AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, \
114     AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, \
115     AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
116
117 #define BIT9_FMTS \
118     AV_PIX_FMT_GBRP9, AV_PIX_FMT_GRAY9, \
119     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, \
120     AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
121
122 #define BIT10_FMTS \
123     AV_PIX_FMT_GRAY10, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, \
124     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, \
125     AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
126
127 #define BIT12_FMTS \
128     AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, \
129     AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, \
130     AV_PIX_FMT_GRAY12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRP12,
131
132 #define BIT14_FMTS \
133     AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, \
134     AV_PIX_FMT_GRAY14, AV_PIX_FMT_GBRP14,
135
136 #define BIT16_FMTS \
137     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, \
138     AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, \
139     AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16,
140
141 static int query_formats(AVFilterContext *ctx)
142 {
143     LUT2Context *s = ctx->priv;
144     static const enum AVPixelFormat all_pix_fmts[] = {
145         BIT8_FMTS
146         BIT9_FMTS
147         BIT10_FMTS
148         BIT12_FMTS
149         AV_PIX_FMT_NONE
150     };
151     static const enum AVPixelFormat bit8_pix_fmts[] = {
152         BIT8_FMTS
153         AV_PIX_FMT_NONE
154     };
155     static const enum AVPixelFormat bit9_pix_fmts[] = {
156         BIT9_FMTS
157         AV_PIX_FMT_NONE
158     };
159     static const enum AVPixelFormat bit10_pix_fmts[] = {
160         BIT10_FMTS
161         AV_PIX_FMT_NONE
162     };
163     static const enum AVPixelFormat bit12_pix_fmts[] = {
164         BIT12_FMTS
165         AV_PIX_FMT_NONE
166     };
167     static const enum AVPixelFormat bit14_pix_fmts[] = {
168         BIT14_FMTS
169         AV_PIX_FMT_NONE
170     };
171     static const enum AVPixelFormat bit16_pix_fmts[] = {
172         BIT16_FMTS
173         AV_PIX_FMT_NONE
174     };
175     const enum AVPixelFormat *pix_fmts;
176     int ret;
177
178     if (s->tlut2 || !s->odepth)
179         return ff_set_common_formats(ctx, ff_make_format_list(all_pix_fmts));
180
181     ret = ff_formats_ref(ff_make_format_list(all_pix_fmts), &ctx->inputs[0]->outcfg.formats);
182     if (ret < 0)
183         return ret;
184
185     switch (s->odepth) {
186     case 8:  pix_fmts = bit8_pix_fmts;  break;
187     case 9:  pix_fmts = bit9_pix_fmts;  break;
188     case 10: pix_fmts = bit10_pix_fmts; break;
189     case 12: pix_fmts = bit12_pix_fmts; break;
190     case 14: pix_fmts = bit14_pix_fmts; break;
191     case 16: pix_fmts = bit16_pix_fmts; break;
192     default: av_log(ctx, AV_LOG_ERROR, "Unsupported output bit depth %d.\n", s->odepth);
193              return AVERROR(EINVAL);
194     }
195
196     return ff_formats_ref(ff_make_format_list(pix_fmts), &ctx->outputs[0]->incfg.formats);
197 }
198
199 static int config_inputx(AVFilterLink *inlink)
200 {
201     AVFilterContext *ctx = inlink->dst;
202     LUT2Context *s = ctx->priv;
203     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
204     int hsub = desc->log2_chroma_w;
205     int vsub = desc->log2_chroma_h;
206
207     s->nb_planesx = av_pix_fmt_count_planes(inlink->format);
208     s->heightx[1] = s->heightx[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
209     s->heightx[0] = s->heightx[3] = inlink->h;
210     s->widthx[1]  = s->widthx[2]  = AV_CEIL_RSHIFT(inlink->w, hsub);
211     s->widthx[0]  = s->widthx[3]  = inlink->w;
212
213     s->var_values[VAR_W] = inlink->w;
214     s->var_values[VAR_H] = inlink->h;
215     s->depthx = desc->comp[0].depth;
216     s->var_values[VAR_BITDEPTHX] = s->depthx;
217
218     if (s->tlut2) {
219         s->depthy = desc->comp[0].depth;
220         s->var_values[VAR_BITDEPTHY] = s->depthy;
221     }
222
223     return 0;
224 }
225
226 static int config_inputy(AVFilterLink *inlink)
227 {
228     AVFilterContext *ctx = inlink->dst;
229     LUT2Context *s = ctx->priv;
230     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
231     int hsub = desc->log2_chroma_w;
232     int vsub = desc->log2_chroma_h;
233
234     s->nb_planesy = av_pix_fmt_count_planes(inlink->format);
235     s->depthy = desc->comp[0].depth;
236     s->var_values[VAR_BITDEPTHY] = s->depthy;
237     s->heighty[1] = s->heighty[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
238     s->heighty[0] = s->heighty[3] = inlink->h;
239     s->widthy[1]  = s->widthy[2]  = AV_CEIL_RSHIFT(inlink->w, hsub);
240     s->widthy[0]  = s->widthy[3]  = inlink->w;
241
242     return 0;
243 }
244
245 #define DEFINE_LUT2(zname, xname, yname, ztype, xtype, ytype, zdiv, xdiv, ydiv)  \
246 static int lut2_##zname##_##xname##_##yname(AVFilterContext *ctx,                \
247                                              void *arg,                          \
248                                              int jobnr, int nb_jobs)             \
249 {                                                                                \
250     LUT2Context *s = ctx->priv;                                                  \
251     ThreadData *td = arg;                                                        \
252     AVFrame *out = td->out;                                                      \
253     AVFrame *srcx = td->srcx;                                                    \
254     AVFrame *srcy = td->srcy;                                                    \
255     const int odepth = s->odepth;                                                \
256     int p, y, x;                                                                 \
257                                                                                  \
258     for (p = 0; p < s->nb_planes; p++) {                                         \
259         const int slice_start = (s->heightx[p] * jobnr) / nb_jobs;               \
260         const int slice_end = (s->heightx[p] * (jobnr+1)) / nb_jobs;             \
261         const uint16_t *lut = s->lut[p];                                         \
262         const xtype *srcxx;                                                      \
263         const ytype *srcyy;                                                      \
264         ztype *dst;                                                              \
265                                                                                  \
266         dst   = (ztype *)(out->data[p] + slice_start * out->linesize[p]);        \
267         srcxx = (const xtype *)(srcx->data[p] + slice_start * srcx->linesize[p]);\
268         srcyy = (const ytype *)(srcy->data[p] + slice_start * srcy->linesize[p]);\
269                                                                                  \
270         for (y = slice_start; y < slice_end; y++) {                              \
271             for (x = 0; x < s->widthx[p]; x++) {                                 \
272                 dst[x] = av_clip_uintp2_c(lut[(srcyy[x] << s->depthx) | srcxx[x]], odepth); \
273             }                                                                    \
274                                                                                  \
275             dst   += out->linesize[p] / zdiv;                                    \
276             srcxx += srcx->linesize[p] / xdiv;                                   \
277             srcyy += srcy->linesize[p] / ydiv;                                   \
278         }                                                                        \
279     }                                                                            \
280     return 0;                                                                    \
281 }
282
283 DEFINE_LUT2(8,   8,  8,  uint8_t,  uint8_t,  uint8_t, 1, 1, 1)
284 DEFINE_LUT2(8,   8, 16,  uint8_t,  uint8_t, uint16_t, 1, 1, 2)
285 DEFINE_LUT2(8,  16,  8,  uint8_t, uint16_t,  uint8_t, 1, 2, 1)
286 DEFINE_LUT2(8,  16, 16,  uint8_t, uint16_t, uint16_t, 1, 2, 2)
287 DEFINE_LUT2(16,  8,  8, uint16_t,  uint8_t,  uint8_t, 2, 1, 1)
288 DEFINE_LUT2(16,  8, 16, uint16_t,  uint8_t, uint16_t, 2, 1, 2)
289 DEFINE_LUT2(16, 16,  8, uint16_t, uint16_t,  uint8_t, 2, 2, 1)
290 DEFINE_LUT2(16, 16, 16, uint16_t, uint16_t, uint16_t, 2, 2, 2)
291
292 static int process_frame(FFFrameSync *fs)
293 {
294     AVFilterContext *ctx = fs->parent;
295     LUT2Context *s = fs->opaque;
296     AVFilterLink *outlink = ctx->outputs[0];
297     AVFrame *out, *srcx = NULL, *srcy = NULL;
298     int ret;
299
300     if ((ret = ff_framesync_get_frame(&s->fs, 0, &srcx, 0)) < 0 ||
301         (ret = ff_framesync_get_frame(&s->fs, 1, &srcy, 0)) < 0)
302         return ret;
303
304     if (ctx->is_disabled || !srcy) {
305         out = av_frame_clone(srcx);
306         if (!out)
307             return AVERROR(ENOMEM);
308     } else {
309         ThreadData td;
310
311         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
312         if (!out)
313             return AVERROR(ENOMEM);
314         av_frame_copy_props(out, srcx);
315
316         td.out  = out;
317         td.srcx = srcx;
318         td.srcy = srcy;
319         ctx->internal->execute(ctx, s->lut2, &td, NULL, FFMIN(s->heightx[1], ff_filter_get_nb_threads(ctx)));
320     }
321
322     out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
323
324     return ff_filter_frame(outlink, out);
325 }
326
327 static int config_output(AVFilterLink *outlink)
328 {
329     AVFilterContext *ctx = outlink->src;
330     LUT2Context *s = ctx->priv;
331     int p, ret;
332
333     s->depth = s->depthx + s->depthy;
334     s->nb_planes = s->nb_planesx;
335
336     s->lut2 = s->depth > 16 ? lut2_16_16_16 : lut2_8_8_8;
337     if (s->odepth) {
338         if (s->depthx == 8 && s->depthy == 8 && s->odepth > 8)
339             s->lut2 = lut2_16_8_8;
340         if (s->depthx > 8 && s->depthy == 8 && s->odepth > 8)
341             s->lut2 = lut2_16_16_8;
342         if (s->depthx == 8 && s->depthy > 8 && s->odepth > 8)
343             s->lut2 = lut2_16_8_16;
344         if (s->depthx == 8 && s->depthy == 8 && s->odepth == 8)
345             s->lut2 = lut2_8_8_8;
346         if (s->depthx > 8 && s->depthy == 8 && s->odepth == 8)
347             s->lut2 = lut2_8_16_8;
348         if (s->depthx == 8 && s->depthy > 8 && s->odepth == 8)
349             s->lut2 = lut2_8_8_16;
350         if (s->depthx > 8 && s->depthy > 8 && s->odepth == 8)
351             s->lut2 = lut2_8_16_16;
352     } else {
353         s->odepth = s->depthx;
354     }
355
356     for (p = 0; p < s->nb_planes; p++) {
357         if (!s->lut[p])
358             s->lut[p] = av_malloc_array(1 << s->depth, sizeof(uint16_t));
359         if (!s->lut[p])
360             return AVERROR(ENOMEM);
361     }
362
363     for (p = 0; p < s->nb_planes; p++) {
364         double res;
365         int x, y;
366
367         /* create the parsed expression */
368         av_expr_free(s->comp_expr[p]);
369         s->comp_expr[p] = NULL;
370         ret = av_expr_parse(&s->comp_expr[p], s->comp_expr_str[p],
371                             var_names, NULL, NULL, NULL, NULL, 0, ctx);
372         if (ret < 0) {
373             av_log(ctx, AV_LOG_ERROR,
374                    "Error when parsing the expression '%s' for the component %d.\n",
375                    s->comp_expr_str[p], p);
376             return AVERROR(EINVAL);
377         }
378
379         /* compute the lut */
380         for (y = 0; y < (1 << s->depthy); y++) {
381             s->var_values[VAR_Y] = y;
382             for (x = 0; x < (1 << s->depthx); x++) {
383                 s->var_values[VAR_X] = x;
384                 res = av_expr_eval(s->comp_expr[p], s->var_values, s);
385                 if (isnan(res)) {
386                     av_log(ctx, AV_LOG_ERROR,
387                            "Error when evaluating the expression '%s' for the values %d and %d for the component %d.\n",
388                            s->comp_expr_str[p], x, y, p);
389                     return AVERROR(EINVAL);
390                 }
391
392                 s->lut[p][(y << s->depthx) + x] = res;
393             }
394         }
395     }
396
397     return 0;
398 }
399
400 static int lut2_config_output(AVFilterLink *outlink)
401 {
402     AVFilterContext *ctx = outlink->src;
403     LUT2Context *s = ctx->priv;
404     AVFilterLink *srcx = ctx->inputs[0];
405     AVFilterLink *srcy = ctx->inputs[1];
406     FFFrameSyncIn *in;
407     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
408     int hsub = desc->log2_chroma_w;
409     int vsub = desc->log2_chroma_h;
410     int ret;
411
412     outlink->w = srcx->w;
413     outlink->h = srcx->h;
414     outlink->time_base = srcx->time_base;
415     outlink->sample_aspect_ratio = srcx->sample_aspect_ratio;
416     outlink->frame_rate = srcx->frame_rate;
417
418     s->nb_planes = av_pix_fmt_count_planes(outlink->format);
419     s->height[1] = s->height[2] = AV_CEIL_RSHIFT(outlink->h, vsub);
420     s->height[0] = s->height[3] = outlink->h;
421     s->width[1]  = s->width[2]  = AV_CEIL_RSHIFT(outlink->w, hsub);
422     s->width[0]  = s->width[3]  = outlink->w;
423
424     if (!s->odepth && srcx->format != srcy->format) {
425         av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
426         return AVERROR(EINVAL);
427     }
428
429     if (srcx->w != srcy->w || srcx->h != srcy->h) {
430         av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
431                "(size %dx%d) do not match the corresponding "
432                "second input link %s parameters (size %dx%d)\n",
433                ctx->input_pads[0].name, srcx->w, srcx->h,
434                ctx->input_pads[1].name,
435                srcy->w, srcy->h);
436         return AVERROR(EINVAL);
437     }
438
439     if (s->nb_planesx != s->nb_planesy) {
440         av_log(ctx, AV_LOG_ERROR, "First input link %s number of planes "
441                "(%d) do not match the corresponding "
442                "second input link %s number of planes (%d)\n",
443                ctx->input_pads[0].name, s->nb_planesx,
444                ctx->input_pads[1].name, s->nb_planesy);
445         return AVERROR(EINVAL);
446     }
447
448     if (s->nb_planesx != s->nb_planes) {
449         av_log(ctx, AV_LOG_ERROR, "First input link %s number of planes "
450                "(%d) do not match the corresponding "
451                "output link %s number of planes (%d)\n",
452                ctx->input_pads[0].name, s->nb_planesx,
453                ctx->output_pads[0].name, s->nb_planes);
454         return AVERROR(EINVAL);
455     }
456
457     if (s->widthx[1] != s->widthy[1] || s->heightx[1] != s->heighty[1]) {
458         av_log(ctx, AV_LOG_ERROR, "First input link %s 2nd plane "
459                "(size %dx%d) do not match the corresponding "
460                "second input link %s 2nd plane (size %dx%d)\n",
461                ctx->input_pads[0].name, s->widthx[1], s->heightx[1],
462                ctx->input_pads[1].name,
463                s->widthy[1], s->heighty[1]);
464         return AVERROR(EINVAL);
465     }
466
467     if (s->widthx[2] != s->widthy[2] || s->heightx[2] != s->heighty[2]) {
468         av_log(ctx, AV_LOG_ERROR, "First input link %s 3rd plane "
469                "(size %dx%d) do not match the corresponding "
470                "second input link %s 3rd plane (size %dx%d)\n",
471                ctx->input_pads[0].name, s->widthx[2], s->heightx[2],
472                ctx->input_pads[1].name,
473                s->widthy[2], s->heighty[2]);
474         return AVERROR(EINVAL);
475     }
476
477     if (s->widthx[1] != s->width[1] || s->heightx[1] != s->height[1]) {
478         av_log(ctx, AV_LOG_ERROR, "First input link %s 2nd plane "
479                "(size %dx%d) do not match the corresponding "
480                "output link %s 2nd plane (size %dx%d)\n",
481                ctx->input_pads[0].name, s->widthx[1], s->heightx[1],
482                ctx->output_pads[0].name, s->width[1], s->height[1]);
483         return AVERROR(EINVAL);
484     }
485
486     if (s->widthx[2] != s->width[2] || s->heightx[2] != s->height[2]) {
487         av_log(ctx, AV_LOG_ERROR, "First input link %s 3rd plane "
488                "(size %dx%d) do not match the corresponding "
489                "output link %s 3rd plane (size %dx%d)\n",
490                ctx->input_pads[0].name, s->widthx[2], s->heightx[2],
491                ctx->output_pads[0].name, s->width[2], s->height[2]);
492         return AVERROR(EINVAL);
493     }
494
495     if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0)
496         return ret;
497
498     in = s->fs.in;
499     in[0].time_base = srcx->time_base;
500     in[1].time_base = srcy->time_base;
501     in[0].sync   = 2;
502     in[0].before = EXT_STOP;
503     in[0].after  = EXT_INFINITY;
504     in[1].sync   = 1;
505     in[1].before = EXT_STOP;
506     in[1].after  = EXT_INFINITY;
507     s->fs.opaque   = s;
508     s->fs.on_event = process_frame;
509
510     if ((ret = config_output(outlink)) < 0)
511         return ret;
512
513     ret = ff_framesync_configure(&s->fs);
514     outlink->time_base = s->fs.time_base;
515
516     return ret;
517 }
518
519 static int activate(AVFilterContext *ctx)
520 {
521     LUT2Context *s = ctx->priv;
522     return ff_framesync_activate(&s->fs);
523 }
524
525 static const AVFilterPad inputs[] = {
526     {
527         .name         = "srcx",
528         .type         = AVMEDIA_TYPE_VIDEO,
529         .config_props = config_inputx,
530     },
531     {
532         .name         = "srcy",
533         .type         = AVMEDIA_TYPE_VIDEO,
534         .config_props = config_inputy,
535     },
536     { NULL }
537 };
538
539 static const AVFilterPad outputs[] = {
540     {
541         .name          = "default",
542         .type          = AVMEDIA_TYPE_VIDEO,
543         .config_props  = lut2_config_output,
544     },
545     { NULL }
546 };
547
548 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
549                            char *res, int res_len, int flags)
550 {
551     int ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
552
553     if (ret < 0)
554         return ret;
555
556     return config_output(ctx->outputs[0]);
557 }
558
559 #define lut2_options options
560
561 FRAMESYNC_DEFINE_CLASS(lut2, LUT2Context, fs);
562
563 const AVFilter ff_vf_lut2 = {
564     .name          = "lut2",
565     .description   = NULL_IF_CONFIG_SMALL("Compute and apply a lookup table from two video inputs."),
566     .preinit       = lut2_framesync_preinit,
567     .priv_size     = sizeof(LUT2Context),
568     .priv_class    = &lut2_class,
569     .uninit        = uninit,
570     .query_formats = query_formats,
571     .activate      = activate,
572     .inputs        = inputs,
573     .outputs       = outputs,
574     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
575                      AVFILTER_FLAG_SLICE_THREADS,
576     .process_command = process_command,
577 };
578
579 #if CONFIG_TLUT2_FILTER
580
581 static av_cold int init(AVFilterContext *ctx)
582 {
583     LUT2Context *s = ctx->priv;
584
585     s->tlut2 = !strcmp(ctx->filter->name, "tlut2");
586
587     return 0;
588 }
589
590 static int tlut2_filter_frame(AVFilterLink *inlink, AVFrame *frame)
591 {
592     AVFilterContext *ctx = inlink->dst;
593     LUT2Context *s = ctx->priv;
594     AVFilterLink *outlink = ctx->outputs[0];
595
596     if (s->prev_frame) {
597         AVFrame *out;
598
599         if (ctx->is_disabled) {
600             out = av_frame_clone(frame);
601         } else {
602             ThreadData td;
603
604             out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
605             if (!out) {
606                 av_frame_free(&s->prev_frame);
607                 s->prev_frame = frame;
608                 return AVERROR(ENOMEM);
609             }
610
611             av_frame_copy_props(out, frame);
612
613             td.out  = out;
614             td.srcx = frame;
615             td.srcy = s->prev_frame;
616             ctx->internal->execute(ctx, s->lut2, &td, NULL, FFMIN(s->heightx[1], ff_filter_get_nb_threads(ctx)));
617         }
618         av_frame_free(&s->prev_frame);
619         s->prev_frame = frame;
620         return ff_filter_frame(outlink, out);
621     }
622     s->prev_frame = frame;
623     return 0;
624 }
625
626 static const AVOption tlut2_options[] = {
627     { "c0", "set component #0 expression", OFFSET(comp_expr_str[0]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
628     { "c1", "set component #1 expression", OFFSET(comp_expr_str[1]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
629     { "c2", "set component #2 expression", OFFSET(comp_expr_str[2]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
630     { "c3", "set component #3 expression", OFFSET(comp_expr_str[3]),  AV_OPT_TYPE_STRING, { .str = "x" }, .flags = TFLAGS },
631     { NULL }
632 };
633
634 AVFILTER_DEFINE_CLASS(tlut2);
635
636 static const AVFilterPad tlut2_inputs[] = {
637     {
638         .name          = "default",
639         .type          = AVMEDIA_TYPE_VIDEO,
640         .filter_frame  = tlut2_filter_frame,
641         .config_props  = config_inputx,
642     },
643     { NULL }
644 };
645
646 static const AVFilterPad tlut2_outputs[] = {
647     {
648         .name          = "default",
649         .type          = AVMEDIA_TYPE_VIDEO,
650         .config_props  = config_output,
651     },
652     { NULL }
653 };
654
655 const AVFilter ff_vf_tlut2 = {
656     .name          = "tlut2",
657     .description   = NULL_IF_CONFIG_SMALL("Compute and apply a lookup table from two successive frames."),
658     .priv_size     = sizeof(LUT2Context),
659     .priv_class    = &tlut2_class,
660     .query_formats = query_formats,
661     .init          = init,
662     .uninit        = uninit,
663     .inputs        = tlut2_inputs,
664     .outputs       = tlut2_outputs,
665     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
666                      AVFILTER_FLAG_SLICE_THREADS,
667     .process_command = process_command,
668 };
669
670 #endif