From patchwork Thu Mar 4 15:05:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 26087 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 9EBBB449C91 for ; Thu, 4 Mar 2021 17:06:10 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 6C875680C9A; Thu, 4 Mar 2021 17:06:10 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2DC60680C9A for ; Thu, 4 Mar 2021 17:06:04 +0200 (EET) Received: by mail-ej1-f54.google.com with SMTP id mm21so49677648ejb.12 for ; Thu, 04 Mar 2021 07:06:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=TMD+DmXCMSDXJy6EPeEu05H9soOZDAV53akvwd0nqaE=; b=QbabOv3DUzHwd3zZrmAx2224c/WeIaT2/Cr0sdqEGAvYYMOqgvwgqJt56RBefztbqC krMXXuKBAZkYqr9OIx+B9FP44sKNG5sV8kG+cID/bSG8gau6I6ntf6FN/LbcY2cafq8v G6SkFeFHsybu2Ip+nfqoFOjoqP0ScpK71kQ5BoxVs51mjNzUZaTsDDswDphQZEYp17Pt YPwcDtwQUosbmd9rw/pDz9730634Hlq6hd5CvQb3A0zfepdAXullxNslUPeCUoQAGwE/ 14zE/XscwSkDVDi8StsxF7neOQo1eZUA3vohaiXaDfVBThNkJW/N4zcq72EGb0MyRy1h jjAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=TMD+DmXCMSDXJy6EPeEu05H9soOZDAV53akvwd0nqaE=; b=Ujm5JWar0NFRd021nT6sL6eXxofVb5yIEqldeual4nDolpXu8jAyRZpu8fQAM62cP6 ubCcucwxZL8p9oIJOZDFi52K/TQAl33SH5/K70/uMAkpDDG5lLhuXkQNRbOx734PdOt9 qDa1nKHfSQu9nxOrd2tAxC8g8Br56YgjmonxEVpfO3HnEAPxCVnXQzyklp8pyIhOd5WK 7Bl3J/sX0t9QDlb5ye76aOaC9urDE7Jwp+WZLXDXFyTZEg5Y5xQq6Ih+V2Ay8r3jQDrO rx8s7uVMAy0D1wEsGhXZ6o1Ov1hWff1HJ8cvRiCtgyIOsHP6ECG9ugf5QlDCj4NhTqYG TnoQ== X-Gm-Message-State: AOAM531b+h6U3MGCn0cyhivQO3kIxSEtozojyuKkxEjrTs0DTeo6pS5E 0ELvb5ItnLa2qMeBW3khBcijOrvLmhRk0w== X-Google-Smtp-Source: ABdhPJzvHtP88hpUu8RndHatGrC2Xt7zPApc6I0DFPyF42esda34Jg4y4GvB0rpIOBRzhOalMr0Psg== X-Received: by 2002:a17:906:23e9:: with SMTP id j9mr4621812ejg.78.1614870363532; Thu, 04 Mar 2021 07:06:03 -0800 (PST) Received: from localhost.localdomain ([94.250.185.123]) by smtp.gmail.com with ESMTPSA id bt14sm11005edb.92.2021.03.04.07.06.02 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Mar 2021 07:06:03 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Thu, 4 Mar 2021 16:05:53 +0100 Message-Id: <20210304150553.8945-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH] avfilter/f_ebur128: add all sample rates support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- libavfilter/f_ebur128.c | 73 ++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c index e81520838d..55913bac1d 100644 --- a/libavfilter/f_ebur128.c +++ b/libavfilter/f_ebur128.c @@ -24,7 +24,6 @@ * @see http://tech.ebu.ch/loudness * @see https://www.youtube.com/watch?v=iuEtQqC-Sqo "EBU R128 Introduction - Florian Camerer" * @todo implement start/stop/reset through filter command injection - * @todo support other frequencies to avoid resampling */ #include @@ -45,20 +44,6 @@ #define MAX_CHANNELS 63 -/* pre-filter coefficients */ -#define PRE_B0 1.53512485958697 -#define PRE_B1 -2.69169618940638 -#define PRE_B2 1.19839281085285 -#define PRE_A1 -1.69065929318241 -#define PRE_A2 0.73248077421585 - -/* RLB-filter coefficients */ -#define RLB_B0 1.0 -#define RLB_B1 -2.0 -#define RLB_B2 1.0 -#define RLB_A1 -1.99004745483398 -#define RLB_A2 0.99007225036621 - #define ABS_THRES -70 ///< silence gate: we discard anything below this absolute (LUFS) threshold #define ABS_UP_THRES 10 ///< upper loud limit to consider (ABS_THRES being the minimum) #define HIST_GRAIN 100 ///< defines histogram precision @@ -80,6 +65,7 @@ struct hist_entry { struct integrator { double *cache[MAX_CHANNELS]; ///< window of filtered samples (N ms) int cache_pos; ///< focus on the last added bin in the cache array + int cache_size; double sum[MAX_CHANNELS]; ///< sum of the last N ms filtered samples (cache content) int filled; ///< 1 if the cache is completely filled, 0 otherwise double rel_threshold; ///< relative threshold @@ -128,9 +114,11 @@ typedef struct EBUR128Context { double x[MAX_CHANNELS * 3]; ///< 3 input samples cache for each channel double y[MAX_CHANNELS * 3]; ///< 3 pre-filter samples cache for each channel double z[MAX_CHANNELS * 3]; ///< 3 RLB-filter samples cache for each channel + double pre_b[3]; ///< pre-filter numerator coefficients + double pre_a[3]; ///< pre-filter denominator coefficients + double rlb_b[3]; ///< rlb-filter numerator coefficients + double rlb_a[3]; ///< rlb-filter denominator coefficients -#define I400_BINS (48000 * 4 / 10) -#define I3000_BINS (48000 * 3) struct integrator i400; ///< 400ms integrator, used for Momentary loudness (M), and Integrated loudness (I) struct integrator i3000; ///< 3s integrator, used for Short term loudness (S), and Loudness Range (LRA) @@ -388,6 +376,32 @@ static int config_audio_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; EBUR128Context *ebur128 = ctx->priv; + double f0 = 1681.974450955533; + double G = 3.999843853973347; + double Q = 0.7071752369554196; + + double K = tan(M_PI * f0 / (double)inlink->sample_rate); + double Vh = pow(10.0, G / 20.0); + double Vb = pow(Vh, 0.4996667741545416); + + double a0 = 1.0 + K / Q + K * K; + + ebur128->pre_b[0] = (Vh + Vb * K / Q + K * K) / a0; + ebur128->pre_b[1] = 2.0 * (K * K - Vh) / a0; + ebur128->pre_b[2] = (Vh - Vb * K / Q + K * K) / a0; + ebur128->pre_a[1] = 2.0 * (K * K - 1.0) / a0; + ebur128->pre_a[2] = (1.0 - K / Q + K * K) / a0; + + f0 = 38.13547087602444; + Q = 0.5003270373238773; + K = tan(M_PI * f0 / (double)inlink->sample_rate); + + ebur128->rlb_b[0] = 1.0; + ebur128->rlb_b[1] = -2.0; + ebur128->rlb_b[2] = 1.0; + ebur128->rlb_a[1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K); + ebur128->rlb_a[2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K); + /* Force 100ms framing in case of metadata injection: the frames must have * a granularity of the window overlap to be accurately exploited. * As for the true peaks mode, it just simplifies the resampling buffer @@ -418,6 +432,9 @@ static int config_audio_output(AVFilterLink *outlink) if (!ebur128->ch_weighting) return AVERROR(ENOMEM); +#define I400_BINS (outlink->sample_rate * 4 / 10) +#define I3000_BINS (outlink->sample_rate * 3) + for (i = 0; i < nb_channels; i++) { /* channel weighting */ const uint64_t chl = av_channel_layout_extract_channel(outlink->channel_layout, i); @@ -433,8 +450,10 @@ static int config_audio_output(AVFilterLink *outlink) continue; /* bins buffer for the two integration window (400ms and 3s) */ - ebur128->i400.cache[i] = av_calloc(I400_BINS, sizeof(*ebur128->i400.cache[0])); - ebur128->i3000.cache[i] = av_calloc(I3000_BINS, sizeof(*ebur128->i3000.cache[0])); + ebur128->i400.cache_size = I400_BINS; + ebur128->i3000.cache_size = I3000_BINS; + ebur128->i400.cache[i] = av_calloc(ebur128->i400.cache_size, sizeof(*ebur128->i400.cache[0])); + ebur128->i3000.cache[i] = av_calloc(ebur128->i3000.cache_size, sizeof(*ebur128->i3000.cache[0])); if (!ebur128->i400.cache[i] || !ebur128->i3000.cache[i]) return AVERROR(ENOMEM); } @@ -613,7 +632,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) #define MOVE_TO_NEXT_CACHED_ENTRY(time) do { \ ebur128->i##time.cache_pos++; \ - if (ebur128->i##time.cache_pos == I##time##_BINS) { \ + if (ebur128->i##time.cache_pos == \ + ebur128->i##time.cache_size) { \ ebur128->i##time.filled = 1; \ ebur128->i##time.cache_pos = 0; \ } \ @@ -634,20 +654,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) continue; /* Y[i] = X[i]*b0 + X[i-1]*b1 + X[i-2]*b2 - Y[i-1]*a1 - Y[i-2]*a2 */ -#define FILTER(Y, X, name) do { \ +#define FILTER(Y, X, NUM, DEN) do { \ double *dst = ebur128->Y + ch*3; \ double *src = ebur128->X + ch*3; \ dst[2] = dst[1]; \ dst[1] = dst[0]; \ - dst[0] = src[0]*name##_B0 + src[1]*name##_B1 + src[2]*name##_B2 \ - - dst[1]*name##_A1 - dst[2]*name##_A2; \ + dst[0] = src[0]*NUM[0] + src[1]*NUM[1] + src[2]*NUM[2] \ + - dst[1]*DEN[1] - dst[2]*DEN[2]; \ } while (0) // TODO: merge both filters in one? - FILTER(y, x, PRE); // apply pre-filter + FILTER(y, x, ebur128->pre_b, ebur128->pre_a); // apply pre-filter ebur128->x[ch * 3 + 2] = ebur128->x[ch * 3 + 1]; ebur128->x[ch * 3 + 1] = ebur128->x[ch * 3 ]; - FILTER(z, y, RLB); // apply RLB-filter + FILTER(z, y, ebur128->rlb_b, ebur128->rlb_a); // apply RLB-filter bin = ebur128->z[ch * 3] * ebur128->z[ch * 3]; @@ -896,7 +916,6 @@ static int query_formats(AVFilterContext *ctx) int ret; static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_NONE }; - static const int input_srate[] = {48000, -1}; // ITU-R BS.1770 provides coeff only for 48kHz static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE }; /* set optional output video format */ @@ -920,7 +939,7 @@ static int query_formats(AVFilterContext *ctx) (ret = ff_channel_layouts_ref(layouts, &outlink->incfg.channel_layouts)) < 0) return ret; - formats = ff_make_format_list(input_srate); + formats = ff_all_samplerates(); if ((ret = ff_formats_ref(formats, &inlink->outcfg.samplerates)) < 0 || (ret = ff_formats_ref(formats, &outlink->incfg.samplerates)) < 0) return ret;