Browse code

lavf/segment: add increment_tc option

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>

Martin Vignali authored on 2016/02/29 05:36:42
Showing 3 changed files
... ...
@@ -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, \