FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_colorchannelmixer.c
Date: 2022-12-09 07:38:14
Exec Total Coverage
Lines: 56 206 27.2%
Functions: 4 40 10.0%
Branches: 12 40 30.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2013 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 <float.h>
22
23 #include "libavutil/opt.h"
24 #include "libavutil/pixdesc.h"
25 #include "avfilter.h"
26 #include "drawutils.h"
27 #include "formats.h"
28 #include "internal.h"
29 #include "video.h"
30 #include "preserve_color.h"
31
32 #define R 0
33 #define G 1
34 #define B 2
35 #define A 3
36
37 typedef struct ThreadData {
38 AVFrame *in, *out;
39 } ThreadData;
40
41 typedef struct ColorChannelMixerContext {
42 const AVClass *class;
43 double rr, rg, rb, ra;
44 double gr, gg, gb, ga;
45 double br, bg, bb, ba;
46 double ar, ag, ab, aa;
47 double preserve_amount;
48 int preserve_color;
49
50 int *lut[4][4];
51
52 int *buffer;
53
54 uint8_t rgba_map[4];
55
56 int (*filter_slice[2])(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
57 } ColorChannelMixerContext;
58
59 static float lerpf(float v0, float v1, float f)
60 {
61 return v0 + (v1 - v0) * f;
62 }
63
64 static void preservel(float *r, float *g, float *b, float lin, float lout, float max)
65 {
66 if (lout <= 0.f)
67 lout = 1.f / (max * 2.f);
68 *r *= lin / lout;
69 *g *= lin / lout;
70 *b *= lin / lout;
71 }
72
73 #define DEPTH 8
74 #include "colorchannelmixer_template.c"
75
76 #undef DEPTH
77 #define DEPTH 16
78 #include "colorchannelmixer_template.c"
79
80 #undef DEPTH
81 #define DEPTH 32
82 #include "colorchannelmixer_template.c"
83
84 #define OFFSET(x) offsetof(ColorChannelMixerContext, x)
85 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
86
87 static const AVOption colorchannelmixer_options[] = {
88 { "rr", "set the red gain for the red channel", OFFSET(rr), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
89 { "rg", "set the green gain for the red channel", OFFSET(rg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
90 { "rb", "set the blue gain for the red channel", OFFSET(rb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
91 { "ra", "set the alpha gain for the red channel", OFFSET(ra), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
92 { "gr", "set the red gain for the green channel", OFFSET(gr), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
93 { "gg", "set the green gain for the green channel", OFFSET(gg), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
94 { "gb", "set the blue gain for the green channel", OFFSET(gb), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
95 { "ga", "set the alpha gain for the green channel", OFFSET(ga), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
96 { "br", "set the red gain for the blue channel", OFFSET(br), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
97 { "bg", "set the green gain for the blue channel", OFFSET(bg), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
98 { "bb", "set the blue gain for the blue channel", OFFSET(bb), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
99 { "ba", "set the alpha gain for the blue channel", OFFSET(ba), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
100 { "ar", "set the red gain for the alpha channel", OFFSET(ar), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
101 { "ag", "set the green gain for the alpha channel", OFFSET(ag), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
102 { "ab", "set the blue gain for the alpha channel", OFFSET(ab), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS },
103 { "aa", "set the alpha gain for the alpha channel", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS },
104 { "pc", "set the preserve color mode", OFFSET(preserve_color), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_PRESERVE-1, FLAGS, "preserve" },
105 { "none", "disabled", 0, AV_OPT_TYPE_CONST, {.i64=P_NONE}, 0, 0, FLAGS, "preserve" },
106 { "lum", "luminance", 0, AV_OPT_TYPE_CONST, {.i64=P_LUM}, 0, 0, FLAGS, "preserve" },
107 { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64=P_MAX}, 0, 0, FLAGS, "preserve" },
108 { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=P_AVG}, 0, 0, FLAGS, "preserve" },
109 { "sum", "sum", 0, AV_OPT_TYPE_CONST, {.i64=P_SUM}, 0, 0, FLAGS, "preserve" },
110 { "nrm", "norm", 0, AV_OPT_TYPE_CONST, {.i64=P_NRM}, 0, 0, FLAGS, "preserve" },
111 { "pwr", "power", 0, AV_OPT_TYPE_CONST, {.i64=P_PWR}, 0, 0, FLAGS, "preserve" },
112 { "pa", "set the preserve color amount", OFFSET(preserve_amount), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS },
113 { NULL }
114 };
115
116 AVFILTER_DEFINE_CLASS(colorchannelmixer);
117
118 static const enum AVPixelFormat pix_fmts[] = {
119 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
120 AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
121 AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR,
122 AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR,
123 AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
124 AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48,
125 AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
126 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
127 AV_PIX_FMT_GBRP9,
128 AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
129 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
130 AV_PIX_FMT_GBRP14,
131 AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
132 AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32,
133 AV_PIX_FMT_NONE
134 };
135
136 static int filter_slice_gbrp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
137 {
138 return filter_slice_rgba_planar_8(ctx, arg, jobnr, nb_jobs, 0, 8, 0);
139 }
140
141 static int filter_slice_gbrap(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
142 {
143 return filter_slice_rgba_planar_8(ctx, arg, jobnr, nb_jobs, 1, 8, 0);
144 }
145
146 static int filter_slice_gbrp_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
147 {
148 return filter_slice_rgba_planar_8(ctx, arg, jobnr, nb_jobs, 0, 8, 1);
149 }
150
151 static int filter_slice_gbrap_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
152 {
153 return filter_slice_rgba_planar_8(ctx, arg, jobnr, nb_jobs, 1, 8, 1);
154 }
155
156 static int filter_slice_gbrp9(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
157 {
158 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 9, 0);
159 }
160
161 static int filter_slice_gbrp10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
162 {
163 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 10, 0);
164 }
165
166 static int filter_slice_gbrap10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
167 {
168 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 1, 10, 0);
169 }
170
171 static int filter_slice_gbrp12(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
172 {
173 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 12, 0);
174 }
175
176 static int filter_slice_gbrap12(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
177 {
178 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 1, 12, 0);
179 }
180
181 static int filter_slice_gbrp14(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
182 {
183 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 14, 0);
184 }
185
186 static int filter_slice_gbrp16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
187 {
188 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 16, 0);
189 }
190
191 static int filter_slice_gbrap16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
192 {
193 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 1, 16, 0);
194 }
195
196 static int filter_slice_gbrp9_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
197 {
198 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 9, 1);
199 }
200
201 static int filter_slice_gbrp10_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
202 {
203 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 10, 1);
204 }
205
206 static int filter_slice_gbrap10_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
207 {
208 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 1, 10, 1);
209 }
210
211 static int filter_slice_gbrp12_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
212 {
213 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 12, 1);
214 }
215
216 static int filter_slice_gbrap12_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
217 {
218 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 1, 12, 1);
219 }
220
221 static int filter_slice_gbrp14_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
222 {
223 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 14, 1);
224 }
225
226 static int filter_slice_gbrp16_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
227 {
228 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 0, 16, 1);
229 }
230
231 static int filter_slice_gbrap16_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
232 {
233 return filter_slice_rgba_planar_16(ctx, arg, jobnr, nb_jobs, 1, 16, 1);
234 }
235
236 static int filter_slice_rgba64(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
237 {
238 return filter_slice_rgba_packed_16(ctx, arg, jobnr, nb_jobs, 1, 4, 0, 16);
239 }
240
241 static int filter_slice_rgb48(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
242 {
243 return filter_slice_rgba_packed_16(ctx, arg, jobnr, nb_jobs, 0, 3, 0, 16);
244 }
245
246 static int filter_slice_rgba64_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
247 {
248 return filter_slice_rgba_packed_16(ctx, arg, jobnr, nb_jobs, 1, 4, 1, 16);
249 }
250
251 static int filter_slice_rgb48_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
252 {
253 return filter_slice_rgba_packed_16(ctx, arg, jobnr, nb_jobs, 0, 3, 1, 16);
254 }
255
256 static int filter_slice_rgba(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
257 {
258 return filter_slice_rgba_packed_8(ctx, arg, jobnr, nb_jobs, 1, 4, 0, 8);
259 }
260
261 450 static int filter_slice_rgb24(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
262 {
263 450 return filter_slice_rgba_packed_8(ctx, arg, jobnr, nb_jobs, 0, 3, 0, 8);
264 }
265
266 static int filter_slice_rgb0(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
267 {
268 return filter_slice_rgba_packed_8(ctx, arg, jobnr, nb_jobs, -1, 4, 0, 8);
269 }
270
271 static int filter_slice_rgba_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
272 {
273 return filter_slice_rgba_packed_8(ctx, arg, jobnr, nb_jobs, 1, 4, 1, 8);
274 }
275
276 static int filter_slice_rgb24_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
277 {
278 return filter_slice_rgba_packed_8(ctx, arg, jobnr, nb_jobs, 0, 3, 1, 8);
279 }
280
281 static int filter_slice_rgb0_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
282 {
283 return filter_slice_rgba_packed_8(ctx, arg, jobnr, nb_jobs, -1, 4, 1, 8);
284 }
285
286 static int filter_slice_gbrp32(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
287 {
288 return filter_slice_rgba_planar_32(ctx, arg, jobnr, nb_jobs, 0, 1, 0);
289 }
290
291 static int filter_slice_gbrap32(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
292 {
293 return filter_slice_rgba_planar_32(ctx, arg, jobnr, nb_jobs, 1, 1, 0);
294 }
295
296 static int filter_slice_gbrp32_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
297 {
298 return filter_slice_rgba_planar_32(ctx, arg, jobnr, nb_jobs, 0, 1, 1);
299 }
300
301 static int filter_slice_gbrap32_pl(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
302 {
303 return filter_slice_rgba_planar_32(ctx, arg, jobnr, nb_jobs, 1, 1, 1);
304 }
305
306 1 static int config_output(AVFilterLink *outlink)
307 {
308 1 AVFilterContext *ctx = outlink->src;
309 1 ColorChannelMixerContext *s = ctx->priv;
310 1 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
311 1 const int depth = desc->comp[0].depth;
312 1 int i, j, size, *buffer = s->buffer;
313
314 1 ff_fill_rgba_map(s->rgba_map, outlink->format);
315
316 1 size = 1 << depth;
317
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!s->buffer) {
318 1 s->buffer = buffer = av_malloc(16 * size * sizeof(*s->buffer));
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!s->buffer)
320 return AVERROR(ENOMEM);
321
322
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (i = 0; i < 4; i++)
323
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 for (j = 0; j < 4; j++, buffer += size)
324 16 s->lut[i][j] = buffer;
325 }
326
327
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 1 times.
257 for (i = 0; i < size; i++) {
328 256 s->lut[R][R][i] = lrint(i * s->rr);
329 256 s->lut[R][G][i] = lrint(i * s->rg);
330 256 s->lut[R][B][i] = lrint(i * s->rb);
331 256 s->lut[R][A][i] = lrint(i * s->ra);
332
333 256 s->lut[G][R][i] = lrint(i * s->gr);
334 256 s->lut[G][G][i] = lrint(i * s->gg);
335 256 s->lut[G][B][i] = lrint(i * s->gb);
336 256 s->lut[G][A][i] = lrint(i * s->ga);
337
338 256 s->lut[B][R][i] = lrint(i * s->br);
339 256 s->lut[B][G][i] = lrint(i * s->bg);
340 256 s->lut[B][B][i] = lrint(i * s->bb);
341 256 s->lut[B][A][i] = lrint(i * s->ba);
342
343 256 s->lut[A][R][i] = lrint(i * s->ar);
344 256 s->lut[A][G][i] = lrint(i * s->ag);
345 256 s->lut[A][B][i] = lrint(i * s->ab);
346 256 s->lut[A][A][i] = lrint(i * s->aa);
347 }
348
349
1/18
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1 switch (outlink->format) {
350 1 case AV_PIX_FMT_BGR24:
351 case AV_PIX_FMT_RGB24:
352 1 s->filter_slice[0] = filter_slice_rgb24;
353 1 s->filter_slice[1] = filter_slice_rgb24_pl;
354 1 break;
355 case AV_PIX_FMT_0BGR:
356 case AV_PIX_FMT_0RGB:
357 case AV_PIX_FMT_BGR0:
358 case AV_PIX_FMT_RGB0:
359 s->filter_slice[0] = filter_slice_rgb0;
360 s->filter_slice[1] = filter_slice_rgb0_pl;
361 break;
362 case AV_PIX_FMT_ABGR:
363 case AV_PIX_FMT_ARGB:
364 case AV_PIX_FMT_BGRA:
365 case AV_PIX_FMT_RGBA:
366 s->filter_slice[0] = filter_slice_rgba;
367 s->filter_slice[1] = filter_slice_rgba_pl;
368 break;
369 case AV_PIX_FMT_BGR48:
370 case AV_PIX_FMT_RGB48:
371 s->filter_slice[0] = filter_slice_rgb48;
372 s->filter_slice[1] = filter_slice_rgb48_pl;
373 break;
374 case AV_PIX_FMT_BGRA64:
375 case AV_PIX_FMT_RGBA64:
376 s->filter_slice[0] = filter_slice_rgba64;
377 s->filter_slice[1] = filter_slice_rgba64_pl;
378 break;
379 case AV_PIX_FMT_GBRP:
380 s->filter_slice[0] = filter_slice_gbrp;
381 s->filter_slice[1] = filter_slice_gbrp_pl;
382 break;
383 case AV_PIX_FMT_GBRAP:
384 s->filter_slice[0] = filter_slice_gbrap;
385 s->filter_slice[1] = filter_slice_gbrap_pl;
386 break;
387 case AV_PIX_FMT_GBRP9:
388 s->filter_slice[0] = filter_slice_gbrp9;
389 s->filter_slice[1] = filter_slice_gbrp9_pl;
390 break;
391 case AV_PIX_FMT_GBRP10:
392 s->filter_slice[0] = filter_slice_gbrp10;
393 s->filter_slice[1] = filter_slice_gbrp10_pl;
394 break;
395 case AV_PIX_FMT_GBRAP10:
396 s->filter_slice[0] = filter_slice_gbrap10;
397 s->filter_slice[1] = filter_slice_gbrap10_pl;
398 break;
399 case AV_PIX_FMT_GBRP12:
400 s->filter_slice[0] = filter_slice_gbrp12;
401 s->filter_slice[1] = filter_slice_gbrp12_pl;
402 break;
403 case AV_PIX_FMT_GBRAP12:
404 s->filter_slice[0] = filter_slice_gbrap12;
405 s->filter_slice[1] = filter_slice_gbrap12_pl;
406 break;
407 case AV_PIX_FMT_GBRP14:
408 s->filter_slice[0] = filter_slice_gbrp14;
409 s->filter_slice[1] = filter_slice_gbrp14_pl;
410 break;
411 case AV_PIX_FMT_GBRP16:
412 s->filter_slice[0] = filter_slice_gbrp16;
413 s->filter_slice[1] = filter_slice_gbrp16_pl;
414 break;
415 case AV_PIX_FMT_GBRAP16:
416 s->filter_slice[0] = filter_slice_gbrap16;
417 s->filter_slice[1] = filter_slice_gbrap16_pl;
418 break;
419 case AV_PIX_FMT_GBRPF32:
420 s->filter_slice[0] = filter_slice_gbrp32;
421 s->filter_slice[1] = filter_slice_gbrp32_pl;
422 break;
423 case AV_PIX_FMT_GBRAPF32:
424 s->filter_slice[0] = filter_slice_gbrap32;
425 s->filter_slice[1] = filter_slice_gbrap32_pl;
426 break;
427 }
428
429 1 return 0;
430 }
431
432 50 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
433 {
434 50 AVFilterContext *ctx = inlink->dst;
435 50 ColorChannelMixerContext *s = ctx->priv;
436 50 AVFilterLink *outlink = ctx->outputs[0];
437 50 const int pc = s->preserve_color > 0;
438 ThreadData td;
439 AVFrame *out;
440
441
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 if (av_frame_is_writable(in)) {
442 50 out = in;
443 } else {
444 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
445 if (!out) {
446 av_frame_free(&in);
447 return AVERROR(ENOMEM);
448 }
449 av_frame_copy_props(out, in);
450 }
451
452 50 td.in = in;
453 50 td.out = out;
454 50 ff_filter_execute(ctx, s->filter_slice[pc], &td, NULL,
455
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
456
457
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (in != out)
458 av_frame_free(&in);
459 50 return ff_filter_frame(outlink, out);
460 }
461
462 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
463 char *res, int res_len, int flags)
464 {
465 int ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
466
467 if (ret < 0)
468 return ret;
469
470 return config_output(ctx->outputs[0]);
471 }
472
473 1 static av_cold void uninit(AVFilterContext *ctx)
474 {
475 1 ColorChannelMixerContext *s = ctx->priv;
476
477 1 av_freep(&s->buffer);
478 1 }
479
480 static const AVFilterPad colorchannelmixer_inputs[] = {
481 {
482 .name = "default",
483 .type = AVMEDIA_TYPE_VIDEO,
484 .filter_frame = filter_frame,
485 },
486 };
487
488 static const AVFilterPad colorchannelmixer_outputs[] = {
489 {
490 .name = "default",
491 .type = AVMEDIA_TYPE_VIDEO,
492 .config_props = config_output,
493 },
494 };
495
496 const AVFilter ff_vf_colorchannelmixer = {
497 .name = "colorchannelmixer",
498 .description = NULL_IF_CONFIG_SMALL("Adjust colors by mixing color channels."),
499 .priv_size = sizeof(ColorChannelMixerContext),
500 .priv_class = &colorchannelmixer_class,
501 .uninit = uninit,
502 FILTER_INPUTS(colorchannelmixer_inputs),
503 FILTER_OUTPUTS(colorchannelmixer_outputs),
504 FILTER_PIXFMTS_ARRAY(pix_fmts),
505 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
506 .process_command = process_command,
507 };
508