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