From f382cc702be6cc28266fe61a33e43573cb22be57 Mon Sep 17 00:00:00 2001
From: Philippe
Date: Fri, 1 Apr 2022 18:39:11 +0200
Subject: [PATCH] feat: add option for segment-relative VTT timings (#4083)
This PR fixes #3242 where for some live streams using segmented VTT, text timings are relative to segment start instead of being absolute.
The PR introduces a new setting: `manifest.segmentRelativeVttTiming: boolean` allowing such alternative timing offset calculation.
The setting is off by default, preserving the current player behaviour.
Co-authored-by: Joey Parrish
---
demo/common/message_ids.js | 1 +
demo/config.js | 4 +-
demo/locales/en.json | 1 +
demo/locales/source.json | 4 +
externs/shaka/player.js | 5 +
externs/shaka/text.js | 6 +-
lib/media/media_source_engine.js | 13 +-
lib/player.js | 7 +
lib/text/text_engine.js | 11 +-
lib/text/vtt_text_parser.js | 6 +-
lib/util/player_configuration.js | 1 +
test/player_unit.js | 2 +
test/test/util/fake_media_source_engine.js | 4 +
test/text/cue_integration.js | 4 +-
test/text/lrc_text_parser_unit.js | 13 +-
test/text/mp4_ttml_parser_unit.js | 10 +-
test/text/mp4_vtt_parser_unit.js | 11 +-
test/text/sbv_text_parser_unit.js | 11 +-
test/text/srt_text_parser_unit.js | 11 +-
test/text/ssa_text_parser_unit.js | 46 +++---
test/text/text_engine_unit.js | 45 +++++-
test/text/ttml_text_parser_unit.js | 154 ++++++++++-----------
test/text/vtt_text_parser_unit.js | 112 ++++++++-------
23 files changed, 279 insertions(+), 203 deletions(-)
diff --git a/demo/common/message_ids.js b/demo/common/message_ids.js
index 2b58d068c0..6ddc23d326 100644
--- a/demo/common/message_ids.js
+++ b/demo/common/message_ids.js
@@ -232,6 +232,7 @@ shakaDemo.MessageIds = {
RESTRICTIONS_SECTION_HEADER: 'DEMO_RESTRICTIONS_SECTION_HEADER',
SAFE_SEEK_OFFSET: 'DEMO_SAFE_SEEK_OFFSET',
SAFE_SKIP_DISTANCE: 'DEMO_SAFE_SKIP_DISTANCE',
+ SEGMENT_RELATIVE_VTT_TIMING: 'DEMO_SEGMENT_RELATIVE_VTT_TIMING',
SESSION_ID: 'DEMO_SESSION_ID',
SHAKA_CONTROLS: 'DEMO_SHAKA_CONTROLS',
SLOW_HALF_LIFE: 'DEMO_SLOW_HALF_LIFE',
diff --git a/demo/config.js b/demo/config.js
index 738e01719b..a220857b3d 100644
--- a/demo/config.js
+++ b/demo/config.js
@@ -237,7 +237,9 @@ shakaDemo.Config = class {
.addBoolInput_(MessageIds.DISABLE_TEXT,
'manifest.disableText')
.addBoolInput_(MessageIds.DISABLE_THUMBNAILS,
- 'manifest.disableThumbnails');
+ 'manifest.disableThumbnails')
+ .addBoolInput_(MessageIds.SEGMENT_RELATIVE_VTT_TIMING,
+ 'manifest.segmentRelativeVttTiming');
this.addRetrySection_('manifest', MessageIds.MANIFEST_RETRY_SECTION_HEADER);
}
diff --git a/demo/locales/en.json b/demo/locales/en.json
index 670d8717a0..d51d7a676e 100644
--- a/demo/locales/en.json
+++ b/demo/locales/en.json
@@ -180,6 +180,7 @@
"DEMO_RESTRICTIONS_SECTION_HEADER": "Restrictions",
"DEMO_SAFE_SEEK_OFFSET": "Safe Seek Offset",
"DEMO_SAFE_SKIP_DISTANCE": "Safe Skip Distance",
+ "DEMO_SEGMENT_RELATIVE_VTT_TIMING": "Enable segment-relative VTT Timing",
"DEMO_SESSION_ID": "Session ID",
"DEMO_SAVE_BUTTON": "Save",
"DEMO_SHAKA": "Shaka",
diff --git a/demo/locales/source.json b/demo/locales/source.json
index 2e75a0f548..d005b400bb 100644
--- a/demo/locales/source.json
+++ b/demo/locales/source.json
@@ -727,6 +727,10 @@
"description": "A button to save a custom asset.",
"message": "Save"
},
+ "DEMO_SEGMENT_RELATIVE_VTT_TIMING": {
+ "description": "The name of a configuration value.",
+ "message": "Enable segment-relative VTT Timing"
+ },
"DEMO_SESSION_ID": {
"description": "The name of a configuration value.",
"message": "Session ID"
diff --git a/externs/shaka/player.js b/externs/shaka/player.js
index b56db3f62e..da49d258be 100644
--- a/externs/shaka/player.js
+++ b/externs/shaka/player.js
@@ -792,6 +792,7 @@ shaka.extern.HlsManifestConfiguration;
* disableText: boolean,
* disableThumbnails: boolean,
* defaultPresentationDelay: number,
+ * segmentRelativeVttTiming: boolean,
* dash: shaka.extern.DashManifestConfiguration,
* hls: shaka.extern.HlsManifestConfiguration
* }}
@@ -823,6 +824,10 @@ shaka.extern.HlsManifestConfiguration;
* configured or set as 0.
* For HLS, the default value is 3 segments duration if not configured or
* set as 0.
+ * @property {boolean} segmentRelativeVttTiming
+ * Option to calculate VTT text timings relative to the segment start
+ * instead of relative to the period start (which is the default).
+ * Defaults to false
.
* @property {shaka.extern.DashManifestConfiguration} dash
* Advanced parameters used by the DASH manifest parser.
* @property {shaka.extern.HlsManifestConfiguration} hls
diff --git a/externs/shaka/text.js b/externs/shaka/text.js
index 6403664c6e..d50d2c2b33 100644
--- a/externs/shaka/text.js
+++ b/externs/shaka/text.js
@@ -442,7 +442,8 @@ shaka.extern.TextParser = class {
* @typedef {{
* periodStart: number,
* segmentStart: number,
- * segmentEnd: number
+ * segmentEnd: number,
+ * vttOffset: number
* }}
*
* @property {number} periodStart
@@ -451,6 +452,9 @@ shaka.extern.TextParser = class {
* The absolute start time of the segment in seconds.
* @property {number} segmentEnd
* The absolute end time of the segment in seconds.
+ * @property {number} vttOffset
+ * The start time relative to either segment or period start depending
+ * on segmentRelativeVttTiming
configuration.
*
* @exportDoc
*/
diff --git a/lib/media/media_source_engine.js b/lib/media/media_source_engine.js
index 3f5fe6be23..8fd1931161 100644
--- a/lib/media/media_source_engine.js
+++ b/lib/media/media_source_engine.js
@@ -66,6 +66,9 @@ shaka.media.MediaSourceEngine = class {
/** @private {shaka.text.TextEngine} */
this.textEngine_ = null;
+ /** @private {boolean} */
+ this.segmentRelativeVttTiming_ = false;
+
const onMetadataNoOp = (metadata, timestampOffset, segmentEnd) => {};
/** @private {!function(!Array.,
@@ -367,7 +370,8 @@ shaka.media.MediaSourceEngine = class {
if (!this.textEngine_) {
this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
}
- this.textEngine_.initParser(mimeType, sequenceMode);
+ this.textEngine_.initParser(mimeType, sequenceMode,
+ this.segmentRelativeVttTiming_);
}
/**
@@ -1113,6 +1117,13 @@ shaka.media.MediaSourceEngine = class {
}
}
+ /**
+ * @param {boolean} segmentRelativeVttTiming
+ */
+ setSegmentRelativeVttTiming(segmentRelativeVttTiming) {
+ this.segmentRelativeVttTiming_ = segmentRelativeVttTiming;
+ }
+
/**
* Apply platform-specific transformations to this segment to work around
* issues in the platform.
diff --git a/lib/player.js b/lib/player.js
index 7e4892caa1..d9952af152 100644
--- a/lib/player.js
+++ b/lib/player.js
@@ -1571,6 +1571,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
(metadata, offset, endTime) => {
this.processTimedMetadataMediaSrc_(metadata, offset, endTime);
});
+ const {segmentRelativeVttTiming} = this.config_.manifest;
+ mediaSourceEngine.setSegmentRelativeVttTiming(segmentRelativeVttTiming);
// Wait for media source engine to finish opening. This promise should
// NEVER be rejected as per the media source engine implementation.
@@ -3020,6 +3022,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}
if (this.mediaSourceEngine_) {
+ const {segmentRelativeVttTiming} = this.config_.manifest;
+ this.mediaSourceEngine_.setSegmentRelativeVttTiming(
+ segmentRelativeVttTiming);
+
const textDisplayerFactory = this.config_.textDisplayFactory;
if (this.lastTextFactory_ != textDisplayerFactory) {
const displayer =
@@ -4864,6 +4870,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
periodStart: 0,
segmentStart: 0,
segmentEnd: this.video_.duration,
+ vttOffset: 0,
};
const data = shaka.util.BufferUtils.toUint8(buffer);
const cues = obj.parseMedia(data, time);
diff --git a/lib/text/text_engine.js b/lib/text/text_engine.js
index 151ff0898d..c968542572 100644
--- a/lib/text/text_engine.js
+++ b/lib/text/text_engine.js
@@ -31,6 +31,9 @@ shaka.text.TextEngine = class {
/** @private {shaka.extern.TextDisplayer} */
this.displayer_ = displayer;
+ /** @private {boolean} */
+ this.segmentRelativeVttTiming_ = false;
+
/** @private {number} */
this.timestampOffset_ = 0;
@@ -130,8 +133,9 @@ shaka.text.TextEngine = class {
*
* @param {string} mimeType
* @param {boolean} sequenceMode
+ * @param {boolean} segmentRelativeVttTiming
*/
- initParser(mimeType, sequenceMode) {
+ initParser(mimeType, sequenceMode, segmentRelativeVttTiming) {
// No parser for CEA, which is extracted from video and side-loaded
// into TextEngine and TextDisplayer.
if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
@@ -149,6 +153,7 @@ shaka.text.TextEngine = class {
shaka.log.alwaysWarn(
'Text parsers should have a "setSequenceMode" method!');
}
+ this.segmentRelativeVttTiming_ = segmentRelativeVttTiming;
}
/**
@@ -174,11 +179,15 @@ shaka.text.TextEngine = class {
return;
}
+ const vttOffset = this.segmentRelativeVttTiming_ ?
+ startTime : this.timestampOffset_;
+
/** @type {shaka.extern.TextParser.TimeContext} **/
const time = {
periodStart: this.timestampOffset_,
segmentStart: startTime,
segmentEnd: endTime,
+ vttOffset: vttOffset,
};
// Parse the buffer and add the new cues.
diff --git a/lib/text/vtt_text_parser.js b/lib/text/vtt_text_parser.js
index 73334e545f..51bd1ad02a 100644
--- a/lib/text/vtt_text_parser.js
+++ b/lib/text/vtt_text_parser.js
@@ -62,9 +62,13 @@ shaka.text.VttTextParser = class {
shaka.util.Error.Code.INVALID_TEXT_HEADER);
}
+ // Depending on "segmentRelativeVttTiming" configuration,
+ // "vttOffset" will correspond to either "periodStart" (default)
+ // or "segmentStart", for segmented VTT where timings are relative
+ // to the beginning of each segment.
// NOTE: "periodStart" is the timestamp offset applied via TextEngine.
// It is no longer closely tied to periods, but the name stuck around.
- let offset = time.periodStart;
+ let offset = time.vttOffset;
// Do not honor the 'X-TIMESTAMP-MAP' value when in sequence mode.
// That is because it is used mainly (solely?) to account for the timestamp
diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js
index 4ca307f179..f0444d32ee 100644
--- a/lib/util/player_configuration.js
+++ b/lib/util/player_configuration.js
@@ -89,6 +89,7 @@ shaka.util.PlayerConfiguration = class {
disableText: false,
disableThumbnails: false,
defaultPresentationDelay: 0,
+ segmentRelativeVttTiming: false,
dash: {
clockSyncUri: '',
ignoreDrmInfo: false,
diff --git a/test/player_unit.js b/test/player_unit.js
index 968205a897..2375e010db 100644
--- a/test/player_unit.js
+++ b/test/player_unit.js
@@ -140,6 +140,8 @@ describe('Player', () => {
jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
setUseEmbeddedText: jasmine.createSpy('setUseEmbeddedText'),
getUseEmbeddedText: jasmine.createSpy('getUseEmbeddedText'),
+ setSegmentRelativeVttTiming:
+ jasmine.createSpy('setSegmentRelativeVttTiming'),
getTextDisplayer: () => textDisplayer,
ended: jasmine.createSpy('ended').and.returnValue(false),
};
diff --git a/test/test/util/fake_media_source_engine.js b/test/test/util/fake_media_source_engine.js
index 5e142e214e..9fc9966398 100644
--- a/test/test/util/fake_media_source_engine.js
+++ b/test/test/util/fake_media_source_engine.js
@@ -132,6 +132,10 @@ shaka.test.FakeMediaSourceEngine = class {
this.getTextDisplayer =
jasmine.createSpy('getTextDisplayer')
.and.returnValue(new shaka.test.FakeTextDisplayer());
+
+ /** @type {!jasmine.Spy} */
+ this.setSegmentRelativeVttTiming =
+ jasmine.createSpy('setSegmentRelativeVttTiming').and.stub();
}
/** @override */
diff --git a/test/text/cue_integration.js b/test/text/cue_integration.js
index 09d7b1fac6..89b850b3ba 100644
--- a/test/text/cue_integration.js
+++ b/test/text/cue_integration.js
@@ -23,7 +23,7 @@ describe('Cue', () => {
'WEBVTT\n\n' +
'00:00:20.000 --> 00:00:40.000\n' +
'Test',
- {periodStart: 7, segmentStart: 10, segmentEnd: 60});
+ {periodStart: 7, segmentStart: 10, segmentEnd: 60, vttOffset: 7});
expect(cues.length).toBe(1);
expect(cues[0].startTime).toBe(27);
expect(cues[0].endTime).toBe(47);
@@ -38,7 +38,7 @@ describe('Cue', () => {
'ID1\n' +
'00:00:20.000 --> 00:00:40.000 align:middle size:56% vertical:lr\n' +
'Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
expect(cues.length).toBe(1);
});
diff --git a/test/text/lrc_text_parser_unit.js b/test/text/lrc_text_parser_unit.js
index 396e816948..8896a71937 100644
--- a/test/text/lrc_text_parser_unit.js
+++ b/test/text/lrc_text_parser_unit.js
@@ -12,7 +12,7 @@ describe('LrcTextParser', () => {
it('supports no cues', () => {
verifyHelper([],
'',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles a blank line at the start of the file', () => {
@@ -22,7 +22,7 @@ describe('LrcTextParser', () => {
],
'\n\n' +
'[00:00.00]Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles a blank line at the end of the file', () => {
@@ -32,7 +32,7 @@ describe('LrcTextParser', () => {
],
'[00:00.00]Test' +
'\n\n',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles no blank line at the end of the file', () => {
@@ -41,8 +41,7 @@ describe('LrcTextParser', () => {
{startTime: 0, endTime: 2, payload: 'Test'},
],
'[00:00.00]Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports multiple cues', () => {
@@ -55,7 +54,7 @@ describe('LrcTextParser', () => {
'[00:00.00]Test\n' +
'[00:10.00]Test2\n' +
'[00:20.00]Test3',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports different time formats', () => {
@@ -74,7 +73,7 @@ describe('LrcTextParser', () => {
'[00:30,1]Test4\n' +
'[00:40,001]Test5\n' +
'[00:50,02]Test6',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
/**
diff --git a/test/text/mp4_ttml_parser_unit.js b/test/text/mp4_ttml_parser_unit.js
index d1410062bd..565ae179e7 100644
--- a/test/text/mp4_ttml_parser_unit.js
+++ b/test/text/mp4_ttml_parser_unit.js
@@ -47,7 +47,7 @@ describe('Mp4TtmlParser', () => {
it('handles media segments with multiple mdats', () => {
const parser = new shaka.text.Mp4TtmlParser();
parser.parseInit(ttmlInitSegment);
- const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
const ret = parser.parseMedia(ttmlSegmentMultipleMDAT, time);
// Bodies.
expect(ret.length).toBe(2);
@@ -60,8 +60,10 @@ describe('Mp4TtmlParser', () => {
});
it('accounts for offset', () => {
- const time1 = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
- const time2 = {periodStart: 7, segmentStart: 0, segmentEnd: 0};
+ const time1 =
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
+ const time2 =
+ {periodStart: 7, segmentStart: 0, segmentEnd: 0, vttOffset: 7};
const parser = new shaka.text.Mp4TtmlParser();
parser.parseInit(ttmlInitSegment);
@@ -165,7 +167,7 @@ describe('Mp4TtmlParser', () => {
];
const parser = new shaka.text.Mp4TtmlParser();
parser.parseInit(ttmlInitSegment);
- const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
const result = parser.parseMedia(ttmlSegment, time);
shaka.test.TtmlUtils.verifyHelper(
cues, result, {startTime: 23, endTime: 53.5});
diff --git a/test/text/mp4_vtt_parser_unit.js b/test/text/mp4_vtt_parser_unit.js
index 92ef55710a..5b527b83e6 100644
--- a/test/text/mp4_vtt_parser_unit.js
+++ b/test/text/mp4_vtt_parser_unit.js
@@ -71,7 +71,7 @@ describe('Mp4VttParser', () => {
const parser = new shaka.text.Mp4VttParser();
parser.parseInit(vttInitSegment);
- const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
const result = parser.parseMedia(vttSegment, time);
verifyHelper(cues, result);
});
@@ -99,7 +99,7 @@ describe('Mp4VttParser', () => {
const parser = new shaka.text.Mp4VttParser();
parser.parseInit(vttInitSegment);
- const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
const result = parser.parseMedia(vttSegmentMultiPayload, time);
verifyHelper(cues, result);
});
@@ -127,7 +127,7 @@ describe('Mp4VttParser', () => {
const parser = new shaka.text.Mp4VttParser();
parser.parseInit(vttInitSegment);
- const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
const result = parser.parseMedia(vttSegSettings, time);
verifyHelper(cues, result);
});
@@ -149,7 +149,7 @@ describe('Mp4VttParser', () => {
const parser = new shaka.text.Mp4VttParser();
parser.parseInit(vttInitSegment);
- const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0};
+ const time = {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0};
const result = parser.parseMedia(vttSegNoDuration, time);
verifyHelper(cues, result);
});
@@ -171,7 +171,8 @@ describe('Mp4VttParser', () => {
const parser = new shaka.text.Mp4VttParser();
parser.parseInit(vttInitSegment);
- const time = {periodStart: 10, segmentStart: 0, segmentEnd: 0};
+ const time =
+ {periodStart: 10, segmentStart: 0, segmentEnd: 0, vttOffset: 10};
const result = parser.parseMedia(vttSegment, time);
verifyHelper(cues, result);
});
diff --git a/test/text/sbv_text_parser_unit.js b/test/text/sbv_text_parser_unit.js
index b06fc2c77f..31896f5bb5 100644
--- a/test/text/sbv_text_parser_unit.js
+++ b/test/text/sbv_text_parser_unit.js
@@ -12,7 +12,7 @@ describe('SbvTextParser', () => {
it('supports no cues', () => {
verifyHelper([],
'',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles a blank line at the end of the file', () => {
@@ -22,7 +22,7 @@ describe('SbvTextParser', () => {
],
'0:00:20.000,0:00:40.000\n' +
'Test\n\n',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles no blank line at the end of the file', () => {
@@ -32,8 +32,7 @@ describe('SbvTextParser', () => {
],
'0:00:20.000,0:00:40.000\n' +
'Test\n',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles no newline after the final text payload', () => {
@@ -43,7 +42,7 @@ describe('SbvTextParser', () => {
],
'0:00:20.000,0:00:40.000\n' +
'Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports multiple cues', () => {
@@ -56,7 +55,7 @@ describe('SbvTextParser', () => {
'Test\n\n' +
'0:00:40.000,0:00:50.000\n' +
'Test2',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
/**
diff --git a/test/text/srt_text_parser_unit.js b/test/text/srt_text_parser_unit.js
index 5284d73671..39223cf211 100644
--- a/test/text/srt_text_parser_unit.js
+++ b/test/text/srt_text_parser_unit.js
@@ -12,7 +12,7 @@ describe('SrtTextParser', () => {
it('supports no cues', () => {
verifyHelper([],
'',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles a blank line at the end of the file', () => {
@@ -23,7 +23,7 @@ describe('SrtTextParser', () => {
'1\n' +
'00:00:20,000 --> 00:00:40,000\n' +
'Test\n\n',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles no blank line at the end of the file', () => {
@@ -34,8 +34,7 @@ describe('SrtTextParser', () => {
'1\n' +
'00:00:20,000 --> 00:00:40,000\n' +
'Test\n',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles no newline after the final text payload', () => {
@@ -46,7 +45,7 @@ describe('SrtTextParser', () => {
'1\n' +
'00:00:20,000 --> 00:00:40,000\n' +
'Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports multiple cues', () => {
@@ -61,7 +60,7 @@ describe('SrtTextParser', () => {
'2\n' +
'00:00:40,000 --> 00:00:50,000\n' +
'Test2',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
/**
diff --git a/test/text/ssa_text_parser_unit.js b/test/text/ssa_text_parser_unit.js
index ff3354ae5f..2711014339 100644
--- a/test/text/ssa_text_parser_unit.js
+++ b/test/text/ssa_text_parser_unit.js
@@ -15,7 +15,7 @@ describe('SsaTextParser', () => {
it('supports no cues', () => {
verifyHelper([],
'',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles a blank line at the start of the file', () => {
@@ -38,7 +38,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles a blank line at the end of the file', () => {
@@ -61,7 +61,7 @@ describe('SsaTextParser', () => {
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test' +
'\n\n',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('handles no blank line at the end of the file', () => {
@@ -83,8 +83,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports no styles', () => {
@@ -99,8 +98,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('support no script info', () => {
@@ -120,8 +118,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports only events', () => {
@@ -134,8 +131,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports text with commas', () => {
@@ -148,8 +144,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test,1,Test2',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports different time formats', () => {
@@ -168,7 +163,7 @@ describe('SsaTextParser', () => {
',,{\\pos(400,570)}Test2\n' +
'Dialogue: 0,0:00:08.01,0:00:10.001,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test3',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports multiple cues', () => {
@@ -187,7 +182,7 @@ describe('SsaTextParser', () => {
',,{\\pos(400,570)}Test2\n' +
'Dialogue: 0,0:00:08.01,0:00:10.10,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test3',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0});
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports fontFamily style', () => {
@@ -212,8 +207,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports color & backgroundColor style', () => {
@@ -239,8 +233,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports bold style', () => {
@@ -265,8 +258,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports italic style', () => {
@@ -291,8 +283,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports underline style', () => {
@@ -317,8 +308,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports letterSpacing style', () => {
@@ -343,8 +333,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
it('supports V4 style', () => {
@@ -369,8 +358,7 @@ describe('SsaTextParser', () => {
'Effect, Text\n' +
'Dialogue: 0,0:00:00.00,0:00:02.00,DefaultVCD, NTP,0000,0000,0000' +
',,{\\pos(400,570)}Test',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0,
- });
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
});
/**
diff --git a/test/text/text_engine_unit.js b/test/text/text_engine_unit.js
index 46c6606de1..87059c5b8c 100644
--- a/test/text/text_engine_unit.js
+++ b/test/text/text_engine_unit.js
@@ -51,7 +51,10 @@ describe('TextEngine', () => {
TextEngine.registerParser(dummyMimeType, mockParserPlugIn);
textEngine = new TextEngine(mockDisplayer);
- textEngine.initParser(dummyMimeType, false);
+ textEngine.initParser(
+ dummyMimeType,
+ /* sequenceMode= */ false,
+ /* segmentRelativeVttTiming= */ false);
});
afterEach(() => {
@@ -96,7 +99,7 @@ describe('TextEngine', () => {
await textEngine.appendBuffer(dummyData, 0, 3);
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
dummyData,
- {periodStart: 0, segmentStart: 0, segmentEnd: 3},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 3, vttOffset: 0},
]);
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
@@ -111,7 +114,7 @@ describe('TextEngine', () => {
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
dummyData,
- {periodStart: 0, segmentStart: 3, segmentEnd: 5},
+ {periodStart: 0, segmentStart: 3, segmentEnd: 5, vttOffset: 0},
]);
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
@@ -263,7 +266,7 @@ describe('TextEngine', () => {
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
dummyData,
- {periodStart: 0, segmentStart: 0, segmentEnd: 3},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 3, vttOffset: 0},
]);
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
[
@@ -277,7 +280,7 @@ describe('TextEngine', () => {
expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
dummyData,
- {periodStart: 4, segmentStart: 4, segmentEnd: 7},
+ {periodStart: 4, segmentStart: 4, segmentEnd: 7, vttOffset: 4},
]);
expect(mockDisplayer.appendSpy).toHaveBeenCalledOnceMoreWith([
[
@@ -286,6 +289,38 @@ describe('TextEngine', () => {
],
]);
});
+
+ it('vttOffset when segmentRelativeVttTiming is set', async () => {
+ textEngine.initParser(
+ dummyMimeType,
+ /* sequenceMode= */ false,
+ /* segmentRelativeVttTiming= */ true);
+
+ mockParseMedia.and.callFake((data, time) => {
+ return [
+ createFakeCue(time.periodStart + 0,
+ time.periodStart + 1),
+ createFakeCue(time.periodStart + 2,
+ time.periodStart + 3),
+ ];
+ });
+
+ await textEngine.appendBuffer(dummyData, 0, 3);
+
+ expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
+ dummyData,
+ {periodStart: 0, segmentStart: 0, segmentEnd: 3, vttOffset: 0},
+ ]);
+
+ textEngine.setTimestampOffset(8);
+ await textEngine.appendBuffer(dummyData, 4, 7);
+
+ // vttOffset should equal segmentStart instead of periodStart
+ expect(mockParseMedia).toHaveBeenCalledOnceMoreWith([
+ dummyData,
+ {periodStart: 8, segmentStart: 4, segmentEnd: 7, vttOffset: 4},
+ ]);
+ });
});
describe('bufferStart/bufferEnd', () => {
diff --git a/test/text/ttml_text_parser_unit.js b/test/text/ttml_text_parser_unit.js
index 2c36ba75df..d1fda62b2b 100644
--- a/test/text/ttml_text_parser_unit.js
+++ b/test/text/ttml_text_parser_unit.js
@@ -22,14 +22,14 @@ describe('TtmlTextParser', () => {
it('supports no cues', () => {
verifyHelper([],
'',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{});
});
it('supports empty text string', () => {
verifyHelper([],
'',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{});
});
@@ -37,7 +37,7 @@ describe('TtmlTextParser', () => {
verifyHelper(
[],
' \r\n
',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{});
});
@@ -64,7 +64,7 @@ describe('TtmlTextParser', () => {
},
],
'' + ttBody + '',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{startTime: 62.03, endTime: 62.05});
// When xml:space="preserve", take them into account.
verifyHelper(
@@ -90,7 +90,7 @@ describe('TtmlTextParser', () => {
},
],
'' + ttBody + '',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{startTime: 62.03, endTime: 62.05});
// The default value for xml:space is "default".
verifyHelper(
@@ -106,7 +106,7 @@ describe('TtmlTextParser', () => {
},
],
'' + ttBody + '',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{startTime: 62.03, endTime: 62.05});
// Any other value is rejected as an error.
@@ -137,7 +137,7 @@ describe('TtmlTextParser', () => {
},
],
'' + ttBody + '',
- {periodStart: 0, segmentStart: 0, segmentEnd: 0},
+ {periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0},
{startTime: 62.03, endTime: 62.05});
});
@@ -207,7 +207,7 @@ describe('TtmlTextParser', () => {
'First cue
Second cue' +
'
' +
'' +
'