1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <float.h>
20
21 #include "libavutil/avassert.h"
22 #include "libavutil/common.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "internal.h"
27 #include "libavcodec/avfft.h"
28
29 enum BufferTypes {
30 CURRENT,
31 PREV,
32 NEXT,
33 BSIZE
34 };
35
36 typedef struct PlaneContext {
37 int planewidth, planeheight;
38 int nox, noy;
39 int b;
40 int o;
41 float n;
42
43 float *buffer[BSIZE];
44 FFTComplex *hdata, *vdata;
45 int data_linesize;
46 int buffer_linesize;
47
48 FFTContext *fft, *ifft;
49 } PlaneContext;
50
51 typedef struct FFTdnoizContext {
52 const AVClass *class;
53
54 float sigma;
55 float amount;
56 int block_bits;
57 float overlap;
58 int nb_prev;
59 int nb_next;
60 int planesf;
61
62 AVFrame *prev, *cur, *next;
63
64 int depth;
65 int nb_planes;
66 PlaneContext planes[4];
67
68 void (*import_row)(FFTComplex *dst, uint8_t *src, int rw);
69 void (*export_row)(FFTComplex *src, uint8_t *dst, int rw, float scale, int depth);
70 } FFTdnoizContext;
71
72 #define OFFSET(x) offsetof(FFTdnoizContext, x)
73 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
74 static const AVOption fftdnoiz_options[] = {
75 { "sigma", "set denoise strength",
76 OFFSET(sigma), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 30, .flags = FLAGS },
77 { "amount", "set amount of denoising",
78 OFFSET(amount), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0.01, 1, .flags = FLAGS },
79 { "block", "set block log2(size)",
80 OFFSET(block_bits), AV_OPT_TYPE_INT, {.i64=4}, 3, 6, .flags = FLAGS },
81 { "overlap", "set block overlap",
82 OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0.2, 0.8, .flags = FLAGS },
83 { "prev", "set number of previous frames for temporal denoising",
84 OFFSET(nb_prev), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = FLAGS },
85 { "next", "set number of next frames for temporal denoising",
86 OFFSET(nb_next), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = FLAGS },
87 { "planes", "set planes to filter",
88 OFFSET(planesf), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, .flags = FLAGS },
89 { NULL }
90 };
91
92 AVFILTER_DEFINE_CLASS(fftdnoiz);
93
init(AVFilterContext * ctx)94 static av_cold int init(AVFilterContext *ctx)
95 {
96 FFTdnoizContext *s = ctx->priv;
97 int i;
98
99 for (i = 0; i < 4; i++) {
100 PlaneContext *p = &s->planes[i];
101
102 p->fft = av_fft_init(s->block_bits, 0);
103 p->ifft = av_fft_init(s->block_bits, 1);
104 if (!p->fft || !p->ifft)
105 return AVERROR(ENOMEM);
106 }
107
108 return 0;
109 }
110
query_formats(AVFilterContext * ctx)111 static int query_formats(AVFilterContext *ctx)
112 {
113 static const enum AVPixelFormat pix_fmts[] = {
114 AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9,
115 AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12,
116 AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
117 AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
118 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
119 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
120 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
121 AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
122 AV_PIX_FMT_YUVJ411P,
123 AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
124 AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
125 AV_PIX_FMT_YUV440P10,
126 AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
127 AV_PIX_FMT_YUV440P12,
128 AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
129 AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
130 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
131 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
132 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P,
133 AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16,
134 AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16,
135 AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16,
136 AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
137 AV_PIX_FMT_NONE
138 };
139 AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
140 if (!fmts_list)
141 return AVERROR(ENOMEM);
142 return ff_set_common_formats(ctx, fmts_list);
143 }
144
145 typedef struct ThreadData {
146 float *src, *dst;
147 } ThreadData;
148
import_row8(FFTComplex * dst,uint8_t * src,int rw)149 static void import_row8(FFTComplex *dst, uint8_t *src, int rw)
150 {
151 int j;
152
153 for (j = 0; j < rw; j++) {
154 dst[j].re = src[j];
155 dst[j].im = 0;
156 }
157 }
158
export_row8(FFTComplex * src,uint8_t * dst,int rw,float scale,int depth)159 static void export_row8(FFTComplex *src, uint8_t *dst, int rw, float scale, int depth)
160 {
161 int j;
162
163 for (j = 0; j < rw; j++)
164 dst[j] = av_clip_uint8(src[j].re * scale + 0.5f);
165 }
166
import_row16(FFTComplex * dst,uint8_t * srcp,int rw)167 static void import_row16(FFTComplex *dst, uint8_t *srcp, int rw)
168 {
169 uint16_t *src = (uint16_t *)srcp;
170 int j;
171
172 for (j = 0; j < rw; j++) {
173 dst[j].re = src[j];
174 dst[j].im = 0;
175 }
176 }
177
export_row16(FFTComplex * src,uint8_t * dstp,int rw,float scale,int depth)178 static void export_row16(FFTComplex *src, uint8_t *dstp, int rw, float scale, int depth)
179 {
180 uint16_t *dst = (uint16_t *)dstp;
181 int j;
182
183 for (j = 0; j < rw; j++)
184 dst[j] = av_clip_uintp2_c(src[j].re * scale + 0.5f, depth);
185 }
186
config_input(AVFilterLink * inlink)187 static int config_input(AVFilterLink *inlink)
188 {
189 AVFilterContext *ctx = inlink->dst;
190 const AVPixFmtDescriptor *desc;
191 FFTdnoizContext *s = ctx->priv;
192 int i;
193
194 desc = av_pix_fmt_desc_get(inlink->format);
195 s->depth = desc->comp[0].depth;
196
197 if (s->depth <= 8) {
198 s->import_row = import_row8;
199 s->export_row = export_row8;
200 } else {
201 s->import_row = import_row16;
202 s->export_row = export_row16;
203 s->sigma *= 1 << (s->depth - 8) * (1 + s->nb_prev + s->nb_next);
204 }
205
206 s->planes[1].planewidth = s->planes[2].planewidth = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
207 s->planes[0].planewidth = s->planes[3].planewidth = inlink->w;
208 s->planes[1].planeheight = s->planes[2].planeheight = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
209 s->planes[0].planeheight = s->planes[3].planeheight = inlink->h;
210
211 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
212
213 for (i = 0; i < s->nb_planes; i++) {
214 PlaneContext *p = &s->planes[i];
215 int size;
216
217 p->b = 1 << s->block_bits;
218 p->n = 1.f / (p->b * p->b);
219 p->o = p->b * s->overlap;
220 size = p->b - p->o;
221 p->nox = (p->planewidth + (size - 1)) / size;
222 p->noy = (p->planeheight + (size - 1)) / size;
223
224 av_log(ctx, AV_LOG_DEBUG, "nox:%d noy:%d size:%d\n", p->nox, p->noy, size);
225
226 p->buffer_linesize = p->b * p->nox * sizeof(FFTComplex);
227 p->buffer[CURRENT] = av_calloc(p->b * p->noy, p->buffer_linesize);
228 if (!p->buffer[CURRENT])
229 return AVERROR(ENOMEM);
230 if (s->nb_prev > 0) {
231 p->buffer[PREV] = av_calloc(p->b * p->noy, p->buffer_linesize);
232 if (!p->buffer[PREV])
233 return AVERROR(ENOMEM);
234 }
235 if (s->nb_next > 0) {
236 p->buffer[NEXT] = av_calloc(p->b * p->noy, p->buffer_linesize);
237 if (!p->buffer[NEXT])
238 return AVERROR(ENOMEM);
239 }
240 p->data_linesize = 2 * p->b * sizeof(float);
241 p->hdata = av_calloc(p->b, p->data_linesize);
242 p->vdata = av_calloc(p->b, p->data_linesize);
243 if (!p->hdata || !p->vdata)
244 return AVERROR(ENOMEM);
245 }
246
247 return 0;
248 }
249
import_plane(FFTdnoizContext * s,uint8_t * srcp,int src_linesize,float * buffer,int buffer_linesize,int plane)250 static void import_plane(FFTdnoizContext *s,
251 uint8_t *srcp, int src_linesize,
252 float *buffer, int buffer_linesize, int plane)
253 {
254 PlaneContext *p = &s->planes[plane];
255 const int width = p->planewidth;
256 const int height = p->planeheight;
257 const int block = p->b;
258 const int overlap = p->o;
259 const int size = block - overlap;
260 const int nox = p->nox;
261 const int noy = p->noy;
262 const int bpp = (s->depth + 7) / 8;
263 const int data_linesize = p->data_linesize / sizeof(FFTComplex);
264 FFTComplex *hdata = p->hdata;
265 FFTComplex *vdata = p->vdata;
266 int x, y, i, j;
267
268 buffer_linesize /= sizeof(float);
269 for (y = 0; y < noy; y++) {
270 for (x = 0; x < nox; x++) {
271 const int rh = FFMIN(block, height - y * size);
272 const int rw = FFMIN(block, width - x * size);
273 uint8_t *src = srcp + src_linesize * y * size + x * size * bpp;
274 float *bdst = buffer + buffer_linesize * y * block + x * block * 2;
275 FFTComplex *ssrc, *dst = hdata;
276
277 for (i = 0; i < rh; i++) {
278 s->import_row(dst, src, rw);
279 for (j = rw; j < block; j++) {
280 dst[j].re = dst[block - j - 1].re;
281 dst[j].im = 0;
282 }
283 av_fft_permute(p->fft, dst);
284 av_fft_calc(p->fft, dst);
285
286 src += src_linesize;
287 dst += data_linesize;
288 }
289
290 dst = hdata;
291 for (; i < block; i++) {
292 for (j = 0; j < block; j++) {
293 dst[j].re = dst[(block - i - 1) * data_linesize + j].re;
294 dst[j].im = dst[(block - i - 1) * data_linesize + j].im;
295 }
296 }
297
298 ssrc = hdata;
299 dst = vdata;
300 for (i = 0; i < block; i++) {
301 for (j = 0; j < block; j++)
302 dst[j] = ssrc[j * data_linesize + i];
303 av_fft_permute(p->fft, dst);
304 av_fft_calc(p->fft, dst);
305 memcpy(bdst, dst, block * sizeof(FFTComplex));
306
307 dst += data_linesize;
308 bdst += buffer_linesize;
309 }
310 }
311 }
312 }
313
export_plane(FFTdnoizContext * s,uint8_t * dstp,int dst_linesize,float * buffer,int buffer_linesize,int plane)314 static void export_plane(FFTdnoizContext *s,
315 uint8_t *dstp, int dst_linesize,
316 float *buffer, int buffer_linesize, int plane)
317 {
318 PlaneContext *p = &s->planes[plane];
319 const int depth = s->depth;
320 const int bpp = (depth + 7) / 8;
321 const int width = p->planewidth;
322 const int height = p->planeheight;
323 const int block = p->b;
324 const int overlap = p->o;
325 const int hoverlap = overlap / 2;
326 const int size = block - overlap;
327 const int nox = p->nox;
328 const int noy = p->noy;
329 const int data_linesize = p->data_linesize / sizeof(FFTComplex);
330 const float scale = 1.f / (block * block);
331 FFTComplex *hdata = p->hdata;
332 FFTComplex *vdata = p->vdata;
333 int x, y, i, j;
334
335 buffer_linesize /= sizeof(float);
336 for (y = 0; y < noy; y++) {
337 for (x = 0; x < nox; x++) {
338 const int woff = x == 0 ? 0 : hoverlap;
339 const int hoff = y == 0 ? 0 : hoverlap;
340 const int rw = x == 0 ? block : FFMIN(size, width - x * size - woff);
341 const int rh = y == 0 ? block : FFMIN(size, height - y * size - hoff);
342 float *bsrc = buffer + buffer_linesize * y * block + x * block * 2;
343 uint8_t *dst = dstp + dst_linesize * (y * size + hoff) + (x * size + woff) * bpp;
344 FFTComplex *hdst, *ddst = vdata;
345
346 hdst = hdata;
347 for (i = 0; i < block; i++) {
348 memcpy(ddst, bsrc, block * sizeof(FFTComplex));
349 av_fft_permute(p->ifft, ddst);
350 av_fft_calc(p->ifft, ddst);
351 for (j = 0; j < block; j++) {
352 hdst[j * data_linesize + i] = ddst[j];
353 }
354
355 ddst += data_linesize;
356 bsrc += buffer_linesize;
357 }
358
359 hdst = hdata + hoff * data_linesize;
360 for (i = 0; i < rh; i++) {
361 av_fft_permute(p->ifft, hdst);
362 av_fft_calc(p->ifft, hdst);
363 s->export_row(hdst + woff, dst, rw, scale, depth);
364
365 hdst += data_linesize;
366 dst += dst_linesize;
367 }
368 }
369 }
370 }
371
filter_plane3d2(FFTdnoizContext * s,int plane,float * pbuffer,float * nbuffer)372 static void filter_plane3d2(FFTdnoizContext *s, int plane, float *pbuffer, float *nbuffer)
373 {
374 PlaneContext *p = &s->planes[plane];
375 const int block = p->b;
376 const int nox = p->nox;
377 const int noy = p->noy;
378 const int buffer_linesize = p->buffer_linesize / sizeof(float);
379 const float sigma = s->sigma * s->sigma * block * block;
380 const float limit = 1.f - s->amount;
381 float *cbuffer = p->buffer[CURRENT];
382 const float cfactor = sqrtf(3.f) * 0.5f;
383 const float scale = 1.f / 3.f;
384 int y, x, i, j;
385
386 for (y = 0; y < noy; y++) {
387 for (x = 0; x < nox; x++) {
388 float *cbuff = cbuffer + buffer_linesize * y * block + x * block * 2;
389 float *pbuff = pbuffer + buffer_linesize * y * block + x * block * 2;
390 float *nbuff = nbuffer + buffer_linesize * y * block + x * block * 2;
391
392 for (i = 0; i < block; i++) {
393 for (j = 0; j < block; j++) {
394 float sumr, sumi, difr, difi, mpr, mpi, mnr, mni;
395 float factor, power, sumpnr, sumpni;
396
397 sumpnr = pbuff[2 * j ] + nbuff[2 * j ];
398 sumpni = pbuff[2 * j + 1] + nbuff[2 * j + 1];
399 sumr = cbuff[2 * j ] + sumpnr;
400 sumi = cbuff[2 * j + 1] + sumpni;
401 difr = cfactor * (nbuff[2 * j ] - pbuff[2 * j ]);
402 difi = cfactor * (pbuff[2 * j + 1] - nbuff[2 * j + 1]);
403 mpr = cbuff[2 * j ] - 0.5f * sumpnr + difi;
404 mnr = mpr - difi - difi;
405 mpi = cbuff[2 * j + 1] - 0.5f * sumpni + difr;
406 mni = mpi - difr - difr;
407 power = sumr * sumr + sumi * sumi + 1e-15f;
408 factor = FFMAX((power - sigma) / power, limit);
409 sumr *= factor;
410 sumi *= factor;
411 power = mpr * mpr + mpi * mpi + 1e-15f;
412 factor = FFMAX((power - sigma) / power, limit);
413 mpr *= factor;
414 mpi *= factor;
415 power = mnr * mnr + mni * mni + 1e-15f;
416 factor = FFMAX((power - sigma) / power, limit);
417 mnr *= factor;
418 mni *= factor;
419 cbuff[2 * j ] = (sumr + mpr + mnr) * scale;
420 cbuff[2 * j + 1] = (sumi + mpi + mni) * scale;
421
422 }
423
424 cbuff += buffer_linesize;
425 pbuff += buffer_linesize;
426 nbuff += buffer_linesize;
427 }
428 }
429 }
430 }
431
filter_plane3d1(FFTdnoizContext * s,int plane,float * pbuffer)432 static void filter_plane3d1(FFTdnoizContext *s, int plane, float *pbuffer)
433 {
434 PlaneContext *p = &s->planes[plane];
435 const int block = p->b;
436 const int nox = p->nox;
437 const int noy = p->noy;
438 const int buffer_linesize = p->buffer_linesize / sizeof(float);
439 const float sigma = s->sigma * s->sigma * block * block;
440 const float limit = 1.f - s->amount;
441 float *cbuffer = p->buffer[CURRENT];
442 int y, x, i, j;
443
444 for (y = 0; y < noy; y++) {
445 for (x = 0; x < nox; x++) {
446 float *cbuff = cbuffer + buffer_linesize * y * block + x * block * 2;
447 float *pbuff = pbuffer + buffer_linesize * y * block + x * block * 2;
448
449 for (i = 0; i < block; i++) {
450 for (j = 0; j < block; j++) {
451 float factor, power, re, im, pre, pim;
452 float sumr, sumi, difr, difi;
453
454 re = cbuff[j * 2 ];
455 pre = pbuff[j * 2 ];
456 im = cbuff[j * 2 + 1];
457 pim = pbuff[j * 2 + 1];
458
459 sumr = re + pre;
460 sumi = im + pim;
461 difr = re - pre;
462 difi = im - pim;
463
464 power = sumr * sumr + sumi * sumi + 1e-15f;
465 factor = FFMAX(limit, (power - sigma) / power);
466 sumr *= factor;
467 sumi *= factor;
468 power = difr * difr + difi * difi + 1e-15f;
469 factor = FFMAX(limit, (power - sigma) / power);
470 difr *= factor;
471 difi *= factor;
472
473 cbuff[j * 2 ] = (sumr + difr) * 0.5f;
474 cbuff[j * 2 + 1] = (sumi + difi) * 0.5f;
475 }
476
477 cbuff += buffer_linesize;
478 pbuff += buffer_linesize;
479 }
480 }
481 }
482 }
483
filter_plane2d(FFTdnoizContext * s,int plane)484 static void filter_plane2d(FFTdnoizContext *s, int plane)
485 {
486 PlaneContext *p = &s->planes[plane];
487 const int block = p->b;
488 const int nox = p->nox;
489 const int noy = p->noy;
490 const int buffer_linesize = p->buffer_linesize / 4;
491 const float sigma = s->sigma * s->sigma * block * block;
492 const float limit = 1.f - s->amount;
493 float *buffer = p->buffer[CURRENT];
494 int y, x, i, j;
495
496 for (y = 0; y < noy; y++) {
497 for (x = 0; x < nox; x++) {
498 float *buff = buffer + buffer_linesize * y * block + x * block * 2;
499
500 for (i = 0; i < block; i++) {
501 for (j = 0; j < block; j++) {
502 float factor, power, re, im;
503
504 re = buff[j * 2 ];
505 im = buff[j * 2 + 1];
506 power = re * re + im * im + 1e-15f;
507 factor = FFMAX(limit, (power - sigma) / power);
508 buff[j * 2 ] *= factor;
509 buff[j * 2 + 1] *= factor;
510 }
511
512 buff += buffer_linesize;
513 }
514 }
515 }
516 }
517
filter_frame(AVFilterLink * inlink,AVFrame * in)518 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
519 {
520 AVFilterContext *ctx = inlink->dst;
521 FFTdnoizContext *s = ctx->priv;
522 AVFilterLink *outlink = ctx->outputs[0];
523 int direct, plane;
524 AVFrame *out;
525
526 if (s->nb_next > 0 && s->nb_prev > 0) {
527 av_frame_free(&s->prev);
528 s->prev = s->cur;
529 s->cur = s->next;
530 s->next = in;
531
532 if (!s->prev && s->cur) {
533 s->prev = av_frame_clone(s->cur);
534 if (!s->prev)
535 return AVERROR(ENOMEM);
536 }
537 if (!s->cur)
538 return 0;
539 } else if (s->nb_next > 0) {
540 av_frame_free(&s->cur);
541 s->cur = s->next;
542 s->next = in;
543
544 if (!s->cur)
545 return 0;
546 } else if (s->nb_prev > 0) {
547 av_frame_free(&s->prev);
548 s->prev = s->cur;
549 s->cur = in;
550
551 if (!s->prev)
552 s->prev = av_frame_clone(s->cur);
553 if (!s->prev)
554 return AVERROR(ENOMEM);
555 } else {
556 s->cur = in;
557 }
558
559 if (av_frame_is_writable(in) && s->nb_next == 0 && s->nb_prev == 0) {
560 direct = 1;
561 out = in;
562 } else {
563 direct = 0;
564 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
565 if (!out)
566 return AVERROR(ENOMEM);
567 av_frame_copy_props(out, s->cur);
568 }
569
570 for (plane = 0; plane < s->nb_planes; plane++) {
571 PlaneContext *p = &s->planes[plane];
572
573 if (!((1 << plane) & s->planesf) || ctx->is_disabled) {
574 if (!direct)
575 av_image_copy_plane(out->data[plane], out->linesize[plane],
576 s->cur->data[plane], s->cur->linesize[plane],
577 p->planewidth, p->planeheight);
578 continue;
579 }
580
581 if (s->next) {
582 import_plane(s, s->next->data[plane], s->next->linesize[plane],
583 p->buffer[NEXT], p->buffer_linesize, plane);
584 }
585
586 if (s->prev) {
587 import_plane(s, s->prev->data[plane], s->prev->linesize[plane],
588 p->buffer[PREV], p->buffer_linesize, plane);
589 }
590
591 import_plane(s, s->cur->data[plane], s->cur->linesize[plane],
592 p->buffer[CURRENT], p->buffer_linesize, plane);
593
594 if (s->next && s->prev) {
595 filter_plane3d2(s, plane, p->buffer[PREV], p->buffer[NEXT]);
596 } else if (s->next) {
597 filter_plane3d1(s, plane, p->buffer[NEXT]);
598 } else if (s->prev) {
599 filter_plane3d1(s, plane, p->buffer[PREV]);
600 } else {
601 filter_plane2d(s, plane);
602 }
603
604 export_plane(s, out->data[plane], out->linesize[plane],
605 p->buffer[CURRENT], p->buffer_linesize, plane);
606 }
607
608 if (s->nb_next == 0 && s->nb_prev == 0) {
609 if (direct) {
610 s->cur = NULL;
611 } else {
612 av_frame_free(&s->cur);
613 }
614 }
615 return ff_filter_frame(outlink, out);
616 }
617
request_frame(AVFilterLink * outlink)618 static int request_frame(AVFilterLink *outlink)
619 {
620 AVFilterContext *ctx = outlink->src;
621 FFTdnoizContext *s = ctx->priv;
622 int ret = 0;
623
624 ret = ff_request_frame(ctx->inputs[0]);
625
626 if (ret == AVERROR_EOF && (s->nb_next > 0)) {
627 AVFrame *buf;
628
629 if (s->next && s->nb_next > 0)
630 buf = av_frame_clone(s->next);
631 else if (s->cur)
632 buf = av_frame_clone(s->cur);
633 else
634 buf = av_frame_clone(s->prev);
635 if (!buf)
636 return AVERROR(ENOMEM);
637
638 ret = filter_frame(ctx->inputs[0], buf);
639 if (ret < 0)
640 return ret;
641 ret = AVERROR_EOF;
642 }
643
644 return ret;
645 }
646
uninit(AVFilterContext * ctx)647 static av_cold void uninit(AVFilterContext *ctx)
648 {
649 FFTdnoizContext *s = ctx->priv;
650 int i;
651
652 for (i = 0; i < 4; i++) {
653 PlaneContext *p = &s->planes[i];
654
655 av_freep(&p->hdata);
656 av_freep(&p->vdata);
657 av_freep(&p->buffer[PREV]);
658 av_freep(&p->buffer[CURRENT]);
659 av_freep(&p->buffer[NEXT]);
660 av_fft_end(p->fft);
661 av_fft_end(p->ifft);
662 }
663
664 av_frame_free(&s->prev);
665 av_frame_free(&s->cur);
666 av_frame_free(&s->next);
667 }
668
669 static const AVFilterPad fftdnoiz_inputs[] = {
670 {
671 .name = "default",
672 .type = AVMEDIA_TYPE_VIDEO,
673 .filter_frame = filter_frame,
674 .config_props = config_input,
675 },
676 { NULL }
677 };
678
679 static const AVFilterPad fftdnoiz_outputs[] = {
680 {
681 .name = "default",
682 .type = AVMEDIA_TYPE_VIDEO,
683 .request_frame = request_frame,
684 },
685 { NULL }
686 };
687
688 AVFilter ff_vf_fftdnoiz = {
689 .name = "fftdnoiz",
690 .description = NULL_IF_CONFIG_SMALL("Denoise frames using 3D FFT."),
691 .priv_size = sizeof(FFTdnoizContext),
692 .init = init,
693 .uninit = uninit,
694 .query_formats = query_formats,
695 .inputs = fftdnoiz_inputs,
696 .outputs = fftdnoiz_outputs,
697 .priv_class = &fftdnoiz_class,
698 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
699 };
700