@@ -223,8 +223,6 @@ External library support:
--enable-libcdio enable audio CD grabbing with libcdio [no]
--enable-libdc1394 enable IIDC-1394 grabbing using libdc1394
and libraw1394 [no]
- --enable-libebur128 enable libebur128 for EBU R128 measurement,
- needed for loudnorm filter [no]
--enable-libfdk-aac enable AAC de/encoding via libfdk-aac [no]
--enable-libflite enable flite (voice synthesis) support via libflite [no]
--enable-libfontconfig enable libfontconfig, useful for drawtext filter [no]
@@ -1486,7 +1484,6 @@ EXTERNAL_LIBRARY_LIST="
libcdio
libcelt
libdc1394
- libebur128
libfdk_aac
libflite
libfontconfig
@@ -3051,7 +3048,6 @@ hqdn3d_filter_deps="gpl"
interlace_filter_deps="gpl"
kerndeint_filter_deps="gpl"
ladspa_filter_deps="ladspa dlopen"
-loudnorm_filter_deps="libebur128"
mcdeint_filter_deps="avcodec gpl"
movie_filter_deps="avcodec avformat"
mpdecimate_filter_deps="gpl"
@@ -5690,7 +5686,6 @@ enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 &&
{ check_lib celt/celt.h celt_decoder_create_custom -lcelt0 ||
die "ERROR: libcelt must be installed and version must be >= 0.11.0."; }
enabled libcaca && require_pkg_config caca caca.h caca_create_canvas
-enabled libebur128 && require ebur128 ebur128.h ebur128_relative_threshold -lebur128
enabled libfdk_aac && { use_pkg_config fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen ||
{ require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac &&
warn "using libfdk without pkg-config"; } }
@@ -2917,9 +2917,6 @@ EBU R128 loudness normalization. Includes both dynamic and linear normalization
Support for both single pass (livestreams, files) and double pass (files) modes.
This algorithm can target IL, LRA, and maximum true peak.
-To enable compilation of this filter you need to configure FFmpeg with
-@code{--enable-libebur128}.
-
The filter accepts the following options:
@table @option
@@ -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
@@ -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);
new file mode 100644
@@ -0,0 +1,782 @@
+/*
+ * 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 FF_EBUR128_ERROR_NOMEM;
+ 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 FF_EBUR128_SUCCESS;
+}
+
+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,
+ int 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 FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_ERROR_INVALID_MODE;
+ }
+ }
+
+ 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 FF_EBUR128_SUCCESS;
+ }
+
+ 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 FF_EBUR128_SUCCESS;
+ }
+ gated_loudness /= (double) above_thresh_counter;
+ *out = ebur128_energy_to_loudness(gated_loudness);
+ return FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_ERROR_INVALID_MODE;
+
+ ebur128_calc_relative_threshold(st, &above_thresh_counter,
+ &relative_threshold);
+
+ if (!above_thresh_counter) {
+ *out = -70.0;
+ return FF_EBUR128_SUCCESS;
+ }
+
+ *out = ebur128_energy_to_loudness(relative_threshold);
+ return FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_ERROR_INVALID_MODE;
+ }
+ ebur128_calc_gating_block(st, interval_frames, out);
+ return FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_SUCCESS;
+ }
+ *out = ebur128_energy_to_loudness(energy);
+ return FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_SUCCESS;
+ }
+ *out = ebur128_energy_to_loudness(energy);
+ return FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_SUCCESS;
+ }
+ *out = ebur128_energy_to_loudness(energy);
+ return FF_EBUR128_SUCCESS;
+}
+
+/* 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 FF_EBUR128_ERROR_INVALID_MODE;
+ }
+ }
+ }
+
+ 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 FF_EBUR128_SUCCESS;
+ }
+
+ 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 FF_EBUR128_SUCCESS;
+ }
+
+ 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 FF_EBUR128_SUCCESS;
+}
+
+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 FF_EBUR128_ERROR_INVALID_MODE;
+ } else if (channel_number >= st->channels) {
+ return FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX;
+ }
+ *out = st->d->sample_peak[channel_number];
+ return FF_EBUR128_SUCCESS;
+}
new file mode 100644
@@ -0,0 +1,313 @@
+/*
+ * 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 error
+ * Error return values.
+ */
+enum error {
+ FF_EBUR128_SUCCESS = 0,
+ FF_EBUR128_ERROR_NOMEM,
+ FF_EBUR128_ERROR_INVALID_MODE,
+ FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX,
+};
+
+/** \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,
+ int 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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX 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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_MODE 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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_MODE 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
+ * - FF_EBUR128_SUCCESS 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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_MODE 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.
+ * The current window can be changed by calling ebur128_set_max_window().
+ *
+ * @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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_MODE 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. FF_EBUR128_ERROR_NOMEM or FF_EBUR128_ERROR_INVALID_MODE will be
+ * returned in this case.
+ * @return
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_NOMEM in case of memory allocation error.
+ * - FF_EBUR128_ERROR_INVALID_MODE 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. FF_EBUR128_ERROR_NOMEM or FF_EBUR128_ERROR_INVALID_MODE will be
+ * returned in this case.
+ * @return
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_NOMEM in case of memory allocation error.
+ * - FF_EBUR128_ERROR_INVALID_MODE 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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_SAMPLE_PEAK" has not
+ * been set.
+ * - FF_EBUR128_ERROR_INVALID_CHANNEL_INDEX 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
+ * - FF_EBUR128_SUCCESS on success.
+ * - FF_EBUR128_ERROR_INVALID_MODE if mode "FF_EBUR128_MODE_I" has not
+ * been set.
+ */
+int ff_ebur128_relative_threshold(FFEBUR128State * st, double *out);
+
+#endif /* AVFILTER_EBUR128_H */
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_audnorm 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 - fix indentation Signed-off-by: Marton Balint <cus@passwd.hu> --- configure | 5 - doc/filters.texi | 3 - libavfilter/Makefile | 2 +- libavfilter/af_loudnorm.c | 60 ++-- libavfilter/ebur128.c | 782 ++++++++++++++++++++++++++++++++++++++++++++++ libavfilter/ebur128.h | 313 +++++++++++++++++++ 6 files changed, 1126 insertions(+), 39 deletions(-) create mode 100644 libavfilter/ebur128.c create mode 100644 libavfilter/ebur128.h