Commit | Line | Data |
---|---|---|
d371e7b9 AK |
1 | /* |
2 | * | |
3 | * This file is part of Libav. | |
4 | * | |
5 | * Libav is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2.1 of the License, or (at your option) any later version. | |
9 | * | |
10 | * Libav is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with Libav; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
18 | */ | |
19 | ||
20 | /** | |
21 | * @file | |
22 | * sample format and channel layout conversion audio filter | |
23 | */ | |
24 | ||
25 | #include "libavutil/avassert.h" | |
26 | #include "libavutil/avstring.h" | |
1d9c2dc8 | 27 | #include "libavutil/common.h" |
9f122356 | 28 | #include "libavutil/dict.h" |
d371e7b9 AK |
29 | #include "libavutil/mathematics.h" |
30 | #include "libavutil/opt.h" | |
31 | ||
32 | #include "libavresample/avresample.h" | |
33 | ||
34 | #include "audio.h" | |
35 | #include "avfilter.h" | |
ff1f51a8 | 36 | #include "formats.h" |
d371e7b9 AK |
37 | #include "internal.h" |
38 | ||
39 | typedef struct ResampleContext { | |
40 | AVAudioResampleContext *avr; | |
9f122356 | 41 | AVDictionary *options; |
d371e7b9 AK |
42 | |
43 | int64_t next_pts; | |
1ffb6456 | 44 | |
565e4993 | 45 | /* set by filter_frame() to signal an output frame to request_frame() */ |
1ffb6456 | 46 | int got_output; |
d371e7b9 AK |
47 | } ResampleContext; |
48 | ||
9f122356 JR |
49 | static av_cold int init(AVFilterContext *ctx, const char *args) |
50 | { | |
51 | ResampleContext *s = ctx->priv; | |
52 | ||
53 | if (args) { | |
54 | int ret = av_dict_parse_string(&s->options, args, "=", ":", 0); | |
55 | if (ret < 0) { | |
56 | av_log(ctx, AV_LOG_ERROR, "error setting option string: %s\n", args); | |
57 | return ret; | |
58 | } | |
59 | ||
60 | /* do not allow the user to override basic format options */ | |
61 | av_dict_set(&s->options, "in_channel_layout", NULL, 0); | |
62 | av_dict_set(&s->options, "out_channel_layout", NULL, 0); | |
63 | av_dict_set(&s->options, "in_sample_fmt", NULL, 0); | |
64 | av_dict_set(&s->options, "out_sample_fmt", NULL, 0); | |
65 | av_dict_set(&s->options, "in_sample_rate", NULL, 0); | |
66 | av_dict_set(&s->options, "out_sample_rate", NULL, 0); | |
67 | } | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
d371e7b9 AK |
72 | static av_cold void uninit(AVFilterContext *ctx) |
73 | { | |
74 | ResampleContext *s = ctx->priv; | |
75 | ||
76 | if (s->avr) { | |
77 | avresample_close(s->avr); | |
78 | avresample_free(&s->avr); | |
79 | } | |
9f122356 | 80 | av_dict_free(&s->options); |
d371e7b9 AK |
81 | } |
82 | ||
83 | static int query_formats(AVFilterContext *ctx) | |
84 | { | |
85 | AVFilterLink *inlink = ctx->inputs[0]; | |
86 | AVFilterLink *outlink = ctx->outputs[0]; | |
87 | ||
b74a1da4 AK |
88 | AVFilterFormats *in_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO); |
89 | AVFilterFormats *out_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO); | |
ff1f51a8 AK |
90 | AVFilterFormats *in_samplerates = ff_all_samplerates(); |
91 | AVFilterFormats *out_samplerates = ff_all_samplerates(); | |
92 | AVFilterChannelLayouts *in_layouts = ff_all_channel_layouts(); | |
93 | AVFilterChannelLayouts *out_layouts = ff_all_channel_layouts(); | |
d371e7b9 | 94 | |
b74a1da4 AK |
95 | ff_formats_ref(in_formats, &inlink->out_formats); |
96 | ff_formats_ref(out_formats, &outlink->in_formats); | |
d371e7b9 | 97 | |
b74a1da4 AK |
98 | ff_formats_ref(in_samplerates, &inlink->out_samplerates); |
99 | ff_formats_ref(out_samplerates, &outlink->in_samplerates); | |
ff1f51a8 AK |
100 | |
101 | ff_channel_layouts_ref(in_layouts, &inlink->out_channel_layouts); | |
102 | ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts); | |
103 | ||
d371e7b9 AK |
104 | return 0; |
105 | } | |
106 | ||
107 | static int config_output(AVFilterLink *outlink) | |
108 | { | |
109 | AVFilterContext *ctx = outlink->src; | |
110 | AVFilterLink *inlink = ctx->inputs[0]; | |
111 | ResampleContext *s = ctx->priv; | |
112 | char buf1[64], buf2[64]; | |
113 | int ret; | |
114 | ||
115 | if (s->avr) { | |
116 | avresample_close(s->avr); | |
117 | avresample_free(&s->avr); | |
118 | } | |
119 | ||
120 | if (inlink->channel_layout == outlink->channel_layout && | |
121 | inlink->sample_rate == outlink->sample_rate && | |
7b556be6 JR |
122 | (inlink->format == outlink->format || |
123 | (av_get_channel_layout_nb_channels(inlink->channel_layout) == 1 && | |
124 | av_get_channel_layout_nb_channels(outlink->channel_layout) == 1 && | |
125 | av_get_planar_sample_fmt(inlink->format) == | |
126 | av_get_planar_sample_fmt(outlink->format)))) | |
d371e7b9 AK |
127 | return 0; |
128 | ||
129 | if (!(s->avr = avresample_alloc_context())) | |
130 | return AVERROR(ENOMEM); | |
131 | ||
9f122356 JR |
132 | if (s->options) { |
133 | AVDictionaryEntry *e = NULL; | |
134 | while ((e = av_dict_get(s->options, "", e, AV_DICT_IGNORE_SUFFIX))) | |
135 | av_log(ctx, AV_LOG_VERBOSE, "lavr option: %s=%s\n", e->key, e->value); | |
136 | ||
137 | av_opt_set_dict(s->avr, &s->options); | |
138 | } | |
139 | ||
d371e7b9 AK |
140 | av_opt_set_int(s->avr, "in_channel_layout", inlink ->channel_layout, 0); |
141 | av_opt_set_int(s->avr, "out_channel_layout", outlink->channel_layout, 0); | |
142 | av_opt_set_int(s->avr, "in_sample_fmt", inlink ->format, 0); | |
143 | av_opt_set_int(s->avr, "out_sample_fmt", outlink->format, 0); | |
144 | av_opt_set_int(s->avr, "in_sample_rate", inlink ->sample_rate, 0); | |
145 | av_opt_set_int(s->avr, "out_sample_rate", outlink->sample_rate, 0); | |
146 | ||
d371e7b9 AK |
147 | if ((ret = avresample_open(s->avr)) < 0) |
148 | return ret; | |
149 | ||
150 | outlink->time_base = (AVRational){ 1, outlink->sample_rate }; | |
151 | s->next_pts = AV_NOPTS_VALUE; | |
152 | ||
153 | av_get_channel_layout_string(buf1, sizeof(buf1), | |
154 | -1, inlink ->channel_layout); | |
155 | av_get_channel_layout_string(buf2, sizeof(buf2), | |
156 | -1, outlink->channel_layout); | |
157 | av_log(ctx, AV_LOG_VERBOSE, | |
e0d8427d | 158 | "fmt:%s srate:%d cl:%s -> fmt:%s srate:%d cl:%s\n", |
d371e7b9 AK |
159 | av_get_sample_fmt_name(inlink ->format), inlink ->sample_rate, buf1, |
160 | av_get_sample_fmt_name(outlink->format), outlink->sample_rate, buf2); | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static int request_frame(AVFilterLink *outlink) | |
166 | { | |
167 | AVFilterContext *ctx = outlink->src; | |
168 | ResampleContext *s = ctx->priv; | |
1ffb6456 AK |
169 | int ret = 0; |
170 | ||
171 | s->got_output = 0; | |
172 | while (ret >= 0 && !s->got_output) | |
173 | ret = ff_request_frame(ctx->inputs[0]); | |
d371e7b9 AK |
174 | |
175 | /* flush the lavr delay buffer */ | |
176 | if (ret == AVERROR_EOF && s->avr) { | |
177 | AVFilterBufferRef *buf; | |
178 | int nb_samples = av_rescale_rnd(avresample_get_delay(s->avr), | |
179 | outlink->sample_rate, | |
180 | ctx->inputs[0]->sample_rate, | |
181 | AV_ROUND_UP); | |
182 | ||
183 | if (!nb_samples) | |
184 | return ret; | |
185 | ||
186 | buf = ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples); | |
187 | if (!buf) | |
188 | return AVERROR(ENOMEM); | |
189 | ||
e7ba5b1d | 190 | ret = avresample_convert(s->avr, buf->extended_data, |
d371e7b9 AK |
191 | buf->linesize[0], nb_samples, |
192 | NULL, 0, 0); | |
193 | if (ret <= 0) { | |
194 | avfilter_unref_buffer(buf); | |
195 | return (ret == 0) ? AVERROR_EOF : ret; | |
196 | } | |
197 | ||
198 | buf->pts = s->next_pts; | |
565e4993 | 199 | return ff_filter_frame(outlink, buf); |
d371e7b9 AK |
200 | } |
201 | return ret; | |
202 | } | |
203 | ||
565e4993 | 204 | static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf) |
d371e7b9 AK |
205 | { |
206 | AVFilterContext *ctx = inlink->dst; | |
207 | ResampleContext *s = ctx->priv; | |
208 | AVFilterLink *outlink = ctx->outputs[0]; | |
cd991462 | 209 | int ret; |
d371e7b9 AK |
210 | |
211 | if (s->avr) { | |
212 | AVFilterBufferRef *buf_out; | |
cd991462 | 213 | int delay, nb_samples; |
d371e7b9 AK |
214 | |
215 | /* maximum possible samples lavr can output */ | |
216 | delay = avresample_get_delay(s->avr); | |
217 | nb_samples = av_rescale_rnd(buf->audio->nb_samples + delay, | |
218 | outlink->sample_rate, inlink->sample_rate, | |
219 | AV_ROUND_UP); | |
220 | ||
221 | buf_out = ff_get_audio_buffer(outlink, AV_PERM_WRITE, nb_samples); | |
cd991462 AK |
222 | if (!buf_out) { |
223 | ret = AVERROR(ENOMEM); | |
224 | goto fail; | |
225 | } | |
226 | ||
e7ba5b1d | 227 | ret = avresample_convert(s->avr, buf_out->extended_data, |
d371e7b9 | 228 | buf_out->linesize[0], nb_samples, |
e7ba5b1d | 229 | buf->extended_data, buf->linesize[0], |
d371e7b9 | 230 | buf->audio->nb_samples); |
ac9a8956 | 231 | if (ret <= 0) { |
cd991462 | 232 | avfilter_unref_buffer(buf_out); |
ac9a8956 JG |
233 | if (ret < 0) |
234 | goto fail; | |
cd991462 | 235 | } |
d371e7b9 AK |
236 | |
237 | av_assert0(!avresample_available(s->avr)); | |
238 | ||
239 | if (s->next_pts == AV_NOPTS_VALUE) { | |
240 | if (buf->pts == AV_NOPTS_VALUE) { | |
241 | av_log(ctx, AV_LOG_WARNING, "First timestamp is missing, " | |
242 | "assuming 0.\n"); | |
243 | s->next_pts = 0; | |
244 | } else | |
245 | s->next_pts = av_rescale_q(buf->pts, inlink->time_base, | |
246 | outlink->time_base); | |
247 | } | |
248 | ||
249 | if (ret > 0) { | |
250 | buf_out->audio->nb_samples = ret; | |
251 | if (buf->pts != AV_NOPTS_VALUE) { | |
252 | buf_out->pts = av_rescale_q(buf->pts, inlink->time_base, | |
253 | outlink->time_base) - | |
254 | av_rescale(delay, outlink->sample_rate, | |
255 | inlink->sample_rate); | |
256 | } else | |
257 | buf_out->pts = s->next_pts; | |
258 | ||
259 | s->next_pts = buf_out->pts + buf_out->audio->nb_samples; | |
260 | ||
565e4993 | 261 | ret = ff_filter_frame(outlink, buf_out); |
1ffb6456 | 262 | s->got_output = 1; |
d371e7b9 | 263 | } |
cd991462 AK |
264 | |
265 | fail: | |
d371e7b9 | 266 | avfilter_unref_buffer(buf); |
1ffb6456 | 267 | } else { |
7b556be6 | 268 | buf->format = outlink->format; |
565e4993 | 269 | ret = ff_filter_frame(outlink, buf); |
1ffb6456 AK |
270 | s->got_output = 1; |
271 | } | |
cd991462 AK |
272 | |
273 | return ret; | |
d371e7b9 AK |
274 | } |
275 | ||
568c70e7 MR |
276 | static const AVFilterPad avfilter_af_resample_inputs[] = { |
277 | { | |
278 | .name = "default", | |
279 | .type = AVMEDIA_TYPE_AUDIO, | |
565e4993 | 280 | .filter_frame = filter_frame, |
568c70e7 MR |
281 | .min_perms = AV_PERM_READ |
282 | }, | |
283 | { NULL } | |
284 | }; | |
285 | ||
286 | static const AVFilterPad avfilter_af_resample_outputs[] = { | |
287 | { | |
288 | .name = "default", | |
289 | .type = AVMEDIA_TYPE_AUDIO, | |
290 | .config_props = config_output, | |
291 | .request_frame = request_frame | |
292 | }, | |
293 | { NULL } | |
294 | }; | |
295 | ||
d371e7b9 AK |
296 | AVFilter avfilter_af_resample = { |
297 | .name = "resample", | |
298 | .description = NULL_IF_CONFIG_SMALL("Audio resampling and conversion."), | |
299 | .priv_size = sizeof(ResampleContext), | |
300 | ||
9f122356 | 301 | .init = init, |
d371e7b9 AK |
302 | .uninit = uninit, |
303 | .query_formats = query_formats, | |
304 | ||
568c70e7 MR |
305 | .inputs = avfilter_af_resample_inputs, |
306 | .outputs = avfilter_af_resample_outputs, | |
d371e7b9 | 307 | }; |