From 74177c1919558e6ea8e0bc9adeca60fcd19c9ccd 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_live option, allow
live-friendly M3U8 list format generation
Allow to create M3U8 list which are HLS-friendly. In particular, write a
fake EXT-X-TARGETDURATION duration field based on the specified
segment_time.
For a full discussion read trac ticket #1492:
http://ffmpeg.org/trac/ffmpeg/ticket/1642
---
libavformat/segment.c | 17 +++++++++++++++--
1 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/libavformat/segment.c b/libavformat/segment.c
index c695761..b25ee48 100644
|
a
|
b
|
typedef struct {
|
| 55 | 55 | char *format; ///< format to use for output segment files |
| 56 | 56 | char *list; ///< filename for the segment list file |
| 57 | 57 | int list_count; ///< list counter |
| | 58 | int list_live; ///< force live-friendly list generation |
| 58 | 59 | int list_size; ///< number of entries for the segment list file |
| 59 | 60 | double list_max_segment_time; ///< max segment time in the current list |
| 60 | 61 | ListType list_type; ///< set the list type |
| … |
… |
static int segment_list_open(AVFormatContext *s)
|
| 154 | 155 | avio_printf(seg->list_pb, "#EXTM3U\n"); |
| 155 | 156 | avio_printf(seg->list_pb, "#EXT-X-VERSION:3\n"); |
| 156 | 157 | avio_printf(seg->list_pb, "#EXT-X-MEDIA-SEQUENCE:%d\n", seg->list_count); |
| | 158 | if (seg->list_live) |
| | 159 | avio_printf(seg->list_pb, |
| | 160 | "#EXT-X-TARGETDURATION:%"PRId64"\n", seg->time / 1000000); |
| 157 | 161 | } |
| 158 | 162 | |
| 159 | 163 | return ret; |
| … |
… |
static void segment_list_close(AVFormatContext *s)
|
| 164 | 168 | SegmentContext *seg = s->priv_data; |
| 165 | 169 | |
| 166 | 170 | if (seg->list_type == LIST_TYPE_M3U8) { |
| 167 | | avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", |
| 168 | | (int)ceil(seg->list_max_segment_time)); |
| | 171 | if (!seg->list_live) |
| | 172 | avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", |
| | 173 | (int)ceil(seg->list_max_segment_time)); |
| 169 | 174 | avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n"); |
| 170 | 175 | } |
| 171 | 176 | seg->list_count++; |
| … |
… |
static int seg_write_header(AVFormatContext *s)
|
| 282 | 287 | return AVERROR(EINVAL); |
| 283 | 288 | } |
| 284 | 289 | |
| | 290 | if (seg->list_live && seg->times_str) { |
| | 291 | av_log(s, AV_LOG_ERROR, |
| | 292 | "segment_live set to 1 and segment_times options are mutually exclusive:" |
| | 293 | "specify -segment_time if you want a live-friendly list\n"); |
| | 294 | return AVERROR(EINVAL); |
| | 295 | } |
| | 296 | |
| 285 | 297 | if (seg->times_str) { |
| 286 | 298 | if ((ret = parse_times(s, &seg->times, &seg->nb_times, seg->times_str)) < 0) |
| 287 | 299 | return ret; |
| … |
… |
static int seg_write_trailer(struct AVFormatContext *s)
|
| 448 | 460 | static const AVOption options[] = { |
| 449 | 461 | { "segment_format", "set container format used for the segments", OFFSET(format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, |
| 450 | 462 | { "segment_list", "set the segment list filename", OFFSET(list), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, |
| | 463 | { "segment_list_live", "enable live-friendly list generation (useful for HLS)", OFFSET(list_live), AV_OPT_TYPE_INT, {.dbl = 0}, 0, 1, E }, |
| 451 | 464 | { "segment_list_size", "set the maximum number of playlist entries", OFFSET(list_size), AV_OPT_TYPE_INT, {.dbl = 0}, 0, INT_MAX, E }, |
| 452 | 465 | { "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.dbl = LIST_TYPE_UNDEFINED}, -1, LIST_TYPE_NB-1, E, "list_type" }, |
| 453 | 466 | { "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" }, |