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