]> git.sesse.net Git - ffmpeg/blob - libavfilter/af_aresample.c
lavfi: fix resample with differing formats
[ffmpeg] / libavfilter / af_aresample.c
1 /*
2  * Copyright (c) 2011 Stefano Sabatini
3  * Copyright (c) 2011 Mina Nagy Zaki
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  * resampling audio filter
25  */
26
27 #include "libswresample/swresample.h"
28 #include "avfilter.h"
29 #include "audio.h"
30 #include "internal.h"
31
32 typedef struct {
33     int out_rate;
34     double ratio;
35     struct SwrContext *swr;
36 } AResampleContext;
37
38 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
39 {
40     AResampleContext *aresample = ctx->priv;
41     int ret;
42
43     if (args) {
44         if ((ret = ff_parse_sample_rate(&aresample->out_rate, args, ctx)) < 0)
45             return ret;
46     } else {
47         aresample->out_rate = -1;
48     }
49
50     return 0;
51 }
52
53 static av_cold void uninit(AVFilterContext *ctx)
54 {
55     AResampleContext *aresample = ctx->priv;
56     swr_free(&aresample->swr);
57 }
58
59 static int query_formats(AVFilterContext *ctx)
60 {
61     AResampleContext *aresample = ctx->priv;
62
63     AVFilterLink *inlink  = ctx->inputs[0];
64     AVFilterLink *outlink = ctx->outputs[0];
65
66     AVFilterFormats        *in_formats      = avfilter_all_formats(AVMEDIA_TYPE_AUDIO);
67     AVFilterFormats        *out_formats     = avfilter_all_formats(AVMEDIA_TYPE_AUDIO);
68     AVFilterFormats        *in_samplerates  = ff_all_samplerates();
69     AVFilterFormats        *out_samplerates;
70
71
72     AVFilterChannelLayouts *in_layouts      = ff_all_channel_layouts();
73     AVFilterChannelLayouts *out_layouts     = ff_all_channel_layouts();
74
75     avfilter_formats_ref(in_formats,  &inlink->out_formats);
76     avfilter_formats_ref(out_formats, &outlink->in_formats);
77
78     avfilter_formats_ref(in_samplerates,  &inlink->out_samplerates);
79
80     ff_channel_layouts_ref(in_layouts,  &inlink->out_channel_layouts);
81     ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts);
82
83     if(aresample->out_rate > 0) {
84         int sample_rates[] = { aresample->out_rate, -1 };
85         ff_set_common_samplerates(ctx, avfilter_make_format_list(sample_rates));
86     } else {
87         out_samplerates = ff_all_samplerates();
88         avfilter_formats_ref(out_samplerates, &outlink->in_samplerates);
89     }
90
91     return 0;
92 }
93
94
95 static int config_output(AVFilterLink *outlink)
96 {
97     int ret;
98     AVFilterContext *ctx = outlink->src;
99     AVFilterLink *inlink = ctx->inputs[0];
100     AResampleContext *aresample = ctx->priv;
101
102     if (aresample->out_rate == -1)
103         aresample->out_rate = outlink->sample_rate;
104     else
105         outlink->sample_rate = aresample->out_rate;
106     outlink->time_base = (AVRational) {1, aresample->out_rate};
107
108     //TODO: make the resampling parameters (filter size, phrase shift, linear, cutoff) configurable
109     aresample->swr = swr_alloc_set_opts(aresample->swr,
110                                         outlink->channel_layout, outlink->format, outlink->sample_rate,
111                                         inlink->channel_layout, inlink->format, inlink->sample_rate,
112                                         0, ctx);
113     if (!aresample->swr)
114         return AVERROR(ENOMEM);
115     ret = swr_init(aresample->swr);
116     if (ret < 0)
117         return ret;
118
119     aresample->ratio = (double)outlink->sample_rate / inlink->sample_rate;
120
121     av_log(ctx, AV_LOG_INFO, "r:%"PRId64"Hz -> r:%"PRId64"Hz\n",
122            inlink->sample_rate, outlink->sample_rate);
123     return 0;
124 }
125
126 static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamplesref)
127 {
128     AResampleContext *aresample = inlink->dst->priv;
129     const int n_in  = insamplesref->audio->nb_samples;
130     int n_out       = n_in * aresample->ratio;
131     AVFilterLink *const outlink = inlink->dst->outputs[0];
132     AVFilterBufferRef *outsamplesref = ff_get_audio_buffer(outlink, AV_PERM_WRITE, n_out);
133
134     n_out = swr_convert(aresample->swr, outsamplesref->data, n_out,
135                                  (void *)insamplesref->data, n_in);
136
137     avfilter_copy_buffer_ref_props(outsamplesref, insamplesref);
138     outsamplesref->audio->sample_rate = outlink->sample_rate;
139     outsamplesref->audio->nb_samples  = n_out;
140     outsamplesref->pts = insamplesref->pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE :
141         av_rescale(outlink->sample_rate, insamplesref->pts, inlink ->sample_rate);
142
143     ff_filter_samples(outlink, outsamplesref);
144     avfilter_unref_buffer(insamplesref);
145 }
146
147 AVFilter avfilter_af_aresample = {
148     .name          = "aresample",
149     .description   = NULL_IF_CONFIG_SMALL("Resample audio data."),
150     .init          = init,
151     .uninit        = uninit,
152     .query_formats = query_formats,
153     .priv_size     = sizeof(AResampleContext),
154
155     .inputs    = (const AVFilterPad[]) {{ .name      = "default",
156                                     .type            = AVMEDIA_TYPE_AUDIO,
157                                     .filter_samples  = filter_samples,
158                                     .min_perms       = AV_PERM_READ, },
159                                   { .name = NULL}},
160     .outputs   = (const AVFilterPad[]) {{ .name      = "default",
161                                     .config_props    = config_output,
162                                     .type            = AVMEDIA_TYPE_AUDIO, },
163                                   { .name = NULL}},
164 };