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 | #include "libavresample/avresample.h" |
||
20 | #include "libavutil/attributes.h" |
||
21 | #include "libavutil/audio_fifo.h" |
||
22 | #include "libavutil/common.h" |
||
23 | #include "libavutil/mathematics.h" |
||
24 | #include "libavutil/opt.h" |
||
25 | #include "libavutil/samplefmt.h" |
||
26 | |||
27 | #include "audio.h" |
||
28 | #include "avfilter.h" |
||
29 | #include "internal.h" |
||
30 | |||
31 | typedef struct ASyncContext { |
||
32 | const AVClass *class; |
||
33 | |||
34 | AVAudioResampleContext *avr; |
||
35 | int64_t pts; ///< timestamp in samples of the first sample in fifo |
||
36 | int min_delta; ///< pad/trim min threshold in samples |
||
37 | int first_frame; ///< 1 until filter_frame() has processed at least 1 frame with a pts != AV_NOPTS_VALUE |
||
38 | int64_t first_pts; ///< user-specified first expected pts, in samples |
||
39 | int comp; ///< current resample compensation |
||
40 | |||
41 | /* options */ |
||
42 | int resample; |
||
43 | float min_delta_sec; |
||
44 | int max_comp; |
||
45 | |||
46 | /* set by filter_frame() to signal an output frame to request_frame() */ |
||
47 | int got_output; |
||
48 | } ASyncContext; |
||
49 | |||
50 | #define OFFSET(x) offsetof(ASyncContext, x) |
||
51 | #define A AV_OPT_FLAG_AUDIO_PARAM |
||
52 | #define F AV_OPT_FLAG_FILTERING_PARAM |
||
53 | static const AVOption asyncts_options[] = { |
||
54 | { "compensate", "Stretch/squeeze the data to make it match the timestamps", OFFSET(resample), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, A|F }, |
||
55 | { "min_delta", "Minimum difference between timestamps and audio data " |
||
56 | "(in seconds) to trigger padding/trimmin the data.", OFFSET(min_delta_sec), AV_OPT_TYPE_FLOAT, { .dbl = 0.1 }, 0, INT_MAX, A|F }, |
||
57 | { "max_comp", "Maximum compensation in samples per second.", OFFSET(max_comp), AV_OPT_TYPE_INT, { .i64 = 500 }, 0, INT_MAX, A|F }, |
||
58 | { "first_pts", "Assume the first pts should be this value.", OFFSET(first_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, A|F }, |
||
59 | { NULL } |
||
60 | }; |
||
61 | |||
62 | AVFILTER_DEFINE_CLASS(asyncts); |
||
63 | |||
64 | static av_cold int init(AVFilterContext *ctx) |
||
65 | { |
||
66 | ASyncContext *s = ctx->priv; |
||
67 | |||
68 | s->pts = AV_NOPTS_VALUE; |
||
69 | s->first_frame = 1; |
||
70 | |||
71 | return 0; |
||
72 | } |
||
73 | |||
74 | static av_cold void uninit(AVFilterContext *ctx) |
||
75 | { |
||
76 | ASyncContext *s = ctx->priv; |
||
77 | |||
78 | if (s->avr) { |
||
79 | avresample_close(s->avr); |
||
80 | avresample_free(&s->avr); |
||
81 | } |
||
82 | } |
||
83 | |||
84 | static int config_props(AVFilterLink *link) |
||
85 | { |
||
86 | ASyncContext *s = link->src->priv; |
||
87 | int ret; |
||
88 | |||
89 | s->min_delta = s->min_delta_sec * link->sample_rate; |
||
90 | link->time_base = (AVRational){1, link->sample_rate}; |
||
91 | |||
92 | s->avr = avresample_alloc_context(); |
||
93 | if (!s->avr) |
||
94 | return AVERROR(ENOMEM); |
||
95 | |||
96 | av_opt_set_int(s->avr, "in_channel_layout", link->channel_layout, 0); |
||
97 | av_opt_set_int(s->avr, "out_channel_layout", link->channel_layout, 0); |
||
98 | av_opt_set_int(s->avr, "in_sample_fmt", link->format, 0); |
||
99 | av_opt_set_int(s->avr, "out_sample_fmt", link->format, 0); |
||
100 | av_opt_set_int(s->avr, "in_sample_rate", link->sample_rate, 0); |
||
101 | av_opt_set_int(s->avr, "out_sample_rate", link->sample_rate, 0); |
||
102 | |||
103 | if (s->resample) |
||
104 | av_opt_set_int(s->avr, "force_resampling", 1, 0); |
||
105 | |||
106 | if ((ret = avresample_open(s->avr)) < 0) |
||
107 | return ret; |
||
108 | |||
109 | return 0; |
||
110 | } |
||
111 | |||
112 | /* get amount of data currently buffered, in samples */ |
||
113 | static int64_t get_delay(ASyncContext *s) |
||
114 | { |
||
115 | return avresample_available(s->avr) + avresample_get_delay(s->avr); |
||
116 | } |
||
117 | |||
118 | static void handle_trimming(AVFilterContext *ctx) |
||
119 | { |
||
120 | ASyncContext *s = ctx->priv; |
||
121 | |||
122 | if (s->pts < s->first_pts) { |
||
123 | int delta = FFMIN(s->first_pts - s->pts, avresample_available(s->avr)); |
||
124 | av_log(ctx, AV_LOG_VERBOSE, "Trimming %d samples from start\n", |
||
125 | delta); |
||
126 | avresample_read(s->avr, NULL, delta); |
||
127 | s->pts += delta; |
||
128 | } else if (s->first_frame) |
||
129 | s->pts = s->first_pts; |
||
130 | } |
||
131 | |||
132 | static int request_frame(AVFilterLink *link) |
||
133 | { |
||
134 | AVFilterContext *ctx = link->src; |
||
135 | ASyncContext *s = ctx->priv; |
||
136 | int ret = 0; |
||
137 | int nb_samples; |
||
138 | |||
139 | s->got_output = 0; |
||
140 | while (ret >= 0 && !s->got_output) |
||
141 | ret = ff_request_frame(ctx->inputs[0]); |
||
142 | |||
143 | /* flush the fifo */ |
||
144 | if (ret == AVERROR_EOF) { |
||
145 | if (s->first_pts != AV_NOPTS_VALUE) |
||
146 | handle_trimming(ctx); |
||
147 | |||
148 | if (nb_samples = get_delay(s)) { |
||
149 | AVFrame *buf = ff_get_audio_buffer(link, nb_samples); |
||
150 | if (!buf) |
||
151 | return AVERROR(ENOMEM); |
||
152 | ret = avresample_convert(s->avr, buf->extended_data, |
||
153 | buf->linesize[0], nb_samples, NULL, 0, 0); |
||
154 | if (ret <= 0) { |
||
155 | av_frame_free(&buf); |
||
156 | return (ret < 0) ? ret : AVERROR_EOF; |
||
157 | } |
||
158 | |||
159 | buf->pts = s->pts; |
||
160 | return ff_filter_frame(link, buf); |
||
161 | } |
||
162 | } |
||
163 | |||
164 | return ret; |
||
165 | } |
||
166 | |||
167 | static int write_to_fifo(ASyncContext *s, AVFrame *buf) |
||
168 | { |
||
169 | int ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data, |
||
170 | buf->linesize[0], buf->nb_samples); |
||
171 | av_frame_free(&buf); |
||
172 | return ret; |
||
173 | } |
||
174 | |||
175 | static int filter_frame(AVFilterLink *inlink, AVFrame *buf) |
||
176 | { |
||
177 | AVFilterContext *ctx = inlink->dst; |
||
178 | ASyncContext *s = ctx->priv; |
||
179 | AVFilterLink *outlink = ctx->outputs[0]; |
||
180 | int nb_channels = av_get_channel_layout_nb_channels(buf->channel_layout); |
||
181 | int64_t pts = (buf->pts == AV_NOPTS_VALUE) ? buf->pts : |
||
182 | av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); |
||
183 | int out_size, ret; |
||
184 | int64_t delta; |
||
185 | int64_t new_pts; |
||
186 | |||
187 | /* buffer data until we get the next timestamp */ |
||
188 | if (s->pts == AV_NOPTS_VALUE || pts == AV_NOPTS_VALUE) { |
||
189 | if (pts != AV_NOPTS_VALUE) { |
||
190 | s->pts = pts - get_delay(s); |
||
191 | } |
||
192 | return write_to_fifo(s, buf); |
||
193 | } |
||
194 | |||
195 | if (s->first_pts != AV_NOPTS_VALUE) { |
||
196 | handle_trimming(ctx); |
||
197 | if (!avresample_available(s->avr)) |
||
198 | return write_to_fifo(s, buf); |
||
199 | } |
||
200 | |||
201 | /* when we have two timestamps, compute how many samples would we have |
||
202 | * to add/remove to get proper sync between data and timestamps */ |
||
203 | delta = pts - s->pts - get_delay(s); |
||
204 | out_size = avresample_available(s->avr); |
||
205 | |||
206 | if (labs(delta) > s->min_delta || |
||
207 | (s->first_frame && delta && s->first_pts != AV_NOPTS_VALUE)) { |
||
208 | av_log(ctx, AV_LOG_VERBOSE, "Discontinuity - %"PRId64" samples.\n", delta); |
||
209 | out_size = av_clipl_int32((int64_t)out_size + delta); |
||
210 | } else { |
||
211 | if (s->resample) { |
||
212 | // adjust the compensation if delta is non-zero |
||
213 | int delay = get_delay(s); |
||
214 | int comp = s->comp + av_clip(delta * inlink->sample_rate / delay, |
||
215 | -s->max_comp, s->max_comp); |
||
216 | if (comp != s->comp) { |
||
217 | av_log(ctx, AV_LOG_VERBOSE, "Compensating %d samples per second.\n", comp); |
||
218 | if (avresample_set_compensation(s->avr, comp, inlink->sample_rate) == 0) { |
||
219 | s->comp = comp; |
||
220 | } |
||
221 | } |
||
222 | } |
||
223 | // adjust PTS to avoid monotonicity errors with input PTS jitter |
||
224 | pts -= delta; |
||
225 | delta = 0; |
||
226 | } |
||
227 | |||
228 | if (out_size > 0) { |
||
229 | AVFrame *buf_out = ff_get_audio_buffer(outlink, out_size); |
||
230 | if (!buf_out) { |
||
231 | ret = AVERROR(ENOMEM); |
||
232 | goto fail; |
||
233 | } |
||
234 | |||
235 | if (s->first_frame && delta > 0) { |
||
236 | int planar = av_sample_fmt_is_planar(buf_out->format); |
||
237 | int planes = planar ? nb_channels : 1; |
||
238 | int block_size = av_get_bytes_per_sample(buf_out->format) * |
||
239 | (planar ? 1 : nb_channels); |
||
240 | |||
241 | int ch; |
||
242 | |||
243 | av_samples_set_silence(buf_out->extended_data, 0, delta, |
||
244 | nb_channels, buf->format); |
||
245 | |||
246 | for (ch = 0; ch < planes; ch++) |
||
247 | buf_out->extended_data[ch] += delta * block_size; |
||
248 | |||
249 | avresample_read(s->avr, buf_out->extended_data, out_size); |
||
250 | |||
251 | for (ch = 0; ch < planes; ch++) |
||
252 | buf_out->extended_data[ch] -= delta * block_size; |
||
253 | } else { |
||
254 | avresample_read(s->avr, buf_out->extended_data, out_size); |
||
255 | |||
256 | if (delta > 0) { |
||
257 | av_samples_set_silence(buf_out->extended_data, out_size - delta, |
||
258 | delta, nb_channels, buf->format); |
||
259 | } |
||
260 | } |
||
261 | buf_out->pts = s->pts; |
||
262 | ret = ff_filter_frame(outlink, buf_out); |
||
263 | if (ret < 0) |
||
264 | goto fail; |
||
265 | s->got_output = 1; |
||
266 | } else if (avresample_available(s->avr)) { |
||
267 | av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping " |
||
268 | "whole buffer.\n"); |
||
269 | } |
||
270 | |||
271 | /* drain any remaining buffered data */ |
||
272 | avresample_read(s->avr, NULL, avresample_available(s->avr)); |
||
273 | |||
274 | new_pts = pts - avresample_get_delay(s->avr); |
||
275 | /* check for s->pts monotonicity */ |
||
276 | if (new_pts > s->pts) { |
||
277 | s->pts = new_pts; |
||
278 | ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data, |
||
279 | buf->linesize[0], buf->nb_samples); |
||
280 | } else { |
||
281 | av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping " |
||
282 | "whole buffer.\n"); |
||
283 | ret = 0; |
||
284 | } |
||
285 | |||
286 | s->first_frame = 0; |
||
287 | fail: |
||
288 | av_frame_free(&buf); |
||
289 | |||
290 | return ret; |
||
291 | } |
||
292 | |||
293 | static const AVFilterPad avfilter_af_asyncts_inputs[] = { |
||
294 | { |
||
295 | .name = "default", |
||
296 | .type = AVMEDIA_TYPE_AUDIO, |
||
297 | .filter_frame = filter_frame |
||
298 | }, |
||
299 | { NULL } |
||
300 | }; |
||
301 | |||
302 | static const AVFilterPad avfilter_af_asyncts_outputs[] = { |
||
303 | { |
||
304 | .name = "default", |
||
305 | .type = AVMEDIA_TYPE_AUDIO, |
||
306 | .config_props = config_props, |
||
307 | .request_frame = request_frame |
||
308 | }, |
||
309 | { NULL } |
||
310 | }; |
||
311 | |||
312 | AVFilter avfilter_af_asyncts = { |
||
313 | .name = "asyncts", |
||
314 | .description = NULL_IF_CONFIG_SMALL("Sync audio data to timestamps"), |
||
315 | .init = init, |
||
316 | .uninit = uninit, |
||
317 | .priv_size = sizeof(ASyncContext), |
||
318 | .priv_class = &asyncts_class, |
||
319 | .inputs = avfilter_af_asyncts_inputs, |
||
320 | .outputs = avfilter_af_asyncts_outputs, |
||
321 | };>>>>=>>>>>>>> |