diff --git a/doc/muxers.texi b/doc/muxers.texi index 24ffc7c8ee..83c21dba67 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -479,6 +479,24 @@ extension. @item segment_list @var{name} Generate also a listfile named @var{name}. If not specified no listfile is generated. +@item segment_list_flags @var{flags} +Set flags affecting the segment list generation. + +It currently supports the following flags: +@table @var +@item cache +Allow caching (only affects M3U8 list files). + +@item live +Allow live-friendly file generation. + +This currently only affects M3U8 lists. In particular, write a fake +EXT-X-TARGETDURATION duration field at the top of the file, based on +the specified @var{segment_time}. +@end table + +Default value is @code{cache}. + @item segment_list_size @var{size} Overwrite the listfile once it reaches @var{size} entries. If 0 the listfile is never overwritten. Default value is 0. @@ -584,6 +602,14 @@ and @code{libfaac} encoders: @example ffmpeg -i in.mkv -map 0 -codec:v libx264 -codec:a libfaac -f ssegment -segment_list out.list out%03d.ts @end example + +@item +Segment the input file, and create an M3U8 live playlist (can be used +as live HLS source): +@example +ffmpeg -re -i in.mkv -codec copy -map 0 -f segment -segment_list playlist.m3u8 \ +-segment_list_flags +live -segment_time 10 out%03d.mkv +@end example @end itemize @section mp3 diff --git a/libavformat/segment.c b/libavformat/segment.c index 5ae15bd1f0..dc123495da 100644 --- a/libavformat/segment.c +++ b/libavformat/segment.c @@ -46,6 +46,9 @@ typedef enum { #define LIST_TYPE_EXT LIST_TYPE_CSV +#define SEGMENT_LIST_FLAG_CACHE 1 +#define SEGMENT_LIST_FLAG_LIVE 2 + typedef struct { const AVClass *class; /**< Class for private options. */ int segment_idx; ///< index of the segment file to write, starting from 0 @@ -55,6 +58,7 @@ typedef struct { char *format; ///< format to use for output segment files char *list; ///< filename for the segment list file int list_count; ///< list counter + int list_flags; ///< flags affecting list generation int list_size; ///< number of entries for the segment list file double list_max_segment_time; ///< max segment time in the current list ListType list_type; ///< set the list type @@ -156,6 +160,11 @@ static int segment_list_open(AVFormatContext *s) avio_printf(seg->list_pb, "#EXTM3U\n"); avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n"); avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->list_count); + avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n", + !!(seg->list_flags & SEGMENT_LIST_FLAG_CACHE)); + if (seg->list_flags & SEGMENT_LIST_FLAG_LIVE) + avio_printf(seg->list_pb, + "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000); } return ret; @@ -166,8 +175,9 @@ static void segment_list_close(AVFormatContext *s) SegmentContext *seg = s->priv_data; if (seg->list_type == LIST_TYPE_M3U8) { - avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", - (int)ceil(seg->list_max_segment_time)); + if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE)) + avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", + (int)ceil(seg->list_max_segment_time)); avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n"); } seg->list_count++; @@ -284,6 +294,13 @@ static int seg_write_header(AVFormatContext *s) return AVERROR(EINVAL); } + if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && seg->times_str) { + av_log(s, AV_LOG_ERROR, + "segment_flags +live and segment_times options are mutually exclusive:" + "specify -segment_time if you want a live-friendly list\n"); + return AVERROR(EINVAL); + } + if (seg->times_str) { if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0) return ret; @@ -450,6 +467,11 @@ static int seg_write_trailer(struct AVFormatContext *s) static const AVOption options[] = { { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + + { "segment_list_flags","set flags affecting segment list generation", OFFSET(list_flags), AV_OPT_TYPE_FLAGS, {.i64 = SEGMENT_LIST_FLAG_CACHE }, 0, UINT_MAX, E, "list_flags"}, + { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"}, + { "live", "enable live-friendly list generation (useful for HLS)", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_LIVE }, INT_MIN, INT_MAX, E, "list_flags"}, + { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, { "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.i64 = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" }, { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" }, diff --git a/libavformat/version.h b/libavformat/version.h index df5315b0e2..306023fc4a 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 54 #define LIBAVFORMAT_VERSION_MINOR 26 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \