[FFmpeg-devel] [PATCH] avfilter: add ahistogram multimedia filter
Paul B Mahol
onemda at gmail.com
Tue Dec 29 23:15:17 CET 2015
Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/avf_ahistogram.c | 235 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 237 insertions(+)
create mode 100644 libavfilter/avf_ahistogram.c
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index e334016..931ced6 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 a039a39..0f96c3e 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..46862c7
--- /dev/null
+++ b/libavfilter/avf_ahistogram.c
@@ -0,0 +1,235 @@
+/*
+ * 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, NB_SCALES };
+enum AmplitudeScale { ALINEAR, ALOG, NB_ASCALES };
+
+typedef struct AudioHistogramContext {
+ const AVClass *class;
+ AVFrame *out;
+ int w, h;
+ AVRational frame_rate;
+ uint64_t *histogram;
+ int ascale;
+ int scale;
+} AudioHistogramContext;
+
+#define OFFSET(x) offsetof(AudioHistogramContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption ahistogram_options[] = {
+ { "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=LINEAR}, 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" },
+ { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=LINEAR}, 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" },
+ { 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_FLT, AV_SAMPLE_FMT_NONE };
+ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, 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->histogram = av_malloc_array(s->w, sizeof(*s->histogram));
+ if (!s->histogram)
+ 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;
+
+ return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AVFilterLink *outlink = ctx->outputs[0];
+ AudioHistogramContext *s = ctx->priv;
+ const float *src = (const float *)in->data[0];
+ const int w = s->w;
+ int y, n, bin;
+ uint64_t max = 0;
+ uint8_t *dst;
+
+ 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);
+ }
+ }
+ memset(s->histogram, 0, sizeof(*s->histogram) * w);
+ for (n = 0; n < outlink->h; n++)
+ memset(s->out->data[0] + n * s->out->linesize[0], 0, outlink->w * 4);
+ s->out->pts = in->pts;
+
+ switch (s->ascale) {
+ case ALINEAR:
+ for (n = 0; n < in->nb_samples * in->channels; n++) {
+ bin = lrint(av_clipf(fabsf(src[n]), 0, 1) * (w - 1));
+
+ s->histogram[bin]++;
+ }
+ break;
+ case ALOG:
+ for (n = 0; n < in->nb_samples * in->channels; n++) {
+ bin = lrint(av_clipf(1 + log10(fabsf(src[n])) / 6, 0, 1) * (w - 1));
+
+ s->histogram[bin]++;
+ }
+ break;
+ }
+
+ av_frame_free(&in);
+
+ for (n = 0; n < w; n++) {
+ max = FFMAX(s->histogram[n], max);
+ }
+
+ for (n = 0; n < w; n++) {
+ int h;
+
+ switch (s->scale) {
+ case LINEAR:
+ h = (s->histogram[n] / (double)max) * (s->h - 1);
+ break;
+ case SQRT:
+ h = (sqrt(s->histogram[n]) / sqrt(max)) * (s->h - 1);
+ break;
+ case CBRT:
+ h = (cbrt(s->histogram[n]) / cbrt(max)) * (s->h - 1);
+ break;
+ case LOG:
+ h = (log2(s->histogram[n] + 1) / log2(max + 1)) * (s->h - 1);
+ break;
+ }
+
+ for (y = s->h - h; y < s->h; y++) {
+ dst = s->out->data[0] + y * s->out->linesize[0] + n * 4;
+ dst[0] = 255;
+ dst[1] = 255;
+ dst[2] = 255;
+ dst[3] = 255;
+ }
+ }
+
+ return ff_filter_frame(outlink, av_frame_clone(s->out));
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ AudioHistogramContext *s = ctx->priv;
+
+ av_frame_free(&s->out);
+ av_freep(&s->histogram);
+}
+
+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,
+};
--
1.9.1
More information about the ffmpeg-devel
mailing list