lavfi: add aresample filter

Based on a patch by Stefano.

Signed-off-by: Stefano Sabatini <stefano.sabatini-lala@poste.it>
This commit is contained in:
Mina Nagy Zaki 2011-08-17 13:00:20 +02:00 committed by Stefano Sabatini
parent 7c94740b84
commit 3a9e227fb1
5 changed files with 369 additions and 2 deletions

View File

@ -124,6 +124,19 @@ aformat=s16:mono\\,stereo:all
Pass the audio source unchanged to the output.
@section aresample
Resample the input audio to the specified sample rate.
The filter accepts exactly one parameter, the output sample rate. If not
specified then the filter will automatically convert between its input
and output sample rates.
For example, to resample the input audio to 44100Hz:
@example
aresample=44100
@end example
@c man end AUDIO FILTERS
@chapter Audio Sources

View File

@ -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

351
libavfilter/af_aresample.c Normal file
View File

@ -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}},
};

View File

@ -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);

View File

@ -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, \