diff --git a/doc/demuxers.texi b/doc/demuxers.texi index f76829adfe..81ab399cdc 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -320,6 +320,10 @@ Default value is 1000. @item http_persistent Use persistent HTTP connections. Applicable only for HTTP streams. Enabled by default. + +@item http_multiple +Use multiple HTTP connections for downloading HTTP segments. +Enabled by default. @end table @section image2 diff --git a/libavformat/hls.c b/libavformat/hls.c index e1846ad773..26bf61c805 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -96,6 +96,8 @@ struct playlist { uint8_t* read_buffer; AVIOContext *input; int input_read_done; + AVIOContext *input_next; + int input_next_requested; AVFormatContext *parent; int index; AVFormatContext *ctx; @@ -209,6 +211,7 @@ typedef struct HLSContext { char *allowed_extensions; int max_reload; int http_persistent; + int http_multiple; AVIOContext *playlist_pb; } HLSContext; @@ -261,6 +264,9 @@ static void free_playlist_list(HLSContext *c) if (pls->input) ff_format_io_close(c->ctx, &pls->input); pls->input_read_done = 0; + if (pls->input_next) + ff_format_io_close(c->ctx, &pls->input_next); + pls->input_next_requested = 0; if (pls->ctx) { pls->ctx->pb = NULL; avformat_close_input(&pls->ctx); @@ -924,6 +930,14 @@ static struct segment *current_segment(struct playlist *pls) return pls->segments[pls->cur_seq_no - pls->start_seq_no]; } +static struct segment *next_segment(struct playlist *pls) +{ + int n = pls->cur_seq_no - pls->start_seq_no + 1; + if (n >= pls->n_segments) + return NULL; + return pls->segments[n]; +} + enum ReadFromURLMode { READ_NORMAL, READ_COMPLETE, @@ -1365,6 +1379,7 @@ static int read_data(void *opaque, uint8_t *buf, int buf_size) int ret; int just_opened = 0; int reload_count = 0; + struct segment *seg; restart: if (!v->needed) @@ -1372,7 +1387,6 @@ restart: if (!v->input || (c->http_persistent && v->input_read_done)) { int64_t reload_interval; - struct segment *seg; /* Check that the playlist is still needed before opening a new * segment. */ @@ -1430,11 +1444,18 @@ reload: if (ret) return ret; - ret = open_input(c, v, seg, &v->input); + if (c->http_multiple && av_strstart(seg->url, "http", NULL) && v->input_next_requested) { + FFSWAP(AVIOContext *, v->input, v->input_next); + v->input_next_requested = 0; + ret = 0; + } else { + ret = open_input(c, v, seg, &v->input); + } if (ret < 0) { if (ff_check_interrupt(c->interrupt_callback)) return AVERROR_EXIT; - av_log(v->parent, AV_LOG_WARNING, "Failed to open segment of playlist %d\n", + av_log(v->parent, AV_LOG_WARNING, "Failed to open segment %d of playlist %d\n", + v->cur_seq_no, v->index); v->cur_seq_no += 1; goto reload; @@ -1442,6 +1463,20 @@ reload: just_opened = 1; } + seg = next_segment(v); + if (c->http_multiple && !v->input_next_requested && seg) { + ret = open_input(c, v, seg, &v->input_next); + if (ret < 0) { + if (ff_check_interrupt(c->interrupt_callback)) + return AVERROR_EXIT; + av_log(v->parent, AV_LOG_WARNING, "Failed to open segment %d of playlist %d\n", + v->cur_seq_no + 1, + v->index); + } else { + v->input_next_requested = 1; + } + } + if (v->init_sec_buf_read_offset < v->init_sec_data_len) { /* Push init section out first before first actual segment */ int copy_size = FFMIN(v->init_sec_data_len - v->init_sec_buf_read_offset, buf_size); @@ -1964,6 +1999,9 @@ static int recheck_discard_flags(AVFormatContext *s, int first) if (pls->input) ff_format_io_close(pls->parent, &pls->input); pls->input_read_done = 0; + if (pls->input_next) + ff_format_io_close(pls->parent, &pls->input_next); + pls->input_next_requested = 0; pls->needed = 0; changed = 1; av_log(s, AV_LOG_INFO, "No longer receiving playlist %d\n", i); @@ -2199,6 +2237,9 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, if (pls->input) ff_format_io_close(pls->parent, &pls->input); pls->input_read_done = 0; + if (pls->input_next) + ff_format_io_close(pls->parent, &pls->input_next); + pls->input_next_requested = 0; av_packet_unref(&pls->pkt); reset_packet(&pls->pkt); pls->pb.eof_reached = 0; @@ -2255,6 +2296,8 @@ static const AVOption hls_options[] = { OFFSET(max_reload), AV_OPT_TYPE_INT, {.i64 = 1000}, 0, INT_MAX, FLAGS}, {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, + {"http_multiple", "Use multiple HTTP connections for fetching segments", + OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS}, {NULL} };