Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Wang Cao <wangcao-at-google.com@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Cc: Wang Cao <wangcao@google.com>
Subject: [FFmpeg-devel] [PATCH] avfilter/alimiter: Add an option "comp_delay" that removes the delay introduced by lookahead buffer
Date: Fri, 15 Apr 2022 11:49:35 -0700
Message-ID: <20220415184934.1891758-1-wangcao@google.com> (raw)
In-Reply-To: <CAPYw7P7RWeQ0oO=RYrsRUVK6=m9-x-kZW-DXg=g+fzYG=Vd+3g@mail.gmail.com>

1. The option also flushes all the valid audio samples in the lookahead
   buffer so the audio integrity is preserved. Previously the the output
   audio will lose the amount of audio samples equal to the size of
   lookahead buffer
2. Add a FATE test to verify that when the filter is working as
   passthrough filter, all audio samples are properly handled from the
   input to the output.

Signed-off-by: Wang Cao <wangcao@google.com>
---
 doc/filters.texi            |  5 +++
 libavfilter/af_alimiter.c   | 74 +++++++++++++++++++++++++++++++++++++
 tests/fate/filter-audio.mak | 12 ++++++
 3 files changed, 91 insertions(+)

diff --git a/doc/filters.texi b/doc/filters.texi
index a161754233..2af0953c89 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -1978,6 +1978,11 @@ in release time while 1 produces higher release times.
 @item level
 Auto level output signal. Default is enabled.
 This normalizes audio back to 0dB if enabled.
+
+@item comp_delay
+Compensate the delay introduced by using the lookahead buffer set with attack
+parameter. Also flush the valid audio data in the lookahead buffer when the 
+stream hits EOF.
 @end table
 
 Depending on picked setting it is recommended to upsample input 2x or 4x times
diff --git a/libavfilter/af_alimiter.c b/libavfilter/af_alimiter.c
index 133f98f165..d10a90859b 100644
--- a/libavfilter/af_alimiter.c
+++ b/libavfilter/af_alimiter.c
@@ -55,6 +55,12 @@ typedef struct AudioLimiterContext {
     int *nextpos;
     double *nextdelta;
 
+    int lookahead_delay_samples;
+    int lookahead_flush_samples;
+    int64_t output_pts;
+    int64_t next_output_pts;
+    int comp_delay;
+
     double delta;
     int nextiter;
     int nextlen;
@@ -73,6 +79,7 @@ static const AVOption alimiter_options[] = {
     { "asc",       "enable asc",       OFFSET(auto_release), AV_OPT_TYPE_BOOL,   {.i64=0},      0,    1, AF },
     { "asc_level", "set asc level",    OFFSET(asc_coeff),    AV_OPT_TYPE_DOUBLE, {.dbl=0.5},    0,    1, AF },
     { "level",     "auto level",       OFFSET(auto_level),   AV_OPT_TYPE_BOOL,   {.i64=1},      0,    1, AF },
+    { "comp_delay","compensate delay", OFFSET(comp_delay),   AV_OPT_TYPE_BOOL,   {.i64=0},      0,    1, AF },
     { NULL }
 };
 
@@ -129,6 +136,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     AVFrame *out;
     double *buf;
     int n, c, i;
+    int num_output_samples = in->nb_samples;
+    int trim_offset;
 
     if (av_frame_is_writable(in)) {
         out = in;
@@ -271,10 +280,71 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
     if (in != out)
         av_frame_free(&in);
+    
+    if (!s->comp_delay) {
+        return ff_filter_frame(outlink, out);
+    }
+
+    if (s->output_pts == AV_NOPTS_VALUE) {
+        s->output_pts = in->pts;
+    }
+
+    if (s->lookahead_delay_samples > 0) {
+        // The current output frame is completely silence
+        if (s->lookahead_delay_samples >= in->nb_samples) {
+            s->lookahead_delay_samples -= in->nb_samples;
+            return 0;
+        }
+
+        // Trim the silence part
+        trim_offset = av_samples_get_buffer_size(
+            NULL, inlink->ch_layout.nb_channels, s->lookahead_delay_samples,
+            (enum AVSampleFormat)out->format, 1);
+        out->data[0] += trim_offset;
+        out->nb_samples = in->nb_samples - s->lookahead_delay_samples;
+        s->lookahead_delay_samples = 0;
+        num_output_samples = out->nb_samples;
+    }
+
+    if (s->lookahead_delay_samples < 0) {
+        return AVERROR_BUG;
+    }
+
+    out->pts = s->output_pts;
+    s->next_output_pts = s->output_pts + num_output_samples;
+    s->output_pts = s->next_output_pts;

     return ff_filter_frame(outlink, out);
 }

+static int request_frame(AVFilterLink* outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    AudioLimiterContext *s = (AudioLimiterContext*)ctx->priv;
+    int ret;
+    AVFilterLink *inlink;
+    AVFrame *silence_frame;
+
+    ret = ff_request_frame(ctx->inputs[0]);
+
+    if (ret != AVERROR_EOF || s->lookahead_flush_samples == 0 || !s->comp_delay) {
+      // Not necessarily an error, just not EOF.
+      return ret;
+    }
+
+    // We reach here when input filters have finished producing data (i.e. EOF),
+    // but because of the attack param, s->buffer still has meaningful
+    // audio content that needs flushing.
+    inlink = ctx->inputs[0];
+    // Pushes silence frame to flush valid audio in the s->buffer
+    silence_frame = ff_get_audio_buffer(inlink, s->lookahead_flush_samples);
+    ret = filter_frame(inlink, silence_frame);
+    if (ret < 0) {
+      return ret;
+    }
+    return AVERROR_EOF;
+}
+
 static int config_input(AVFilterLink *inlink)
 {
     AVFilterContext *ctx = inlink->dst;
@@ -294,6 +364,9 @@ static int config_input(AVFilterLink *inlink)
     memset(s->nextpos, -1, obuffer_size * sizeof(*s->nextpos));
     s->buffer_size = inlink->sample_rate * s->attack * inlink->ch_layout.nb_channels;
     s->buffer_size -= s->buffer_size % inlink->ch_layout.nb_channels;
+    // the current logic outputs the next sample from the lookahead buffer from the beginning so the amount of delay
+    // compensation is less than the lookahead buffer size by 1 .
+    s->lookahead_delay_samples = s->lookahead_flush_samples = s->buffer_size / inlink->ch_layout.nb_channels - 1;
 
     if (s->buffer_size <= 0) {
         av_log(ctx, AV_LOG_ERROR, "Attack is too small.\n");
@@ -325,6 +398,7 @@ static const AVFilterPad alimiter_outputs[] = {
     {
         .name = "default",
         .type = AVMEDIA_TYPE_AUDIO,
+        .request_frame = request_frame,
     },
 };

diff --git a/tests/fate/filter-audio.mak b/tests/fate/filter-audio.mak
index eff32b9f81..3a51ca18a6 100644
--- a/tests/fate/filter-audio.mak
+++ b/tests/fate/filter-audio.mak
@@ -63,6 +63,18 @@ fate-filter-agate: tests/data/asynth-44100-2.wav
 fate-filter-agate: SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav
 fate-filter-agate: CMD = framecrc -i $(SRC) -af aresample,agate=level_in=10:range=0:threshold=1:ratio=1:attack=1:knee=1:makeup=4,aresample

+tests/data/filter-alimiter-passthrough: TAG = GEN
+tests/data/filter-alimiter-passthrough: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data
+	$(M)$(TARGET_EXEC) $(TARGET_PATH)/$< -nostdin \
+	-i $(TARGET_PATH)/tests/data/asynth-44100-2.wav -af aresample -f crc $(TARGET_PATH)/$@ -y 2>/dev/null
+
+FATE_AFILTER-$(call FILTERDEMDECENCMUX, AFADE, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-alimiter-passthrough
+fate-filter-alimiter-passthrough: tests/data/asynth-44100-2.wav
+fate-filter-alimiter-passthrough: tests/data/filter-alimiter-passthrough
+fate-filter-alimiter-passthrough: SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav
+fate-filter-alimiter-passthrough: REF = $(TARGET_PATH)/tests/data/filter-alimiter-passthrough
+fate-filter-alimiter-passthrough: CMD = crc -i $(SRC) -af aresample,alimiter=level_in=1:level_out=1:limit=1:level=0:comp_delay=1,aresample
+
 FATE_AFILTER-$(call FILTERDEMDECENCMUX, AFADE, WAV, PCM_S16LE, PCM_S16LE, WAV) += fate-filter-alimiter
 fate-filter-alimiter: tests/data/asynth-44100-2.wav
 fate-filter-alimiter: SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav
--
2.36.0.rc0.470.gd361397f0d-goog

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

  parent reply	other threads:[~2022-04-15 18:50 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-27  6:08 [FFmpeg-devel] [PATCH] avfilter/alimiter: Add "flush_buffer" option to flush the remaining valid data to the output Wang Cao
2022-03-27 21:40 ` Marton Balint
2022-03-30 21:03   ` [FFmpeg-devel] [PATCH] avfilter/alimiter: Remove the delay introduced by lookahead buffer Wang Cao
2022-04-04 15:46     ` Paul B Mahol
2022-04-04 15:49   ` [FFmpeg-devel] [PATCH] avfilter/alimiter: Add "flush_buffer" option to flush the remaining valid data to the output Paul B Mahol
2022-04-04 22:28     ` Marton Balint
2022-04-05 18:56       ` Wang Cao
2022-04-06 11:49         ` Paul B Mahol
2022-04-07  7:46           ` Paul B Mahol
2022-04-07 21:56             ` Wang Cao
2022-04-08 18:42               ` Paul B Mahol
2022-04-08 20:41                 ` Wang Cao
2022-04-09 12:37                   ` Paul B Mahol
2022-04-11 20:59                     ` Wang Cao
2022-04-12 19:42                       ` Paul B Mahol
2022-04-13  4:11                         ` Wang Cao
2022-04-15 18:49                         ` Wang Cao [this message]
2022-04-29 20:48                           ` [FFmpeg-devel] [PATCH] avfilter/alimiter: Add an option "comp_delay" that removes the delay introduced by lookahead buffer Wang Cao
2022-05-02  8:26                             ` Paul B Mahol
2022-05-05 21:14                               ` [FFmpeg-devel] [PATCH] avfilter/alimiter:add latency compensation Wang Cao
2022-05-10 22:45                                 ` Wang Cao
2022-05-12 12:05                                   ` Paul B Mahol
2022-05-05 21:17                               ` [FFmpeg-devel] [PATCH] avfilter/alimiter: Add an option "comp_delay" that removes the delay introduced by lookahead buffer Wang Cao
2022-04-15 18:51                         ` [FFmpeg-devel] [PATCH] avfilter/alimiter: Add "flush_buffer" option to flush the remaining valid data to the output Wang Cao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220415184934.1891758-1-wangcao@google.com \
    --to=wangcao-at-google.com@ffmpeg.org \
    --cc=ffmpeg-devel@ffmpeg.org \
    --cc=wangcao@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git