[FFmpeg-cvslog] avfilter: add ahistogram multimedia filter
Paul B Mahol
git at videolan.org
Fri Jan 15 19:11:47 CET 2016
ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Tue Dec 29 21:22:26 2015 +0100| [7d76294ce006692d341beedada7a5cde017f1697] | committer: Paul B Mahol
avfilter: add ahistogram multimedia filter
Signed-off-by: Paul B Mahol <onemda at gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=7d76294ce006692d341beedada7a5cde017f1697
---
Changelog | 1 +
doc/filters.texi | 78 ++++++++
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/avf_ahistogram.c | 411 ++++++++++++++++++++++++++++++++++++++++++
libavfilter/version.h | 2 +-
6 files changed, 493 insertions(+), 1 deletion(-)
diff --git a/Changelog b/Changelog
index df2a30a..ed18cf1 100644
--- a/Changelog
+++ b/Changelog
@@ -53,6 +53,7 @@ version <next>:
- showspectrumpic filter
- libstagefright support removed
- spectrumsynth filter
+- ahistogram filter
version 2.8:
diff --git a/doc/filters.texi b/doc/filters.texi
index 9b3acc9..f4bda6a 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -13282,6 +13282,84 @@ tools.
Below is a description of the currently available multimedia filters.
+ at section ahistogram
+
+Convert input audio to a video output, displaying the volume histogram.
+
+The filter accepts the following options:
+
+ at table @option
+ at item dmode
+Specify how histogram is calculated.
+
+It accepts the following values:
+ at table @samp
+ at item single
+Use single histogram for all channels.
+ at item separate
+Use separate histogram for each channel.
+ at end table
+Default is @code{single}.
+
+ at item rate, r
+Set frame rate, expressed as number of frames per second. Default
+value is "25".
+
+ at item size, s
+Specify the video size for the output. For the syntax of this option, check the
+ at ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}.
+Default value is @code{hd720}.
+
+ at item scale
+Set display scale.
+
+It accepts the following values:
+ at table @samp
+ at item log
+logarithmic
+ at item sqrt
+square root
+ at item cbrt
+cubic root
+ at item lin
+linear
+ at item rlog
+reverse logarithmic
+ at end table
+Default is @code{log}.
+
+ at item ascale
+Set amplitude scale.
+
+It accepts the following values:
+ at table @samp
+ at item log
+logarithmic
+ at item lin
+linear
+ at end table
+Default is @code{log}.
+
+ at item acount
+Set how much frames to accumulate in histogram.
+Defauls is 1. Setting this to -1 accumulates all frames.
+
+ at item rheight
+Set histogram ratio of window height.
+
+ at item slide
+Set sonogram sliding.
+
+It accepts the following values:
+ at table @samp
+ at item replace
+replace old rows with new ones.
+ at item scroll
+scroll from top to bottom.
+ at end table
+Default is @code{replace}.
+ at end table
+
@section aphasemeter
Convert input audio to a video output, displaying the audio phase.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 9257a92..e3e3561 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -280,6 +280,7 @@ OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o
# multimedia filters
OBJS-$(CONFIG_ADRAWGRAPH_FILTER) += f_drawgraph.o
+OBJS-$(CONFIG_AHISTOGRAM_FILTER) += avf_ahistogram.o
OBJS-$(CONFIG_APHASEMETER_FILTER) += avf_aphasemeter.o
OBJS-$(CONFIG_AVECTORSCOPE_FILTER) += avf_avectorscope.o
OBJS-$(CONFIG_CONCAT_FILTER) += avf_concat.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index d4815d6..1faf393 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -300,6 +300,7 @@ void avfilter_register_all(void)
/* multimedia filters */
REGISTER_FILTER(ADRAWGRAPH, adrawgraph, avf);
+ REGISTER_FILTER(AHISTOGRAM, ahistogram, avf);
REGISTER_FILTER(APHASEMETER, aphasemeter, avf);
REGISTER_FILTER(AVECTORSCOPE, avectorscope, avf);
REGISTER_FILTER(CONCAT, concat, avf);
diff --git a/libavfilter/avf_ahistogram.c b/libavfilter/avf_ahistogram.c
new file mode 100644
index 0000000..55f7462
--- /dev/null
+++ b/libavfilter/avf_ahistogram.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2015 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 "libavutil/avassert.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "audio.h"
+#include "video.h"
+#include "internal.h"
+
+enum DisplayScale { LINEAR, SQRT, CBRT, LOG, RLOG, NB_SCALES };
+enum AmplitudeScale { ALINEAR, ALOG, NB_ASCALES };
+enum SlideMode { REPLACE, SCROLL, NB_SLIDES };
+enum DisplayMode { SINGLE, SEPARATE, NB_DMODES };
+enum HistogramMode { ACCUMULATE, CURRENT, NB_HMODES };
+
+typedef struct AudioHistogramContext {
+ const AVClass *class;
+ AVFrame *out;
+ int w, h;
+ AVRational frame_rate;
+ uint64_t *achistogram;
+ uint64_t *shistogram;
+ int ascale;
+ int scale;
+ float phisto;
+ int histogram_h;
+ int apos;
+ int ypos;
+ int slide;
+ int dmode;
+ int dchannels;
+ int count;
+ int frame_count;
+ float *combine_buffer;
+ AVFrame *in[101];
+ int first;
+} AudioHistogramContext;
+
+#define OFFSET(x) offsetof(AudioHistogramContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption ahistogram_options[] = {
+ { "dmode", "set method to display channels", OFFSET(dmode), AV_OPT_TYPE_INT, {.i64=SINGLE}, 0, NB_DMODES-1, FLAGS, "dmode" },
+ { "single", "all channels use single histogram", 0, AV_OPT_TYPE_CONST, {.i64=SINGLE}, 0, 0, FLAGS, "dmode" },
+ { "separate", "each channel have own histogram", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "dmode" },
+ { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
+ { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
+ { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS },
+ { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS },
+ { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, LINEAR, NB_SCALES-1, FLAGS, "scale" },
+ { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "scale" },
+ { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, "scale" },
+ { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, "scale" },
+ { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" },
+ { "rlog", "reverse logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=RLOG}, 0, 0, FLAGS, "scale" },
+ { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=ALOG}, LINEAR, NB_ASCALES-1, FLAGS, "ascale" },
+ { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=ALOG}, 0, 0, FLAGS, "ascale" },
+ { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=ALINEAR}, 0, 0, FLAGS, "ascale" },
+ { "acount", "how much frames to accumulate", OFFSET(count), AV_OPT_TYPE_INT, {.i64=1}, -1, 100, FLAGS },
+ { "rheight", "set histogram ratio of window height", OFFSET(phisto), AV_OPT_TYPE_FLOAT, {.dbl=0.10}, 0, 1, FLAGS },
+ { "slide", "set sonogram sliding", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=REPLACE}, 0, NB_SLIDES-1, FLAGS, "slide" },
+ { "replace", "replace old rows with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, "slide" },
+ { "scroll", "scroll from top to bottom", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, "slide" },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(ahistogram);
+
+static int query_formats(AVFilterContext *ctx)
+{
+ AVFilterFormats *formats = NULL;
+ AVFilterChannelLayouts *layouts = NULL;
+ AVFilterLink *inlink = ctx->inputs[0];
+ AVFilterLink *outlink = ctx->outputs[0];
+ static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };
+ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE };
+ int ret = AVERROR(EINVAL);
+
+ formats = ff_make_format_list(sample_fmts);
+ if ((ret = ff_formats_ref (formats, &inlink->out_formats )) < 0 ||
+ (layouts = ff_all_channel_counts()) == NULL ||
+ (ret = ff_channel_layouts_ref (layouts, &inlink->out_channel_layouts)) < 0)
+ return ret;
+
+ formats = ff_all_samplerates();
+ if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0)
+ return ret;
+
+ formats = ff_make_format_list(pix_fmts);
+ if ((ret = ff_formats_ref(formats, &outlink->in_formats)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AudioHistogramContext *s = ctx->priv;
+ int nb_samples;
+
+ nb_samples = FFMAX(1024, ((double)inlink->sample_rate / av_q2d(s->frame_rate)) + 0.5);
+ inlink->partial_buf_size =
+ inlink->min_samples =
+ inlink->max_samples = nb_samples;
+
+ s->dchannels = s->dmode == SINGLE ? 1 : inlink->channels;
+ s->shistogram = av_calloc(s->w, s->dchannels * sizeof(*s->shistogram));
+ if (!s->shistogram)
+ return AVERROR(ENOMEM);
+
+ s->achistogram = av_calloc(s->w, s->dchannels * sizeof(*s->achistogram));
+ if (!s->achistogram)
+ return AVERROR(ENOMEM);
+
+ return 0;
+}
+
+static int config_output(AVFilterLink *outlink)
+{
+ AudioHistogramContext *s = outlink->src->priv;
+
+ outlink->w = s->w;
+ outlink->h = s->h;
+ outlink->sample_aspect_ratio = (AVRational){1,1};
+ outlink->frame_rate = s->frame_rate;
+
+ s->histogram_h = s->h * s->phisto;
+ s->ypos = s->h * s->phisto;
+
+ if (s->dmode == SEPARATE) {
+ s->combine_buffer = av_malloc_array(outlink->w * 3, sizeof(*s->combine_buffer));
+ if (!s->combine_buffer)
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AVFilterLink *outlink = ctx->outputs[0];
+ AudioHistogramContext *s = ctx->priv;
+ const int H = s->histogram_h;
+ const int w = s->w;
+ int c, y, n, p, bin;
+ uint64_t acmax = 0;
+
+ if (!s->out || s->out->width != outlink->w ||
+ s->out->height != outlink->h) {
+ av_frame_free(&s->out);
+ s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+ if (!s->out) {
+ av_frame_free(&in);
+ return AVERROR(ENOMEM);
+ }
+ for (n = H; n < s->h; n++) {
+ memset(s->out->data[0] + n * s->out->linesize[0], 0, w);
+ memset(s->out->data[1] + n * s->out->linesize[0], 127, w);
+ memset(s->out->data[2] + n * s->out->linesize[0], 127, w);
+ memset(s->out->data[3] + n * s->out->linesize[0], 0, w);
+ }
+ }
+
+ if (s->dmode == SEPARATE) {
+ for (y = 0; y < w; y++) {
+ s->combine_buffer[3 * y ] = 0;
+ s->combine_buffer[3 * y + 1] = 127.5;
+ s->combine_buffer[3 * y + 2] = 127.5;
+ }
+ }
+
+ for (n = 0; n < H; n++) {
+ memset(s->out->data[0] + n * s->out->linesize[0], 0, w);
+ memset(s->out->data[1] + n * s->out->linesize[0], 127, w);
+ memset(s->out->data[2] + n * s->out->linesize[0], 127, w);
+ memset(s->out->data[3] + n * s->out->linesize[0], 0, w);
+ }
+ s->out->pts = in->pts;
+
+ s->first = s->frame_count;
+
+ switch (s->ascale) {
+ case ALINEAR:
+ for (c = 0; c < inlink->channels; c++) {
+ const float *src = (const float *)in->extended_data[c];
+ uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w];
+
+ for (n = 0; n < in->nb_samples; n++) {
+ bin = lrint(av_clipf(fabsf(src[n]), 0, 1) * (w - 1));
+
+ achistogram[bin]++;
+ }
+
+ if (s->in[s->first] && s->count >= 0) {
+ uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w];
+ const float *src2 = (const float *)s->in[s->first]->extended_data[c];
+
+ for (n = 0; n < in->nb_samples; n++) {
+ bin = lrint(av_clipf(fabsf(src2[n]), 0, 1) * (w - 1));
+
+ shistogram[bin]++;
+ }
+ }
+ }
+ break;
+ case ALOG:
+ for (c = 0; c < inlink->channels; c++) {
+ const float *src = (const float *)in->extended_data[c];
+ uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w];
+
+ for (n = 0; n < in->nb_samples; n++) {
+ bin = lrint(av_clipf(1 + log10(fabsf(src[n])) / 6, 0, 1) * (w - 1));
+
+ achistogram[bin]++;
+ }
+
+ if (s->in[s->first] && s->count >= 0) {
+ uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w];
+ const float *src2 = (const float *)s->in[s->first]->extended_data[c];
+
+ for (n = 0; n < in->nb_samples; n++) {
+ bin = lrint(av_clipf(1 + log10(fabsf(src2[n])) / 6, 0, 1) * (w - 1));
+
+ shistogram[bin]++;
+ }
+ }
+ }
+ break;
+ }
+
+ av_frame_free(&s->in[s->frame_count]);
+ s->in[s->frame_count] = in;
+ s->frame_count++;
+ if (s->frame_count > s->count)
+ s->frame_count = 0;
+
+ for (n = 0; n < w * s->dchannels; n++) {
+ acmax = FFMAX(s->achistogram[n] - s->shistogram[n], acmax);
+ }
+
+ for (c = 0; c < s->dchannels; c++) {
+ uint64_t *shistogram = &s->shistogram[c * w];
+ uint64_t *achistogram = &s->achistogram[c * w];
+ float yf, uf, vf;
+
+ if (s->dmode == SEPARATE) {
+ yf = 256.0f / s->dchannels;
+ uf = yf * M_PI;
+ vf = yf * M_PI;
+ uf *= 0.5 * sin((2 * M_PI * c) / s->dchannels);
+ vf *= 0.5 * cos((2 * M_PI * c) / s->dchannels);
+ }
+
+ for (n = 0; n < w; n++) {
+ double a, aa;
+ int h;
+
+ a = achistogram[n] - shistogram[n];
+
+ switch (s->scale) {
+ case LINEAR:
+ aa = a / (double)acmax;
+ break;
+ case SQRT:
+ aa = sqrt(a) / sqrt(acmax);
+ break;
+ case CBRT:
+ aa = cbrt(a) / cbrt(acmax);
+ break;
+ case LOG:
+ aa = log2(a + 1) / log2(acmax + 1);
+ break;
+ case RLOG:
+ aa = 1. - log2(a + 1) / log2(acmax + 1);
+ if (aa == 1.)
+ aa = 0;
+ break;
+ }
+
+ h = aa * (H - 1);
+
+ if (s->dmode == SINGLE) {
+
+ for (y = H - h; y < H; y++) {
+ s->out->data[0][y * s->out->linesize[0] + n] = 255;
+ s->out->data[3][y * s->out->linesize[0] + n] = 255;
+ }
+
+ if (s->h - H > 0) {
+ h = aa * 255;
+
+ s->out->data[0][s->ypos * s->out->linesize[0] + n] = h;
+ s->out->data[1][s->ypos * s->out->linesize[1] + n] = 127;
+ s->out->data[2][s->ypos * s->out->linesize[2] + n] = 127;
+ s->out->data[3][s->ypos * s->out->linesize[3] + n] = 255;
+ }
+ } else if (s->dmode == SEPARATE) {
+ float *out = &s->combine_buffer[3 * n];
+ int old;
+
+ old = s->out->data[0][(H - h) * s->out->linesize[0] + n];
+ for (y = H - h; y < H; y++) {
+ if (s->out->data[0][y * s->out->linesize[0] + n] != old)
+ break;
+ old = s->out->data[0][y * s->out->linesize[0] + n];
+ s->out->data[0][y * s->out->linesize[0] + n] = yf;
+ s->out->data[1][y * s->out->linesize[1] + n] = 128+uf;
+ s->out->data[2][y * s->out->linesize[2] + n] = 128+vf;
+ s->out->data[3][y * s->out->linesize[3] + n] = 255;
+ }
+
+ out[0] += aa * yf;
+ out[1] += aa * uf;
+ out[2] += aa * vf;
+ }
+ }
+ }
+
+ if (s->h - H > 0) {
+ if (s->dmode == SEPARATE) {
+ for (n = 0; n < w; n++) {
+ float *cb = &s->combine_buffer[3 * n];
+
+ s->out->data[0][s->ypos * s->out->linesize[0] + n] = cb[0];
+ s->out->data[1][s->ypos * s->out->linesize[1] + n] = cb[1];
+ s->out->data[2][s->ypos * s->out->linesize[2] + n] = cb[2];
+ s->out->data[3][s->ypos * s->out->linesize[3] + n] = 255;
+ }
+ }
+
+ if (s->slide == SCROLL) {
+ for (p = 0; p < 4; p++) {
+ for (y = s->h; y >= H + 1; y--) {
+ memmove(s->out->data[p] + (y ) * s->out->linesize[p],
+ s->out->data[p] + (y-1) * s->out->linesize[p], w);
+ }
+ }
+ }
+
+ s->ypos++;
+ if (s->slide == SCROLL || s->ypos >= s->h)
+ s->ypos = H;
+ }
+
+ return ff_filter_frame(outlink, av_frame_clone(s->out));
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ AudioHistogramContext *s = ctx->priv;
+ int i;
+
+ av_frame_free(&s->out);
+ av_freep(&s->shistogram);
+ av_freep(&s->achistogram);
+ av_freep(&s->combine_buffer);
+ for (i = 0; i < 101; i++)
+ av_frame_free(&s->in[i]);
+}
+
+static const AVFilterPad audiovectorscope_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ .config_props = config_input,
+ .filter_frame = filter_frame,
+ },
+ { NULL }
+};
+
+static const AVFilterPad audiovectorscope_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .config_props = config_output,
+ },
+ { NULL }
+};
+
+AVFilter ff_avf_ahistogram = {
+ .name = "ahistogram",
+ .description = NULL_IF_CONFIG_SMALL("Convert input audio to histogram video output."),
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .priv_size = sizeof(AudioHistogramContext),
+ .inputs = audiovectorscope_inputs,
+ .outputs = audiovectorscope_outputs,
+ .priv_class = &ahistogram_class,
+};
diff --git a/libavfilter/version.h b/libavfilter/version.h
index d13929e..d1f3802 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -30,7 +30,7 @@
#include "libavutil/version.h"
#define LIBAVFILTER_VERSION_MAJOR 6
-#define LIBAVFILTER_VERSION_MINOR 24
+#define LIBAVFILTER_VERSION_MINOR 25
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
More information about the ffmpeg-cvslog
mailing list