2 * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (C) 2012 Clément Bœsch <u pkh me>
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * Generic equation change filter
25 * Originally written by Michael Niedermayer for the MPlayer project, and
26 * ported by Clément Bœsch for FFmpeg.
29 #include "libavutil/avassert.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/eval.h"
32 #include "libavutil/mem.h"
33 #include "libavutil/opt.h"
34 #include "libavutil/pixdesc.h"
40 #define MAX_NB_THREADS 32
43 enum InterpolationMethods
{
49 static const char *const var_names
[] = { "X", "Y", "W", "H", "N", "SW", "SH", "T", NULL
};
50 enum { VAR_X
, VAR_Y
, VAR_W
, VAR_H
, VAR_N
, VAR_SW
, VAR_SH
, VAR_T
, VAR_VARS_NB
};
52 typedef struct GEQContext
{
54 AVExpr
*e
[NB_PLANES
][MAX_NB_THREADS
]; ///< expressions for each plane and thread
55 char *expr_str
[4+3]; ///< expression strings for each plane
56 AVFrame
*picref
; ///< current input buffer
57 uint8_t *dst
; ///< reference pointer to the 8bits output
58 uint16_t *dst16
; ///< reference pointer to the 16bits output
59 float *dst32
; ///< reference pointer to the 32bits output
60 double values
[VAR_VARS_NB
]; ///< expression values
61 int hsub
, vsub
; ///< chroma subsampling
62 int planes
; ///< number of planes
67 double *pixel_sums
[NB_PLANES
];
68 int needs_sum
[NB_PLANES
];
71 enum { Y
= 0, U
, V
, A
, G
, B
, R
};
73 #define OFFSET(x) offsetof(GEQContext, x)
74 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
76 static const AVOption geq_options
[] = {
77 { "lum_expr", "set luminance expression", OFFSET(expr_str
[Y
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
78 { "lum", "set luminance expression", OFFSET(expr_str
[Y
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
79 { "cb_expr", "set chroma blue expression", OFFSET(expr_str
[U
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
80 { "cb", "set chroma blue expression", OFFSET(expr_str
[U
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
81 { "cr_expr", "set chroma red expression", OFFSET(expr_str
[V
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
82 { "cr", "set chroma red expression", OFFSET(expr_str
[V
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
83 { "alpha_expr", "set alpha expression", OFFSET(expr_str
[A
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
84 { "a", "set alpha expression", OFFSET(expr_str
[A
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
85 { "red_expr", "set red expression", OFFSET(expr_str
[R
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
86 { "r", "set red expression", OFFSET(expr_str
[R
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
87 { "green_expr", "set green expression", OFFSET(expr_str
[G
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
88 { "g", "set green expression", OFFSET(expr_str
[G
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
89 { "blue_expr", "set blue expression", OFFSET(expr_str
[B
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
90 { "b", "set blue expression", OFFSET(expr_str
[B
]), AV_OPT_TYPE_STRING
, {.str
=NULL
}, 0, 0, FLAGS
},
91 { "interpolation","set interpolation method", OFFSET(interpolation
), AV_OPT_TYPE_INT
, {.i64
=INTERP_BILINEAR
}, 0, NB_INTERP
-1, FLAGS
, .unit
= "interp" },
92 { "i", "set interpolation method", OFFSET(interpolation
), AV_OPT_TYPE_INT
, {.i64
=INTERP_BILINEAR
}, 0, NB_INTERP
-1, FLAGS
, .unit
= "interp" },
93 { "nearest", "nearest interpolation", 0, AV_OPT_TYPE_CONST
, {.i64
=INTERP_NEAREST
}, 0, 0, FLAGS
, .unit
= "interp" },
94 { "n", "nearest interpolation", 0, AV_OPT_TYPE_CONST
, {.i64
=INTERP_NEAREST
}, 0, 0, FLAGS
, .unit
= "interp" },
95 { "bilinear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST
, {.i64
=INTERP_BILINEAR
}, 0, 0, FLAGS
, .unit
= "interp" },
96 { "b", "bilinear interpolation", 0, AV_OPT_TYPE_CONST
, {.i64
=INTERP_BILINEAR
}, 0, 0, FLAGS
, .unit
= "interp" },
100 AVFILTER_DEFINE_CLASS(geq
);
102 static inline double getpix(void *priv
, double x
, double y
, int plane
)
105 GEQContext
*geq
= priv
;
106 AVFrame
*picref
= geq
->picref
;
107 const uint8_t *src
= picref
->data
[plane
];
108 int linesize
= picref
->linesize
[plane
];
109 const int w
= (plane
== 1 || plane
== 2) ? AV_CEIL_RSHIFT(picref
->width
, geq
->hsub
) : picref
->width
;
110 const int h
= (plane
== 1 || plane
== 2) ? AV_CEIL_RSHIFT(picref
->height
, geq
->vsub
) : picref
->height
;
115 if (geq
->interpolation
== INTERP_BILINEAR
) {
118 xi
= x
= av_clipd(x
, 0, w
- 1);
119 yi
= y
= av_clipd(y
, 0, h
- 1);
120 xn
= FFMIN(xi
+ 1, w
- 1);
121 yn
= FFMIN(yi
+ 1, h
- 1);
126 if (geq
->bps
> 8 && geq
->bps
<= 16) {
127 const uint16_t *src16
= (const uint16_t*)src
;
130 return (1-y
)*((1-x
)*src16
[xi
+ yi
* linesize
] + x
*src16
[xn
+ yi
* linesize
])
131 + y
*((1-x
)*src16
[xi
+ yn
* linesize
] + x
*src16
[xn
+ yn
* linesize
]);
132 } else if (geq
->bps
== 32) {
133 const float *src32
= (const float*)src
;
136 return (1-y
)*((1-x
)*src32
[xi
+ yi
* linesize
] + x
*src32
[xn
+ yi
* linesize
])
137 + y
*((1-x
)*src32
[xi
+ yn
* linesize
] + x
*src32
[xn
+ yn
* linesize
]);
138 } else if (geq
->bps
== 8) {
139 return (1-y
)*((1-x
)*src
[xi
+ yi
* linesize
] + x
*src
[xn
+ yi
* linesize
])
140 + y
*((1-x
)*src
[xi
+ yn
* linesize
] + x
*src
[xn
+ yn
* linesize
]);
143 xi
= av_clipd(x
, 0, w
- 1);
144 yi
= av_clipd(y
, 0, h
- 1);
146 if (geq
->bps
> 8 && geq
->bps
<= 16) {
147 const uint16_t *src16
= (const uint16_t*)src
;
150 return src16
[xi
+ yi
* linesize
];
151 } else if (geq
->bps
== 32) {
152 const float *src32
= (const float*)src
;
155 return src32
[xi
+ yi
* linesize
];
156 } else if (geq
->bps
== 8) {
157 return src
[xi
+ yi
* linesize
];
164 static int calculate_sums(GEQContext
*geq
, int plane
, int w
, int h
)
167 AVFrame
*picref
= geq
->picref
;
168 const uint8_t *src
= picref
->data
[plane
];
169 int linesize
= picref
->linesize
[plane
];
171 if (!geq
->pixel_sums
[plane
])
172 geq
->pixel_sums
[plane
] = av_malloc_array(w
, h
* sizeof (*geq
->pixel_sums
[plane
]));
173 if (!geq
->pixel_sums
[plane
])
174 return AVERROR(ENOMEM
);
177 else if (geq
->bps
> 8 && geq
->bps
<= 16)
179 for (yi
= 0; yi
< h
; yi
++) {
180 if (geq
->bps
> 8 && geq
->bps
<= 16) {
181 const uint16_t *src16
= (const uint16_t*)src
;
184 for (xi
= 0; xi
< w
; xi
++) {
185 linesum
+= src16
[xi
+ yi
* linesize
];
186 geq
->pixel_sums
[plane
][xi
+ yi
* w
] = linesum
;
188 } else if (geq
->bps
== 8) {
191 for (xi
= 0; xi
< w
; xi
++) {
192 linesum
+= src
[xi
+ yi
* linesize
];
193 geq
->pixel_sums
[plane
][xi
+ yi
* w
] = linesum
;
195 } else if (geq
->bps
== 32) {
196 const float *src32
= (const float*)src
;
199 for (xi
= 0; xi
< w
; xi
++) {
200 linesum
+= src32
[xi
+ yi
* linesize
];
201 geq
->pixel_sums
[plane
][xi
+ yi
* w
] = linesum
;
205 for (xi
= 0; xi
< w
; xi
++) {
206 geq
->pixel_sums
[plane
][xi
+ yi
* w
] += geq
->pixel_sums
[plane
][xi
+ yi
* w
- w
];
212 static inline double getpix_integrate_internal(GEQContext
*geq
, int x
, int y
, int plane
, int w
, int h
)
215 double boundary
= getpix_integrate_internal(geq
, w
- 1, y
, plane
, w
, h
);
216 return 2*boundary
- getpix_integrate_internal(geq
, 2*(w
- 1) - x
, y
, plane
, w
, h
);
217 } else if (y
> h
- 1) {
218 double boundary
= getpix_integrate_internal(geq
, x
, h
- 1, plane
, w
, h
);
219 return 2*boundary
- getpix_integrate_internal(geq
, x
, 2*(h
- 1) - y
, plane
, w
, h
);
221 if (x
== -1) return 0;
222 return - getpix_integrate_internal(geq
, -x
-2, y
, plane
, w
, h
);
224 if (y
== -1) return 0;
225 return - getpix_integrate_internal(geq
, x
, -y
-2, plane
, w
, h
);
228 return geq
->pixel_sums
[plane
][x
+ y
* w
];
231 static inline double getpix_integrate(void *priv
, double x
, double y
, int plane
) {
232 GEQContext
*geq
= priv
;
233 AVFrame
*picref
= geq
->picref
;
234 const uint8_t *src
= picref
->data
[plane
];
235 const int w
= (plane
== 1 || plane
== 2) ? AV_CEIL_RSHIFT(picref
->width
, geq
->hsub
) : picref
->width
;
236 const int h
= (plane
== 1 || plane
== 2) ? AV_CEIL_RSHIFT(picref
->height
, geq
->vsub
) : picref
->height
;
241 return getpix_integrate_internal(geq
, lrint(av_clipd(x
, -w
, 2*w
)), lrint(av_clipd(y
, -h
, 2*h
)), plane
, w
, h
);
244 //TODO: cubic interpolate
245 //TODO: keep the last few frames
246 static double lum(void *priv
, double x
, double y
) { return getpix(priv
, x
, y
, 0); }
247 static double cb(void *priv
, double x
, double y
) { return getpix(priv
, x
, y
, 1); }
248 static double cr(void *priv
, double x
, double y
) { return getpix(priv
, x
, y
, 2); }
249 static double alpha(void *priv
, double x
, double y
) { return getpix(priv
, x
, y
, 3); }
251 static double lumsum(void *priv
, double x
, double y
) { return getpix_integrate(priv
, x
, y
, 0); }
252 static double cbsum(void *priv
, double x
, double y
) { return getpix_integrate(priv
, x
, y
, 1); }
253 static double crsub(void *priv
, double x
, double y
) { return getpix_integrate(priv
, x
, y
, 2); }
254 static double alphasum(void *priv
, double x
, double y
) { return getpix_integrate(priv
, x
, y
, 3); }
256 static av_cold
int geq_init(AVFilterContext
*ctx
)
258 GEQContext
*geq
= ctx
->priv
;
261 if (!geq
->expr_str
[Y
] && !geq
->expr_str
[G
] && !geq
->expr_str
[B
] && !geq
->expr_str
[R
]) {
262 av_log(ctx
, AV_LOG_ERROR
, "A luminance or RGB expression is mandatory\n");
263 ret
= AVERROR(EINVAL
);
266 geq
->is_rgb
= !geq
->expr_str
[Y
];
268 if ((geq
->expr_str
[Y
] || geq
->expr_str
[U
] || geq
->expr_str
[V
]) && (geq
->expr_str
[G
] || geq
->expr_str
[B
] || geq
->expr_str
[R
])) {
269 av_log(ctx
, AV_LOG_ERROR
, "Either YCbCr or RGB but not both must be specified\n");
270 ret
= AVERROR(EINVAL
);
274 if (!geq
->expr_str
[U
] && !geq
->expr_str
[V
]) {
275 /* No chroma at all: fallback on luma */
276 geq
->expr_str
[U
] = av_strdup(geq
->expr_str
[Y
]);
277 geq
->expr_str
[V
] = av_strdup(geq
->expr_str
[Y
]);
279 /* One chroma unspecified, fallback on the other */
280 if (!geq
->expr_str
[U
]) geq
->expr_str
[U
] = av_strdup(geq
->expr_str
[V
]);
281 if (!geq
->expr_str
[V
]) geq
->expr_str
[V
] = av_strdup(geq
->expr_str
[U
]);
284 if (!geq
->expr_str
[A
] && geq
->bps
!= 32) {
285 geq
->expr_str
[A
] = av_asprintf("%d", (1<<geq
->bps
) - 1);
286 } else if (!geq
->expr_str
[A
]) {
287 geq
->expr_str
[A
] = av_asprintf("%f", 1.f
);
289 if (!geq
->expr_str
[G
])
290 geq
->expr_str
[G
] = av_strdup("g(X,Y)");
291 if (!geq
->expr_str
[B
])
292 geq
->expr_str
[B
] = av_strdup("b(X,Y)");
293 if (!geq
->expr_str
[R
])
294 geq
->expr_str
[R
] = av_strdup("r(X,Y)");
297 (!geq
->expr_str
[G
] || !geq
->expr_str
[B
] || !geq
->expr_str
[R
])
299 (!geq
->expr_str
[U
] || !geq
->expr_str
[V
] || !geq
->expr_str
[A
])) {
300 ret
= AVERROR(ENOMEM
);
304 for (plane
= 0; plane
< NB_PLANES
; plane
++) {
305 static double (*const p
[])(void *, double, double) = {
306 lum
, cb
, cr
, alpha
,
307 lumsum
, cbsum
, crsub
, alphasum
,
309 static const char *const func2_yuv_names
[] = {
310 "lum" , "cb" , "cr" , "alpha" , "p",
311 "lumsum", "cbsum", "crsum", "alphasum", "psum",
313 static const char *const func2_rgb_names
[] = {
314 "g" , "b" , "r" , "alpha" , "p",
315 "gsum", "bsum", "rsum", "alphasum", "psum",
317 const char *const *func2_names
= geq
->is_rgb
? func2_rgb_names
: func2_yuv_names
;
318 double (*const func2
[])(void *, double, double) = {
319 lum
, cb
, cr
, alpha
, p
[plane
],
320 lumsum
, cbsum
, crsub
, alphasum
, p
[plane
+ 4],
322 int counter
[10] = {0};
324 for (int i
= 0; i
< MAX_NB_THREADS
; i
++) {
325 ret
= av_expr_parse(&geq
->e
[plane
][i
], geq
->expr_str
[plane
< 3 && geq
->is_rgb
? plane
+4 : plane
], var_names
,
326 NULL
, NULL
, func2_names
, func2
, 0, ctx
);
331 av_expr_count_func(geq
->e
[plane
][0], counter
, FF_ARRAY_ELEMS(counter
), 2);
332 geq
->needs_sum
[plane
] = counter
[5] + counter
[6] + counter
[7] + counter
[8] + counter
[9];
339 static int geq_query_formats(const AVFilterContext
*ctx
,
340 AVFilterFormatsConfig
**cfg_in
,
341 AVFilterFormatsConfig
**cfg_out
)
343 const GEQContext
*geq
= ctx
->priv
;
344 static const enum AVPixelFormat yuv_pix_fmts
[] = {
345 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
346 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
347 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUVA420P
,
349 AV_PIX_FMT_YUV444P9
, AV_PIX_FMT_YUV422P9
, AV_PIX_FMT_YUV420P9
,
350 AV_PIX_FMT_YUVA444P9
, AV_PIX_FMT_YUVA422P9
, AV_PIX_FMT_YUVA420P9
,
351 AV_PIX_FMT_YUV444P10
, AV_PIX_FMT_YUV422P10
, AV_PIX_FMT_YUV420P10
,
352 AV_PIX_FMT_YUV440P10
,
353 AV_PIX_FMT_YUVA444P10
, AV_PIX_FMT_YUVA422P10
, AV_PIX_FMT_YUVA420P10
,
354 AV_PIX_FMT_GRAY9
, AV_PIX_FMT_GRAY10
,
355 AV_PIX_FMT_YUV444P12
, AV_PIX_FMT_YUV422P12
, AV_PIX_FMT_YUV420P12
,
356 AV_PIX_FMT_GRAY12
, AV_PIX_FMT_GRAY14
,
357 AV_PIX_FMT_YUV444P14
, AV_PIX_FMT_YUV422P14
, AV_PIX_FMT_YUV420P14
,
358 AV_PIX_FMT_YUV444P16
, AV_PIX_FMT_YUV422P16
, AV_PIX_FMT_YUV420P16
,
359 AV_PIX_FMT_YUVA444P16
, AV_PIX_FMT_YUVA422P16
, AV_PIX_FMT_YUVA420P16
,
364 static const enum AVPixelFormat rgb_pix_fmts
[] = {
365 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
367 AV_PIX_FMT_GBRP10
, AV_PIX_FMT_GBRAP10
,
368 AV_PIX_FMT_GBRP12
, AV_PIX_FMT_GBRAP12
,
370 AV_PIX_FMT_GBRP16
, AV_PIX_FMT_GBRAP16
,
371 AV_PIX_FMT_GBRPF32
, AV_PIX_FMT_GBRAPF32
,
374 const enum AVPixelFormat
*pix_fmts
= geq
->is_rgb
? rgb_pix_fmts
: yuv_pix_fmts
;
376 return ff_set_common_formats_from_list2(ctx
, cfg_in
, cfg_out
, pix_fmts
);
379 static int geq_config_props(AVFilterLink
*inlink
)
381 GEQContext
*geq
= inlink
->dst
->priv
;
382 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
386 geq
->hsub
= desc
->log2_chroma_w
;
387 geq
->vsub
= desc
->log2_chroma_h
;
388 geq
->bps
= desc
->comp
[0].depth
;
389 geq
->planes
= desc
->nb_components
;
393 typedef struct ThreadData
{
400 static int slice_geq_filter(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
402 GEQContext
*geq
= ctx
->priv
;
403 ThreadData
*td
= arg
;
404 const int height
= td
->height
;
405 const int width
= td
->width
;
406 const int plane
= td
->plane
;
407 const int linesize
= td
->linesize
;
408 const int slice_start
= (height
* jobnr
) / nb_jobs
;
409 const int slice_end
= (height
* (jobnr
+1)) / nb_jobs
;
412 double values
[VAR_VARS_NB
];
413 values
[VAR_W
] = geq
->values
[VAR_W
];
414 values
[VAR_H
] = geq
->values
[VAR_H
];
415 values
[VAR_N
] = geq
->values
[VAR_N
];
416 values
[VAR_SW
] = geq
->values
[VAR_SW
];
417 values
[VAR_SH
] = geq
->values
[VAR_SH
];
418 values
[VAR_T
] = geq
->values
[VAR_T
];
421 uint8_t *ptr
= geq
->dst
+ linesize
* slice_start
;
422 for (y
= slice_start
; y
< slice_end
; y
++) {
425 for (x
= 0; x
< width
; x
++) {
427 ptr
[x
] = av_expr_eval(geq
->e
[plane
][jobnr
], values
, geq
);
431 } else if (geq
->bps
<= 16) {
432 uint16_t *ptr16
= geq
->dst16
+ (linesize
/2) * slice_start
;
433 for (y
= slice_start
; y
< slice_end
; y
++) {
435 for (x
= 0; x
< width
; x
++) {
437 ptr16
[x
] = av_expr_eval(geq
->e
[plane
][jobnr
], values
, geq
);
442 float *ptr32
= geq
->dst32
+ (linesize
/4) * slice_start
;
443 for (y
= slice_start
; y
< slice_end
; y
++) {
445 for (x
= 0; x
< width
; x
++) {
447 ptr32
[x
] = av_expr_eval(geq
->e
[plane
][jobnr
], values
, geq
);
456 static int geq_filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
459 FilterLink
*inl
= ff_filter_link(inlink
);
460 AVFilterContext
*ctx
= inlink
->dst
;
461 const int nb_threads
= FFMIN(MAX_NB_THREADS
, ff_filter_get_nb_threads(ctx
));
462 GEQContext
*geq
= ctx
->priv
;
463 AVFilterLink
*outlink
= inlink
->dst
->outputs
[0];
466 geq
->values
[VAR_N
] = inl
->frame_count_out
,
467 geq
->values
[VAR_T
] = in
->pts
== AV_NOPTS_VALUE
? NAN
: in
->pts
* av_q2d(inlink
->time_base
),
470 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
473 return AVERROR(ENOMEM
);
475 av_frame_copy_props(out
, in
);
477 for (plane
= 0; plane
< geq
->planes
&& out
->data
[plane
]; plane
++) {
478 const int width
= (plane
== 1 || plane
== 2) ? AV_CEIL_RSHIFT(inlink
->w
, geq
->hsub
) : inlink
->w
;
479 const int height
= (plane
== 1 || plane
== 2) ? AV_CEIL_RSHIFT(inlink
->h
, geq
->vsub
) : inlink
->h
;
480 const int linesize
= out
->linesize
[plane
];
483 geq
->dst
= out
->data
[plane
];
484 geq
->dst16
= (uint16_t*)out
->data
[plane
];
485 geq
->dst32
= (float*)out
->data
[plane
];
487 geq
->values
[VAR_W
] = width
;
488 geq
->values
[VAR_H
] = height
;
489 geq
->values
[VAR_SW
] = width
/ (double)inlink
->w
;
490 geq
->values
[VAR_SH
] = height
/ (double)inlink
->h
;
495 td
.linesize
= linesize
;
497 if (geq
->needs_sum
[plane
])
498 calculate_sums(geq
, plane
, width
, height
);
500 ff_filter_execute(ctx
, slice_geq_filter
, &td
,
501 NULL
, FFMIN(height
, nb_threads
));
504 av_frame_free(&geq
->picref
);
505 return ff_filter_frame(outlink
, out
);
508 static av_cold
void geq_uninit(AVFilterContext
*ctx
)
511 GEQContext
*geq
= ctx
->priv
;
513 for (i
= 0; i
< NB_PLANES
; i
++)
514 for (int j
= 0; j
< MAX_NB_THREADS
; j
++)
515 av_expr_free(geq
->e
[i
][j
]);
516 for (i
= 0; i
< NB_PLANES
; i
++)
517 av_freep(&geq
->pixel_sums
);
520 static const AVFilterPad geq_inputs
[] = {
523 .type
= AVMEDIA_TYPE_VIDEO
,
524 .config_props
= geq_config_props
,
525 .filter_frame
= geq_filter_frame
,
529 const FFFilter ff_vf_geq
= {
531 .p
.description
= NULL_IF_CONFIG_SMALL("Apply generic equation to each pixel."),
532 .p
.priv_class
= &geq_class
,
533 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
534 .priv_size
= sizeof(GEQContext
),
536 .uninit
= geq_uninit
,
537 FILTER_INPUTS(geq_inputs
),
538 FILTER_OUTPUTS(ff_video_default_filterpad
),
539 FILTER_QUERY_FUNC2(geq_query_formats
),