diff --git a/demo/common/message_ids.js b/demo/common/message_ids.js index c938b82673..767382aad5 100644 --- a/demo/common/message_ids.js +++ b/demo/common/message_ids.js @@ -206,6 +206,7 @@ shakaDemo.MessageIds = { IGNORE_HLS_IMAGE_FAILURES: 'DEMO_IGNORE_HLS_IMAGE_FAILURES', IGNORE_HLS_TEXT_FAILURES: 'DEMO_IGNORE_HLS_TEXT_FAILURES', IGNORE_MANIFEST_PROGRAM_DATE_TIME: 'DEMO_IGNORE_MANIFEST_PROGRAM_DATE_TIME', + IGNORE_MANIFEST_TIMESTAMPS_IN_SEGMENTS_MODE: 'DEMO_IGNORE_MANIFEST_TIMESTAMPS_IN_SEGMENTS_MODE', IGNORE_MIN_BUFFER_TIME: 'DEMO_IGNORE_MIN_BUFFER_TIME', IGNORE_TEXT_FAILURES: 'DEMO_IGNORE_TEXT_FAILURES', INACCURATE_MANIFEST_TOLERANCE: 'DEMO_INACCURATE_MANIFEST_TOLERANCE', diff --git a/demo/config.js b/demo/config.js index b72ab6293c..7b0350c7c0 100644 --- a/demo/config.js +++ b/demo/config.js @@ -226,6 +226,8 @@ shakaDemo.Config = class { 'manifest.hls.liveSegmentsDelay') .addBoolInput_(MessageIds.HLS_SEQUENCE_MODE, 'manifest.hls.sequenceMode') + .addBoolInput_(MessageIds.IGNORE_MANIFEST_TIMESTAMPS_IN_SEGMENTS_MODE, + 'manifest.hls.ignoreManifestTimestampsInSegmentsMode') .addNumberInput_(MessageIds.AVAILABILITY_WINDOW_OVERRIDE, 'manifest.availabilityWindowOverride', /* canBeDecimal= */ true, diff --git a/demo/locales/en.json b/demo/locales/en.json index 8e529baeab..63f6addcc3 100644 --- a/demo/locales/en.json +++ b/demo/locales/en.json @@ -110,6 +110,7 @@ "DEMO_IMA_MANIFEST_TYPE": "Manifest type (for DAI Content)", "DEMO_IMA_VIDEO_ID": "Video ID (for VOD DAI Content)", "DEMO_IGNORE_MANIFEST_PROGRAM_DATE_TIME": "Ignore Program Date Time from manifest", + "DEMO_IGNORE_MANIFEST_TIMESTAMPS_IN_SEGMENTS_MODE": "Ignore Manifest Timestamps in Segments Mode", "DEMO_IGNORE_MIN_BUFFER_TIME": "Ignore Min Buffer Time", "DEMO_IGNORE_TEXT_FAILURES": "Ignore Text Stream Failures", "DEMO_INACCURATE_MANIFEST_TOLERANCE": "Inaccurate Manifest Tolerance", diff --git a/demo/locales/source.json b/demo/locales/source.json index 1069b210a5..aae51fd4ec 100644 --- a/demo/locales/source.json +++ b/demo/locales/source.json @@ -447,6 +447,10 @@ "description": "The name of a configuration value.", "message": "Ignore Program Date Time from manifest" }, + "DEMO_IGNORE_MANIFEST_TIMESTAMPS_IN_SEGMENTS_MODE": { + "description": "The name of a configuration value.", + "message": "Ignore Manifest Timestamps in Segments Mode" + }, "DEMO_IGNORE_MIN_BUFFER_TIME": { "description": "The name of a configuration value.", "message": "Ignore Min Buffer Time" diff --git a/externs/shaka/manifest.js b/externs/shaka/manifest.js index 4f8d30ed50..8afef71fcd 100644 --- a/externs/shaka/manifest.js +++ b/externs/shaka/manifest.js @@ -19,6 +19,7 @@ * offlineSessionIds: !Array., * minBufferTime: number, * sequenceMode: boolean, + * ignoreManifestTimestampsInSegmentsMode: boolean, * type: string * }} * @@ -77,6 +78,14 @@ * @property {boolean} sequenceMode * If true, we will append the media segments using sequence mode; that is to * say, ignoring any timestamps inside the media files. + * @property {boolean} ignoreManifestTimestampsInSegmentsMode + * If true, don't adjust the timestamp offset to account for manifest + * segment durations being out of sync with segment durations. In other + * words, assume that there are no gaps in the segments when appending + * to the SourceBuffer, even if the manifest and segment times disagree. + * Only applies when sequenceMode is false, and only for HLS + * streams. + * Defaults to false. * @property {string} type * Indicates the type of the manifest. It can be 'HLS' or * 'DASH'. diff --git a/externs/shaka/player.js b/externs/shaka/player.js index e432ec9c0f..b94c671cdd 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -841,7 +841,8 @@ shaka.extern.DashManifestConfiguration; * mediaPlaylistFullMimeType: string, * useSafariBehaviorForLive: boolean, * liveSegmentsDelay: number, - * sequenceMode: boolean + * sequenceMode: boolean, + * ignoreManifestTimestampsInSegmentsMode: boolean * }} * * @property {boolean} ignoreTextStreamFailures @@ -887,6 +888,13 @@ shaka.extern.DashManifestConfiguration; * "sequence mode" (ignoring their internal timestamps). * Defaults to true except on WebOS 3, Tizen 2, * Tizen 3 and PlayStation 4 whose default value is false. + * @property {boolean} ignoreManifestTimestampsInSegmentsMode + * If true, don't adjust the timestamp offset to account for manifest + * segment durations being out of sync with segment durations. In other + * words, assume that there are no gaps in the segments when appending + * to the SourceBuffer, even if the manifest and segment times disagree. + * Only applies when sequenceMode is false. + * Defaults to false. * @exportDoc */ shaka.extern.HlsManifestConfiguration; diff --git a/lib/dash/dash_parser.js b/lib/dash/dash_parser.js index 1b6e0c3eb1..69c07c091c 100644 --- a/lib/dash/dash_parser.js +++ b/lib/dash/dash_parser.js @@ -495,6 +495,7 @@ shaka.dash.DashParser = class { offlineSessionIds: [], minBufferTime: minBufferTime || 0, sequenceMode: this.config_.dash.sequenceMode, + ignoreManifestTimestampsInSegmentsMode: false, type: shaka.media.ManifestParser.DASH, }; diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index d1ba3ff6d3..56b3f3d317 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -761,6 +761,8 @@ shaka.hls.HlsParser = class { offlineSessionIds: [], minBufferTime: 0, sequenceMode: this.config_.hls.sequenceMode, + ignoreManifestTimestampsInSegmentsMode: + this.config_.hls.ignoreManifestTimestampsInSegmentsMode, type: shaka.media.ManifestParser.HLS, }; this.playerInterface_.makeTextStreamsForClosedCaptions(this.manifest_); diff --git a/lib/media/media_source_engine.js b/lib/media/media_source_engine.js index c6e3a8c41e..610d6fc77c 100644 --- a/lib/media/media_source_engine.js +++ b/lib/media/media_source_engine.js @@ -127,6 +127,9 @@ shaka.media.MediaSourceEngine = class { /** @private {string} */ this.manifestType_ = shaka.media.ManifestParser.UNKNOWN; + /** @private {boolean} */ + this.ignoreManifestTimestampsInSegmentsMode_ = false; + /** @private {!shaka.util.PublicPromise.} */ this.textSequenceModeOffset_ = new shaka.util.PublicPromise(); } @@ -351,17 +354,25 @@ shaka.media.MediaSourceEngine = class { * sequence. * @param {string=} manifestType * Indicates the type of the manifest. + * @param {boolean=} ignoreManifestTimestampsInSegmentsMode + * If true, don't adjust the timestamp offset to account for manifest + * segment durations being out of sync with segment durations. In other + * words, assume that there are no gaps in the segments when appending + * to the SourceBuffer, even if the manifest and segment times disagree. * * @return {!Promise} */ async init(streamsByType, sequenceMode=false, - manifestType=shaka.media.ManifestParser.UNKNOWN) { + manifestType=shaka.media.ManifestParser.UNKNOWN, + ignoreManifestTimestampsInSegmentsMode=false) { const ContentType = shaka.util.ManifestParserUtils.ContentType; await this.mediaSourceOpen_; this.sequenceMode_ = sequenceMode; this.manifestType_ = manifestType; + this.ignoreManifestTimestampsInSegmentsMode_ = + ignoreManifestTimestampsInSegmentsMode; for (const contentType of streamsByType.keys()) { const stream = streamsByType.get(contentType); @@ -614,7 +625,8 @@ shaka.media.MediaSourceEngine = class { } const attemptTimestampOffsetCalculation = !this.sequenceMode_ && - this.manifestType_ == shaka.media.ManifestParser.HLS; + this.manifestType_ == shaka.media.ManifestParser.HLS && + !this.ignoreManifestTimestampsInSegmentsMode_; let timestampOffset = this.sourceBuffers_[contentType].timestampOffset; diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index 4c747d1f89..64fccc4365 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -812,7 +812,9 @@ shaka.media.StreamingEngine = class { await mediaSourceEngine.init(streamsByType, this.manifest_.sequenceMode, - this.manifest_.type); + this.manifest_.type, + this.manifest_.ignoreManifestTimestampsInSegmentsMode, + ); this.destroyer_.ensureNotDestroyed(); this.updateDuration(); diff --git a/lib/offline/manifest_converter.js b/lib/offline/manifest_converter.js index 1fd37d1e07..cd6502e39e 100644 --- a/lib/offline/manifest_converter.js +++ b/lib/offline/manifest_converter.js @@ -89,6 +89,7 @@ shaka.offline.ManifestConverter = class { textStreams: textStreams, imageStreams: imageStreams, sequenceMode: manifestDB.sequenceMode || false, + ignoreManifestTimestampsInSegmentsMode: false, type: manifestDB.type || shaka.media.ManifestParser.UNKNOWN, }; } diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 8c3a282907..6602d3bc83 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -150,6 +150,7 @@ shaka.util.PlayerConfiguration = class { useSafariBehaviorForLive: true, liveSegmentsDelay: 3, sequenceMode: supportsSequenceMode, + ignoreManifestTimestampsInSegmentsMode: false, }, }; diff --git a/test/media/playhead_unit.js b/test/media/playhead_unit.js index 6ab1abd44d..e09ddf3713 100644 --- a/test/media/playhead_unit.js +++ b/test/media/playhead_unit.js @@ -134,6 +134,7 @@ describe('Playhead', () => { minBufferTime: 10, offlineSessionIds: [], sequenceMode: false, + ignoreManifestTimestampsInSegmentsMode: false, type: 'UNKNOWN', }; diff --git a/test/media/streaming_engine_integration.js b/test/media/streaming_engine_integration.js index fcfe98c8ed..1a0b10a6bf 100644 --- a/test/media/streaming_engine_integration.js +++ b/test/media/streaming_engine_integration.js @@ -594,6 +594,7 @@ describe('StreamingEngine', () => { textStreams: [], imageStreams: [], sequenceMode: false, + ignoreManifestTimestampsInSegmentsMode: false, type: 'UNKNOWN', variants: [{ id: 1, diff --git a/test/media/streaming_engine_unit.js b/test/media/streaming_engine_unit.js index e6100ffaff..ab91da5dbb 100644 --- a/test/media/streaming_engine_unit.js +++ b/test/media/streaming_engine_unit.js @@ -520,7 +520,8 @@ describe('StreamingEngine', () => { expectedMseInit.set(ContentType.TEXT, textStream); expect(mediaSourceEngine.init).toHaveBeenCalledWith(expectedMseInit, - /** sequenceMode= */ false, /** manifestType= */ 'UNKNOWN'); + /** sequenceMode= */ false, /** manifestType= */ 'UNKNOWN', + /** ignoreManifestTimestampsInSegmentsMode= */ false); expect(mediaSourceEngine.init).toHaveBeenCalledTimes(1); expect(mediaSourceEngine.setDuration).toHaveBeenCalledTimes(1); diff --git a/test/test/util/manifest_generator.js b/test/test/util/manifest_generator.js index 162ce92632..959c294119 100644 --- a/test/test/util/manifest_generator.js +++ b/test/test/util/manifest_generator.js @@ -97,6 +97,8 @@ shaka.test.ManifestGenerator.Manifest = class { this.minBufferTime = 0; /** @type {boolean} */ this.sequenceMode = false; + /** @type {boolean} */ + this.ignoreManifestTimestampsInSegmentsMode = false; /** @type {string} */ this.type = 'UNKNOWN'; diff --git a/test/test/util/streaming_engine_util.js b/test/test/util/streaming_engine_util.js index 4c95ccf627..e8a41e0cae 100644 --- a/test/test/util/streaming_engine_util.js +++ b/test/test/util/streaming_engine_util.js @@ -284,6 +284,7 @@ shaka.test.StreamingEngineUtil = class { textStreams: [], imageStreams: [], sequenceMode: false, + ignoreManifestTimestampsInSegmentsMode: false, type: 'UNKNOWN', };