diff --git a/demo/common/message_ids.js b/demo/common/message_ids.js index 187e13de96..883dce6331 100644 --- a/demo/common/message_ids.js +++ b/demo/common/message_ids.js @@ -174,6 +174,7 @@ shakaDemo.MessageIds = { CMCD_SECTION_HEADER: 'DEMO_CMCD_SECTION_HEADER', CONNECTION_TIMEOUT: 'DEMO_CONNECTION_TIMEOUT', CONTENT_ID: 'DEMO_CONTENT_ID', + DASH_SEQUENCE_MODE: 'DEMO_DASH_SEQUENCE_MODE', DEFAULT_AUDIO_CODEC: 'DEMO_DEFAULT_AUDIO_CODEC', DEFAULT_PRESENTATION_DELAY: 'DEMO_DEFAULT_PRESENTATION_DELAY', DEFAULT_VIDEO_CODEC: 'DEMO_DEFAULT_VIDEO_CODEC', @@ -195,6 +196,7 @@ shakaDemo.MessageIds = { FORCE_TRANSMUX: 'DEMO_FORCE_TRANSMUX', FUZZ_FACTOR: 'DEMO_FUZZ_FACTOR', GAP_DETECTION_THRESHOLD: 'DEMO_GAP_DETECTION_THRESHOLD', + HLS_SEQUENCE_MODE: 'DEMO_HLS_SEQUENCE_MODE', IGNORE_DASH_EMPTY_ADAPTATION_SET: 'DEMO_IGNORE_DASH_EMPTY_ADAPTATION_SET', IGNORE_DASH_DRM: 'DEMO_IGNORE_DASH_DRM', IGNORE_DASH_MAX_SEGMENT_DURATION: 'DEMO_IGNORE_DASH_MAX_SEGMENT_DURATION', @@ -255,7 +257,6 @@ shakaDemo.MessageIds = { SAFE_SEEK_OFFSET: 'DEMO_SAFE_SEEK_OFFSET', SAFE_SKIP_DISTANCE: 'DEMO_SAFE_SKIP_DISTANCE', SEGMENT_RELATIVE_VTT_TIMING: 'DEMO_SEGMENT_RELATIVE_VTT_TIMING', - SEQUENCE_MODE: 'DEMO_SEQUENCE_MODE', 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 a5f6fb2426..365d75df38 100644 --- a/demo/config.js +++ b/demo/config.js @@ -224,6 +224,8 @@ shakaDemo.Config = class { 'manifest.hls.useSafariBehaviorForLive') .addNumberInput_(MessageIds.LIVE_SEGMENTS_DELAY, 'manifest.hls.liveSegmentsDelay') + .addBoolInput_(MessageIds.HLS_SEQUENCE_MODE, + 'manifest.hls.sequenceMode') .addNumberInput_(MessageIds.AVAILABILITY_WINDOW_OVERRIDE, 'manifest.availabilityWindowOverride', /* canBeDecimal= */ true, @@ -239,7 +241,7 @@ shakaDemo.Config = class { /* canBeDecimal= */ false, /* canBeZero= */ false, /* canBeUnset= */ true) - .addBoolInput_(MessageIds.SEQUENCE_MODE, + .addBoolInput_(MessageIds.DASH_SEQUENCE_MODE, 'manifest.dash.sequenceMode') .addBoolInput_(MessageIds.DISABLE_AUDIO, 'manifest.disableAudio') diff --git a/demo/locales/en.json b/demo/locales/en.json index 8a90fa8499..a597111cd4 100644 --- a/demo/locales/en.json +++ b/demo/locales/en.json @@ -54,6 +54,7 @@ "DEMO_CUSTOM_INTRO_TWO": "Press the button below to add a custom asset.", "DEMO_DASH": "DASH", "DEMO_DASH_IF": "DASH-IF", + "DEMO_DASH_SEQUENCE_MODE": "Enable DASH sequence mode", "DEMO_DEFAULT_AUDIO_CODEC": "Default Audio Codec", "DEMO_DEFAULT_PRESENTATION_DELAY": "Default Presentation Delay", "DEMO_DEFAULT_VIDEO_CODEC": "Default Video Codec", @@ -184,6 +185,7 @@ "DEMO_FAILURE_MISC": "Shaka Player failed to load! If you are using an ad blocker, try switching to compiled mode at the bottom of the page.", "DEMO_FAILURE_NO_BROWSER_SUPPORT": "Your browser is not supported!", "DEMO_HLS_FULL_MIME_TYPE": "Full Mime Type for Playing Media Playlists Directly", + "DEMO_HLS_SEQUENCE_MODE": "Enable HLS sequence mode", "DEMO_PLAY": "Play", "DEMO_PLAYREADY": "PlayReady DRM", "DEMO_PREFER_FORCED_SUBS": "Prefer Forced Subs", @@ -199,7 +201,6 @@ "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_SEQUENCE_MODE": "Enable sequence mode", "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 9abcd780d5..942c53f00b 100644 --- a/demo/locales/source.json +++ b/demo/locales/source.json @@ -219,6 +219,10 @@ "description": "Text that describes an asset that comes from the Dash Industry Forum asset library.", "message": "[PROPER_NAME:DASH-IF]" }, + "DEMO_DASH_SEQUENCE_MODE": { + "description": "The name of a configuration value.", + "message": "Enable DASH sequence mode" + }, "DEMO_DEFAULT_AUDIO_CODEC": { "description": "The name of a configuration value.", "message": "Default Audio Codec" @@ -379,6 +383,10 @@ "description": "Text that describes an asset that is packaged in an HLS manifest.", "message": "[PROPER_NAME:HLS]" }, + "DEMO_HLS_SEQUENCE_MODE": { + "description": "The name of a configuration value.", + "message": "Enable HLS sequence mode" + }, "DEMO_HLS_TAB": { "description": "The header for a tab within the custom asset creation dialog.", "message": "[PROPER_NAME:HLS]" @@ -803,10 +811,6 @@ "description": "The name of a configuration value.", "message": "Enable segment-relative VTT Timing" }, - "DEMO_SEQUENCE_MODE": { - "description": "The name of a configuration value.", - "message": "Enable sequence mode" - }, "DEMO_SESSION_ID": { "description": "The name of a configuration value.", "message": "Session ID" diff --git a/externs/shaka/manifest.js b/externs/shaka/manifest.js index de5b124093..4f8d30ed50 100644 --- a/externs/shaka/manifest.js +++ b/externs/shaka/manifest.js @@ -18,7 +18,8 @@ * imageStreams: !Array., * offlineSessionIds: !Array., * minBufferTime: number, - * sequenceMode: boolean + * sequenceMode: boolean, + * type: string * }} * * @description @@ -76,6 +77,9 @@ * @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 {string} type + * Indicates the type of the manifest. It can be 'HLS' or + * 'DASH'. * * @exportDoc */ diff --git a/externs/shaka/offline.js b/externs/shaka/offline.js index 54a7534dad..43ede57a32 100644 --- a/externs/shaka/offline.js +++ b/externs/shaka/offline.js @@ -74,7 +74,8 @@ shaka.extern.StoredContent; * drmInfo: ?shaka.extern.DrmInfo, * appMetadata: Object, * isIncomplete: (boolean|undefined), - * sequenceMode: (boolean|undefined) + * sequenceMode: (boolean|undefined), + * type: (string|undefined) * }} * * @property {number} creationTime @@ -102,6 +103,9 @@ shaka.extern.StoredContent; * @property {(boolean|undefined)} sequenceMode * If true, we will append the media segments using sequence mode; that is to * say, ignoring any timestamps inside the media files. + * @property {(string|undefined)} type + * Indicates the type of the manifest. It can be 'HLS' or + * 'DASH'. */ shaka.extern.ManifestDB; diff --git a/externs/shaka/player.js b/externs/shaka/player.js index a7f41e501c..e9e10ada89 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -482,13 +482,13 @@ shaka.extern.MetadataRawFrame; /** * @typedef {{ * key: string, - * data: (ArrayBuffer|string), + * data: (ArrayBuffer|string|number), * description: string * }} * * @description metadata frame parsed. * @property {string} key - * @property {ArrayBuffer|string} data + * @property {ArrayBuffer|string|number} data * @property {string} description * @exportDoc */ @@ -840,7 +840,8 @@ shaka.extern.DashManifestConfiguration; * ignoreManifestProgramDateTime: boolean, * mediaPlaylistFullMimeType: string, * useSafariBehaviorForLive: boolean, - * liveSegmentsDelay: number + * liveSegmentsDelay: number, + * sequenceMode: boolean * }} * * @property {boolean} ignoreTextStreamFailures @@ -881,6 +882,11 @@ shaka.extern.DashManifestConfiguration; * The default presentation delay will be calculated as a number of segments. * This is the number of segments for this calculation.. * Defaults to 3. + * @property {boolean} sequenceMode + * If true, the media segments are appended to the SourceBuffer in + * "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. * @exportDoc */ shaka.extern.HlsManifestConfiguration; diff --git a/lib/dash/dash_parser.js b/lib/dash/dash_parser.js index 71ec5416e4..5b85b4b62c 100644 --- a/lib/dash/dash_parser.js +++ b/lib/dash/dash_parser.js @@ -499,6 +499,7 @@ shaka.dash.DashParser = class { offlineSessionIds: [], minBufferTime: minBufferTime || 0, sequenceMode: this.config_.dash.sequenceMode, + type: shaka.media.ManifestParser.DASH, }; // We only need to do clock sync when we're using presentation start diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 21b07f0db8..2ddcfb4ffa 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -758,7 +758,8 @@ shaka.hls.HlsParser = class { imageStreams, offlineSessionIds: [], minBufferTime: 0, - sequenceMode: true, + sequenceMode: this.config_.hls.sequenceMode, + type: shaka.media.ManifestParser.HLS, }; this.playerInterface_.makeTextStreamsForClosedCaptions(this.manifest_); } @@ -3730,13 +3731,9 @@ shaka.hls.HlsParser.PresentationType_ = { LIVE: 'LIVE', }; -if (!shaka.util.Platform.isTizen3() && - !shaka.util.Platform.isTizen2() && - !shaka.util.Platform.isWebOS3()) { - shaka.media.ManifestParser.registerParserByExtension( - 'm3u8', () => new shaka.hls.HlsParser()); - shaka.media.ManifestParser.registerParserByMime( - 'application/x-mpegurl', () => new shaka.hls.HlsParser()); - shaka.media.ManifestParser.registerParserByMime( - 'application/vnd.apple.mpegurl', () => new shaka.hls.HlsParser()); -} +shaka.media.ManifestParser.registerParserByExtension( + 'm3u8', () => new shaka.hls.HlsParser()); +shaka.media.ManifestParser.registerParserByMime( + 'application/x-mpegurl', () => new shaka.hls.HlsParser()); +shaka.media.ManifestParser.registerParserByMime( + 'application/vnd.apple.mpegurl', () => new shaka.hls.HlsParser()); diff --git a/lib/media/manifest_parser.js b/lib/media/manifest_parser.js index 0463d79bee..94d2f2b894 100644 --- a/lib/media/manifest_parser.js +++ b/lib/media/manifest_parser.js @@ -250,6 +250,24 @@ shaka.media.ManifestParser = class { }; +/** + * @const {string} + */ +shaka.media.ManifestParser.HLS = 'HLS'; + + +/** + * @const {string} + */ +shaka.media.ManifestParser.DASH = 'DASH'; + + +/** + * @const {string} + */ +shaka.media.ManifestParser.UNKNOWN = 'UNKNOWN'; + + /** * Contains the parser factory functions indexed by MIME type. * diff --git a/lib/media/media_source_engine.js b/lib/media/media_source_engine.js index 81bb8fafe2..bab68e038e 100644 --- a/lib/media/media_source_engine.js +++ b/lib/media/media_source_engine.js @@ -12,6 +12,7 @@ goog.require('shaka.media.Capabilities'); goog.require('shaka.media.ContentWorkarounds'); goog.require('shaka.media.ClosedCaptionParser'); goog.require('shaka.media.IClosedCaptionParser'); +goog.require('shaka.media.ManifestParser'); goog.require('shaka.media.SegmentReference'); goog.require('shaka.media.TimeRangesUtils'); goog.require('shaka.text.TextEngine'); @@ -25,6 +26,8 @@ goog.require('shaka.util.IDestroyable'); goog.require('shaka.util.Id3Utils'); goog.require('shaka.util.ManifestParserUtils'); goog.require('shaka.util.MimeUtils'); +goog.require('shaka.util.Mp4BoxParsers'); +goog.require('shaka.util.Mp4Parser'); goog.require('shaka.util.Platform'); goog.require('shaka.util.PublicPromise'); goog.require('shaka.util.TsParser'); @@ -121,6 +124,9 @@ shaka.media.MediaSourceEngine = class { /** @private {boolean} */ this.sequenceMode_ = false; + /** @private {string} */ + this.manifestType_ = shaka.media.ManifestParser.UNKNOWN; + /** @private {!shaka.util.PublicPromise.} */ this.textSequenceModeOffset_ = new shaka.util.PublicPromise(); } @@ -343,15 +349,19 @@ shaka.media.MediaSourceEngine = class { * @param {boolean=} sequenceMode * If true, the media segments are appended to the SourceBuffer in strict * sequence. + * @param {string=} manifestType + * Indicates the type of the manifest. * * @return {!Promise} */ - async init(streamsByType, sequenceMode=false) { + async init(streamsByType, sequenceMode=false, + manifestType=shaka.media.ManifestParser.UNKNOWN) { const ContentType = shaka.util.ManifestParserUtils.ContentType; await this.mediaSourceOpen_; this.sequenceMode_ = sequenceMode; + this.manifestType_ = manifestType; for (const contentType of streamsByType.keys()) { const stream = streamsByType.get(contentType); @@ -364,11 +374,15 @@ shaka.media.MediaSourceEngine = class { if (contentType == ContentType.TEXT) { this.reinitText(mimeType, sequenceMode); } else { - const forceTransmux = this.config_.forceTransmux; + let needTransmux = this.config_.forceTransmux; + if (!shaka.media.Capabilities.isTypeSupported(mimeType) || + (!sequenceMode && + shaka.media.MediaSourceEngine.RAW_FORMATS.includes(mimeType))) { + needTransmux = true; + } const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine; - if ((forceTransmux || - !shaka.media.Capabilities.isTypeSupported(mimeType)) && - TransmuxerEngine.isSupported(mimeType, contentType)) { + if (needTransmux && + TransmuxerEngine.isSupported(mimeType, contentType)) { const transmuxerPlugin = TransmuxerEngine.findTransmuxer(mimeType); if (transmuxerPlugin) { const transmuxer = transmuxerPlugin(); @@ -584,6 +598,9 @@ shaka.media.MediaSourceEngine = class { return; } + const attemptTimestampOffsetCalculation = !this.sequenceMode_ && + this.manifestType_ == shaka.media.ManifestParser.HLS; + let timestampOffset = this.sourceBuffers_[contentType].timestampOffset; const uint8ArrayData = shaka.util.BufferUtils.toUint8(data); @@ -591,33 +608,103 @@ shaka.media.MediaSourceEngine = class { if (this.transmuxers_[contentType]) { mimeType = this.transmuxers_[contentType].getOrginalMimeType(); } - if (shaka.util.TsParser.probe(uint8ArrayData)) { + if (shaka.media.MediaSourceEngine.RAW_FORMATS.includes(mimeType)) { + const frames = shaka.util.Id3Utils.getID3Frames(uint8ArrayData); + if (frames.length && reference) { + if (attemptTimestampOffsetCalculation) { + const metadataTimestamp = frames.find((frame) => { + return frame.description === + 'com.apple.streaming.transportStreamTimestamp'; + }); + if (metadataTimestamp && metadataTimestamp.data) { + const calculatedTimestampOffset = reference.startTime - + Math.round(metadataTimestamp.data) / 1000; + const timestampOffsetDifference = + Math.abs(timestampOffset - calculatedTimestampOffset); + if (timestampOffsetDifference >= 0.01 || seeked || adaptation) { + timestampOffset = calculatedTimestampOffset; + this.enqueueOperation_(contentType, () => + this.abort_(contentType)); + this.enqueueOperation_( + contentType, + () => this.setTimestampOffset_( + contentType, timestampOffset)); + } + } + } + /** @private {shaka.extern.ID3Metadata} */ + const metadata = { + cueTime: reference.startTime, + data: uint8ArrayData, + frames: frames, + dts: reference.startTime, + pts: reference.startTime, + }; + this.onMetadata_([metadata], /* offset= */ 0, reference.endTime); + } + } else if (attemptTimestampOffsetCalculation && + mimeType.includes('/mp4') && + reference && reference.timestampOffset == 0 && + reference.initSegmentReference && + reference.initSegmentReference.timescale) { + const timescale = reference.initSegmentReference.timescale; + if (!isNaN(timescale)) { + const Mp4Parser = shaka.util.Mp4Parser; + let startTime = 0; + let parsedMedia = false; + new Mp4Parser() + .box('moof', Mp4Parser.children) + .box('traf', Mp4Parser.children) + .fullBox('tfdt', (box) => { + goog.asserts.assert( + box.version == 0 || box.version == 1, + 'TFDT version can only be 0 or 1'); + + const parsedTFDTBox = shaka.util.Mp4BoxParsers.parseTFDT( + box.reader, box.version); + const baseTime = parsedTFDTBox.baseMediaDecodeTime; + startTime = baseTime / timescale; + parsedMedia = true; + box.parser.stop(); + }).parse(data, /* partialOkay= */ true); + if (parsedMedia) { + const calculatedTimestampOffset = reference.startTime - startTime; + const timestampOffsetDifference = + Math.abs(timestampOffset - calculatedTimestampOffset); + if (timestampOffsetDifference >= 0.01 || seeked || adaptation) { + timestampOffset = calculatedTimestampOffset; + this.enqueueOperation_(contentType, () => this.abort_(contentType)); + this.enqueueOperation_( + contentType, + () => this.setTimestampOffset_(contentType, timestampOffset)); + } + } + } + } else if (shaka.util.TsParser.probe(uint8ArrayData)) { const tsParser = new shaka.util.TsParser().parse(uint8ArrayData); - // The SourceBuffer timestampOffset may or may not be set yet, so this is - // the timestamp offset that would eventually compute for this segment - // either way. - timestampOffset = - reference.startTime - (tsParser.getStartTime()[contentType] || 0); + const startTime = tsParser.getStartTime()[contentType]; + if (startTime != null) { + const calculatedTimestampOffset = reference.startTime - startTime; + const timestampOffsetDifference = + Math.abs(timestampOffset - calculatedTimestampOffset); + if (timestampOffsetDifference >= 0.01 || seeked || adaptation) { + timestampOffset = calculatedTimestampOffset; + // The SourceBuffer timestampOffset may or may not be set yet, + // so this is the timestamp offset that would eventually compute + // for this segment either way. + if (attemptTimestampOffsetCalculation) { + this.enqueueOperation_(contentType, () => this.abort_(contentType)); + this.enqueueOperation_( + contentType, + () => this.setTimestampOffset_(contentType, timestampOffset)); + } + } + } const metadata = tsParser.getMetadata(); if (metadata.length) { this.onMetadata_(metadata, timestampOffset, reference ? reference.endTime : null); } - } else { - if (shaka.media.MediaSourceEngine.RAW_FORMATS.includes(mimeType)) { - const frames = shaka.util.Id3Utils.getID3Frames(uint8ArrayData); - if (frames.length && reference) { - /** @private {shaka.extern.ID3Metadata} */ - const metadata = { - cueTime: reference.startTime, - data: uint8ArrayData, - frames: frames, - dts: reference.startTime, - pts: reference.startTime, - }; - this.onMetadata_([metadata], /* offset= */ 0, reference.endTime); - } - } } if (hasClosedCaptions && contentType == ContentType.VIDEO) { if (!this.textEngine_) { diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index f679704206..83f1dffd68 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -790,7 +790,9 @@ shaka.media.StreamingEngine = class { // Init MediaSourceEngine. const mediaSourceEngine = this.playerInterface_.mediaSourceEngine; - await mediaSourceEngine.init(streamsByType, this.manifest_.sequenceMode); + await mediaSourceEngine.init(streamsByType, + this.manifest_.sequenceMode, + this.manifest_.type); this.destroyer_.ensureNotDestroyed(); this.updateDuration(); diff --git a/lib/offline/manifest_converter.js b/lib/offline/manifest_converter.js index 14ebd48b90..1fd37d1e07 100644 --- a/lib/offline/manifest_converter.js +++ b/lib/offline/manifest_converter.js @@ -8,6 +8,7 @@ goog.provide('shaka.offline.ManifestConverter'); goog.require('goog.asserts'); goog.require('shaka.media.InitSegmentReference'); +goog.require('shaka.media.ManifestParser'); goog.require('shaka.media.PresentationTimeline'); goog.require('shaka.media.SegmentIndex'); goog.require('shaka.media.SegmentReference'); @@ -88,6 +89,7 @@ shaka.offline.ManifestConverter = class { textStreams: textStreams, imageStreams: imageStreams, sequenceMode: manifestDB.sequenceMode || false, + type: manifestDB.type || shaka.media.ManifestParser.UNKNOWN, }; } diff --git a/lib/offline/storage.js b/lib/offline/storage.js index b64e91f9ee..c7620b7e04 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -833,6 +833,7 @@ shaka.offline.Storage = class { appMetadata: metadata, isIncomplete: true, sequenceMode: manifest.sequenceMode, + type: manifest.type, }; return {manifestDB, toDownload}; diff --git a/lib/util/id3_utils.js b/lib/util/id3_utils.js index 86824c2f58..2878e11418 100644 --- a/lib/util/id3_utils.js +++ b/lib/util/id3_utils.js @@ -211,10 +211,26 @@ shaka.util.Id3Utils = class { } const text = StringUtils.fromUTF8( BufferUtils.toUint8(frame.data, 0, textEndIndex)); - const data = BufferUtils.toArrayBuffer( - frame.data.subarray(text.length + 1)); metadataFrame.description = text; - metadataFrame.data = data; + if (text == 'com.apple.streaming.transportStreamTimestamp') { + const data = frame.data.subarray(text.length + 1); + // timestamp is 33 bit expressed as a big-endian eight-octet number, + // with the upper 31 bits set to zero. + const pts33Bit = data[3] & 0x1; + let timestamp = + (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7]; + timestamp /= 45; + + if (pts33Bit) { + timestamp += 47721858.84; + } // 2^32 / 90 + + metadataFrame.data = timestamp; + } else { + const data = BufferUtils.toArrayBuffer( + frame.data.subarray(text.length + 1)); + metadataFrame.data = data; + } return metadataFrame; } else if (frame.type[0] === 'T') { /* diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index 5c630f4a78..a7122c392c 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -90,6 +90,14 @@ shaka.util.PlayerConfiguration = class { minHdcpVersion: '', }; + let supportsSequenceMode = true; + if (shaka.util.Platform.isTizen3() || + shaka.util.Platform.isTizen2() || + shaka.util.Platform.isWebOS3() || + shaka.util.Platform.isPS4()) { + supportsSequenceMode = false; + } + const manifest = { retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(), availabilityWindowOverride: NaN, @@ -141,6 +149,7 @@ shaka.util.PlayerConfiguration = class { 'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"', useSafariBehaviorForLive: true, liveSegmentsDelay: 3, + sequenceMode: supportsSequenceMode, }, }; diff --git a/test/dash/dash_parser_manifest_unit.js b/test/dash/dash_parser_manifest_unit.js index e0e0815085..1ba40cdc50 100644 --- a/test/dash/dash_parser_manifest_unit.js +++ b/test/dash/dash_parser_manifest_unit.js @@ -155,6 +155,7 @@ describe('DashParser Manifest', () => { ], shaka.test.ManifestGenerator.generate((manifest) => { manifest.sequenceMode = true; + manifest.type = shaka.media.ManifestParser.DASH; manifest.anyTimeline(); manifest.minBufferTime = 75; manifest.addPartialVariant((variant) => { @@ -239,6 +240,7 @@ describe('DashParser Manifest', () => { ], shaka.test.ManifestGenerator.generate((manifest) => { manifest.sequenceMode = false; + manifest.type = shaka.media.ManifestParser.DASH; manifest.anyTimeline(); manifest.minBufferTime = 75; manifest.addPartialVariant((variant) => { diff --git a/test/hls/hls_parser_unit.js b/test/hls/hls_parser_unit.js index 95f0df16db..70fa06083f 100644 --- a/test/hls/hls_parser_unit.js +++ b/test/hls/hls_parser_unit.js @@ -40,6 +40,8 @@ describe('HlsParser', () => { let selfInitializingSegmentData; /** @type {!Uint8Array} */ let aes128Key; + /** @type {!boolean} */ + let sequenceMode; afterEach(() => { shaka.log.alwaysWarn = originalAlwaysWarn; @@ -65,6 +67,7 @@ describe('HlsParser', () => { fakeNetEngine = new shaka.test.FakeNetworkingEngine(); config = shaka.util.PlayerConfiguration.createDefault().manifest; + sequenceMode = config.hls.sequenceMode; onEventSpy = jasmine.createSpy('onEvent'); newDrmInfoSpy = jasmine.createSpy('newDrmInfo'); playerInterface = { @@ -169,7 +172,8 @@ describe('HlsParser', () => { ].join(''); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.language = 'en'; @@ -245,7 +249,8 @@ describe('HlsParser', () => { ].join(''); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.language = 'en'; @@ -317,7 +322,8 @@ describe('HlsParser', () => { stream.size(960, 540); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -355,7 +361,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', 'avc1.4d001e'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -385,7 +392,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', 'avc1'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -414,7 +422,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', 'avc1'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -446,7 +455,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', 'avc1'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -475,7 +485,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -510,7 +521,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -545,7 +557,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', ''); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -573,7 +586,8 @@ describe('HlsParser', () => { stream.mime('audio/aac', ''); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -600,7 +614,8 @@ describe('HlsParser', () => { stream.mime('audio/mpeg', ''); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -639,7 +654,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -676,7 +692,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -707,7 +724,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -737,7 +755,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', 'avc1,mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -767,7 +786,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', /** @type {?} */ (jasmine.any(String))); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -802,7 +822,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', /** @type {?} */ (jasmine.any(String))); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -833,7 +854,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', /** @type {?} */ (jasmine.any(String))); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -864,7 +886,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', /** @type {?} */ (jasmine.any(String))); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -916,7 +939,8 @@ describe('HlsParser', () => { stream.language = 'fr'; }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -959,7 +983,8 @@ describe('HlsParser', () => { stream.language = 'fr'; }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -1010,7 +1035,8 @@ describe('HlsParser', () => { ]; }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -1062,7 +1088,8 @@ describe('HlsParser', () => { 'public.accessibility.describes-music-and-sound', ]; }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -1127,7 +1154,8 @@ describe('HlsParser', () => { 'public.accessibility.describes-music-and-sound', ]; }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setResponseText('test:/master', master); @@ -1163,7 +1191,8 @@ describe('HlsParser', () => { stream.mime('video/mp4', 'avc1'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); // The extra parameters should be stripped by the parser. @@ -1221,7 +1250,8 @@ describe('HlsParser', () => { stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -1292,7 +1322,8 @@ describe('HlsParser', () => { stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -1354,7 +1385,8 @@ describe('HlsParser', () => { stream.kind = TextStreamKind.SUBTITLE; stream.mime('application/mp4', ''); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -1419,7 +1451,8 @@ describe('HlsParser', () => { stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -1482,7 +1515,8 @@ describe('HlsParser', () => { stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2094,7 +2128,8 @@ describe('HlsParser', () => { stream.language = 'en'; stream.mime('application/mp4', 'stpp.ttml.im1t'); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2145,7 +2180,8 @@ describe('HlsParser', () => { manifest.addPartialTextStream((stream) => { stream.mime('text/vtt', 'vtt'); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2189,7 +2225,8 @@ describe('HlsParser', () => { manifest.addPartialTextStream((stream) => { stream.kind = TextStreamKind.SUBTITLE; }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2258,7 +2295,8 @@ describe('HlsParser', () => { stream.segmentIndex = new shaka.media.SegmentIndex(segments); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2402,7 +2440,8 @@ describe('HlsParser', () => { manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.VIDEO); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2441,7 +2480,8 @@ describe('HlsParser', () => { manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.VIDEO); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2485,7 +2525,8 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -2571,7 +2612,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -2709,7 +2751,8 @@ describe('HlsParser', () => { stream.language = 'de'; }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -2843,7 +2886,8 @@ describe('HlsParser', () => { }); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -2884,7 +2928,8 @@ describe('HlsParser', () => { }); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -2922,7 +2967,8 @@ describe('HlsParser', () => { }); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -2957,7 +3003,8 @@ describe('HlsParser', () => { stream.addDrmInfo('org.w3.clearkey'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -2991,7 +3038,8 @@ describe('HlsParser', () => { stream.addDrmInfo('org.w3.clearkey'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -3027,7 +3075,8 @@ describe('HlsParser', () => { }); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setResponseText('test:/master', master); @@ -3059,7 +3108,8 @@ describe('HlsParser', () => { }); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setResponseText('test:/master', master); @@ -3088,7 +3138,8 @@ describe('HlsParser', () => { }); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setResponseText('test:/master', master); @@ -3115,7 +3166,8 @@ describe('HlsParser', () => { stream.addDrmInfo('org.w3.clearkey'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setResponseText('test:/master', master); @@ -3141,7 +3193,8 @@ describe('HlsParser', () => { stream.addDrmInfo('org.w3.clearkey'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setResponseText('test:/master', master); @@ -3179,7 +3232,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine.setHeaders( @@ -3652,7 +3706,8 @@ describe('HlsParser', () => { stream.language = 'fr'; }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -3702,7 +3757,8 @@ describe('HlsParser', () => { stream.size(1920, 1080); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); const actual = await testHlsParser(master, media, manifest, media2); @@ -3754,7 +3810,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); fakeNetEngine @@ -3941,7 +3998,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -3979,7 +4037,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -4018,7 +4077,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -4058,7 +4118,8 @@ describe('HlsParser', () => { stream.mime('audio/mp4', 'mp4a'); }); }); - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; }); await testHlsParser(master, media, manifest); @@ -4101,7 +4162,8 @@ describe('HlsParser', () => { ].join(''); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.VIDEO, (stream) => { @@ -4130,7 +4192,8 @@ describe('HlsParser', () => { parser.configure(config); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.AUDIO, (stream) => { @@ -4156,7 +4219,8 @@ describe('HlsParser', () => { parser.configure(config); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.AUDIO, (stream) => { @@ -4182,7 +4246,8 @@ describe('HlsParser', () => { parser.configure(config); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.addPartialStream(ContentType.AUDIO, (stream) => { @@ -4232,7 +4297,8 @@ describe('HlsParser', () => { ].join(''); const manifest = shaka.test.ManifestGenerator.generate((manifest) => { - manifest.sequenceMode = true; + manifest.sequenceMode = sequenceMode; + manifest.type = shaka.media.ManifestParser.HLS; manifest.anyTimeline(); manifest.addPartialVariant((variant) => { variant.language = 'en'; diff --git a/test/media/playhead_unit.js b/test/media/playhead_unit.js index 3ac0812fcc..6ab1abd44d 100644 --- a/test/media/playhead_unit.js +++ b/test/media/playhead_unit.js @@ -134,6 +134,7 @@ describe('Playhead', () => { minBufferTime: 10, offlineSessionIds: [], sequenceMode: false, + type: 'UNKNOWN', }; config = shaka.util.PlayerConfiguration.createDefault().streaming; diff --git a/test/media/streaming_engine_integration.js b/test/media/streaming_engine_integration.js index 2678798eb6..b95294c860 100644 --- a/test/media/streaming_engine_integration.js +++ b/test/media/streaming_engine_integration.js @@ -592,6 +592,7 @@ describe('StreamingEngine', () => { textStreams: [], imageStreams: [], sequenceMode: false, + type: 'UNKNOWN', variants: [{ id: 1, video: { diff --git a/test/media/streaming_engine_unit.js b/test/media/streaming_engine_unit.js index 67653c96bd..1c02851260 100644 --- a/test/media/streaming_engine_unit.js +++ b/test/media/streaming_engine_unit.js @@ -521,7 +521,7 @@ describe('StreamingEngine', () => { expectedMseInit.set(ContentType.TEXT, textStream); expect(mediaSourceEngine.init).toHaveBeenCalledWith(expectedMseInit, - /** sequenceMode= */ false); + /** sequenceMode= */ false, /** manifestType= */ 'UNKNOWN'); 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 52d05001da..162ce92632 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 {string} */ + this.type = 'UNKNOWN'; /** @type {shaka.extern.Manifest} */ const foo = this; diff --git a/test/test/util/streaming_engine_util.js b/test/test/util/streaming_engine_util.js index 95ec24066f..6366762f92 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, + type: 'UNKNOWN', }; /** @type {shaka.extern.Variant} */