1 /*
2 * Copyright (c) 2012 Laurent de Soras
3 * Copyright (c) 2013 Fredrik Mellbin
4 * Copyright (c) 2015 Paul B Mahol
5 * Copyright (c) 2015 James Darnley
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "libavutil/imgutils.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "libavutil/qsort.h"
28 #include "avfilter.h"
29 #include "formats.h"
30 #include "internal.h"
31 #include "removegrain.h"
32 #include "video.h"
33
34 #define OFFSET(x) offsetof(RemoveGrainContext, x)
35 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
36
37 static const AVOption removegrain_options[] = {
38 { "m0", "set mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
39 { "m1", "set mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
40 { "m2", "set mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
41 { "m3", "set mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, FLAGS },
42 {NULL}
43 };
44
45 AVFILTER_DEFINE_CLASS(removegrain);
46
47 static const enum AVPixelFormat pix_fmts[] = {
48 AV_PIX_FMT_GRAY8,
49 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
50 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
51 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
52 AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
53 AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
54 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
55 AV_PIX_FMT_NONE
56 };
57
58 #define REMOVE_GRAIN_SORT_AXIS \
59 const int ma1 = FFMAX(a1, a8); \
60 const int mi1 = FFMIN(a1, a8); \
61 const int ma2 = FFMAX(a2, a7); \
62 const int mi2 = FFMIN(a2, a7); \
63 const int ma3 = FFMAX(a3, a6); \
64 const int mi3 = FFMIN(a3, a6); \
65 const int ma4 = FFMAX(a4, a5); \
66 const int mi4 = FFMIN(a4, a5);
67
mode01(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)68 static int mode01(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
69 {
70 const int mi = FFMIN(FFMIN(FFMIN(a1, a2), FFMIN(a3, a4)), FFMIN(FFMIN(a5, a6), FFMIN(a7, a8)));
71 const int ma = FFMAX(FFMAX(FFMAX(a1, a2), FFMAX(a3, a4)), FFMAX(FFMAX(a5, a6), FFMAX(a7, a8)));
72
73 return av_clip(c, mi, ma);
74 }
75
cmp_int(const void * p1,const void * p2)76 static int cmp_int(const void *p1, const void *p2)
77 {
78 int left = *(const int *)p1;
79 int right = *(const int *)p2;
80 return FFDIFFSIGN(left, right);
81 }
82
mode02(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)83 static int mode02(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
84 {
85 int a[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
86
87 AV_QSORT(a, 8, int, cmp_int);
88
89 return av_clip(c, a[2 - 1 ], a[7 - 1]);
90 }
91
mode03(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)92 static int mode03(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
93 {
94 int a[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
95
96 AV_QSORT(a, 8, int, cmp_int);
97
98 return av_clip(c, a[3 - 1 ], a[6 - 1]);
99 }
100
mode04(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)101 static int mode04(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
102 {
103 int a[8] = { a1, a2, a3, a4, a5, a6, a7, a8 };
104
105 AV_QSORT(a, 8, int, cmp_int);
106
107 return av_clip(c, a[4 - 1 ], a[5 - 1]);
108 }
109
mode05(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)110 static int mode05(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
111 {
112 REMOVE_GRAIN_SORT_AXIS
113
114 const int c1 = FFABS(c - av_clip(c, mi1, ma1));
115 const int c2 = FFABS(c - av_clip(c, mi2, ma2));
116 const int c3 = FFABS(c - av_clip(c, mi3, ma3));
117 const int c4 = FFABS(c - av_clip(c, mi4, ma4));
118
119 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
120
121 /* When adding SIMD notice the return order here: 4, 2, 3, 1. */
122 if (mindiff == c4) {
123 return av_clip(c, mi4, ma4);
124 } else if (mindiff == c2) {
125 return av_clip(c, mi2, ma2);
126 } else if (mindiff == c3) {
127 return av_clip(c, mi3, ma3);
128 }
129
130 return av_clip(c, mi1, ma1);
131 }
132
mode06(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)133 static int mode06(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
134 {
135 REMOVE_GRAIN_SORT_AXIS
136
137 const int d1 = ma1 - mi1;
138 const int d2 = ma2 - mi2;
139 const int d3 = ma3 - mi3;
140 const int d4 = ma4 - mi4;
141
142 const int cli1 = av_clip(c, mi1, ma1);
143 const int cli2 = av_clip(c, mi2, ma2);
144 const int cli3 = av_clip(c, mi3, ma3);
145 const int cli4 = av_clip(c, mi4, ma4);
146
147 const int c1 = av_clip_uint16((FFABS(c - cli1) << 1) + d1);
148 const int c2 = av_clip_uint16((FFABS(c - cli2) << 1) + d2);
149 const int c3 = av_clip_uint16((FFABS(c - cli3) << 1) + d3);
150 const int c4 = av_clip_uint16((FFABS(c - cli4) << 1) + d4);
151
152 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
153
154 if (mindiff == c4) {
155 return cli4;
156 } else if (mindiff == c2) {
157 return cli2;
158 } else if (mindiff == c3) {
159 return cli3;
160 }
161
162 return cli1;
163 }
164
mode07(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)165 static int mode07(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
166 {
167 REMOVE_GRAIN_SORT_AXIS
168
169 const int d1 = ma1 - mi1;
170 const int d2 = ma2 - mi2;
171 const int d3 = ma3 - mi3;
172 const int d4 = ma4 - mi4;
173
174 const int cli1 = av_clip(c, mi1, ma1);
175 const int cli2 = av_clip(c, mi2, ma2);
176 const int cli3 = av_clip(c, mi3, ma3);
177 const int cli4 = av_clip(c, mi4, ma4);
178
179 const int c1 = FFABS(c - cli1) + d1;
180 const int c2 = FFABS(c - cli2) + d2;
181 const int c3 = FFABS(c - cli3) + d3;
182 const int c4 = FFABS(c - cli4) + d4;
183
184 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
185
186 if (mindiff == c4) {
187 return cli4;
188 } else if (mindiff == c2) {
189 return cli2;
190 } else if (mindiff == c3) {
191 return cli3;
192 }
193
194 return cli1;
195 }
196
mode08(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)197 static int mode08(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
198 {
199 REMOVE_GRAIN_SORT_AXIS
200
201 const int d1 = ma1 - mi1;
202 const int d2 = ma2 - mi2;
203 const int d3 = ma3 - mi3;
204 const int d4 = ma4 - mi4;
205
206 const int cli1 = av_clip(c, mi1, ma1);
207 const int cli2 = av_clip(c, mi2, ma2);
208 const int cli3 = av_clip(c, mi3, ma3);
209 const int cli4 = av_clip(c, mi4, ma4);
210
211 const int c1 = av_clip_uint16(FFABS(c - cli1) + (d1 << 1));
212 const int c2 = av_clip_uint16(FFABS(c - cli2) + (d2 << 1));
213 const int c3 = av_clip_uint16(FFABS(c - cli3) + (d3 << 1));
214 const int c4 = av_clip_uint16(FFABS(c - cli4) + (d4 << 1));
215
216 const int mindiff = FFMIN(FFMIN(c1, c2), FFMIN(c3, c4));
217
218 if (mindiff == c4) {
219 return cli4;
220 } else if (mindiff == c2) {
221 return cli2;
222 } else if (mindiff == c3) {
223 return cli3;
224 }
225
226 return cli1;
227 }
228
mode09(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)229 static int mode09(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
230 {
231 REMOVE_GRAIN_SORT_AXIS
232
233 const int d1 = ma1 - mi1;
234 const int d2 = ma2 - mi2;
235 const int d3 = ma3 - mi3;
236 const int d4 = ma4 - mi4;
237
238 const int mindiff = FFMIN(FFMIN(d1, d2), FFMIN(d3, d4));
239
240 if (mindiff == d4) {
241 return av_clip(c, mi4, ma4);
242 } else if (mindiff == d2) {
243 return av_clip(c, mi2, ma2);
244 } else if (mindiff == d3) {
245 return av_clip(c, mi3, ma3);
246 }
247
248 return av_clip(c, mi1, ma1);
249 }
250
mode10(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)251 static int mode10(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
252 {
253 const int d1 = FFABS(c - a1);
254 const int d2 = FFABS(c - a2);
255 const int d3 = FFABS(c - a3);
256 const int d4 = FFABS(c - a4);
257 const int d5 = FFABS(c - a5);
258 const int d6 = FFABS(c - a6);
259 const int d7 = FFABS(c - a7);
260 const int d8 = FFABS(c - a8);
261
262 const int mindiff = FFMIN(FFMIN(FFMIN(d1, d2), FFMIN(d3, d4)),
263 FFMIN(FFMIN(d5, d6), FFMIN(d7, d8)));
264
265 if (mindiff == d7) return a7;
266 if (mindiff == d8) return a8;
267 if (mindiff == d6) return a6;
268 if (mindiff == d2) return a2;
269 if (mindiff == d3) return a3;
270 if (mindiff == d1) return a1;
271 if (mindiff == d5) return a5;
272
273 return a4;
274 }
275
mode1112(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)276 static int mode1112(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
277 {
278 const int sum = 4 * c + 2 * (a2 + a4 + a5 + a7) + a1 + a3 + a6 + a8;
279 const int val = (sum + 8) >> 4;
280
281 return val;
282 }
283
mode1314(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)284 static int mode1314(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
285 {
286 const int d1 = FFABS(a1 - a8);
287 const int d2 = FFABS(a2 - a7);
288 const int d3 = FFABS(a3 - a6);
289
290 const int mindiff = FFMIN(FFMIN(d1, d2), d3);
291
292 if (mindiff == d2) {
293 return (a2 + a7 + 1) >> 1;
294 }
295 if (mindiff == d3) {
296 return (a3 + a6 + 1) >> 1;
297 }
298
299 return (a1 + a8 + 1) >> 1;
300 }
301
mode1516(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)302 static int mode1516(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
303 {
304 const int d1 = FFABS(a1 - a8);
305 const int d2 = FFABS(a2 - a7);
306 const int d3 = FFABS(a3 - a6);
307
308 const int mindiff = FFMIN(FFMIN(d1, d2), d3);
309 const int average = (2 * (a2 + a7) + a1 + a3 + a6 + a8 + 4) >> 3;
310
311 if (mindiff == d2) {
312 return av_clip(average, FFMIN(a2, a7), FFMAX(a2, a7));
313 }
314 if (mindiff == d3) {
315 return av_clip(average, FFMIN(a3, a6), FFMAX(a3, a6));
316 }
317
318 return av_clip(average, FFMIN(a1, a8), FFMAX(a1, a8));
319 }
320
mode17(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)321 static int mode17(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
322 {
323 REMOVE_GRAIN_SORT_AXIS
324
325 const int l = FFMAX(FFMAX(mi1, mi2), FFMAX(mi3, mi4));
326 const int u = FFMIN(FFMIN(ma1, ma2), FFMIN(ma3, ma4));
327
328 return av_clip(c, FFMIN(l, u), FFMAX(l, u));
329 }
330
mode18(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)331 static int mode18(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
332 {
333 const int d1 = FFMAX(FFABS(c - a1), FFABS(c - a8));
334 const int d2 = FFMAX(FFABS(c - a2), FFABS(c - a7));
335 const int d3 = FFMAX(FFABS(c - a3), FFABS(c - a6));
336 const int d4 = FFMAX(FFABS(c - a4), FFABS(c - a5));
337
338 const int mindiff = FFMIN(FFMIN(d1, d2), FFMIN(d3, d4));
339
340 if (mindiff == d4) {
341 return av_clip(c, FFMIN(a4, a5), FFMAX(a4, a5));
342 }
343 if (mindiff == d2) {
344 return av_clip(c, FFMIN(a2, a7), FFMAX(a2, a7));
345 }
346 if (mindiff == d3) {
347 return av_clip(c, FFMIN(a3, a6), FFMAX(a3, a6));
348 }
349
350 return av_clip(c, FFMIN(a1, a8), FFMAX(a1, a8));
351 }
352
mode19(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)353 static int mode19(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
354 {
355 const int sum = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;
356 const int val = (sum + 4) >> 3;
357
358 return val;
359 }
360
mode20(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)361 static int mode20(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
362 {
363 const int sum = a1 + a2 + a3 + a4 + c + a5 + a6 + a7 + a8;
364 const int val = (sum + 4) / 9;
365
366 return val;
367 }
368
mode21(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)369 static int mode21(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
370 {
371 const int l1l = (a1 + a8) >> 1;
372 const int l2l = (a2 + a7) >> 1;
373 const int l3l = (a3 + a6) >> 1;
374 const int l4l = (a4 + a5) >> 1;
375
376 const int l1h = (a1 + a8 + 1) >> 1;
377 const int l2h = (a2 + a7 + 1) >> 1;
378 const int l3h = (a3 + a6 + 1) >> 1;
379 const int l4h = (a4 + a5 + 1) >> 1;
380
381 const int mi = FFMIN(FFMIN(l1l, l2l), FFMIN(l3l, l4l));
382 const int ma = FFMAX(FFMAX(l1h, l2h), FFMAX(l3h, l4h));
383
384 return av_clip(c, mi, ma);
385 }
386
mode22(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)387 static int mode22(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
388 {
389 const int l1 = (a1 + a8 + 1) >> 1;
390 const int l2 = (a2 + a7 + 1) >> 1;
391 const int l3 = (a3 + a6 + 1) >> 1;
392 const int l4 = (a4 + a5 + 1) >> 1;
393
394 const int mi = FFMIN(FFMIN(l1, l2), FFMIN(l3, l4));
395 const int ma = FFMAX(FFMAX(l1, l2), FFMAX(l3, l4));
396
397 return av_clip(c, mi, ma);
398 }
399
mode23(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)400 static int mode23(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
401 {
402 REMOVE_GRAIN_SORT_AXIS
403
404 const int linediff1 = ma1 - mi1;
405 const int linediff2 = ma2 - mi2;
406 const int linediff3 = ma3 - mi3;
407 const int linediff4 = ma4 - mi4;
408
409 const int u1 = FFMIN(c - ma1, linediff1);
410 const int u2 = FFMIN(c - ma2, linediff2);
411 const int u3 = FFMIN(c - ma3, linediff3);
412 const int u4 = FFMIN(c - ma4, linediff4);
413 const int u = FFMAX(FFMAX(FFMAX(u1, u2), FFMAX(u3, u4)), 0);
414
415 const int d1 = FFMIN(mi1 - c, linediff1);
416 const int d2 = FFMIN(mi2 - c, linediff2);
417 const int d3 = FFMIN(mi3 - c, linediff3);
418 const int d4 = FFMIN(mi4 - c, linediff4);
419 const int d = FFMAX(FFMAX(FFMAX(d1, d2), FFMAX(d3, d4)), 0);
420
421 return c - u + d; // This probably will never overflow.
422 }
423
mode24(int c,int a1,int a2,int a3,int a4,int a5,int a6,int a7,int a8)424 static int mode24(int c, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
425 {
426 REMOVE_GRAIN_SORT_AXIS
427
428 const int linediff1 = ma1 - mi1;
429 const int linediff2 = ma2 - mi2;
430 const int linediff3 = ma3 - mi3;
431 const int linediff4 = ma4 - mi4;
432
433 const int tu1 = c - ma1;
434 const int tu2 = c - ma2;
435 const int tu3 = c - ma3;
436 const int tu4 = c - ma4;
437
438 const int u1 = FFMIN(tu1, linediff1 - tu1);
439 const int u2 = FFMIN(tu2, linediff2 - tu2);
440 const int u3 = FFMIN(tu3, linediff3 - tu3);
441 const int u4 = FFMIN(tu4, linediff4 - tu4);
442 const int u = FFMAX(FFMAX(FFMAX(u1, u2), FFMAX(u3, u4)), 0);
443
444 const int td1 = mi1 - c;
445 const int td2 = mi2 - c;
446 const int td3 = mi3 - c;
447 const int td4 = mi4 - c;
448
449 const int d1 = FFMIN(td1, linediff1 - td1);
450 const int d2 = FFMIN(td2, linediff2 - td2);
451 const int d3 = FFMIN(td3, linediff3 - td3);
452 const int d4 = FFMIN(td4, linediff4 - td4);
453 const int d = FFMAX(FFMAX(FFMAX(d1, d2), FFMAX(d3, d4)), 0);
454
455 return c - u + d; // This probably will never overflow.
456 }
457
config_input(AVFilterLink * inlink)458 static int config_input(AVFilterLink *inlink)
459 {
460 RemoveGrainContext *s = inlink->dst->priv;
461 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
462 int i;
463
464 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
465
466 s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
467 s->planeheight[0] = s->planeheight[3] = inlink->h;
468 s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
469 s->planewidth[0] = s->planewidth[3] = inlink->w;
470
471 for (i = 0; i < s->nb_planes; i++) {
472 switch (s->mode[i]) {
473 case 1: s->rg[i] = mode01; break;
474 case 2: s->rg[i] = mode02; break;
475 case 3: s->rg[i] = mode03; break;
476 case 4: s->rg[i] = mode04; break;
477 case 5: s->rg[i] = mode05; break;
478 case 6: s->rg[i] = mode06; break;
479 case 7: s->rg[i] = mode07; break;
480 case 8: s->rg[i] = mode08; break;
481 case 9: s->rg[i] = mode09; break;
482 case 10: s->rg[i] = mode10; break;
483 case 11: s->rg[i] = mode1112; break;
484 case 12: s->rg[i] = mode1112; break;
485 case 13: s->skip_odd = 1;
486 s->rg[i] = mode1314; break;
487 case 14: s->skip_even = 1;
488 s->rg[i] = mode1314; break;
489 case 15: s->skip_odd = 1;
490 s->rg[i] = mode1516; break;
491 case 16: s->skip_even = 1;
492 s->rg[i] = mode1516; break;
493 case 17: s->rg[i] = mode17; break;
494 case 18: s->rg[i] = mode18; break;
495 case 19: s->rg[i] = mode19; break;
496 case 20: s->rg[i] = mode20; break;
497 case 21: s->rg[i] = mode21; break;
498 case 22: s->rg[i] = mode22; break;
499 case 23: s->rg[i] = mode23; break;
500 case 24: s->rg[i] = mode24; break;
501 }
502 }
503
504 #if ARCH_X86
505 ff_removegrain_init_x86(s);
506 #endif
507
508 return 0;
509 }
510
511 typedef struct ThreadData {
512 AVFrame *in, *out;
513 int plane;
514 } ThreadData;
515
filter_slice(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)516 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
517 {
518 RemoveGrainContext *s = ctx->priv;
519 ThreadData *td = arg;
520 AVFrame *in = td->in;
521 AVFrame *out = td->out;
522 const int i = td->plane;
523 const int height = s->planeheight[i];
524 const int om = in->linesize[i] - 1;
525 const int o0 = in->linesize[i] ;
526 const int op = in->linesize[i] + 1;
527 int start = (height * jobnr ) / nb_jobs;
528 int end = (height * (jobnr+1)) / nb_jobs;
529 int x, y;
530
531 start = FFMAX(1, start);
532 end = FFMIN(height-1, end);
533 for (y = start; y < end; y++) {
534 uint8_t *dst = out->data[i];
535 uint8_t *src = in->data[i];
536
537 src = in->data[i] + y * in->linesize[i];
538 dst = out->data[i] + y * out->linesize[i];
539
540 if (s->skip_even && !(y & 1)) {
541 memcpy(dst, src, s->planewidth[i]);
542 continue;
543 }
544 if (s->skip_odd && y & 1) {
545 memcpy(dst, src, s->planewidth[i]);
546 continue;
547 }
548
549 *dst++ = *src++;
550
551 if (s->fl[i]) {
552 int w_asm = (s->planewidth[i] - 2) & ~15;
553
554 s->fl[i](dst, src, in->linesize[i], w_asm);
555
556 x = 1 + w_asm;
557 dst += w_asm;
558 src += w_asm;
559 } else
560 x = 1;
561
562 for (; x < s->planewidth[i] - 1; x++) {
563 const int a1 = src[-op];
564 const int a2 = src[-o0];
565 const int a3 = src[-om];
566 const int a4 = src[-1 ];
567 const int c = src[ 0 ];
568 const int a5 = src[ 1 ];
569 const int a6 = src[ om];
570 const int a7 = src[ o0];
571 const int a8 = src[ op];
572
573 const int res = s->rg[i](c, a1, a2, a3, a4, a5, a6, a7, a8);
574
575 *dst = res;
576 dst++, src++;
577 }
578 dst[0] = src[0];
579 }
580
581 return 0;
582 }
583
filter_frame(AVFilterLink * inlink,AVFrame * in)584 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
585 {
586 AVFilterContext *ctx = inlink->dst;
587 AVFilterLink *outlink = ctx->outputs[0];
588 RemoveGrainContext *s = ctx->priv;
589 ThreadData td;
590 AVFrame *out;
591 int i;
592
593 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
594 if (!out) {
595 av_frame_free(&in);
596 return AVERROR(ENOMEM);
597 }
598 av_frame_copy_props(out, in);
599
600 for (i = 0; i < s->nb_planes; i++) {
601 uint8_t *dst = out->data[i];
602 uint8_t *src = in->data[i];
603
604 if (s->mode[i] == 0) {
605 av_image_copy_plane(dst, out->linesize[i],
606 src, in->linesize[i],
607 s->planewidth[i], s->planeheight[i]);
608 continue;
609 }
610
611 memcpy(dst, src, s->planewidth[i]);
612
613 td.in = in; td.out = out; td.plane = i;
614 ff_filter_execute(ctx, filter_slice, &td, NULL,
615 FFMIN(s->planeheight[i], ff_filter_get_nb_threads(ctx)));
616
617 src = in->data[i] + (s->planeheight[i] - 1) * in->linesize[i];
618 dst = out->data[i] + (s->planeheight[i] - 1) * out->linesize[i];
619 memcpy(dst, src, s->planewidth[i]);
620 }
621
622 av_frame_free(&in);
623 return ff_filter_frame(outlink, out);
624 }
625
626 static const AVFilterPad removegrain_inputs[] = {
627 {
628 .name = "default",
629 .type = AVMEDIA_TYPE_VIDEO,
630 .filter_frame = filter_frame,
631 .config_props = config_input,
632 },
633 };
634
635 static const AVFilterPad removegrain_outputs[] = {
636 {
637 .name = "default",
638 .type = AVMEDIA_TYPE_VIDEO,
639 },
640 };
641
642 const AVFilter ff_vf_removegrain = {
643 .name = "removegrain",
644 .description = NULL_IF_CONFIG_SMALL("Remove grain."),
645 .priv_size = sizeof(RemoveGrainContext),
646 FILTER_INPUTS(removegrain_inputs),
647 FILTER_OUTPUTS(removegrain_outputs),
648 FILTER_PIXFMTS_ARRAY(pix_fmts),
649 .priv_class = &removegrain_class,
650 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
651 };
652