Address trac ticket #1642.
Stefano Sabatini authored on 2012/08/15 18:06:34... | ... |
@@ -502,6 +502,10 @@ muxer according to the provided pattern, and should not contain the |
502 | 502 |
|
503 | 503 |
@var{segment_start_time} and @var{segment_end_time} specify |
504 | 504 |
the segment start and end time expressed in seconds. |
505 |
+ |
|
506 |
+@item m3u8 |
|
507 |
+Generate an extended M3U8 file, version 4, compliant with |
|
508 |
+@url{http://tools.ietf.org/id/draft-pantos-http-live-streaming-08.txt}. |
|
505 | 509 |
@end table |
506 | 510 |
|
507 | 511 |
Default value is "flat". |
... | ... |
@@ -20,6 +20,8 @@ |
20 | 20 |
|
21 | 21 |
/** |
22 | 22 |
* @file generic segmenter |
23 |
+ * M3U8 specification can be find here: |
|
24 |
+ * @url{http://tools.ietf.org/id/draft-pantos-http-live-streaming-08.txt} |
|
23 | 25 |
*/ |
24 | 26 |
|
25 | 27 |
#include <float.h> |
... | ... |
@@ -37,6 +39,7 @@ |
37 | 37 |
typedef enum { |
38 | 38 |
LIST_TYPE_FLAT = 0, |
39 | 39 |
LIST_TYPE_EXT, |
40 |
+ LIST_TYPE_M3U8, |
|
40 | 41 |
LIST_TYPE_NB, |
41 | 42 |
} ListType; |
42 | 43 |
|
... | ... |
@@ -120,12 +123,25 @@ static int segment_list_open(AVFormatContext *s) |
120 | 120 |
if (ret < 0) |
121 | 121 |
return ret; |
122 | 122 |
seg->list_max_segment_time = 0; |
123 |
+ |
|
124 |
+ if (seg->list_type == LIST_TYPE_M3U8) { |
|
125 |
+ avio_printf(seg->list_pb, "#EXTM3U\n"); |
|
126 |
+ avio_printf(seg->list_pb, "#EXT-X-VERSION:4\n"); |
|
127 |
+ } |
|
128 |
+ |
|
123 | 129 |
return ret; |
124 | 130 |
} |
125 | 131 |
|
126 | 132 |
static void segment_list_close(AVFormatContext *s) |
127 | 133 |
{ |
128 | 134 |
SegmentContext *seg = s->priv_data; |
135 |
+ |
|
136 |
+ if (seg->list_type == LIST_TYPE_M3U8) { |
|
137 |
+ avio_printf(seg->list_pb, "#EXT-X-TARGETDURATION:%d\n", |
|
138 |
+ (int)(seg->list_max_segment_time + 0.5)); |
|
139 |
+ avio_printf(seg->list_pb, "#EXT-X-ENDLIST\n"); |
|
140 |
+ } |
|
141 |
+ |
|
129 | 142 |
avio_close(seg->list_pb); |
130 | 143 |
} |
131 | 144 |
|
... | ... |
@@ -153,6 +169,9 @@ static int segment_end(AVFormatContext *s) |
153 | 153 |
avio_printf(seg->list_pb, "%s\n", oc->filename); |
154 | 154 |
} else if (seg->list_type == LIST_TYPE_EXT) { |
155 | 155 |
avio_printf(seg->list_pb, "%s,%f,%f\n", oc->filename, seg->start_time, seg->end_time); |
156 |
+ } else if (seg->list_type == LIST_TYPE_M3U8) { |
|
157 |
+ avio_printf(seg->list_pb, "#EXTINF:%f,\n%s\n", |
|
158 |
+ seg->end_time - seg->start_time, oc->filename); |
|
156 | 159 |
} |
157 | 160 |
seg->list_max_segment_time = FFMAX(seg->end_time - seg->start_time, seg->list_max_segment_time); |
158 | 161 |
avio_flush(seg->list_pb); |
... | ... |
@@ -395,6 +414,7 @@ static const AVOption options[] = { |
395 | 395 |
{ "segment_list_type", "set the segment list type", OFFSET(list_type), AV_OPT_TYPE_INT, {.dbl = LIST_TYPE_FLAT}, 0, LIST_TYPE_NB-1, E, "list_type" }, |
396 | 396 |
{ "flat", "flat format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_FLAT }, INT_MIN, INT_MAX, 0, "list_type" }, |
397 | 397 |
{ "ext", "extended format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_EXT }, INT_MIN, INT_MAX, 0, "list_type" }, |
398 |
+ { "m3u8", "M3U8 format", 0, AV_OPT_TYPE_CONST, {.dbl=LIST_TYPE_M3U8 }, INT_MIN, INT_MAX, 0, "list_type" }, |
|
398 | 399 |
{ "segment_time", "set segment duration", OFFSET(time_str),AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, |
399 | 400 |
{ "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta_str), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, E }, |
400 | 401 |
{ "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, |
... | ... |
@@ -31,7 +31,7 @@ |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 54 |
33 | 33 |
#define LIBAVFORMAT_VERSION_MINOR 25 |
34 |
-#define LIBAVFORMAT_VERSION_MICRO 101 |
|
34 |
+#define LIBAVFORMAT_VERSION_MICRO 102 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFORMAT_VERSION_MINOR, \ |