mirror of https://github.com/FFmpeg/FFmpeg.git
Based on a patch by Stefano. Signed-off-by: Stefano Sabatini <stefano.sabatini-lala@poste.it>pull/2/head
parent
7c94740b84
commit
3a9e227fb1
5 changed files with 369 additions and 2 deletions
@ -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}}, |
||||
}; |
Loading…
Reference in new issue