2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at>
3 * Copyright (c) 2013 Paul B Mahol
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "libavutil/opt.h"
28 #include "libavutil/imgutils.h"
29 #include "libavutil/lfg.h"
30 #include "libavutil/parseutils.h"
31 #include "libavutil/pixdesc.h"
32 #include "libavutil/x86/asm.h"
38 #define MAX_NOISE 5120
39 #define MAX_SHIFT 1024
40 #define MAX_RES (MAX_NOISE-MAX_SHIFT)
42 #define NOISE_UNIFORM 1
43 #define NOISE_TEMPORAL 2
44 #define NOISE_AVERAGED 8
45 #define NOISE_PATTERN 16
53 int8_t *prev_shift
[MAX_RES
][3];
62 FilterParams param
[4];
63 int rand_shift
[MAX_RES
];
65 void (*line_noise
)(uint8_t *dst
, const uint8_t *src
, const int8_t *noise
, int len
, int shift
);
66 void (*line_noise_avg
)(uint8_t *dst
, const uint8_t *src
, int len
, const int8_t * const *shift
);
69 typedef struct ThreadData
{
73 #define OFFSET(x) offsetof(NoiseContext, x)
74 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
76 #define NOISE_PARAMS(name, x, param) \
77 {#name"_seed", "set component #"#x" noise seed", OFFSET(param.seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS}, \
78 {#name"_strength", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \
79 {#name"s", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \
80 {#name"_flags", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, #name"_flags"}, \
81 {#name"f", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, #name"_flags"}, \
82 {"a", "averaged noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_AVERAGED}, 0, 0, FLAGS, #name"_flags"}, \
83 {"p", "(semi)regular pattern", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_PATTERN}, 0, 0, FLAGS, #name"_flags"}, \
84 {"t", "temporal noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_TEMPORAL}, 0, 0, FLAGS, #name"_flags"}, \
85 {"u", "uniform noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_UNIFORM}, 0, 0, FLAGS, #name"_flags"},
87 static const AVOption noise_options
[] = {
88 NOISE_PARAMS(all
, 0, all
)
89 NOISE_PARAMS(c0
, 0, param
[0])
90 NOISE_PARAMS(c1
, 1, param
[1])
91 NOISE_PARAMS(c2
, 2, param
[2])
92 NOISE_PARAMS(c3
, 3, param
[3])
96 AVFILTER_DEFINE_CLASS(noise
);
98 static const int8_t patt
[4] = { -1, 0, 1, 0 };
100 #define RAND_N(range) ((int) ((double) range * av_lfg_get(lfg) / (UINT_MAX + 1.0)))
101 static av_cold
int init_noise(NoiseContext
*n
, int comp
)
103 int8_t *noise
= av_malloc(MAX_NOISE
* sizeof(int8_t));
104 FilterParams
*fp
= &n
->param
[comp
];
105 AVLFG
*lfg
= &n
->param
[comp
].lfg
;
106 int strength
= fp
->strength
;
107 int flags
= fp
->flags
;
111 return AVERROR(ENOMEM
);
113 av_lfg_init(&fp
->lfg
, fp
->seed
);
115 for (i
= 0, j
= 0; i
< MAX_NOISE
; i
++, j
++) {
116 if (flags
& NOISE_UNIFORM
) {
117 if (flags
& NOISE_AVERAGED
) {
118 if (flags
& NOISE_PATTERN
) {
119 noise
[i
] = (RAND_N(strength
) - strength
/ 2) / 6
120 + patt
[j
% 4] * strength
* 0.25 / 3;
122 noise
[i
] = (RAND_N(strength
) - strength
/ 2) / 3;
125 if (flags
& NOISE_PATTERN
) {
126 noise
[i
] = (RAND_N(strength
) - strength
/ 2) / 2
127 + patt
[j
% 4] * strength
* 0.25;
129 noise
[i
] = RAND_N(strength
) - strength
/ 2;
133 double x1
, x2
, w
, y1
;
135 x1
= 2.0 * av_lfg_get(lfg
) / (float)UINT_MAX
- 1.0;
136 x2
= 2.0 * av_lfg_get(lfg
) / (float)UINT_MAX
- 1.0;
137 w
= x1
* x1
+ x2
* x2
;
140 w
= sqrt((-2.0 * log(w
)) / w
);
142 y1
*= strength
/ sqrt(3.0);
143 if (flags
& NOISE_PATTERN
) {
145 y1
+= patt
[j
% 4] * strength
* 0.35;
147 y1
= av_clipf(y1
, -128, 127);
148 if (flags
& NOISE_AVERAGED
)
156 for (i
= 0; i
< MAX_RES
; i
++)
157 for (j
= 0; j
< 3; j
++)
158 fp
->prev_shift
[i
][j
] = noise
+ (av_lfg_get(lfg
) & (MAX_SHIFT
- 1));
160 if (!n
->rand_shift_init
) {
161 for (i
= 0; i
< MAX_RES
; i
++)
162 n
->rand_shift
[i
] = av_lfg_get(lfg
) & (MAX_SHIFT
- 1);
163 n
->rand_shift_init
= 1;
170 static int query_formats(AVFilterContext
*ctx
)
172 AVFilterFormats
*formats
= NULL
;
175 for (fmt
= 0; av_pix_fmt_desc_get(fmt
); fmt
++) {
176 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(fmt
);
177 if (desc
->flags
& AV_PIX_FMT_FLAG_PLANAR
&& !((desc
->comp
[0].depth_minus1
+ 1) & 7))
178 ff_add_format(&formats
, fmt
);
181 ff_set_common_formats(ctx
, formats
);
185 static int config_input(AVFilterLink
*inlink
)
187 NoiseContext
*n
= inlink
->dst
->priv
;
188 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
191 n
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
193 if ((ret
= av_image_fill_linesizes(n
->bytewidth
, inlink
->format
, inlink
->w
)) < 0)
196 n
->height
[1] = n
->height
[2] = FF_CEIL_RSHIFT(inlink
->h
, desc
->log2_chroma_h
);
197 n
->height
[0] = n
->height
[3] = inlink
->h
;
202 static inline void line_noise_c(uint8_t *dst
, const uint8_t *src
, const int8_t *noise
,
208 for (i
= 0; i
< len
; i
++) {
209 int v
= src
[i
] + noise
[i
];
211 dst
[i
] = av_clip_uint8(v
);
215 #define ASMALIGN(ZEROBITS) ".p2align " #ZEROBITS "\n\t"
217 static void line_noise_mmx(uint8_t *dst
, const uint8_t *src
,
218 const int8_t *noise
, int len
, int shift
)
221 x86_reg mmx_len
= len
&(~7);
225 "mov %3, %%"REG_a
" \n\t"
226 "pcmpeqb %%mm7, %%mm7 \n\t"
227 "psllw $15, %%mm7 \n\t"
228 "packsswb %%mm7, %%mm7 \n\t"
231 "movq (%0, %%"REG_a
"), %%mm0 \n\t"
232 "movq (%1, %%"REG_a
"), %%mm1 \n\t"
233 "pxor %%mm7, %%mm0 \n\t"
234 "paddsb %%mm1, %%mm0 \n\t"
235 "pxor %%mm7, %%mm0 \n\t"
236 "movq %%mm0, (%2, %%"REG_a
") \n\t"
237 "add $8, %%"REG_a
" \n\t"
239 :: "r" (src
+mmx_len
), "r" (noise
+mmx_len
), "r" (dst
+mmx_len
), "g" (-mmx_len
)
243 line_noise_c(dst
+mmx_len
, src
+mmx_len
, noise
+mmx_len
, len
-mmx_len
, 0);
247 static void line_noise_mmxext(uint8_t *dst
, const uint8_t *src
,
248 const int8_t *noise
, int len
, int shift
)
250 #if HAVE_MMXEXT_INLINE
251 x86_reg mmx_len
= len
&(~7);
255 "mov %3, %%"REG_a
" \n\t"
256 "pcmpeqb %%mm7, %%mm7 \n\t"
257 "psllw $15, %%mm7 \n\t"
258 "packsswb %%mm7, %%mm7 \n\t"
261 "movq (%0, %%"REG_a
"), %%mm0 \n\t"
262 "movq (%1, %%"REG_a
"), %%mm1 \n\t"
263 "pxor %%mm7, %%mm0 \n\t"
264 "paddsb %%mm1, %%mm0 \n\t"
265 "pxor %%mm7, %%mm0 \n\t"
266 "movntq %%mm0, (%2, %%"REG_a
") \n\t"
267 "add $8, %%"REG_a
" \n\t"
269 :: "r" (src
+mmx_len
), "r" (noise
+mmx_len
), "r" (dst
+mmx_len
), "g" (-mmx_len
)
273 line_noise_c(dst
+mmx_len
, src
+mmx_len
, noise
+mmx_len
, len
-mmx_len
, 0);
277 static inline void line_noise_avg_c(uint8_t *dst
, const uint8_t *src
,
278 int len
, const int8_t * const *shift
)
281 const int8_t *src2
= (const int8_t*)src
;
283 for (i
= 0; i
< len
; i
++) {
284 const int n
= shift
[0][i
] + shift
[1][i
] + shift
[2][i
];
285 dst
[i
] = src2
[i
] + ((n
* src2
[i
]) >> 7);
289 static inline void line_noise_avg_mmx(uint8_t *dst
, const uint8_t *src
,
290 int len
, const int8_t * const *shift
)
292 #if HAVE_MMX_INLINE && HAVE_6REGS
293 x86_reg mmx_len
= len
&(~7);
296 "mov %5, %%"REG_a
" \n\t"
299 "movq (%1, %%"REG_a
"), %%mm1 \n\t"
300 "movq (%0, %%"REG_a
"), %%mm0 \n\t"
301 "paddb (%2, %%"REG_a
"), %%mm1 \n\t"
302 "paddb (%3, %%"REG_a
"), %%mm1 \n\t"
303 "movq %%mm0, %%mm2 \n\t"
304 "movq %%mm1, %%mm3 \n\t"
305 "punpcklbw %%mm0, %%mm0 \n\t"
306 "punpckhbw %%mm2, %%mm2 \n\t"
307 "punpcklbw %%mm1, %%mm1 \n\t"
308 "punpckhbw %%mm3, %%mm3 \n\t"
309 "pmulhw %%mm0, %%mm1 \n\t"
310 "pmulhw %%mm2, %%mm3 \n\t"
311 "paddw %%mm1, %%mm1 \n\t"
312 "paddw %%mm3, %%mm3 \n\t"
313 "paddw %%mm0, %%mm1 \n\t"
314 "paddw %%mm2, %%mm3 \n\t"
315 "psrlw $8, %%mm1 \n\t"
316 "psrlw $8, %%mm3 \n\t"
317 "packuswb %%mm3, %%mm1 \n\t"
318 "movq %%mm1, (%4, %%"REG_a
") \n\t"
319 "add $8, %%"REG_a
" \n\t"
321 :: "r" (src
+mmx_len
), "r" (shift
[0]+mmx_len
), "r" (shift
[1]+mmx_len
), "r" (shift
[2]+mmx_len
),
322 "r" (dst
+mmx_len
), "g" (-mmx_len
)
327 const int8_t *shift2
[3]={shift
[0]+mmx_len
, shift
[1]+mmx_len
, shift
[2]+mmx_len
};
328 line_noise_avg_c(dst
+mmx_len
, src
+mmx_len
, len
-mmx_len
, shift2
);
333 static void noise(uint8_t *dst
, const uint8_t *src
,
334 int dst_linesize
, int src_linesize
,
335 int width
, int start
, int end
, NoiseContext
*n
, int comp
)
337 FilterParams
*p
= &n
->param
[comp
];
338 int8_t *noise
= p
->noise
;
339 const int flags
= p
->flags
;
340 AVLFG
*lfg
= &p
->lfg
;
345 av_image_copy_plane(dst
, dst_linesize
, src
, src_linesize
, width
, end
- start
);
349 for (y
= start
; y
< end
; y
++) {
350 const int ix
= y
& (MAX_RES
- 1);
351 if (flags
& NOISE_TEMPORAL
)
352 shift
= av_lfg_get(lfg
) & (MAX_SHIFT
- 1);
354 shift
= n
->rand_shift
[ix
];
356 if (flags
& NOISE_AVERAGED
) {
357 n
->line_noise_avg(dst
, src
, width
, (const int8_t**)p
->prev_shift
[ix
]);
358 p
->prev_shift
[ix
][shift
& 3] = noise
+ shift
;
360 n
->line_noise(dst
, src
, noise
, width
, shift
);
367 static int filter_slice(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
369 NoiseContext
*s
= ctx
->priv
;
370 ThreadData
*td
= arg
;
373 for (plane
= 0; plane
< s
->nb_planes
; plane
++) {
374 const int height
= s
->height
[plane
];
375 const int start
= (height
* jobnr
) / nb_jobs
;
376 const int end
= (height
* (jobnr
+1)) / nb_jobs
;
377 noise(td
->out
->data
[plane
] + start
* td
->out
->linesize
[plane
],
378 td
->in
->data
[plane
] + start
* td
->in
->linesize
[plane
],
379 td
->out
->linesize
[plane
], td
->in
->linesize
[plane
],
380 s
->bytewidth
[plane
], start
, end
, s
, plane
);
385 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*inpicref
)
387 AVFilterContext
*ctx
= inlink
->dst
;
388 AVFilterLink
*outlink
= ctx
->outputs
[0];
389 NoiseContext
*n
= ctx
->priv
;
393 if (av_frame_is_writable(inpicref
)) {
396 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
398 av_frame_free(&inpicref
);
399 return AVERROR(ENOMEM
);
401 av_frame_copy_props(out
, inpicref
);
404 td
.in
= inpicref
; td
.out
= out
;
405 ctx
->internal
->execute(ctx
, filter_slice
, &td
, NULL
, FFMIN(n
->height
[0], ctx
->graph
->nb_threads
));
409 av_frame_free(&inpicref
);
410 return ff_filter_frame(outlink
, out
);
413 static av_cold
int init(AVFilterContext
*ctx
)
415 NoiseContext
*n
= ctx
->priv
;
417 int cpu_flags
= av_get_cpu_flags();
419 for (i
= 0; i
< 4; i
++) {
420 if (n
->all
.seed
>= 0)
421 n
->param
[i
].seed
= n
->all
.seed
;
423 n
->param
[i
].seed
= 123457;
425 n
->param
[i
].strength
= n
->all
.strength
;
427 n
->param
[i
].flags
= n
->all
.flags
;
430 for (i
= 0; i
< 4; i
++) {
431 if (n
->param
[i
].strength
&& ((ret
= init_noise(n
, i
)) < 0))
435 n
->line_noise
= line_noise_c
;
436 n
->line_noise_avg
= line_noise_avg_c
;
438 if (HAVE_MMX_INLINE
&&
439 cpu_flags
& AV_CPU_FLAG_MMX
) {
440 n
->line_noise
= line_noise_mmx
;
442 n
->line_noise_avg
= line_noise_avg_mmx
;
445 if (HAVE_MMXEXT_INLINE
&&
446 cpu_flags
& AV_CPU_FLAG_MMXEXT
)
447 n
->line_noise
= line_noise_mmxext
;
452 static av_cold
void uninit(AVFilterContext
*ctx
)
454 NoiseContext
*n
= ctx
->priv
;
457 for (i
= 0; i
< 4; i
++)
458 av_freep(&n
->param
[i
].noise
);
461 static const AVFilterPad noise_inputs
[] = {
464 .type
= AVMEDIA_TYPE_VIDEO
,
465 .filter_frame
= filter_frame
,
466 .config_props
= config_input
,
471 static const AVFilterPad noise_outputs
[] = {
474 .type
= AVMEDIA_TYPE_VIDEO
,
479 AVFilter ff_vf_noise
= {
481 .description
= NULL_IF_CONFIG_SMALL("Add noise."),
482 .priv_size
= sizeof(NoiseContext
),
485 .query_formats
= query_formats
,
486 .inputs
= noise_inputs
,
487 .outputs
= noise_outputs
,
488 .priv_class
= &noise_class
,
489 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,