From 3a9e227fb178a938a833121b838559c8f545e740 Mon Sep 17 00:00:00 2001 From: Mina Nagy Zaki Date: Wed, 17 Aug 2011 13:00:20 +0200 Subject: lavfi: add aresample filter Based on a patch by Stefano. Signed-off-by: Stefano Sabatini --- libavfilter/Makefile | 2 + libavfilter/af_aresample.c | 351 +++++++++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + libavfilter/avfilter.h | 4 +- 4 files changed, 356 insertions(+), 2 deletions(-) create mode 100644 libavfilter/af_aresample.c (limited to 'libavfilter') diff --git a/libavfilter/Makefile b/libavfilter/Makefile index d29202b499..aa4654e207 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -2,6 +2,7 @@ include $(SUBDIR)../config.mak NAME = avfilter FFLIBS = avutil +FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += avcodec FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec FFLIBS-$(CONFIG_SCALE_FILTER) += swscale FFLIBS-$(CONFIG_MP_FILTER) += avcodec @@ -20,6 +21,7 @@ OBJS-$(CONFIG_AVCODEC) += avcodec.o OBJS-$(CONFIG_AFORMAT_FILTER) += af_aformat.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o +OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c new file mode 100644 index 0000000000..31e1b5a4e3 --- /dev/null +++ b/libavfilter/af_aresample.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2011 Stefano Sabatini + * Copyright (c) 2011 Mina Nagy Zaki + * + * 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 + */ + +/** + * @file + * resampling audio filter + */ + +#include "libavutil/eval.h" +#include "libavcodec/avcodec.h" +#include "avfilter.h" +#include "internal.h" + +typedef struct { + struct AVResampleContext *resample; + int out_rate; + double ratio; + AVFilterBufferRef *outsamplesref; + int unconsumed_nb_samples, + max_cached_nb_samples; + int16_t *cached_data[8], + *resampled_data[8]; +} AResampleContext; + +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + AResampleContext *aresample = ctx->priv; + int ret; + + if (args) { + if ((ret = ff_parse_sample_rate(&aresample->out_rate, args, ctx)) < 0) + return ret; + } else { + aresample->out_rate = -1; + } + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AResampleContext *aresample = ctx->priv; + if (aresample->outsamplesref) { + int nb_channels = + av_get_channel_layout_nb_channels( + aresample->outsamplesref->audio->channel_layout); + avfilter_unref_buffer(aresample->outsamplesref); + while (nb_channels--) { + av_freep(&(aresample->cached_data[nb_channels])); + av_freep(&(aresample->resampled_data[nb_channels])); + } + } + + if (aresample->resample) + av_resample_close(aresample->resample); +} + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = ctx->inputs[0]; + AResampleContext *aresample = ctx->priv; + + if (aresample->out_rate == -1) + aresample->out_rate = outlink->sample_rate; + else + outlink->sample_rate = aresample->out_rate; + + //TODO: make the resampling parameters configurable + aresample->resample = av_resample_init(aresample->out_rate, inlink->sample_rate, + 16, 10, 0, 0.8); + + aresample->ratio = (double)outlink->sample_rate / inlink->sample_rate; + + av_log(ctx, AV_LOG_INFO, "r:%"PRId64"Hz -> r:%"PRId64"Hz\n", + inlink->sample_rate, outlink->sample_rate); + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + AVFilterFormats *formats = NULL; + + avfilter_add_format(&formats, AV_SAMPLE_FMT_S16); + if (!formats) + return AVERROR(ENOMEM); + avfilter_set_common_sample_formats(ctx, formats); + + formats = avfilter_all_channel_layouts(); + if (!formats) + return AVERROR(ENOMEM); + avfilter_set_common_channel_layouts(ctx, formats); + + formats = avfilter_all_packing_formats(); + if (!formats) + return AVERROR(ENOMEM); + avfilter_set_common_packing_formats(ctx, formats); + + return 0; +} + +static void deinterleave(int16_t **outp, int16_t *in, + int nb_channels, int nb_samples) +{ + int16_t *out[8]; + memcpy(out, outp, nb_channels * sizeof(int16_t*)); + + switch (nb_channels) { + case 2: + while (nb_samples--) { + *out[0]++ = *in++; + *out[1]++ = *in++; + } + break; + case 3: + while (nb_samples--) { + *out[0]++ = *in++; + *out[1]++ = *in++; + *out[2]++ = *in++; + } + break; + case 4: + while (nb_samples--) { + *out[0]++ = *in++; + *out[1]++ = *in++; + *out[2]++ = *in++; + *out[3]++ = *in++; + } + break; + case 5: + while (nb_samples--) { + *out[0]++ = *in++; + *out[1]++ = *in++; + *out[2]++ = *in++; + *out[3]++ = *in++; + *out[4]++ = *in++; + } + break; + case 6: + while (nb_samples--) { + *out[0]++ = *in++; + *out[1]++ = *in++; + *out[2]++ = *in++; + *out[3]++ = *in++; + *out[4]++ = *in++; + *out[5]++ = *in++; + } + break; + case 8: + while (nb_samples--) { + *out[0]++ = *in++; + *out[1]++ = *in++; + *out[2]++ = *in++; + *out[3]++ = *in++; + *out[4]++ = *in++; + *out[5]++ = *in++; + *out[6]++ = *in++; + *out[7]++ = *in++; + } + break; + } +} + +static void interleave(int16_t *out, int16_t **inp, + int nb_channels, int nb_samples) +{ + int16_t *in[8]; + memcpy(in, inp, nb_channels * sizeof(int16_t*)); + + switch (nb_channels) { + case 2: + while (nb_samples--) { + *out++ = *in[0]++; + *out++ = *in[1]++; + } + break; + case 3: + while (nb_samples--) { + *out++ = *in[0]++; + *out++ = *in[1]++; + *out++ = *in[2]++; + } + break; + case 4: + while (nb_samples--) { + *out++ = *in[0]++; + *out++ = *in[1]++; + *out++ = *in[2]++; + *out++ = *in[3]++; + } + break; + case 5: + while (nb_samples--) { + *out++ = *in[0]++; + *out++ = *in[1]++; + *out++ = *in[2]++; + *out++ = *in[3]++; + *out++ = *in[4]++; + } + break; + case 6: + while (nb_samples--) { + *out++ = *in[0]++; + *out++ = *in[1]++; + *out++ = *in[2]++; + *out++ = *in[3]++; + *out++ = *in[4]++; + *out++ = *in[5]++; + } + break; + case 8: + while (nb_samples--) { + *out++ = *in[0]++; + *out++ = *in[1]++; + *out++ = *in[2]++; + *out++ = *in[3]++; + *out++ = *in[4]++; + *out++ = *in[5]++; + *out++ = *in[6]++; + *out++ = *in[7]++; + } + break; + } +} + +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamplesref) +{ + AResampleContext *aresample = inlink->dst->priv; + AVFilterLink * const outlink = inlink->dst->outputs[0]; + int i, + in_nb_samples = insamplesref->audio->nb_samples, + cached_nb_samples = in_nb_samples + aresample->unconsumed_nb_samples, + requested_out_nb_samples = aresample->ratio * cached_nb_samples, + nb_channels = + av_get_channel_layout_nb_channels(inlink->channel_layout); + + if (cached_nb_samples > aresample->max_cached_nb_samples) { + for (i = 0; i < nb_channels; i++) { + aresample->cached_data[i] = + av_realloc(aresample->cached_data[i], cached_nb_samples * sizeof(int16_t)); + aresample->resampled_data[i] = + av_realloc(aresample->resampled_data[i], + FFALIGN(sizeof(int16_t) * requested_out_nb_samples, 16)); + + if (aresample->cached_data[i] == NULL || aresample->resampled_data[i] == NULL) + return; + } + aresample->max_cached_nb_samples = cached_nb_samples; + + if (aresample->outsamplesref) + avfilter_unref_buffer(aresample->outsamplesref); + + aresample->outsamplesref = avfilter_get_audio_buffer(outlink, + AV_PERM_WRITE | AV_PERM_REUSE2, + inlink->format, + requested_out_nb_samples, + insamplesref->audio->channel_layout, + insamplesref->audio->planar); + + avfilter_copy_buffer_ref_props(aresample->outsamplesref, insamplesref); + aresample->outsamplesref->pts = + insamplesref->pts / inlink->sample_rate * outlink->sample_rate; + aresample->outsamplesref->audio->sample_rate = outlink->sample_rate; + outlink->out_buf = aresample->outsamplesref; + } + + /* av_resample() works with planar audio buffers */ + if (!inlink->planar && nb_channels > 1) { + int16_t *out[8]; + for (i = 0; i < nb_channels; i++) + out[i] = aresample->cached_data[i] + aresample->unconsumed_nb_samples; + + deinterleave(out, (int16_t *)insamplesref->data[0], + nb_channels, in_nb_samples); + } else { + for (i = 0; i < nb_channels; i++) + memcpy(aresample->cached_data[i] + aresample->unconsumed_nb_samples, + insamplesref->data[i], + in_nb_samples * sizeof(int16_t)); + } + + for (i = 0; i < nb_channels; i++) { + int consumed_nb_samples; + const int is_last = i+1 == nb_channels; + + aresample->outsamplesref->audio->nb_samples = + av_resample(aresample->resample, + aresample->resampled_data[i], aresample->cached_data[i], + &consumed_nb_samples, + cached_nb_samples, + requested_out_nb_samples, is_last); + + /* move unconsumed data back to the beginning of the cache */ + aresample->unconsumed_nb_samples = cached_nb_samples - consumed_nb_samples; + memmove(aresample->cached_data[i], + aresample->cached_data[i] + consumed_nb_samples, + aresample->unconsumed_nb_samples * sizeof(int16_t)); + } + + + /* copy resampled data to the output samplesref */ + if (!inlink->planar && nb_channels > 1) { + interleave((int16_t *)aresample->outsamplesref->data[0], + aresample->resampled_data, + nb_channels, aresample->outsamplesref->audio->nb_samples); + } else { + for (i = 0; i < nb_channels; i++) + memcpy(aresample->outsamplesref->data[i], aresample->resampled_data[i], + aresample->outsamplesref->audio->nb_samples * sizeof(int16_t)); + } + + avfilter_filter_samples(outlink, avfilter_ref_buffer(aresample->outsamplesref, ~0)); + avfilter_unref_buffer(insamplesref); +} + +AVFilter avfilter_af_aresample = { + .name = "aresample", + .description = NULL_IF_CONFIG_SMALL("Resample audio data."), + .init = init, + .uninit = uninit, + .query_formats = query_formats, + .priv_size = sizeof(AResampleContext), + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_samples = filter_samples, + .min_perms = AV_PERM_READ, }, + { .name = NULL}}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .config_props = config_output, + .type = AVMEDIA_TYPE_AUDIO, }, + { .name = NULL}}, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index af829d62e4..f623d00979 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -36,6 +36,7 @@ void avfilter_register_all(void) REGISTER_FILTER (AFORMAT, aformat, af); REGISTER_FILTER (ANULL, anull, af); + REGISTER_FILTER (ARESAMPLE, aresample, af); REGISTER_FILTER (ANULLSRC, anullsrc, asrc); diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 474cd90b0d..4df18d200c 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -29,8 +29,8 @@ #include "libavutil/rational.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 31 -#define LIBAVFILTER_VERSION_MICRO 1 +#define LIBAVFILTER_VERSION_MINOR 32 +#define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ -- cgit v1.2.3