2 * Copyright (c) 2012 Nicolas George
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "libavutil/avstring.h"
22 #include "libavutil/avassert.h"
23 #include "libavutil/bprint.h"
24 #include "libavutil/intreadwrite.h"
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/parseutils.h"
28 #include "libavutil/timestamp.h"
29 #include "libavcodec/codec_desc.h"
30 #include "libavcodec/bsf.h"
32 #include "avio_internal.h"
37 typedef enum ConcatMatchMode
{
42 typedef struct ConcatStream
{
50 int64_t file_start_time
;
53 int64_t user_duration
;
55 ConcatStream
*streams
;
58 AVDictionary
*metadata
;
59 AVDictionary
*options
;
72 ConcatMatchMode stream_match_mode
;
73 unsigned auto_convert
;
74 int segment_time_metadata
;
77 static int concat_probe(const AVProbeData
*probe
)
79 return memcmp(probe
->buf
, "ffconcat version 1.0", 20) ?
80 0 : AVPROBE_SCORE_MAX
;
83 static char *get_keyword(uint8_t **cursor
)
85 char *ret
= *cursor
+= strspn(*cursor
, SPACE_CHARS
);
86 *cursor
+= strcspn(*cursor
, SPACE_CHARS
);
89 *cursor
+= strspn(*cursor
, SPACE_CHARS
);
94 static int safe_filename(const char *f
)
96 const char *start
= f
;
100 if (!((unsigned)((*f
| 32) - 'a') < 26 ||
101 (unsigned)(*f
- '0') < 10 || *f
== '_' || *f
== '-')) {
113 #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
115 static int add_file(AVFormatContext
*avf
, char *filename
, ConcatFile
**rfile
,
116 unsigned *nb_files_alloc
)
118 ConcatContext
*cat
= avf
->priv_data
;
126 if (cat
->safe
&& !safe_filename(filename
)) {
127 av_log(avf
, AV_LOG_ERROR
, "Unsafe file name '%s'\n", filename
);
128 FAIL(AVERROR(EPERM
));
131 proto
= avio_find_protocol_name(filename
);
132 if (proto
&& av_strstart(filename
, proto
, &ptr
) &&
133 (*ptr
== ':' || *ptr
== ',')) {
137 url_len
= strlen(avf
->url
) + strlen(filename
) + 16;
138 if (!(url
= av_malloc(url_len
)))
139 FAIL(AVERROR(ENOMEM
));
140 ff_make_absolute_url(url
, url_len
, avf
->url
, filename
);
144 if (cat
->nb_files
>= *nb_files_alloc
) {
145 size_t n
= FFMAX(*nb_files_alloc
* 2, 16);
146 ConcatFile
*new_files
;
147 if (n
<= cat
->nb_files
|| n
> SIZE_MAX
/ sizeof(*cat
->files
) ||
148 !(new_files
= av_realloc(cat
->files
, n
* sizeof(*cat
->files
))))
149 FAIL(AVERROR(ENOMEM
));
150 cat
->files
= new_files
;
154 file
= &cat
->files
[cat
->nb_files
++];
155 memset(file
, 0, sizeof(*file
));
159 file
->start_time
= AV_NOPTS_VALUE
;
160 file
->duration
= AV_NOPTS_VALUE
;
161 file
->next_dts
= AV_NOPTS_VALUE
;
162 file
->inpoint
= AV_NOPTS_VALUE
;
163 file
->outpoint
= AV_NOPTS_VALUE
;
164 file
->user_duration
= AV_NOPTS_VALUE
;
174 static int copy_stream_props(AVStream
*st
, AVStream
*source_st
)
178 if (st
->codecpar
->codec_id
|| !source_st
->codecpar
->codec_id
) {
179 if (st
->codecpar
->extradata_size
< source_st
->codecpar
->extradata_size
) {
180 ret
= ff_alloc_extradata(st
->codecpar
,
181 source_st
->codecpar
->extradata_size
);
185 if (source_st
->codecpar
->extradata_size
)
186 memcpy(st
->codecpar
->extradata
, source_st
->codecpar
->extradata
,
187 source_st
->codecpar
->extradata_size
);
190 if ((ret
= avcodec_parameters_copy(st
->codecpar
, source_st
->codecpar
)) < 0)
192 st
->r_frame_rate
= source_st
->r_frame_rate
;
193 st
->avg_frame_rate
= source_st
->avg_frame_rate
;
194 st
->sample_aspect_ratio
= source_st
->sample_aspect_ratio
;
195 avpriv_set_pts_info(st
, 64, source_st
->time_base
.num
, source_st
->time_base
.den
);
197 av_dict_copy(&st
->metadata
, source_st
->metadata
, 0);
201 static int detect_stream_specific(AVFormatContext
*avf
, int idx
)
203 ConcatContext
*cat
= avf
->priv_data
;
204 AVStream
*st
= cat
->avf
->streams
[idx
];
205 ConcatStream
*cs
= &cat
->cur_file
->streams
[idx
];
206 const AVBitStreamFilter
*filter
;
210 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))
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");
219 av_log(avf
, AV_LOG_ERROR
, "h264_mp4toannexb bitstream filter "
220 "required for H.264 streams\n");
221 return AVERROR_BSF_NOT_FOUND
;
223 ret
= av_bsf_alloc(filter
, &bsf
);
228 ret
= avcodec_parameters_copy(bsf
->par_in
, st
->codecpar
);
232 ret
= av_bsf_init(bsf
);
236 ret
= avcodec_parameters_copy(st
->codecpar
, bsf
->par_out
);
243 static int match_streams_one_to_one(AVFormatContext
*avf
)
245 ConcatContext
*cat
= avf
->priv_data
;
249 for (i
= cat
->cur_file
->nb_streams
; i
< cat
->avf
->nb_streams
; i
++) {
250 if (i
< avf
->nb_streams
) {
251 st
= avf
->streams
[i
];
253 if (!(st
= avformat_new_stream(avf
, NULL
)))
254 return AVERROR(ENOMEM
);
256 if ((ret
= copy_stream_props(st
, cat
->avf
->streams
[i
])) < 0)
258 cat
->cur_file
->streams
[i
].out_stream_index
= i
;
263 static int match_streams_exact_id(AVFormatContext
*avf
)
265 ConcatContext
*cat
= avf
->priv_data
;
269 for (i
= cat
->cur_file
->nb_streams
; i
< cat
->avf
->nb_streams
; i
++) {
270 st
= cat
->avf
->streams
[i
];
271 for (j
= 0; j
< avf
->nb_streams
; j
++) {
272 if (avf
->streams
[j
]->id
== st
->id
) {
273 av_log(avf
, AV_LOG_VERBOSE
,
274 "Match slave stream #%d with stream #%d id 0x%x\n",
276 if ((ret
= copy_stream_props(avf
->streams
[j
], st
)) < 0)
278 cat
->cur_file
->streams
[i
].out_stream_index
= j
;
285 static int match_streams(AVFormatContext
*avf
)
287 ConcatContext
*cat
= avf
->priv_data
;
291 if (cat
->cur_file
->nb_streams
>= cat
->avf
->nb_streams
)
293 map
= av_realloc(cat
->cur_file
->streams
,
294 cat
->avf
->nb_streams
* sizeof(*map
));
296 return AVERROR(ENOMEM
);
297 cat
->cur_file
->streams
= map
;
298 memset(map
+ cat
->cur_file
->nb_streams
, 0,
299 (cat
->avf
->nb_streams
- cat
->cur_file
->nb_streams
) * sizeof(*map
));
301 for (i
= cat
->cur_file
->nb_streams
; i
< cat
->avf
->nb_streams
; i
++) {
302 map
[i
].out_stream_index
= -1;
303 if ((ret
= detect_stream_specific(avf
, i
)) < 0)
306 switch (cat
->stream_match_mode
) {
307 case MATCH_ONE_TO_ONE
:
308 ret
= match_streams_one_to_one(avf
);
311 ret
= match_streams_exact_id(avf
);
318 cat
->cur_file
->nb_streams
= cat
->avf
->nb_streams
;
322 static int64_t get_best_effort_duration(ConcatFile
*file
, AVFormatContext
*avf
)
324 if (file
->user_duration
!= AV_NOPTS_VALUE
)
325 return file
->user_duration
;
326 if (file
->outpoint
!= AV_NOPTS_VALUE
)
327 return av_sat_sub64(file
->outpoint
, file
->file_inpoint
);
328 if (avf
->duration
> 0)
329 return av_sat_sub64(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
;
335 static int open_file(AVFormatContext
*avf
, unsigned fileno
)
337 ConcatContext
*cat
= avf
->priv_data
;
338 ConcatFile
*file
= &cat
->files
[fileno
];
339 AVDictionary
*options
= NULL
;
343 avformat_close_input(&cat
->avf
);
345 cat
->avf
= avformat_alloc_context();
347 return AVERROR(ENOMEM
);
349 cat
->avf
->flags
|= avf
->flags
& ~AVFMT_FLAG_CUSTOM_IO
;
350 cat
->avf
->interrupt_callback
= avf
->interrupt_callback
;
352 if ((ret
= ff_copy_whiteblacklists(cat
->avf
, avf
)) < 0)
355 ret
= av_dict_copy(&options
, file
->options
, 0);
359 if ((ret
= avformat_open_input(&cat
->avf
, file
->url
, NULL
, &options
)) < 0 ||
360 (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
);
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
);
371 cat
->cur_file
= file
;
372 file
->start_time
= !fileno
? 0 :
373 cat
->files
[fileno
- 1].start_time
+
374 cat
->files
[fileno
- 1].duration
;
375 file
->file_start_time
= (cat
->avf
->start_time
== AV_NOPTS_VALUE
) ? 0 : cat
->avf
->start_time
;
376 file
->file_inpoint
= (file
->inpoint
== AV_NOPTS_VALUE
) ? file
->file_start_time
: file
->inpoint
;
377 file
->duration
= get_best_effort_duration(file
, cat
->avf
);
379 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);
385 if ((ret
= match_streams(avf
)) < 0)
387 if (file
->inpoint
!= AV_NOPTS_VALUE
) {
388 if ((ret
= avformat_seek_file(cat
->avf
, -1, INT64_MIN
, file
->inpoint
, file
->inpoint
, 0)) < 0)
394 static int concat_read_close(AVFormatContext
*avf
)
396 ConcatContext
*cat
= avf
->priv_data
;
399 for (i
= 0; i
< cat
->nb_files
; i
++) {
400 av_freep(&cat
->files
[i
].url
);
401 for (j
= 0; j
< cat
->files
[i
].nb_streams
; j
++) {
402 if (cat
->files
[i
].streams
[j
].bsf
)
403 av_bsf_free(&cat
->files
[i
].streams
[j
].bsf
);
405 av_freep(&cat
->files
[i
].streams
);
406 av_dict_free(&cat
->files
[i
].metadata
);
407 av_dict_free(&cat
->files
[i
].options
);
410 avformat_close_input(&cat
->avf
);
411 av_freep(&cat
->files
);
416 #define NEEDS_UNSAFE (1 << 0)
417 #define NEEDS_FILE (1 << 1)
418 #define NEEDS_STREAM (1 << 2)
420 typedef struct ParseSyntax
{
426 typedef enum ParseDirective
{
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 },
460 static int concat_parse_script(AVFormatContext
*avf
)
462 ConcatContext
*cat
= avf
->priv_data
;
463 unsigned nb_files_alloc
= 0;
465 uint8_t *cursor
, *keyword
;
466 ConcatFile
*file
= NULL
;
467 AVStream
*stream
= NULL
;
468 AVChapter
*chapter
= NULL
;
469 unsigned line
= 0, arg
;
470 const ParseSyntax
*dir
;
471 char *arg_kw
[MAX_ARGS
];
472 char *arg_str
[MAX_ARGS
] = { 0 };
473 int64_t arg_int
[MAX_ARGS
];
476 av_bprint_init(&bp
, 0, AV_BPRINT_SIZE_UNLIMITED
);
478 while ((ret
= ff_read_line_to_bprint_overwrite(avf
->pb
, &bp
)) >= 0) {
481 keyword
= get_keyword(&cursor
);
482 if (!*keyword
|| *keyword
== '#')
484 for (dir
= syntax
; dir
< syntax
+ FF_ARRAY_ELEMS(syntax
); dir
++)
485 if (!strcmp(dir
->keyword
, keyword
))
487 if (dir
>= syntax
+ FF_ARRAY_ELEMS(syntax
)) {
488 av_log(avf
, AV_LOG_ERROR
, "Line %d: unknown keyword '%s'\n",
490 FAIL(AVERROR_INVALIDDATA
);
494 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
);
498 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
);
502 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
);
507 /* Arguments parsing */
508 for (arg
= 0; arg
< FF_ARRAY_ELEMS(dir
->args
) && dir
->args
[arg
]; arg
++) {
509 switch (dir
->args
[arg
]) {
510 case 'd': /* duration */
511 arg_kw
[arg
] = get_keyword(&cursor
);
512 ret
= av_parse_time(&arg_int
[arg
], arg_kw
[arg
], 1);
514 av_log(avf
, AV_LOG_ERROR
, "Line %d: invalid duration '%s'\n",
519 case 'i': /* integer */
520 arg_int
[arg
] = strtol(get_keyword(&cursor
), NULL
, 0);
522 case 'k': /* keyword */
523 arg_kw
[arg
] = get_keyword(&cursor
);
525 case 's': /* string */
526 av_assert0(!arg_str
[arg
]);
527 arg_str
[arg
] = av_get_token((const char **)&cursor
, SPACE_CHARS
);
529 FAIL(AVERROR(ENOMEM
));
530 if (!*arg_str
[arg
]) {
531 av_log(avf
, AV_LOG_ERROR
, "Line %d: string required\n", line
);
532 FAIL(AVERROR_INVALIDDATA
);
540 /* Directive action */
541 switch ((ParseDirective
)(dir
- syntax
)) {
544 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
);
551 ret
= add_file(avf
, arg_str
[0], &file
, &nb_files_alloc
);
558 file
->user_duration
= arg_int
[0];
562 file
->inpoint
= arg_int
[0];
566 file
->outpoint
= arg_int
[0];
570 ret
= av_dict_set(&file
->metadata
, arg_kw
[0], arg_str
[1], AV_DICT_DONT_STRDUP_VAL
);
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
);
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]);
588 ret
= av_dict_set(&file
->options
, arg_kw
[0], arg_str
[1], AV_DICT_DONT_STRDUP_VAL
);
595 stream
= avformat_new_stream(avf
, NULL
);
597 FAIL(AVERROR(ENOMEM
));
601 stream
->id
= arg_int
[0];
604 ret
= av_dict_set(&stream
->metadata
, arg_kw
[0], arg_str
[1], AV_DICT_DONT_STRDUP_VAL
);
611 const AVCodecDescriptor
*codec
= avcodec_descriptor_get_by_name(arg_kw
[0]);
613 av_log(avf
, AV_LOG_ERROR
, "Line %d: codec '%s' not found\n", line
, arg_kw
[0]);
614 FAIL(AVERROR_DECODER_NOT_FOUND
);
616 stream
->codecpar
->codec_type
= codec
->type
;
617 stream
->codecpar
->codec_id
= codec
->id
;
622 int size
= ff_hex_to_data(NULL
, arg_kw
[0]);
623 ret
= ff_alloc_extradata(stream
->codecpar
, size
);
626 ff_hex_to_data(stream
->codecpar
->extradata
, arg_kw
[0]);
631 chapter
= avpriv_new_chapter(avf
, arg_int
[0], AV_TIME_BASE_Q
,
632 arg_int
[1], arg_int
[2], NULL
);
643 ret
= AVERROR_INVALIDDATA
;
647 if (file
->inpoint
!= AV_NOPTS_VALUE
&& file
->outpoint
!= AV_NOPTS_VALUE
) {
648 if (file
->inpoint
> file
->outpoint
||
649 file
->outpoint
- (uint64_t)file
->inpoint
> INT64_MAX
)
650 ret
= AVERROR_INVALIDDATA
;
654 for (arg
= 0; arg
< MAX_ARGS
; arg
++)
655 av_freep(&arg_str
[arg
]);
656 av_bprint_finalize(&bp
, NULL
);
657 return ret
== AVERROR_EOF
? 0 : ret
;
660 static int concat_read_header(AVFormatContext
*avf
)
662 ConcatContext
*cat
= avf
->priv_data
;
667 ret
= concat_parse_script(avf
);
670 if (!cat
->nb_files
) {
671 av_log(avf
, AV_LOG_ERROR
, "No files to concat\n");
672 return AVERROR_INVALIDDATA
;
675 for (i
= 0; i
< cat
->nb_files
; i
++) {
676 if (cat
->files
[i
].start_time
== AV_NOPTS_VALUE
)
677 cat
->files
[i
].start_time
= time
;
679 time
= cat
->files
[i
].start_time
;
680 if (cat
->files
[i
].user_duration
== AV_NOPTS_VALUE
) {
681 if (cat
->files
[i
].inpoint
== AV_NOPTS_VALUE
|| cat
->files
[i
].outpoint
== AV_NOPTS_VALUE
||
682 cat
->files
[i
].outpoint
- (uint64_t)cat
->files
[i
].inpoint
!= av_sat_sub64(cat
->files
[i
].outpoint
, cat
->files
[i
].inpoint
)
685 cat
->files
[i
].user_duration
= cat
->files
[i
].outpoint
- cat
->files
[i
].inpoint
;
687 cat
->files
[i
].duration
= cat
->files
[i
].user_duration
;
688 if (time
+ (uint64_t)cat
->files
[i
].user_duration
> INT64_MAX
)
689 return AVERROR_INVALIDDATA
;
690 time
+= cat
->files
[i
].user_duration
;
692 if (i
== cat
->nb_files
) {
693 avf
->duration
= time
;
697 cat
->stream_match_mode
= avf
->nb_streams
? MATCH_EXACT_ID
:
699 if ((ret
= open_file(avf
, 0)) < 0)
705 static int open_next_file(AVFormatContext
*avf
)
707 ConcatContext
*cat
= avf
->priv_data
;
708 unsigned fileno
= cat
->cur_file
- cat
->files
;
710 cat
->cur_file
->duration
= get_best_effort_duration(cat
->cur_file
, cat
->avf
);
712 if (++fileno
>= cat
->nb_files
) {
716 return open_file(avf
, fileno
);
719 static int filter_packet(AVFormatContext
*avf
, ConcatStream
*cs
, AVPacket
*pkt
)
724 ret
= av_bsf_send_packet(cs
->bsf
, pkt
);
726 av_log(avf
, AV_LOG_ERROR
, "h264_mp4toannexb filter "
727 "failed to send input packet\n");
732 ret
= av_bsf_receive_packet(cs
->bsf
, pkt
);
734 if (ret
< 0 && (ret
!= AVERROR(EAGAIN
) && ret
!= AVERROR_EOF
)) {
735 av_log(avf
, AV_LOG_ERROR
, "h264_mp4toannexb filter "
736 "failed to receive output packet\n");
743 /* Returns true if the packet dts is greater or equal to the specified outpoint. */
744 static int packet_after_outpoint(ConcatContext
*cat
, AVPacket
*pkt
)
746 if (cat
->cur_file
->outpoint
!= AV_NOPTS_VALUE
&& pkt
->dts
!= AV_NOPTS_VALUE
) {
747 return av_compare_ts(pkt
->dts
, cat
->avf
->streams
[pkt
->stream_index
]->time_base
,
748 cat
->cur_file
->outpoint
, AV_TIME_BASE_Q
) >= 0;
753 static int concat_read_packet(AVFormatContext
*avf
, AVPacket
*pkt
)
755 ConcatContext
*cat
= avf
->priv_data
;
769 ret
= av_read_frame(cat
->avf
, pkt
);
770 if (ret
== AVERROR_EOF
) {
771 if ((ret
= open_next_file(avf
)) < 0)
777 if ((ret
= match_streams(avf
)) < 0) {
780 if (packet_after_outpoint(cat
, pkt
)) {
781 av_packet_unref(pkt
);
782 if ((ret
= open_next_file(avf
)) < 0)
786 cs
= &cat
->cur_file
->streams
[pkt
->stream_index
];
787 if (cs
->out_stream_index
< 0) {
788 av_packet_unref(pkt
);
793 if ((ret
= filter_packet(avf
, cs
, pkt
)) < 0)
796 st
= cat
->avf
->streams
[pkt
->stream_index
];
798 av_log(avf
, AV_LOG_DEBUG
, "file:%d stream:%d pts:%s pts_time:%s dts:%s dts_time:%s",
799 (unsigned)(cat
->cur_file
- cat
->files
), pkt
->stream_index
,
800 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
801 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
803 delta
= av_rescale_q(cat
->cur_file
->start_time
- cat
->cur_file
->file_inpoint
,
805 cat
->avf
->streams
[pkt
->stream_index
]->time_base
);
806 if (pkt
->pts
!= AV_NOPTS_VALUE
)
808 if (pkt
->dts
!= AV_NOPTS_VALUE
)
810 av_log(avf
, AV_LOG_DEBUG
, " -> pts:%s pts_time:%s dts:%s dts_time:%s\n",
811 av_ts2str(pkt
->pts
), av_ts2timestr(pkt
->pts
, &st
->time_base
),
812 av_ts2str(pkt
->dts
), av_ts2timestr(pkt
->dts
, &st
->time_base
));
813 if (cat
->cur_file
->metadata
) {
815 char* packed_metadata
= av_packet_pack_dictionary(cat
->cur_file
->metadata
, &metadata_len
);
816 if (!packed_metadata
)
817 return AVERROR(ENOMEM
);
818 ret
= av_packet_add_side_data(pkt
, AV_PKT_DATA_STRINGS_METADATA
,
819 packed_metadata
, metadata_len
);
821 av_freep(&packed_metadata
);
826 if (cat
->cur_file
->duration
== AV_NOPTS_VALUE
&& sti
->cur_dts
!= AV_NOPTS_VALUE
) {
827 int64_t next_dts
= av_rescale_q(sti
->cur_dts
, st
->time_base
, AV_TIME_BASE_Q
);
828 if (cat
->cur_file
->next_dts
== AV_NOPTS_VALUE
|| next_dts
> cat
->cur_file
->next_dts
) {
829 cat
->cur_file
->next_dts
= next_dts
;
833 pkt
->stream_index
= cs
->out_stream_index
;
837 static int try_seek(AVFormatContext
*avf
, int stream
,
838 int64_t min_ts
, int64_t ts
, int64_t max_ts
, int flags
)
840 ConcatContext
*cat
= avf
->priv_data
;
841 int64_t t0
= cat
->cur_file
->start_time
- cat
->cur_file
->file_inpoint
;
844 min_ts
= min_ts
== INT64_MIN
? INT64_MIN
: min_ts
- t0
;
845 max_ts
= max_ts
== INT64_MAX
? INT64_MAX
: max_ts
- t0
;
847 if (stream
>= cat
->avf
->nb_streams
)
849 ff_rescale_interval(AV_TIME_BASE_Q
, cat
->avf
->streams
[stream
]->time_base
,
850 &min_ts
, &ts
, &max_ts
);
852 return avformat_seek_file(cat
->avf
, stream
, min_ts
, ts
, max_ts
, flags
);
855 static int real_seek(AVFormatContext
*avf
, int stream
,
856 int64_t min_ts
, int64_t ts
, int64_t max_ts
, int flags
, AVFormatContext
*cur_avf
)
858 ConcatContext
*cat
= avf
->priv_data
;
859 int ret
, left
, right
;
862 if (stream
>= avf
->nb_streams
)
863 return AVERROR(EINVAL
);
864 ff_rescale_interval(avf
->streams
[stream
]->time_base
, AV_TIME_BASE_Q
,
865 &min_ts
, &ts
, &max_ts
);
869 right
= cat
->nb_files
;
871 /* Always support seek to start */
874 else if (!cat
->seekable
)
875 return AVERROR(ESPIPE
); /* XXX: can we use it? */
877 while (right
- left
> 1) {
878 int mid
= (left
+ right
) / 2;
879 if (ts
< cat
->files
[mid
].start_time
)
885 if (cat
->cur_file
!= &cat
->files
[left
]) {
886 if ((ret
= open_file(avf
, left
)) < 0)
892 ret
= try_seek(avf
, stream
, min_ts
, ts
, max_ts
, flags
);
894 left
< cat
->nb_files
- 1 &&
895 cat
->files
[left
+ 1].start_time
< max_ts
) {
896 if (cat
->cur_file
== &cat
->files
[left
])
898 if ((ret
= open_file(avf
, left
+ 1)) < 0)
900 ret
= try_seek(avf
, stream
, min_ts
, ts
, max_ts
, flags
);
905 static int concat_seek(AVFormatContext
*avf
, int stream
,
906 int64_t min_ts
, int64_t ts
, int64_t max_ts
, int flags
)
908 ConcatContext
*cat
= avf
->priv_data
;
909 ConcatFile
*cur_file_saved
= cat
->cur_file
;
910 AVFormatContext
*cur_avf_saved
= cat
->avf
;
913 if (flags
& (AVSEEK_FLAG_BYTE
| AVSEEK_FLAG_FRAME
))
914 return AVERROR(ENOSYS
);
916 if ((ret
= real_seek(avf
, stream
, min_ts
, ts
, max_ts
, flags
, cur_avf_saved
)) < 0) {
917 if (cat
->cur_file
!= cur_file_saved
) {
919 avformat_close_input(&cat
->avf
);
921 cat
->avf
= cur_avf_saved
;
922 cat
->cur_file
= cur_file_saved
;
924 if (cat
->cur_file
!= cur_file_saved
) {
925 avformat_close_input(&cur_avf_saved
);
932 #define OFFSET(x) offsetof(ConcatContext, x)
933 #define DEC AV_OPT_FLAG_DECODING_PARAM
935 static const AVOption options
[] = {
936 { "safe", "enable safe mode",
937 OFFSET(safe
), AV_OPT_TYPE_BOOL
, {.i64
= 1}, 0, 1, DEC
},
938 { "auto_convert", "automatically convert bitstream format",
939 OFFSET(auto_convert
), AV_OPT_TYPE_BOOL
, {.i64
= 1}, 0, 1, DEC
},
940 { "segment_time_metadata", "output file segment start time and duration as packet metadata",
941 OFFSET(segment_time_metadata
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, DEC
},
945 static const AVClass concat_class
= {
946 .class_name
= "concat demuxer",
947 .item_name
= av_default_item_name
,
949 .version
= LIBAVUTIL_VERSION_INT
,
953 const FFInputFormat ff_concat_demuxer
= {
955 .p
.long_name
= NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
956 .p
.priv_class
= &concat_class
,
957 .priv_data_size
= sizeof(ConcatContext
),
958 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
959 .read_probe
= concat_probe
,
960 .read_header
= concat_read_header
,
961 .read_packet
= concat_read_packet
,
962 .read_close
= concat_read_close
,
963 .read_seek2
= concat_seek
,