summaryrefslogtreecommitdiff
path: root/libavfilter
diff options
context:
space:
mode:
authorMarton Balint <cus@passwd.hu>2016-10-15 19:21:22 +0200
committerMarton Balint <cus@passwd.hu>2016-11-11 19:37:54 +0100
commit005d058f4230f3207ebcf1131df7426d4f57392f (patch)
treea65646eab5ac5a0df5dd51da3354018bf23bbc88 /libavfilter
parent7b8445f03d10faf7ed232e6201bf04ba73d980d7 (diff)
lavfi/loudnorm: add an internal libebur128 library
Also contains the following changes to the library: - add ff_ prefix to functions - remove cplusplus defines. - add FF_ prefix to contants and some structs - remove true peak calculation feature, since it uses its own resampler, and af_loudnorm does not need it. - remove version info and some fprintf(stderr) functions - convert to use av_malloc - always use histogram mode for LRA calculation, otherwise LRA data is slowly consuming memory making af_loudnorm unfit for 24/7 operation. It also uses a BSD style linked list implementation which is probably not available on all platforms. So let's just remove the classic mode which not uses histogram. - add ff_thread_once for calculating static histogram tables - convert some functions to void which cannot fail - remove intrinsics and some unused headers - add support for planar audio - remove channel / sample rate changer function, in ffmpeg usually we simply alloc a new context - convert some static variables to defines - declare static histogram variables as aligned - convert some initalizations to mallocz - add window size parameter to init function and remove window size setter function - convert return codes to AVERROR - fix indentation Signed-off-by: Marton Balint <cus@passwd.hu>
Diffstat (limited to 'libavfilter')
-rw-r--r--libavfilter/Makefile2
-rw-r--r--libavfilter/af_loudnorm.c60
-rw-r--r--libavfilter/ebur128.c784
-rw-r--r--libavfilter/ebur128.h296
-rw-r--r--libavfilter/version.h2
5 files changed, 1112 insertions, 32 deletions
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 7ed46966a2..cdddb1b076 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -93,7 +93,7 @@ OBJS-$(CONFIG_HDCD_FILTER) += af_hdcd.o
OBJS-$(CONFIG_HIGHPASS_FILTER) += af_biquads.o
OBJS-$(CONFIG_JOIN_FILTER) += af_join.o
OBJS-$(CONFIG_LADSPA_FILTER) += af_ladspa.o
-OBJS-$(CONFIG_LOUDNORM_FILTER) += af_loudnorm.o
+OBJS-$(CONFIG_LOUDNORM_FILTER) += af_loudnorm.o ebur128.o
OBJS-$(CONFIG_LOWPASS_FILTER) += af_biquads.o
OBJS-$(CONFIG_PAN_FILTER) += af_pan.o
OBJS-$(CONFIG_REPLAYGAIN_FILTER) += af_replaygain.o
diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c
index 604697e6f7..9d91c76047 100644
--- a/libavfilter/af_loudnorm.c
+++ b/libavfilter/af_loudnorm.c
@@ -24,7 +24,7 @@
#include "avfilter.h"
#include "internal.h"
#include "audio.h"
-#include <ebur128.h>
+#include "ebur128.h"
enum FrameType {
FIRST_FRAME,
@@ -91,8 +91,8 @@ typedef struct LoudNormContext {
int prev_nb_samples;
int channels;
- ebur128_state *r128_in;
- ebur128_state *r128_out;
+ FFEBUR128State *r128_in;
+ FFEBUR128State *r128_out;
} LoudNormContext;
#define OFFSET(x) offsetof(LoudNormContext, x)
@@ -437,15 +437,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
buf = s->buf;
limiter_buf = s->limiter_buf;
- ebur128_add_frames_double(s->r128_in, src, in->nb_samples);
+ ff_ebur128_add_frames_double(s->r128_in, src, in->nb_samples);
if (s->frame_type == FIRST_FRAME && in->nb_samples < frame_size(inlink->sample_rate, 3000)) {
double offset, offset_tp, true_peak;
- ebur128_loudness_global(s->r128_in, &global);
+ ff_ebur128_loudness_global(s->r128_in, &global);
for (c = 0; c < inlink->channels; c++) {
double tmp;
- ebur128_sample_peak(s->r128_in, c, &tmp);
+ ff_ebur128_sample_peak(s->r128_in, c, &tmp);
if (c == 0 || tmp > true_peak)
true_peak = tmp;
}
@@ -467,7 +467,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
s->buf_index += inlink->channels;
}
- ebur128_loudness_shortterm(s->r128_in, &shortterm);
+ ff_ebur128_loudness_shortterm(s->r128_in, &shortterm);
if (shortterm < s->measured_thresh) {
s->above_threshold = 0;
@@ -497,7 +497,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
subframe_length = frame_size(inlink->sample_rate, 100);
true_peak_limiter(s, dst, subframe_length, inlink->channels);
- ebur128_add_frames_double(s->r128_out, dst, subframe_length);
+ ff_ebur128_add_frames_double(s->r128_out, dst, subframe_length);
s->pts +=
out->nb_samples =
@@ -536,12 +536,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
s->limiter_buf_index = s->limiter_buf_index + subframe_length < s->limiter_buf_size ? s->limiter_buf_index + subframe_length : s->limiter_buf_index + subframe_length - s->limiter_buf_size;
true_peak_limiter(s, dst, in->nb_samples, inlink->channels);
- ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
+ ff_ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
- ebur128_loudness_range(s->r128_in, &lra);
- ebur128_loudness_global(s->r128_in, &global);
- ebur128_loudness_shortterm(s->r128_in, &shortterm);
- ebur128_relative_threshold(s->r128_in, &relative_threshold);
+ ff_ebur128_loudness_range(s->r128_in, &lra);
+ ff_ebur128_loudness_global(s->r128_in, &global);
+ ff_ebur128_loudness_shortterm(s->r128_in, &shortterm);
+ ff_ebur128_relative_threshold(s->r128_in, &relative_threshold);
if (s->above_threshold == 0) {
double shortterm_out;
@@ -549,7 +549,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
if (shortterm > s->measured_thresh)
s->prev_delta *= 1.0058;
- ebur128_loudness_shortterm(s->r128_out, &shortterm_out);
+ ff_ebur128_loudness_shortterm(s->r128_out, &shortterm_out);
if (shortterm_out >= s->target_i)
s->above_threshold = 1;
}
@@ -611,7 +611,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
}
dst = (double *)out->data[0];
- ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
+ ff_ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
break;
case LINEAR_MODE:
@@ -624,7 +624,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
}
dst = (double *)out->data[0];
- ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
+ ff_ebur128_add_frames_double(s->r128_out, dst, in->nb_samples);
s->pts += in->nb_samples;
break;
}
@@ -725,17 +725,17 @@ static int config_input(AVFilterLink *inlink)
AVFilterContext *ctx = inlink->dst;
LoudNormContext *s = ctx->priv;
- s->r128_in = ebur128_init(inlink->channels, inlink->sample_rate, EBUR128_MODE_I | EBUR128_MODE_S | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK);
+ s->r128_in = ff_ebur128_init(inlink->channels, inlink->sample_rate, 0, FF_EBUR128_MODE_I | FF_EBUR128_MODE_S | FF_EBUR128_MODE_LRA | FF_EBUR128_MODE_SAMPLE_PEAK);
if (!s->r128_in)
return AVERROR(ENOMEM);
- s->r128_out = ebur128_init(inlink->channels, inlink->sample_rate, EBUR128_MODE_I | EBUR128_MODE_S | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK);
+ s->r128_out = ff_ebur128_init(inlink->channels, inlink->sample_rate, 0, FF_EBUR128_MODE_I | FF_EBUR128_MODE_S | FF_EBUR128_MODE_LRA | FF_EBUR128_MODE_SAMPLE_PEAK);
if (!s->r128_out)
return AVERROR(ENOMEM);
if (inlink->channels == 1 && s->dual_mono) {
- ebur128_set_channel(s->r128_in, 0, EBUR128_DUAL_MONO);
- ebur128_set_channel(s->r128_out, 0, EBUR128_DUAL_MONO);
+ ff_ebur128_set_channel(s->r128_in, 0, FF_EBUR128_DUAL_MONO);
+ ff_ebur128_set_channel(s->r128_out, 0, FF_EBUR128_DUAL_MONO);
}
s->buf_size = frame_size(inlink->sample_rate, 3000) * inlink->channels;
@@ -799,22 +799,22 @@ static av_cold void uninit(AVFilterContext *ctx)
if (!s->r128_in || !s->r128_out)
goto end;
- ebur128_loudness_range(s->r128_in, &lra_in);
- ebur128_loudness_global(s->r128_in, &i_in);
- ebur128_relative_threshold(s->r128_in, &thresh_in);
+ ff_ebur128_loudness_range(s->r128_in, &lra_in);
+ ff_ebur128_loudness_global(s->r128_in, &i_in);
+ ff_ebur128_relative_threshold(s->r128_in, &thresh_in);
for (c = 0; c < s->channels; c++) {
double tmp;
- ebur128_sample_peak(s->r128_in, c, &tmp);
+ ff_ebur128_sample_peak(s->r128_in, c, &tmp);
if ((c == 0) || (tmp > tp_in))
tp_in = tmp;
}
- ebur128_loudness_range(s->r128_out, &lra_out);
- ebur128_loudness_global(s->r128_out, &i_out);
- ebur128_relative_threshold(s->r128_out, &thresh_out);
+ ff_ebur128_loudness_range(s->r128_out, &lra_out);
+ ff_ebur128_loudness_global(s->r128_out, &i_out);
+ ff_ebur128_relative_threshold(s->r128_out, &thresh_out);
for (c = 0; c < s->channels; c++) {
double tmp;
- ebur128_sample_peak(s->r128_out, c, &tmp);
+ ff_ebur128_sample_peak(s->r128_out, c, &tmp);
if ((c == 0) || (tmp > tp_out))
tp_out = tmp;
}
@@ -881,9 +881,9 @@ static av_cold void uninit(AVFilterContext *ctx)
end:
if (s->r128_in)
- ebur128_destroy(&s->r128_in);
+ ff_ebur128_destroy(&s->r128_in);
if (s->r128_out)
- ebur128_destroy(&s->r128_out);
+ ff_ebur128_destroy(&s->r128_out);
av_freep(&s->limiter_buf);
av_freep(&s->prev_smp);
av_freep(&s->buf);
diff --git a/libavfilter/ebur128.c b/libavfilter/ebur128.c
new file mode 100644
index 0000000000..8e216c4d58
--- /dev/null
+++ b/libavfilter/ebur128.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 2011 Jan Kokemüller
+ *
+ * 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
+ *
+ * This file is based on libebur128 which is available at
+ * https://github.com/jiixyj/libebur128/
+ *
+ * Libebur128 has the following copyright:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+*/
+
+#include "ebur128.h"
+
+#include <float.h>
+#include <limits.h>
+#include <math.h> /* You may have to define _USE_MATH_DEFINES if you use MSVC */
+
+#include "libavutil/common.h"
+#include "libavutil/mem.h"
+#include "libavutil/thread.h"
+
+#define CHECK_ERROR(condition, errorcode, goto_point) \
+ if ((condition)) { \
+ errcode = (errorcode); \
+ goto goto_point; \
+ }
+
+#define ALMOST_ZERO 0.000001
+
+#define RELATIVE_GATE (-10.0)
+#define RELATIVE_GATE_FACTOR pow(10.0, RELATIVE_GATE / 10.0)
+#define MINUS_20DB pow(10.0, -20.0 / 10.0)
+
+struct FFEBUR128StateInternal {
+ /** Filtered audio data (used as ring buffer). */
+ double *audio_data;
+ /** Size of audio_data array. */
+ size_t audio_data_frames;
+ /** Current index for audio_data. */
+ size_t audio_data_index;
+ /** How many frames are needed for a gating block. Will correspond to 400ms
+ * of audio at initialization, and 100ms after the first block (75% overlap
+ * as specified in the 2011 revision of BS1770). */
+ unsigned long needed_frames;
+ /** The channel map. Has as many elements as there are channels. */
+ int *channel_map;
+ /** How many samples fit in 100ms (rounded). */
+ unsigned long samples_in_100ms;
+ /** BS.1770 filter coefficients (nominator). */
+ double b[5];
+ /** BS.1770 filter coefficients (denominator). */
+ double a[5];
+ /** BS.1770 filter state. */
+ double v[5][5];
+ /** Histograms, used to calculate LRA. */
+ unsigned long *block_energy_histogram;
+ unsigned long *short_term_block_energy_histogram;
+ /** Keeps track of when a new short term block is needed. */
+ size_t short_term_frame_counter;
+ /** Maximum sample peak, one per channel */
+ double *sample_peak;
+ /** The maximum window duration in ms. */
+ unsigned long window;
+ /** Data pointer array for interleaved data */
+ void **data_ptrs;
+};
+
+static AVOnce histogram_init = AV_ONCE_INIT;
+static DECLARE_ALIGNED(32, double, histogram_energies)[1000];
+static DECLARE_ALIGNED(32, double, histogram_energy_boundaries)[1001];
+
+static void ebur128_init_filter(FFEBUR128State * st)
+{
+ int i, j;
+
+ double f0 = 1681.974450955533;
+ double G = 3.999843853973347;
+ double Q = 0.7071752369554196;
+
+ double K = tan(M_PI * f0 / (double) st->samplerate);
+ double Vh = pow(10.0, G / 20.0);
+ double Vb = pow(Vh, 0.4996667741545416);
+
+ double pb[3] = { 0.0, 0.0, 0.0 };
+ double pa[3] = { 1.0, 0.0, 0.0 };
+ double rb[3] = { 1.0, -2.0, 1.0 };
+ double ra[3] = { 1.0, 0.0, 0.0 };
+
+ double a0 = 1.0 + K / Q + K * K;
+ pb[0] = (Vh + Vb * K / Q + K * K) / a0;
+ pb[1] = 2.0 * (K * K - Vh) / a0;
+ pb[2] = (Vh - Vb * K / Q + K * K) / a0;
+ pa[1] = 2.0 * (K * K - 1.0) / a0;
+ pa[2] = (1.0 - K / Q + K * K) / a0;
+
+ f0 = 38.13547087602444;
+ Q = 0.5003270373238773;
+ K = tan(M_PI * f0 / (double) st->samplerate);
+
+ ra[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K);
+ ra[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K);
+
+ st->d->b[0] = pb[0] * rb[0];
+ st->d->b[1] = pb[0] * rb[1] + pb[1] * rb[0];
+ st->d->b[2] = pb[0] * rb[2] + pb[1] * rb[1] + pb[2] * rb[0];
+ st->d->b[3] = pb[1] * rb[2] + pb[2] * rb[1];
+ st->d->b[4] = pb[2] * rb[2];
+
+ st->d->a[0] = pa[0] * ra[0];
+ st->d->a[1] = pa[0] * ra[1] + pa[1] * ra[0];
+ st->d->a[2] = pa[0] * ra[2] + pa[1] * ra[1] + pa[2] * ra[0];
+ st->d->a[3] = pa[1] * ra[2] + pa[2] * ra[1];
+ st->d->a[4] = pa[2] * ra[2];
+
+ for (i = 0; i < 5; ++i) {
+ for (j = 0; j < 5; ++j) {
+ st->d->v[i][j] = 0.0;
+ }
+ }
+}
+
+static int ebur128_init_channel_map(FFEBUR128State * st)
+{
+ size_t i;
+ st->d->channel_map =
+ (int *) av_malloc_array(st->channels, sizeof(int));
+ if (!st->d->channel_map)
+ return AVERROR(ENOMEM);
+ if (st->channels == 4) {
+ st->d->channel_map[0] = FF_EBUR128_LEFT;
+ st->d->channel_map[1] = FF_EBUR128_RIGHT;
+ st->d->channel_map[2] = FF_EBUR128_LEFT_SURROUND;
+ st->d->channel_map[3] = FF_EBUR128_RIGHT_SURROUND;
+ } else if (st->channels == 5) {
+ st->d->channel_map[0] = FF_EBUR128_LEFT;
+ st->d->channel_map[1] = FF_EBUR128_RIGHT;
+ st->d->channel_map[2] = FF_EBUR128_CENTER;
+ st->d->channel_map[3] = FF_EBUR128_LEFT_SURROUND;
+ st->d->channel_map[4] = FF_EBUR128_RIGHT_SURROUND;
+ } else {
+ for (i = 0; i < st->channels; ++i) {
+ switch (i) {
+ case 0:
+ st->d->channel_map[i] = FF_EBUR128_LEFT;
+ break;
+ case 1:
+ st->d->channel_map[i] = FF_EBUR128_RIGHT;
+ break;
+ case 2:
+ st->d->channel_map[i] = FF_EBUR128_CENTER;
+ break;
+ case 3:
+ st->d->channel_map[i] = FF_EBUR128_UNUSED;
+ break;
+ case 4:
+ st->d->channel_map[i] = FF_EBUR128_LEFT_SURROUND;
+ break;
+ case 5:
+ st->d->channel_map[i] = FF_EBUR128_RIGHT_SURROUND;
+ break;
+ default:
+ st->d->channel_map[i] = FF_EBUR128_UNUSED;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static inline void init_histogram(void)
+{
+ int i;
+ /* initialize static constants */
+ histogram_energy_boundaries[0] = pow(10.0, (-70.0 + 0.691) / 10.0);
+ for (i = 0; i < 1000; ++i) {
+ histogram_energies[i] =
+ pow(10.0, ((double) i / 10.0 - 69.95 + 0.691) / 10.0);
+ }
+ for (i = 1; i < 1001; ++i) {
+ histogram_energy_boundaries[i] =
+ pow(10.0, ((double) i / 10.0 - 70.0 + 0.691) / 10.0);
+ }
+}
+
+FFEBUR128State *ff_ebur128_init(unsigned int channels,
+ unsigned long samplerate,
+ unsigned long window, int mode)
+{
+ int errcode;
+ FFEBUR128State *st;
+
+ st = (FFEBUR128State *) av_malloc(sizeof(FFEBUR128State));
+ CHECK_ERROR(!st, 0, exit)
+ st->d = (struct FFEBUR128StateInternal *)
+ av_malloc(sizeof(struct FFEBUR128StateInternal));
+ CHECK_ERROR(!st->d, 0, free_state)
+ st->channels = channels;
+ errcode = ebur128_init_channel_map(st);
+ CHECK_ERROR(errcode, 0, free_internal)
+
+ st->d->sample_peak =
+ (double *) av_mallocz_array(channels, sizeof(double));
+ CHECK_ERROR(!st->d->sample_peak, 0, free_channel_map)
+
+ st->samplerate = samplerate;
+ st->d->samples_in_100ms = (st->samplerate + 5) / 10;
+ st->mode = mode;
+ if ((mode & FF_EBUR128_MODE_S) == FF_EBUR128_MODE_S) {
+ st->d->window = FFMAX(window, 3000);
+ } else if ((mode & FF_EBUR128_MODE_M) == FF_EBUR128_MODE_M) {
+ st->d->window = FFMAX(window, 400);
+ } else {
+ goto free_sample_peak;
+ }
+ st->d->audio_data_frames = st->samplerate * st->d->window / 1000;
+ if (st->d->audio_data_frames % st->d->samples_in_100ms) {
+ /* round up to multiple of samples_in_100ms */
+ st->d->audio_data_frames = st->d->audio_data_frames
+ + st->d->samples_in_100ms
+ - (st->d->audio_data_frames % st->d->samples_in_100ms);
+ }
+ st->d->audio_data =
+ (double *) av_mallocz_array(st->d->audio_data_frames,
+ st->channels * sizeof(double));
+ CHECK_ERROR(!st->d->audio_data, 0, free_sample_peak)
+
+ ebur128_init_filter(st);
+
+ st->d->block_energy_histogram =
+ av_mallocz(1000 * sizeof(unsigned long));
+ CHECK_ERROR(!st->d->block_energy_histogram, 0, free_audio_data)
+ st->d->short_term_block_energy_histogram =
+ av_mallocz(1000 * sizeof(unsigned long));
+ CHECK_ERROR(!st->d->short_term_block_energy_histogram, 0,
+ free_block_energy_histogram)
+ st->d->short_term_frame_counter = 0;
+
+ /* the first block needs 400ms of audio data */
+ st->d->needed_frames = st->d->samples_in_100ms * 4;
+ /* start at the beginning of the buffer */
+ st->d->audio_data_index = 0;
+
+ if (ff_thread_once(&histogram_init, &init_histogram) != 0)
+ goto free_short_term_block_energy_histogram;
+
+ st->d->data_ptrs = av_malloc_array(channels, sizeof(void *));
+ CHECK_ERROR(!st->d->data_ptrs, 0,
+ free_short_term_block_energy_histogram);
+
+ return st;
+
+free_short_term_block_energy_histogram:
+ av_free(st->d->short_term_block_energy_histogram);
+free_block_energy_histogram:
+ av_free(st->d->block_energy_histogram);
+free_audio_data:
+ av_free(st->d->audio_data);
+free_sample_peak:
+ av_free(st->d->sample_peak);
+free_channel_map:
+ av_free(st->d->channel_map);
+free_internal:
+ av_free(st->d);
+free_state:
+ av_free(st);
+exit:
+ return NULL;
+}
+
+void ff_ebur128_destroy(FFEBUR128State ** st)
+{
+ av_free((*st)->d->block_energy_histogram);
+ av_free((*st)->d->short_term_block_energy_histogram);
+ av_free((*st)->d->audio_data);
+ av_free((*st)->d->channel_map);
+ av_free((*st)->d->sample_peak);
+ av_free((*st)->d->data_ptrs);
+ av_free((*st)->d);
+ av_free(*st);
+ *st = NULL;
+}
+
+#define EBUR128_FILTER(type, min_scale, max_scale) \
+static void ebur128_filter_##type(FFEBUR128State* st, const type** srcs, \
+ size_t src_index, size_t frames, \
+ int stride) { \
+ static double scaling_factor = -((double) min_scale) > (double) max_scale ? \
+ -((double) min_scale) : (double) max_scale; \
+ double* audio_data = st->d->audio_data + st->d->audio_data_index; \
+ size_t i, c; \
+ \
+ if ((st->mode & FF_EBUR128_MODE_SAMPLE_PEAK) == FF_EBUR128_MODE_SAMPLE_PEAK) { \
+ for (c = 0; c < st->channels; ++c) { \
+ double max = 0.0; \
+ for (i = 0; i < frames; ++i) { \
+ type v = srcs[c][src_index + i * stride]; \
+ if (v > max) { \
+ max = v; \
+ } else if (-v > max) { \
+ max = -1.0 * v; \
+ } \
+ } \
+ max /= scaling_factor; \
+ if (max > st->d->sample_peak[c]) st->d->sample_peak[c] = max; \
+ } \
+ } \
+ for (c = 0; c < st->channels; ++c) { \
+ int ci = st->d->channel_map[c] - 1; \
+ if (ci < 0) continue; \
+ else if (ci == FF_EBUR128_DUAL_MONO - 1) ci = 0; /*dual mono */ \
+ for (i = 0; i < frames; ++i) { \
+ st->d->v[ci][0] = (double) (srcs[c][src_index + i * stride] / scaling_factor) \
+ - st->d->a[1] * st->d->v[ci][1] \
+ - st->d->a[2] * st->d->v[ci][2] \
+ - st->d->a[3] * st->d->v[ci][3] \
+ - st->d->a[4] * st->d->v[ci][4]; \
+ audio_data[i * st->channels + c] = \
+ st->d->b[0] * st->d->v[ci][0] \
+ + st->d->b[1] * st->d->v[ci][1] \
+ + st->d->b[2] * st->d->v[ci][2] \
+ + st->d->b[3] * st->d->v[ci][3] \
+ + st->d->b[4] * st->d->v[ci][4]; \
+ st->d->v[ci][4] = st->d->v[ci][3]; \
+ st->d->v[ci][3] = st->d->v[ci][2]; \
+ st->d->v[ci][2] = st->d->v[ci][1]; \
+ st->d->v[ci][1] = st->d->v[ci][0]; \
+ } \
+ st->d->v[ci][4] = fabs(st->d->v[ci][4]) < DBL_MIN ? 0.0 : st->d->v[ci][4]; \
+ st->d->v[ci][3] = fabs(st->d->v[ci][3]) < DBL_MIN ? 0.0 : st->d->v[ci][3]; \
+ st->d->v[ci][2] = fabs(st->d->v[ci][2]) < DBL_MIN ? 0.0 : st->d->v[ci][2]; \
+ st->d->v[ci][1] = fabs(st->d->v[ci][1]) < DBL_MIN ? 0.0 : st->d->v[ci][1]; \
+ } \
+}
+EBUR128_FILTER(short, SHRT_MIN, SHRT_MAX)
+EBUR128_FILTER(int, INT_MIN, INT_MAX)
+EBUR128_FILTER(float, -1.0f, 1.0f) EBUR128_FILTER(double, -1.0, 1.0)
+
+static double ebur128_energy_to_loudness(double energy)
+{
+ return 10 * (log(energy) / log(10.0)) - 0.691;
+}
+
+static size_t find_histogram_index(double energy)
+{
+ size_t index_min = 0;
+ size_t index_max = 1000;
+ size_t index_mid;
+
+ do {
+ index_mid = (index_min + index_max) / 2;
+ if (energy >= histogram_energy_boundaries[index_mid]) {
+ index_min = index_mid;
+ } else {
+ index_max = index_mid;
+ }
+ } while (index_max - index_min != 1);
+
+ return index_min;
+}
+
+static void ebur128_calc_gating_block(FFEBUR128State * st,
+ size_t frames_per_block,
+ double *optional_output)
+{
+ size_t i, c;
+ double sum = 0.0;
+ double channel_sum;
+ for (c = 0; c < st->channels; ++c) {
+ if (st->d->channel_map[c] == FF_EBUR128_UNUSED)
+ continue;
+ channel_sum = 0.0;
+ if (st->d->audio_data_index < frames_per_block * st->channels) {
+ for (i = 0; i < st->d->audio_data_index / st->channels; ++i) {
+ channel_sum += st->d->audio_data[i * st->channels + c] *
+ st->d->audio_data[i * st->channels + c];
+ }
+ for (i = st->d->audio_data_frames -
+ (frames_per_block -
+ st->d->audio_data_index / st->channels);
+ i < st->d->audio_data_frames; ++i) {
+ channel_sum += st->d->audio_data[i * st->channels + c] *
+ st->d->audio_data[i * st->channels + c];
+ }
+ } else {
+ for (i =
+ st->d->audio_data_index / st->channels - frames_per_block;
+ i < st->d->audio_data_index / st->channels; ++i) {
+ channel_sum +=
+ st->d->audio_data[i * st->channels +
+ c] * st->d->audio_data[i *
+ st->channels +
+ c];
+ }
+ }
+ if (st->d->channel_map[c] == FF_EBUR128_Mp110 ||
+ st->d->channel_map[c] == FF_EBUR128_Mm110 ||
+ st->d->channel_map[c] == FF_EBUR128_Mp060 ||
+ st->d->channel_map[c] == FF_EBUR128_Mm060 ||
+ st->d->channel_map[c] == FF_EBUR128_Mp090 ||
+ st->d->channel_map[c] == FF_EBUR128_Mm090) {
+ channel_sum *= 1.41;
+ } else if (st->d->channel_map[c] == FF_EBUR128_DUAL_MONO) {
+ channel_sum *= 2.0;
+ }
+ sum += channel_sum;
+ }
+ sum /= (double) frames_per_block;
+ if (optional_output) {
+ *optional_output = sum;
+ } else if (sum >= histogram_energy_boundaries[0]) {
+ ++st->d->block_energy_histogram[find_histogram_index(sum)];
+ }
+}
+
+int ff_ebur128_set_channel(FFEBUR128State * st,
+ unsigned int channel_number, int value)
+{
+ if (channel_number >= st->channels) {
+ return 1;
+ }
+ if (value == FF_EBUR128_DUAL_MONO &&
+ (st->channels != 1 || channel_number != 0)) {
+ return 1;
+ }
+ st->d->channel_map[channel_number] = value;
+ return 0;
+}
+
+static int ebur128_energy_shortterm(FFEBUR128State * st, double *out);
+#define FF_EBUR128_ADD_FRAMES_PLANAR(type) \
+void ff_ebur128_add_frames_planar_##type(FFEBUR128State* st, const type** srcs, \
+ size_t frames, int stride) { \
+ size_t src_index = 0; \
+ while (frames > 0) { \
+ if (frames >= st->d->needed_frames) { \
+ ebur128_filter_##type(st, srcs, src_index, st->d->needed_frames, stride); \
+ src_index += st->d->needed_frames * stride; \
+ frames -= st->d->needed_frames; \
+ st->d->audio_data_index += st->d->needed_frames * st->channels; \
+ /* calculate the new gating block */ \
+ if ((st->mode & FF_EBUR128_MODE_I) == FF_EBUR128_MODE_I) { \
+ ebur128_calc_gating_block(st, st->d->samples_in_100ms * 4, NULL); \
+ } \
+ if ((st->mode & FF_EBUR128_MODE_LRA) == FF_EBUR128_MODE_LRA) { \
+ st->d->short_term_frame_counter += st->d->needed_frames; \
+ if (st->d->short_term_frame_counter == st->d->samples_in_100ms * 30) { \
+ double st_energy; \
+ ebur128_energy_shortterm(st, &st_energy); \
+ if (st_energy >= histogram_energy_boundaries[0]) { \
+ ++st->d->short_term_block_energy_histogram[ \
+ find_histogram_index(st_energy)]; \
+ } \
+ st->d->short_term_frame_counter = st->d->samples_in_100ms * 20; \
+ } \
+ } \
+ /* 100ms are needed for all blocks besides the first one */ \
+ st->d->needed_frames = st->d->samples_in_100ms; \
+ /* reset audio_data_index when buffer full */ \
+ if (st->d->audio_data_index == st->d->audio_data_frames * st->channels) { \
+ st->d->audio_data_index = 0; \
+ } \
+ } else { \
+ ebur128_filter_##type(st, srcs, src_index, frames, stride); \
+ st->d->audio_data_index += frames * st->channels; \
+ if ((st->mode & FF_EBUR128_MODE_LRA) == FF_EBUR128_MODE_LRA) { \
+ st->d->short_term_frame_counter += frames; \
+ } \
+ st->d->needed_frames -= frames; \
+ frames = 0; \
+ } \
+ } \
+}
+FF_EBUR128_ADD_FRAMES_PLANAR(short)
+FF_EBUR128_ADD_FRAMES_PLANAR(int)
+FF_EBUR128_ADD_FRAMES_PLANAR(float)
+FF_EBUR128_ADD_FRAMES_PLANAR(double)
+#define FF_EBUR128_ADD_FRAMES(type) \
+void ff_ebur128_add_frames_##type(FFEBUR128State* st, const type* src, \
+ size_t frames) { \
+ int i; \
+ const type **buf = (const type**)st->d->data_ptrs; \
+ for (i = 0; i < st->channels; i++) \
+ buf[i] = src + i; \
+ ff_ebur128_add_frames_planar_##type(st, buf, frames, st->channels); \
+}
+FF_EBUR128_ADD_FRAMES(short)
+FF_EBUR128_ADD_FRAMES(int)
+FF_EBUR128_ADD_FRAMES(float)
+FF_EBUR128_ADD_FRAMES(double)
+
+static int ebur128_calc_relative_threshold(FFEBUR128State * st,
+ size_t * above_thresh_counter,
+ double *relative_threshold)
+{
+ size_t i;
+ *relative_threshold = 0.0;
+ *above_thresh_counter = 0;
+
+ for (i = 0; i < 1000; ++i) {
+ *relative_threshold += st->d->block_energy_histogram[i] *
+ histogram_energies[i];
+ *above_thresh_counter += st->d->block_energy_histogram[i];
+ }
+
+ if (*above_thresh_counter != 0) {
+ *relative_threshold /= (double) *above_thresh_counter;
+ *relative_threshold *= RELATIVE_GATE_FACTOR;
+ }
+
+ return 0;
+}
+
+static int ebur128_gated_loudness(FFEBUR128State ** sts, size_t size,
+ double *out)
+{
+ double gated_loudness = 0.0;
+ double relative_threshold;
+ size_t above_thresh_counter;
+ size_t i, j, start_index;
+
+ for (i = 0; i < size; i++) {
+ if (sts[i]
+ && (sts[i]->mode & FF_EBUR128_MODE_I) != FF_EBUR128_MODE_I) {
+ return AVERROR(EINVAL);
+ }
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!sts[i])
+ continue;
+ ebur128_calc_relative_threshold(sts[i], &above_thresh_counter,
+ &relative_threshold);
+ }
+ if (!above_thresh_counter) {
+ *out = -HUGE_VAL;
+ return 0;
+ }
+
+ above_thresh_counter = 0;
+ if (relative_threshold < histogram_energy_boundaries[0]) {
+ start_index = 0;
+ } else {
+ start_index = find_histogram_index(relative_threshold);
+ if (relative_threshold > histogram_energies[start_index]) {
+ ++start_index;
+ }
+ }
+ for (i = 0; i < size; i++) {
+ if (!sts[i])
+ continue;
+ for (j = start_index; j < 1000; ++j) {
+ gated_loudness += sts[i]->d->block_energy_histogram[j] *
+ histogram_energies[j];
+ above_thresh_counter += sts[i]->d->block_energy_histogram[j];
+ }
+ }
+ if (!above_thresh_counter) {
+ *out = -HUGE_VAL;
+ return 0;
+ }
+ gated_loudness /= (double) above_thresh_counter;
+ *out = ebur128_energy_to_loudness(gated_loudness);
+ return 0;
+}
+
+int ff_ebur128_relative_threshold(FFEBUR128State * st, double *out)
+{
+ double relative_threshold;
+ size_t above_thresh_counter;
+
+ if (st && (st->mode & FF_EBUR128_MODE_I) != FF_EBUR128_MODE_I)
+ return AVERROR(EINVAL);
+
+ ebur128_calc_relative_threshold(st, &above_thresh_counter,
+ &relative_threshold);
+
+ if (!above_thresh_counter) {
+ *out = -70.0;
+ return 0;
+ }
+
+ *out = ebur128_energy_to_loudness(relative_threshold);
+ return 0;
+}
+
+int ff_ebur128_loudness_global(FFEBUR128State * st, double *out)
+{
+ return ebur128_gated_loudness(&st, 1, out);
+}
+
+int ff_ebur128_loudness_global_multiple(FFEBUR128State ** sts, size_t size,
+ double *out)
+{
+ return ebur128_gated_loudness(sts, size, out);
+}
+
+static int ebur128_energy_in_interval(FFEBUR128State * st,
+ size_t interval_frames, double *out)
+{
+ if (interval_frames > st->d->audio_data_frames) {
+ return AVERROR(EINVAL);
+ }
+ ebur128_calc_gating_block(st, interval_frames, out);
+ return 0;
+}
+
+static int ebur128_energy_shortterm(FFEBUR128State * st, double *out)
+{
+ return ebur128_energy_in_interval(st, st->d->samples_in_100ms * 30,
+ out);
+}
+
+int ff_ebur128_loudness_momentary(FFEBUR128State * st, double *out)
+{
+ double energy;
+ int error = ebur128_energy_in_interval(st, st->d->samples_in_100ms * 4,
+ &energy);
+ if (error) {
+ return error;
+ } else if (energy <= 0.0) {
+ *out = -HUGE_VAL;
+ return 0;
+ }
+ *out = ebur128_energy_to_loudness(energy);
+ return 0;
+}
+
+int ff_ebur128_loudness_shortterm(FFEBUR128State * st, double *out)
+{
+ double energy;
+ int error = ebur128_energy_shortterm(st, &energy);
+ if (error) {
+ return error;
+ } else if (energy <= 0.0) {
+ *out = -HUGE_VAL;
+ return 0;
+ }
+ *out = ebur128_energy_to_loudness(energy);
+ return 0;
+}
+
+int ff_ebur128_loudness_window(FFEBUR128State * st,
+ unsigned long window, double *out)
+{
+ double energy;
+ size_t interval_frames = st->samplerate * window / 1000;
+ int error = ebur128_energy_in_interval(st, interval_frames, &energy);
+ if (error) {
+ return error;
+ } else if (energy <= 0.0) {
+ *out = -HUGE_VAL;
+ return 0;
+ }
+ *out = ebur128_energy_to_loudness(energy);
+ return 0;
+}
+
+/* EBU - TECH 3342 */
+int ff_ebur128_loudness_range_multiple(FFEBUR128State ** sts, size_t size,
+ double *out)
+{
+ size_t i, j;
+ size_t stl_size;
+ double stl_power, stl_integrated;
+ /* High and low percentile energy */
+ double h_en, l_en;
+ unsigned long hist[1000] = { 0 };
+ size_t percentile_low, percentile_high;
+ size_t index;
+
+ for (i = 0; i < size; ++i) {
+ if (sts[i]) {
+ if ((sts[i]->mode & FF_EBUR128_MODE_LRA) !=
+ FF_EBUR128_MODE_LRA) {
+ return AVERROR(EINVAL);
+ }
+ }
+ }
+
+ stl_size = 0;
+ stl_power = 0.0;
+ for (i = 0; i < size; ++i) {
+ if (!sts[i])
+ continue;
+ for (j = 0; j < 1000; ++j) {
+ hist[j] += sts[i]->d->short_term_block_energy_histogram[j];
+ stl_size += sts[i]->d->short_term_block_energy_histogram[j];
+ stl_power += sts[i]->d->short_term_block_energy_histogram[j]
+ * histogram_energies[j];
+ }
+ }
+ if (!stl_size) {
+ *out = 0.0;
+ return 0;
+ }
+
+ stl_power /= stl_size;
+ stl_integrated = MINUS_20DB * stl_power;
+
+ if (stl_integrated < histogram_energy_boundaries[0]) {
+ index = 0;
+ } else {
+ index = find_histogram_index(stl_integrated);
+ if (stl_integrated > histogram_energies[index]) {
+ ++index;
+ }
+ }
+ stl_size = 0;
+ for (j = index; j < 1000; ++j) {
+ stl_size += hist[j];
+ }
+ if (!stl_size) {
+ *out = 0.0;
+ return 0;
+ }
+
+ percentile_low = (size_t) ((stl_size - 1) * 0.1 + 0.5);
+ percentile_high = (size_t) ((stl_size - 1) * 0.95 + 0.5);
+
+ stl_size = 0;
+ j = index;
+ while (stl_size <= percentile_low) {
+ stl_size += hist[j++];
+ }
+ l_en = histogram_energies[j - 1];
+ while (stl_size <= percentile_high) {
+ stl_size += hist[j++];
+ }
+ h_en = histogram_energies[j - 1];
+ *out =
+ ebur128_energy_to_loudness(h_en) -
+ ebur128_energy_to_loudness(l_en);
+ return 0;
+}
+
+int ff_ebur128_loudness_range(FFEBUR128State * st, double *out)
+{
+ return ff_ebur128_loudness_range_multiple(&st, 1, out);
+}
+
+int ff_ebur128_sample_peak(FFEBUR128State * st,
+ unsigned int channel_number, double *out)
+{
+ if ((st->mode & FF_EBUR128_MODE_SAMPLE_PEAK) !=
+ FF_EBUR128_MODE_SAMPLE_PEAK) {
+ return AVERROR(EINVAL);
+ } else if (channel_number >= st->channels) {
+ return AVERROR(EINVAL);
+ }
+ *out = st->d->sample_peak[channel_number];
+ return 0;
+}
diff --git a/libavfilter/ebur128.h b/libavfilter/ebur128.h
new file mode 100644
index 0000000000..c39c80d6ec
--- /dev/null
+++ b/libavfilter/ebur128.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2011 Jan Kokemüller
+ *
+ * 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
+ *
+ * This file is based on libebur128 which is available at
+ * https://github.com/jiixyj/libebur128/
+ *
+*/
+
+#ifndef AVFILTER_EBUR128_H
+#define AVFILTER_EBUR128_H
+
+/** \file ebur128.h
+ * \brief libebur128 - a library for loudness measurement according to
+ * the EBU R128 standard.
+ */
+
+#include <stddef.h> /* for size_t */
+
+/** \enum channel
+ * Use these values when setting the channel map with ebur128_set_channel().
+ * See definitions in ITU R-REC-BS 1770-4
+ */
+enum channel {
+ FF_EBUR128_UNUSED = 0, /**< unused channel (for example LFE channel) */
+ FF_EBUR128_LEFT,
+ FF_EBUR128_Mp030 = 1, /**< itu M+030 */
+ FF_EBUR128_RIGHT,
+ FF_EBUR128_Mm030 = 2, /**< itu M-030 */
+ FF_EBUR128_CENTER,
+ FF_EBUR128_Mp000 = 3, /**< itu M+000 */
+ FF_EBUR128_LEFT_SURROUND,
+ FF_EBUR128_Mp110 = 4, /**< itu M+110 */
+ FF_EBUR128_RIGHT_SURROUND,
+ FF_EBUR128_Mm110 = 5, /**< itu M-110 */
+ FF_EBUR128_DUAL_MONO, /**< a channel that is counted twice */
+ FF_EBUR128_MpSC, /**< itu M+SC */
+ FF_EBUR128_MmSC, /**< itu M-SC */
+ FF_EBUR128_Mp060, /**< itu M+060 */
+ FF_EBUR128_Mm060, /**< itu M-060 */
+ FF_EBUR128_Mp090, /**< itu M+090 */
+ FF_EBUR128_Mm090, /**< itu M-090 */
+ FF_EBUR128_Mp135, /**< itu M+135 */
+ FF_EBUR128_Mm135, /**< itu M-135 */
+ FF_EBUR128_Mp180, /**< itu M+180 */
+ FF_EBUR128_Up000, /**< itu U+000 */
+ FF_EBUR128_Up030, /**< itu U+030 */
+ FF_EBUR128_Um030, /**< itu U-030 */
+ FF_EBUR128_Up045, /**< itu U+045 */
+ FF_EBUR128_Um045, /**< itu U-030 */
+ FF_EBUR128_Up090, /**< itu U+090 */
+ FF_EBUR128_Um090, /**< itu U-090 */
+ FF_EBUR128_Up110, /**< itu U+110 */
+ FF_EBUR128_Um110, /**< itu U-110 */
+ FF_EBUR128_Up135, /**< itu U+135 */
+ FF_EBUR128_Um135, /**< itu U-135 */
+ FF_EBUR128_Up180, /**< itu U+180 */
+ FF_EBUR128_Tp000, /**< itu T+000 */
+ FF_EBUR128_Bp000, /**< itu B+000 */
+ FF_EBUR128_Bp045, /**< itu B+045 */
+ FF_EBUR128_Bm045 /**< itu B-045 */
+};
+
+/** \enum mode
+ * Use these values in ebur128_init (or'ed). Try to use the lowest possible
+ * modes that suit your needs, as performance will be better.
+ */
+enum mode {
+ /** can call ebur128_loudness_momentary */
+ FF_EBUR128_MODE_M = (1 << 0),
+ /** can call ebur128_loudness_shortterm */
+ FF_EBUR128_MODE_S = (1 << 1) | FF_EBUR128_MODE_M,
+ /** can call ebur128_loudness_global_* and ebur128_relative_threshold */
+ FF_EBUR128_MODE_I = (1 << 2) | FF_EBUR128_MODE_M,
+ /** can call ebur128_loudness_range */
+ FF_EBUR128_MODE_LRA = (1 << 3) | FF_EBUR128_MODE_S,
+ /** can call ebur128_sample_peak */
+ FF_EBUR128_MODE_SAMPLE_PEAK = (1 << 4) | FF_EBUR128_MODE_M,
+};
+
+/** forward declaration of FFEBUR128StateInternal */
+struct FFEBUR128StateInternal;
+
+/** \brief Contains information about the state of a loudness measurement.
+ *
+ * You should not need to modify this struct directly.
+ */
+typedef struct {
+ int mode; /**< The current mode. */
+ unsigned int channels; /**< The number of channels. */
+ unsigned long samplerate; /**< The sample rate. */
+ struct FFEBUR128StateInternal *d; /**< Internal state. */
+} FFEBUR128State;
+
+/** \brief Initialize library state.
+ *
+ * @param channels the number of channels.
+ * @param samplerate the sample rate.
+ * @param window set the maximum window size in ms, set to 0 for auto.
+ * @param mode see the mode enum for possible values.
+ * @return an initialized library state.
+ */
+FFEBUR128State *ff_ebur128_init(unsigned int channels,
+ unsigned long samplerate,
+ unsigned long window, int mode);
+
+/** \brief Destroy library state.
+ *
+ * @param st pointer to a library state.
+ */
+void ff_ebur128_destroy(FFEBUR128State ** st);
+
+/** \brief Set channel type.
+ *
+ * The default is:
+ * - 0 -> FF_EBUR128_LEFT
+ * - 1 -> FF_EBUR128_RIGHT
+ * - 2 -> FF_EBUR128_CENTER
+ * - 3 -> FF_EBUR128_UNUSED
+ * - 4 -> FF_EBUR128_LEFT_SURROUND
+ * - 5 -> FF_EBUR128_RIGHT_SURROUND
+ *
+ * @param st library state.
+ * @param channel_number zero based channel index.
+ * @param value channel type from the "channel" enum.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if invalid channel index.
+ */
+int ff_ebur128_set_channel(FFEBUR128State * st,
+ unsigned int channel_number, int value);
+
+/** \brief Add frames to be processed.
+ *
+ * @param st library state.
+ * @param src array of source frames. Channels must be interleaved.
+ * @param frames number of frames. Not number of samples!
+ */
+void ff_ebur128_add_frames_short(FFEBUR128State * st,
+ const short *src, size_t frames);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_frames_int(FFEBUR128State * st,
+ const int *src, size_t frames);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_frames_float(FFEBUR128State * st,
+ const float *src, size_t frames);
+/** \brief See \ref ebur128_add_frames_short */
+void ff_ebur128_add_frames_double(FFEBUR128State * st,
+ const double *src, size_t frames);
+
+/** \brief Add frames to be processed.
+ *
+ * @param st library state.
+ * @param srcs array of source frame channel data pointers
+ * @param frames number of frames. Not number of samples!
+ * @param stride number of samples to skip to for the next sample of the same channel
+ */
+void ff_ebur128_add_frames_planar_short(FFEBUR128State * st,
+ const short **srcs,
+ size_t frames, int stride);
+/** \brief See \ref ebur128_add_frames_planar_short */
+void ff_ebur128_add_frames_planar_int(FFEBUR128State * st,
+ const int **srcs,
+ size_t frames, int stride);
+/** \brief See \ref ebur128_add_frames_planar_short */
+void ff_ebur128_add_frames_planar_float(FFEBUR128State * st,
+ const float **srcs,
+ size_t frames, int stride);
+/** \brief See \ref ebur128_add_frames_planar_short */
+void ff_ebur128_add_frames_planar_double(FFEBUR128State * st,
+ const double **srcs,
+ size_t frames, int stride);
+
+/** \brief Get global integrated loudness in LUFS.
+ *
+ * @param st library state.
+ * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative
+ * infinity.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_I" has not been set.
+ */
+int ff_ebur128_loudness_global(FFEBUR128State * st, double *out);
+/** \brief Get global integrated loudness in LUFS across multiple instances.
+ *
+ * @param sts array of library states.
+ * @param size length of sts
+ * @param out integrated loudness in LUFS. -HUGE_VAL if result is negative
+ * infinity.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_I" has not been set.
+ */
+int ff_ebur128_loudness_global_multiple(FFEBUR128State ** sts,
+ size_t size, double *out);
+
+/** \brief Get momentary loudness (last 400ms) in LUFS.
+ *
+ * @param st library state.
+ * @param out momentary loudness in LUFS. -HUGE_VAL if result is negative
+ * infinity.
+ * @return
+ * - 0 on success.
+ */
+int ff_ebur128_loudness_momentary(FFEBUR128State * st, double *out);
+/** \brief Get short-term loudness (last 3s) in LUFS.
+ *
+ * @param st library state.
+ * @param out short-term loudness in LUFS. -HUGE_VAL if result is negative
+ * infinity.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_S" has not been set.
+ */
+int ff_ebur128_loudness_shortterm(FFEBUR128State * st, double *out);
+
+/** \brief Get loudness of the specified window in LUFS.
+ *
+ * window must not be larger than the current window set in st.
+ *
+ * @param st library state.
+ * @param window window in ms to calculate loudness.
+ * @param out loudness in LUFS. -HUGE_VAL if result is negative infinity.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if window larger than current window in st.
+ */
+int ff_ebur128_loudness_window(FFEBUR128State * st,
+ unsigned long window, double *out);
+
+/** \brief Get loudness range (LRA) of programme in LU.
+ *
+ * Calculates loudness range according to EBU 3342.
+ *
+ * @param st library state.
+ * @param out loudness range (LRA) in LU. Will not be changed in case of
+ * error. AVERROR(EINVAL) will be returned in this case.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_LRA" has not been set.
+ */
+int ff_ebur128_loudness_range(FFEBUR128State * st, double *out);
+/** \brief Get loudness range (LRA) in LU across multiple instances.
+ *
+ * Calculates loudness range according to EBU 3342.
+ *
+ * @param sts array of library states.
+ * @param size length of sts
+ * @param out loudness range (LRA) in LU. Will not be changed in case of
+ * error. AVERROR(EINVAL) will be returned in this case.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_LRA" has not been set.
+ */
+int ff_ebur128_loudness_range_multiple(FFEBUR128State ** sts,
+ size_t size, double *out);
+
+/** \brief Get maximum sample peak of selected channel in float format.
+ *
+ * @param st library state
+ * @param channel_number channel to analyse
+ * @param out maximum sample peak in float format (1.0 is 0 dBFS)
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_SAMPLE_PEAK" has not been set.
+ * - AVERROR(EINVAL) if invalid channel index.
+ */
+int ff_ebur128_sample_peak(FFEBUR128State * st,
+ unsigned int channel_number, double *out);
+
+/** \brief Get relative threshold in LUFS.
+ *
+ * @param st library state
+ * @param out relative threshold in LUFS.
+ * @return
+ * - 0 on success.
+ * - AVERROR(EINVAL) if mode "FF_EBUR128_MODE_I" has not been set.
+ */
+int ff_ebur128_relative_threshold(FFEBUR128State * st, double *out);
+
+#endif /* AVFILTER_EBUR128_H */
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 82a5f63377..b8c9b81125 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 66
+#define LIBAVFILTER_VERSION_MINOR 67
#define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \