From patchwork Sun Feb 5 19:43:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Franklin Phillips X-Patchwork-Id: 2428 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.89.21 with SMTP id n21csp1503529vsb; Sun, 5 Feb 2017 11:44:46 -0800 (PST) X-Received: by 10.28.203.75 with SMTP id b72mr6701539wmg.110.1486323886528; Sun, 05 Feb 2017 11:44:46 -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 p12si39286034wrd.158.2017.02.05.11.44.46; Sun, 05 Feb 2017 11:44:46 -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 897CC689A2B; Sun, 5 Feb 2017 21:44:41 +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.15.15]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C5247689817 for ; Sun, 5 Feb 2017 21:44:34 +0200 (EET) Received: from localhost.localdomain ([175.201.237.219]) by mail.gmx.com (mrgmx001 [212.227.17.184]) with ESMTPSA (Nemesis) id 0Mdren-1coaRo3YrN-00Pcni; Sun, 05 Feb 2017 20:44:36 +0100 From: Franklin Phillips To: ffmpeg-devel@ffmpeg.org Date: Sun, 5 Feb 2017 19:43:43 +0000 Message-Id: <20170205194344.333-1-franklinphillips@gmx.com> X-Mailer: git-send-email 2.11.0 X-Provags-ID: V03:K0:ICL4PUWQKckmuzQWJ0OxNgVc+p5VSlSvyTnB3vPcNTY4fVfFXBC bbPrd3rDf+Fs0OC7hRu/zUNVmu+YsozvgCNIm9OnWXd7J67aLqNrkIsa8ujJcduVy/qVKhl uETTKmGwYJ7yGXk3TXWYM/+yirsGOqvs4iAUBurqPsS/aDTEyfr7th809ACGnrn9XZ7vgWP 4wLlapLbqDvoHFpGeJLyQ== X-UI-Out-Filterresults: notjunk:1; V01:K0:BIKJ4b7buBw=:gkUxqzboZjqJuw/0Tnoh9O v5xKPnVWEh+3CdKqAZNssOKZwdFesLZOK6EYPVpPH9kqjUmlkOod7LOwZ5rUxns0eqyvX+pXd LyxdyMu8dKmQcfoEv6SDDRF2orJTNZ4f3eqQ4NEpWnZxFIhdDOT4491Dm+laIj8QxocBbgB8D SRPPvPCTDhZ5ZUSpD0+LH5vbRq0iEE24ktJodvTjwXj7x5xr6xzaYgxoNf3dnbGp5d96VRCdr xYV/mkY60IvTfrgnmH5MFDxIc2C2CzkblIDpQCcS1fQs8/JoG9SfdqqmDbZM4nIlDotBUulR1 +rFu03YZpWbTUOR/BGuCzmp/Xj1aKibkdQH9qoKvSCpGJItPqLWIOEmqxZwf7SGKNMrUUyXEd f+lry4KfAxUbwi3/FD0v6ep9Qmj7CXQJtFONb3s0ZhP07Znb3TffGlQv/bJi6O77imEo4dgV/ blxsdkwwm02AHg04cxQKnDr2pVu3kTEgz6b402EYHLe/CuVA8xKPq6VTvVaVqYX07sxUQURrL 82T8Vaj+r3egTRbW/0OCmv4HjSqBnUxzPhwZeolO7zWuYsqqi8bWMGZanUQqhkfIpuemTq/B4 tarOn5mT7L8+yqG1nfh70sg+95LiBFxcuaZIJ0skksv1irlNBnhv6KFmICim4FMQX3PITulvy R3EUM2R4pi22VMj3SvMEorZ8DWd//2ypQwyI/ByQrT5/LH39BJTAyYlCT1Xc+GK1/eHkgB/We 3FVFhQCGf77khXvGAMRjfAhe1Ql6ka8y9ApitTVwzgillxUcELMd1+V4Y44= Subject: [FFmpeg-devel] [PATCH v2 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: 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 } };