Signed-off-by: Paul B Mahol <one...@gmail.com> --- doc/filters.texi | 15 +++ libavfilter/Makefile | 1 + libavfilter/af_adownsample.c | 171 +++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 4 files changed, 188 insertions(+) create mode 100644 libavfilter/af_adownsample.c
diff --git a/doc/filters.texi b/doc/filters.texi index 465eeb4732..d6aac5730d 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -714,6 +714,21 @@ Compute derivative/integral of audio stream. Applying both filters one after another produces original audio. +@section adownsample, aupsample +Downsample or upsample audio by integer factor. + +For downsampling only the first out of each factor samples is retained, +the others are discarded +For upsampling, factor-1 zero-value samples are inserted between each pair of input samples. +As a result, the original spectrum is replicated into the new frequency space and attenuated. + +A description of the accepted parameters follows. + +@table @option +@item factor +Set factor of downsampling/upsampling. Default is @code{1}. +@end table + @section aecho Apply echoing to the input audio. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index a38bc35231..ed36454f9e 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -43,6 +43,7 @@ OBJS-$(CONFIG_ADECLICK_FILTER) += af_adeclick.o OBJS-$(CONFIG_ADECLIP_FILTER) += af_adeclick.o OBJS-$(CONFIG_ADELAY_FILTER) += af_adelay.o OBJS-$(CONFIG_ADERIVATIVE_FILTER) += af_aderivative.o +OBJS-$(CONFIG_ADOWNSAMPLE_FILTER) += af_adownsample.o OBJS-$(CONFIG_AECHO_FILTER) += af_aecho.o OBJS-$(CONFIG_AEMPHASIS_FILTER) += af_aemphasis.o OBJS-$(CONFIG_AEVAL_FILTER) += aeval.o diff --git a/libavfilter/af_adownsample.c b/libavfilter/af_adownsample.c new file mode 100644 index 0000000000..1ee661e383 --- /dev/null +++ b/libavfilter/af_adownsample.c @@ -0,0 +1,171 @@ +/* + * 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 AudioDownSampleContext { + const AVClass *class; + int factor; + + int64_t next_pts; +} AudioDownSampleContext; + +#define OFFSET(x) offsetof(AudioDownSampleContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption adownsample_options[] = { + { "factor", "set downsampling factor", OFFSET(factor), AV_OPT_TYPE_INT, {.i64=1}, 1, 64, A }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(adownsample); + +static int query_formats(AVFilterContext *ctx) +{ + AudioDownSampleContext *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; + AudioDownSampleContext *s = ctx->priv; + + s->next_pts = AV_NOPTS_VALUE; + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AudioDownSampleContext *s = ctx->priv; + const int factor = s->factor; + AVFrame *in, *out; + int nb_samples; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + nb_samples = ff_inlink_queued_samples(inlink); + + if (nb_samples >= s->factor) { + nb_samples = (nb_samples / factor) * factor; + ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &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 < out->nb_samples; n++) + dst[n] = src[n * factor]; + } + + 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(outlink, out); + } + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static const AVFilterPad adownsample_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad adownsample_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +AVFilter ff_af_adownsample = { + .name = "adownsample", + .description = NULL_IF_CONFIG_SMALL("Downsample audio by integer factor."), + .query_formats = query_formats, + .priv_size = sizeof(AudioDownSampleContext), + .priv_class = &adownsample_class, + .activate = activate, + .inputs = adownsample_inputs, + .outputs = adownsample_outputs, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 29b372a1db..ddff0741a4 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -35,6 +35,7 @@ extern AVFilter ff_af_adeclick; extern AVFilter ff_af_adeclip; extern AVFilter ff_af_adelay; extern AVFilter ff_af_aderivative; +extern AVFilter ff_af_adownsample; extern AVFilter ff_af_aecho; extern AVFilter ff_af_aemphasis; extern AVFilter ff_af_aeval; -- 2.17.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".