FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/concatdec.c
Date: 2022-12-05 03:11:11
Exec Total Coverage
Lines: 304 527 57.7%
Functions: 17 21 81.0%
Branches: 153 353 43.3%

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 License
8 * 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. See the
14 * 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 #include "libavutil/avstring.h"
22 #include "libavutil/avassert.h"
23 #include "libavutil/bprint.h"
24 #include "libavutil/intreadwrite.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/parseutils.h"
27 #include "libavutil/timestamp.h"
28 #include "libavcodec/codec_desc.h"
29 #include "libavcodec/bsf.h"
30 #include "avformat.h"
31 #include "avio_internal.h"
32 #include "demux.h"
33 #include "internal.h"
34 #include "url.h"
35
36 typedef enum ConcatMatchMode {
37 MATCH_ONE_TO_ONE,
38 MATCH_EXACT_ID,
39 } ConcatMatchMode;
40
41 typedef struct ConcatStream {
42 AVBSFContext *bsf;
43 int out_stream_index;
44 } ConcatStream;
45
46 typedef struct {
47 char *url;
48 int64_t start_time;
49 int64_t file_start_time;
50 int64_t file_inpoint;
51 int64_t duration;
52 int64_t user_duration;
53 int64_t next_dts;
54 ConcatStream *streams;
55 int64_t inpoint;
56 int64_t outpoint;
57 AVDictionary *metadata;
58 AVDictionary *options;
59 int nb_streams;
60 } ConcatFile;
61
62 typedef struct {
63 AVClass *class;
64 ConcatFile *files;
65 ConcatFile *cur_file;
66 unsigned nb_files;
67 AVFormatContext *avf;
68 int safe;
69 int seekable;
70 int eof;
71 ConcatMatchMode stream_match_mode;
72 unsigned auto_convert;
73 int segment_time_metadata;
74 } ConcatContext;
75
76 6782 static int concat_probe(const AVProbeData *probe)
77 {
78 6782 return memcmp(probe->buf, "ffconcat version 1.0", 20) ?
79
2/2
✓ Branch 0 taken 6777 times.
✓ Branch 1 taken 5 times.
6782 0 : AVPROBE_SCORE_MAX;
80 }
81
82 407 static char *get_keyword(uint8_t **cursor)
83 {
84 407 char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
85 407 *cursor += strcspn(*cursor, SPACE_CHARS);
86
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 197 times.
407 if (**cursor) {
87 210 *((*cursor)++) = 0;
88 210 *cursor += strspn(*cursor, SPACE_CHARS);
89 }
90 407 return ret;
91 }
92
93 static int safe_filename(const char *f)
94 {
95 const char *start = f;
96
97 for (; *f; f++) {
98 /* A-Za-z0-9_- */
99 if (!((unsigned)((*f | 32) - 'a') < 26 ||
100 (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {
101 if (f == start)
102 return 0;
103 else if (*f == '/')
104 start = f + 1;
105 else if (*f != '.')
106 return 0;
107 }
108 }
109 return 1;
110 }
111
112 #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
113
114 68 static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
115 unsigned *nb_files_alloc)
116 {
117 68 ConcatContext *cat = avf->priv_data;
118 ConcatFile *file;
119 68 char *url = NULL;
120 const char *proto;
121 const char *ptr;
122 size_t url_len;
123 int ret;
124
125
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
68 if (cat->safe && !safe_filename(filename)) {
126 av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);
127 FAIL(AVERROR(EPERM));
128 }
129
130 68 proto = avio_find_protocol_name(filename);
131
2/4
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 68 times.
68 if (proto && av_strstart(filename, proto, &ptr) &&
132 (*ptr == ':' || *ptr == ',')) {
133 url = filename;
134 filename = NULL;
135 } else {
136 68 url_len = strlen(avf->url) + strlen(filename) + 16;
137
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
68 if (!(url = av_malloc(url_len)))
138 FAIL(AVERROR(ENOMEM));
139 68 ff_make_absolute_url(url, url_len, avf->url, filename);
140 68 av_freep(&filename);
141 }
142
143
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 61 times.
68 if (cat->nb_files >= *nb_files_alloc) {
144 7 size_t n = FFMAX(*nb_files_alloc * 2, 16);
145 ConcatFile *new_files;
146
3/6
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
14 if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
147 7 !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
148 FAIL(AVERROR(ENOMEM));
149 7 cat->files = new_files;
150 7 *nb_files_alloc = n;
151 }
152
153 68 file = &cat->files[cat->nb_files++];
154 68 memset(file, 0, sizeof(*file));
155 68 *rfile = file;
156
157 68 file->url = url;
158 68 file->start_time = AV_NOPTS_VALUE;
159 68 file->duration = AV_NOPTS_VALUE;
160 68 file->next_dts = AV_NOPTS_VALUE;
161 68 file->inpoint = AV_NOPTS_VALUE;
162 68 file->outpoint = AV_NOPTS_VALUE;
163 68 file->user_duration = AV_NOPTS_VALUE;
164
165 68 return 0;
166
167 fail:
168 av_free(url);
169 av_free(filename);
170 return ret;
171 }
172
173 136 static int copy_stream_props(AVStream *st, AVStream *source_st)
174 {
175 int ret;
176
177
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
136 if (st->codecpar->codec_id || !source_st->codecpar->codec_id) {
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 if (st->codecpar->extradata_size < source_st->codecpar->extradata_size) {
179 ret = ff_alloc_extradata(st->codecpar,
180 source_st->codecpar->extradata_size);
181 if (ret < 0)
182 return ret;
183 }
184
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 63 times.
126 if (source_st->codecpar->extradata_size)
185 63 memcpy(st->codecpar->extradata, source_st->codecpar->extradata,
186 63 source_st->codecpar->extradata_size);
187 126 return 0;
188 }
189
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if ((ret = avcodec_parameters_copy(st->codecpar, source_st->codecpar)) < 0)
190 return ret;
191 10 st->r_frame_rate = source_st->r_frame_rate;
192 10 st->avg_frame_rate = source_st->avg_frame_rate;
193 10 st->sample_aspect_ratio = source_st->sample_aspect_ratio;
194 10 avpriv_set_pts_info(st, 64, source_st->time_base.num, source_st->time_base.den);
195
196 10 av_dict_copy(&st->metadata, source_st->metadata, 0);
197 10 ff_stream_side_data_copy(st, source_st);
198 10 return 0;
199 }
200
201 136 static int detect_stream_specific(AVFormatContext *avf, int idx)
202 {
203 136 ConcatContext *cat = avf->priv_data;
204 136 AVStream *st = cat->avf->streams[idx];
205 136 ConcatStream *cs = &cat->cur_file->streams[idx];
206 const AVBitStreamFilter *filter;
207 AVBSFContext *bsf;
208 int ret;
209
210
2/4
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 136 times.
136 if (cat->auto_convert && st->codecpar->codec_id == AV_CODEC_ID_H264) {
211 if (!st->codecpar->extradata_size ||
212 (st->codecpar->extradata_size >= 3 && AV_RB24(st->codecpar->extradata) == 1) ||
213 (st->codecpar->extradata_size >= 4 && AV_RB32(st->codecpar->extradata) == 1))
214 return 0;
215 av_log(cat->avf, AV_LOG_INFO,
216 "Auto-inserting h264_mp4toannexb bitstream filter\n");
217 filter = av_bsf_get_by_name("h264_mp4toannexb");
218 if (!filter) {
219 av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb bitstream filter "
220 "required for H.264 streams\n");
221 return AVERROR_BSF_NOT_FOUND;
222 }
223 ret = av_bsf_alloc(filter, &bsf);
224 if (ret < 0)
225 return ret;
226 cs->bsf = bsf;
227
228 ret = avcodec_parameters_copy(bsf->par_in, st->codecpar);
229 if (ret < 0)
230 return ret;
231
232 ret = av_bsf_init(bsf);
233 if (ret < 0)
234 return ret;
235
236 ret = avcodec_parameters_copy(st->codecpar, bsf->par_out);
237 if (ret < 0)
238 return ret;
239 }
240 136 return 0;
241 }
242
243 64 static int match_streams_one_to_one(AVFormatContext *avf)
244 {
245 64 ConcatContext *cat = avf->priv_data;
246 AVStream *st;
247 int i, ret;
248
249
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 64 times.
192 for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
250
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 8 times.
128 if (i < avf->nb_streams) {
251 120 st = avf->streams[i];
252 } else {
253
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!(st = avformat_new_stream(avf, NULL)))
254 return AVERROR(ENOMEM);
255 }
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
128 if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0)
257 return ret;
258 128 cat->cur_file->streams[i].out_stream_index = i;
259 }
260 64 return 0;
261 }
262
263 4 static int match_streams_exact_id(AVFormatContext *avf)
264 {
265 4 ConcatContext *cat = avf->priv_data;
266 AVStream *st;
267 int i, j, ret;
268
269
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
12 for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
270 8 st = cat->avf->streams[i];
271
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 8 times.
24 for (j = 0; j < avf->nb_streams; j++) {
272
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if (avf->streams[j]->id == st->id) {
273 8 av_log(avf, AV_LOG_VERBOSE,
274 "Match slave stream #%d with stream #%d id 0x%x\n",
275 i, j, st->id);
276
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if ((ret = copy_stream_props(avf->streams[j], st)) < 0)
277 return ret;
278 8 cat->cur_file->streams[i].out_stream_index = j;
279 }
280 }
281 }
282 4 return 0;
283 }
284
285 1202 static int match_streams(AVFormatContext *avf)
286 {
287 1202 ConcatContext *cat = avf->priv_data;
288 ConcatStream *map;
289 int i, ret;
290
291
2/2
✓ Branch 0 taken 1134 times.
✓ Branch 1 taken 68 times.
1202 if (cat->cur_file->nb_streams >= cat->avf->nb_streams)
292 1134 return 0;
293 68 map = av_realloc(cat->cur_file->streams,
294 68 cat->avf->nb_streams * sizeof(*map));
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (!map)
296 return AVERROR(ENOMEM);
297 68 cat->cur_file->streams = map;
298 68 memset(map + cat->cur_file->nb_streams, 0,
299 68 (cat->avf->nb_streams - cat->cur_file->nb_streams) * sizeof(*map));
300
301
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 68 times.
204 for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
302 136 map[i].out_stream_index = -1;
303
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 136 times.
136 if ((ret = detect_stream_specific(avf, i)) < 0)
304 return ret;
305 }
306
2/3
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
68 switch (cat->stream_match_mode) {
307 64 case MATCH_ONE_TO_ONE:
308 64 ret = match_streams_one_to_one(avf);
309 64 break;
310 4 case MATCH_EXACT_ID:
311 4 ret = match_streams_exact_id(avf);
312 4 break;
313 default:
314 ret = AVERROR_BUG;
315 }
316
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (ret < 0)
317 return ret;
318 68 cat->cur_file->nb_streams = cat->avf->nb_streams;
319 68 return 0;
320 }
321
322 136 static int64_t get_best_effort_duration(ConcatFile *file, AVFormatContext *avf)
323 {
324
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 130 times.
136 if (file->user_duration != AV_NOPTS_VALUE)
325 6 return file->user_duration;
326
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 20 times.
130 if (file->outpoint != AV_NOPTS_VALUE)
327 110 return file->outpoint - file->file_inpoint;
328
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (avf->duration > 0)
329 20 return avf->duration - (file->file_inpoint - file->file_start_time);
330 if (file->next_dts != AV_NOPTS_VALUE)
331 return file->next_dts - file->file_inpoint;
332 return AV_NOPTS_VALUE;
333 }
334
335 68 static int open_file(AVFormatContext *avf, unsigned fileno)
336 {
337 68 ConcatContext *cat = avf->priv_data;
338 68 ConcatFile *file = &cat->files[fileno];
339 68 AVDictionary *options = NULL;
340 int ret;
341
342
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 5 times.
68 if (cat->avf)
343 63 avformat_close_input(&cat->avf);
344
345 68 cat->avf = avformat_alloc_context();
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (!cat->avf)
347 return AVERROR(ENOMEM);
348
349 68 cat->avf->flags |= avf->flags & ~AVFMT_FLAG_CUSTOM_IO;
350 68 cat->avf->interrupt_callback = avf->interrupt_callback;
351
352
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
68 if ((ret = ff_copy_whiteblacklists(cat->avf, avf)) < 0)
353 return ret;
354
355 68 ret = av_dict_copy(&options, file->options, 0);
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (ret < 0)
357 return ret;
358
359
2/4
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 68 times.
136 if ((ret = avformat_open_input(&cat->avf, file->url, NULL, &options)) < 0 ||
360 68 (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
361 av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
362 av_dict_free(&options);
363 avformat_close_input(&cat->avf);
364 return ret;
365 }
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (options) {
367 av_log(avf, AV_LOG_WARNING, "Unused options for '%s'.\n", file->url);
368 /* TODO log unused options once we have a proper string API */
369 av_dict_free(&options);
370 }
371 68 cat->cur_file = file;
372
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 5 times.
68 file->start_time = !fileno ? 0 :
373 63 cat->files[fileno - 1].start_time +
374 63 cat->files[fileno - 1].duration;
375
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 file->file_start_time = (cat->avf->start_time == AV_NOPTS_VALUE) ? 0 : cat->avf->start_time;
376
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 58 times.
68 file->file_inpoint = (file->inpoint == AV_NOPTS_VALUE) ? file->file_start_time : file->inpoint;
377 68 file->duration = get_best_effort_duration(file, cat->avf);
378
379
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (cat->segment_time_metadata) {
380 av_dict_set_int(&file->metadata, "lavf.concatdec.start_time", file->start_time, 0);
381 if (file->duration != AV_NOPTS_VALUE)
382 av_dict_set_int(&file->metadata, "lavf.concatdec.duration", file->duration, 0);
383 }
384
385
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
68 if ((ret = match_streams(avf)) < 0)
386 return ret;
387
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 10 times.
68 if (file->inpoint != AV_NOPTS_VALUE) {
388
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
58 if ((ret = avformat_seek_file(cat->avf, -1, INT64_MIN, file->inpoint, file->inpoint, 0)) < 0)
389 return ret;
390 }
391 68 return 0;
392 }
393
394 5 static int concat_read_close(AVFormatContext *avf)
395 {
396 5 ConcatContext *cat = avf->priv_data;
397 unsigned i, j;
398
399
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 5 times.
73 for (i = 0; i < cat->nb_files; i++) {
400 68 av_freep(&cat->files[i].url);
401
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 68 times.
204 for (j = 0; j < cat->files[i].nb_streams; j++) {
402
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 136 times.
136 if (cat->files[i].streams[j].bsf)
403 av_bsf_free(&cat->files[i].streams[j].bsf);
404 }
405 68 av_freep(&cat->files[i].streams);
406 68 av_dict_free(&cat->files[i].metadata);
407 68 av_dict_free(&cat->files[i].options);
408 }
409
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (cat->avf)
410 5 avformat_close_input(&cat->avf);
411 5 av_freep(&cat->files);
412 5 return 0;
413 }
414
415 #define MAX_ARGS 3
416 #define NEEDS_UNSAFE (1 << 0)
417 #define NEEDS_FILE (1 << 1)
418 #define NEEDS_STREAM (1 << 2)
419
420 typedef struct ParseSyntax {
421 const char *keyword;
422 char args[MAX_ARGS];
423 uint8_t flags;
424 } ParseSyntax;
425
426 typedef enum ParseDirective {
427 DIR_FFCONCAT,
428 DIR_FILE,
429 DIR_DURATION,
430 DIR_INPOINT,
431 DIR_OUTPOINT,
432 DIR_FPMETA,
433 DIR_FPMETAS,
434 DIR_OPTION,
435 DIR_STREAM,
436 DIR_EXSID,
437 DIR_STMETA,
438 DIR_STCODEC,
439 DIR_STEDATA,
440 DIR_CHAPTER,
441 } ParseDirective;
442
443 static const ParseSyntax syntax[] = {
444 [DIR_FFCONCAT ] = { "ffconcat", "kk", 0 },
445 [DIR_FILE ] = { "file", "s", 0 },
446 [DIR_DURATION ] = { "duration", "d", NEEDS_FILE },
447 [DIR_INPOINT ] = { "inpoint", "d", NEEDS_FILE },
448 [DIR_OUTPOINT ] = { "outpoint", "d", NEEDS_FILE },
449 [DIR_FPMETA ] = { "file_packet_meta", "ks", NEEDS_FILE },
450 [DIR_FPMETAS ] = { "file_packet_metadata", "s", NEEDS_FILE },
451 [DIR_OPTION ] = { "option", "ks", NEEDS_FILE | NEEDS_UNSAFE },
452 [DIR_STREAM ] = { "stream", "", 0 },
453 [DIR_EXSID ] = { "exact_stream_id", "i", NEEDS_STREAM },
454 [DIR_STMETA ] = { "stream_meta", "ks", NEEDS_STREAM },
455 [DIR_STCODEC ] = { "stream_codec", "k", NEEDS_STREAM },
456 [DIR_STEDATA ] = { "stream_extradata", "k", NEEDS_STREAM },
457 [DIR_CHAPTER ] = { "chapter", "idd", 0 },
458 };
459
460 5 static int concat_parse_script(AVFormatContext *avf)
461 {
462 5 ConcatContext *cat = avf->priv_data;
463 5 unsigned nb_files_alloc = 0;
464 AVBPrint bp;
465 uint8_t *cursor, *keyword;
466 5 ConcatFile *file = NULL;
467 5 AVStream *stream = NULL;
468 5 AVChapter *chapter = NULL;
469 5 unsigned line = 0, arg;
470 const ParseSyntax *dir;
471 char *arg_kw[MAX_ARGS];
472 5 char *arg_str[MAX_ARGS] = { 0 };
473 int64_t arg_int[MAX_ARGS];
474 int ret;
475
476 5 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
477
478
2/2
✓ Branch 1 taken 273 times.
✓ Branch 2 taken 5 times.
278 while ((ret = ff_read_line_to_bprint_overwrite(avf->pb, &bp)) >= 0) {
479 273 line++;
480 273 cursor = bp.str;
481 273 keyword = get_keyword(&cursor);
482
3/4
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 199 times.
273 if (!*keyword || *keyword == '#')
483 74 continue;
484
1/2
✓ Branch 0 taken 741 times.
✗ Branch 1 not taken.
741 for (dir = syntax; dir < syntax + FF_ARRAY_ELEMS(syntax); dir++)
485
2/2
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 542 times.
741 if (!strcmp(dir->keyword, keyword))
486 199 break;
487
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
199 if (dir >= syntax + FF_ARRAY_ELEMS(syntax)) {
488 av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
489 line, keyword);
490 FAIL(AVERROR_INVALIDDATA);
491 }
492
493 /* Flags check */
494
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 199 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
199 if ((dir->flags & NEEDS_UNSAFE) && cat->safe) {
495 av_log(avf, AV_LOG_ERROR, "Line %d: %s not allowed if safe\n", line, keyword);
496 FAIL(AVERROR_INVALIDDATA);
497 }
498
3/4
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 120 times.
199 if ((dir->flags & NEEDS_FILE) && !cat->nb_files) {
499 av_log(avf, AV_LOG_ERROR, "Line %d: %s without file\n", line, keyword);
500 FAIL(AVERROR_INVALIDDATA);
501 }
502
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 195 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
199 if ((dir->flags & NEEDS_STREAM) && !avf->nb_streams) {
503 av_log(avf, AV_LOG_ERROR, "Line %d: %s without stream\n", line, keyword);
504 FAIL(AVERROR_INVALIDDATA);
505 }
506
507 /* Arguments parsing */
508
3/4
✓ Branch 0 taken 407 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✓ Branch 3 taken 199 times.
407 for (arg = 0; arg < FF_ARRAY_ELEMS(dir->args) && dir->args[arg]; arg++) {
509
4/5
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 74 times.
✗ Branch 4 not taken.
208 switch (dir->args[arg]) {
510 116 case 'd': /* duration */
511 116 arg_kw[arg] = get_keyword(&cursor);
512 116 ret = av_parse_time(&arg_int[arg], arg_kw[arg], 1);
513
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 if (ret < 0) {
514 av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n",
515 line, arg_kw[arg]);
516 goto fail;
517 }
518 116 break;
519 2 case 'i': /* integer */
520 2 arg_int[arg] = strtol(get_keyword(&cursor), NULL, 0);
521 2 break;
522 16 case 'k': /* keyword */
523 16 arg_kw[arg] = get_keyword(&cursor);
524 16 break;
525 74 case 's': /* string */
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 av_assert0(!arg_str[arg]);
527 74 arg_str[arg] = av_get_token((const char **)&cursor, SPACE_CHARS);
528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 if (!arg_str[arg])
529 FAIL(AVERROR(ENOMEM));
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 if (!*arg_str[arg]) {
531 av_log(avf, AV_LOG_ERROR, "Line %d: string required\n", line);
532 FAIL(AVERROR_INVALIDDATA);
533 }
534 74 break;
535 default:
536 FAIL(AVERROR_BUG);
537 }
538 }
539
540 /* Directive action */
541
9/15
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 58 times.
✓ Branch 4 taken 55 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
199 switch ((ParseDirective)(dir - syntax)) {
542
543 5 case DIR_FFCONCAT:
544
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (strcmp(arg_kw[0], "version") || strcmp(arg_kw[1], "1.0")) {
545 av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line);
546 FAIL(AVERROR_INVALIDDATA);
547 }
548 199 break;
549
550 68 case DIR_FILE:
551 68 ret = add_file(avf, arg_str[0], &file, &nb_files_alloc);
552 68 arg_str[0] = NULL;
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
68 if (ret < 0)
554 goto fail;
555 68 break;
556
557 3 case DIR_DURATION:
558 3 file->user_duration = arg_int[0];
559 3 break;
560
561 58 case DIR_INPOINT:
562 58 file->inpoint = arg_int[0];
563 58 break;
564
565 55 case DIR_OUTPOINT:
566 55 file->outpoint = arg_int[0];
567 55 break;
568
569 4 case DIR_FPMETA:
570 4 ret = av_dict_set(&file->metadata, arg_kw[0], arg_str[1], AV_DICT_DONT_STRDUP_VAL);
571 4 arg_str[1] = NULL;
572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ret < 0)
573 FAIL(ret);
574 4 break;
575
576 case DIR_FPMETAS:
577 if ((ret = av_dict_parse_string(&file->metadata, arg_str[0], "=", "", 0)) < 0) {
578 av_log(avf, AV_LOG_ERROR, "Line %d: failed to parse metadata string\n", line);
579 FAIL(AVERROR_INVALIDDATA);
580 }
581 av_log(avf, AV_LOG_WARNING,
582 "'file_packet_metadata key=value:key=value' is deprecated, "
583 "use multiple 'file_packet_meta key value' instead\n");
584 av_freep(&arg_str[0]);
585 break;
586
587 case DIR_OPTION:
588 ret = av_dict_set(&file->options, arg_kw[0], arg_str[1], AV_DICT_DONT_STRDUP_VAL);
589 arg_str[1] = NULL;
590 if (ret < 0)
591 FAIL(ret);
592 break;
593
594 2 case DIR_STREAM:
595 2 stream = avformat_new_stream(avf, NULL);
596
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!stream)
597 FAIL(AVERROR(ENOMEM));
598 2 break;
599
600 2 case DIR_EXSID:
601 2 stream->id = arg_int[0];
602 2 break;
603 2 case DIR_STMETA:
604 2 ret = av_dict_set(&stream->metadata, arg_kw[0], arg_str[1], AV_DICT_DONT_STRDUP_VAL);
605 2 arg_str[1] = NULL;
606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ret < 0)
607 FAIL(ret);
608 2 break;
609
610 case DIR_STCODEC: {
611 const AVCodecDescriptor *codec = avcodec_descriptor_get_by_name(arg_kw[0]);
612 if (!codec) {
613 av_log(avf, AV_LOG_ERROR, "Line %d: codec '%s' not found\n", line, arg_kw[0]);
614 FAIL(AVERROR_DECODER_NOT_FOUND);
615 }
616 stream->codecpar->codec_type = codec->type;
617 stream->codecpar->codec_id = codec->id;
618 break;
619 }
620
621 case DIR_STEDATA: {
622 int size = ff_hex_to_data(NULL, arg_kw[0]);
623 ret = ff_alloc_extradata(stream->codecpar, size);
624 if (ret < 0)
625 FAIL(ret);
626 ff_hex_to_data(stream->codecpar->extradata, arg_kw[0]);
627 break;
628 }
629
630 case DIR_CHAPTER:
631 chapter = avpriv_new_chapter(avf, arg_int[0], AV_TIME_BASE_Q,
632 arg_int[1], arg_int[2], NULL);
633 if (!chapter)
634 FAIL(ENOMEM);
635 break;
636
637 default:
638 FAIL(AVERROR_BUG);
639 }
640 }
641
642 5 fail:
643
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 5 times.
20 for (arg = 0; arg < MAX_ARGS; arg++)
644 15 av_freep(&arg_str[arg]);
645 5 av_bprint_finalize(&bp, NULL);
646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 return ret == AVERROR_EOF ? 0 : ret;
647 }
648
649 5 static int concat_read_header(AVFormatContext *avf)
650 {
651 5 ConcatContext *cat = avf->priv_data;
652 5 int64_t time = 0;
653 unsigned i;
654 int ret;
655
656 5 ret = concat_parse_script(avf);
657
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (ret < 0)
658 return ret;
659
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!cat->nb_files) {
660 av_log(avf, AV_LOG_ERROR, "No files to concat\n");
661 return AVERROR_INVALIDDATA;
662 }
663
664
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 for (i = 0; i < cat->nb_files; i++) {
665
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (cat->files[i].start_time == AV_NOPTS_VALUE)
666 5 cat->files[i].start_time = time;
667 else
668 time = cat->files[i].start_time;
669
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (cat->files[i].user_duration == AV_NOPTS_VALUE) {
670
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
5 if (cat->files[i].inpoint == AV_NOPTS_VALUE || cat->files[i].outpoint == AV_NOPTS_VALUE)
671 break;
672 cat->files[i].user_duration = cat->files[i].outpoint - cat->files[i].inpoint;
673 }
674 cat->files[i].duration = cat->files[i].user_duration;
675 time += cat->files[i].user_duration;
676 }
677
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (i == cat->nb_files) {
678 avf->duration = time;
679 cat->seekable = 1;
680 }
681
682 5 cat->stream_match_mode = avf->nb_streams ? MATCH_EXACT_ID :
683 MATCH_ONE_TO_ONE;
684
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if ((ret = open_file(avf, 0)) < 0)
685 return ret;
686
687 5 return 0;
688 }
689
690 68 static int open_next_file(AVFormatContext *avf)
691 {
692 68 ConcatContext *cat = avf->priv_data;
693 68 unsigned fileno = cat->cur_file - cat->files;
694
695 68 cat->cur_file->duration = get_best_effort_duration(cat->cur_file, cat->avf);
696
697
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 63 times.
68 if (++fileno >= cat->nb_files) {
698 5 cat->eof = 1;
699 5 return AVERROR_EOF;
700 }
701 63 return open_file(avf, fileno);
702 }
703
704 1081 static int filter_packet(AVFormatContext *avf, ConcatStream *cs, AVPacket *pkt)
705 {
706 int ret;
707
708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1081 times.
1081 if (cs->bsf) {
709 ret = av_bsf_send_packet(cs->bsf, pkt);
710 if (ret < 0) {
711 av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter "
712 "failed to send input packet\n");
713 return ret;
714 }
715
716 while (!ret)
717 ret = av_bsf_receive_packet(cs->bsf, pkt);
718
719 if (ret < 0 && (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)) {
720 av_log(avf, AV_LOG_ERROR, "h264_mp4toannexb filter "
721 "failed to receive output packet\n");
722 return ret;
723 }
724 }
725 1081 return 0;
726 }
727
728 /* Returns true if the packet dts is greater or equal to the specified outpoint. */
729 1134 static int packet_after_outpoint(ConcatContext *cat, AVPacket *pkt)
730 {
731
3/4
✓ Branch 0 taken 570 times.
✓ Branch 1 taken 564 times.
✓ Branch 2 taken 570 times.
✗ Branch 3 not taken.
1134 if (cat->cur_file->outpoint != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE) {
732 570 return av_compare_ts(pkt->dts, cat->avf->streams[pkt->stream_index]->time_base,
733 570 cat->cur_file->outpoint, AV_TIME_BASE_Q) >= 0;
734 }
735 564 return 0;
736 }
737
738 1086 static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
739 {
740 1086 ConcatContext *cat = avf->priv_data;
741 int ret;
742 int64_t delta;
743 ConcatStream *cs;
744 AVStream *st;
745 FFStream *sti;
746
747
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1086 times.
1086 if (cat->eof)
748 return AVERROR_EOF;
749
750
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1086 times.
1086 if (!cat->avf)
751 return AVERROR(EIO);
752
753 while (1) {
754 1149 ret = av_read_frame(cat->avf, pkt);
755
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1134 times.
1149 if (ret == AVERROR_EOF) {
756
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 13 times.
15 if ((ret = open_next_file(avf)) < 0)
757 2 return ret;
758 13 continue;
759 }
760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1134 times.
1134 if (ret < 0)
761 return ret;
762
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1134 times.
1134 if ((ret = match_streams(avf)) < 0) {
763 return ret;
764 }
765
2/2
✓ Branch 1 taken 53 times.
✓ Branch 2 taken 1081 times.
1134 if (packet_after_outpoint(cat, pkt)) {
766 53 av_packet_unref(pkt);
767
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 50 times.
53 if ((ret = open_next_file(avf)) < 0)
768 3 return ret;
769 50 continue;
770 }
771 1081 cs = &cat->cur_file->streams[pkt->stream_index];
772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1081 times.
1081 if (cs->out_stream_index < 0) {
773 av_packet_unref(pkt);
774 continue;
775 }
776 1081 break;
777 }
778
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1081 times.
1081 if ((ret = filter_packet(avf, cs, pkt)) < 0)
779 return ret;
780
781 1081 st = cat->avf->streams[pkt->stream_index];
782 1081 sti = ffstream(st);
783 5405 av_log(avf, AV_LOG_DEBUG, "file:%d stream:%d pts:%s pts_time:%s dts:%s dts_time:%s",
784 1081 (unsigned)(cat->cur_file - cat->files), pkt->stream_index,
785 1081 av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
786 1081 av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
787
788 1081 delta = av_rescale_q(cat->cur_file->start_time - cat->cur_file->file_inpoint,
789 1081 AV_TIME_BASE_Q,
790 1081 cat->avf->streams[pkt->stream_index]->time_base);
791
1/2
✓ Branch 0 taken 1081 times.
✗ Branch 1 not taken.
1081 if (pkt->pts != AV_NOPTS_VALUE)
792 1081 pkt->pts += delta;
793
1/2
✓ Branch 0 taken 1081 times.
✗ Branch 1 not taken.
1081 if (pkt->dts != AV_NOPTS_VALUE)
794 1081 pkt->dts += delta;
795 1081 av_log(avf, AV_LOG_DEBUG, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
796 1081 av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &st->time_base),
797 1081 av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &st->time_base));
798
2/2
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 950 times.
1081 if (cat->cur_file->metadata) {
799 size_t metadata_len;
800 131 char* packed_metadata = av_packet_pack_dictionary(cat->cur_file->metadata, &metadata_len);
801
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 131 times.
131 if (!packed_metadata)
802 return AVERROR(ENOMEM);
803 131 ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA,
804 packed_metadata, metadata_len);
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 131 times.
131 if (ret < 0) {
806 av_freep(&packed_metadata);
807 return ret;
808 }
809 }
810
811
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1081 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1081 if (cat->cur_file->duration == AV_NOPTS_VALUE && sti->cur_dts != AV_NOPTS_VALUE) {
812 int64_t next_dts = av_rescale_q(sti->cur_dts, st->time_base, AV_TIME_BASE_Q);
813 if (cat->cur_file->next_dts == AV_NOPTS_VALUE || next_dts > cat->cur_file->next_dts) {
814 cat->cur_file->next_dts = next_dts;
815 }
816 }
817
818 1081 pkt->stream_index = cs->out_stream_index;
819 1081 return 0;
820 }
821
822 static int try_seek(AVFormatContext *avf, int stream,
823 int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
824 {
825 ConcatContext *cat = avf->priv_data;
826 int64_t t0 = cat->cur_file->start_time - cat->cur_file->file_inpoint;
827
828 ts -= t0;
829 min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
830 max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
831 if (stream >= 0) {
832 if (stream >= cat->avf->nb_streams)
833 return AVERROR(EIO);
834 ff_rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
835 &min_ts, &ts, &max_ts);
836 }
837 return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
838 }
839
840 static int real_seek(AVFormatContext *avf, int stream,
841 int64_t min_ts, int64_t ts, int64_t max_ts, int flags, AVFormatContext *cur_avf)
842 {
843 ConcatContext *cat = avf->priv_data;
844 int ret, left, right;
845
846 if (stream >= 0) {
847 if (stream >= avf->nb_streams)
848 return AVERROR(EINVAL);
849 ff_rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
850 &min_ts, &ts, &max_ts);
851 }
852
853 left = 0;
854 right = cat->nb_files;
855
856 /* Always support seek to start */
857 if (ts <= 0)
858 right = 1;
859 else if (!cat->seekable)
860 return AVERROR(ESPIPE); /* XXX: can we use it? */
861
862 while (right - left > 1) {
863 int mid = (left + right) / 2;
864 if (ts < cat->files[mid].start_time)
865 right = mid;
866 else
867 left = mid;
868 }
869
870 if (cat->cur_file != &cat->files[left]) {
871 if ((ret = open_file(avf, left)) < 0)
872 return ret;
873 } else {
874 cat->avf = cur_avf;
875 }
876
877 ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
878 if (ret < 0 &&
879 left < cat->nb_files - 1 &&
880 cat->files[left + 1].start_time < max_ts) {
881 if (cat->cur_file == &cat->files[left])
882 cat->avf = NULL;
883 if ((ret = open_file(avf, left + 1)) < 0)
884 return ret;
885 ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
886 }
887 return ret;
888 }
889
890 static int concat_seek(AVFormatContext *avf, int stream,
891 int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
892 {
893 ConcatContext *cat = avf->priv_data;
894 ConcatFile *cur_file_saved = cat->cur_file;
895 AVFormatContext *cur_avf_saved = cat->avf;
896 int ret;
897
898 if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
899 return AVERROR(ENOSYS);
900 cat->avf = NULL;
901 if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags, cur_avf_saved)) < 0) {
902 if (cat->cur_file != cur_file_saved) {
903 if (cat->avf)
904 avformat_close_input(&cat->avf);
905 }
906 cat->avf = cur_avf_saved;
907 cat->cur_file = cur_file_saved;
908 } else {
909 if (cat->cur_file != cur_file_saved) {
910 avformat_close_input(&cur_avf_saved);
911 }
912 cat->eof = 0;
913 }
914 return ret;
915 }
916
917 #define OFFSET(x) offsetof(ConcatContext, x)
918 #define DEC AV_OPT_FLAG_DECODING_PARAM
919
920 static const AVOption options[] = {
921 { "safe", "enable safe mode",
922 OFFSET(safe), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DEC },
923 { "auto_convert", "automatically convert bitstream format",
924 OFFSET(auto_convert), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DEC },
925 { "segment_time_metadata", "output file segment start time and duration as packet metadata",
926 OFFSET(segment_time_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC },
927 { NULL }
928 };
929
930 static const AVClass concat_class = {
931 .class_name = "concat demuxer",
932 .item_name = av_default_item_name,
933 .option = options,
934 .version = LIBAVUTIL_VERSION_INT,
935 };
936
937
938 const AVInputFormat ff_concat_demuxer = {
939 .name = "concat",
940 .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
941 .priv_data_size = sizeof(ConcatContext),
942 .flags_internal = FF_FMT_INIT_CLEANUP,
943 .read_probe = concat_probe,
944 .read_header = concat_read_header,
945 .read_packet = concat_read_packet,
946 .read_close = concat_read_close,
947 .read_seek2 = concat_seek,
948 .priv_class = &concat_class,
949 };
950