Commit 26db9100 authored by Luca Barbato's avatar Luca Barbato

segment: support applehttp style list

parent b522000e
...@@ -37,6 +37,7 @@ typedef struct { ...@@ -37,6 +37,7 @@ typedef struct {
AVFormatContext *avf; AVFormatContext *avf;
char *format; /**< Set by a private option. */ char *format; /**< Set by a private option. */
char *list; /**< 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. */ float time; /**< Set by a private option. */
int size; /**< Set by a private option. */ int size; /**< Set by a private option. */
int wrap; /**< Set by a private option. */ int wrap; /**< Set by a private option. */
...@@ -48,6 +49,11 @@ typedef struct { ...@@ -48,6 +49,11 @@ typedef struct {
AVIOContext *pb; AVIOContext *pb;
} SegmentContext; } SegmentContext;
enum {
LIST_FLAT,
LIST_HLS
};
static int segment_mux_init(AVFormatContext *s) static int segment_mux_init(AVFormatContext *s)
{ {
SegmentContext *seg = s->priv_data; SegmentContext *seg = s->priv_data;
...@@ -72,6 +78,36 @@ static int segment_mux_init(AVFormatContext *s) ...@@ -72,6 +78,36 @@ static int segment_mux_init(AVFormatContext *s)
return 0; 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) static int segment_start(AVFormatContext *s, int write_header)
{ {
SegmentContext *c = s->priv_data; SegmentContext *c = s->priv_data;
...@@ -152,7 +188,7 @@ static int seg_write_header(AVFormatContext *s) ...@@ -152,7 +188,7 @@ static int seg_write_header(AVFormatContext *s)
if (!seg->write_header_trailer) if (!seg->write_header_trailer)
seg->individual_header_trailer = 0; 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, if ((ret = avio_open2(&seg->pb, seg->list, AVIO_FLAG_WRITE,
&s->interrupt_callback, NULL)) < 0) &s->interrupt_callback, NULL)) < 0)
goto fail; goto fail;
...@@ -211,8 +247,13 @@ static int seg_write_header(AVFormatContext *s) ...@@ -211,8 +247,13 @@ static int seg_write_header(AVFormatContext *s)
} }
if (seg->list) { if (seg->list) {
avio_printf(seg->pb, "%s\n", oc->filename); if (seg->list_type == LIST_HLS) {
avio_flush(seg->pb); if ((ret = segment_hls_window(s, 0)) < 0)
goto fail;
} else {
avio_printf(seg->pb, "%s\n", oc->filename);
avio_flush(seg->pb);
}
} }
fail: fail:
...@@ -252,13 +293,18 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -252,13 +293,18 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt)
oc = seg->avf; oc = seg->avf;
if (seg->list) { if (seg->list) {
avio_printf(seg->pb, "%s\n", oc->filename); if (seg->list_type == LIST_HLS) {
avio_flush(seg->pb); if ((ret = segment_hls_window(s, 0)) < 0)
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)
goto fail; 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) ...@@ -281,15 +327,25 @@ static int seg_write_trailer(struct AVFormatContext *s)
AVFormatContext *oc = seg->avf; AVFormatContext *oc = seg->avf;
int ret; int ret;
if (!seg->write_header_trailer) { if (!seg->write_header_trailer) {
ret = segment_end(oc, 0); if ((ret = segment_end(oc, 0)) < 0)
goto fail;
open_null_ctx(&oc->pb); open_null_ctx(&oc->pb);
av_write_trailer(oc); ret = av_write_trailer(oc);
close_null_ctx(oc->pb); close_null_ctx(oc->pb);
} else { } else {
ret = segment_end(oc, 1); 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); avformat_free_context(oc);
return ret; return ret;
} }
...@@ -301,6 +357,9 @@ static const AVOption options[] = { ...@@ -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_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", "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_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 }, { "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 }, { "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 }, { "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 },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment