diff --git a/doc/filters.texi b/doc/filters.texi index 15d9d0fd8d..da8d12604a 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -25969,6 +25969,18 @@ replace old rows with new ones. scroll from top to bottom. @end table Default is @code{replace}. + +@item hmode +Set histogram mode. + +It accepts the following values: +@table @samp +@item abs +Use absolute values of samples. +@item sign +Use untouched values of samples. +@end table +Default is @code{abs}. @end table @section aphasemeter diff --git a/libavfilter/avf_ahistogram.c b/libavfilter/avf_ahistogram.c index 7ae5dd180a..03093a162f 100644 --- a/libavfilter/avf_ahistogram.c +++ b/libavfilter/avf_ahistogram.c @@ -32,7 +32,7 @@ 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 }; +enum HistogramMode { ABS, SIGN, NB_HMODES }; typedef struct AudioHistogramContext { const AVClass *class; @@ -49,6 +49,7 @@ typedef struct AudioHistogramContext { int ypos; int slide; int dmode; + int hmode; int dchannels; int count; int frame_count; @@ -56,6 +57,8 @@ typedef struct AudioHistogramContext { AVFrame *in[101]; int first; int nb_samples; + + int (*get_bin)(float in, int w); } AudioHistogramContext; #define OFFSET(x) offsetof(AudioHistogramContext, x) @@ -83,6 +86,9 @@ static const AVOption ahistogram_options[] = { { "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" }, + { "hmode", "set histograms mode", OFFSET(hmode), AV_OPT_TYPE_INT, {.i64=ABS}, 0, NB_HMODES-1, FLAGS, "hmode" }, + { "abs", "use absolute samples", 0, AV_OPT_TYPE_CONST, {.i64=ABS}, 0, 0, FLAGS, "hmode" }, + { "sign", "use unchanged samples", 0, AV_OPT_TYPE_CONST, {.i64=SIGN},0, 0, FLAGS, "hmode" }, { NULL } }; @@ -133,6 +139,26 @@ static int config_input(AVFilterLink *inlink) return 0; } +static int get_lin_bin_abs(float in, int w) +{ + return lrintf(av_clipf(fabsf(in), 0.f, 1.f) * (w - 1)); +} + +static int get_lin_bin_sign(float in, int w) +{ + return lrintf((1.f + av_clipf(in, -1.f, 1.f)) * 0.5f * (w - 1)); +} + +static int get_log_bin_abs(float in, int w) +{ + return lrintf(av_clipf(1.f + log10f(fabsf(in)) / 6.f, 0.f, 1.f) * (w - 1)); +} + +static int get_log_bin_sign(float in, int w) +{ + return (w / 2) + FFSIGN(in) * lrintf(av_clipf(1.f + log10f(fabsf(in)) / 6.f, 0.f, 1.f) * (w / 2)); +} + static int config_output(AVFilterLink *outlink) { AudioHistogramContext *s = outlink->src->priv; @@ -145,6 +171,27 @@ static int config_output(AVFilterLink *outlink) s->histogram_h = s->h * s->phisto; s->ypos = s->h * s->phisto; + switch (s->ascale) { + case ALINEAR: + switch (s->hmode) { + case ABS: s->get_bin = get_lin_bin_abs; break; + case SIGN: s->get_bin = get_lin_bin_sign; break; + default: + return AVERROR_BUG; + } + break; + case ALOG: + switch (s->hmode) { + case ABS: s->get_bin = get_log_bin_abs; break; + case SIGN: s->get_bin = get_log_bin_sign; break; + default: + return AVERROR_BUG; + } + break; + default: + return AVERROR_BUG; + } + if (s->dmode == SEPARATE) { s->combine_buffer = av_malloc_array(outlink->w * 3, sizeof(*s->combine_buffer)); if (!s->combine_buffer) @@ -207,7 +254,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) 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)); + bin = s->get_bin(src[n], w); achistogram[bin]++; } @@ -217,7 +264,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) 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)); + bin = s->get_bin(src2[n], w); shistogram[bin]++; } @@ -230,7 +277,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) 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)); + bin = s->get_bin(src[n], w); achistogram[bin]++; } @@ -240,7 +287,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) 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)); + bin = s->get_bin(src2[n], w); shistogram[bin]++; } @@ -265,7 +312,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) float yf, uf, vf; if (s->dmode == SEPARATE) { - yf = 256.0f / s->dchannels; + yf = 255.0f / s->dchannels; uf = yf * M_PI; vf = yf * M_PI; uf *= 0.5 * sin((2 * M_PI * c) / s->dchannels); @@ -312,7 +359,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->h - H > 0) { h = aa * 255; - s->out->data[0][s->ypos * s->out->linesize[0] + n] = h; + s->out->data[0][s->ypos * s->out->linesize[0] + n] = av_clip(h, 0, 255); 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; @@ -326,9 +373,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) 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[0][y * s->out->linesize[0] + n] = av_clip(yf, 0, 255); + s->out->data[1][y * s->out->linesize[1] + n] = av_clip(128.f+uf, 0, 255); + s->out->data[2][y * s->out->linesize[2] + n] = av_clip(128.f+vf, 0, 255); s->out->data[3][y * s->out->linesize[3] + n] = 255; }