Browse code

avformat/hlsenc: hls_start_number_source and start_number

start_number option starts the playlist sequence number
(#EXT-X-MEDIA-SEQUENCE) from the specified number. Unless hls_flags
single_file is set, it also specifies starting sequence numbers of
segment and subtitle filenames. Sometimes it is usefull to have unique
starting numbers at each run, but currently it is only achiveable by
setting this parameter manually.
This patch enables to specify start_number source parameter by
introducing hls_start_number_source with 3 possible values:
generic/epoch/datetime. This ensures to set start sequence number
automatically for practically unique numbers. Generic option is the
default and this is the curent behaviour: start_number option value
specifies the start sequence number. (start_number default value is 0)
If hls_start_number_source is set to epoch, then the start number will
be the seconds since epoch (1970-01-01 00:00:00). If set to datetime,
then the start sequence number will be based on the current date/time
value as YYYYmmddHHMMSS. e.g. 20161231235659.
Hls speficication allows 64 bit integers as sequence numbers. This patch
also changes some code where only 32 bit integer values were handled
correctly.

Reviewed-by: Moritz Barsnick <barsnick@gmx.net>
Signed-off-by: Bela Bodecs <bodecsb@vivanet.hu>
Signed-off-by: Steven Liu <lq@chinaffmpeg.org>

Bodecs Bela authored on 2017/01/12 21:43:10
Showing 2 changed files
... ...
@@ -416,9 +416,34 @@ This option is useful to avoid to fill the disk with many segment
416 416
 files, and limits the maximum number of segment files written to disk
417 417
 to @var{wrap}.
418 418
 
419
+
420
+@item hls_start_number_source
421
+Start the playlist sequence number (@code{#EXT-X-MEDIA-SEQUENCE}) according to the specified source.
422
+Unless @code{hls_flags single_file} is set, it also specifies source of starting sequence numbers of
423
+segment and subtitle filenames. In any case, if @code{hls_flags append_list}
424
+is set and read playlist sequence number is greater than the specified start sequence number,
425
+then that value will be used as start value.
426
+
427
+It accepts the following values:
428
+
429
+@table @option
430
+
431
+@item generic (default)
432
+Set the starting sequence numbers according to @var{start_number} option value.
433
+
434
+@item epoch
435
+The start number will be the seconds since epoch (1970-01-01 00:00:00)
436
+
437
+@item datetime
438
+The start number will be based on the current date/time as YYYYmmddHHMMSS. e.g. 20161231235759.
439
+
440
+@end table
441
+
419 442
 @item start_number @var{number}
420
-Start the playlist sequence number from @var{number}. Default value is
421
-0.
443
+Start the playlist sequence number (@code{#EXT-X-MEDIA-SEQUENCE}) from the specified @var{number}
444
+when @var{hls_start_number_source} value is @var{generic}. (This is the default case.)
445
+Unless @code{hls_flags single_file} is set, it also specifies starting sequence numbers of segment and subtitle filenames.
446
+Default value is 0.
422 447
 
423 448
 @item hls_allow_cache @var{allowcache}
424 449
 Explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments.
... ...
@@ -39,6 +39,12 @@
39 39
 #include "internal.h"
40 40
 #include "os_support.h"
41 41
 
42
+typedef enum {
43
+  HLS_START_SEQUENCE_AS_START_NUMBER = 0,
44
+  HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH = 1,
45
+  HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2,  // YYYYMMDDhhmmss
46
+} StartSequenceSourceType;
47
+
42 48
 #define KEYSIZE 16
43 49
 #define LINE_BUFFER_SIZE 1024
44 50
 
... ...
@@ -83,6 +89,7 @@ typedef struct HLSContext {
83 83
     unsigned number;
84 84
     int64_t sequence;
85 85
     int64_t start_sequence;
86
+    uint32_t start_sequence_source_type;  // enum StartSequenceSourceType
86 87
     AVOutputFormat *oformat;
87 88
     AVOutputFormat *vtt_oformat;
88 89
 
... ...
@@ -594,7 +601,16 @@ static int parse_playlist(AVFormatContext *s, const char *url)
594 594
     while (!avio_feof(in)) {
595 595
         read_chomp_line(in, line, sizeof(line));
596 596
         if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
597
-            hls->sequence = atoi(ptr);
597
+            int64_t tmp_sequence = strtoll(ptr, NULL, 10);
598
+            if (tmp_sequence < hls->sequence)
599
+              av_log(hls, AV_LOG_VERBOSE,
600
+                     "Found playlist sequence number was smaller """
601
+                     "than specified start sequence number: %"PRId64" < %"PRId64", "
602
+                     "omitting\n", tmp_sequence, hls->start_sequence);
603
+            else {
604
+              av_log(hls, AV_LOG_DEBUG, "Found playlist sequence number: %"PRId64"\n", tmp_sequence);
605
+              hls->sequence = tmp_sequence;
606
+            }
598 607
         } else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) {
599 608
             is_segment = 1;
600 609
             hls->discontinuity = 1;
... ...
@@ -805,9 +821,8 @@ static int hls_start(AVFormatContext *s)
805 805
             av_strlcpy(vtt_oc->filename, c->vtt_basename,
806 806
                   sizeof(vtt_oc->filename));
807 807
     } else if (c->max_seg_size > 0) {
808
-        if (av_get_frame_filename2(oc->filename, sizeof(oc->filename),
809
-            c->basename, c->wrap ? c->sequence % c->wrap : c->sequence,
810
-            AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
808
+        if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
809
+            c->basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
811 810
                 av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -use_localtime 1 with it\n", c->basename);
812 811
                 return AVERROR(EINVAL);
813 812
         }
... ...
@@ -882,16 +897,14 @@ static int hls_start(AVFormatContext *s)
882 882
                 }
883 883
                 av_free(fn_copy);
884 884
             }
885
-        } else if (av_get_frame_filename2(oc->filename, sizeof(oc->filename),
886
-                                  c->basename, c->wrap ? c->sequence % c->wrap : c->sequence,
887
-                                  AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
885
+        } else if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
886
+                   c->basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
888 887
             av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -use_localtime 1 with it\n", c->basename);
889 888
             return AVERROR(EINVAL);
890 889
         }
891 890
         if( c->vtt_basename) {
892
-            if (av_get_frame_filename2(vtt_oc->filename, sizeof(vtt_oc->filename),
893
-                              c->vtt_basename, c->wrap ? c->sequence % c->wrap : c->sequence,
894
-                              AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
891
+            if (replace_int_data_in_filename(vtt_oc->filename, sizeof(vtt_oc->filename),
892
+                c->vtt_basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
895 893
                 av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->vtt_basename);
896 894
                 return AVERROR(EINVAL);
897 895
             }
... ...
@@ -979,6 +992,22 @@ static int hls_write_header(AVFormatContext *s)
979 979
     int basename_size;
980 980
     int vtt_basename_size;
981 981
 
982
+    if (hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_SECONDS_SINCE_EPOCH || hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_FORMATTED_DATETIME) {
983
+        time_t t = time(NULL); // we will need it in either case
984
+        if (hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_SECONDS_SINCE_EPOCH) {
985
+            hls->start_sequence = (int64_t)t;
986
+        } else if (hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_FORMATTED_DATETIME) {
987
+            char b[15];
988
+            struct tm *p, tmbuf;
989
+            if (!(p = localtime_r(&t, &tmbuf)))
990
+                return AVERROR(ENOMEM);
991
+            if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
992
+                return AVERROR(ENOMEM);
993
+            hls->start_sequence = strtoll(b, NULL, 10);
994
+        }
995
+        av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
996
+    }
997
+
982 998
     hls->sequence       = hls->start_sequence;
983 999
     hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
984 1000
     hls->start_pts      = AV_NOPTS_VALUE;
... ...
@@ -1366,7 +1395,10 @@ static const AVOption options[] = {
1366 1366
     {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, "pl_type" },
1367 1367
     {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, "pl_type" },
1368 1368
     {"method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
1369
-
1369
+    {"hls_start_number_source", "set source of first number in sequence", OFFSET(start_sequence_source_type), AV_OPT_TYPE_INT, {.i64 = HLS_START_SEQUNCE_AS_START_NUMBER }, 0, HLS_START_SEQUNCE_AS_FORMATTED_DATETIME, E, "start_sequence_source_type" },
1370
+    {"generic", "start_number value (default)", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUNCE_AS_START_NUMBER }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
1371
+    {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUNCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
1372
+    {"datetime", "current datetime as YYYYMMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUNCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
1370 1373
     { NULL },
1371 1374
 };
1372 1375