segment: support applehttp style list
authorLuca Barbato <lu_zero@gentoo.org>
Tue, 9 Oct 2012 00:49:42 +0000 (02:49 +0200)
committerLuca Barbato <lu_zero@gentoo.org>
Wed, 10 Oct 2012 16:56:55 +0000 (18:56 +0200)
libavformat/segment.c

index 8ac04e2..fd52835 100644 (file)
@@ -37,6 +37,7 @@ typedef struct {
     AVFormatContext *avf;
     char *format;          /**< Set by a private option. */
     char *list;            /**< Set by a private option. */
+    int  list_type;        /**< Set by a private option. */
     float time;            /**< Set by a private option. */
     int  size;             /**< Set by a private option. */
     int  wrap;             /**< Set by a private option. */
@@ -48,6 +49,11 @@ typedef struct {
     AVIOContext *pb;
 } SegmentContext;
 
+enum {
+    LIST_FLAT,
+    LIST_HLS
+};
+
 static int segment_mux_init(AVFormatContext *s)
 {
     SegmentContext *seg = s->priv_data;
@@ -72,6 +78,36 @@ static int segment_mux_init(AVFormatContext *s)
     return 0;
 }
 
+static int segment_hls_window(AVFormatContext *s, int last)
+{
+    SegmentContext *seg = s->priv_data;
+    int i, ret = 0;
+    char buf[1024];
+
+    if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
+                              &s->interrupt_callback, NULL)) < 0)
+        goto fail;
+
+    avio_printf(seg->pb, "#EXTM3U\n");
+    avio_printf(seg->pb, "#EXT-X-VERSION:3\n");
+    avio_printf(seg->pb, "#EXT-X-TARGETDURATION:%d\n", (int)seg->time);
+    avio_printf(seg->pb, "#EXT-X-MEDIA-SEQUENCE:%d\n",
+                FFMAX(0, seg->number - seg->size));
+
+    for (i = FFMAX(0, seg->number - seg->size);
+         i < seg->number; i++) {
+        avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time);
+        av_get_frame_filename(buf, sizeof(buf), s->filename, i);
+        avio_printf(seg->pb, "%s\n", buf);
+    }
+
+    if (last)
+        avio_printf(seg->pb, "#EXT-X-ENDLIST\n");
+fail:
+    avio_closep(&seg->pb);
+    return ret;
+}
+
 static int segment_start(AVFormatContext *s, int write_header)
 {
     SegmentContext *c = s->priv_data;
@@ -152,7 +188,7 @@ static int seg_write_header(AVFormatContext *s)
     if (!seg->write_header_trailer)
         seg->individual_header_trailer = 0;
 
-    if (seg->list)
+    if (seg->list && seg->list_type != LIST_HLS)
         if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
                               &s->interrupt_callback, NULL)) < 0)
             goto fail;
@@ -211,8 +247,13 @@ static int seg_write_header(AVFormatContext *s)
     }
 
     if (seg->list) {
-        avio_printf(seg->pb, "%s\n", oc->filename);
-        avio_flush(seg->pb);
+        if (seg->list_type == LIST_HLS) {
+            if ((ret = segment_hls_window(s, 0)) < 0)
+                goto fail;
+        } else {
+            avio_printf(seg->pb, "%s\n", oc->filename);
+            avio_flush(seg->pb);
+        }
     }
 
 fail:
@@ -252,13 +293,18 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
         oc = seg->avf;
 
         if (seg->list) {
-            avio_printf(seg->pb, "%s\n", oc->filename);
-            avio_flush(seg->pb);
-            if (seg->size && !(seg->number % seg->size)) {
-                avio_close(seg->pb);
-                if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
-                                      &s->interrupt_callback, NULL)) < 0)
+            if (seg->list_type == LIST_HLS) {
+                if ((ret = segment_hls_window(s, 0)) < 0)
                     goto fail;
+            } else {
+                avio_printf(seg->pb, "%s\n", oc->filename);
+                avio_flush(seg->pb);
+                if (seg->size && !(seg->number % seg->size)) {
+                    avio_closep(&seg->pb);
+                    if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
+                                          &s->interrupt_callback, NULL)) < 0)
+                        goto fail;
+                }
             }
         }
     }
@@ -281,15 +327,25 @@ static int seg_write_trailer(struct AVFormatContext *s)
     AVFormatContext *oc = seg->avf;
     int ret;
     if (!seg->write_header_trailer) {
-        ret = segment_end(oc, 0);
+        if ((ret = segment_end(oc, 0)) < 0)
+            goto fail;
         open_null_ctx(&oc->pb);
-        av_write_trailer(oc);
+        ret = av_write_trailer(oc);
         close_null_ctx(oc->pb);
     } else {
         ret = segment_end(oc, 1);
     }
-    if (seg->list)
-        avio_close(seg->pb);
+
+    if (ret < 0)
+        goto fail;
+
+    if (seg->list && seg->list_type == LIST_HLS) {
+        if ((ret = segment_hls_window(s, 1) < 0))
+            goto fail;
+    }
+
+fail:
+    avio_close(seg->pb);
     avformat_free_context(oc);
     return ret;
 }
@@ -301,6 +357,9 @@ static const AVOption options[] = {
     { "segment_time",      "segment length in seconds",               OFFSET(time),    AV_OPT_TYPE_FLOAT,  {.dbl = 2},     0, FLT_MAX, E },
     { "segment_list",      "output the segment list",                 OFFSET(list),    AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,       E },
     { "segment_list_size", "maximum number of playlist entries",      OFFSET(size),    AV_OPT_TYPE_INT,    {.i64 = 5},     0, INT_MAX, E },
+    { "segment_list_type", "segment list format",                     OFFSET(list_type),    AV_OPT_TYPE_INT,    {.i64 = LIST_FLAT},     0, 2, E, "list_type" },
+    {   "flat",            "plain list (default)",                    0,               AV_OPT_TYPE_CONST,  {.i64 = LIST_FLAT}, 0, 0, E, "list_type" },
+    {   "hls",             "Apple HTTP Live Streaming compatible",    0,               AV_OPT_TYPE_CONST,  {.i64 = LIST_HLS},  0, 0, E, "list_type" },
     { "segment_wrap",      "number after which the index wraps",      OFFSET(wrap),    AV_OPT_TYPE_INT,    {.i64 = 0},     0, INT_MAX, E },
     { "individual_header_trailer", "write header/trailer to each segment", OFFSET(individual_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
     { "write_header_trailer", "write a header to the first segment and a trailer to the last one", OFFSET(write_header_trailer), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },