2 * This file is part of Libav.
4 * Libav 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.
9 * Libav 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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with Libav; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "libavresample/avresample.h"
20 #include "libavutil/audio_fifo.h"
21 #include "libavutil/common.h"
22 #include "libavutil/mathematics.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/samplefmt.h"
30 typedef struct ASyncContext
{
33 AVAudioResampleContext
*avr
;
34 int64_t pts
; ///< timestamp in samples of the first sample in fifo
35 int min_delta
; ///< pad/trim min threshold in samples
36 int first_frame
; ///< 1 until filter_frame() has processed at least 1 frame with a pts != AV_NOPTS_VALUE
37 int64_t first_pts
; ///< user-specified first expected pts, in samples
44 /* set by filter_frame() to signal an output frame to request_frame() */
48 #define OFFSET(x) offsetof(ASyncContext, x)
49 #define A AV_OPT_FLAG_AUDIO_PARAM
50 static const AVOption options
[] = {
51 { "compensate", "Stretch/squeeze the data to make it match the timestamps", OFFSET(resample
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, 1, A
},
52 { "min_delta", "Minimum difference between timestamps and audio data "
53 "(in seconds) to trigger padding/trimmin the data.", OFFSET(min_delta_sec
), AV_OPT_TYPE_FLOAT
, { .dbl
= 0.1 }, 0, INT_MAX
, A
},
54 { "max_comp", "Maximum compensation in samples per second.", OFFSET(max_comp
), AV_OPT_TYPE_INT
, { .i64
= 500 }, 0, INT_MAX
, A
},
55 { "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
},
59 static const AVClass async_class
= {
60 .class_name
= "asyncts filter",
61 .item_name
= av_default_item_name
,
63 .version
= LIBAVUTIL_VERSION_INT
,
66 static int init(AVFilterContext
*ctx
, const char *args
)
68 ASyncContext
*s
= ctx
->priv
;
71 s
->class = &async_class
;
72 av_opt_set_defaults(s
);
74 if ((ret
= av_set_options_string(s
, args
, "=", ":")) < 0) {
75 av_log(ctx
, AV_LOG_ERROR
, "Error parsing options string '%s'.\n", args
);
80 s
->pts
= AV_NOPTS_VALUE
;
86 static void uninit(AVFilterContext
*ctx
)
88 ASyncContext
*s
= ctx
->priv
;
91 avresample_close(s
->avr
);
92 avresample_free(&s
->avr
);
96 static int config_props(AVFilterLink
*link
)
98 ASyncContext
*s
= link
->src
->priv
;
101 s
->min_delta
= s
->min_delta_sec
* link
->sample_rate
;
102 link
->time_base
= (AVRational
){1, link
->sample_rate
};
104 s
->avr
= avresample_alloc_context();
106 return AVERROR(ENOMEM
);
108 av_opt_set_int(s
->avr
, "in_channel_layout", link
->channel_layout
, 0);
109 av_opt_set_int(s
->avr
, "out_channel_layout", link
->channel_layout
, 0);
110 av_opt_set_int(s
->avr
, "in_sample_fmt", link
->format
, 0);
111 av_opt_set_int(s
->avr
, "out_sample_fmt", link
->format
, 0);
112 av_opt_set_int(s
->avr
, "in_sample_rate", link
->sample_rate
, 0);
113 av_opt_set_int(s
->avr
, "out_sample_rate", link
->sample_rate
, 0);
116 av_opt_set_int(s
->avr
, "force_resampling", 1, 0);
118 if ((ret
= avresample_open(s
->avr
)) < 0)
124 /* get amount of data currently buffered, in samples */
125 static int64_t get_delay(ASyncContext
*s
)
127 return avresample_available(s
->avr
) + avresample_get_delay(s
->avr
);
130 static void handle_trimming(AVFilterContext
*ctx
)
132 ASyncContext
*s
= ctx
->priv
;
134 if (s
->pts
< s
->first_pts
) {
135 int delta
= FFMIN(s
->first_pts
- s
->pts
, avresample_available(s
->avr
));
136 av_log(ctx
, AV_LOG_VERBOSE
, "Trimming %d samples from start\n",
138 avresample_read(s
->avr
, NULL
, delta
);
140 } else if (s
->first_frame
)
141 s
->pts
= s
->first_pts
;
144 static int request_frame(AVFilterLink
*link
)
146 AVFilterContext
*ctx
= link
->src
;
147 ASyncContext
*s
= ctx
->priv
;
152 while (ret
>= 0 && !s
->got_output
)
153 ret
= ff_request_frame(ctx
->inputs
[0]);
156 if (ret
== AVERROR_EOF
) {
157 if (s
->first_pts
!= AV_NOPTS_VALUE
)
158 handle_trimming(ctx
);
160 if (nb_samples
= get_delay(s
)) {
161 AVFrame
*buf
= ff_get_audio_buffer(link
, nb_samples
);
163 return AVERROR(ENOMEM
);
164 ret
= avresample_convert(s
->avr
, buf
->extended_data
,
165 buf
->linesize
[0], nb_samples
, NULL
, 0, 0);
168 return (ret
< 0) ? ret
: AVERROR_EOF
;
172 return ff_filter_frame(link
, buf
);
179 static int write_to_fifo(ASyncContext
*s
, AVFrame
*buf
)
181 int ret
= avresample_convert(s
->avr
, NULL
, 0, 0, buf
->extended_data
,
182 buf
->linesize
[0], buf
->nb_samples
);
187 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*buf
)
189 AVFilterContext
*ctx
= inlink
->dst
;
190 ASyncContext
*s
= ctx
->priv
;
191 AVFilterLink
*outlink
= ctx
->outputs
[0];
192 int nb_channels
= av_get_channel_layout_nb_channels(buf
->channel_layout
);
193 int64_t pts
= (buf
->pts
== AV_NOPTS_VALUE
) ? buf
->pts
:
194 av_rescale_q(buf
->pts
, inlink
->time_base
, outlink
->time_base
);
198 /* buffer data until we get the next timestamp */
199 if (s
->pts
== AV_NOPTS_VALUE
|| pts
== AV_NOPTS_VALUE
) {
200 if (pts
!= AV_NOPTS_VALUE
) {
201 s
->pts
= pts
- get_delay(s
);
203 return write_to_fifo(s
, buf
);
206 if (s
->first_pts
!= AV_NOPTS_VALUE
) {
207 handle_trimming(ctx
);
208 if (!avresample_available(s
->avr
))
209 return write_to_fifo(s
, buf
);
212 /* when we have two timestamps, compute how many samples would we have
213 * to add/remove to get proper sync between data and timestamps */
214 delta
= pts
- s
->pts
- get_delay(s
);
215 out_size
= avresample_available(s
->avr
);
217 if (labs(delta
) > s
->min_delta
||
218 (s
->first_frame
&& delta
&& s
->first_pts
!= AV_NOPTS_VALUE
)) {
219 av_log(ctx
, AV_LOG_VERBOSE
, "Discontinuity - %"PRId64
" samples.\n", delta
);
220 out_size
= av_clipl_int32((int64_t)out_size
+ delta
);
223 int comp
= av_clip(delta
, -s
->max_comp
, s
->max_comp
);
224 av_log(ctx
, AV_LOG_VERBOSE
, "Compensating %d samples per second.\n", comp
);
225 avresample_set_compensation(s
->avr
, comp
, inlink
->sample_rate
);
231 AVFrame
*buf_out
= ff_get_audio_buffer(outlink
, out_size
);
233 ret
= AVERROR(ENOMEM
);
237 if (s
->first_frame
&& delta
> 0) {
240 av_samples_set_silence(buf_out
->extended_data
, 0, delta
,
241 nb_channels
, buf
->format
);
243 for (ch
= 0; ch
< nb_channels
; ch
++)
244 buf_out
->extended_data
[ch
] += delta
;
246 avresample_read(s
->avr
, buf_out
->extended_data
, out_size
);
248 for (ch
= 0; ch
< nb_channels
; ch
++)
249 buf_out
->extended_data
[ch
] -= delta
;
251 avresample_read(s
->avr
, buf_out
->extended_data
, out_size
);
254 av_samples_set_silence(buf_out
->extended_data
, out_size
- delta
,
255 delta
, nb_channels
, buf
->format
);
258 buf_out
->pts
= s
->pts
;
259 ret
= ff_filter_frame(outlink
, buf_out
);
263 } else if (avresample_available(s
->avr
)) {
264 av_log(ctx
, AV_LOG_WARNING
, "Non-monotonous timestamps, dropping "
268 /* drain any remaining buffered data */
269 avresample_read(s
->avr
, NULL
, avresample_available(s
->avr
));
271 s
->pts
= pts
- avresample_get_delay(s
->avr
);
272 ret
= avresample_convert(s
->avr
, NULL
, 0, 0, buf
->extended_data
,
273 buf
->linesize
[0], buf
->nb_samples
);
282 static const AVFilterPad avfilter_af_asyncts_inputs
[] = {
285 .type
= AVMEDIA_TYPE_AUDIO
,
286 .filter_frame
= filter_frame
,
291 static const AVFilterPad avfilter_af_asyncts_outputs
[] = {
294 .type
= AVMEDIA_TYPE_AUDIO
,
295 .config_props
= config_props
,
296 .request_frame
= request_frame
301 AVFilter avfilter_af_asyncts
= {
303 .description
= NULL_IF_CONFIG_SMALL("Sync audio data to timestamps"),
308 .priv_size
= sizeof(ASyncContext
),
310 .inputs
= avfilter_af_asyncts_inputs
,
311 .outputs
= avfilter_af_asyncts_outputs
,