On Tue, 9 Oct 2012, Luca Barbato wrote:

---

Should be ok now, Martin, did I overlook something else?

libavformat/segment.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)

diff --git a/libavformat/segment.c b/libavformat/segment.c
index 8ac04e2..9a53333 100644
--- a/libavformat/segment.c
+++ b/libavformat/segment.c
@@ -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,34 @@ static int segment_mux_init(AVFormatContext *s)
    return 0;
}

+static void segment_hls_header(SegmentContext *seg)
+{
+    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));
+}

The extra newlines just look weird here to me.

+
+static void segment_hls_window(AVFormatContext *s)
+{
+    SegmentContext *seg = s->priv_data;
+    int i;
+    char buf[1024];
+
+    segment_hls_header(seg);
+    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);
+    }
+    avio_flush(seg->pb);
+}
+
static int segment_start(AVFormatContext *s, int write_header)
{
    SegmentContext *c = s->priv_data;
@@ -211,6 +245,10 @@ static int seg_write_header(AVFormatContext *s)
    }

    if (seg->list) {
+        if (seg->list_type == LIST_HLS) {
+            segment_hls_header(seg);
+            avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time);
+        }
        avio_printf(seg->pb, "%s\n", oc->filename);
        avio_flush(seg->pb);
    }
@@ -252,14 +290,18 @@ static int seg_write_packet(AVFormatContext *s, AVPacket 
*pkt)
        oc = seg->avf;

        if (seg->list) {
-            avio_printf(seg->pb, "%s\n", oc->filename);
+            if (seg->list_type != LIST_HLS)
+//                avio_printf(seg->pb, "#EXTINF:%d,\n", (int)seg->time);
+                avio_printf(seg->pb, "%s\n", oc->filename);
            avio_flush(seg->pb);
-            if (seg->size && !(seg->number % seg->size)) {
+            if (seg->list_type == LIST_HLS ||
+                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)
                    goto fail;
            }
+            segment_hls_window(s);

Hmm, I guess this works, although it keeps the file open for writing all the time (instead of just opening, writing and closing each time you want to update it).

        }
    }

@@ -301,6 +343,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 = 0},     0, 2, E, "list_type" },

This could use LIST_FLAT instead of 0 as default here

+    {   "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 },
--
1.7.12

You might want to write a list terminator to the file when the muxer is closed, to indicate to players that no more segments will be added (that is, the stream transitions from live to on-demand).

Other than that, this looks acceptable to me.

// Martin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to