From patchwork Fri Apr 19 13:20:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 12812 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 9A04C44960E for ; Fri, 19 Apr 2019 16:21:03 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 849E668A9B1; Fri, 19 Apr 2019 16:21:03 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 06E60687EE4 for ; Fri, 19 Apr 2019 16:20:56 +0300 (EEST) Received: by mail-wr1-f42.google.com with SMTP id s15so6905084wra.12 for ; Fri, 19 Apr 2019 06:20:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=ND/LTVWPuw0JfLpSs8Sz8jy8eUFT3H693BkgpdrQ1aM=; b=JA2rxC7KgQ3/RY3tZ1/cIYjLNtYDhu827q5FnIhV4U8YqJDYgyg+rZgXIh8cr6F2Er KvmRp/7Pe7FBti0osvXJJZfdClCB4/+kZjZVNwuDwflxJSFBi3DuQu8sBkGclVwhaG8X lF/mwGwO6eegczbPJzWOCB9VBzCbbQlHIwLoEoFlNtbwC7BlWu0Tni3MqolcLBpsYVlp 70XYcI3LAg4l3e3g2+yvDibXCkvAj/tRLdVBmnOPii0Wp+mYQPGKxhM61q2POSIQ9Tu8 AAa3pumzpdGVSQgjdHe3zPBrD2cpb7LjqcUzDwHnXH4F/pR2QOLi7gZjiebV8NM3d+SB xvHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=ND/LTVWPuw0JfLpSs8Sz8jy8eUFT3H693BkgpdrQ1aM=; b=pB8RICmCAlFd1qqLfG3tJUB61wvNvkClDpY8lRJCKiwk2UDG9UCNpcFIsDKy394uFM DzW+d3gXY1/u9FobKkLRkn9LX0Rvdo1/8N3NMsZIa+uvEgYJ6N+xnvygACp3y7i3wivT YfXNCMrwx+6WijEEX5ZZDa9FcRdEBeTJurIide0KhQuvURj6+iVSIw0AV+iphB21IiWb 6QCZOd8fAdzOrWivmb7PTKKuGz264ShGtU2reS7Hz9FwPm+W41y8JpUYvY96VWMBRayo dXvXNWLcnba9MFcunf2kJcvdlVKlvLXgC8+Iax2A6XCnaZ15ZsEeFj41eFPRgI8XOjE4 2u5g== X-Gm-Message-State: APjAAAUgdxPNbbhyW3PYUYEHQQag3Gk2vMlRqQIZGno2UaQVeNyv6J35 UtgMS854KZ2AIvuWhFp9HHyKs0nA X-Google-Smtp-Source: APXvYqyzvwB+0ec0aNodYiczpXQhvNyQhMxHmaI6jsIEpCowZW9c078ffYmonFaUT6n1JB0J04/KVg== X-Received: by 2002:adf:ee42:: with SMTP id w2mr2878462wro.161.1555680055155; Fri, 19 Apr 2019 06:20:55 -0700 (PDT) Received: from localhost.localdomain ([37.244.238.255]) by smtp.gmail.com with ESMTPSA id 192sm6605582wme.13.2019.04.19.06.20.54 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 19 Apr 2019 06:20:54 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Fri, 19 Apr 2019 15:20:40 +0200 Message-Id: <20190419132040.9182-2-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190419132040.9182-1-onemda@gmail.com> References: <20190419132040.9182-1-onemda@gmail.com> Subject: [FFmpeg-devel] [PATCH 2/2] avfilter: add audio sampling filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- Now with 2 filters merged into one and more sample formats. --- doc/filters.texi | 18 +++ libavfilter/Makefile | 1 + libavfilter/af_asampling.c | 280 +++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 4 files changed, 300 insertions(+) create mode 100644 libavfilter/af_asampling.c diff --git a/doc/filters.texi b/doc/filters.texi index 465eeb4732..34aa5cc173 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2018,6 +2018,24 @@ atrim=end=5,areverse @end example @end itemize +@section asampling +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 mode +Can be either @code{down} or @code{up}. Default is @code{up}. + +@item factor +Set factor of downsampling/upsampling. Default is @code{1}. +@end table + @section asetnsamples Set the number of samples per each output audio frame. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 682df45ef5..f814ad2c04 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -72,6 +72,7 @@ OBJS-$(CONFIG_APULSATOR_FILTER) += af_apulsator.o OBJS-$(CONFIG_AREALTIME_FILTER) += f_realtime.o OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o OBJS-$(CONFIG_AREVERSE_FILTER) += f_reverse.o +OBJS-$(CONFIG_ASAMPLING_FILTER) += af_asampling.o OBJS-$(CONFIG_ASELECT_FILTER) += f_select.o OBJS-$(CONFIG_ASENDCMD_FILTER) += f_sendcmd.o OBJS-$(CONFIG_ASETNSAMPLES_FILTER) += af_asetnsamples.o diff --git a/libavfilter/af_asampling.c b/libavfilter/af_asampling.c new file mode 100644 index 0000000000..9c12bb02ab --- /dev/null +++ b/libavfilter/af_asampling.c @@ -0,0 +1,280 @@ +/* + * 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 AudioSamplingContext { + const AVClass *class; + + int mode; + int factor; + + int min_in_samples; + int64_t next_pts; + + void (*filter)(AVFrame *out, AVFrame *in, int mode, int factor); +} AudioSamplingContext; + +#define OFFSET(x) offsetof(AudioSamplingContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption asampling_options[] = { + { "mode", "set sampling mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, A, "mode" }, + { "down", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "mode" }, + { "up", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "mode" }, + { "factor", "set downsampling factor", OFFSET(factor), AV_OPT_TYPE_INT, {.i64=1}, 1, 64, A }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(asampling); + +static int query_formats(AVFilterContext *ctx) +{ + AudioSamplingContext *s = ctx->priv; + AVFilterChannelLayouts *layouts; + AVFilterFormats *formats; + int sample_rates[] = { 44100, -1 }; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_DBL, 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; + + if (s->mode) + sample_rates[0] = avff->formats[0] * s->factor; + else + sample_rates[0] = avff->formats[0] / s->factor; + + return ff_formats_ref(ff_make_format_list(sample_rates), + &ctx->outputs[0]->in_samplerates); +} + +static void filter_dbl(AVFrame *out, AVFrame *in, int mode, int factor) +{ + const double *src = (const double *)in->extended_data[0]; + double *dst = (double *)out->extended_data[0]; + const int channels = in->channels; + + if (mode) { + for (int n = 0; n < in->nb_samples; n++) { + for (int c = 0; c < in->channels; c++) + dst[c] = *src++; + dst += factor * channels; + } + } else { + for (int n = 0; n < out->nb_samples; n++) { + for (int c = 0; c < in->channels; c++) + *dst++ = src[c]; + src += factor * channels; + } + } +} + +static void filter_flt(AVFrame *out, AVFrame *in, int mode, int factor) +{ + const float *src = (const float *)in->extended_data[0]; + float *dst = (float *)out->extended_data[0]; + const int channels = in->channels; + + if (mode) { + for (int n = 0; n < in->nb_samples; n++) { + for (int c = 0; c < in->channels; c++) + dst[c] = *src++; + dst += factor * channels; + } + } else { + for (int n = 0; n < out->nb_samples; n++) { + for (int c = 0; c < in->channels; c++) + *dst++ = src[c]; + src += factor * channels; + } + } +} + +static void filter_dblp(AVFrame *out, AVFrame *in, int mode, int factor) +{ + if (mode) { + 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]; + } + } else { + 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]; + } + } +} + +static void filter_fltp(AVFrame *out, AVFrame *in, int mode, int factor) +{ + if (mode) { + for (int c = 0; c < in->channels; c++) { + const float *src = (const float *)in->extended_data[c]; + float *dst = (float *)out->extended_data[c]; + + for (int n = 0; n < in->nb_samples; n++) + dst[n * factor] = src[n]; + } + } else { + for (int c = 0; c < in->channels; c++) { + const float *src = (const float *)in->extended_data[c]; + float *dst = (float *)out->extended_data[c]; + + for (int n = 0; n < out->nb_samples; n++) + dst[n] = src[n * factor]; + } + } +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + AudioSamplingContext *s = ctx->priv; + + s->next_pts = AV_NOPTS_VALUE; + + if (s->mode) + s->min_in_samples = 1; + else + s->min_in_samples = s->factor; + + switch (inlink->format) { + case AV_SAMPLE_FMT_FLT: s->filter = filter_flt; break; + case AV_SAMPLE_FMT_FLTP: s->filter = filter_fltp; break; + case AV_SAMPLE_FMT_DBL: s->filter = filter_dbl; break; + case AV_SAMPLE_FMT_DBLP: s->filter = filter_dblp; break; + } + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AudioSamplingContext *s = ctx->priv; + const int factor = s->factor; + AVFrame *in, *out; + int nb_samples, nb_out_samples; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + nb_samples = ff_inlink_queued_samples(inlink); + + if (nb_samples >= s->min_in_samples) { + if (!s->mode) + nb_samples = (nb_samples / factor) * factor; + ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); + + if (s->mode) + nb_out_samples = in->nb_samples * s->factor; + else + nb_out_samples = in->nb_samples / s->factor; + + out = ff_get_audio_buffer(outlink, nb_out_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } + + if (s->next_pts == AV_NOPTS_VALUE) + s->next_pts = in->pts; + + s->filter(out, in, s->mode, s->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 asampling_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad asampling_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + }, + { NULL } +}; + +AVFilter ff_af_asampling = { + .name = "asampling", + .description = NULL_IF_CONFIG_SMALL("Upsample or downsample audio by integer factor."), + .query_formats = query_formats, + .priv_size = sizeof(AudioSamplingContext), + .priv_class = &asampling_class, + .activate = activate, + .inputs = asampling_inputs, + .outputs = asampling_outputs, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 4d3039d6ba..4d669d4722 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -64,6 +64,7 @@ extern AVFilter ff_af_apulsator; extern AVFilter ff_af_arealtime; extern AVFilter ff_af_aresample; extern AVFilter ff_af_areverse; +extern AVFilter ff_af_asampling; extern AVFilter ff_af_aselect; extern AVFilter ff_af_asendcmd; extern AVFilter ff_af_asetnsamples;