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/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" 33#include "avfilter.h" 34#include "formats.h" 35#include "internal.h" 36#include "video.h" 37 38#define MAX_NOISE 5120 39#define MAX_SHIFT 1024 40#define MAX_RES (MAX_NOISE-MAX_SHIFT) 41 42#define NOISE_UNIFORM 1 43#define NOISE_TEMPORAL 2 44#define NOISE_AVERAGED 8 45#define NOISE_PATTERN 16 46 47typedef struct { 48 int strength; 49 unsigned flags; 50 AVLFG lfg; 51 int seed; 52 int8_t *noise; 53 int8_t *prev_shift[MAX_RES][3]; 54} FilterParams; 55 56typedef struct { 57 const AVClass *class; 58 int nb_planes; 59 int bytewidth[4]; 60 int height[4]; 61 FilterParams all; 62 FilterParams param[4]; 63 int rand_shift[MAX_RES]; 64 int rand_shift_init; 65 void (*line_noise)(uint8_t *dst, const uint8_t *src, int8_t *noise, int len, int shift); 66 void (*line_noise_avg)(uint8_t *dst, const uint8_t *src, int len, int8_t **shift); 67} NoiseContext; 68 69typedef struct ThreadData { 70 AVFrame *in, *out; 71} ThreadData; 72 73#define OFFSET(x) offsetof(NoiseContext, x) 74#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 75 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"}, 86 87static 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]) 93 {NULL} 94}; 95 96AVFILTER_DEFINE_CLASS(noise); 97 98static const int8_t patt[4] = { -1, 0, 1, 0 }; 99 100#define RAND_N(range) ((int) ((double) range * av_lfg_get(lfg) / (UINT_MAX + 1.0))) 101static av_cold int init_noise(NoiseContext *n, int comp) 102{ 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; 108 int i, j; 109 110 if (!noise) 111 return AVERROR(ENOMEM); 112 113 av_lfg_init(&fp->lfg, fp->seed); 114 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; 121 } else { 122 noise[i] = (RAND_N(strength) - strength / 2) / 3; 123 } 124 } else { 125 if (flags & NOISE_PATTERN) { 126 noise[i] = (RAND_N(strength) - strength / 2) / 2 127 + patt[j % 4] * strength * 0.25; 128 } else { 129 noise[i] = RAND_N(strength) - strength / 2; 130 } 131 } 132 } else { 133 double x1, x2, w, y1; 134 do { 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; 138 } while (w >= 1.0); 139 140 w = sqrt((-2.0 * log(w)) / w); 141 y1 = x1 * w; 142 y1 *= strength / sqrt(3.0); 143 if (flags & NOISE_PATTERN) { 144 y1 /= 2; 145 y1 += patt[j % 4] * strength * 0.35; 146 } 147 y1 = av_clipf(y1, -128, 127); 148 if (flags & NOISE_AVERAGED) 149 y1 /= 3.0; 150 noise[i] = (int)y1; 151 } 152 if (RAND_N(6) == 0) 153 j--; 154 } 155 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)); 159 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; 164 } 165 166 fp->noise = noise; 167 return 0; 168} 169 170static int query_formats(AVFilterContext *ctx) 171{ 172 AVFilterFormats *formats = NULL; 173 int fmt; 174 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); 179 } 180 181 ff_set_common_formats(ctx, formats); 182 return 0; 183} 184 185static int config_input(AVFilterLink *inlink) 186{ 187 NoiseContext *n = inlink->dst->priv; 188 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); 189 int ret; 190 191 n->nb_planes = av_pix_fmt_count_planes(inlink->format); 192 193 if ((ret = av_image_fill_linesizes(n->bytewidth, inlink->format, inlink->w)) < 0) 194 return ret; 195 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; 198 199 return 0; 200} 201 202static inline void line_noise_c(uint8_t *dst, const uint8_t *src, int8_t *noise, 203 int len, int shift) 204{ 205 int i; 206 207 noise += shift; 208 for (i = 0; i < len; i++) { 209 int v = src[i] + noise[i]; 210 211 dst[i] = av_clip_uint8(v); 212 } 213} 214 215#define ASMALIGN(ZEROBITS) ".p2align " #ZEROBITS "\n\t" 216 217static void line_noise_mmx(uint8_t *dst, const uint8_t *src, 218 int8_t *noise, int len, int shift) 219{ 220#if HAVE_MMX_INLINE 221 x86_reg mmx_len= len&(~7); 222 noise+=shift; 223 224 __asm__ volatile( 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" 229 ASMALIGN(4) 230 "1: \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" 238 " js 1b \n\t" 239 :: "r" (src+mmx_len), "r" (noise+mmx_len), "r" (dst+mmx_len), "g" (-mmx_len) 240 : "%"REG_a 241 ); 242 if (mmx_len!=len) 243 line_noise_c(dst+mmx_len, src+mmx_len, noise+mmx_len, len-mmx_len, 0); 244#endif 245} 246 247static void line_noise_mmxext(uint8_t *dst, const uint8_t *src, 248 int8_t *noise, int len, int shift) 249{ 250#if HAVE_MMXEXT_INLINE 251 x86_reg mmx_len= len&(~7); 252 noise+=shift; 253 254 __asm__ volatile( 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" 259 ASMALIGN(4) 260 "1: \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" 268 " js 1b \n\t" 269 :: "r" (src+mmx_len), "r" (noise+mmx_len), "r" (dst+mmx_len), "g" (-mmx_len) 270 : "%"REG_a 271 ); 272 if (mmx_len != len) 273 line_noise_c(dst+mmx_len, src+mmx_len, noise+mmx_len, len-mmx_len, 0); 274#endif 275} 276 277static inline void line_noise_avg_c(uint8_t *dst, const uint8_t *src, 278 int len, int8_t **shift) 279{ 280 int i; 281 int8_t *src2 = (int8_t*)src; 282 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); 286 } 287} 288 289static inline void line_noise_avg_mmx(uint8_t *dst, const uint8_t *src, 290 int len, int8_t **shift) 291{ 292#if HAVE_MMX_INLINE && HAVE_6REGS 293 x86_reg mmx_len= len&(~7); 294 295 __asm__ volatile( 296 "mov %5, %%"REG_a" \n\t" 297 ASMALIGN(4) 298 "1: \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" 320 " js 1b \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) 323 : "%"REG_a 324 ); 325 326 if (mmx_len != len){ 327 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); 329 } 330#endif 331} 332 333static 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) 336{ 337 FilterParams *p = &n->param[comp]; 338 int8_t *noise = p->noise; 339 const int flags = p->flags; 340 AVLFG *lfg = &p->lfg; 341 int shift, y; 342 343 if (!noise) { 344 if (dst != src) 345 av_image_copy_plane(dst, dst_linesize, src, src_linesize, width, end - start); 346 return; 347 } 348 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); 353 else 354 shift = n->rand_shift[ix]; 355 356 if (flags & NOISE_AVERAGED) { 357 n->line_noise_avg(dst, src, width, p->prev_shift[ix]); 358 p->prev_shift[ix][shift & 3] = noise + shift; 359 } else { 360 n->line_noise(dst, src, noise, width, shift); 361 } 362 dst += dst_linesize; 363 src += src_linesize; 364 } 365} 366 367static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) 368{ 369 NoiseContext *s = ctx->priv; 370 ThreadData *td = arg; 371 int plane; 372 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); 381 } 382 return 0; 383} 384 385static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) 386{ 387 AVFilterContext *ctx = inlink->dst; 388 AVFilterLink *outlink = ctx->outputs[0]; 389 NoiseContext *n = ctx->priv; 390 ThreadData td; 391 AVFrame *out; 392 393 if (av_frame_is_writable(inpicref)) { 394 out = inpicref; 395 } else { 396 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 397 if (!out) { 398 av_frame_free(&inpicref); 399 return AVERROR(ENOMEM); 400 } 401 av_frame_copy_props(out, inpicref); 402 } 403 404 td.in = inpicref; td.out = out; 405 ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(n->height[0], ctx->graph->nb_threads)); 406 emms_c(); 407 408 if (inpicref != out) 409 av_frame_free(&inpicref); 410 return ff_filter_frame(outlink, out); 411} 412 413static av_cold int init(AVFilterContext *ctx) 414{ 415 NoiseContext *n = ctx->priv; 416 int ret, i; 417 int cpu_flags = av_get_cpu_flags(); 418 419 for (i = 0; i < 4; i++) { 420 if (n->all.seed >= 0) 421 n->param[i].seed = n->all.seed; 422 else 423 n->param[i].seed = 123457; 424 if (n->all.strength) 425 n->param[i].strength = n->all.strength; 426 if (n->all.flags) 427 n->param[i].flags = n->all.flags; 428 } 429 430 for (i = 0; i < 4; i++) { 431 if (n->param[i].strength && ((ret = init_noise(n, i)) < 0)) 432 return ret; 433 } 434 435 n->line_noise = line_noise_c; 436 n->line_noise_avg = line_noise_avg_c; 437 438 if (HAVE_MMX_INLINE && 439 cpu_flags & AV_CPU_FLAG_MMX) { 440 n->line_noise = line_noise_mmx; 441#if HAVE_6REGS 442 n->line_noise_avg = line_noise_avg_mmx; 443#endif 444 } 445 if (HAVE_MMXEXT_INLINE && 446 cpu_flags & AV_CPU_FLAG_MMXEXT) 447 n->line_noise = line_noise_mmxext; 448 449 return 0; 450} 451 452static av_cold void uninit(AVFilterContext *ctx) 453{ 454 NoiseContext *n = ctx->priv; 455 int i; 456 457 for (i = 0; i < 4; i++) 458 av_freep(&n->param[i].noise); 459} 460 461static const AVFilterPad noise_inputs[] = { 462 { 463 .name = "default", 464 .type = AVMEDIA_TYPE_VIDEO, 465 .filter_frame = filter_frame, 466 .config_props = config_input, 467 }, 468 { NULL } 469}; 470 471static const AVFilterPad noise_outputs[] = { 472 { 473 .name = "default", 474 .type = AVMEDIA_TYPE_VIDEO, 475 }, 476 { NULL } 477}; 478 479AVFilter ff_vf_noise = { 480 .name = "noise", 481 .description = NULL_IF_CONFIG_SMALL("Add noise."), 482 .priv_size = sizeof(NoiseContext), 483 .init = init, 484 .uninit = uninit, 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, 490}; 491