FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/concatdec.c
Date: 2026-09-26 14:26:21
Exec Total Coverage
Lines: 349 574 60.8%
Functions: 20 24 83.3%
Branches: 187 401 46.6%

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