yading@10: /* yading@10: * Copyright (c) 2002 Michael Niedermayer yading@10: * Copyright (c) 2013 Paul B Mahol yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the yading@10: * GNU General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU General Public License along yading@10: * with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * noise generator yading@10: */ yading@10: yading@10: #include "libavutil/opt.h" yading@10: #include "libavutil/imgutils.h" yading@10: #include "libavutil/lfg.h" yading@10: #include "libavutil/parseutils.h" yading@10: #include "libavutil/pixdesc.h" yading@10: #include "libavutil/x86/asm.h" yading@10: #include "avfilter.h" yading@10: #include "formats.h" yading@10: #include "internal.h" yading@10: #include "video.h" yading@10: yading@10: #define MAX_NOISE 4096 yading@10: #define MAX_SHIFT 1024 yading@10: #define MAX_RES (MAX_NOISE-MAX_SHIFT) yading@10: yading@10: #define NOISE_UNIFORM 1 yading@10: #define NOISE_TEMPORAL 2 yading@10: #define NOISE_AVERAGED 8 yading@10: #define NOISE_PATTERN 16 yading@10: yading@10: typedef struct { yading@10: int strength; yading@10: unsigned flags; yading@10: int shiftptr; yading@10: AVLFG lfg; yading@10: int seed; yading@10: int8_t *noise; yading@10: int8_t *prev_shift[MAX_RES][3]; yading@10: } FilterParams; yading@10: yading@10: typedef struct { yading@10: const AVClass *class; yading@10: int nb_planes; yading@10: int linesize[4]; yading@10: int height[4]; yading@10: FilterParams all; yading@10: FilterParams param[4]; yading@10: int rand_shift[MAX_RES]; yading@10: int rand_shift_init; yading@10: void (*line_noise)(uint8_t *dst, const uint8_t *src, int8_t *noise, int len, int shift); yading@10: void (*line_noise_avg)(uint8_t *dst, const uint8_t *src, int len, int8_t **shift); yading@10: } NoiseContext; yading@10: yading@10: #define OFFSET(x) offsetof(NoiseContext, x) yading@10: #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM yading@10: yading@10: #define NOISE_PARAMS(name, x, param) \ yading@10: {#name"_seed", "set component #"#x" noise seed", OFFSET(param.seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS}, \ yading@10: {#name"_strength", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \ yading@10: {#name"s", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \ yading@10: {#name"_flags", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, #name"_flags"}, \ yading@10: {#name"f", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, #name"_flags"}, \ yading@10: {"a", "averaged noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_AVERAGED}, 0, 0, FLAGS, #name"_flags"}, \ yading@10: {"p", "(semi)regular pattern", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_PATTERN}, 0, 0, FLAGS, #name"_flags"}, \ yading@10: {"t", "temporal noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_TEMPORAL}, 0, 0, FLAGS, #name"_flags"}, \ yading@10: {"u", "uniform noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_UNIFORM}, 0, 0, FLAGS, #name"_flags"}, yading@10: yading@10: static const AVOption noise_options[] = { yading@10: NOISE_PARAMS(all, 0, all) yading@10: NOISE_PARAMS(c0, 0, param[0]) yading@10: NOISE_PARAMS(c1, 1, param[1]) yading@10: NOISE_PARAMS(c2, 2, param[2]) yading@10: NOISE_PARAMS(c3, 3, param[3]) yading@10: {NULL} yading@10: }; yading@10: yading@10: AVFILTER_DEFINE_CLASS(noise); yading@10: yading@10: static const int8_t patt[4] = { -1, 0, 1, 0 }; yading@10: yading@10: #define RAND_N(range) ((int) ((double) range * av_lfg_get(lfg) / (UINT_MAX + 1.0))) yading@10: static int init_noise(NoiseContext *n, int comp) yading@10: { yading@10: int8_t *noise = av_malloc(MAX_NOISE * sizeof(int8_t)); yading@10: FilterParams *fp = &n->param[comp]; yading@10: AVLFG *lfg = &n->param[comp].lfg; yading@10: int strength = fp->strength; yading@10: int flags = fp->flags; yading@10: int i, j; yading@10: yading@10: if (!noise) yading@10: return AVERROR(ENOMEM); yading@10: yading@10: av_lfg_init(&fp->lfg, fp->seed); yading@10: yading@10: for (i = 0, j = 0; i < MAX_NOISE; i++, j++) { yading@10: if (flags & NOISE_UNIFORM) { yading@10: if (flags & NOISE_AVERAGED) { yading@10: if (flags & NOISE_PATTERN) { yading@10: noise[i] = (RAND_N(strength) - strength / 2) / 6 yading@10: + patt[j % 4] * strength * 0.25 / 3; yading@10: } else { yading@10: noise[i] = (RAND_N(strength) - strength / 2) / 3; yading@10: } yading@10: } else { yading@10: if (flags & NOISE_PATTERN) { yading@10: noise[i] = (RAND_N(strength) - strength / 2) / 2 yading@10: + patt[j % 4] * strength * 0.25; yading@10: } else { yading@10: noise[i] = RAND_N(strength) - strength / 2; yading@10: } yading@10: } yading@10: } else { yading@10: double x1, x2, w, y1; yading@10: do { yading@10: x1 = 2.0 * av_lfg_get(lfg) / (float)UINT_MAX - 1.0; yading@10: x2 = 2.0 * av_lfg_get(lfg) / (float)UINT_MAX - 1.0; yading@10: w = x1 * x1 + x2 * x2; yading@10: } while (w >= 1.0); yading@10: yading@10: w = sqrt((-2.0 * log(w)) / w); yading@10: y1 = x1 * w; yading@10: y1 *= strength / sqrt(3.0); yading@10: if (flags & NOISE_PATTERN) { yading@10: y1 /= 2; yading@10: y1 += patt[j % 4] * strength * 0.35; yading@10: } yading@10: y1 = av_clipf(y1, -128, 127); yading@10: if (flags & NOISE_AVERAGED) yading@10: y1 /= 3.0; yading@10: noise[i] = (int)y1; yading@10: } yading@10: if (RAND_N(6) == 0) yading@10: j--; yading@10: } yading@10: yading@10: for (i = 0; i < MAX_RES; i++) yading@10: for (j = 0; j < 3; j++) yading@10: fp->prev_shift[i][j] = noise + (av_lfg_get(lfg) & (MAX_SHIFT - 1)); yading@10: yading@10: if (!n->rand_shift_init) { yading@10: for (i = 0; i < MAX_RES; i++) yading@10: n->rand_shift[i] = av_lfg_get(lfg) & (MAX_SHIFT - 1); yading@10: n->rand_shift_init = 1; yading@10: } yading@10: yading@10: fp->noise = noise; yading@10: fp->shiftptr = 0; yading@10: return 0; yading@10: } yading@10: yading@10: static int query_formats(AVFilterContext *ctx) yading@10: { yading@10: AVFilterFormats *formats = NULL; yading@10: int fmt; yading@10: yading@10: for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { yading@10: const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); yading@10: if (desc->flags & PIX_FMT_PLANAR && !((desc->comp[0].depth_minus1 + 1) & 7)) yading@10: ff_add_format(&formats, fmt); yading@10: } yading@10: yading@10: ff_set_common_formats(ctx, formats); yading@10: return 0; yading@10: } yading@10: yading@10: static int config_input(AVFilterLink *inlink) yading@10: { yading@10: NoiseContext *n = inlink->dst->priv; yading@10: const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); yading@10: int i, ret; yading@10: yading@10: for (i = 0; i < desc->nb_components; i++) yading@10: n->nb_planes = FFMAX(n->nb_planes, desc->comp[i].plane); yading@10: n->nb_planes++; yading@10: yading@10: if ((ret = av_image_fill_linesizes(n->linesize, inlink->format, inlink->w)) < 0) yading@10: return ret; yading@10: yading@10: n->height[1] = n->height[2] = inlink->h >> desc->log2_chroma_h; yading@10: n->height[0] = n->height[3] = inlink->h; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static inline void line_noise_c(uint8_t *dst, const uint8_t *src, int8_t *noise, yading@10: int len, int shift) yading@10: { yading@10: int i; yading@10: yading@10: noise += shift; yading@10: for (i = 0; i < len; i++) { yading@10: int v = src[i] + noise[i]; yading@10: yading@10: dst[i] = av_clip_uint8(v); yading@10: } yading@10: } yading@10: yading@10: #define ASMALIGN(ZEROBITS) ".p2align " #ZEROBITS "\n\t" yading@10: yading@10: static void line_noise_mmx(uint8_t *dst, const uint8_t *src, yading@10: int8_t *noise, int len, int shift) yading@10: { yading@10: #if HAVE_MMX_INLINE yading@10: x86_reg mmx_len= len&(~7); yading@10: noise+=shift; yading@10: yading@10: __asm__ volatile( yading@10: "mov %3, %%"REG_a" \n\t" yading@10: "pcmpeqb %%mm7, %%mm7 \n\t" yading@10: "psllw $15, %%mm7 \n\t" yading@10: "packsswb %%mm7, %%mm7 \n\t" yading@10: ASMALIGN(4) yading@10: "1: \n\t" yading@10: "movq (%0, %%"REG_a"), %%mm0 \n\t" yading@10: "movq (%1, %%"REG_a"), %%mm1 \n\t" yading@10: "pxor %%mm7, %%mm0 \n\t" yading@10: "paddsb %%mm1, %%mm0 \n\t" yading@10: "pxor %%mm7, %%mm0 \n\t" yading@10: "movq %%mm0, (%2, %%"REG_a") \n\t" yading@10: "add $8, %%"REG_a" \n\t" yading@10: " js 1b \n\t" yading@10: :: "r" (src+mmx_len), "r" (noise+mmx_len), "r" (dst+mmx_len), "g" (-mmx_len) yading@10: : "%"REG_a yading@10: ); yading@10: if (mmx_len!=len) yading@10: line_noise_c(dst+mmx_len, src+mmx_len, noise+mmx_len, len-mmx_len, 0); yading@10: #endif yading@10: } yading@10: yading@10: static void line_noise_mmxext(uint8_t *dst, const uint8_t *src, yading@10: int8_t *noise, int len, int shift) yading@10: { yading@10: #if HAVE_MMXEXT_INLINE yading@10: x86_reg mmx_len= len&(~7); yading@10: noise+=shift; yading@10: yading@10: __asm__ volatile( yading@10: "mov %3, %%"REG_a" \n\t" yading@10: "pcmpeqb %%mm7, %%mm7 \n\t" yading@10: "psllw $15, %%mm7 \n\t" yading@10: "packsswb %%mm7, %%mm7 \n\t" yading@10: ASMALIGN(4) yading@10: "1: \n\t" yading@10: "movq (%0, %%"REG_a"), %%mm0 \n\t" yading@10: "movq (%1, %%"REG_a"), %%mm1 \n\t" yading@10: "pxor %%mm7, %%mm0 \n\t" yading@10: "paddsb %%mm1, %%mm0 \n\t" yading@10: "pxor %%mm7, %%mm0 \n\t" yading@10: "movntq %%mm0, (%2, %%"REG_a") \n\t" yading@10: "add $8, %%"REG_a" \n\t" yading@10: " js 1b \n\t" yading@10: :: "r" (src+mmx_len), "r" (noise+mmx_len), "r" (dst+mmx_len), "g" (-mmx_len) yading@10: : "%"REG_a yading@10: ); yading@10: if (mmx_len != len) yading@10: line_noise_c(dst+mmx_len, src+mmx_len, noise+mmx_len, len-mmx_len, 0); yading@10: #endif yading@10: } yading@10: yading@10: static inline void line_noise_avg_c(uint8_t *dst, const uint8_t *src, yading@10: int len, int8_t **shift) yading@10: { yading@10: int i; yading@10: int8_t *src2 = (int8_t*)src; yading@10: yading@10: for (i = 0; i < len; i++) { yading@10: const int n = shift[0][i] + shift[1][i] + shift[2][i]; yading@10: dst[i] = src2[i] + ((n * src2[i]) >> 7); yading@10: } yading@10: } yading@10: yading@10: static inline void line_noise_avg_mmx(uint8_t *dst, const uint8_t *src, yading@10: int len, int8_t **shift) yading@10: { yading@10: #if HAVE_MMX_INLINE yading@10: x86_reg mmx_len= len&(~7); yading@10: yading@10: __asm__ volatile( yading@10: "mov %5, %%"REG_a" \n\t" yading@10: ASMALIGN(4) yading@10: "1: \n\t" yading@10: "movq (%1, %%"REG_a"), %%mm1 \n\t" yading@10: "movq (%0, %%"REG_a"), %%mm0 \n\t" yading@10: "paddb (%2, %%"REG_a"), %%mm1 \n\t" yading@10: "paddb (%3, %%"REG_a"), %%mm1 \n\t" yading@10: "movq %%mm0, %%mm2 \n\t" yading@10: "movq %%mm1, %%mm3 \n\t" yading@10: "punpcklbw %%mm0, %%mm0 \n\t" yading@10: "punpckhbw %%mm2, %%mm2 \n\t" yading@10: "punpcklbw %%mm1, %%mm1 \n\t" yading@10: "punpckhbw %%mm3, %%mm3 \n\t" yading@10: "pmulhw %%mm0, %%mm1 \n\t" yading@10: "pmulhw %%mm2, %%mm3 \n\t" yading@10: "paddw %%mm1, %%mm1 \n\t" yading@10: "paddw %%mm3, %%mm3 \n\t" yading@10: "paddw %%mm0, %%mm1 \n\t" yading@10: "paddw %%mm2, %%mm3 \n\t" yading@10: "psrlw $8, %%mm1 \n\t" yading@10: "psrlw $8, %%mm3 \n\t" yading@10: "packuswb %%mm3, %%mm1 \n\t" yading@10: "movq %%mm1, (%4, %%"REG_a") \n\t" yading@10: "add $8, %%"REG_a" \n\t" yading@10: " js 1b \n\t" yading@10: :: "r" (src+mmx_len), "r" (shift[0]+mmx_len), "r" (shift[1]+mmx_len), "r" (shift[2]+mmx_len), yading@10: "r" (dst+mmx_len), "g" (-mmx_len) yading@10: : "%"REG_a yading@10: ); yading@10: yading@10: if (mmx_len != len){ yading@10: int8_t *shift2[3]={shift[0]+mmx_len, shift[1]+mmx_len, shift[2]+mmx_len}; yading@10: line_noise_avg_c(dst+mmx_len, src+mmx_len, len-mmx_len, shift2); yading@10: } yading@10: #endif yading@10: } yading@10: yading@10: static void noise(uint8_t *dst, const uint8_t *src, yading@10: int dst_linesize, int src_linesize, yading@10: int width, int height, NoiseContext *n, int comp) yading@10: { yading@10: int8_t *noise = n->param[comp].noise; yading@10: int flags = n->param[comp].flags; yading@10: AVLFG *lfg = &n->param[comp].lfg; yading@10: int shift, y; yading@10: yading@10: if (!noise) { yading@10: if (dst != src) { yading@10: for (y = 0; y < height; y++) { yading@10: memcpy(dst, src, width); yading@10: dst += dst_linesize; yading@10: src += src_linesize; yading@10: } yading@10: } yading@10: yading@10: return; yading@10: } yading@10: yading@10: for (y = 0; y < height; y++) { yading@10: if (flags & NOISE_TEMPORAL) yading@10: shift = av_lfg_get(lfg) & (MAX_SHIFT - 1); yading@10: else yading@10: shift = n->rand_shift[y]; yading@10: yading@10: if (flags & NOISE_AVERAGED) { yading@10: n->line_noise_avg(dst, src, width, n->param[comp].prev_shift[y]); yading@10: n->param[comp].prev_shift[y][n->param[comp].shiftptr] = noise + shift; yading@10: } else { yading@10: n->line_noise(dst, src, noise, width, shift); yading@10: } yading@10: dst += dst_linesize; yading@10: src += src_linesize; yading@10: } yading@10: yading@10: n->param[comp].shiftptr++; yading@10: if (n->param[comp].shiftptr == 3) yading@10: n->param[comp].shiftptr = 0; yading@10: } yading@10: yading@10: static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) yading@10: { yading@10: NoiseContext *n = inlink->dst->priv; yading@10: AVFilterLink *outlink = inlink->dst->outputs[0]; yading@10: AVFrame *out; yading@10: int i; yading@10: yading@10: if (av_frame_is_writable(inpicref)) { yading@10: out = inpicref; yading@10: } else { yading@10: out = ff_get_video_buffer(outlink, outlink->w, outlink->h); yading@10: if (!out) { yading@10: av_frame_free(&inpicref); yading@10: return AVERROR(ENOMEM); yading@10: } yading@10: av_frame_copy_props(out, inpicref); yading@10: } yading@10: yading@10: for (i = 0; i < n->nb_planes; i++) yading@10: noise(out->data[i], inpicref->data[i], out->linesize[i], yading@10: inpicref->linesize[i], n->linesize[i], n->height[i], n, i); yading@10: yading@10: if (inpicref != out) yading@10: av_frame_free(&inpicref); yading@10: return ff_filter_frame(outlink, out); yading@10: } yading@10: yading@10: static av_cold int init(AVFilterContext *ctx) yading@10: { yading@10: NoiseContext *n = ctx->priv; yading@10: int ret, i; yading@10: int cpu_flags = av_get_cpu_flags(); yading@10: yading@10: for (i = 0; i < 4; i++) { yading@10: if (n->all.seed >= 0) yading@10: n->param[i].seed = n->all.seed; yading@10: else yading@10: n->param[i].seed = 123457; yading@10: if (n->all.strength) yading@10: n->param[i].strength = n->all.strength; yading@10: if (n->all.flags) yading@10: n->param[i].flags = n->all.flags; yading@10: } yading@10: yading@10: for (i = 0; i < 4; i++) { yading@10: if (n->param[i].strength && ((ret = init_noise(n, i)) < 0)) yading@10: return ret; yading@10: } yading@10: yading@10: if (HAVE_MMX_INLINE && yading@10: cpu_flags & AV_CPU_FLAG_MMX) { yading@10: n->line_noise = line_noise_mmx; yading@10: n->line_noise_avg = line_noise_avg_mmx; yading@10: } yading@10: if (HAVE_MMXEXT_INLINE && yading@10: cpu_flags & AV_CPU_FLAG_MMXEXT) yading@10: n->line_noise = line_noise_mmxext; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold void uninit(AVFilterContext *ctx) yading@10: { yading@10: NoiseContext *n = ctx->priv; yading@10: int i; yading@10: yading@10: for (i = 0; i < 4; i++) yading@10: av_freep(&n->param[i].noise); yading@10: } yading@10: yading@10: static const AVFilterPad noise_inputs[] = { yading@10: { yading@10: .name = "default", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .get_video_buffer = ff_null_get_video_buffer, yading@10: .filter_frame = filter_frame, yading@10: .config_props = config_input, yading@10: }, yading@10: { NULL } yading@10: }; yading@10: yading@10: static const AVFilterPad noise_outputs[] = { yading@10: { yading@10: .name = "default", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: }, yading@10: { NULL } yading@10: }; yading@10: yading@10: AVFilter avfilter_vf_noise = { yading@10: .name = "noise", yading@10: .description = NULL_IF_CONFIG_SMALL("Add noise."), yading@10: .priv_size = sizeof(NoiseContext), yading@10: .init = init, yading@10: .uninit = uninit, yading@10: .query_formats = query_formats, yading@10: .inputs = noise_inputs, yading@10: .outputs = noise_outputs, yading@10: .priv_class = &noise_class, yading@10: };