From patchwork Thu Jan 18 16:13:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wm4 X-Patchwork-Id: 7328 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.78.2 with SMTP id r2csp7084759jaa; Thu, 18 Jan 2018 08:13:02 -0800 (PST) X-Google-Smtp-Source: ACJfBovVc8oQYKBMipaGzH5iB4draUXlnfkCFPQhsRL7XqljJwVsSwYW+0xwgnO74dBQsODrGd1I X-Received: by 10.28.45.151 with SMTP id t145mr5309770wmt.129.1516291982641; Thu, 18 Jan 2018 08:13:02 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516291982; cv=none; d=google.com; s=arc-20160816; b=E4FekC1MpOr8l1qtmIfkVLZDhTZMj8oqFGfDOUXIjqvgJJcLPutZKrNkUP6DAnwzoF mDPueNoeCmn35N+S+9MbScwFOilTuEUIllf6luYjgemQda/8o8DfzarTfEkaQjH0nHk1 Dk79g+FpIyeWGSJvb/CFo4S4/Y8pHu6Jzh4J/6klfw5mBovrTwLsXmd1IfxNGZUAKMcv 6rSgh79W68AxnyV7gQo71CQfhgWc5aFVrZi6APS+kXIGK8tnbLspr8HXYI+MCKkkqc07 6Uj5JS1jiGXbGSdzIMzn/8EcDnbtRGYz0YdQfJLE2yiU+zy/nb5EZ93NC+4+M5eGSvF2 uGdQ== 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:message-id:date:to:from:dkim-signature :delivered-to:arc-authentication-results; bh=qVYaEA9U9lgNZFth+y++WUaT3AUtMX2cu5jCZIHbvaE=; b=AW1byP57lUk4XxRWO5GHt3ww0UXrzXHLPQjFficYHijPPB7DzWe4TnU05l6ixVBgCw KBPetv5XGgeR5/pTa/0WU5P70oHWw6SrwpW+GCuFCiPvQUUCu0GCPSEANgF7XdUF8sW2 PB4qlaloDN05Kj1ZXyVKcXXScLFKSZ1pbBTbgIzNton4Sp9aeIJ1CGUWxjEbsDrdJVo1 Ihs0qyVuWbRpX+WKAtPBhv4oe4Jp/OLTxUwDJ0kSud0n+YX/R+yDVFOuuDE3/JLJIHPF uu2Y25LzoFo8yGawhVEDorqbAqR1V5Mc2E+/Gfsid5agE/MNSZMRuhJqL30ydwx4lSCq LURw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@googlemail.com header.s=20161025 header.b=EWhKU/Ur; 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; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 90si7168191wrc.326.2018.01.18.08.13.01; Thu, 18 Jan 2018 08:13: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=@googlemail.com header.s=20161025 header.b=EWhKU/Ur; 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; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=googlemail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 4C73268A273; Thu, 18 Jan 2018 18:13:00 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm0-f41.google.com (mail-wm0-f41.google.com [74.125.82.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3642768A255 for ; Thu, 18 Jan 2018 18:12:53 +0200 (EET) Received: by mail-wm0-f41.google.com with SMTP id 141so23855926wme.3 for ; Thu, 18 Jan 2018 08:12:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=6V48rUryShop0YDnG0BckujmuBS3NKFEh8eqMxC3Kbw=; b=EWhKU/UrxduuoDdwUjwAMvTBSGIrQzBotQEZDmpySjaHq+/cMOCSHdVqxSrsYAF9O2 skkRDxsUtSlJFiwFcxXL6qk6K/TdMZtk5pFbNh0rQNXnBE0KlxiddztO8DYiPe5Vc6HP M1kMyfLV2Kk/4UEV6WNRIauqCTLlAvLxZGbIQDIIzanUDkJpJiZKzU4sPUUiwoeolTm1 QSKwMRvpIwM1PKoQ5f3JGcW7xa4/T5SGrWfxUPcwWLOeBc6DTfWxwbbUs9FQHnOiZCRK 89I4/VQLbuXlx+bHwMNJzW91YghcUSihkjaDf228bspX75ybMRiyrQjXPPUJvRPBZEWs R30A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=6V48rUryShop0YDnG0BckujmuBS3NKFEh8eqMxC3Kbw=; b=Bhap31l6Q/8cFh0nC6Uq5odaGLZ6O7UECNwAYkJIi6ctX9dd00cSWpgZD6rFeioEey V3EuRNJ7HKBHThPt53/pxOZ26BzGk/WVk9zfDjrAP0/w0rgVoPnsWA6Qpb00SaDJ9oqU 9Kf90ctjg4c4XCK4VSGZDq8/V96D9WyCB4JSbg1xjAP//IL73WaZi9Kn/mPRwChPQgaQ s1/x831eLGo1vYrdoxjFDHT7ram6YZrXsTQAX44uMc1+b4U1blJBjH7gr0aJXszzWQc/ F/Vink39f6zDm+ySihrxKpURU2KgiuJ/gnB/OcUMXBq0aGLnIe/BlzIA6EppgOWRfSmh MM4A== X-Gm-Message-State: AKwxyteNcUOSrqWkwNGUHp0mmvZqVsgAjvbbD9VnZeqLzfV6u+qCS9KI 0VxhsAtQZWcSAo6ayntP0w8quQ== X-Received: by 10.80.241.23 with SMTP id w23mr8605778edl.111.1516291972727; Thu, 18 Jan 2018 08:12:52 -0800 (PST) Received: from debian.speedport.ip (p2003006CCD4EDC38E4471C3052AD979E.dip0.t-ipconnect.de. [2003:6c:cd4e:dc38:e447:1c30:52ad:979e]) by smtp.googlemail.com with ESMTPSA id a16sm5341828edd.19.2018.01.18.08.12.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jan 2018 08:12:52 -0800 (PST) From: wm4 To: ffmpeg-devel@ffmpeg.org Date: Thu, 18 Jan 2018 17:13:01 +0100 Message-Id: <20180118161301.32154-1-nfxjfg@googlemail.com> X-Mailer: git-send-email 2.15.1 Subject: [FFmpeg-devel] [RFC] [PATCH] hls: export range of currently available segments 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: wm4 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" The intention is to: 1. let API users know whether a HLS stream is live 2. let API users know about the safe seek range This is supposed to avoid Bad things happening when seeking outside of range. There are lots of problems with this patch: - the API consists of adding yet another set of obscure format specific fields to an already huge struct - determination of the first timestamp is fishy - is it actually safe to seek to the very first segment? - the range is only updated when the playlist is reloaded, but it could happen that the user accesses the fields while not having read any packets for a while, so the fields can be easily stale - same when enabling/disabling streams (which happens by setting public fields, and the hls code only knows about this when reading a new packet or when seeking) I'm open for ideas. Also this change is untested (other than that it compiles). --- libavformat/avformat.h | 36 ++++++++++++++++++++++++++++++++++++ libavformat/hls.c | 37 +++++++++++++++++++++++++++++++++++++ libavformat/options.c | 3 +++ 3 files changed, 76 insertions(+) diff --git a/libavformat/avformat.h b/libavformat/avformat.h index b0387214c5..eb1b6b4a68 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1898,6 +1898,42 @@ typedef struct AVFormatContext { * - decoding: set by user */ int max_streams; + + /** + * If this is set to 1, this is a live stream. Typically it will be + * unseekable, or allow seeking within a time window only. + * + * If this is set to 0, it is unknown what kind of stream this is. For + * specific protocols (such as HLS), it will indicate that it is + * definitely a static stream. + * + * Other values are reserved for the future, but if used will also indicate + * some kind of seek-restricted stream. + * + * - encoding: unused + * - decoding: set by libavformat + */ + int is_live_stream; + + /** + * If this field and available_range_end are set to a value other than + * AV_NOPTS_VALUE, they indicate the currently available media range in + * AV_TIME_BASE fractional seconds. This is supported by specific network + * protocols only, such as HLS. Generally this means you can seek within + * this range. + * + * - encoding: unused + * - decoding: set by libavformat + */ + int64_t available_range_start; + + /** + * See available_range_start field. + * + * - encoding: unused + * - decoding: set by libavformat + */ + int64_t available_range_end; } AVFormatContext; #if FF_API_FORMAT_GET_SET diff --git a/libavformat/hls.c b/libavformat/hls.c index 950cc4c3bd..557b185f4b 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -215,6 +215,8 @@ typedef struct HLSContext { AVIOContext *playlist_pb; } HLSContext; +static int playlist_needed(struct playlist *pls); + static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) { int len = ff_get_line(s, buf, maxlen); @@ -707,6 +709,40 @@ static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url, return ret; } +static void update_available_range(HLSContext *c) +{ + int n, i; + + c->ctx->available_range_start = AV_NOPTS_VALUE; + c->ctx->available_range_end = AV_NOPTS_VALUE; + c->ctx->is_live_stream = 0; + + for (n = 0; n < c->n_playlists; n++) { + struct playlist *pls = c->playlists[n]; + int64_t ts; + + if (!playlist_needed(pls) || !pls->n_segments) + continue; + + // Not sure where to get the actual start timestamp - might require + // waiting until the first packet for every active stream on pls is read. + ts = c->first_timestamp == AV_NOPTS_VALUE ? 0 : c->first_timestamp; + if (c->ctx->available_range_start == AV_NOPTS_VALUE || + c->ctx->available_range_start < ts) + c->ctx->available_range_start = ts; + + for (i = 0; 0 < pls->n_segments; i++) + ts += pls->segments[i]->duration; + + if (c->ctx->available_range_end == AV_NOPTS_VALUE || + c->ctx->available_range_end > ts) + c->ctx->available_range_end = ts; + + if (!pls->finished) + c->ctx->is_live_stream = 1; + } +} + static int parse_playlist(HLSContext *c, const char *url, struct playlist *pls, AVIOContext *in) { @@ -930,6 +966,7 @@ fail: av_free(new_url); if (close_in) ff_format_io_close(c->ctx, &in); + update_available_range(c); return ret; } diff --git a/libavformat/options.c b/libavformat/options.c index 9371c72667..875f81341e 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -138,6 +138,9 @@ static void avformat_get_context_defaults(AVFormatContext *s) s->io_open = io_open_default; s->io_close = io_close_default; + s->available_range_start = AV_NOPTS_VALUE; + s->available_range_end = AV_NOPTS_VALUE; + av_opt_set_defaults(s); }