From patchwork Wed Dec 13 00:35:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 6729 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.161.94 with SMTP id m30csp4768355jah; Tue, 12 Dec 2017 16:42:02 -0800 (PST) X-Google-Smtp-Source: ACJfBouDkyjF8BDgUwjG5AZycm1yk2SS+LlmG70Nvm7wG1POzDGB4JllpCbN8xq3LaXWlSVm19jz X-Received: by 10.28.10.5 with SMTP id 5mr380401wmk.74.1513125722416; Tue, 12 Dec 2017 16:42:02 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513125722; cv=none; d=google.com; s=arc-20160816; b=kXQkKfBBFvM6IFyeOyrc/SjYZPn1+x4DyXfN1+Yyw57c8qRbEjunS/OQvwtNnb3mIA igsVfMyFktgzrL63y8uuqe/2ZZYwfOHb01ztthniNP6GJM3T14HrxN0QW2kt+ZNn2smK ZTXUZsk6QlqjIVe7xtk9Kk6pdxvXrUqxtmKFhjSet+uiowx48MjluwWDdfq60lklqxlo SLa29cJEY4DMYE+P2XZ39UPVvFx+HkUSLt+Dr++KmXiq6huu1C8Lbsb//dVSExL41YTh F3i2z0/+NgNgmko/9LVIonxCrTGec+1SYmY66OSvZfd33SDxBQ6yGvu2VT+KB9QGoBB5 /Y4g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=uRdd1qNvis1Vmkbj2sT4k0X/9AsmY8raYS/ifWUiMiA=; b=nQzzejvC7w+u/5hhzBzAfipocWLDCY63RLLC/BIc8FwCzzwNuvZBKE6992aKEBWwRy Awka3wuMb50b9ONpvzh2QClwlGfMtPVmPD7i03sTOh5ao0/dTmSTNUh5z391vHwAZUBk QlfS/VrKVIeZ7XSXijZfKUfGzNNlq0AJerWMuGEZPw+tWpIw8tTCTC/eDJeL3Vl3bhRg mOmaD+qFNiYQS0SERF6SgJC8py3ltUDXZY7pClCVfBBg2845Ev9A8c0D4uMwzyjmBrge opWIO+qq8F0pv4mjTDMWeJS1fHU5X2TTxU5Gp9pmFCSlpZ1dHEzY1lYW6tWLkGRIWVY0 rx/Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@tmm1-net.20150623.gappssmtp.com header.s=20150623 header.b=E9EKyh54; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id z5si553727wmd.88.2017.12.12.16.42.02; Tue, 12 Dec 2017 16:42:02 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@tmm1-net.20150623.gappssmtp.com header.s=20150623 header.b=E9EKyh54; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 42AE068A342; Wed, 13 Dec 2017 02:41:53 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pf0-f170.google.com (mail-pf0-f170.google.com [209.85.192.170]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BC6C0680C95 for ; Wed, 13 Dec 2017 02:41:46 +0200 (EET) Received: by mail-pf0-f170.google.com with SMTP id l24so565636pfj.6 for ; Tue, 12 Dec 2017 16:41:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tmm1-net.20150623.gappssmtp.com; s=20150623; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=4+dDOAnDgNQd1aM9QQd4kCKPk10b7ATQsSYLajY1x6I=; b=E9EKyh54RoJBnBQ0sJg0LBclWbs+Gu5GKV/bCPqjvToGEPuqKWRiuy9L1FaAM75d3b Qiz9knqjM4adbntB/saH940HR+sv/P/oMt1ttwTJjh3X2X3adcwDJHUAkLvDiPAqNucf yVj0hGdUQFeYJGK4GsufnnnXlczzEyFkB+hv0be5ltRT29pJisxTdPwPEGd9Yp0UtZmz HvUErpskxmJil30egA8/i/+fQPMQsNqD7QD3F2/7oJT0o9CEyZnIyi/oxjrWdHMFZiQz DRT81KaDi7MGq8mQq0P11TohO1O6o5ecKEEsONk/1VlwW6gYzfUoiQCHJu3vWXzxrFEn qSYA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=4+dDOAnDgNQd1aM9QQd4kCKPk10b7ATQsSYLajY1x6I=; b=YggpeK54S/5cOSvGcOcD3RGj6+k/7WOct+R5cmmEZHd49bTrCBiYcxZvfauNfJCRws iMWsvOvauXMynDGnB6F5JrTPrF+kZxw4ZMCRZJuzM2lYsleX6Unb4+WwG3cdi0ugwhAk 6XPzLdgGAqedNiiRF/yU/qtI76Hzh1dM/iVuGBHP14asJ6xu4e0kt+hTvPJHdPbcDLQo cbRa7CcPB0R5HOxTEQD2BoFVY7p4TH+ufyvAyuNxowNLgenV07Qu+fcoJ1o/taooX6Y0 ZZGsPlfyftP14uncDvizuZdSh92e5+UR3w9uhVbb7vah/Bc7ZJ99ErI0aFK43IZkn5t6 E0ww== X-Gm-Message-State: AKGB3mLS4kWIhmbOfUuAN2gDh/9XQfEPWF1ZCER0tb2jlG0y3R0ktYYq 1zuVBqPtYQvd63IM+mCWhbaDvGkF X-Received: by 10.84.196.164 with SMTP id l33mr4081475pld.19.1513125321826; Tue, 12 Dec 2017 16:35:21 -0800 (PST) Received: from localhost.localdomain (c-69-181-54-242.hsd1.ca.comcast.net. [69.181.54.242]) by smtp.gmail.com with ESMTPSA id b25sm394282pfd.182.2017.12.12.16.35.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 12 Dec 2017 16:35:21 -0800 (PST) From: Aman Gupta To: ffmpeg-devel@ffmpeg.org Date: Tue, 12 Dec 2017 16:35:11 -0800 Message-Id: <20171213003511.25342-6-ffmpeg@tmm1.net> X-Mailer: git-send-email 2.14.3 (Apple Git-98) In-Reply-To: <20171213003511.25342-1-ffmpeg@tmm1.net> References: <20171213003511.25342-1-ffmpeg@tmm1.net> Subject: [FFmpeg-devel] [PATCH v2 5/5] avformat/hls: add http_multiple option X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: nfxjfg@googlemail.com, Aman Gupta , barsnick@gmx.net, kjeyapal@akamai.com MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Aman Gupta This improves network throughput of the hls demuxer by avoiding the latency introduced by downloading segments one at a time. The problem is particularly noticable over high-latency network connections: for instance, if RTT is 250ms, there will a 250ms idle period between when one segment response is read and the next one starts. The obvious solution to this is to use HTTP pipelining, where a second request can be sent (on the persistent http/1.1 connection) before the first response is fully read. Unfortunately the way the http protocol is implemented in avformat makes implementing pipleining very complex. Instead, this commit simulates pipelining using two separate persistent http connections. This has the advantage of working independently of the http_persistent option, and can be used with http/1.0 servers as well. The pair of connections is swapped every time a new segment starts downloading, and a request for the next segment is sent on the secondary connection right away. This means the second response will be ready and waiting by the time the current response is fully read. --- doc/demuxers.texi | 3 +++ libavformat/hls.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 18ff7101ac..f2181fbb93 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -319,6 +319,9 @@ Default value is 1000. @item http_persistent Use persistent HTTP connections. Applicable only for HTTP streams. + +@item http_multiple +Use multiple HTTP connections for downloading HTTP segments. @end table @section image2 diff --git a/libavformat/hls.c b/libavformat/hls.c index f75c8f5eaa..487fa9a82f 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); @@ -920,6 +926,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, @@ -1361,6 +1375,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) @@ -1368,7 +1383,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. */ @@ -1426,11 +1440,20 @@ reload: if (ret) return ret; - ret = open_input(c, v, seg, &v->input); + if (c->http_multiple && v->input_next_requested) { + AVIOContext *tmp = v->input; + v->input = v->input_next; + v->input_next = tmp; + 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; @@ -1438,6 +1461,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); @@ -1960,6 +1997,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); @@ -2195,6 +2235,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; @@ -2251,6 +2294,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 = 0}, 0, 1, FLAGS }, + {"http_multiple", "Use multiple HTTP connections for fetching segments", + OFFSET(http_multiple), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS}, {NULL} };