From: Wang Cao <wangcao-at-google.com@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Subject: Re: [FFmpeg-devel] [PATCH] avfilter/alimiter:add latency compensation
Date: Tue, 10 May 2022 15:45:16 -0700
Message-ID: <CAL3k9=qPaAZcWG8oR46RX2kVQLKoTZUtzwKVORumqoK9FpY4PA@mail.gmail.com> (raw)
In-Reply-To: <20220505211416.671275-1-wangcao@google.com>
On Thu, May 5, 2022 at 2:14 PM Wang Cao <wangcao@google.com> wrote:
> Also added 2 FATE tests to verify delay is compenated correctly
>
> Signed-off-by: Wang Cao <wangcao@google.com>
> ---
> doc/filters.texi | 5 +++
> libavfilter/af_alimiter.c | 90 +++++++++++++++++++++++++++++++++++++
> tests/fate/filter-audio.mak | 24 ++++++++--
> 3 files changed, 116 insertions(+), 3 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index a161754233..75a43edd88 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 latency
> +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..01265758d7 100644
> --- a/libavfilter/af_alimiter.c
> +++ b/libavfilter/af_alimiter.c
> @@ -26,6 +26,7 @@
>
> #include "libavutil/channel_layout.h"
> #include "libavutil/common.h"
> +#include "libavutil/fifo.h"
> #include "libavutil/opt.h"
>
> #include "audio.h"
> @@ -33,6 +34,11 @@
> #include "formats.h"
> #include "internal.h"
>
> +typedef struct MetaItem {
> + int64_t pts;
> + int nb_samples;
> +} MetaItem;
> +
> typedef struct AudioLimiterContext {
> const AVClass *class;
>
> @@ -55,6 +61,14 @@ typedef struct AudioLimiterContext {
> int *nextpos;
> double *nextdelta;
>
> + int in_trim;
> + int out_pad;
> + int64_t next_in_pts;
> + int64_t next_out_pts;
> + int latency;
> +
> + AVFifo *fifo;
> +
> double delta;
> int nextiter;
> int nextlen;
> @@ -73,6 +87,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 },
> + { "latency", "compensate delay", OFFSET(latency),
> AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AF },
> { NULL }
> };
>
> @@ -129,6 +144,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame
> *in)
> AVFrame *out;
> double *buf;
> int n, c, i;
> + int new_out_samples;
> + int64_t out_duration;
> + int64_t in_duration;
> + int64_t in_pts;
> + MetaItem meta;
>
> if (av_frame_is_writable(in)) {
> out = in;
> @@ -269,12 +289,69 @@ static int filter_frame(AVFilterLink *inlink,
> AVFrame *in)
> dst += channels;
> }
>
> + in_duration = av_rescale_q(in->nb_samples, inlink->time_base,
> av_make_q(1, in->sample_rate));
> + in_pts = in->pts;
> + meta = (MetaItem){ in->pts, in->nb_samples };
> + av_fifo_write(s->fifo, &meta, 1);
> if (in != out)
> av_frame_free(&in);
>
> + new_out_samples = out->nb_samples;
> + if (s->in_trim > 0) {
> + int trim = FFMIN(new_out_samples, s->in_trim);
> + new_out_samples -= trim;
> + s->in_trim -= trim;
> + }
> +
> + if (new_out_samples <= 0) {
> + av_frame_free(&out);
> + return 0;
> + } else if (new_out_samples < out->nb_samples) {
> + int offset = out->nb_samples - new_out_samples;
> + memmove(out->extended_data[0], out->extended_data[0] +
> sizeof(double) * offset * out->ch_layout.nb_channels,
> + sizeof(double) * new_out_samples *
> out->ch_layout.nb_channels);
> + out->nb_samples = new_out_samples;
> + s->in_trim = 0;
> + }
> +
> + av_fifo_read(s->fifo, &meta, 1);
> +
> + out_duration = av_rescale_q(out->nb_samples, inlink->time_base,
> av_make_q(1, out->sample_rate));
> + in_duration = av_rescale_q(meta.nb_samples, inlink->time_base,
> av_make_q(1, out->sample_rate));
> + in_pts = meta.pts;
> +
> + if (s->next_out_pts != AV_NOPTS_VALUE && out->pts != s->next_out_pts
> &&
> + s->next_in_pts != AV_NOPTS_VALUE && in_pts == s->next_in_pts) {
> + out->pts = s->next_out_pts;
> + } else {
> + out->pts = in_pts;
> + }
> + s->next_in_pts = in_pts + in_duration;
> + s->next_out_pts = out->pts + out_duration;
> +
> return ff_filter_frame(outlink, out);
> }
>
> +static int request_frame(AVFilterLink* outlink)
> +{
> + AVFilterContext *ctx = outlink->src;
> + AudioLimiterContext *s = (AudioLimiterContext*)ctx->priv;
> + int ret;
> +
> + ret = ff_request_frame(ctx->inputs[0]);
> +
> + if (ret == AVERROR_EOF && s->out_pad > 0) {
> + AVFrame *frame = ff_get_audio_buffer(outlink, FFMIN(1024,
> s->out_pad));
> + if (!frame)
> + return AVERROR(ENOMEM);
> +
> + s->out_pad -= frame->nb_samples;
> + frame->pts = s->next_in_pts;
> + return filter_frame(ctx->inputs[0], frame);
> + }
> + return ret;
> +}
> +
> static int config_input(AVFilterLink *inlink)
> {
> AVFilterContext *ctx = inlink->dst;
> @@ -294,6 +371,16 @@ 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;
> + if (s->latency) {
> + s->in_trim = s->out_pad = s->buffer_size /
> inlink->ch_layout.nb_channels - 1;
> + }
> + s->next_out_pts = AV_NOPTS_VALUE;
> + s->next_in_pts = AV_NOPTS_VALUE;
> +
> + s->fifo = av_fifo_alloc2(8, sizeof(MetaItem), AV_FIFO_FLAG_AUTO_GROW);
> + if (!s->fifo) {
> + return AVERROR(ENOMEM);
> + }
>
> if (s->buffer_size <= 0) {
> av_log(ctx, AV_LOG_ERROR, "Attack is too small.\n");
> @@ -310,6 +397,8 @@ static av_cold void uninit(AVFilterContext *ctx)
> av_freep(&s->buffer);
> av_freep(&s->nextdelta);
> av_freep(&s->nextpos);
> +
> + av_fifo_freep2(&s->fifo);
> }
>
> static const AVFilterPad alimiter_inputs[] = {
> @@ -325,6 +414,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..e33ffdf37f 100644
> --- a/tests/fate/filter-audio.mak
> +++ b/tests/fate/filter-audio.mak
> @@ -63,11 +63,29 @@ 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
>
> -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
> +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_ALIMITER += fate-filter-alimiter-passthrough-default-attack
> +fate-filter-alimiter-passthrough-default-attack:
> tests/data/filter-alimiter-passthrough
> +fate-filter-alimiter-passthrough-default-attack: REF =
> $(TARGET_PATH)/tests/data/filter-alimiter-passthrough
> +fate-filter-alimiter-passthrough-default-attack: CMD = crc -i $(SRC) -af
> aresample,alimiter=level_in=1:level_out=1:limit=1:level=0:latency=1,aresample
> +
> +FATE_ALIMITER += fate-filter-alimiter-passthrough-large-attack
> +fate-filter-alimiter-passthrough-large-attack:
> tests/data/filter-alimiter-passthrough
> +fate-filter-alimiter-passthrough-large-attack: REF =
> $(TARGET_PATH)/tests/data/filter-alimiter-passthrough
> +fate-filter-alimiter-passthrough-large-attack: CMD = crc -i $(SRC) -af
> aresample,alimiter=level_in=1:level_out=1:limit=1:level=0:latency=1:attack=80,aresample
> +
> +FATE_ALIMITER += fate-filter-alimiter
> fate-filter-alimiter: CMD = framecrc -i $(SRC) -af
> aresample,alimiter=level_in=1:level_out=2:limit=0.2,aresample
>
> +$(FATE_ALIMITER): tests/data/asynth-44100-2.wav
> +$(FATE_ALIMITER): SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav
> +
> +FATE_AFILTER-$(call FILTERDEMDECENCMUX, ATRIM, WAV, PCM_S16LE, PCM_S16LE,
> WAV) += $(FATE_ALIMITER)
> +
> FATE_AFILTER-$(call FILTERDEMDECENCMUX, AMERGE, WAV, PCM_S16LE,
> PCM_S16LE, WAV) += fate-filter-amerge
> fate-filter-amerge: tests/data/asynth-44100-1.wav
> fate-filter-amerge: SRC = $(TARGET_PATH)/tests/data/asynth-44100-1.wav
> --
> 2.36.0.512.ge40c2bad7a-goog
>
> Hi folks, I don't know how FATE failed on the server but this specific
"make fate-filter-alimiter" and
"make tests/data/filter-alimiter-passthrough" passed on my local machine.
The code is basically referenced from af_ladspa. Can you advice on this
implementation? Thanks!
--
Wang Cao | Software Engineer | wangcao@google.com | 650-203-7807
_______________________________________________
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".
next prev parent reply other threads:[~2022-05-10 22:45 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 ` [FFmpeg-devel] [PATCH] avfilter/alimiter: Add an option "comp_delay" that removes the delay introduced by lookahead buffer Wang Cao
2022-04-29 20:48 ` 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 [this message]
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='CAL3k9=qPaAZcWG8oR46RX2kVQLKoTZUtzwKVORumqoK9FpY4PA@mail.gmail.com' \
--to=wangcao-at-google.com@ffmpeg.org \
--cc=ffmpeg-devel@ffmpeg.org \
/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