[FFmpeg-devel] [PATCH] avfilter: add ITU-R 468 noise meter
Paul B Mahol
onemda at gmail.com
Wed Dec 16 22:34:45 CET 2015
Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
configure | 1 +
doc/filters.texi | 9 ++
libavfilter/Makefile | 1 +
libavfilter/af_itur468.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++
libavfilter/allfilters.c | 1 +
5 files changed, 234 insertions(+)
create mode 100644 libavfilter/af_itur468.c
diff --git a/configure b/configure
index ed219e4..ca89377 100755
--- a/configure
+++ b/configure
@@ -2862,6 +2862,7 @@ geq_filter_deps="gpl"
histeq_filter_deps="gpl"
hqdn3d_filter_deps="gpl"
interlace_filter_deps="gpl"
+itur468_filter_deps="gpl"
kerndeint_filter_deps="gpl"
ladspa_filter_deps="ladspa dlopen"
mcdeint_filter_deps="avcodec gpl"
diff --git a/doc/filters.texi b/doc/filters.texi
index 78fbd47..68feea8 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -2265,6 +2265,15 @@ Applies only to double-pole filter.
The default is 0.707q and gives a Butterworth response.
@end table
+ at section itur468
+
+ITU R468 scanner filter is optimised for measuring low-level background noise.
+It rises 6 dB/oct at low frequencies, has a peak of around +12 dB at 6.3 kHz,
+and fails off rapidly after that. It returns audio stream unchanged.
+
+Measurements are returned in form of metadata: @code{lavfi.itur468.1.noise} for
+the first channel and so on.
+
@section join
Join multiple input streams into one multi-channel stream.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index dea012a..6129767 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -77,6 +77,7 @@ OBJS-$(CONFIG_EQUALIZER_FILTER) += af_biquads.o
OBJS-$(CONFIG_EXTRASTEREO_FILTER) += af_extrastereo.o
OBJS-$(CONFIG_FLANGER_FILTER) += af_flanger.o generate_wave_table.o
OBJS-$(CONFIG_HIGHPASS_FILTER) += af_biquads.o
+OBJS-$(CONFIG_ITUR468_FILTER) += af_itur468.o
OBJS-$(CONFIG_JOIN_FILTER) += af_join.o
OBJS-$(CONFIG_LADSPA_FILTER) += af_ladspa.o
OBJS-$(CONFIG_LOWPASS_FILTER) += af_biquads.o
diff --git a/libavfilter/af_itur468.c b/libavfilter/af_itur468.c
new file mode 100644
index 0000000..8e28575
--- /dev/null
+++ b/libavfilter/af_itur468.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2010 Fons Adriaensen <fons at kokkinizita.net>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <float.h>
+#include <math.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/dict.h"
+#include "libavutil/xga_font_data.h"
+#include "libavutil/opt.h"
+#include "audio.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "internal.h"
+
+typedef struct ITUR468Filter {
+ double whp;
+ double a11, a12;
+ double a21, a22;
+ double a31, a32;
+ double b30, b31, b32;
+ double zhp;
+ double z11, z12;
+ double z21, z22;
+ double z31, z32;
+
+ double a1, b1;
+ double a2, b2;
+ double z1, z2;
+} ITUR468Filter;
+
+typedef struct {
+ const AVClass *class;
+
+ ITUR468Filter *filter;
+} ITUR468Context;
+
+static int config_output(AVFilterLink *outlink)
+{
+ AVFilterContext *ctx = outlink->src;
+ AVFilterLink *inlink = ctx->inputs[0];
+ ITUR468Context *s = ctx->priv;
+ int c;
+
+ s->filter = av_calloc(inlink->channels, sizeof(*s->filter));
+ if (!s->filter)
+ return AVERROR(ENOMEM);
+
+ for (c = 0; c < inlink->channels; c++) {
+ ITUR468Filter *f = &s->filter[c];
+
+ f->whp = 3.8715217e-01;
+ f->a11 = -8.4163201e-01;
+ f->a12 = 3.0498350e-01;
+ f->a21 = -6.5680242e-01;
+ f->a22 = 2.3733993e-01;
+ f->a31 = -3.3843556e-01;
+ f->a32 = 4.3756709e-01;
+ f->b30 = 9.8607997e-01;
+ f->b31 = 5.4846389e-01;
+ f->b32 = -8.2465158e-02;
+ f->a1 = 670. / inlink->sample_rate;
+ f->b1 = 3.5 / inlink->sample_rate;
+ f->a2 = 6.6 / inlink->sample_rate;
+ f->b2 = 0.65 / inlink->sample_rate;
+ }
+
+ return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ ITUR468Context *s = ctx->priv;
+ AVDictionary **metadata;
+ metadata = avpriv_frame_get_metadatap(in);
+ int n, c;
+
+ for (c = 0; c < inlink->channels; c++) {
+ ITUR468Filter *f = &s->filter[c];
+ double *src = (double *)in->extended_data[c];
+ double out, x, zhp, z11, z12, z21, z22, z31, z32, z1, z2;
+
+ zhp = f->zhp;
+ z11 = f->z11;
+ z12 = f->z12;
+ z21 = f->z21;
+ z22 = f->z22;
+ z31 = f->z31;
+ z32 = f->z32;
+ z1 = f->z1;
+ z2 = f->z2;
+
+ for (n = 0; n < in->nb_samples; n++) {
+ x = src[n];
+ zhp += f->whp * (x - zhp) + 1e-20;
+ x -= zhp;
+ x -= f->a11 * z11 + f->a12 * z12;
+ z12 = z11;
+ z11 = x;
+ x -= f->a21 * z21 + f->a22 * z22;
+ z22 = z21;
+ z21 = x;
+ x -= f->a31 * z31 + f->a32 * z32;
+ out = f->b30 * x + f->b31 * z31 + f->b32 * z32;
+ z32 = z31;
+ z31 = x;
+ x = out;
+ x = fabs(x) + 1e-30;
+ z1 -= z1 * f->b1;
+ if (x > z1)
+ z1 += f->a1 * (x - z1);
+
+ z2 -= z2 * f->b2;
+ if (z1 > z2)
+ z2 += f->a2 * (z1 - z2);
+ }
+
+ f->zhp = zhp;
+ f->z11 = z11;
+ f->z12 = z12;
+ f->z21 = z21;
+ f->z22 = z22;
+ f->z31 = z31;
+ f->z32 = z32;
+ f->z1 = z1;
+ f->z2 = z2;
+ if (metadata) {
+ uint8_t value[128];
+ uint8_t key[128];
+
+ snprintf(key, sizeof(key), "lavfi.itur468.%d.noise", c + 1);
+ snprintf(value, sizeof(value), "%+1.1lf", 20 * log10(1.1453 * f->z2));
+ av_dict_set(metadata, key, value, 0);
+ }
+ }
+
+ return ff_filter_frame(ctx->outputs[0], in);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ AVFilterFormats *formats;
+ AVFilterChannelLayouts *layouts;
+ AVFilterLink *inlink = ctx->inputs[0];
+ AVFilterLink *outlink = ctx->outputs[0];
+ int ret;
+
+ static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE };
+ static const int input_srate[] = {48000, -1};
+
+ formats = ff_make_format_list(sample_fmts);
+ if ((ret = ff_formats_ref(formats, &inlink->out_formats)) < 0 ||
+ (ret = ff_formats_ref(formats, &outlink->in_formats)) < 0)
+ return ret;
+
+ layouts = ff_all_channel_counts();
+ if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0 ||
+ (ret = ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts)) < 0)
+ return ret;
+
+ formats = ff_make_format_list(input_srate);
+ if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0 ||
+ (ret = ff_formats_ref(formats, &outlink->in_samplerates)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ ITUR468Context *s = ctx->priv;
+
+ av_freep(&s->filter);
+}
+
+static const AVFilterPad inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ .filter_frame = filter_frame,
+ },
+ { NULL }
+};
+
+static const AVFilterPad outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ .config_props = config_output,
+ },
+ { NULL }
+};
+
+AVFilter ff_af_itur468 = {
+ .name = "itur468",
+ .description = NULL_IF_CONFIG_SMALL("ITU-R 468 noise meter."),
+ .priv_size = sizeof(ITUR468Context),
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .inputs = inputs,
+ .outputs = outputs,
+};
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 131e067..e7c2848 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -98,6 +98,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(EXTRASTEREO, extrastereo, af);
REGISTER_FILTER(FLANGER, flanger, af);
REGISTER_FILTER(HIGHPASS, highpass, af);
+ REGISTER_FILTER(ITUR468, itur468, af);
REGISTER_FILTER(JOIN, join, af);
REGISTER_FILTER(LADSPA, ladspa, af);
REGISTER_FILTER(LOWPASS, lowpass, af);
--
1.9.1
More information about the ffmpeg-devel
mailing list