diff mbox

[FFmpeg-devel,2/3] avfilter: add audio upsample filter

Message ID 20190418211734.11300-2-onemda@gmail.com
State New
Headers show

Commit Message

Paul B Mahol April 18, 2019, 9:17 p.m. UTC
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 libavfilter/Makefile       |   1 +
 libavfilter/af_aupsample.c | 159 +++++++++++++++++++++++++++++++++++++
 libavfilter/allfilters.c   |   1 +
 3 files changed, 161 insertions(+)
 create mode 100644 libavfilter/af_aupsample.c

Comments

Carl Eugen Hoyos April 18, 2019, 11:08 p.m. UTC | #1
2019-04-18 23:17 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  libavfilter/Makefile       |   1 +
>  libavfilter/af_aupsample.c | 159 +++++++++++++++++++++++++++++++++++++
>  libavfilter/allfilters.c   |   1 +
>  3 files changed, 161 insertions(+)
>  create mode 100644 libavfilter/af_aupsample.c
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 682df45ef5..a38bc35231 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -86,6 +86,7 @@ OBJS-$(CONFIG_ASTATS_FILTER)                 +=
> af_astats.o
>  OBJS-$(CONFIG_ASTREAMSELECT_FILTER)          += f_streamselect.o
> framesync.o
>  OBJS-$(CONFIG_ATEMPO_FILTER)                 += af_atempo.o
>  OBJS-$(CONFIG_ATRIM_FILTER)                  += trim.o
> +OBJS-$(CONFIG_AUPSAMPLE_FILTER)              += af_aupsample.o
>  OBJS-$(CONFIG_AZMQ_FILTER)                   += f_zmq.o
>  OBJS-$(CONFIG_BANDPASS_FILTER)               += af_biquads.o
>  OBJS-$(CONFIG_BANDREJECT_FILTER)             += af_biquads.o
> diff --git a/libavfilter/af_aupsample.c b/libavfilter/af_aupsample.c
> new file mode 100644
> index 0000000000..ee35b9c0c6
> --- /dev/null
> +++ b/libavfilter/af_aupsample.c
> @@ -0,0 +1,159 @@
> +/*
> + * Copyright (c) 2019 Paul B Mahol
> + *
> + * 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
> + */
> +
> +#include "libavutil/opt.h"
> +#include "libavutil/samplefmt.h"
> +#include "avfilter.h"
> +#include "audio.h"
> +#include "filters.h"
> +#include "internal.h"
> +
> +typedef struct AudioUpSampleContext {
> +    const AVClass *class;
> +    int factor;
> +
> +    int64_t next_pts;
> +} AudioUpSampleContext;
> +
> +#define OFFSET(x) offsetof(AudioUpSampleContext, x)
> +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
> +
> +static const AVOption aupsample_options[] = {
> +    { "factor", "set upsampling factor", OFFSET(factor), AV_OPT_TYPE_INT,
> {.i64=1}, 1, 64, A },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(aupsample);
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    AudioUpSampleContext *s = ctx->priv;
> +    AVFilterChannelLayouts *layouts;
> +    AVFilterFormats *formats;
> +    int sample_rates[] = { 44100, -1 };
> +    static const enum AVSampleFormat sample_fmts[] = {
> +        AV_SAMPLE_FMT_DBLP,
> +        AV_SAMPLE_FMT_NONE
> +    };
> +    AVFilterFormats *avff;
> +    int ret;
> +
> +    if (!ctx->inputs[0]->in_samplerates ||
> +        !ctx->inputs[0]->in_samplerates->nb_formats) {
> +        return AVERROR(EAGAIN);
> +    }
> +
> +    layouts = ff_all_channel_counts();
> +    if (!layouts)
> +        return AVERROR(ENOMEM);
> +    ret = ff_set_common_channel_layouts(ctx, layouts);
> +    if (ret < 0)
> +        return ret;
> +
> +    formats = ff_make_format_list(sample_fmts);
> +    if (!formats)
> +        return AVERROR(ENOMEM);
> +    ret = ff_set_common_formats(ctx, formats);
> +    if (ret < 0)
> +        return ret;
> +
> +    avff = ctx->inputs[0]->in_samplerates;
> +    sample_rates[0] = avff->formats[0];
> +    if (!ctx->inputs[0]->out_samplerates)
> +        if ((ret = ff_formats_ref(ff_make_format_list(sample_rates),
> +                                  &ctx->inputs[0]->out_samplerates)) < 0)
> +            return ret;
> +
> +    sample_rates[0] = avff->formats[0] * s->factor;
> +    return ff_formats_ref(ff_make_format_list(sample_rates),
> +                         &ctx->outputs[0]->in_samplerates);
> +}
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    AudioUpSampleContext *s = ctx->priv;
> +
> +    s->next_pts = AV_NOPTS_VALUE;
> +
> +    return 0;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    AudioUpSampleContext *s = ctx->priv;
> +    const int factor = s->factor;
> +    AVFrame *out;
> +
> +    if (s->factor == 1)
> +        return ff_filter_frame(outlink, in);
> +
> +    out = ff_get_audio_buffer(outlink, in->nb_samples * s->factor);
> +    if (!out) {
> +        av_frame_free(&in);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    if (s->next_pts == AV_NOPTS_VALUE)
> +        s->next_pts = in->pts;
> +
> +    for (int c = 0; c < in->channels; c++) {
> +        const double *src = (const double *)in->extended_data[c];
> +        double *dst = (double *)out->extended_data[c];
> +
> +        for (int n = 0; n < in->nb_samples; n++)
> +            dst[n*factor] = src[n];
> +    }
> +
> +    out->pts = s->next_pts;
> +    s->next_pts += av_rescale_q(out->nb_samples, (AVRational){1,
> outlink->sample_rate}, outlink->time_base);
> +    av_frame_free(&in);
> +    return ff_filter_frame(ctx->outputs[0], out);
> +}
> +
> +static const AVFilterPad aupsample_inputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_AUDIO,
> +        .filter_frame = filter_frame,
> +        .config_props = config_input,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad aupsample_outputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_AUDIO,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_af_aupsample = {
> +    .name          = "aupsample",
> +    .description   = NULL_IF_CONFIG_SMALL("Upsample
> audio by integer factor."),

Is it faster?
Better quality?

Just wondering, Carl Eugen
Paul B Mahol April 19, 2019, 7:58 a.m. UTC | #2
On 4/19/19, Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
> 2019-04-18 23:17 GMT+02:00, Paul B Mahol <onemda@gmail.com>:
>> Signed-off-by: Paul B Mahol <onemda@gmail.com>
>> ---
>>  libavfilter/Makefile       |   1 +
>>  libavfilter/af_aupsample.c | 159 +++++++++++++++++++++++++++++++++++++
>>  libavfilter/allfilters.c   |   1 +
>>  3 files changed, 161 insertions(+)
>>  create mode 100644 libavfilter/af_aupsample.c
>>
>> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
>> index 682df45ef5..a38bc35231 100644
>> --- a/libavfilter/Makefile
>> +++ b/libavfilter/Makefile
>> @@ -86,6 +86,7 @@ OBJS-$(CONFIG_ASTATS_FILTER)                 +=
>> af_astats.o
>>  OBJS-$(CONFIG_ASTREAMSELECT_FILTER)          += f_streamselect.o
>> framesync.o
>>  OBJS-$(CONFIG_ATEMPO_FILTER)                 += af_atempo.o
>>  OBJS-$(CONFIG_ATRIM_FILTER)                  += trim.o
>> +OBJS-$(CONFIG_AUPSAMPLE_FILTER)              += af_aupsample.o
>>  OBJS-$(CONFIG_AZMQ_FILTER)                   += f_zmq.o
>>  OBJS-$(CONFIG_BANDPASS_FILTER)               += af_biquads.o
>>  OBJS-$(CONFIG_BANDREJECT_FILTER)             += af_biquads.o
>> diff --git a/libavfilter/af_aupsample.c b/libavfilter/af_aupsample.c
>> new file mode 100644
>> index 0000000000..ee35b9c0c6
>> --- /dev/null
>> +++ b/libavfilter/af_aupsample.c
>> @@ -0,0 +1,159 @@
>> +/*
>> + * Copyright (c) 2019 Paul B Mahol
>> + *
>> + * 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
>> + */
>> +
>> +#include "libavutil/opt.h"
>> +#include "libavutil/samplefmt.h"
>> +#include "avfilter.h"
>> +#include "audio.h"
>> +#include "filters.h"
>> +#include "internal.h"
>> +
>> +typedef struct AudioUpSampleContext {
>> +    const AVClass *class;
>> +    int factor;
>> +
>> +    int64_t next_pts;
>> +} AudioUpSampleContext;
>> +
>> +#define OFFSET(x) offsetof(AudioUpSampleContext, x)
>> +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
>> +
>> +static const AVOption aupsample_options[] = {
>> +    { "factor", "set upsampling factor", OFFSET(factor), AV_OPT_TYPE_INT,
>> {.i64=1}, 1, 64, A },
>> +    { NULL }
>> +};
>> +
>> +AVFILTER_DEFINE_CLASS(aupsample);
>> +
>> +static int query_formats(AVFilterContext *ctx)
>> +{
>> +    AudioUpSampleContext *s = ctx->priv;
>> +    AVFilterChannelLayouts *layouts;
>> +    AVFilterFormats *formats;
>> +    int sample_rates[] = { 44100, -1 };
>> +    static const enum AVSampleFormat sample_fmts[] = {
>> +        AV_SAMPLE_FMT_DBLP,
>> +        AV_SAMPLE_FMT_NONE
>> +    };
>> +    AVFilterFormats *avff;
>> +    int ret;
>> +
>> +    if (!ctx->inputs[0]->in_samplerates ||
>> +        !ctx->inputs[0]->in_samplerates->nb_formats) {
>> +        return AVERROR(EAGAIN);
>> +    }
>> +
>> +    layouts = ff_all_channel_counts();
>> +    if (!layouts)
>> +        return AVERROR(ENOMEM);
>> +    ret = ff_set_common_channel_layouts(ctx, layouts);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    formats = ff_make_format_list(sample_fmts);
>> +    if (!formats)
>> +        return AVERROR(ENOMEM);
>> +    ret = ff_set_common_formats(ctx, formats);
>> +    if (ret < 0)
>> +        return ret;
>> +
>> +    avff = ctx->inputs[0]->in_samplerates;
>> +    sample_rates[0] = avff->formats[0];
>> +    if (!ctx->inputs[0]->out_samplerates)
>> +        if ((ret = ff_formats_ref(ff_make_format_list(sample_rates),
>> +                                  &ctx->inputs[0]->out_samplerates)) < 0)
>> +            return ret;
>> +
>> +    sample_rates[0] = avff->formats[0] * s->factor;
>> +    return ff_formats_ref(ff_make_format_list(sample_rates),
>> +                         &ctx->outputs[0]->in_samplerates);
>> +}
>> +
>> +static int config_input(AVFilterLink *inlink)
>> +{
>> +    AVFilterContext *ctx = inlink->dst;
>> +    AudioUpSampleContext *s = ctx->priv;
>> +
>> +    s->next_pts = AV_NOPTS_VALUE;
>> +
>> +    return 0;
>> +}
>> +
>> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
>> +{
>> +    AVFilterContext *ctx = inlink->dst;
>> +    AVFilterLink *outlink = ctx->outputs[0];
>> +    AudioUpSampleContext *s = ctx->priv;
>> +    const int factor = s->factor;
>> +    AVFrame *out;
>> +
>> +    if (s->factor == 1)
>> +        return ff_filter_frame(outlink, in);
>> +
>> +    out = ff_get_audio_buffer(outlink, in->nb_samples * s->factor);
>> +    if (!out) {
>> +        av_frame_free(&in);
>> +        return AVERROR(ENOMEM);
>> +    }
>> +
>> +    if (s->next_pts == AV_NOPTS_VALUE)
>> +        s->next_pts = in->pts;
>> +
>> +    for (int c = 0; c < in->channels; c++) {
>> +        const double *src = (const double *)in->extended_data[c];
>> +        double *dst = (double *)out->extended_data[c];
>> +
>> +        for (int n = 0; n < in->nb_samples; n++)
>> +            dst[n*factor] = src[n];
>> +    }
>> +
>> +    out->pts = s->next_pts;
>> +    s->next_pts += av_rescale_q(out->nb_samples, (AVRational){1,
>> outlink->sample_rate}, outlink->time_base);
>> +    av_frame_free(&in);
>> +    return ff_filter_frame(ctx->outputs[0], out);
>> +}
>> +
>> +static const AVFilterPad aupsample_inputs[] = {
>> +    {
>> +        .name         = "default",
>> +        .type         = AVMEDIA_TYPE_AUDIO,
>> +        .filter_frame = filter_frame,
>> +        .config_props = config_input,
>> +    },
>> +    { NULL }
>> +};
>> +
>> +static const AVFilterPad aupsample_outputs[] = {
>> +    {
>> +        .name         = "default",
>> +        .type         = AVMEDIA_TYPE_AUDIO,
>> +    },
>> +    { NULL }
>> +};
>> +
>> +AVFilter ff_af_aupsample = {
>> +    .name          = "aupsample",
>> +    .description   = NULL_IF_CONFIG_SMALL("Upsample
>> audio by integer factor."),
>
> Is it faster?
> Better quality?

This is not same as resampling.

This is used as part of other filtering. Namely when filter needs to oversample
audio when processing - it then upsample audio before processing and downsample
it by same factor after processing it.
Nicolas George April 19, 2019, 9:04 a.m. UTC | #3
Paul B Mahol (12019-04-19):
> This is used as part of other filtering. Namely when filter needs to oversample
> audio when processing - it then upsample audio before processing and downsample
> it by same factor after processing it.

How do you intend to implement that? Filters cannot call other filters.
Paul B Mahol April 19, 2019, 9:10 a.m. UTC | #4
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> This is used as part of other filtering. Namely when filter needs to
>> oversample
>> audio when processing - it then upsample audio before processing and
>> downsample
>> it by same factor after processing it.
>
> How do you intend to implement that? Filters cannot call other filters.

Users that know what they are doing will do:

upsample=4,some additional filtering,alimiter=parameters,some
additional filtering,downsample=4

This is already done by SoX.
Nicolas George April 19, 2019, 9:13 a.m. UTC | #5
Paul B Mahol (12019-04-19):
> Users that know what they are doing will do:
> 
> upsample=4,some additional filtering,alimiter=parameters,some
> additional filtering,downsample=4

Bad design. We have sample rate negotiation.

> This is already done by SoX.

We are not SoX.
Paul B Mahol April 19, 2019, 9:30 a.m. UTC | #6
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> Users that know what they are doing will do:
>>
>> upsample=4,some additional filtering,alimiter=parameters,some
>> additional filtering,downsample=4
>
> Bad design. We have sample rate negotiation.

This is not about sample rate negotiation.

>
>> This is already done by SoX.
>
> We are not SoX.

Bad argumentation.

>
> --
>   Nicolas George
> _______________________________________________
> 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".
Nicolas George April 19, 2019, 9:32 a.m. UTC | #7
Paul B Mahol (12019-04-19):
> This is not about sample rate negotiation.

Yes it is.

> Bad argumentation.

Then argue better.

Patches rejected for now.
Paul B Mahol April 19, 2019, 9:34 a.m. UTC | #8
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> This is not about sample rate negotiation.
>
> Yes it is.

Give proof.

No, you need to argue better your pathetic behavior.

>
>> Bad argumentation.
>
> Then argue better.
>
> Patches rejected for now.

Look, you  are full of shit. And know nothing about audio processing.
Nicolas George April 19, 2019, 9:48 a.m. UTC | #9
Paul B Mahol (12019-04-19):
> Look, you  are full of shit.

Unacceptable. Excuses expected.
Paul B Mahol April 19, 2019, 9:54 a.m. UTC | #10
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> Look, you  are full of shit.
>
> Unacceptable. Excuses expected.

I will ignore your one liners, snarky replies.
Gyan Doshi April 19, 2019, 10:33 a.m. UTC | #11
On 19-04-2019 02:43 PM, Nicolas George wrote:
> Paul B Mahol (12019-04-19):
>> Users that know what they are doing will do:
>>
>> upsample=4,some additional filtering,alimiter=parameters,some
>> additional filtering,downsample=4
> Bad design. We have sample rate negotiation.

How about a filter that can store link and/or frame props as metadata? 
This filter can be inserted before 'processing' filters.

Then Paul's filters, inserted afterwards, can read those meta keys to 
obtain pre-process props.

Gyan
Paul B Mahol April 19, 2019, 10:51 a.m. UTC | #12
On 4/19/19, Gyan <ffmpeg@gyani.pro> wrote:
>
>
> On 19-04-2019 02:43 PM, Nicolas George wrote:
>> Paul B Mahol (12019-04-19):
>>> Users that know what they are doing will do:
>>>
>>> upsample=4,some additional filtering,alimiter=parameters,some
>>> additional filtering,downsample=4
>> Bad design. We have sample rate negotiation.
>
> How about a filter that can store link and/or frame props as metadata?
> This filter can be inserted before 'processing' filters.
>
> Then Paul's filters, inserted afterwards, can read those meta keys to
> obtain pre-process props.

There are numerous ways how oversampling/undersampling can be done.
This two filters are fundamental blocks of oversampling/undersampling.
The "some additional filtering" can be IIR or FIR lowpass, or FFT where
anything above certain range is set to 0. All mentioned solutions have
same good and bad points.
Implementing this as part of existing filter is not flexible enough
and will limit
us to only some solutions also it needs more lines of code.
Michael Niedermayer April 19, 2019, 1:12 p.m. UTC | #13
On Fri, Apr 19, 2019 at 11:34:04AM +0200, Paul B Mahol wrote:
> On 4/19/19, Nicolas George <george@nsup.org> wrote:
> > Paul B Mahol (12019-04-19):
> >> This is not about sample rate negotiation.
> >
> > Yes it is.
> 
> Give proof.
> 
> No, you need to argue better your pathetic behavior.
> 
> >
> >> Bad argumentation.
> >
> > Then argue better.
> >
> > Patches rejected for now.
> 
> Look, you  are full of shit. And know nothing about audio processing.

As i have been asked to look into this (and as we dont have many other
ML admins currently)

This is a technical mailing list, arguing about how much you love each
other does not belong here.

As a remainder
Everyone, the subject of discussions should be FFmpeg and the technologies
sorounding it. Insults, "Ad hominem" argumentation/attacks, and so on lead
to a hostile climate that helps noone not even the one winning the argument
eventually.
If theres a disagreement on a patch (one wanting it in one not wanting it in
and after technical discussion its not resolved), we can if everything else
fails do a vote on the patch. A technical solution with technical arguments
is better though than a vote.

Thanks

[...]
Nicolas George April 19, 2019, 3:25 p.m. UTC | #14
Michael Niedermayer (12019-04-19):
> As i have been asked to look into this

Not to look into this: to take action.

> Everyone, the subject of discussions should be FFmpeg and the technologies
> sorounding it. Insults, "Ad hominem" argumentation/attacks, and so on lead
> to a hostile climate that helps noone not even the one winning the argument
> eventually.

Indeed. I will have a technical discussion about these patches, but not
with somebody who insults me. Until I have reviewed and approved the
patches, which cannot happen until the insults are gone for good,
patches on lavfi are rejected.

Regards,
Paul B Mahol April 19, 2019, 3:31 p.m. UTC | #15
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Michael Niedermayer (12019-04-19):
>> As i have been asked to look into this
>
> Not to look into this: to take action.
>
>> Everyone, the subject of discussions should be FFmpeg and the
>> technologies
>> sorounding it. Insults, "Ad hominem" argumentation/attacks, and so on
>> lead
>> to a hostile climate that helps noone not even the one winning the
>> argument
>> eventually.
>
> Indeed. I will have a technical discussion about these patches, but not
> with somebody who insults me. Until I have reviewed and approved the
> patches, which cannot happen until the insults are gone for good,
> patches on lavfi are rejected.

What you actually want?
Nicolas George April 19, 2019, 3:32 p.m. UTC | #16
Paul B Mahol (12019-04-19):
> What you actually want?

I demand, that you behave in a civil manner.
Paul B Mahol April 19, 2019, 3:41 p.m. UTC | #17
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> What you actually want?
>
> I demand, that you behave in a civil manner.

I'm very civil here.
If I'm not polite than that is because of your snarky comments.

You already blocked several my patches for no apparent
good reason.

In the long run that hurts mostly users.
Nicolas George April 19, 2019, 3:43 p.m. UTC | #18
Paul B Mahol (12019-04-19):
> I'm very civil here.

https://ffmpeg.org/pipermail/ffmpeg-devel/2019-April/242839.html
Paul B Mahol April 19, 2019, 4:15 p.m. UTC | #19
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> I'm very civil here.
>
> https://ffmpeg.org/pipermail/ffmpeg-devel/2019-April/242839.html

I'm really sorry if that hurt you.
Nicolas George April 19, 2019, 5:08 p.m. UTC | #20
Paul B Mahol (12019-04-19):
> I'm really sorry if that hurt you.

That's not excuses for the insult.
Paul B Mahol April 19, 2019, 6:02 p.m. UTC | #21
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> I'm really sorry if that hurt you.
>
> That's not excuses for the insult.

I'm really sorry.
Nicolas George April 19, 2019, 6:04 p.m. UTC | #22
Paul B Mahol (12019-04-19):
> I'm really sorry.

For what, and what do you intend to do about it?
Paul B Mahol April 19, 2019, 6:12 p.m. UTC | #23
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> I'm really sorry.
>
> For what, and what do you intend to do about it?

I'm really sorry for insulting you, and I will try to not repeat it.
Nicolas George April 19, 2019, 6:22 p.m. UTC | #24
Paul B Mahol (12019-04-19):
> I'm really sorry for insulting you, and I will try to not repeat it.

Accepted, although I have a feeling that "try" is weak.

Now, you have complained about what you call my "one liners". I do not
know if you realize, but I have adopted the policy of not wasting lines
to write to you because it is exactly the way you express yourself to me
and others most of the time. If you do not like when it comes to you, do
not send it to other people.

Now that this is clear, please explain in details why you think these
filters are necessary and not redundant with the generic resampling and
automatic format negotiation, and I will either stop my objections or
explain why I think you are mistaken.

Note that the effort you will take in making this discussion civil and
constructive will be taken as an indication on whether your excuses were
sincere or just a way of getting what you want. If in doubt, explain
more, not less.

Regards,
Paul B Mahol April 19, 2019, 8:11 p.m. UTC | #25
On 4/19/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> I'm really sorry for insulting you, and I will try to not repeat it.
>
> Accepted, although I have a feeling that "try" is weak.
>
> Now, you have complained about what you call my "one liners". I do not
> know if you realize, but I have adopted the policy of not wasting lines
> to write to you because it is exactly the way you express yourself to me
> and others most of the time. If you do not like when it comes to you, do
> not send it to other people.
>
> Now that this is clear, please explain in details why you think these
> filters are necessary and not redundant with the generic resampling and
> automatic format negotiation, and I will either stop my objections or
> explain why I think you are mistaken.

Generic resampling via aresample is completely another filtering that have
nothing to do with this filters, for more info read:

https://www.oblique-audio.com/technique/oversampling

>
> Note that the effort you will take in making this discussion civil and
> constructive will be taken as an indication on whether your excuses were
> sincere or just a way of getting what you want. If in doubt, explain
> more, not less.
>
> Regards,
>
> --
>   Nicolas George
>
Nicolas George April 21, 2019, 9:37 a.m. UTC | #26
Paul B Mahol (12019-04-19):
> Generic resampling via aresample is completely another filtering that have
> nothing to do with this filters, for more info read:
> 
> https://www.oblique-audio.com/technique/oversampling

Quite the opposite: this page makes it rather clear that oversampling is
a kind of resampling, with specific settings to ensure particular
properties of the spectrum.

As such, it should go into the generic resampling filter (or even
library), enabled by options. That way, it is available not only
explicitly from expert users but also automatically, when filters
declare specific samples rates.

Regards,
Paul B Mahol April 21, 2019, 9:53 a.m. UTC | #27
On 4/21/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-19):
>> Generic resampling via aresample is completely another filtering that
>> have
>> nothing to do with this filters, for more info read:
>>
>> https://www.oblique-audio.com/technique/oversampling
>
> Quite the opposite: this page makes it rather clear that oversampling is
> a kind of resampling, with specific settings to ensure particular
> properties of the spectrum.

It is not kind of resampling. Resampling is specific and belongs to
separate library.
The oversampling is another thing, and doing only upsampling/downsampling
via libswresample is fragile/incomplete, as you need some kind of lowpass.
(be it IIR, FIR or simple FFT) after upsample and before downsample.
That is why it best fit into lavfi because it is just one block of
processing with
multiple solutions. And not one way do it all solution, set once and
forget what it does.

>
> As such, it should go into the generic resampling filter (or even
> library), enabled by options. That way, it is available not only
> explicitly from expert users but also automatically, when filters
> declare specific samples rates.

Does not make sense to provide this into libswresample library.
Also how that could be exported? It does not belong there.
It would just confuse users, more because it supports only
integer factor multiplication/divide of rates.
Nicolas George April 21, 2019, 10 a.m. UTC | #28
I apperciate the efforts to reply more than half a line.

Paul B Mahol (12019-04-21):
> It is not kind of resampling. Resampling is specific and belongs to
> separate library.

There is no doubt it IS a kind of resampling: it is in the name. The
question is whether is it specific enough to warrant a separate
interface.

> The oversampling is another thing, and doing only upsampling/downsampling
> via libswresample is fragile/incomplete, as you need some kind of lowpass.
> (be it IIR, FIR or simple FFT) after upsample and before downsample.

There are various way of doing a resampling with various properties.
This is just another one.

> That is why it best fit into lavfi because it is just one block of
> processing with multiple solutions.

Making it a separate filter is the easy solution, but not the good one.
It is reporting all the choices to later or the end user. That makes for
easy quick code now, but terrible use later.

> Also how that could be exported? It does not belong there.

Options. We already have them, just add a few. And in that case, it
becomes accessible for all filters.

Regards,
Paul B Mahol April 21, 2019, 10:26 a.m. UTC | #29
On 4/21/19, Nicolas George <george@nsup.org> wrote:
> I apperciate the efforts to reply more than half a line.
>
> Paul B Mahol (12019-04-21):
>> It is not kind of resampling. Resampling is specific and belongs to
>> separate library.
>
> There is no doubt it IS a kind of resampling: it is in the name. The
> question is whether is it specific enough to warrant a separate
> interface.

https://dspguru.com/dsp/faqs/multirate/resampling/

Resampling involves interpolation.
If I do resampling with aresample and resampling with factor 2 from
44100 to 88200
I can see there is still some spectrum data in highest frequencies.

>
>> The oversampling is another thing, and doing only upsampling/downsampling
>> via libswresample is fragile/incomplete, as you need some kind of
>> lowpass.
>> (be it IIR, FIR or simple FFT) after upsample and before downsample.
>
> There are various way of doing a resampling with various properties.
> This is just another one.

If you haven't noticed, this is just upsampling/downsampling without lowpass.
And that is not resampling in any way.

>
>> That is why it best fit into lavfi because it is just one block of
>> processing with multiple solutions.
>
> Making it a separate filter is the easy solution, but not the good one.
> It is reporting all the choices to later or the end user. That makes for
> easy quick code now, but terrible use later.

That is just your opinion now, you need to provide technical terms
to support your statements.

>
>> Also how that could be exported? It does not belong there.
>
> Options. We already have them, just add a few. And in that case, it
> becomes accessible for all filters.

Another option to aresample filter would just confuse users. Two options
which would set rate with completely different outcome.
Nicolas George April 22, 2019, 6:14 a.m. UTC | #30
Paul B Mahol (12019-04-21):
> https://dspguru.com/dsp/faqs/multirate/resampling/
> 
> Resampling involves interpolation.
> If I do resampling with aresample and resampling with factor 2 from
> 44100 to 88200
> I can see there is still some spectrum data in highest frequencies.

Resampling MAY involve interpolation, to enhance the subjective quality
of the output.

> If you haven't noticed, this is just upsampling/downsampling without lowpass.
> And that is not resampling in any way.

No matter how many times you repeat it, saying that changing the sample
rate is not resampling will not make it true.

> That is just your opinion now, you need to provide technical terms
> to support your statements.

I have given technical considerations: we have automatic sample rate
negotiation. These filter would not fit with it.

What have you provided besides your opinion and links to generic
considerations that do not substantiate your views?

> Another option to aresample filter would just confuse users.

Less so than yet another pair of filters that do almost exactly the same
thing as others.

Regards,
Paul B Mahol April 22, 2019, 10:21 a.m. UTC | #31
On 4/22/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-21):
>> https://dspguru.com/dsp/faqs/multirate/resampling/
>>
>> Resampling involves interpolation.
>> If I do resampling with aresample and resampling with factor 2 from
>> 44100 to 88200
>> I can see there is still some spectrum data in highest frequencies.
>
> Resampling MAY involve interpolation, to enhance the subjective quality
> of the output.

It is not MAY, it MUST involve interpolation in almost any scenario.

>
>> If you haven't noticed, this is just upsampling/downsampling without
>> lowpass.
>> And that is not resampling in any way.
>
> No matter how many times you repeat it, saying that changing the sample
> rate is not resampling will not make it true.

By your flawed reasoning asetrate filter is doing resamping too.

>
>> That is just your opinion now, you need to provide technical terms
>> to support your statements.
>
> I have given technical considerations: we have automatic sample rate
> negotiation. These filter would not fit with it.

If you haven't tried them, they fit well with sample rate negotiation.
Filters change sample rate metadata by integer factor up or down.

>
> What have you provided besides your opinion and links to generic
> considerations that do not substantiate your views?

I have provided actual facts, and not opinions unlike you.

>
>> Another option to aresample filter would just confuse users.
>
> Less so than yet another pair of filters that do almost exactly the same
> thing as others.

No filter in libavfilter does what those filters do.

And there is reason why SoX have those as separate filters and not some
strange combination with their generic resampling effect.
Paul B Mahol April 25, 2019, 3:56 p.m. UTC | #32
On 4/22/19, Paul B Mahol <onemda@gmail.com> wrote:
> On 4/22/19, Nicolas George <george@nsup.org> wrote:
>> Paul B Mahol (12019-04-21):
>>> https://dspguru.com/dsp/faqs/multirate/resampling/
>>>
>>> Resampling involves interpolation.
>>> If I do resampling with aresample and resampling with factor 2 from
>>> 44100 to 88200
>>> I can see there is still some spectrum data in highest frequencies.
>>
>> Resampling MAY involve interpolation, to enhance the subjective quality
>> of the output.
>
> It is not MAY, it MUST involve interpolation in almost any scenario.
>
>>
>>> If you haven't noticed, this is just upsampling/downsampling without
>>> lowpass.
>>> And that is not resampling in any way.
>>
>> No matter how many times you repeat it, saying that changing the sample
>> rate is not resampling will not make it true.
>
> By your flawed reasoning asetrate filter is doing resamping too.
>
>>
>>> That is just your opinion now, you need to provide technical terms
>>> to support your statements.
>>
>> I have given technical considerations: we have automatic sample rate
>> negotiation. These filter would not fit with it.
>
> If you haven't tried them, they fit well with sample rate negotiation.
> Filters change sample rate metadata by integer factor up or down.
>
>>
>> What have you provided besides your opinion and links to generic
>> considerations that do not substantiate your views?
>
> I have provided actual facts, and not opinions unlike you.
>
>>
>>> Another option to aresample filter would just confuse users.
>>
>> Less so than yet another pair of filters that do almost exactly the same
>> thing as others.
>
> No filter in libavfilter does what those filters do.
>
> And there is reason why SoX have those as separate filters and not some
> strange combination with their generic resampling effect.
>

As there appears to be no more comments or valid arguments against this
set I will apply it.
Nicolas George April 25, 2019, 3:57 p.m. UTC | #33
Paul B Mahol (12019-04-25):
> As there appears to be no more comments or valid arguments against this

Saying it does not make it true.

> set I will apply it.

Do it and I will revert and ask that your commits rights be revoked.
Paul B Mahol April 25, 2019, 5:07 p.m. UTC | #34
On 4/25/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-25):
>> As there appears to be no more comments or valid arguments against this
>
> Saying it does not make it true.

You haven't replied any more, and nothing that you said earlier is valid.

>
>> set I will apply it.
>
> Do it and I will revert and ask that your commits rights be revoked.

Michael, please remove Nicolas from committers as he want to revert
my patches and do not have valid technical arguments for his actions.
Nicolas George April 25, 2019, 5:09 p.m. UTC | #35
Paul B Mahol (12019-04-25):
> You haven't replied any more, and nothing that you said earlier is valid.

I have stated: rejected until it plays well with sample rate
negotiation. You have not reacted to it.
Paul B Mahol April 25, 2019, 5:18 p.m. UTC | #36
On 4/25/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-25):
>> You haven't replied any more, and nothing that you said earlier is valid.
>
> I have stated: rejected until it plays well with sample rate
> negotiation. You have not reacted to it.

I did reacted. It plays well with negotiation.
Can you be more specific what exactly you mean by "plays well with
sample rate negotiation"?
Nicolas George April 25, 2019, 5:25 p.m. UTC | #37
Paul B Mahol (12019-04-25):
> I did reacted. It plays well with negotiation.

I missed that part, sorry. But it did not address my concern.

> Can you be more specific what exactly you mean by "plays well with
> sample rate negotiation"?

If the user judges that this is the kind of sample rate change that is
necessary, then when the negotiation detects a sample rate change is
necessary, these filters are used.

Regards,
Hendrik Leppkes April 25, 2019, 5:31 p.m. UTC | #38
On Thu, Apr 25, 2019 at 7:25 PM Nicolas George <george@nsup.org> wrote:
>
> Paul B Mahol (12019-04-25):
> > I did reacted. It plays well with negotiation.
>
> I missed that part, sorry. But it did not address my concern.
>
> > Can you be more specific what exactly you mean by "plays well with
> > sample rate negotiation"?
>
> If the user judges that this is the kind of sample rate change that is
> necessary, then when the negotiation detects a sample rate change is
> necessary, these filters are used.
>

The point of this kind of filtering is not that they are necessary to
perform filtering, because you can filter at any sample rate, but that
oversampling for certain filters, and downsampling after, improves
quality. As such, negotiation is not really equipped to handle it,
since its designed to deal with compatibility between filters, not
quality concerns, and you also wouldn't want to remove the ability to
filter at ordinary sample rates either.

- Hendrik
Nicolas George April 25, 2019, 5:54 p.m. UTC | #39
Hendrik Leppkes (12019-04-25):
> The point of this kind of filtering is not that they are necessary to
> perform filtering, because you can filter at any sample rate, but that
> oversampling for certain filters, and downsampling after, improves
> quality. As such, negotiation is not really equipped to handle it,
> since its designed to deal with compatibility between filters, not
> quality concerns, and you also wouldn't want to remove the ability to
> filter at ordinary sample rates either.

I do not want them to be available ONLY with negotiation. If people want
to add them, fine.

But when negotiation decides a conversion needs to happen somewhere,
these treatments must be available for it, at the place where it is
needed, which is often not known to the user.

And the simplest way of achieving that is to make it an option of the
resample filter, or even better, en option if lswr. After all, it is
only the audio equivalent of sws_flags=neighbor.

Regards,
Paul B Mahol April 25, 2019, 6:41 p.m. UTC | #40
On 4/25/19, Nicolas George <george@nsup.org> wrote:
> Hendrik Leppkes (12019-04-25):
>> The point of this kind of filtering is not that they are necessary to
>> perform filtering, because you can filter at any sample rate, but that
>> oversampling for certain filters, and downsampling after, improves
>> quality. As such, negotiation is not really equipped to handle it,
>> since its designed to deal with compatibility between filters, not
>> quality concerns, and you also wouldn't want to remove the ability to
>> filter at ordinary sample rates either.
>
> I do not want them to be available ONLY with negotiation. If people want
> to add them, fine.
>
> But when negotiation decides a conversion needs to happen somewhere,
> these treatments must be available for it, at the place where it is
> needed, which is often not known to the user.
>
> And the simplest way of achieving that is to make it an option of the
> resample filter, or even better, en option if lswr. After all, it is
> only the audio equivalent of sws_flags=neighbor.

The filter does not take sample rate as argument, but integer factor instead.
So that make it impossible to be used with lswr.
Nicolas George April 25, 2019, 6:43 p.m. UTC | #41
Paul B Mahol (12019-04-25):
> The filter does not take sample rate as argument, but integer factor instead.
> So that make it impossible to be used with lswr.

No need for an argument: the filter knows the negotiated input and
output sample rate.
Paul B Mahol April 25, 2019, 6:51 p.m. UTC | #42
On 4/25/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-04-25):
>> The filter does not take sample rate as argument, but integer factor
>> instead.
>> So that make it impossible to be used with lswr.
>
> No need for an argument: the filter knows the negotiated input and
> output sample rate.

Not really, it works with 2x/3x/4x/5x.. sample rate or 1/2 / 1/3 / 1/4 / 1/5
o sample rate in other direction.
It can not be done in aresample filter because of limitations of query_formats
code in libavfilter and doing it anyway for aresample filter would
cause many regressions.
Nicolas George April 25, 2019, 7:13 p.m. UTC | #43
Paul B Mahol (12019-04-25):
> Not really, it works with 2x/3x/4x/5x.. sample rate or 1/2 / 1/3 / 1/4 / 1/5
> o sample rate in other direction.

I do not think so: the algorithm can work for absolutely any ratio. The
effect on the spectrum will not be as clean if the ratio is not an
integer, but that is another problem.

> It can not be done in aresample filter because of limitations of query_formats
> code in libavfilter

Please elaborate.

>		      and doing it anyway for aresample filter would
> cause many regressions.

I do not think so. Please elaborate.
Paul B Mahol Oct. 25, 2020, 12:41 p.m. UTC | #44
On Thu, Apr 25, 2019 at 9:14 PM Nicolas George <george@nsup.org> wrote:

> Paul B Mahol (12019-04-25):
> > Not really, it works with 2x/3x/4x/5x.. sample rate or 1/2 / 1/3 / 1/4 /
> 1/5
> > o sample rate in other direction.
>
> I do not think so: the algorithm can work for absolutely any ratio. The
> effect on the spectrum will not be as clean if the ratio is not an
> integer, but that is another problem.
>
> > It can not be done in aresample filter because of limitations of
> query_formats
> > code in libavfilter
>
> Please elaborate.
>
> >                     and doing it anyway for aresample filter would
> > cause many regressions.
>
> I do not think so. Please elaborate.
>

I really lost any motivation to deal with Nicolas George behavior.
So I please ask again for TC to deal with this.
There are too many great patches lost because of his behavior.
Paul B Mahol Nov. 25, 2023, 4:13 p.m. UTC | #45
On Sun, Oct 25, 2020 at 1:41 PM Paul B Mahol <onemda@gmail.com> wrote:

>
>
> On Thu, Apr 25, 2019 at 9:14 PM Nicolas George <george@nsup.org> wrote:
>
>> Paul B Mahol (12019-04-25):
>> > Not really, it works with 2x/3x/4x/5x.. sample rate or 1/2 / 1/3 / 1/4
>> / 1/5
>> > o sample rate in other direction.
>>
>> I do not think so: the algorithm can work for absolutely any ratio. The
>> effect on the spectrum will not be as clean if the ratio is not an
>> integer, but that is another problem.
>>
>> > It can not be done in aresample filter because of limitations of
>> query_formats
>> > code in libavfilter
>>
>> Please elaborate.
>>
>> >                     and doing it anyway for aresample filter would
>> > cause many regressions.
>>
>> I do not think so. Please elaborate.
>>
>
> I really lost any motivation to deal with Nicolas George behavior.
> So I please ask again for TC to deal with this.
> There are too many great patches lost because of his behavior.
>

I still want that TC and CC take this into reconsidering.
diff mbox

Patch

diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 682df45ef5..a38bc35231 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -86,6 +86,7 @@  OBJS-$(CONFIG_ASTATS_FILTER)                 += af_astats.o
 OBJS-$(CONFIG_ASTREAMSELECT_FILTER)          += f_streamselect.o framesync.o
 OBJS-$(CONFIG_ATEMPO_FILTER)                 += af_atempo.o
 OBJS-$(CONFIG_ATRIM_FILTER)                  += trim.o
+OBJS-$(CONFIG_AUPSAMPLE_FILTER)              += af_aupsample.o
 OBJS-$(CONFIG_AZMQ_FILTER)                   += f_zmq.o
 OBJS-$(CONFIG_BANDPASS_FILTER)               += af_biquads.o
 OBJS-$(CONFIG_BANDREJECT_FILTER)             += af_biquads.o
diff --git a/libavfilter/af_aupsample.c b/libavfilter/af_aupsample.c
new file mode 100644
index 0000000000..ee35b9c0c6
--- /dev/null
+++ b/libavfilter/af_aupsample.c
@@ -0,0 +1,159 @@ 
+/*
+ * Copyright (c) 2019 Paul B Mahol
+ *
+ * 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
+ */
+
+#include "libavutil/opt.h"
+#include "libavutil/samplefmt.h"
+#include "avfilter.h"
+#include "audio.h"
+#include "filters.h"
+#include "internal.h"
+
+typedef struct AudioUpSampleContext {
+    const AVClass *class;
+    int factor;
+
+    int64_t next_pts;
+} AudioUpSampleContext;
+
+#define OFFSET(x) offsetof(AudioUpSampleContext, x)
+#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption aupsample_options[] = {
+    { "factor", "set upsampling factor", OFFSET(factor), AV_OPT_TYPE_INT, {.i64=1}, 1, 64, A },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(aupsample);
+
+static int query_formats(AVFilterContext *ctx)
+{
+    AudioUpSampleContext *s = ctx->priv;
+    AVFilterChannelLayouts *layouts;
+    AVFilterFormats *formats;
+    int sample_rates[] = { 44100, -1 };
+    static const enum AVSampleFormat sample_fmts[] = {
+        AV_SAMPLE_FMT_DBLP,
+        AV_SAMPLE_FMT_NONE
+    };
+    AVFilterFormats *avff;
+    int ret;
+
+    if (!ctx->inputs[0]->in_samplerates ||
+        !ctx->inputs[0]->in_samplerates->nb_formats) {
+        return AVERROR(EAGAIN);
+    }
+
+    layouts = ff_all_channel_counts();
+    if (!layouts)
+        return AVERROR(ENOMEM);
+    ret = ff_set_common_channel_layouts(ctx, layouts);
+    if (ret < 0)
+        return ret;
+
+    formats = ff_make_format_list(sample_fmts);
+    if (!formats)
+        return AVERROR(ENOMEM);
+    ret = ff_set_common_formats(ctx, formats);
+    if (ret < 0)
+        return ret;
+
+    avff = ctx->inputs[0]->in_samplerates;
+    sample_rates[0] = avff->formats[0];
+    if (!ctx->inputs[0]->out_samplerates)
+        if ((ret = ff_formats_ref(ff_make_format_list(sample_rates),
+                                  &ctx->inputs[0]->out_samplerates)) < 0)
+            return ret;
+
+    sample_rates[0] = avff->formats[0] * s->factor;
+    return ff_formats_ref(ff_make_format_list(sample_rates),
+                         &ctx->outputs[0]->in_samplerates);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AudioUpSampleContext *s = ctx->priv;
+
+    s->next_pts = AV_NOPTS_VALUE;
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    AudioUpSampleContext *s = ctx->priv;
+    const int factor = s->factor;
+    AVFrame *out;
+
+    if (s->factor == 1)
+        return ff_filter_frame(outlink, in);
+
+    out = ff_get_audio_buffer(outlink, in->nb_samples * s->factor);
+    if (!out) {
+        av_frame_free(&in);
+        return AVERROR(ENOMEM);
+    }
+
+    if (s->next_pts == AV_NOPTS_VALUE)
+        s->next_pts = in->pts;
+
+    for (int c = 0; c < in->channels; c++) {
+        const double *src = (const double *)in->extended_data[c];
+        double *dst = (double *)out->extended_data[c];
+
+        for (int n = 0; n < in->nb_samples; n++)
+            dst[n*factor] = src[n];
+    }
+
+    out->pts = s->next_pts;
+    s->next_pts += av_rescale_q(out->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
+    av_frame_free(&in);
+    return ff_filter_frame(ctx->outputs[0], out);
+}
+
+static const AVFilterPad aupsample_inputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_AUDIO,
+        .filter_frame = filter_frame,
+        .config_props = config_input,
+    },
+    { NULL }
+};
+
+static const AVFilterPad aupsample_outputs[] = {
+    {
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_AUDIO,
+    },
+    { NULL }
+};
+
+AVFilter ff_af_aupsample = {
+    .name          = "aupsample",
+    .description   = NULL_IF_CONFIG_SMALL("Upsample audio by integer factor."),
+    .query_formats = query_formats,
+    .priv_size     = sizeof(AudioUpSampleContext),
+    .priv_class    = &aupsample_class,
+    .inputs        = aupsample_inputs,
+    .outputs       = aupsample_outputs,
+};
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 4d3039d6ba..29b372a1db 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -78,6 +78,7 @@  extern AVFilter ff_af_astats;
 extern AVFilter ff_af_astreamselect;
 extern AVFilter ff_af_atempo;
 extern AVFilter ff_af_atrim;
+extern AVFilter ff_af_aupsample;
 extern AVFilter ff_af_azmq;
 extern AVFilter ff_af_bandpass;
 extern AVFilter ff_af_bandreject;