Re: [FFmpeg-devel] [PATCH] avfilter/alimiter:add latency compensation
On Wed, May 11, 2022 at 12:45 AM Wang Cao wrote: > On Thu, May 5, 2022 at 2:14 PM Wang Cao wrote: > > > Also added 2 FATE tests to verify delay is compenated correctly > Applied with minor fixes and tests removed for now. > > > > Signed-off-by: Wang Cao > > --- > > 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); > >
Re: [FFmpeg-devel] [PATCH] avfilter/alimiter:add latency compensation
On Thu, May 5, 2022 at 2:14 PM Wang Cao wrote: > Also added 2 FATE tests to verify delay is compenated correctly > > Signed-off-by: Wang Cao > --- > 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) > +
[FFmpeg-devel] [PATCH] avfilter/alimiter:add latency compensation
Also added 2 FATE tests to verify delay is compenated correctly Signed-off-by: Wang Cao --- 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