2 * Copyright (c) 2012 Nicolas George
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * concat audio-video filter
26 #include "libavutil/avassert.h"
27 #include "libavutil/avstring.h"
28 #include "libavutil/channel_layout.h"
29 #include "libavutil/opt.h"
31 #define FF_BUFQUEUE_SIZE 256
32 #include "bufferqueue.h"
41 unsigned nb_streams
[TYPE_ALL
]; /**< number of out streams of each type */
43 unsigned cur_idx
; /**< index of the first input of current segment */
44 int64_t delta_ts
; /**< timestamp to add to produce output timestamps */
45 unsigned nb_in_active
; /**< number of active inputs in current segment */
51 struct FFBufQueue queue
;
55 #define OFFSET(x) offsetof(ConcatContext, x)
56 #define A AV_OPT_FLAG_AUDIO_PARAM
57 #define F AV_OPT_FLAG_FILTERING_PARAM
58 #define V AV_OPT_FLAG_VIDEO_PARAM
60 static const AVOption concat_options
[] = {
61 { "n", "specify the number of segments", OFFSET(nb_segments
),
62 AV_OPT_TYPE_INT
, { .i64
= 2 }, 2, INT_MAX
, V
|A
|F
},
63 { "v", "specify the number of video streams",
64 OFFSET(nb_streams
[AVMEDIA_TYPE_VIDEO
]),
65 AV_OPT_TYPE_INT
, { .i64
= 1 }, 0, INT_MAX
, V
|F
},
66 { "a", "specify the number of audio streams",
67 OFFSET(nb_streams
[AVMEDIA_TYPE_AUDIO
]),
68 AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, A
|F
},
69 { "unsafe", "enable unsafe mode",
71 AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, V
|A
|F
},
75 AVFILTER_DEFINE_CLASS(concat
);
77 static int query_formats(AVFilterContext
*ctx
)
79 ConcatContext
*cat
= ctx
->priv
;
80 unsigned type
, nb_str
, idx0
= 0, idx
, str
, seg
;
81 AVFilterFormats
*formats
, *rates
= NULL
;
82 AVFilterChannelLayouts
*layouts
= NULL
;
84 for (type
= 0; type
< TYPE_ALL
; type
++) {
85 nb_str
= cat
->nb_streams
[type
];
86 for (str
= 0; str
< nb_str
; str
++) {
89 /* Set the output formats */
90 formats
= ff_all_formats(type
);
92 return AVERROR(ENOMEM
);
93 ff_formats_ref(formats
, &ctx
->outputs
[idx
]->in_formats
);
94 if (type
== AVMEDIA_TYPE_AUDIO
) {
95 rates
= ff_all_samplerates();
97 return AVERROR(ENOMEM
);
98 ff_formats_ref(rates
, &ctx
->outputs
[idx
]->in_samplerates
);
99 layouts
= ff_all_channel_layouts();
101 return AVERROR(ENOMEM
);
102 ff_channel_layouts_ref(layouts
, &ctx
->outputs
[idx
]->in_channel_layouts
);
105 /* Set the same formats for each corresponding input */
106 for (seg
= 0; seg
< cat
->nb_segments
; seg
++) {
107 ff_formats_ref(formats
, &ctx
->inputs
[idx
]->out_formats
);
108 if (type
== AVMEDIA_TYPE_AUDIO
) {
109 ff_formats_ref(rates
, &ctx
->inputs
[idx
]->out_samplerates
);
110 ff_channel_layouts_ref(layouts
, &ctx
->inputs
[idx
]->out_channel_layouts
);
112 idx
+= ctx
->nb_outputs
;
121 static int config_output(AVFilterLink
*outlink
)
123 AVFilterContext
*ctx
= outlink
->src
;
124 ConcatContext
*cat
= ctx
->priv
;
125 unsigned out_no
= FF_OUTLINK_IDX(outlink
);
126 unsigned in_no
= out_no
, seg
;
127 AVFilterLink
*inlink
= ctx
->inputs
[in_no
];
129 /* enhancement: find a common one */
130 outlink
->time_base
= AV_TIME_BASE_Q
;
131 outlink
->w
= inlink
->w
;
132 outlink
->h
= inlink
->h
;
133 outlink
->sample_aspect_ratio
= inlink
->sample_aspect_ratio
;
134 outlink
->format
= inlink
->format
;
135 for (seg
= 1; seg
< cat
->nb_segments
; seg
++) {
136 inlink
= ctx
->inputs
[in_no
+= ctx
->nb_outputs
];
137 if (!outlink
->sample_aspect_ratio
.num
)
138 outlink
->sample_aspect_ratio
= inlink
->sample_aspect_ratio
;
139 /* possible enhancement: unsafe mode, do not check */
140 if (outlink
->w
!= inlink
->w
||
141 outlink
->h
!= inlink
->h
||
142 outlink
->sample_aspect_ratio
.num
!= inlink
->sample_aspect_ratio
.num
&&
143 inlink
->sample_aspect_ratio
.num
||
144 outlink
->sample_aspect_ratio
.den
!= inlink
->sample_aspect_ratio
.den
) {
145 av_log(ctx
, AV_LOG_ERROR
, "Input link %s parameters "
146 "(size %dx%d, SAR %d:%d) do not match the corresponding "
147 "output link %s parameters (%dx%d, SAR %d:%d)\n",
148 ctx
->input_pads
[in_no
].name
, inlink
->w
, inlink
->h
,
149 inlink
->sample_aspect_ratio
.num
,
150 inlink
->sample_aspect_ratio
.den
,
151 ctx
->input_pads
[out_no
].name
, outlink
->w
, outlink
->h
,
152 outlink
->sample_aspect_ratio
.num
,
153 outlink
->sample_aspect_ratio
.den
);
155 return AVERROR(EINVAL
);
162 static int push_frame(AVFilterContext
*ctx
, unsigned in_no
, AVFrame
*buf
)
164 ConcatContext
*cat
= ctx
->priv
;
165 unsigned out_no
= in_no
% ctx
->nb_outputs
;
166 AVFilterLink
* inlink
= ctx
-> inputs
[ in_no
];
167 AVFilterLink
*outlink
= ctx
->outputs
[out_no
];
168 struct concat_in
*in
= &cat
->in
[in_no
];
170 buf
->pts
= av_rescale_q(buf
->pts
, inlink
->time_base
, outlink
->time_base
);
173 /* add duration to input PTS */
174 if (inlink
->sample_rate
)
175 /* use number of audio samples */
176 in
->pts
+= av_rescale_q(buf
->nb_samples
,
177 av_make_q(1, inlink
->sample_rate
),
179 else if (in
->nb_frames
>= 2)
180 /* use mean duration */
181 in
->pts
= av_rescale(in
->pts
, in
->nb_frames
, in
->nb_frames
- 1);
183 buf
->pts
+= cat
->delta_ts
;
184 return ff_filter_frame(outlink
, buf
);
187 static int process_frame(AVFilterLink
*inlink
, AVFrame
*buf
)
189 AVFilterContext
*ctx
= inlink
->dst
;
190 ConcatContext
*cat
= ctx
->priv
;
191 unsigned in_no
= FF_INLINK_IDX(inlink
);
193 if (in_no
< cat
->cur_idx
) {
194 av_log(ctx
, AV_LOG_ERROR
, "Frame after EOF on input %s\n",
195 ctx
->input_pads
[in_no
].name
);
197 } else if (in_no
>= cat
->cur_idx
+ ctx
->nb_outputs
) {
198 ff_bufqueue_add(ctx
, &cat
->in
[in_no
].queue
, buf
);
200 return push_frame(ctx
, in_no
, buf
);
205 static AVFrame
*get_video_buffer(AVFilterLink
*inlink
, int w
, int h
)
207 AVFilterContext
*ctx
= inlink
->dst
;
208 unsigned in_no
= FF_INLINK_IDX(inlink
);
209 AVFilterLink
*outlink
= ctx
->outputs
[in_no
% ctx
->nb_outputs
];
211 return ff_get_video_buffer(outlink
, w
, h
);
214 static AVFrame
*get_audio_buffer(AVFilterLink
*inlink
, int nb_samples
)
216 AVFilterContext
*ctx
= inlink
->dst
;
217 unsigned in_no
= FF_INLINK_IDX(inlink
);
218 AVFilterLink
*outlink
= ctx
->outputs
[in_no
% ctx
->nb_outputs
];
220 return ff_get_audio_buffer(outlink
, nb_samples
);
223 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*buf
)
225 return process_frame(inlink
, buf
);
228 static void close_input(AVFilterContext
*ctx
, unsigned in_no
)
230 ConcatContext
*cat
= ctx
->priv
;
232 cat
->in
[in_no
].eof
= 1;
234 av_log(ctx
, AV_LOG_VERBOSE
, "EOF on %s, %d streams left in segment.\n",
235 ctx
->input_pads
[in_no
].name
, cat
->nb_in_active
);
238 static void find_next_delta_ts(AVFilterContext
*ctx
, int64_t *seg_delta
)
240 ConcatContext
*cat
= ctx
->priv
;
241 unsigned i
= cat
->cur_idx
;
242 unsigned imax
= i
+ ctx
->nb_outputs
;
245 pts
= cat
->in
[i
++].pts
;
246 for (; i
< imax
; i
++)
247 pts
= FFMAX(pts
, cat
->in
[i
].pts
);
248 cat
->delta_ts
+= pts
;
252 static int send_silence(AVFilterContext
*ctx
, unsigned in_no
, unsigned out_no
,
255 ConcatContext
*cat
= ctx
->priv
;
256 AVFilterLink
*outlink
= ctx
->outputs
[out_no
];
257 int64_t base_pts
= cat
->in
[in_no
].pts
+ cat
->delta_ts
- seg_delta
;
258 int64_t nb_samples
, sent
= 0;
259 int frame_nb_samples
, ret
;
260 AVRational rate_tb
= { 1, ctx
->inputs
[in_no
]->sample_rate
};
262 int nb_channels
= av_get_channel_layout_nb_channels(outlink
->channel_layout
);
266 nb_samples
= av_rescale_q(seg_delta
- cat
->in
[in_no
].pts
,
267 outlink
->time_base
, rate_tb
);
268 frame_nb_samples
= FFMAX(9600, rate_tb
.den
/ 5); /* arbitrary */
270 frame_nb_samples
= FFMIN(frame_nb_samples
, nb_samples
);
271 buf
= ff_get_audio_buffer(outlink
, frame_nb_samples
);
273 return AVERROR(ENOMEM
);
274 av_samples_set_silence(buf
->extended_data
, 0, frame_nb_samples
,
275 nb_channels
, outlink
->format
);
276 buf
->pts
= base_pts
+ av_rescale_q(sent
, rate_tb
, outlink
->time_base
);
277 ret
= ff_filter_frame(outlink
, buf
);
280 sent
+= frame_nb_samples
;
281 nb_samples
-= frame_nb_samples
;
286 static int flush_segment(AVFilterContext
*ctx
)
289 ConcatContext
*cat
= ctx
->priv
;
290 unsigned str
, str_max
;
293 find_next_delta_ts(ctx
, &seg_delta
);
294 cat
->cur_idx
+= ctx
->nb_outputs
;
295 cat
->nb_in_active
= ctx
->nb_outputs
;
296 av_log(ctx
, AV_LOG_VERBOSE
, "Segment finished at pts=%"PRId64
"\n",
299 if (cat
->cur_idx
< ctx
->nb_inputs
) {
300 /* pad audio streams with silence */
301 str
= cat
->nb_streams
[AVMEDIA_TYPE_VIDEO
];
302 str_max
= str
+ cat
->nb_streams
[AVMEDIA_TYPE_AUDIO
];
303 for (; str
< str_max
; str
++) {
304 ret
= send_silence(ctx
, cat
->cur_idx
- ctx
->nb_outputs
+ str
, str
,
309 /* flush queued buffers */
310 /* possible enhancement: flush in PTS order */
311 str_max
= cat
->cur_idx
+ ctx
->nb_outputs
;
312 for (str
= cat
->cur_idx
; str
< str_max
; str
++) {
313 while (cat
->in
[str
].queue
.available
) {
314 ret
= push_frame(ctx
, str
, ff_bufqueue_get(&cat
->in
[str
].queue
));
323 static int request_frame(AVFilterLink
*outlink
)
325 AVFilterContext
*ctx
= outlink
->src
;
326 ConcatContext
*cat
= ctx
->priv
;
327 unsigned out_no
= FF_OUTLINK_IDX(outlink
);
328 unsigned in_no
= out_no
+ cat
->cur_idx
;
329 unsigned str
, str_max
;
333 if (in_no
>= ctx
->nb_inputs
)
335 if (!cat
->in
[in_no
].eof
) {
336 ret
= ff_request_frame(ctx
->inputs
[in_no
]);
337 if (ret
!= AVERROR_EOF
)
339 close_input(ctx
, in_no
);
341 /* cycle on all inputs to finish the segment */
342 /* possible enhancement: request in PTS order */
343 str_max
= cat
->cur_idx
+ ctx
->nb_outputs
- 1;
344 for (str
= cat
->cur_idx
; cat
->nb_in_active
;
345 str
= str
== str_max
? cat
->cur_idx
: str
+ 1) {
346 if (cat
->in
[str
].eof
)
348 ret
= ff_request_frame(ctx
->inputs
[str
]);
349 if (ret
== AVERROR_EOF
)
350 close_input(ctx
, str
);
354 ret
= flush_segment(ctx
);
357 in_no
+= ctx
->nb_outputs
;
361 static av_cold
int init(AVFilterContext
*ctx
)
363 ConcatContext
*cat
= ctx
->priv
;
364 unsigned seg
, type
, str
;
366 /* create input pads */
367 for (seg
= 0; seg
< cat
->nb_segments
; seg
++) {
368 for (type
= 0; type
< TYPE_ALL
; type
++) {
369 for (str
= 0; str
< cat
->nb_streams
[type
]; str
++) {
372 .get_video_buffer
= get_video_buffer
,
373 .get_audio_buffer
= get_audio_buffer
,
374 .filter_frame
= filter_frame
,
376 pad
.name
= av_asprintf("in%d:%c%d", seg
, "va"[type
], str
);
377 ff_insert_inpad(ctx
, ctx
->nb_inputs
, &pad
);
381 /* create output pads */
382 for (type
= 0; type
< TYPE_ALL
; type
++) {
383 for (str
= 0; str
< cat
->nb_streams
[type
]; str
++) {
386 .config_props
= config_output
,
387 .request_frame
= request_frame
,
389 pad
.name
= av_asprintf("out:%c%d", "va"[type
], str
);
390 ff_insert_outpad(ctx
, ctx
->nb_outputs
, &pad
);
394 cat
->in
= av_calloc(ctx
->nb_inputs
, sizeof(*cat
->in
));
396 return AVERROR(ENOMEM
);
397 cat
->nb_in_active
= ctx
->nb_outputs
;
401 static av_cold
void uninit(AVFilterContext
*ctx
)
403 ConcatContext
*cat
= ctx
->priv
;
406 for (i
= 0; i
< ctx
->nb_inputs
; i
++) {
407 av_freep(&ctx
->input_pads
[i
].name
);
408 ff_bufqueue_discard_all(&cat
->in
[i
].queue
);
410 for (i
= 0; i
< ctx
->nb_outputs
; i
++)
411 av_freep(&ctx
->output_pads
[i
].name
);
415 AVFilter ff_avf_concat
= {
417 .description
= NULL_IF_CONFIG_SMALL("Concatenate audio and video streams."),
420 .query_formats
= query_formats
,
421 .priv_size
= sizeof(ConcatContext
),
424 .priv_class
= &concat_class
,
425 .flags
= AVFILTER_FLAG_DYNAMIC_INPUTS
| AVFILTER_FLAG_DYNAMIC_OUTPUTS
,