From ea40b56bd44ed04521b299eff5beee98cabada44 Mon Sep 17 00:00:00 2001
From: Stefano Sabatini <stefasab@gmail.com>
Date: Sat, 1 Sep 2012 18:01:51 +0200
Subject: [PATCH] lavf/segment: add segment_list_flags option
Allow to specify options affecting the segment list generation.
In particular: add +live and +cache flags.
For a full discussion read trac ticket #1642:
http://ffmpeg.org/trac/ffmpeg/ticket/1642
Also add live M3U8 generation example.
TODO: bump micro
---
doc/muxers.texi | 26 ++++++++++++++++++++++++++
libavformat/segment.c | 26 ++++++++++++++++++++++++--
2 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/doc/muxers.texi b/doc/muxers.texi
index 24ffc7c..83c21db 100644
|
a
|
b
|
extension.
|
| 479 | 479 | @item segment_list @var{name} |
| 480 | 480 | Generate also a listfile named @var{name}. If not specified no |
| 481 | 481 | listfile is generated. |
| | 482 | @item segment_list_flags @var{flags} |
| | 483 | Set flags affecting the segment list generation. |
| | 484 | |
| | 485 | It currently supports the following flags: |
| | 486 | @table @var |
| | 487 | @item cache |
| | 488 | Allow caching (only affects M3U8 list files). |
| | 489 | |
| | 490 | @item live |
| | 491 | Allow live-friendly file generation. |
| | 492 | |
| | 493 | This currently only affects M3U8 lists. In particular, write a fake |
| | 494 | EXT-X-TARGETDURATION duration field at the top of the file, based on |
| | 495 | the specified @var{segment_time}. |
| | 496 | @end table |
| | 497 | |
| | 498 | Default value is @code{cache}. |
| | 499 | |
| 482 | 500 | @item segment_list_size @var{size} |
| 483 | 501 | Overwrite the listfile once it reaches @var{size} entries. If 0 |
| 484 | 502 | the listfile is never overwritten. Default value is 0. |
| … |
… |
and @code{libfaac} encoders:
|
| 584 | 602 | @example |
| 585 | 603 | ffmpeg -i in.mkv -map 0 -codec:v libx264 -codec:a libfaac -f ssegment -segment_list out.list out%03d.ts |
| 586 | 604 | @end example |
| | 605 | |
| | 606 | @item |
| | 607 | Segment the input file, and create an M3U8 live playlist (can be used |
| | 608 | as live HLS source): |
| | 609 | @example |
| | 610 | ffmpeg -re -i in.mkv -codec copy -map 0 -f segment -segment_list playlist.m3u8 \ |
| | 611 | -segment_list_flags +live -segment_time 10 out%03d.mkv |
| | 612 | @end example |
| 587 | 613 | @end itemize |
| 588 | 614 | |
| 589 | 615 | @section mp3 |
diff --git a/libavformat/segment.c b/libavformat/segment.c
index 5ae15bd..dc12349 100644
|
a
|
b
|
typedef enum {
|
| 46 | 46 | |
| 47 | 47 | #define LIST_TYPE_EXT LIST_TYPE_CSV |
| 48 | 48 | |
| | 49 | #define SEGMENT_LIST_FLAG_CACHE 1 |
| | 50 | #define SEGMENT_LIST_FLAG_LIVE 2 |
| | 51 | |
| 49 | 52 | typedef struct { |
| 50 | 53 | const AVClass *class; /**< Class for private options. */ |
| 51 | 54 | int segment_idx; ///< index of the segment file to write, starting from 0 |
| … |
… |
typedef struct {
|
| 55 | 58 | char *format; ///< format to use for output segment files |
| 56 | 59 | char *list; ///< filename for the segment list file |
| 57 | 60 | int list_count; ///< list counter |
| | 61 | int list_flags; ///< flags affecting list generation |
| 58 | 62 | int list_size; ///< number of entries for the segment list file |
| 59 | 63 | double list_max_segment_time; ///< max segment time in the current list |
| 60 | 64 | ListType list_type; ///< set the list type |
| … |
… |
static int segment_list_open(AVFormatContext *s)
|
| 156 | 160 | avio_printf(seg->list_pb, "#EXTM3U\n"); |
| 157 | 161 | avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n"); |
| 158 | 162 | avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->list_count); |
| | 163 | avio_printf(seg->list_pb, "#EXT-X-ALLOWCACHE:%d\n", |
| | 164 | !!(seg->list_flags & SEGMENT_LIST_FLAG_CACHE)); |
| | 165 | if (seg->list_flags & SEGMENT_LIST_FLAG_LIVE) |
| | 166 | avio_printf(seg->list_pb, |
| | 167 | "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000); |
| 159 | 168 | } |
| 160 | 169 | |
| 161 | 170 | return ret; |
| … |
… |
static void segment_list_close(AVFormatContext *s)
|
| 166 | 175 | SegmentContext *seg = s->priv_data; |
| 167 | 176 | |
| 168 | 177 | if (seg->list_type == LIST_TYPE_M3U8) { |
| 169 | | avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", |
| 170 | | (int)ceil(seg->list_max_segment_time)); |
| | 178 | if (!(seg->list_flags & SEGMENT_LIST_FLAG_LIVE)) |
| | 179 | avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", |
| | 180 | (int)ceil(seg->list_max_segment_time)); |
| 171 | 181 | avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n"); |
| 172 | 182 | } |
| 173 | 183 | seg->list_count++; |
| … |
… |
static int seg_write_header(AVFormatContext *s)
|
| 284 | 294 | return AVERROR(EINVAL); |
| 285 | 295 | } |
| 286 | 296 | |
| | 297 | if ((seg->list_flags & SEGMENT_LIST_FLAG_LIVE) && seg->times_str) { |
| | 298 | av_log(s, AV_LOG_ERROR, |
| | 299 | "segment_flags +live and segment_times options are mutually exclusive:" |
| | 300 | "specify -segment_time if you want a live-friendly list\n"); |
| | 301 | return AVERROR(EINVAL); |
| | 302 | } |
| | 303 | |
| 287 | 304 | if (seg->times_str) { |
| 288 | 305 | if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0) |
| 289 | 306 | return ret; |
| … |
… |
static int seg_write_trailer(struct AVFormatContext *s)
|
| 450 | 467 | static const AVOption options[] = { |
| 451 | 468 | { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, |
| 452 | 469 | { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, |
| | 470 | |
| | 471 | { "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"}, |
| | 472 | { "cache", "allow list caching", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_LIST_FLAG_CACHE }, INT_MIN, INT_MAX, E, "list_flags"}, |
| | 473 | { "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"}, |
| | 474 | |
| 453 | 475 | { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, |
| 454 | 476 | { "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" }, |
| 455 | 477 | { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.i64=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" }, |