aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBodecs Bela <bodecsb@vivanet.hu>2017-01-12 20:43:10 +0800
committerSteven Liu <lq@chinaffmpeg.org>2017-01-12 20:43:10 +0800
commit8811d6d9a07f9d1b8d22397c0e009d075d67fda0 (patch)
treec2e63311e7365a4a711c1dc9ae8253a70fd0b843
parente1f49712099d3b68c35cf8ebb95cc7f34b06ee55 (diff)
downloadffmpeg-8811d6d9a07f9d1b8d22397c0e009d075d67fda0.tar.gz
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>
-rw-r--r--doc/muxers.texi29
-rw-r--r--libavformat/hlsenc.c54
2 files changed, 70 insertions, 13 deletions
diff --git a/doc/muxers.texi b/doc/muxers.texi
index 351cd8c5c4..26a8f2dbc3 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -416,9 +416,34 @@ This option is useful to avoid to fill the disk with many segment
files, and limits the maximum number of segment files written to disk
to @var{wrap}.
+
+@item hls_start_number_source
+Start the playlist sequence number (@code{#EXT-X-MEDIA-SEQUENCE}) according to the specified source.
+Unless @code{hls_flags single_file} is set, it also specifies source of starting sequence numbers of
+segment and subtitle filenames. In any case, if @code{hls_flags append_list}
+is set and read playlist sequence number is greater than the specified start sequence number,
+then that value will be used as start value.
+
+It accepts the following values:
+
+@table @option
+
+@item generic (default)
+Set the starting sequence numbers according to @var{start_number} option value.
+
+@item epoch
+The start number will be the seconds since epoch (1970-01-01 00:00:00)
+
+@item datetime
+The start number will be based on the current date/time as YYYYmmddHHMMSS. e.g. 20161231235759.
+
+@end table
+
@item start_number @var{number}
-Start the playlist sequence number from @var{number}. Default value is
-0.
+Start the playlist sequence number (@code{#EXT-X-MEDIA-SEQUENCE}) from the specified @var{number}
+when @var{hls_start_number_source} value is @var{generic}. (This is the default case.)
+Unless @code{hls_flags single_file} is set, it also specifies starting sequence numbers of segment and subtitle filenames.
+Default value is 0.
@item hls_allow_cache @var{allowcache}
Explicitly set whether the client MAY (1) or MUST NOT (0) cache media segments.
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index a2c606cb6e..3bb15d3f92 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -39,6 +39,12 @@
#include "internal.h"
#include "os_support.h"
+typedef enum {
+ HLS_START_SEQUENCE_AS_START_NUMBER = 0,
+ HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH = 1,
+ HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // YYYYMMDDhhmmss
+} StartSequenceSourceType;
+
#define KEYSIZE 16
#define LINE_BUFFER_SIZE 1024
@@ -83,6 +89,7 @@ typedef struct HLSContext {
unsigned number;
int64_t sequence;
int64_t start_sequence;
+ uint32_t start_sequence_source_type; // enum StartSequenceSourceType
AVOutputFormat *oformat;
AVOutputFormat *vtt_oformat;
@@ -594,7 +601,16 @@ static int parse_playlist(AVFormatContext *s, const char *url)
while (!avio_feof(in)) {
read_chomp_line(in, line, sizeof(line));
if (av_strstart(line, "#EXT-X-MEDIA-SEQUENCE:", &ptr)) {
- hls->sequence = atoi(ptr);
+ int64_t tmp_sequence = strtoll(ptr, NULL, 10);
+ if (tmp_sequence < hls->sequence)
+ av_log(hls, AV_LOG_VERBOSE,
+ "Found playlist sequence number was smaller """
+ "than specified start sequence number: %"PRId64" < %"PRId64", "
+ "omitting\n", tmp_sequence, hls->start_sequence);
+ else {
+ av_log(hls, AV_LOG_DEBUG, "Found playlist sequence number: %"PRId64"\n", tmp_sequence);
+ hls->sequence = tmp_sequence;
+ }
} else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) {
is_segment = 1;
hls->discontinuity = 1;
@@ -805,9 +821,8 @@ static int hls_start(AVFormatContext *s)
av_strlcpy(vtt_oc->filename, c->vtt_basename,
sizeof(vtt_oc->filename));
} else if (c->max_seg_size > 0) {
- if (av_get_frame_filename2(oc->filename, sizeof(oc->filename),
- c->basename, c->wrap ? c->sequence % c->wrap : c->sequence,
- AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
+ if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+ c->basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -use_localtime 1 with it\n", c->basename);
return AVERROR(EINVAL);
}
@@ -882,16 +897,14 @@ static int hls_start(AVFormatContext *s)
}
av_free(fn_copy);
}
- } else if (av_get_frame_filename2(oc->filename, sizeof(oc->filename),
- c->basename, c->wrap ? c->sequence % c->wrap : c->sequence,
- AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
+ } else if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+ c->basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -use_localtime 1 with it\n", c->basename);
return AVERROR(EINVAL);
}
if( c->vtt_basename) {
- if (av_get_frame_filename2(vtt_oc->filename, sizeof(vtt_oc->filename),
- c->vtt_basename, c->wrap ? c->sequence % c->wrap : c->sequence,
- AV_FRAME_FILENAME_FLAGS_MULTIPLE) < 0) {
+ if (replace_int_data_in_filename(vtt_oc->filename, sizeof(vtt_oc->filename),
+ c->vtt_basename, 'd', c->wrap ? c->sequence % c->wrap : c->sequence) < 1) {
av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", c->vtt_basename);
return AVERROR(EINVAL);
}
@@ -979,6 +992,22 @@ static int hls_write_header(AVFormatContext *s)
int basename_size;
int vtt_basename_size;
+ if (hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_SECONDS_SINCE_EPOCH || hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_FORMATTED_DATETIME) {
+ time_t t = time(NULL); // we will need it in either case
+ if (hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_SECONDS_SINCE_EPOCH) {
+ hls->start_sequence = (int64_t)t;
+ } else if (hls->start_sequence_source_type == HLS_START_SEQUNCE_AS_FORMATTED_DATETIME) {
+ char b[15];
+ struct tm *p, tmbuf;
+ if (!(p = localtime_r(&t, &tmbuf)))
+ return AVERROR(ENOMEM);
+ if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
+ return AVERROR(ENOMEM);
+ hls->start_sequence = strtoll(b, NULL, 10);
+ }
+ av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
+ }
+
hls->sequence = hls->start_sequence;
hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
hls->start_pts = AV_NOPTS_VALUE;
@@ -1366,7 +1395,10 @@ static const AVOption options[] = {
{"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, "pl_type" },
{"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, "pl_type" },
{"method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
-
+ {"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" },
+ {"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" },
+ {"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" },
+ {"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" },
{ NULL },
};