Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at> |
3 |
|
|
* Copyright (c) 2013 Paul B Mahol |
4 |
|
|
* |
5 |
|
|
* This file is part of FFmpeg. |
6 |
|
|
* |
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. |
11 |
|
|
* |
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. |
16 |
|
|
* |
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 |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
/** |
23 |
|
|
* @file |
24 |
|
|
* noise generator |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#include "libavutil/emms.h" |
28 |
|
|
#include "libavutil/mem.h" |
29 |
|
|
#include "libavutil/opt.h" |
30 |
|
|
#include "libavutil/imgutils.h" |
31 |
|
|
#include "libavutil/lfg.h" |
32 |
|
|
#include "libavutil/pixdesc.h" |
33 |
|
|
#include "avfilter.h" |
34 |
|
|
#include "filters.h" |
35 |
|
|
#include "formats.h" |
36 |
|
|
#include "vf_noise.h" |
37 |
|
|
#include "video.h" |
38 |
|
|
|
39 |
|
|
typedef struct ThreadData { |
40 |
|
|
AVFrame *in, *out; |
41 |
|
|
} ThreadData; |
42 |
|
|
|
43 |
|
|
#define OFFSET(x) offsetof(NoiseContext, x) |
44 |
|
|
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
45 |
|
|
|
46 |
|
|
#define NOISE_PARAMS(name, x, param) \ |
47 |
|
|
{#name"_seed", "set component #"#x" noise seed", OFFSET(param.seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS}, \ |
48 |
|
|
{#name"_strength", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \ |
49 |
|
|
{#name"s", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \ |
50 |
|
|
{#name"_flags", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, .unit = #name"_flags"}, \ |
51 |
|
|
{#name"f", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, .unit = #name"_flags"}, \ |
52 |
|
|
{"a", "averaged noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_AVERAGED}, 0, 0, FLAGS, .unit = #name"_flags"}, \ |
53 |
|
|
{"p", "(semi)regular pattern", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_PATTERN}, 0, 0, FLAGS, .unit = #name"_flags"}, \ |
54 |
|
|
{"t", "temporal noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_TEMPORAL}, 0, 0, FLAGS, .unit = #name"_flags"}, \ |
55 |
|
|
{"u", "uniform noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_UNIFORM}, 0, 0, FLAGS, .unit = #name"_flags"}, |
56 |
|
|
|
57 |
|
|
static const AVOption noise_options[] = { |
58 |
|
|
NOISE_PARAMS(all, 0, all) |
59 |
|
|
NOISE_PARAMS(c0, 0, param[0]) |
60 |
|
|
NOISE_PARAMS(c1, 1, param[1]) |
61 |
|
|
NOISE_PARAMS(c2, 2, param[2]) |
62 |
|
|
NOISE_PARAMS(c3, 3, param[3]) |
63 |
|
|
{NULL} |
64 |
|
|
}; |
65 |
|
|
|
66 |
|
|
AVFILTER_DEFINE_CLASS(noise); |
67 |
|
|
|
68 |
|
|
static const int8_t patt[4] = { -1, 0, 1, 0 }; |
69 |
|
|
|
70 |
|
|
#define RAND_N(range) ((int) ((double) range * av_lfg_get(lfg) / (UINT_MAX + 1.0))) |
71 |
|
✗ |
static av_cold int init_noise(NoiseContext *n, int comp) |
72 |
|
|
{ |
73 |
|
✗ |
int8_t *noise = av_malloc(MAX_NOISE * sizeof(int8_t)); |
74 |
|
✗ |
FilterParams *fp = &n->param[comp]; |
75 |
|
✗ |
AVLFG *lfg = &n->param[comp].lfg; |
76 |
|
✗ |
int strength = fp->strength; |
77 |
|
✗ |
int flags = fp->flags; |
78 |
|
|
int i, j; |
79 |
|
|
|
80 |
|
✗ |
if (!noise) |
81 |
|
✗ |
return AVERROR(ENOMEM); |
82 |
|
|
|
83 |
|
✗ |
av_lfg_init(&fp->lfg, fp->seed + comp*31415U); |
84 |
|
|
|
85 |
|
✗ |
for (i = 0, j = 0; i < MAX_NOISE; i++, j++) { |
86 |
|
✗ |
if (flags & NOISE_UNIFORM) { |
87 |
|
✗ |
if (flags & NOISE_AVERAGED) { |
88 |
|
✗ |
if (flags & NOISE_PATTERN) { |
89 |
|
✗ |
noise[i] = (RAND_N(strength) - strength / 2) / 6 |
90 |
|
✗ |
+ patt[j % 4] * strength * 0.25 / 3; |
91 |
|
|
} else { |
92 |
|
✗ |
noise[i] = (RAND_N(strength) - strength / 2) / 3; |
93 |
|
|
} |
94 |
|
|
} else { |
95 |
|
✗ |
if (flags & NOISE_PATTERN) { |
96 |
|
✗ |
noise[i] = (RAND_N(strength) - strength / 2) / 2 |
97 |
|
✗ |
+ patt[j % 4] * strength * 0.25; |
98 |
|
|
} else { |
99 |
|
✗ |
noise[i] = RAND_N(strength) - strength / 2; |
100 |
|
|
} |
101 |
|
|
} |
102 |
|
|
} else { |
103 |
|
|
double x1, x2, w, y1; |
104 |
|
|
do { |
105 |
|
✗ |
x1 = 2.0 * av_lfg_get(lfg) / (float)UINT_MAX - 1.0; |
106 |
|
✗ |
x2 = 2.0 * av_lfg_get(lfg) / (float)UINT_MAX - 1.0; |
107 |
|
✗ |
w = x1 * x1 + x2 * x2; |
108 |
|
✗ |
} while (w >= 1.0); |
109 |
|
|
|
110 |
|
✗ |
w = sqrt((-2.0 * log(w)) / w); |
111 |
|
✗ |
y1 = x1 * w; |
112 |
|
✗ |
y1 *= strength / sqrt(3.0); |
113 |
|
✗ |
if (flags & NOISE_PATTERN) { |
114 |
|
✗ |
y1 /= 2; |
115 |
|
✗ |
y1 += patt[j % 4] * strength * 0.35; |
116 |
|
|
} |
117 |
|
✗ |
y1 = av_clipf(y1, -128, 127); |
118 |
|
✗ |
if (flags & NOISE_AVERAGED) |
119 |
|
✗ |
y1 /= 3.0; |
120 |
|
✗ |
noise[i] = (int)y1; |
121 |
|
|
} |
122 |
|
✗ |
if (RAND_N(6) == 0) |
123 |
|
✗ |
j--; |
124 |
|
|
} |
125 |
|
|
|
126 |
|
✗ |
for (i = 0; i < MAX_RES; i++) |
127 |
|
✗ |
for (j = 0; j < 3; j++) |
128 |
|
✗ |
fp->prev_shift[i][j] = noise + (av_lfg_get(lfg) & (MAX_SHIFT - 1)); |
129 |
|
|
|
130 |
|
✗ |
fp->noise = noise; |
131 |
|
✗ |
return 0; |
132 |
|
|
} |
133 |
|
|
|
134 |
|
✗ |
static int query_formats(const AVFilterContext *ctx, |
135 |
|
|
AVFilterFormatsConfig **cfg_in, |
136 |
|
|
AVFilterFormatsConfig **cfg_out) |
137 |
|
|
{ |
138 |
|
✗ |
AVFilterFormats *formats = NULL; |
139 |
|
|
int fmt, ret; |
140 |
|
|
|
141 |
|
✗ |
for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { |
142 |
|
✗ |
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); |
143 |
|
✗ |
if (desc->flags & AV_PIX_FMT_FLAG_PLANAR && !(desc->comp[0].depth & 7) |
144 |
|
✗ |
&& (ret = ff_add_format(&formats, fmt)) < 0) |
145 |
|
✗ |
return ret; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
✗ |
return ff_set_common_formats2(ctx, cfg_in, cfg_out, formats); |
149 |
|
|
} |
150 |
|
|
|
151 |
|
✗ |
static int config_input(AVFilterLink *inlink) |
152 |
|
|
{ |
153 |
|
✗ |
NoiseContext *n = inlink->dst->priv; |
154 |
|
✗ |
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
155 |
|
|
int ret; |
156 |
|
|
|
157 |
|
✗ |
n->nb_planes = av_pix_fmt_count_planes(inlink->format); |
158 |
|
|
|
159 |
|
✗ |
if ((ret = av_image_fill_linesizes(n->bytewidth, inlink->format, inlink->w)) < 0) |
160 |
|
✗ |
return ret; |
161 |
|
|
|
162 |
|
✗ |
n->height[1] = n->height[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); |
163 |
|
✗ |
n->height[0] = n->height[3] = inlink->h; |
164 |
|
|
|
165 |
|
✗ |
return 0; |
166 |
|
|
} |
167 |
|
|
|
168 |
|
✗ |
void ff_line_noise_c(uint8_t *dst, const uint8_t *src, const int8_t *noise, |
169 |
|
|
int len, int shift) |
170 |
|
|
{ |
171 |
|
|
int i; |
172 |
|
|
|
173 |
|
✗ |
noise += shift; |
174 |
|
✗ |
for (i = 0; i < len; i++) { |
175 |
|
✗ |
int v = src[i] + noise[i]; |
176 |
|
|
|
177 |
|
✗ |
dst[i] = av_clip_uint8(v); |
178 |
|
|
} |
179 |
|
✗ |
} |
180 |
|
|
|
181 |
|
✗ |
void ff_line_noise_avg_c(uint8_t *dst, const uint8_t *src, |
182 |
|
|
int len, const int8_t * const *shift) |
183 |
|
|
{ |
184 |
|
|
int i; |
185 |
|
✗ |
const int8_t *src2 = (const int8_t*)src; |
186 |
|
|
|
187 |
|
✗ |
for (i = 0; i < len; i++) { |
188 |
|
✗ |
const int n = shift[0][i] + shift[1][i] + shift[2][i]; |
189 |
|
✗ |
dst[i] = src2[i] + ((n * src2[i]) >> 7); |
190 |
|
|
} |
191 |
|
✗ |
} |
192 |
|
|
|
193 |
|
✗ |
static void noise(uint8_t *dst, const uint8_t *src, |
194 |
|
|
int dst_linesize, int src_linesize, |
195 |
|
|
int width, int start, int end, NoiseContext *n, int comp) |
196 |
|
|
{ |
197 |
|
✗ |
FilterParams *p = &n->param[comp]; |
198 |
|
✗ |
int8_t *noise = p->noise; |
199 |
|
✗ |
const int flags = p->flags; |
200 |
|
|
int y; |
201 |
|
|
|
202 |
|
✗ |
if (!noise) { |
203 |
|
✗ |
if (dst != src) |
204 |
|
✗ |
av_image_copy_plane(dst, dst_linesize, src, src_linesize, width, end - start); |
205 |
|
✗ |
return; |
206 |
|
|
} |
207 |
|
|
|
208 |
|
✗ |
for (y = start; y < end; y++) { |
209 |
|
✗ |
const int ix = y & (MAX_RES - 1); |
210 |
|
|
int x; |
211 |
|
✗ |
for (x=0; x < width; x+= MAX_RES) { |
212 |
|
✗ |
int w = FFMIN(width - x, MAX_RES); |
213 |
|
✗ |
int shift = p->rand_shift[ix]; |
214 |
|
|
|
215 |
|
✗ |
if (flags & NOISE_AVERAGED) { |
216 |
|
✗ |
n->line_noise_avg(dst + x, src + x, w, (const int8_t**)p->prev_shift[ix]); |
217 |
|
✗ |
p->prev_shift[ix][shift & 3] = noise + shift; |
218 |
|
|
} else { |
219 |
|
✗ |
n->line_noise(dst + x, src + x, noise, w, shift); |
220 |
|
|
} |
221 |
|
|
} |
222 |
|
✗ |
dst += dst_linesize; |
223 |
|
✗ |
src += src_linesize; |
224 |
|
|
} |
225 |
|
|
} |
226 |
|
|
|
227 |
|
✗ |
static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
228 |
|
|
{ |
229 |
|
✗ |
NoiseContext *s = ctx->priv; |
230 |
|
✗ |
ThreadData *td = arg; |
231 |
|
|
int plane; |
232 |
|
|
|
233 |
|
✗ |
for (plane = 0; plane < s->nb_planes; plane++) { |
234 |
|
✗ |
const int height = s->height[plane]; |
235 |
|
✗ |
const int start = (height * jobnr ) / nb_jobs; |
236 |
|
✗ |
const int end = (height * (jobnr+1)) / nb_jobs; |
237 |
|
✗ |
noise(td->out->data[plane] + start * td->out->linesize[plane], |
238 |
|
✗ |
td->in->data[plane] + start * td->in->linesize[plane], |
239 |
|
✗ |
td->out->linesize[plane], td->in->linesize[plane], |
240 |
|
|
s->bytewidth[plane], start, end, s, plane); |
241 |
|
|
} |
242 |
|
✗ |
return 0; |
243 |
|
|
} |
244 |
|
|
|
245 |
|
✗ |
static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) |
246 |
|
|
{ |
247 |
|
✗ |
AVFilterContext *ctx = inlink->dst; |
248 |
|
✗ |
AVFilterLink *outlink = ctx->outputs[0]; |
249 |
|
✗ |
NoiseContext *n = ctx->priv; |
250 |
|
|
ThreadData td; |
251 |
|
|
AVFrame *out; |
252 |
|
|
int comp, i; |
253 |
|
|
|
254 |
|
✗ |
if (av_frame_is_writable(inpicref)) { |
255 |
|
✗ |
out = inpicref; |
256 |
|
|
} else { |
257 |
|
✗ |
out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
258 |
|
✗ |
if (!out) { |
259 |
|
✗ |
av_frame_free(&inpicref); |
260 |
|
✗ |
return AVERROR(ENOMEM); |
261 |
|
|
} |
262 |
|
✗ |
av_frame_copy_props(out, inpicref); |
263 |
|
|
} |
264 |
|
|
|
265 |
|
✗ |
for (comp = 0; comp < 4; comp++) { |
266 |
|
✗ |
FilterParams *fp = &n->param[comp]; |
267 |
|
|
|
268 |
|
✗ |
if ((!fp->rand_shift_init || (fp->flags & NOISE_TEMPORAL)) && fp->strength) { |
269 |
|
|
|
270 |
|
✗ |
for (i = 0; i < MAX_RES; i++) { |
271 |
|
✗ |
fp->rand_shift[i] = av_lfg_get(&fp->lfg) & (MAX_SHIFT - 1); |
272 |
|
|
} |
273 |
|
✗ |
fp->rand_shift_init = 1; |
274 |
|
|
} |
275 |
|
|
} |
276 |
|
|
|
277 |
|
✗ |
td.in = inpicref; td.out = out; |
278 |
|
✗ |
ff_filter_execute(ctx, filter_slice, &td, NULL, |
279 |
|
✗ |
FFMIN(n->height[0], ff_filter_get_nb_threads(ctx))); |
280 |
|
✗ |
emms_c(); |
281 |
|
|
|
282 |
|
✗ |
if (inpicref != out) |
283 |
|
✗ |
av_frame_free(&inpicref); |
284 |
|
✗ |
return ff_filter_frame(outlink, out); |
285 |
|
|
} |
286 |
|
|
|
287 |
|
✗ |
static av_cold int init(AVFilterContext *ctx) |
288 |
|
|
{ |
289 |
|
✗ |
NoiseContext *n = ctx->priv; |
290 |
|
|
int ret, i; |
291 |
|
|
|
292 |
|
✗ |
for (i = 0; i < 4; i++) { |
293 |
|
✗ |
if (n->all.seed >= 0) |
294 |
|
✗ |
n->param[i].seed = n->all.seed; |
295 |
|
|
else |
296 |
|
✗ |
n->param[i].seed = 123457; |
297 |
|
✗ |
if (n->all.strength) |
298 |
|
✗ |
n->param[i].strength = n->all.strength; |
299 |
|
✗ |
if (n->all.flags) |
300 |
|
✗ |
n->param[i].flags = n->all.flags; |
301 |
|
|
} |
302 |
|
|
|
303 |
|
✗ |
for (i = 0; i < 4; i++) { |
304 |
|
✗ |
if (n->param[i].strength && ((ret = init_noise(n, i)) < 0)) |
305 |
|
✗ |
return ret; |
306 |
|
|
} |
307 |
|
|
|
308 |
|
✗ |
n->line_noise = ff_line_noise_c; |
309 |
|
✗ |
n->line_noise_avg = ff_line_noise_avg_c; |
310 |
|
|
|
311 |
|
|
#if ARCH_X86 |
312 |
|
✗ |
ff_noise_init_x86(n); |
313 |
|
|
#endif |
314 |
|
|
|
315 |
|
✗ |
return 0; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
✗ |
static av_cold void uninit(AVFilterContext *ctx) |
319 |
|
|
{ |
320 |
|
✗ |
NoiseContext *n = ctx->priv; |
321 |
|
|
int i; |
322 |
|
|
|
323 |
|
✗ |
for (i = 0; i < 4; i++) |
324 |
|
✗ |
av_freep(&n->param[i].noise); |
325 |
|
✗ |
} |
326 |
|
|
|
327 |
|
|
static const AVFilterPad noise_inputs[] = { |
328 |
|
|
{ |
329 |
|
|
.name = "default", |
330 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
331 |
|
|
.filter_frame = filter_frame, |
332 |
|
|
.config_props = config_input, |
333 |
|
|
}, |
334 |
|
|
}; |
335 |
|
|
|
336 |
|
|
const AVFilter ff_vf_noise = { |
337 |
|
|
.name = "noise", |
338 |
|
|
.description = NULL_IF_CONFIG_SMALL("Add noise."), |
339 |
|
|
.priv_size = sizeof(NoiseContext), |
340 |
|
|
.init = init, |
341 |
|
|
.uninit = uninit, |
342 |
|
|
FILTER_INPUTS(noise_inputs), |
343 |
|
|
FILTER_OUTPUTS(ff_video_default_filterpad), |
344 |
|
|
FILTER_QUERY_FUNC2(query_formats), |
345 |
|
|
.priv_class = &noise_class, |
346 |
|
|
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, |
347 |
|
|
}; |
348 |
|
|
|