Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2012 Nicolas George | ||
3 | * | ||
4 | * This file is part of FFmpeg. | ||
5 | * | ||
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. | ||
10 | * | ||
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. | ||
15 | * | ||
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 | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file | ||
23 | * concat audio-video filter | ||
24 | */ | ||
25 | |||
26 | #include "libavutil/avstring.h" | ||
27 | #include "libavutil/channel_layout.h" | ||
28 | #include "libavutil/mem.h" | ||
29 | #include "libavutil/opt.h" | ||
30 | #include "avfilter.h" | ||
31 | #include "filters.h" | ||
32 | #include "formats.h" | ||
33 | #include "video.h" | ||
34 | #include "audio.h" | ||
35 | |||
36 | #define TYPE_ALL 2 | ||
37 | |||
38 | typedef struct ConcatContext { | ||
39 | const AVClass *class; | ||
40 | unsigned nb_streams[TYPE_ALL]; /**< number of out streams of each type */ | ||
41 | unsigned nb_segments; | ||
42 | unsigned cur_idx; /**< index of the first input of current segment */ | ||
43 | int64_t delta_ts; /**< timestamp to add to produce output timestamps */ | ||
44 | unsigned nb_in_active; /**< number of active inputs in current segment */ | ||
45 | unsigned unsafe; | ||
46 | struct concat_in { | ||
47 | int64_t pts; | ||
48 | int64_t nb_frames; | ||
49 | unsigned eof; | ||
50 | } *in; | ||
51 | } ConcatContext; | ||
52 | |||
53 | #define OFFSET(x) offsetof(ConcatContext, x) | ||
54 | #define A AV_OPT_FLAG_AUDIO_PARAM | ||
55 | #define F AV_OPT_FLAG_FILTERING_PARAM | ||
56 | #define V AV_OPT_FLAG_VIDEO_PARAM | ||
57 | |||
58 | static const AVOption concat_options[] = { | ||
59 | { "n", "specify the number of segments", OFFSET(nb_segments), | ||
60 | AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT_MAX, V|A|F}, | ||
61 | { "v", "specify the number of video streams", | ||
62 | OFFSET(nb_streams[AVMEDIA_TYPE_VIDEO]), | ||
63 | AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, V|F }, | ||
64 | { "a", "specify the number of audio streams", | ||
65 | OFFSET(nb_streams[AVMEDIA_TYPE_AUDIO]), | ||
66 | AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|F}, | ||
67 | { "unsafe", "enable unsafe mode", | ||
68 | OFFSET(unsafe), | ||
69 | AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, V|A|F}, | ||
70 | { NULL } | ||
71 | }; | ||
72 | |||
73 | AVFILTER_DEFINE_CLASS(concat); | ||
74 | |||
75 | 4 | static int query_formats(const AVFilterContext *ctx, | |
76 | AVFilterFormatsConfig **cfg_in, | ||
77 | AVFilterFormatsConfig **cfg_out) | ||
78 | { | ||
79 | 4 | const ConcatContext *cat = ctx->priv; | |
80 | 4 | unsigned type, nb_str, idx0 = 0, idx, str, seg; | |
81 | 4 | AVFilterFormats *formats, *rates = NULL; | |
82 | 4 | AVFilterChannelLayouts *layouts = NULL; | |
83 | int ret; | ||
84 | |||
85 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
12 | for (type = 0; type < TYPE_ALL; type++) { |
86 | 8 | nb_str = cat->nb_streams[type]; | |
87 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
|
14 | for (str = 0; str < nb_str; str++) { |
88 | 6 | idx = idx0; | |
89 | |||
90 | /* Set the output formats */ | ||
91 | 6 | formats = ff_all_formats(type); | |
92 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if ((ret = ff_formats_ref(formats, &cfg_out[idx]->formats)) < 0) |
93 | ✗ | return ret; | |
94 | |||
95 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (type == AVMEDIA_TYPE_AUDIO) { |
96 | 2 | rates = ff_all_samplerates(); | |
97 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if ((ret = ff_formats_ref(rates, &cfg_out[idx]->samplerates)) < 0) |
98 | ✗ | return ret; | |
99 | 2 | layouts = ff_all_channel_layouts(); | |
100 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if ((ret = ff_channel_layouts_ref(layouts, &cfg_out[idx]->channel_layouts)) < 0) |
101 | ✗ | return ret; | |
102 | } | ||
103 | |||
104 | /* Set the same formats for each corresponding input */ | ||
105 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 6 times.
|
22 | for (seg = 0; seg < cat->nb_segments; seg++) { |
106 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if ((ret = ff_formats_ref(formats, &cfg_in[idx]->formats)) < 0) |
107 | ✗ | return ret; | |
108 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | if (type == AVMEDIA_TYPE_AUDIO) { |
109 |
2/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
|
12 | if ((ret = ff_formats_ref(rates, &cfg_in[idx]->samplerates)) < 0 || |
110 | 6 | (ret = ff_channel_layouts_ref(layouts, &cfg_in[idx]->channel_layouts)) < 0) | |
111 | ✗ | return ret; | |
112 | } | ||
113 | 16 | idx += ctx->nb_outputs; | |
114 | } | ||
115 | |||
116 | 6 | idx0++; | |
117 | } | ||
118 | } | ||
119 | 4 | return 0; | |
120 | } | ||
121 | |||
122 | 6 | static int config_output(AVFilterLink *outlink) | |
123 | { | ||
124 | 6 | FilterLink *outl = ff_filter_link(outlink); | |
125 | 6 | AVFilterContext *ctx = outlink->src; | |
126 | 6 | ConcatContext *cat = ctx->priv; | |
127 | 6 | unsigned out_no = FF_OUTLINK_IDX(outlink); | |
128 | 6 | unsigned in_no = out_no, seg; | |
129 | 6 | AVFilterLink *inlink = ctx->inputs[in_no]; | |
130 | 6 | FilterLink *inl = ff_filter_link(inlink); | |
131 | |||
132 | /* enhancement: find a common one */ | ||
133 | 6 | outlink->time_base = AV_TIME_BASE_Q; | |
134 | 6 | outlink->w = inlink->w; | |
135 | 6 | outlink->h = inlink->h; | |
136 | 6 | outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; | |
137 | 6 | outlink->format = inlink->format; | |
138 | 6 | outl->frame_rate = inl->frame_rate; | |
139 | |||
140 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
|
14 | for (seg = 1; seg < cat->nb_segments; seg++) { |
141 | 9 | inlink = ctx->inputs[in_no + seg * ctx->nb_outputs]; | |
142 | 9 | inl = ff_filter_link(inlink); | |
143 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
|
9 | if (outl->frame_rate.num != inl->frame_rate.num || |
144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | outl->frame_rate.den != inl->frame_rate.den) { |
145 | 1 | av_log(ctx, AV_LOG_VERBOSE, | |
146 | "Video inputs have different frame rates, output will be VFR\n"); | ||
147 | 1 | outl->frame_rate = av_make_q(1, 0); | |
148 | 1 | break; | |
149 | } | ||
150 | } | ||
151 | |||
152 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
|
16 | for (seg = 1; seg < cat->nb_segments; seg++) { |
153 | 10 | inlink = ctx->inputs[in_no + seg * ctx->nb_outputs]; | |
154 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
10 | if (!outlink->sample_aspect_ratio.num) |
155 | 5 | outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; | |
156 | /* possible enhancement: unsafe mode, do not check */ | ||
157 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | if (outlink->w != inlink->w || |
158 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | outlink->h != inlink->h || |
159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num && |
160 | ✗ | inlink->sample_aspect_ratio.num || | |
161 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) { |
162 | 1 | av_log(ctx, AV_LOG_ERROR, "Input link %s parameters " | |
163 | "(size %dx%d, SAR %d:%d) do not match the corresponding " | ||
164 | "output link %s parameters (%dx%d, SAR %d:%d)\n", | ||
165 | 1 | ctx->input_pads[in_no].name, inlink->w, inlink->h, | |
166 | inlink->sample_aspect_ratio.num, | ||
167 | inlink->sample_aspect_ratio.den, | ||
168 | 1 | ctx->input_pads[out_no].name, outlink->w, outlink->h, | |
169 | outlink->sample_aspect_ratio.num, | ||
170 | outlink->sample_aspect_ratio.den); | ||
171 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!cat->unsafe) |
172 | ✗ | return AVERROR(EINVAL); | |
173 | } | ||
174 | } | ||
175 | |||
176 | 6 | return 0; | |
177 | } | ||
178 | |||
179 | 463 | static int push_frame(AVFilterContext *ctx, unsigned in_no, AVFrame *buf) | |
180 | { | ||
181 | 463 | ConcatContext *cat = ctx->priv; | |
182 | 463 | unsigned out_no = in_no % ctx->nb_outputs; | |
183 | 463 | AVFilterLink * inlink = ctx-> inputs[ in_no]; | |
184 | 463 | AVFilterLink *outlink = ctx->outputs[out_no]; | |
185 | 463 | struct concat_in *in = &cat->in[in_no]; | |
186 | |||
187 | 463 | buf->pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); | |
188 | 463 | buf->duration = av_rescale_q(buf->duration, inlink->time_base, outlink->time_base); | |
189 | 463 | in->pts = buf->pts; | |
190 | 463 | in->nb_frames++; | |
191 | /* add duration to input PTS */ | ||
192 |
2/2✓ Branch 0 taken 350 times.
✓ Branch 1 taken 113 times.
|
463 | if (inlink->sample_rate) |
193 | /* use number of audio samples */ | ||
194 | 350 | in->pts += av_rescale_q(buf->nb_samples, | |
195 | av_make_q(1, inlink->sample_rate), | ||
196 | outlink->time_base); | ||
197 |
2/2✓ Branch 0 taken 103 times.
✓ Branch 1 taken 10 times.
|
113 | else if (in->nb_frames >= 2) |
198 | /* use mean duration */ | ||
199 | 103 | in->pts = av_rescale(in->pts, in->nb_frames, in->nb_frames - 1); | |
200 | |||
201 | 463 | buf->pts += cat->delta_ts; | |
202 | 463 | return ff_filter_frame(outlink, buf); | |
203 | } | ||
204 | |||
205 | 63 | static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) | |
206 | { | ||
207 | 63 | AVFilterContext *ctx = inlink->dst; | |
208 | 63 | unsigned in_no = FF_INLINK_IDX(inlink); | |
209 | 63 | AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs]; | |
210 | |||
211 | 63 | return ff_get_video_buffer(outlink, w, h); | |
212 | } | ||
213 | |||
214 | 350 | static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples) | |
215 | { | ||
216 | 350 | AVFilterContext *ctx = inlink->dst; | |
217 | 350 | unsigned in_no = FF_INLINK_IDX(inlink); | |
218 | 350 | AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs]; | |
219 | |||
220 | 350 | return ff_get_audio_buffer(outlink, nb_samples); | |
221 | } | ||
222 | |||
223 | 16 | static void close_input(AVFilterContext *ctx, unsigned in_no) | |
224 | { | ||
225 | 16 | ConcatContext *cat = ctx->priv; | |
226 | |||
227 | 16 | cat->in[in_no].eof = 1; | |
228 | 16 | cat->nb_in_active--; | |
229 | 16 | av_log(ctx, AV_LOG_VERBOSE, "EOF on %s, %d streams left in segment.\n", | |
230 | 16 | ctx->input_pads[in_no].name, cat->nb_in_active); | |
231 | 16 | } | |
232 | |||
233 | 10 | static void find_next_delta_ts(AVFilterContext *ctx, int64_t *seg_delta) | |
234 | { | ||
235 | 10 | ConcatContext *cat = ctx->priv; | |
236 | 10 | unsigned i = cat->cur_idx; | |
237 | 10 | unsigned imax = i + ctx->nb_outputs; | |
238 | int64_t pts; | ||
239 | |||
240 | 10 | pts = cat->in[i++].pts; | |
241 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | for (; i < imax; i++) |
242 | 6 | pts = FFMAX(pts, cat->in[i].pts); | |
243 | 10 | cat->delta_ts += pts; | |
244 | 10 | *seg_delta = pts; | |
245 | 10 | } | |
246 | |||
247 | 4 | static int send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no, | |
248 | int64_t seg_delta) | ||
249 | { | ||
250 | 4 | ConcatContext *cat = ctx->priv; | |
251 | 4 | AVFilterLink *outlink = ctx->outputs[out_no]; | |
252 | 4 | int64_t base_pts = cat->in[in_no].pts + cat->delta_ts - seg_delta; | |
253 | 4 | int64_t nb_samples, sent = 0; | |
254 | int frame_nb_samples, ret; | ||
255 | 4 | AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate }; | |
256 | AVFrame *buf; | ||
257 | |||
258 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!rate_tb.den) |
259 | ✗ | return AVERROR_BUG; | |
260 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (cat->in[in_no].pts < INT64_MIN + seg_delta) |
261 | ✗ | return AVERROR_INVALIDDATA; | |
262 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (seg_delta < cat->in[in_no].pts) |
263 | ✗ | return AVERROR_INVALIDDATA; | |
264 | 4 | nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts, | |
265 | outlink->time_base, rate_tb); | ||
266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | frame_nb_samples = FFMAX(9600, rate_tb.den / 5); /* arbitrary */ |
267 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 4 times.
|
14 | while (nb_samples) { |
268 | 10 | frame_nb_samples = FFMIN(frame_nb_samples, nb_samples); | |
269 | 10 | buf = ff_get_audio_buffer(outlink, frame_nb_samples); | |
270 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (!buf) |
271 | ✗ | return AVERROR(ENOMEM); | |
272 | 10 | av_samples_set_silence(buf->extended_data, 0, frame_nb_samples, | |
273 | 10 | outlink->ch_layout.nb_channels, outlink->format); | |
274 | 10 | buf->pts = base_pts + av_rescale_q(sent, rate_tb, outlink->time_base); | |
275 | 10 | ret = ff_filter_frame(outlink, buf); | |
276 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (ret < 0) |
277 | ✗ | return ret; | |
278 | 10 | sent += frame_nb_samples; | |
279 | 10 | nb_samples -= frame_nb_samples; | |
280 | } | ||
281 | 4 | return 0; | |
282 | } | ||
283 | |||
284 | 10 | static int flush_segment(AVFilterContext *ctx) | |
285 | { | ||
286 | int ret; | ||
287 | 10 | ConcatContext *cat = ctx->priv; | |
288 | unsigned str, str_max; | ||
289 | int64_t seg_delta; | ||
290 | |||
291 | 10 | find_next_delta_ts(ctx, &seg_delta); | |
292 | 10 | cat->cur_idx += ctx->nb_outputs; | |
293 | 10 | cat->nb_in_active = ctx->nb_outputs; | |
294 | 10 | av_log(ctx, AV_LOG_VERBOSE, "Segment finished at pts=%"PRId64"\n", | |
295 | cat->delta_ts); | ||
296 | |||
297 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
|
10 | if (cat->cur_idx < ctx->nb_inputs) { |
298 | /* pad audio streams with silence */ | ||
299 | 6 | str = cat->nb_streams[AVMEDIA_TYPE_VIDEO]; | |
300 | 6 | str_max = str + cat->nb_streams[AVMEDIA_TYPE_AUDIO]; | |
301 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
|
10 | for (; str < str_max; str++) { |
302 | 4 | ret = send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str, | |
303 | seg_delta); | ||
304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (ret < 0) |
305 | ✗ | return ret; | |
306 | } | ||
307 | } | ||
308 | 10 | return 0; | |
309 | } | ||
310 | |||
311 | 7 | static av_cold int init(AVFilterContext *ctx) | |
312 | { | ||
313 | 7 | ConcatContext *cat = ctx->priv; | |
314 | unsigned seg, type, str; | ||
315 | int ret; | ||
316 | |||
317 | /* create input pads */ | ||
318 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 7 times.
|
25 | for (seg = 0; seg < cat->nb_segments; seg++) { |
319 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 18 times.
|
54 | for (type = 0; type < TYPE_ALL; type++) { |
320 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 36 times.
|
66 | for (str = 0; str < cat->nb_streams[type]; str++) { |
321 | 30 | AVFilterPad pad = { | |
322 | .type = type, | ||
323 | }; | ||
324 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 12 times.
|
30 | if (type == AVMEDIA_TYPE_VIDEO) |
325 | 18 | pad.get_buffer.video = get_video_buffer; | |
326 | else | ||
327 | 12 | pad.get_buffer.audio = get_audio_buffer; | |
328 | 30 | pad.name = av_asprintf("in%d:%c%d", seg, "va"[type], str); | |
329 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
|
30 | if ((ret = ff_append_inpad_free_name(ctx, &pad)) < 0) |
330 | ✗ | return ret; | |
331 | } | ||
332 | } | ||
333 | } | ||
334 | /* create output pads */ | ||
335 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 7 times.
|
21 | for (type = 0; type < TYPE_ALL; type++) { |
336 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 14 times.
|
25 | for (str = 0; str < cat->nb_streams[type]; str++) { |
337 | 11 | AVFilterPad pad = { | |
338 | .type = type, | ||
339 | .config_props = config_output, | ||
340 | }; | ||
341 | 11 | pad.name = av_asprintf("out:%c%d", "va"[type], str); | |
342 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0) |
343 | ✗ | return ret; | |
344 | } | ||
345 | } | ||
346 | |||
347 | 7 | cat->in = av_calloc(ctx->nb_inputs, sizeof(*cat->in)); | |
348 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (!cat->in) |
349 | ✗ | return AVERROR(ENOMEM); | |
350 | 7 | cat->nb_in_active = ctx->nb_outputs; | |
351 | 7 | return 0; | |
352 | } | ||
353 | |||
354 | 7 | static av_cold void uninit(AVFilterContext *ctx) | |
355 | { | ||
356 | 7 | ConcatContext *cat = ctx->priv; | |
357 | |||
358 | 7 | av_freep(&cat->in); | |
359 | 7 | } | |
360 | |||
361 | 985 | static int activate(AVFilterContext *ctx) | |
362 | { | ||
363 | 985 | ConcatContext *cat = ctx->priv; | |
364 | AVFrame *frame; | ||
365 | unsigned i, j; | ||
366 | int ret, status; | ||
367 | int64_t pts; | ||
368 | |||
369 | /* Forward status back */ | ||
370 |
2/2✓ Branch 0 taken 1815 times.
✓ Branch 1 taken 985 times.
|
2800 | for (i = 0; i < ctx->nb_outputs; i++) { |
371 | 1815 | status = ff_outlink_get_status(ctx->outputs[i]); | |
372 |
2/2✓ Branch 0 taken 1784 times.
✓ Branch 1 taken 31 times.
|
1815 | if (!status) |
373 | 1784 | continue; | |
374 |
2/2✓ Branch 0 taken 92 times.
✓ Branch 1 taken 31 times.
|
123 | for (j = i; j < ctx->nb_inputs; j += ctx->nb_outputs) { |
375 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
|
92 | if (!cat->in[j].eof) { |
376 | ✗ | cat->in[j].eof = 1; | |
377 | ✗ | ff_inlink_set_status(ctx->inputs[j], status); | |
378 | ✗ | return 0; | |
379 | } | ||
380 | } | ||
381 | |||
382 | } | ||
383 | |||
384 | /* Forward available frames */ | ||
385 |
2/2✓ Branch 0 taken 984 times.
✓ Branch 1 taken 1 times.
|
985 | if (cat->cur_idx < ctx->nb_inputs) { |
386 |
2/2✓ Branch 0 taken 1761 times.
✓ Branch 1 taken 521 times.
|
2282 | for (i = 0; i < ctx->nb_outputs; i++) { |
387 | 1761 | ret = ff_inlink_consume_frame(ctx->inputs[cat->cur_idx + i], &frame); | |
388 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1761 times.
|
1761 | if (ret < 0) |
389 | ✗ | return ret; | |
390 |
2/2✓ Branch 0 taken 463 times.
✓ Branch 1 taken 1298 times.
|
1761 | if (ret) { |
391 | 463 | ff_filter_set_ready(ctx, 10); | |
392 | 463 | return push_frame(ctx, cat->cur_idx + i, frame); | |
393 | } | ||
394 | } | ||
395 | } | ||
396 | |||
397 | /* Forward status change */ | ||
398 |
2/2✓ Branch 0 taken 521 times.
✓ Branch 1 taken 1 times.
|
522 | if (cat->cur_idx < ctx->nb_inputs) { |
399 |
2/2✓ Branch 0 taken 942 times.
✓ Branch 1 taken 505 times.
|
1447 | for (i = 0; i < ctx->nb_outputs; i++) { |
400 | 942 | AVFilterLink *inlink = ctx->inputs[cat->cur_idx + i]; | |
401 | |||
402 | 942 | ret = ff_inlink_acknowledge_status(inlink, &status, &pts); | |
403 | /* TODO use pts */ | ||
404 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 926 times.
|
942 | if (ret > 0) { |
405 | 16 | close_input(ctx, cat->cur_idx + i); | |
406 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
|
16 | if (cat->cur_idx + ctx->nb_outputs >= ctx->nb_inputs) { |
407 | 6 | int64_t eof_pts = cat->delta_ts; | |
408 | |||
409 | 6 | eof_pts += av_rescale_q(pts, inlink->time_base, ctx->outputs[i]->time_base); | |
410 | 6 | ff_outlink_set_status(ctx->outputs[i], status, eof_pts); | |
411 | } | ||
412 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
|
16 | if (!cat->nb_in_active) { |
413 | 10 | ret = flush_segment(ctx); | |
414 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (ret < 0) |
415 | ✗ | return ret; | |
416 | } | ||
417 | 16 | ff_filter_set_ready(ctx, 10); | |
418 | 16 | return 0; | |
419 | } | ||
420 | } | ||
421 | } | ||
422 | |||
423 | 506 | ret = FFERROR_NOT_READY; | |
424 |
2/2✓ Branch 0 taken 823 times.
✓ Branch 1 taken 398 times.
|
1221 | for (i = 0; i < ctx->nb_outputs; i++) { |
425 |
2/2✓ Branch 1 taken 477 times.
✓ Branch 2 taken 346 times.
|
823 | if (ff_outlink_frame_wanted(ctx->outputs[i])) { |
426 |
2/2✓ Branch 0 taken 108 times.
✓ Branch 1 taken 369 times.
|
477 | if (cat->in[cat->cur_idx + i].eof) { |
427 |
2/2✓ Branch 0 taken 216 times.
✓ Branch 1 taken 108 times.
|
324 | for (j = 0; j < ctx->nb_outputs; j++) |
428 |
2/2✓ Branch 0 taken 108 times.
✓ Branch 1 taken 108 times.
|
216 | if (!cat->in[cat->cur_idx + j].eof) |
429 | 108 | ff_inlink_request_frame(ctx->inputs[cat->cur_idx + j]); | |
430 | 108 | return 0; | |
431 | } else { | ||
432 | 369 | ff_inlink_request_frame(ctx->inputs[cat->cur_idx + i]); | |
433 | 369 | ret = 0; | |
434 | } | ||
435 | } | ||
436 | } | ||
437 | |||
438 | 398 | return ret; | |
439 | } | ||
440 | |||
441 | ✗ | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
442 | char *res, int res_len, int flags) | ||
443 | { | ||
444 | ✗ | int ret = AVERROR(ENOSYS); | |
445 | |||
446 | ✗ | if (!strcmp(cmd, "next")) { | |
447 | ✗ | av_log(ctx, AV_LOG_VERBOSE, "Command received: next\n"); | |
448 | ✗ | return flush_segment(ctx); | |
449 | } | ||
450 | |||
451 | ✗ | return ret; | |
452 | } | ||
453 | |||
454 | const AVFilter ff_avf_concat = { | ||
455 | .name = "concat", | ||
456 | .description = NULL_IF_CONFIG_SMALL("Concatenate audio and video streams."), | ||
457 | .init = init, | ||
458 | .uninit = uninit, | ||
459 | .activate = activate, | ||
460 | .priv_size = sizeof(ConcatContext), | ||
461 | .inputs = NULL, | ||
462 | .outputs = NULL, | ||
463 | .priv_class = &concat_class, | ||
464 | .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS, | ||
465 | FILTER_QUERY_FUNC2(query_formats), | ||
466 | .process_command = process_command, | ||
467 | }; | ||
468 |