• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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