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