From patchwork Tue Dec 6 21:44:00 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Franklin Phillips X-Patchwork-Id: 1702 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.65.86 with SMTP id o83csp2386026vsa; Tue, 6 Dec 2016 13:44:29 -0800 (PST) X-Received: by 10.28.24.74 with SMTP id 71mr562621wmy.74.1481060669611; Tue, 06 Dec 2016 13:44:29 -0800 (PST) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id t126si5407573wmg.88.2016.12.06.13.44.28; Tue, 06 Dec 2016 13:44:29 -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; 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 ED592689E1C; Tue, 6 Dec 2016 23:44:15 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mout.gmx.net (mout.gmx.net [212.227.17.22]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9E8E2689D8B for ; Tue, 6 Dec 2016 23:44:09 +0200 (EET) Received: from localhost ([94.194.15.190]) by mail.gmx.com (mrgmx103 [212.227.17.174]) with ESMTPSA (Nemesis) id 0MZlEg-1bxPi13aJy-00LXqN; Tue, 06 Dec 2016 22:44:12 +0100 From: Franklin Phillips To: ffmpeg-devel@ffmpeg.org Date: Tue, 6 Dec 2016 21:44:00 +0000 Message-Id: <1481060641-30369-1-git-send-email-franklinphillips@gmx.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <20161129190527.GA1854@debianvm1> References: <20161129190527.GA1854@debianvm1> X-Provags-ID: V03:K0:eJf1K7eqYxoj9h3FO3n6CzPRXLXl2byV8gcXB72lgjR0RoQKbnV doyVpBJKGrBnQcklxgEnmWdF23yNCMWHKb+ElQKL7mSZ6y1dYYZilHqCDbLd8aeDPQgUUsK 80q69Cs4Dc4kZgVDy+vRe45ZMbHlCPYsVAf9cMMhifwb9KuIvDyAc97bJh7ICCK1vmSYnWn 68DukzV4tPELEXhBhlo+w== X-UI-Out-Filterresults: notjunk:1; V01:K0:AR8WwGGL8is=:jOV5llOK8N8rS3RT+h/J3o KP3M3VMJJullCUd1zTJZ0EuGgBCKpFOgbizDK7J9GMEEd30tfMYQoJhVgJr/VveCASp2pJFNU 2YzDnt/bOpPLY5W6RQjdjTryCYRbljVDC0yezpaU5uULTq7pwiiD/HYwGOL5Gb25kZT/1nS9E hvZTEutI4BPzMk/yoGh3P32dujQHq2rn5rPuP4cKc+2qEkC/BQluKnpTzht9MIMGFZQVd/PdU XcJgp5QQZU/BnxS9MAEtim4cUlrlMRfl3XHNZecAB0CdKEBzeY/FQ8wj+jrvI7c+uKmeRTGt+ VPg4+i/1baVabJJq9FOlIp7Yp3qpDdA9BM0i97Px3Ee2Lwj94Gz8yki0P4Q1jesZYICLSrm8r NphB3qRpuTOZIAzgOm5KXgSvwdzAA7LV+7eK12ZmNoI9LVl6QCJC1LFKNnDnWlA7FrlE8E97h NC+8as4LmhKq4PV2qXG/SwYhehUcq/0POEhPtfMqP/h4kSFeO2r6r0TievnErVfa+6aMSjhZv ENf1+kIXFFN0mC8ZoD9oKZJM0hnQyhcqsNVMsNrYlCus9LmTmaEgixRe+CZp0JqBKC+9eSZT4 RDiW4Kh70UYc1Tjom0tZt0F1MdiJqEj3NvcQYUgHWldAxh2IUxO5uQ4+D5B9UBr76lLdQQsaw XE+ZbW3SRs18ftaehs/WQyFw2N2dZm13RDEFug6Hi7on2vnVVxyArT76bOUpD+eSU2TCKadkB CnbRhoY79Q8bfIw6vKDwosITFilaI715qhA9/rSTKkRckPNbOCKhfor4+2I= Subject: [FFmpeg-devel] [PATCH 1/2] avformat/webvttdec: Add support for HLS WebVTT MPEG timestamp map 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: anssi.hannula@iki.fi, Franklin Phillips MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" WebVTT subtitle files in HLS streams contain a header to synchronize the subtitles with the MPEG TS timestamps of the HLS stream. Add an AVOption to prefer MPEG TS style timestamps generated according to the mapping header, if available. Signed-off-by: Franklin Phillips --- libavformat/webvttdec.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/libavformat/webvttdec.c b/libavformat/webvttdec.c index 0aeb8a6..329b25e 100644 --- a/libavformat/webvttdec.c +++ b/libavformat/webvttdec.c @@ -35,6 +35,7 @@ typedef struct { const AVClass *class; FFDemuxSubtitlesQueue q; int kind; + int prefer_hls_mpegts_pts; } WebVTTContext; static int webvtt_probe(AVProbeData *p) @@ -57,12 +58,19 @@ static int64_t read_ts(const char *s) return AV_NOPTS_VALUE; } +static int64_t convert_to_hls_mpegts_ts(int64_t timestamp, int64_t offset) +{ + return (timestamp * 90 + offset) & ((1LL << 33) - 1); +} + static int webvtt_read_header(AVFormatContext *s) { WebVTTContext *webvtt = s->priv_data; AVBPrint header, cue; int res = 0; AVStream *st = avformat_new_stream(s, NULL); + int has_hls_timestamp_map = 0; + int64_t hls_ts_offset; if (!st) return AVERROR(ENOMEM); @@ -93,8 +101,36 @@ static int webvtt_read_header(AVFormatContext *s) /* ignore header chunk */ if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) || !strncmp(p, "WEBVTT", 6) || - !strncmp(p, "NOTE", 4)) + !strncmp(p, "NOTE", 4)) { + + if (webvtt->prefer_hls_mpegts_pts) { + /* + * WebVTT files in HLS streams contain a timestamp offset for + * syncing with the main stream: + * + * X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000 + * (LOCAL and MPEGTS can be reversed even though HLS spec + * does not say so) + */ + + char *hls_timestamp_map = strstr(p, "\nX-TIMESTAMP-MAP="); + if (hls_timestamp_map) { + char *native_str = strstr(hls_timestamp_map, "LOCAL:"); + char *mpegts_str = strstr(hls_timestamp_map, "MPEGTS:"); + if (native_str && mpegts_str) { + int64_t native_ts = read_ts(native_str + 6); + int64_t mpegts_ts = strtoll(mpegts_str + 7, NULL, 10); + + if (native_ts != AV_NOPTS_VALUE) { + hls_ts_offset = mpegts_ts - native_ts * 90; + has_hls_timestamp_map = 1; + avpriv_set_pts_info(st, 33, 1, 90000); + } + } + } + } continue; + } /* optional cue identifier (can be a number like in SRT or some kind of * chaptering id) */ @@ -125,6 +161,12 @@ static int webvtt_read_header(AVFormatContext *s) if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE) break; + if (has_hls_timestamp_map) { + /* convert and truncate to MPEG TS timestamps */ + ts_start = convert_to_hls_mpegts_ts(ts_start, hls_ts_offset); + ts_end = convert_to_hls_mpegts_ts(ts_end, hls_ts_offset); + } + /* optional cue settings */ p += strcspn(p, "\n\t "); while (*p == '\t' || *p == ' ') @@ -200,6 +242,7 @@ static const AVOption options[] = { { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, INT_MIN, INT_MAX, 0, "webvtt_kind" }, + { "prefer_hls_mpegts_pts", "Use WebVTT embedded HLS MPEGTS timestamps if available.", OFFSET(prefer_hls_mpegts_pts), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM }, { NULL } };