diff --git a/src/controller/timeline-controller.js b/src/controller/timeline-controller.js index 0f7719b98b7..b634bd254b6 100644 --- a/src/controller/timeline-controller.js +++ b/src/controller/timeline-controller.js @@ -37,7 +37,7 @@ class TimelineController extends EventHandler { this.textTracks = []; this.tracks = []; this.unparsedVttFrags = []; - this.initPTS = undefined; + this.initPTS = []; this.cueRanges = []; this.captionsTracks = {}; @@ -85,17 +85,19 @@ class TimelineController extends EventHandler { // Triggered when an initial PTS is found; used for synchronisation of WebVTT. onInitPtsFound (data) { - if (typeof this.initPTS === 'undefined') { - this.initPTS = data.initPTS; + let demuxerId = data.id, cc = data.frag.cc, initPTS = data.initPTS; + if (demuxerId === 'main') { + this.initPTS[cc] = initPTS; } // Due to asynchrony, initial PTS may arrive later than the first VTT fragments are loaded. // Parse any unparsed fragments upon receiving the initial PTS. if (this.unparsedVttFrags.length) { - this.unparsedVttFrags.forEach(frag => { + const unparsedVttFrags = this.unparsedVttFrags; + this.unparsedVttFrags = []; + unparsedVttFrags.forEach(frag => { this.onFragLoaded(frag); }); - this.unparsedVttFrags = []; } } @@ -180,7 +182,7 @@ class TimelineController extends EventHandler { onManifestLoaded (data) { this.textTracks = []; this.unparsedVttFrags = this.unparsedVttFrags || []; - this.initPTS = undefined; + this.initPTS = []; this.cueRanges = []; if (this.config.enableWebVTT) { @@ -233,7 +235,7 @@ class TimelineController extends EventHandler { else if (frag.type === 'subtitle') { if (payload.byteLength) { // We need an initial synchronisation PTS. Store fragments as long as none has arrived. - if (typeof this.initPTS === 'undefined') { + if (this.initPTS[frag.cc] === undefined) { this.unparsedVttFrags.push(data); return; } @@ -260,7 +262,7 @@ class TimelineController extends EventHandler { hls = this.hls; // Parse the WebVTT file contents. - WebVTTParser.parse(payload, this.initPTS, vttCCs, frag.cc, function (cues) { + WebVTTParser.parse(payload, this.initPTS[frag.cc], vttCCs, frag.cc, function (cues) { const currentTrack = textTracks[frag.trackId]; // WebVTTParser.parse is an async method and if the currently selected text track mode is set to "disabled" // before parsing is done then don't try to access currentTrack.cues.getCueById as cues will be null @@ -299,7 +301,7 @@ class TimelineController extends EventHandler { frag = data.frag; if (frag.type === 'subtitle') { - if (typeof this.initPTS === 'undefined') { + if (this.initPTS[frag.cc] === undefined) { this.unparsedVttFrags.push(data); return; } diff --git a/src/utils/webvtt-parser.js b/src/utils/webvtt-parser.js index 46f1ceba2ce..e76d60695dd 100644 --- a/src/utils/webvtt-parser.js +++ b/src/utils/webvtt-parser.js @@ -58,6 +58,29 @@ const calculateOffset = function (vttCCs, cc, presentationTime) { vttCCs.presentationOffset = presentationTime; }; +const ptsNormalize = function (value, reference) { + let offset; + if (reference === undefined) { + return value; + } + + if (reference < value) { + // - 2^33 + offset = -8589934592; + } else { + // + 2^33 + offset = 8589934592; + } + /* PTS is 33bit (from 0 to 2^33 -1) + if diff between value and reference is bigger than half of the amplitude (2^32) then it means that + PTS looping occured. fill the gap */ + while (Math.abs(value - reference) > 4294967296) { + value += offset; + } + + return value; +}; + const WebVTTParser = { parse: function (vttByteArray, syncPTS, vttCCs, cc, callBack, errorCallBack) { // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character. @@ -94,7 +117,7 @@ const WebVTTParser = { if (presentationTime) { // If we have MPEGTS, offset = presentation time + discontinuity offset - cueOffset = presentationTime + vttCCs.ccOffset - vttCCs.presentationOffset; + cueOffset = presentationTime - vttCCs.presentationOffset; } cue.startTime += cueOffset - localTime; @@ -141,9 +164,7 @@ const WebVTTParser = { try { // Calculate subtitle offset in milliseconds. // If sync PTS is less than zero, we have a 33-bit wraparound, which is fixed by adding 2^33 = 8589934592. - syncPTS = syncPTS < 0 ? syncPTS + 8589934592 : syncPTS; - // Adjust MPEGTS by sync PTS. - mpegTs -= syncPTS; + mpegTs = ptsNormalize(mpegTs - syncPTS, vttCCs.ccOffset * 90000); // Convert cue time to seconds localTime = cueString2millis(cueTime) / 1000; // Convert MPEGTS to seconds from 90kHz.