For example you can split a file, keeping a continuous timecode between
each segment:
ffmpeg -i src.mov -timecode 10:00:00:00 -vcodec copy -f segment \
-segment_time 2 -reset_timestamps 1 -increment_tc 1 target_%03d.mov
Signed-off-by: Stefano Sabatini <stefasab@gmail.com>
... | ... |
@@ -1045,6 +1045,12 @@ implementation for HLS segmentation. |
1045 | 1045 |
The segment muxer supports the following options: |
1046 | 1046 |
|
1047 | 1047 |
@table @option |
1048 |
+@item increment_tc @var{1|0} |
|
1049 |
+if set to @code{1}, increment timecode between each segment |
|
1050 |
+If this is selected, the input need to have |
|
1051 |
+a timecode in the first video stream. Default value is |
|
1052 |
+@code{0}. |
|
1053 |
+ |
|
1048 | 1054 |
@item reference_stream @var{specifier} |
1049 | 1055 |
Set the reference stream, as specified by the string @var{specifier}. |
1050 | 1056 |
If @var{specifier} is set to @code{auto}, the reference is chosen |
... | ... |
@@ -41,6 +41,7 @@ |
41 | 41 |
#include "libavutil/parseutils.h" |
42 | 42 |
#include "libavutil/mathematics.h" |
43 | 43 |
#include "libavutil/time.h" |
44 |
+#include "libavutil/timecode.h" |
|
44 | 45 |
#include "libavutil/time_internal.h" |
45 | 46 |
#include "libavutil/timestamp.h" |
46 | 47 |
|
... | ... |
@@ -95,6 +96,7 @@ typedef struct SegmentContext { |
95 | 95 |
char *time_str; ///< segment duration specification string |
96 | 96 |
int64_t time; ///< segment duration |
97 | 97 |
int use_strftime; ///< flag to expand filename with strftime |
98 |
+ int increment_tc; ///< flag to increment timecode if found |
|
98 | 99 |
|
99 | 100 |
char *times_str; ///< segment times specification string |
100 | 101 |
int64_t *times; ///< list of segment interval specification |
... | ... |
@@ -337,6 +339,12 @@ static int segment_end(AVFormatContext *s, int write_trailer, int is_last) |
337 | 337 |
SegmentContext *seg = s->priv_data; |
338 | 338 |
AVFormatContext *oc = seg->avf; |
339 | 339 |
int ret = 0; |
340 |
+ AVTimecode tc; |
|
341 |
+ AVRational rate; |
|
342 |
+ AVDictionaryEntry *tcr; |
|
343 |
+ char buf[AV_TIMECODE_STR_SIZE]; |
|
344 |
+ int i; |
|
345 |
+ int err; |
|
340 | 346 |
|
341 | 347 |
av_write_frame(oc, NULL); /* Flush any buffered data (fragmented mp4) */ |
342 | 348 |
if (write_trailer) |
... | ... |
@@ -390,6 +398,29 @@ static int segment_end(AVFormatContext *s, int write_trailer, int is_last) |
390 | 390 |
seg->avf->filename, seg->segment_count); |
391 | 391 |
seg->segment_count++; |
392 | 392 |
|
393 |
+ if (seg->increment_tc) { |
|
394 |
+ tcr = av_dict_get(s->metadata, "timecode", NULL, 0); |
|
395 |
+ if (tcr) { |
|
396 |
+ /* search the first video stream */ |
|
397 |
+ for (i = 0; i < s->nb_streams; i++) { |
|
398 |
+ if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { |
|
399 |
+ rate = s->streams[i]->avg_frame_rate;/* Get fps from the video stream */ |
|
400 |
+ err = av_timecode_init_from_string(&tc, rate, tcr->value, s); |
|
401 |
+ if (err < 0) { |
|
402 |
+ av_log(s, AV_LOG_WARNING, "Could not increment timecode, error occured during timecode creation."); |
|
403 |
+ break; |
|
404 |
+ } |
|
405 |
+ tc.start += (int)((seg->cur_entry.end_time - seg->cur_entry.start_time) * av_q2d(rate));/* increment timecode */ |
|
406 |
+ av_dict_set(&s->metadata, "timecode", |
|
407 |
+ av_timecode_make_string(&tc, buf, 0), 0); |
|
408 |
+ break; |
|
409 |
+ } |
|
410 |
+ } |
|
411 |
+ } else { |
|
412 |
+ av_log(s, AV_LOG_WARNING, "Could not increment timecode, no timecode metadata found"); |
|
413 |
+ } |
|
414 |
+ } |
|
415 |
+ |
|
393 | 416 |
end: |
394 | 417 |
ff_format_io_close(oc, &oc->pb); |
395 | 418 |
|
... | ... |
@@ -948,6 +979,7 @@ static const AVOption options[] = { |
948 | 948 |
{ "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, |
949 | 949 |
{ "segment_wrap_number", "set the number of wrap before the first segment", OFFSET(segment_idx_wrap_nb), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, |
950 | 950 |
{ "strftime", "set filename expansion with strftime at segment creation", OFFSET(use_strftime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, |
951 |
+ { "increment_tc", "increment timecode between each segment", OFFSET(increment_tc), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, E }, |
|
951 | 952 |
{ "break_non_keyframes", "allow breaking segments on non-keyframes", OFFSET(break_non_keyframes), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E }, |
952 | 953 |
|
953 | 954 |
{ "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, E }, |
... | ... |
@@ -31,7 +31,7 @@ |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 57 |
33 | 33 |
#define LIBAVFORMAT_VERSION_MINOR 28 |
34 |
-#define LIBAVFORMAT_VERSION_MICRO 101 |
|
34 |
+#define LIBAVFORMAT_VERSION_MICRO 102 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFORMAT_VERSION_MINOR, \ |