From 4a4e0225feb0cba506eb87a87d2c5007c4606d72 Mon Sep 17 00:00:00 2001 From: Sweidan Omar Date: Wed, 30 Mar 2022 08:49:32 +0000 Subject: [PATCH] git-tfs-id: [http://tfs.userrendszerhaz.hu:8080/tfs/DefaultCollection]$/MediaCube;C33124 --- server/user.mediacube.gui/js/video.js | 1645 +++++++++++++++++-------- 1 file changed, 1154 insertions(+), 491 deletions(-) diff --git a/server/user.mediacube.gui/js/video.js b/server/user.mediacube.gui/js/video.js index 3a6f9d68..5151e7e4 100644 --- a/server/user.mediacube.gui/js/video.js +++ b/server/user.mediacube.gui/js/video.js @@ -1,6 +1,6 @@ /** * @license - * Video.js 7.17.0 + * Video.js 7.18.1 * Copyright Brightcove, Inc. * Available under Apache License Version 2.0 * @@ -16,7 +16,7 @@ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojs = factory()); }(this, (function () { 'use strict'; - var version$5 = "7.17.0"; + var version$5 = "7.18.1"; /** * An Object that contains lifecycle hooks as keys which point to an array @@ -3746,6 +3746,201 @@ var Set = window.Set ? window.Set : SetSham; + var keycode = createCommonjsModule(function (module, exports) { + // Source: http://jsfiddle.net/vWx8V/ + // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes + + /** + * Conenience method returns corresponding value for given keyName or keyCode. + * + * @param {Mixed} keyCode {Number} or keyName {String} + * @return {Mixed} + * @api public + */ + function keyCode(searchInput) { + // Keyboard Events + if (searchInput && 'object' === typeof searchInput) { + var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode; + if (hasKeyCode) searchInput = hasKeyCode; + } // Numbers + + + if ('number' === typeof searchInput) return names[searchInput]; // Everything else (cast to string) + + var search = String(searchInput); // check codes + + var foundNamedKey = codes[search.toLowerCase()]; + if (foundNamedKey) return foundNamedKey; // check aliases + + var foundNamedKey = aliases[search.toLowerCase()]; + if (foundNamedKey) return foundNamedKey; // weird character? + + if (search.length === 1) return search.charCodeAt(0); + return undefined; + } + /** + * Compares a keyboard event with a given keyCode or keyName. + * + * @param {Event} event Keyboard event that should be tested + * @param {Mixed} keyCode {Number} or keyName {String} + * @return {Boolean} + * @api public + */ + + + keyCode.isEventKey = function isEventKey(event, nameOrCode) { + if (event && 'object' === typeof event) { + var keyCode = event.which || event.keyCode || event.charCode; + + if (keyCode === null || keyCode === undefined) { + return false; + } + + if (typeof nameOrCode === 'string') { + // check codes + var foundNamedKey = codes[nameOrCode.toLowerCase()]; + + if (foundNamedKey) { + return foundNamedKey === keyCode; + } // check aliases + + + var foundNamedKey = aliases[nameOrCode.toLowerCase()]; + + if (foundNamedKey) { + return foundNamedKey === keyCode; + } + } else if (typeof nameOrCode === 'number') { + return nameOrCode === keyCode; + } + + return false; + } + }; + + exports = module.exports = keyCode; + /** + * Get by name + * + * exports.code['enter'] // => 13 + */ + + var codes = exports.code = exports.codes = { + 'backspace': 8, + 'tab': 9, + 'enter': 13, + 'shift': 16, + 'ctrl': 17, + 'alt': 18, + 'pause/break': 19, + 'caps lock': 20, + 'esc': 27, + 'space': 32, + 'page up': 33, + 'page down': 34, + 'end': 35, + 'home': 36, + 'left': 37, + 'up': 38, + 'right': 39, + 'down': 40, + 'insert': 45, + 'delete': 46, + 'command': 91, + 'left command': 91, + 'right command': 93, + 'numpad *': 106, + 'numpad +': 107, + 'numpad -': 109, + 'numpad .': 110, + 'numpad /': 111, + 'num lock': 144, + 'scroll lock': 145, + 'my computer': 182, + 'my calculator': 183, + ';': 186, + '=': 187, + ',': 188, + '-': 189, + '.': 190, + '/': 191, + '`': 192, + '[': 219, + '\\': 220, + ']': 221, + "'": 222 + }; // Helper aliases + + var aliases = exports.aliases = { + 'windows': 91, + '⇧': 16, + '⌥': 18, + '⌃': 17, + '⌘': 91, + 'ctl': 17, + 'control': 17, + 'option': 18, + 'pause': 19, + 'break': 19, + 'caps': 20, + 'return': 13, + 'escape': 27, + 'spc': 32, + 'spacebar': 32, + 'pgup': 33, + 'pgdn': 34, + 'ins': 45, + 'del': 46, + 'cmd': 91 + }; + /*! + * Programatically add the following + */ + // lower case chars + + for (i = 97; i < 123; i++) { + codes[String.fromCharCode(i)] = i - 32; + } // numbers + + + for (var i = 48; i < 58; i++) { + codes[i - 48] = i; + } // function keys + + + for (i = 1; i < 13; i++) { + codes['f' + i] = i + 111; + } // numpad keys + + + for (i = 0; i < 10; i++) { + codes['numpad ' + i] = i + 96; + } + /** + * Get by code + * + * exports.name[13] // => 'Enter' + */ + + + var names = exports.names = exports.title = {}; // title for backward compat + // Create reverse mapping + + for (i in codes) { + names[codes[i]] = i; + } // Add aliases + + + for (var alias in aliases) { + codes[alias] = aliases[alias]; + } + }); + keycode.code; + keycode.codes; + keycode.aliases; + keycode.names; + keycode.title; + /** * Player Component - Base class for all UI objects * @@ -4938,8 +5133,11 @@ _proto.handleKeyDown = function handleKeyDown(event) { if (this.player_) { // We only stop propagation here because we want unhandled events to fall - // back to the browser. - event.stopPropagation(); + // back to the browser. Exclude Tab for focus trapping. + if (!keycode.isEventKey(event, 'Tab')) { + event.stopPropagation(); + } + this.player_.handleKeyDown(event); } } @@ -5968,201 +6166,6 @@ trackToJson_: trackToJson_ }; - var keycode = createCommonjsModule(function (module, exports) { - // Source: http://jsfiddle.net/vWx8V/ - // http://stackoverflow.com/questions/5603195/full-list-of-javascript-keycodes - - /** - * Conenience method returns corresponding value for given keyName or keyCode. - * - * @param {Mixed} keyCode {Number} or keyName {String} - * @return {Mixed} - * @api public - */ - function keyCode(searchInput) { - // Keyboard Events - if (searchInput && 'object' === typeof searchInput) { - var hasKeyCode = searchInput.which || searchInput.keyCode || searchInput.charCode; - if (hasKeyCode) searchInput = hasKeyCode; - } // Numbers - - - if ('number' === typeof searchInput) return names[searchInput]; // Everything else (cast to string) - - var search = String(searchInput); // check codes - - var foundNamedKey = codes[search.toLowerCase()]; - if (foundNamedKey) return foundNamedKey; // check aliases - - var foundNamedKey = aliases[search.toLowerCase()]; - if (foundNamedKey) return foundNamedKey; // weird character? - - if (search.length === 1) return search.charCodeAt(0); - return undefined; - } - /** - * Compares a keyboard event with a given keyCode or keyName. - * - * @param {Event} event Keyboard event that should be tested - * @param {Mixed} keyCode {Number} or keyName {String} - * @return {Boolean} - * @api public - */ - - - keyCode.isEventKey = function isEventKey(event, nameOrCode) { - if (event && 'object' === typeof event) { - var keyCode = event.which || event.keyCode || event.charCode; - - if (keyCode === null || keyCode === undefined) { - return false; - } - - if (typeof nameOrCode === 'string') { - // check codes - var foundNamedKey = codes[nameOrCode.toLowerCase()]; - - if (foundNamedKey) { - return foundNamedKey === keyCode; - } // check aliases - - - var foundNamedKey = aliases[nameOrCode.toLowerCase()]; - - if (foundNamedKey) { - return foundNamedKey === keyCode; - } - } else if (typeof nameOrCode === 'number') { - return nameOrCode === keyCode; - } - - return false; - } - }; - - exports = module.exports = keyCode; - /** - * Get by name - * - * exports.code['enter'] // => 13 - */ - - var codes = exports.code = exports.codes = { - 'backspace': 8, - 'tab': 9, - 'enter': 13, - 'shift': 16, - 'ctrl': 17, - 'alt': 18, - 'pause/break': 19, - 'caps lock': 20, - 'esc': 27, - 'space': 32, - 'page up': 33, - 'page down': 34, - 'end': 35, - 'home': 36, - 'left': 37, - 'up': 38, - 'right': 39, - 'down': 40, - 'insert': 45, - 'delete': 46, - 'command': 91, - 'left command': 91, - 'right command': 93, - 'numpad *': 106, - 'numpad +': 107, - 'numpad -': 109, - 'numpad .': 110, - 'numpad /': 111, - 'num lock': 144, - 'scroll lock': 145, - 'my computer': 182, - 'my calculator': 183, - ';': 186, - '=': 187, - ',': 188, - '-': 189, - '.': 190, - '/': 191, - '`': 192, - '[': 219, - '\\': 220, - ']': 221, - "'": 222 - }; // Helper aliases - - var aliases = exports.aliases = { - 'windows': 91, - '⇧': 16, - '⌥': 18, - '⌃': 17, - '⌘': 91, - 'ctl': 17, - 'control': 17, - 'option': 18, - 'pause': 19, - 'break': 19, - 'caps': 20, - 'return': 13, - 'escape': 27, - 'spc': 32, - 'spacebar': 32, - 'pgup': 33, - 'pgdn': 34, - 'ins': 45, - 'del': 46, - 'cmd': 91 - }; - /*! - * Programatically add the following - */ - // lower case chars - - for (i = 97; i < 123; i++) { - codes[String.fromCharCode(i)] = i - 32; - } // numbers - - - for (var i = 48; i < 58; i++) { - codes[i - 48] = i; - } // function keys - - - for (i = 1; i < 13; i++) { - codes['f' + i] = i + 111; - } // numpad keys - - - for (i = 0; i < 10; i++) { - codes['numpad ' + i] = i + 96; - } - /** - * Get by code - * - * exports.name[13] // => 'Enter' - */ - - - var names = exports.names = exports.title = {}; // title for backward compat - // Create reverse mapping - - for (i in codes) { - names[codes[i]] = i; - } // Add aliases - - - for (var alias in aliases) { - codes[alias] = aliases[alias]; - } - }); - keycode.code; - keycode.codes; - keycode.aliases; - keycode.names; - keycode.title; - var MODAL_CLASS_NAME = 'vjs-modal-dialog'; /** * The `ModalDialog` displays over the video and its controls, which blocks @@ -14841,9 +14844,12 @@ _proto.createEl = function createEl$1() { var el = _TimeDisplay.prototype.createEl.call(this); - el.insertBefore(createEl('span', {}, { - 'aria-hidden': true - }, '-'), this.contentEl_); + if (this.options_.displayNegative !== false) { + el.insertBefore(createEl('span', {}, { + 'aria-hidden': true + }, '-'), this.contentEl_); + } + return el; } /** @@ -15320,7 +15326,7 @@ this.on(doc, 'mouseup', this.handleMouseUp_); this.on(doc, 'touchmove', this.handleMouseMove_); this.on(doc, 'touchend', this.handleMouseUp_); - this.handleMouseMove(event); + this.handleMouseMove(event, true); } /** * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`. @@ -15331,6 +15337,7 @@ * @param {EventTarget~Event} event * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered * this function + * @param {boolean} mouseDown this is a flag that should be set to true if `handleMouseMove` is called directly. It allows us to skip things that should not happen if coming from mouse down but should happen on regular mouse move handler. Defaults to false. * * @listens mousemove * @listens touchmove @@ -16233,7 +16240,6 @@ event.stopPropagation(); - this.player_.scrubbing(true); this.videoWasPlaying = !this.player_.paused(); this.player_.pause(); @@ -16244,16 +16250,25 @@ * * @param {EventTarget~Event} event * The `mousemove` event that caused this to run. + * @param {boolean} mouseDown this is a flag that should be set to true if `handleMouseMove` is called directly. It allows us to skip things that should not happen if coming from mouse down but should happen on regular mouse move handler. Defaults to false * * @listens mousemove */ ; - _proto.handleMouseMove = function handleMouseMove(event) { + _proto.handleMouseMove = function handleMouseMove(event, mouseDown) { + if (mouseDown === void 0) { + mouseDown = false; + } + if (!isSingleLeftClick(event)) { return; } + if (!mouseDown && !this.player_.scrubbing()) { + this.player_.scrubbing(true); + } + var newTime; var distance = this.calculateDistance(event); var liveTracker = this.player_.liveTracker; @@ -20178,20 +20193,20 @@ var _proto = AudioTrackMenuItem.prototype; - _proto.createEl = function createEl(type, props, attrs) { + _proto.createEl = function createEl$1(type, props, attrs) { var el = _MenuItem.prototype.createEl.call(this, type, props, attrs); var parentSpan = el.querySelector('.vjs-menu-item-text'); if (this.options_.track.kind === 'main-desc') { - parentSpan.appendChild(_MenuItem.prototype.createEl.call(this, 'span', { + parentSpan.appendChild(createEl('span', { className: 'vjs-icon-placeholder' }, { 'aria-hidden': true })); - parentSpan.appendChild(_MenuItem.prototype.createEl.call(this, 'span', { + parentSpan.appendChild(createEl('span', { className: 'vjs-control-text', - textContent: this.localize('Descriptions') + textContent: ' ' + this.localize('Descriptions') })); } @@ -20215,7 +20230,21 @@ // off for us. - this.track.enabled = true; + this.track.enabled = true; // when native audio tracks are used, we want to make sure that other tracks are turned off + + if (this.player_.tech_.featuresNativeAudioTracks) { + var tracks = this.player_.audioTracks(); + + for (var i = 0; i < tracks.length; i++) { + var track = tracks[i]; // skip the current track since we enabled it above + + if (track === this.track) { + continue; + } + + track.enabled = track === this.track; + } + } } /** * Handle any {@link AudioTrack} change. @@ -20540,18 +20569,11 @@ _proto.handleClick = function handleClick(event) { // select next rate option var currentRate = this.player().playbackRate(); - var rates = this.playbackRates(); // this will select first one if the last one currently selected - - var newRate = rates[0]; - - for (var i = 0; i < rates.length; i++) { - if (rates[i] > currentRate) { - newRate = rates[i]; - break; - } - } + var rates = this.playbackRates(); + var currentIndex = rates.indexOf(currentRate); // this get the next rate and it will select first one if the last one currently selected - this.player().playbackRate(newRate); + var newIndex = (currentIndex + 1) % rates.length; + this.player().playbackRate(rates[newIndex]); } /** * On playbackrateschange, update the menu to account for the new items. @@ -21587,7 +21609,7 @@ // may not have the proper values for things like seekableEnd until then - _this.one(_this.player_, 'canplay', function () { + _this.on(_this.player_, 'canplay', function () { return _this.toggleTracking(); }); // we don't need to track live playback if the document is hidden, // also, tracking when the document is hidden can @@ -22930,7 +22952,11 @@ var endFn = function endFn() { this.trigger('fullscreenchange', { isFullscreen: false - }); + }); // Safari will sometimes set controls on the videoelement when existing fullscreen. + + if (this.el_.controls && !this.options_.nativeControlsForTouch && this.controls()) { + this.el_.controls = false; + } }; var beginFn = function beginFn() { @@ -23340,7 +23366,23 @@ try { var volume = Html5.TEST_VID.volume; Html5.TEST_VID.volume = volume / 2 + 0.1; - return volume !== Html5.TEST_VID.volume; + var canControl = volume !== Html5.TEST_VID.volume; // With the introduction of iOS 15, there are cases where the volume is read as + // changed but reverts back to its original state at the start of the next tick. + // To determine whether volume can be controlled on iOS, + // a timeout is set and the volume is checked asynchronously. + // Since `features` doesn't currently work asynchronously, the value is manually set. + + if (canControl && IS_IOS) { + window.setTimeout(function () { + if (Html5 && Html5.prototype) { + Html5.prototype.featuresVolumeControl = volume !== Html5.TEST_VID.volume; + } + }); // default iOS to false, which will be updated in the timeout above. + + return false; + } + + return canControl; } catch (e) { return false; } @@ -23534,13 +23576,14 @@ * @default {@link Html5.supportsNativeAudioTracks} */ - [['featuresVolumeControl', 'canControlVolume'], ['featuresMuteControl', 'canMuteVolume'], ['featuresPlaybackRate', 'canControlPlaybackRate'], ['featuresSourceset', 'canOverrideAttributes'], ['featuresNativeTextTracks', 'supportsNativeTextTracks'], ['featuresNativeVideoTracks', 'supportsNativeVideoTracks'], ['featuresNativeAudioTracks', 'supportsNativeAudioTracks']].forEach(function (_ref) { + [['featuresMuteControl', 'canMuteVolume'], ['featuresPlaybackRate', 'canControlPlaybackRate'], ['featuresSourceset', 'canOverrideAttributes'], ['featuresNativeTextTracks', 'supportsNativeTextTracks'], ['featuresNativeVideoTracks', 'supportsNativeVideoTracks'], ['featuresNativeAudioTracks', 'supportsNativeAudioTracks']].forEach(function (_ref) { var key = _ref[0], fn = _ref[1]; defineLazyProperty(Html5.prototype, key, function () { return Html5[fn](); }, true); }); + Html5.prototype.featuresVolumeControl = Html5.canControlVolume(); /** * Boolean indicating whether the `HTML5` tech currently supports the media element * moving in the DOM. iOS breaks if you move the media element, so this is set this to @@ -26463,9 +26506,14 @@ ; _proto.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) { + var _this9 = this; + if (data) { if (data.nativeIOSFullscreen) { - this.toggleClass('vjs-ios-native-fs'); + this.addClass('vjs-ios-native-fs'); + this.tech_.one('webkitendfullscreen', function () { + _this9.removeClass('vjs-ios-native-fs'); + }); } this.isFullscreen(data.isFullscreen); @@ -26687,13 +26735,13 @@ ; _proto.play = function play() { - var _this9 = this; + var _this10 = this; var PromiseClass = this.options_.Promise || window.Promise; if (PromiseClass) { return new PromiseClass(function (resolve) { - _this9.play_(resolve); + _this10.play_(resolve); }); } @@ -26711,7 +26759,7 @@ ; _proto.play_ = function play_(callback) { - var _this10 = this; + var _this11 = this; if (callback === void 0) { callback = silencePromise; @@ -26729,7 +26777,7 @@ if (!this.isReady_ || !isSrcReady) { this.waitToPlay_ = function (e) { - _this10.play_(); + _this11.play_(); }; this.one(['ready', 'loadstart'], this.waitToPlay_); // if we are in Safari, there is a high chance that loadstart will trigger after the gesture timeperiod @@ -27246,7 +27294,7 @@ }; _proto.requestFullscreenHelper_ = function requestFullscreenHelper_(fullscreenOptions) { - var _this11 = this; + var _this12 = this; var fsOptions; // Only pass fullscreen options to requestFullscreen in spec-compliant browsers. // Use defaults or player configured option unless passed directly to this method. @@ -27271,9 +27319,9 @@ if (promise) { promise.then(function () { - return _this11.isFullscreen(true); + return _this12.isFullscreen(true); }, function () { - return _this11.isFullscreen(false); + return _this12.isFullscreen(false); }); } @@ -27332,7 +27380,7 @@ }; _proto.exitFullscreenHelper_ = function exitFullscreenHelper_() { - var _this12 = this; + var _this13 = this; if (this.fsApi_.requestFullscreen) { var promise = document[this.fsApi_.exitFullscreen](); @@ -27341,7 +27389,7 @@ // we're splitting the promise here, so, we want to catch the // potential error so that this chain doesn't have unhandled errors silencePromise(promise.then(function () { - return _this12.isFullscreen(false); + return _this13.isFullscreen(false); })); } @@ -27669,7 +27717,7 @@ ; _proto.selectSource = function selectSource(sources) { - var _this13 = this; + var _this14 = this; // Get only the techs specified in `techOrder` that exist and are supported by the // current platform @@ -27717,7 +27765,7 @@ var techName = _ref2[0], tech = _ref2[1]; - if (tech.canPlaySource(source, _this13.options_[techName.toLowerCase()])) { + if (tech.canPlaySource(source, _this14.options_[techName.toLowerCase()])) { return { source: source, tech: techName @@ -27757,7 +27805,7 @@ ; _proto.handleSrc_ = function handleSrc_(source, isRetry) { - var _this14 = this; + var _this15 = this; // getter usage if (typeof source === 'undefined') { @@ -27796,25 +27844,25 @@ this.updateSourceCaches_(sources[0]); // middlewareSource is the source after it has been changed by middleware setSource(this, sources[0], function (middlewareSource, mws) { - _this14.middleware_ = mws; // since sourceSet is async we have to update the cache again after we select a source since + _this15.middleware_ = mws; // since sourceSet is async we have to update the cache again after we select a source since // the source that is selected could be out of order from the cache update above this callback. if (!isRetry) { - _this14.cache_.sources = sources; + _this15.cache_.sources = sources; } - _this14.updateSourceCaches_(middlewareSource); + _this15.updateSourceCaches_(middlewareSource); - var err = _this14.src_(middlewareSource); + var err = _this15.src_(middlewareSource); if (err) { if (sources.length > 1) { - return _this14.handleSrc_(sources.slice(1)); + return _this15.handleSrc_(sources.slice(1)); } - _this14.changingSrc_ = false; // We need to wrap this in a timeout to give folks a chance to add error event handlers + _this15.changingSrc_ = false; // We need to wrap this in a timeout to give folks a chance to add error event handlers - _this14.setTimeout(function () { + _this15.setTimeout(function () { this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) @@ -27823,33 +27871,33 @@ // this needs a better comment about why this is needed - _this14.triggerReady(); + _this15.triggerReady(); return; } - setTech(mws, _this14.tech_); + setTech(mws, _this15.tech_); }); // Try another available source if this one fails before playback. if (this.options_.retryOnError && sources.length > 1) { var retry = function retry() { // Remove the error modal - _this14.error(null); + _this15.error(null); - _this14.handleSrc_(sources.slice(1), true); + _this15.handleSrc_(sources.slice(1), true); }; var stopListeningForErrors = function stopListeningForErrors() { - _this14.off('error', retry); + _this15.off('error', retry); }; this.one('error', retry); this.one('playing', stopListeningForErrors); this.resetRetryOnError_ = function () { - _this14.off('error', retry); + _this15.off('error', retry); - _this14.off('playing', stopListeningForErrors); + _this15.off('playing', stopListeningForErrors); }; } } @@ -27889,7 +27937,7 @@ ; _proto.src_ = function src_(source) { - var _this15 = this; + var _this16 = this; var sourceTech = this.selectSource([source]); @@ -27902,7 +27950,7 @@ this.loadTech_(sourceTech.tech, sourceTech.source); this.tech_.ready(function () { - _this15.changingSrc_ = false; + _this16.changingSrc_ = false; }); return false; } // wait until the tech is ready to set the source @@ -27940,7 +27988,7 @@ ; _proto.reset = function reset() { - var _this16 = this; + var _this17 = this; var PromiseClass = this.options_.Promise || window.Promise; @@ -27949,7 +27997,7 @@ } else { var playPromise = this.play(); silencePromise(playPromise.then(function () { - return _this16.doReset_(); + return _this17.doReset_(); })); } }; @@ -28383,7 +28431,7 @@ ; _proto.error = function error(err) { - var _this17 = this; + var _this18 = this; if (err === undefined) { return this.error_ || null; @@ -28391,10 +28439,10 @@ hooks('beforeerror').forEach(function (hookFunction) { - var newErr = hookFunction(_this17, err); + var newErr = hookFunction(_this18, err); if (!(isObject$1(newErr) && !Array.isArray(newErr) || typeof newErr === 'string' || typeof newErr === 'number' || newErr === null)) { - _this17.log.error('please return a value that MediaError expects in beforeerror hooks'); + _this18.log.error('please return a value that MediaError expects in beforeerror hooks'); return; } @@ -28442,7 +28490,7 @@ this.trigger('error'); // notify hooks of the per player error hooks('error').forEach(function (hookFunction) { - return hookFunction(_this17, _this17.error_); + return hookFunction(_this18, _this18.error_); }); return; } @@ -28918,14 +28966,14 @@ ; _proto.createModal = function createModal(content, options) { - var _this18 = this; + var _this19 = this; options = options || {}; options.content = content || ''; var modal = new ModalDialog(this, options); this.addChild(modal); modal.on('dispose', function () { - _this18.removeChild(modal); + _this19.removeChild(modal); }); modal.open(); return modal; @@ -29156,7 +29204,7 @@ ; _proto.loadMedia = function loadMedia(media, ready) { - var _this19 = this; + var _this20 = this; if (!media || typeof media !== 'object') { return; @@ -29188,7 +29236,7 @@ if (Array.isArray(textTracks)) { textTracks.forEach(function (tt) { - return _this19.addRemoteTextTrack(tt, false); + return _this20.addRemoteTextTrack(tt, false); }); } @@ -30944,9 +30992,9 @@ })(); }); - var DEFAULT_LOCATION$1 = 'http://example.com'; + var DEFAULT_LOCATION = 'http://example.com'; - var resolveUrl$2 = function resolveUrl(baseUrl, relativeUrl) { + var resolveUrl$1 = function resolveUrl(baseUrl, relativeUrl) { // return early if we don't need to resolve if (/^[a-z]+:/i.test(relativeUrl)) { return relativeUrl; @@ -30966,7 +31014,7 @@ var removeLocation = !window.location && !/\/\//i.test(baseUrl); // if the base URL is relative then combine with the current location if (nativeURL) { - baseUrl = new window.URL(baseUrl, window.location || DEFAULT_LOCATION$1); + baseUrl = new window.URL(baseUrl, window.location || DEFAULT_LOCATION); } else if (!/\/\//i.test(baseUrl)) { baseUrl = urlToolkit.buildAbsoluteURL(window.location && window.location.href || '', baseUrl); } @@ -30977,7 +31025,7 @@ // otherwise, return the url unmodified if (removeLocation) { - return newUrl.href.slice(DEFAULT_LOCATION$1.length); + return newUrl.href.slice(DEFAULT_LOCATION.length); } else if (protocolLess) { return newUrl.href.slice(newUrl.protocol.length); } @@ -32898,48 +32946,26 @@ return null; }; - var DEFAULT_LOCATION = 'http://example.com'; - - var resolveUrl$1 = function resolveUrl(baseUrl, relativeUrl) { - // return early if we don't need to resolve - if (/^[a-z]+:/i.test(relativeUrl)) { - return relativeUrl; - } // if baseUrl is a data URI, ignore it and resolve everything relative to window.location - - - if (/^data:/.test(baseUrl)) { - baseUrl = window.location && window.location.href || ''; - } // IE11 supports URL but not the URL constructor - // feature detect the behavior we want - - - var nativeURL = typeof window.URL === 'function'; - var protocolLess = /^\/\//.test(baseUrl); // remove location if window.location isn't available (i.e. we're in node) - // and if baseUrl isn't an absolute url - - var removeLocation = !window.location && !/\/\//i.test(baseUrl); // if the base URL is relative then combine with the current location - - if (nativeURL) { - baseUrl = new window.URL(baseUrl, window.location || DEFAULT_LOCATION); - } else if (!/\/\//i.test(baseUrl)) { - baseUrl = urlToolkit.buildAbsoluteURL(window.location && window.location.href || '', baseUrl); - } - - if (nativeURL) { - var newUrl = new URL(relativeUrl, baseUrl); // if we're a protocol-less url, remove the protocol - // and if we're location-less, remove the location - // otherwise, return the url unmodified - - if (removeLocation) { - return newUrl.href.slice(DEFAULT_LOCATION.length); - } else if (protocolLess) { - return newUrl.href.slice(newUrl.protocol.length); + /** + * Loops through all supported media groups in master and calls the provided + * callback for each group + * + * @param {Object} master + * The parsed master manifest object + * @param {string[]} groups + * The media groups to call the callback for + * @param {Function} callback + * Callback to call for each media group + */ + var forEachMediaGroup$1 = function forEachMediaGroup(master, groups, callback) { + groups.forEach(function (mediaType) { + for (var groupKey in master.mediaGroups[mediaType]) { + for (var labelKey in master.mediaGroups[mediaType][groupKey]) { + var mediaProperties = master.mediaGroups[mediaType][groupKey][labelKey]; + callback(mediaProperties, mediaType, groupKey, labelKey); + } } - - return newUrl.href; - } - - return urlToolkit.buildAbsoluteURL(baseUrl, relativeUrl); + }); }; /** @@ -36146,7 +36172,7 @@ var DOMParser = domParser.DOMParser; - /*! @name mpd-parser @version 0.19.2 @license Apache-2.0 */ + /*! @name mpd-parser @version 0.21.0 @license Apache-2.0 */ var isObject = function isObject(obj) { return !!obj && typeof obj === 'object'; @@ -36220,6 +36246,45 @@ return a; }, []); }; + /** + * Returns the first index that satisfies the matching function, or -1 if not found. + * + * Only necessary because of IE11 support. + * + * @param {Array} list - the list to search through + * @param {Function} matchingFunction - the matching function + * + * @return {number} the matching index or -1 if not found + */ + + + var findIndex = function findIndex(list, matchingFunction) { + for (var i = 0; i < list.length; i++) { + if (matchingFunction(list[i])) { + return i; + } + } + + return -1; + }; + /** + * Returns a union of the included lists provided each element can be identified by a key. + * + * @param {Array} list - list of lists to get the union of + * @param {Function} keyFunction - the function to use as a key for each element + * + * @return {Array} the union of the arrays + */ + + + var union = function union(lists, keyFunction) { + return values(lists.reduce(function (acc, list) { + list.forEach(function (el) { + acc[keyFunction(el)] = el; + }); + return acc; + }, {})); + }; var errors = { INVALID_NUMBER_OF_PERIOD: 'INVALID_NUMBER_OF_PERIOD', @@ -36272,13 +36337,35 @@ if (range || indexRange) { var rangeStr = range ? range : indexRange; - var ranges = rangeStr.split('-'); - var startRange = parseInt(ranges[0], 10); - var endRange = parseInt(ranges[1], 10); // byterange should be inclusive according to + var ranges = rangeStr.split('-'); // default to parsing this as a BigInt if possible + + var startRange = window.BigInt ? window.BigInt(ranges[0]) : parseInt(ranges[0], 10); + var endRange = window.BigInt ? window.BigInt(ranges[1]) : parseInt(ranges[1], 10); // convert back to a number if less than MAX_SAFE_INTEGER + + if (startRange < Number.MAX_SAFE_INTEGER && typeof startRange === 'bigint') { + startRange = Number(startRange); + } + + if (endRange < Number.MAX_SAFE_INTEGER && typeof endRange === 'bigint') { + endRange = Number(endRange); + } + + var length; + + if (typeof endRange === 'bigint' || typeof startRange === 'bigint') { + length = window.BigInt(endRange) - window.BigInt(startRange) + window.BigInt(1); + } else { + length = endRange - startRange + 1; + } + + if (typeof length === 'bigint' && length < Number.MAX_SAFE_INTEGER) { + length = Number(length); + } // byterange should be inclusive according to // RFC 2616, Clause 14.35.1 + segment.byterange = { - length: endRange - startRange + 1, + length: length, offset: startRange }; } @@ -36289,7 +36376,14 @@ var byteRangeToString = function byteRangeToString(byterange) { // `endRange` is one less than `offset + length` because the HTTP range // header uses inclusive ranges - var endRange = byterange.offset + byterange.length - 1; + var endRange; + + if (typeof byterange.offset === 'bigint' || typeof byterange.length === 'bigint') { + endRange = window.BigInt(byterange.offset) + window.BigInt(byterange.length) - window.BigInt(1); + } else { + endRange = byterange.offset + byterange.length - 1; + } + return byterange.offset + "-" + endRange; }; /** @@ -36374,15 +36468,20 @@ _attributes$timescale2 = attributes.timescale, timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2, duration = attributes.duration, - _attributes$start = attributes.start, - start = _attributes$start === void 0 ? 0 : _attributes$start, + _attributes$periodSta = attributes.periodStart, + periodStart = _attributes$periodSta === void 0 ? 0 : _attributes$periodSta, _attributes$minimumUp = attributes.minimumUpdatePeriod, minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp, _attributes$timeShift = attributes.timeShiftBufferDepth, timeShiftBufferDepth = _attributes$timeShift === void 0 ? Infinity : _attributes$timeShift; - var endNumber = parseEndNumber(attributes.endNumber); - var now = (NOW + clientOffset) / 1000; - var periodStartWC = availabilityStartTime + start; + var endNumber = parseEndNumber(attributes.endNumber); // clientOffset is passed in at the top level of mpd-parser and is an offset calculated + // after retrieving UTC server time. + + var now = (NOW + clientOffset) / 1000; // WC stands for Wall Clock. + // Convert the period start time to EPOCH. + + var periodStartWC = availabilityStartTime + periodStart; // Period end in EPOCH is manifest's retrieval time + time until next update. + var periodEndWC = now + minimumUpdatePeriod; var periodDuration = periodEndWC - periodStartWC; var segmentCount = Math.ceil(periodDuration * timescale / duration); @@ -36419,18 +36518,18 @@ */ var toSegments = function toSegments(attributes) { - return function (number, index) { + return function (number) { var duration = attributes.duration, _attributes$timescale3 = attributes.timescale, timescale = _attributes$timescale3 === void 0 ? 1 : _attributes$timescale3, - periodIndex = attributes.periodIndex, + periodStart = attributes.periodStart, _attributes$startNumb = attributes.startNumber, startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb; return { number: startNumber + number, duration: duration / timescale, - timeline: periodIndex, - time: index * duration + timeline: periodStart, + time: number * duration }; }; }; @@ -36489,6 +36588,10 @@ sourceDuration = attributes.sourceDuration, _attributes$indexRang = attributes.indexRange, indexRange = _attributes$indexRang === void 0 ? '' : _attributes$indexRang, + periodStart = attributes.periodStart, + presentationTime = attributes.presentationTime, + _attributes$number = attributes.number, + number = _attributes$number === void 0 ? 0 : _attributes$number, duration = attributes.duration; // base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1) if (!baseUrl) { @@ -36517,11 +36620,15 @@ } } else if (sourceDuration) { segment.duration = sourceDuration; - segment.timeline = 0; - } // This is used for mediaSequence + segment.timeline = periodStart; + } // If presentation time is provided, these segments are being generated by SIDX + // references, and should use the time provided. For the general case of SegmentBase, + // there should only be one segment in the period, so its presentation time is the same + // as its period start. - segment.number = 0; + segment.presentationTime = presentationTime || periodStart; + segment.number = number; return [segment]; }; /** @@ -36537,7 +36644,7 @@ */ - var addSidxSegmentsToPlaylist = function addSidxSegmentsToPlaylist(playlist, sidx, baseUrl) { + var addSidxSegmentsToPlaylist$1 = function addSidxSegmentsToPlaylist(playlist, sidx, baseUrl) { // Retain init segment information var initSegment = playlist.sidx.map ? playlist.sidx.map : null; // Retain source duration from initial main manifest parsing @@ -36553,9 +36660,18 @@ return r.referenceType !== 1; }); var segments = []; - var type = playlist.endList ? 'static' : 'dynamic'; // firstOffset is the offset from the end of the sidx box + var type = playlist.endList ? 'static' : 'dynamic'; + var periodStart = playlist.sidx.timeline; + var presentationTime = periodStart; + var number = playlist.mediaSequence || 0; // firstOffset is the offset from the end of the sidx box - var startIndex = sidxEnd + sidx.firstOffset; + var startIndex; // eslint-disable-next-line + + if (typeof sidx.firstOffset === 'bigint') { + startIndex = window.BigInt(sidxEnd) + sidx.firstOffset; + } else { + startIndex = sidxEnd + sidx.firstOffset; + } for (var i = 0; i < mediaReferences.length; i++) { var reference = sidx.references[i]; // size of the referenced (sub)segment @@ -36565,14 +36681,22 @@ var duration = reference.subsegmentDuration; // should be an inclusive range - var endIndex = startIndex + size - 1; + var endIndex = void 0; // eslint-disable-next-line + + if (typeof startIndex === 'bigint') { + endIndex = startIndex + window.BigInt(size) - window.BigInt(1); + } else { + endIndex = startIndex + size - 1; + } + var indexRange = startIndex + "-" + endIndex; var attributes = { baseUrl: baseUrl, timescale: timescale, timeline: timeline, - // this is used in parseByDuration - periodIndex: timeline, + periodStart: periodStart, + presentationTime: presentationTime, + number: number, duration: duration, sourceDuration: sourceDuration, indexRange: indexRange, @@ -36585,13 +36709,262 @@ } segments.push(segment); - startIndex += size; + + if (typeof startIndex === 'bigint') { + startIndex += window.BigInt(size); + } else { + startIndex += size; + } + + presentationTime += duration / timescale; + number++; } playlist.segments = segments; return playlist; }; + var SUPPORTED_MEDIA_TYPES = ['AUDIO', 'SUBTITLES']; // allow one 60fps frame as leniency (arbitrarily chosen) + + var TIME_FUDGE = 1 / 60; + /** + * Given a list of timelineStarts, combines, dedupes, and sorts them. + * + * @param {TimelineStart[]} timelineStarts - list of timeline starts + * + * @return {TimelineStart[]} the combined and deduped timeline starts + */ + + var getUniqueTimelineStarts = function getUniqueTimelineStarts(timelineStarts) { + return union(timelineStarts, function (_ref) { + var timeline = _ref.timeline; + return timeline; + }).sort(function (a, b) { + return a.timeline > b.timeline ? 1 : -1; + }); + }; + /** + * Finds the playlist with the matching NAME attribute. + * + * @param {Array} playlists - playlists to search through + * @param {string} name - the NAME attribute to search for + * + * @return {Object|null} the matching playlist object, or null + */ + + + var findPlaylistWithName = function findPlaylistWithName(playlists, name) { + for (var i = 0; i < playlists.length; i++) { + if (playlists[i].attributes.NAME === name) { + return playlists[i]; + } + } + + return null; + }; + /** + * Gets a flattened array of media group playlists. + * + * @param {Object} manifest - the main manifest object + * + * @return {Array} the media group playlists + */ + + + var getMediaGroupPlaylists = function getMediaGroupPlaylists(manifest) { + var mediaGroupPlaylists = []; + forEachMediaGroup$1(manifest, SUPPORTED_MEDIA_TYPES, function (properties, type, group, label) { + mediaGroupPlaylists = mediaGroupPlaylists.concat(properties.playlists || []); + }); + return mediaGroupPlaylists; + }; + /** + * Updates the playlist's media sequence numbers. + * + * @param {Object} config - options object + * @param {Object} config.playlist - the playlist to update + * @param {number} config.mediaSequence - the mediaSequence number to start with + */ + + + var updateMediaSequenceForPlaylist = function updateMediaSequenceForPlaylist(_ref2) { + var playlist = _ref2.playlist, + mediaSequence = _ref2.mediaSequence; + playlist.mediaSequence = mediaSequence; + playlist.segments.forEach(function (segment, index) { + segment.number = playlist.mediaSequence + index; + }); + }; + /** + * Updates the media and discontinuity sequence numbers of newPlaylists given oldPlaylists + * and a complete list of timeline starts. + * + * If no matching playlist is found, only the discontinuity sequence number of the playlist + * will be updated. + * + * Since early available timelines are not supported, at least one segment must be present. + * + * @param {Object} config - options object + * @param {Object[]} oldPlaylists - the old playlists to use as a reference + * @param {Object[]} newPlaylists - the new playlists to update + * @param {Object} timelineStarts - all timelineStarts seen in the stream to this point + */ + + + var updateSequenceNumbers = function updateSequenceNumbers(_ref3) { + var oldPlaylists = _ref3.oldPlaylists, + newPlaylists = _ref3.newPlaylists, + timelineStarts = _ref3.timelineStarts; + newPlaylists.forEach(function (playlist) { + playlist.discontinuitySequence = findIndex(timelineStarts, function (_ref4) { + var timeline = _ref4.timeline; + return timeline === playlist.timeline; + }); // Playlists NAMEs come from DASH Representation IDs, which are mandatory + // (see ISO_23009-1-2012 5.3.5.2). + // + // If the same Representation existed in a prior Period, it will retain the same NAME. + + var oldPlaylist = findPlaylistWithName(oldPlaylists, playlist.attributes.NAME); + + if (!oldPlaylist) { + // Since this is a new playlist, the media sequence values can start from 0 without + // consequence. + return; + } // TODO better support for live SIDX + // + // As of this writing, mpd-parser does not support multiperiod SIDX (in live or VOD). + // This is evident by a playlist only having a single SIDX reference. In a multiperiod + // playlist there would need to be multiple SIDX references. In addition, live SIDX is + // not supported when the SIDX properties change on refreshes. + // + // In the future, if support needs to be added, the merging logic here can be called + // after SIDX references are resolved. For now, exit early to prevent exceptions being + // thrown due to undefined references. + + + if (playlist.sidx) { + return; + } // Since we don't yet support early available timelines, we don't need to support + // playlists with no segments. + + + var firstNewSegment = playlist.segments[0]; + var oldMatchingSegmentIndex = findIndex(oldPlaylist.segments, function (oldSegment) { + return Math.abs(oldSegment.presentationTime - firstNewSegment.presentationTime) < TIME_FUDGE; + }); // No matching segment from the old playlist means the entire playlist was refreshed. + // In this case the media sequence should account for this update, and the new segments + // should be marked as discontinuous from the prior content, since the last prior + // timeline was removed. + + if (oldMatchingSegmentIndex === -1) { + updateMediaSequenceForPlaylist({ + playlist: playlist, + mediaSequence: oldPlaylist.mediaSequence + oldPlaylist.segments.length + }); + playlist.segments[0].discontinuity = true; + playlist.discontinuityStarts.unshift(0); // No matching segment does not necessarily mean there's missing content. + // + // If the new playlist's timeline is the same as the last seen segment's timeline, + // then a discontinuity can be added to identify that there's potentially missing + // content. If there's no missing content, the discontinuity should still be rather + // harmless. It's possible that if segment durations are accurate enough, that the + // existence of a gap can be determined using the presentation times and durations, + // but if the segment timing info is off, it may introduce more problems than simply + // adding the discontinuity. + // + // If the new playlist's timeline is different from the last seen segment's timeline, + // then a discontinuity can be added to identify that this is the first seen segment + // of a new timeline. However, the logic at the start of this function that + // determined the disconinuity sequence by timeline index is now off by one (the + // discontinuity of the newest timeline hasn't yet fallen off the manifest...since + // we added it), so the disconinuity sequence must be decremented. + // + // A period may also have a duration of zero, so the case of no segments is handled + // here even though we don't yet support early available periods. + + if (!oldPlaylist.segments.length && playlist.timeline > oldPlaylist.timeline || oldPlaylist.segments.length && playlist.timeline > oldPlaylist.segments[oldPlaylist.segments.length - 1].timeline) { + playlist.discontinuitySequence--; + } + + return; + } // If the first segment matched with a prior segment on a discontinuity (it's matching + // on the first segment of a period), then the discontinuitySequence shouldn't be the + // timeline's matching one, but instead should be the one prior, and the first segment + // of the new manifest should be marked with a discontinuity. + // + // The reason for this special case is that discontinuity sequence shows how many + // discontinuities have fallen off of the playlist, and discontinuities are marked on + // the first segment of a new "timeline." Because of this, while DASH will retain that + // Period while the "timeline" exists, HLS keeps track of it via the discontinuity + // sequence, and that first segment is an indicator, but can be removed before that + // timeline is gone. + + + var oldMatchingSegment = oldPlaylist.segments[oldMatchingSegmentIndex]; + + if (oldMatchingSegment.discontinuity && !firstNewSegment.discontinuity) { + firstNewSegment.discontinuity = true; + playlist.discontinuityStarts.unshift(0); + playlist.discontinuitySequence--; + } + + updateMediaSequenceForPlaylist({ + playlist: playlist, + mediaSequence: oldPlaylist.segments[oldMatchingSegmentIndex].number + }); + }); + }; + /** + * Given an old parsed manifest object and a new parsed manifest object, updates the + * sequence and timing values within the new manifest to ensure that it lines up with the + * old. + * + * @param {Array} oldManifest - the old main manifest object + * @param {Array} newManifest - the new main manifest object + * + * @return {Object} the updated new manifest object + */ + + + var positionManifestOnTimeline = function positionManifestOnTimeline(_ref5) { + var oldManifest = _ref5.oldManifest, + newManifest = _ref5.newManifest; // Starting from v4.1.2 of the IOP, section 4.4.3.3 states: + // + // "MPD@availabilityStartTime and Period@start shall not be changed over MPD updates." + // + // This was added from https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/160 + // + // Because of this change, and the difficulty of supporting periods with changing start + // times, periods with changing start times are not supported. This makes the logic much + // simpler, since periods with the same start time can be considerred the same period + // across refreshes. + // + // To give an example as to the difficulty of handling periods where the start time may + // change, if a single period manifest is refreshed with another manifest with a single + // period, and both the start and end times are increased, then the only way to determine + // if it's a new period or an old one that has changed is to look through the segments of + // each playlist and determine the presentation time bounds to find a match. In addition, + // if the period start changed to exceed the old period end, then there would be no + // match, and it would not be possible to determine whether the refreshed period is a new + // one or the old one. + + var oldPlaylists = oldManifest.playlists.concat(getMediaGroupPlaylists(oldManifest)); + var newPlaylists = newManifest.playlists.concat(getMediaGroupPlaylists(newManifest)); // Save all seen timelineStarts to the new manifest. Although this potentially means that + // there's a "memory leak" in that it will never stop growing, in reality, only a couple + // of properties are saved for each seen Period. Even long running live streams won't + // generate too many Periods, unless the stream is watched for decades. In the future, + // this can be optimized by mapping to discontinuity sequence numbers for each timeline, + // but it may not become an issue, and the additional info can be useful for debugging. + + newManifest.timelineStarts = getUniqueTimelineStarts([oldManifest.timelineStarts, newManifest.timelineStarts]); + updateSequenceNumbers({ + oldPlaylists: oldPlaylists, + newPlaylists: newPlaylists, + timelineStarts: newManifest.timelineStarts + }); + return newManifest; + }; + var generateSidxKey = function generateSidxKey(sidx) { return sidx && sidx.uri + '-' + byteRangeToString(sidx.byterange); }; @@ -36601,42 +36974,52 @@ // assuming playlist IDs are the same across periods // TODO: handle multiperiod where representation sets are not the same // across periods - var name = playlist.attributes.id + (playlist.attributes.lang || ''); // Periods after first + var name = playlist.attributes.id + (playlist.attributes.lang || ''); - if (acc[name]) { - var _acc$name$segments; // first segment of subsequent periods signal a discontinuity + if (!acc[name]) { + // First Period + acc[name] = playlist; + acc[name].attributes.timelineStarts = []; + } else { + // Subsequent Periods + if (playlist.segments) { + var _acc$name$segments; // first segment of subsequent periods signal a discontinuity - if (playlist.segments[0]) { - playlist.segments[0].discontinuity = true; - } + if (playlist.segments[0]) { + playlist.segments[0].discontinuity = true; + } - (_acc$name$segments = acc[name].segments).push.apply(_acc$name$segments, playlist.segments); // bubble up contentProtection, this assumes all DRM content + (_acc$name$segments = acc[name].segments).push.apply(_acc$name$segments, playlist.segments); + } // bubble up contentProtection, this assumes all DRM content // has the same contentProtection if (playlist.attributes.contentProtection) { acc[name].attributes.contentProtection = playlist.attributes.contentProtection; } - } else { - // first Period - acc[name] = playlist; } + acc[name].attributes.timelineStarts.push({ + // Although they represent the same number, it's important to have both to make it + // compatible with HLS potentially having a similar attribute. + start: playlist.attributes.periodStart, + timeline: playlist.attributes.periodStart + }); return acc; }, {})); return mergedPlaylists.map(function (playlist) { - playlist.discontinuityStarts = findIndexes(playlist.segments, 'discontinuity'); + playlist.discontinuityStarts = findIndexes(playlist.segments || [], 'discontinuity'); return playlist; }); }; - var addSidxSegmentsToPlaylist$1 = function addSidxSegmentsToPlaylist$1(playlist, sidxMapping) { + var addSidxSegmentsToPlaylist = function addSidxSegmentsToPlaylist(playlist, sidxMapping) { var sidxKey = generateSidxKey(playlist.sidx); var sidxMatch = sidxKey && sidxMapping[sidxKey] && sidxMapping[sidxKey].sidx; if (sidxMatch) { - addSidxSegmentsToPlaylist(playlist, sidxMatch, playlist.sidx.resolvedUri); + addSidxSegmentsToPlaylist$1(playlist, sidxMatch, playlist.sidx.resolvedUri); } return playlist; @@ -36652,7 +37035,7 @@ } for (var i in playlists) { - playlists[i] = addSidxSegmentsToPlaylist$1(playlists[i], sidxMapping); + playlists[i] = addSidxSegmentsToPlaylist(playlists[i], sidxMapping); } return playlists; @@ -36663,7 +37046,10 @@ var attributes = _ref.attributes, segments = _ref.segments, - sidx = _ref.sidx; + sidx = _ref.sidx, + mediaSequence = _ref.mediaSequence, + discontinuitySequence = _ref.discontinuitySequence, + discontinuityStarts = _ref.discontinuityStarts; var playlist = { attributes: (_attributes = { NAME: attributes.id, @@ -36672,11 +37058,14 @@ }, _attributes['PROGRAM-ID'] = 1, _attributes), uri: '', endList: attributes.type === 'static', - timeline: attributes.periodIndex, + timeline: attributes.periodStart, resolvedUri: '', targetDuration: attributes.duration, - segments: segments, - mediaSequence: segments.length ? segments[0].number : 1 + discontinuitySequence: discontinuitySequence, + discontinuityStarts: discontinuityStarts, + timelineStarts: attributes.timelineStarts, + mediaSequence: mediaSequence, + segments: segments }; if (attributes.contentProtection) { @@ -36699,13 +37088,16 @@ var _m3u8Attributes; var attributes = _ref2.attributes, - segments = _ref2.segments; + segments = _ref2.segments, + mediaSequence = _ref2.mediaSequence, + discontinuityStarts = _ref2.discontinuityStarts, + discontinuitySequence = _ref2.discontinuitySequence; if (typeof segments === 'undefined') { // vtt tracks may use single file in BaseURL segments = [{ uri: attributes.baseUrl, - timeline: attributes.periodIndex, + timeline: attributes.periodStart, resolvedUri: attributes.baseUrl || '', duration: attributes.sourceDuration, number: 0 @@ -36727,11 +37119,14 @@ attributes: m3u8Attributes, uri: '', endList: attributes.type === 'static', - timeline: attributes.periodIndex, + timeline: attributes.periodStart, resolvedUri: attributes.baseUrl || '', targetDuration: attributes.duration, - segments: segments, - mediaSequence: segments.length ? segments[0].number : 1 + timelineStarts: attributes.timelineStarts, + discontinuityStarts: discontinuityStarts, + discontinuitySequence: discontinuitySequence, + mediaSequence: mediaSequence, + segments: segments }; }; @@ -36765,7 +37160,7 @@ }; } - var formatted = addSidxSegmentsToPlaylist$1(formatAudioPlaylist(playlist, isAudioOnly), sidxMapping); + var formatted = addSidxSegmentsToPlaylist(formatAudioPlaylist(playlist, isAudioOnly), sidxMapping); a[label].playlists.push(formatted); if (typeof mainPlaylist === 'undefined' && role === 'main') { @@ -36802,7 +37197,7 @@ }; } - a[label].playlists.push(addSidxSegmentsToPlaylist$1(formatVttPlaylist(playlist), sidxMapping)); + a[label].playlists.push(addSidxSegmentsToPlaylist(formatVttPlaylist(playlist), sidxMapping)); return a; }, {}); }; @@ -36844,7 +37239,8 @@ var attributes = _ref3.attributes, segments = _ref3.segments, - sidx = _ref3.sidx; + sidx = _ref3.sidx, + discontinuityStarts = _ref3.discontinuityStarts; var playlist = { attributes: (_attributes2 = { NAME: attributes.id, @@ -36859,11 +37255,12 @@ }, _attributes2['PROGRAM-ID'] = 1, _attributes2), uri: '', endList: attributes.type === 'static', - timeline: attributes.periodIndex, + timeline: attributes.periodStart, resolvedUri: '', targetDuration: attributes.duration, - segments: segments, - mediaSequence: segments.length ? segments[0].number : 1 + discontinuityStarts: discontinuityStarts, + timelineStarts: attributes.timelineStarts, + segments: segments }; if (attributes.contentProtection) { @@ -36891,14 +37288,87 @@ var attributes = _ref6.attributes; return attributes.mimeType === 'text/vtt' || attributes.contentType === 'text'; }; + /** + * Contains start and timeline properties denoting a timeline start. For DASH, these will + * be the same number. + * + * @typedef {Object} TimelineStart + * @property {number} start - the start time of the timeline + * @property {number} timeline - the timeline number + */ - var toM3u8 = function toM3u8(dashPlaylists, locations, sidxMapping) { - var _mediaGroups; + /** + * Adds appropriate media and discontinuity sequence values to the segments and playlists. + * + * Throughout mpd-parser, the `number` attribute is used in relation to `startNumber`, a + * DASH specific attribute used in constructing segment URI's from templates. However, from + * an HLS perspective, the `number` attribute on a segment would be its `mediaSequence` + * value, which should start at the original media sequence value (or 0) and increment by 1 + * for each segment thereafter. Since DASH's `startNumber` values are independent per + * period, it doesn't make sense to use it for `number`. Instead, assume everything starts + * from a 0 mediaSequence value and increment from there. + * + * Note that VHS currently doesn't use the `number` property, but it can be helpful for + * debugging and making sense of the manifest. + * + * For live playlists, to account for values increasing in manifests when periods are + * removed on refreshes, merging logic should be used to update the numbers to their + * appropriate values (to ensure they're sequential and increasing). + * + * @param {Object[]} playlists - the playlists to update + * @param {TimelineStart[]} timelineStarts - the timeline starts for the manifest + */ - if (sidxMapping === void 0) { - sidxMapping = {}; + + var addMediaSequenceValues = function addMediaSequenceValues(playlists, timelineStarts) { + // increment all segments sequentially + playlists.forEach(function (playlist) { + playlist.mediaSequence = 0; + playlist.discontinuitySequence = findIndex(timelineStarts, function (_ref7) { + var timeline = _ref7.timeline; + return timeline === playlist.timeline; + }); + + if (!playlist.segments) { + return; + } + + playlist.segments.forEach(function (segment, index) { + segment.number = index; + }); + }); + }; + /** + * Given a media group object, flattens all playlists within the media group into a single + * array. + * + * @param {Object} mediaGroupObject - the media group object + * + * @return {Object[]} + * The media group playlists + */ + + + var flattenMediaGroupPlaylists = function flattenMediaGroupPlaylists(mediaGroupObject) { + if (!mediaGroupObject) { + return []; } + return Object.keys(mediaGroupObject).reduce(function (acc, label) { + var labelContents = mediaGroupObject[label]; + return acc.concat(labelContents.playlists); + }, []); + }; + + var toM3u8 = function toM3u8(_ref8) { + var _mediaGroups; + + var dashPlaylists = _ref8.dashPlaylists, + locations = _ref8.locations, + _ref8$sidxMapping = _ref8.sidxMapping, + sidxMapping = _ref8$sidxMapping === void 0 ? {} : _ref8$sidxMapping, + previousManifest = _ref8.previousManifest; + if (!dashPlaylists.length) { return {}; } // grab all main manifest attributes @@ -36911,7 +37381,7 @@ minimumUpdatePeriod = _dashPlaylists$0$attr.minimumUpdatePeriod; var videoPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(videoOnly)).map(formatVideoPlaylist); var audioPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(audioOnly)); - var vttPlaylists = dashPlaylists.filter(vttOnly); + var vttPlaylists = mergeDiscontiguousPlaylists(dashPlaylists.filter(vttOnly)); var captions = dashPlaylists.map(function (playlist) { return playlist.attributes.captionServices; }).filter(Boolean); @@ -36942,19 +37412,35 @@ } var isAudioOnly = manifest.playlists.length === 0; + var organizedAudioGroup = audioPlaylists.length ? organizeAudioPlaylists(audioPlaylists, sidxMapping, isAudioOnly) : null; + var organizedVttGroup = vttPlaylists.length ? organizeVttPlaylists(vttPlaylists, sidxMapping) : null; + var formattedPlaylists = videoPlaylists.concat(flattenMediaGroupPlaylists(organizedAudioGroup), flattenMediaGroupPlaylists(organizedVttGroup)); + var playlistTimelineStarts = formattedPlaylists.map(function (_ref9) { + var timelineStarts = _ref9.timelineStarts; + return timelineStarts; + }); + manifest.timelineStarts = getUniqueTimelineStarts(playlistTimelineStarts); + addMediaSequenceValues(formattedPlaylists, manifest.timelineStarts); - if (audioPlaylists.length) { - manifest.mediaGroups.AUDIO.audio = organizeAudioPlaylists(audioPlaylists, sidxMapping, isAudioOnly); + if (organizedAudioGroup) { + manifest.mediaGroups.AUDIO.audio = organizedAudioGroup; } - if (vttPlaylists.length) { - manifest.mediaGroups.SUBTITLES.subs = organizeVttPlaylists(vttPlaylists, sidxMapping); + if (organizedVttGroup) { + manifest.mediaGroups.SUBTITLES.subs = organizedVttGroup; } if (captions.length) { manifest.mediaGroups['CLOSED-CAPTIONS'].cc = organizeCaptionServices(captions); } + if (previousManifest) { + return positionManifestOnTimeline({ + oldManifest: previousManifest, + newManifest: manifest + }); + } + return manifest; }; /** @@ -36980,12 +37466,12 @@ availabilityStartTime = attributes.availabilityStartTime, _attributes$timescale = attributes.timescale, timescale = _attributes$timescale === void 0 ? 1 : _attributes$timescale, - _attributes$start = attributes.start, - start = _attributes$start === void 0 ? 0 : _attributes$start, + _attributes$periodSta = attributes.periodStart, + periodStart = _attributes$periodSta === void 0 ? 0 : _attributes$periodSta, _attributes$minimumUp = attributes.minimumUpdatePeriod, minimumUpdatePeriod = _attributes$minimumUp === void 0 ? 0 : _attributes$minimumUp; var now = (NOW + clientOffset) / 1000; - var periodStartWC = availabilityStartTime + start; + var periodStartWC = availabilityStartTime + periodStart; var periodEndWC = now + minimumUpdatePeriod; var periodDuration = periodEndWC - periodStartWC; return Math.ceil((periodDuration * timescale - time) / duration); @@ -37016,7 +37502,7 @@ timescale = _attributes$timescale2 === void 0 ? 1 : _attributes$timescale2, _attributes$startNumb = attributes.startNumber, startNumber = _attributes$startNumb === void 0 ? 1 : _attributes$startNumb, - timeline = attributes.periodIndex; + timeline = attributes.periodStart; var segments = []; var time = -1; @@ -37208,7 +37694,7 @@ number: attributes.startNumber || 1, duration: attributes.sourceDuration, time: 0, - timeline: attributes.periodIndex + timeline: attributes.periodStart }]; } @@ -38091,8 +38577,8 @@ * @function * @param {PeriodInformation} period * Period object containing necessary period information - * @param {number} periodIndex - * Index of the Period within the mpd + * @param {number} periodStart + * Start time of the Period within the mpd * @return {RepresentationInformation[]} * List of objects containing Representaion information */ @@ -38113,11 +38599,7 @@ var toAdaptationSets = function toAdaptationSets(mpdAttributes, mpdBaseUrls) { return function (period, index) { var periodBaseUrls = buildBaseUrls(mpdBaseUrls, findChildren(period.node, 'BaseURL')); - var parsedPeriodId = parseInt(period.attributes.id, 10); // fallback to mapping index if Period@id is not a number - - var periodIndex = window.isNaN(parsedPeriodId) ? index : parsedPeriodId; var periodAttributes = merge(mpdAttributes, { - periodIndex: periodIndex, periodStart: period.attributes.start }); @@ -38331,6 +38813,19 @@ return attributes; }; + /* + * Given a DASH manifest string and options, parses the DASH manifest into an object in the + * form outputed by m3u8-parser and accepted by videojs/http-streaming. + * + * For live DASH manifests, if `previousManifest` is provided in options, then the newly + * parsed DASH manifest will have its media sequence and discontinuity sequence values + * updated to reflect its position relative to the prior manifest. + * + * @param {string} manifestString - the DASH manifest as a string + * @param {options} [options] - any options + * + * @return {Object} the manifest object + */ var parse = function parse(manifestString, options) { if (options === void 0) { @@ -38339,7 +38834,12 @@ var parsedManifestInfo = inheritAttributes(stringToMpdXml(manifestString), options); var playlists = toPlaylists(parsedManifestInfo.representationInfo); - return toM3u8(playlists, parsedManifestInfo.locations, options.sidxMapping); + return toM3u8({ + dashPlaylists: playlists, + locations: parsedManifestInfo.locations, + sidxMapping: options.sidxMapping, + previousManifest: options.previousManifest + }); }; /** * Parses the manifest for a UTCTiming node, returning the nodes attributes if found @@ -38357,6 +38857,30 @@ var MAX_UINT32 = Math.pow(2, 32); + var getUint64$1 = function getUint64(uint8) { + var dv = new DataView(uint8.buffer, uint8.byteOffset, uint8.byteLength); + var value; + + if (dv.getBigUint64) { + value = dv.getBigUint64(0); + + if (value < Number.MAX_SAFE_INTEGER) { + return Number(value); + } + + return value; + } + + return dv.getUint32(0) * MAX_UINT32 + dv.getUint32(4); + }; + + var numbers = { + getUint64: getUint64$1, + MAX_UINT32: MAX_UINT32 + }; + + var getUint64 = numbers.getUint64; + var parseSidx = function parseSidx(data) { var view = new DataView(data.buffer, data.byteOffset, data.byteLength), result = { @@ -38374,8 +38898,8 @@ i += 8; } else { // read 64 bits - result.earliestPresentationTime = view.getUint32(i) * MAX_UINT32 + view.getUint32(i + 4); - result.firstOffset = view.getUint32(i + 8) * MAX_UINT32 + view.getUint32(i + 12); + result.earliestPresentationTime = getUint64(data.subarray(i)); + result.firstOffset = getUint64(data.subarray(i + 8)); i += 16; } @@ -39262,12 +39786,12 @@ }; var clock_1 = clock.ONE_SECOND_IN_TS; - /*! @name @videojs/http-streaming @version 2.12.0 @license Apache-2.0 */ + /*! @name @videojs/http-streaming @version 2.13.1 @license Apache-2.0 */ /** * @file resolve-url.js - Handling how URLs are resolved and manipulated */ - var resolveUrl = resolveUrl$2; + var resolveUrl = resolveUrl$1; /** * Checks whether xhr request was redirected and returns correct url depending * on `handleManifestRedirects` option @@ -41797,8 +42321,15 @@ var byterangeStr = function byterangeStr(byterange) { // `byterangeEnd` is one less than `offset + length` because the HTTP range // header uses inclusive ranges - var byterangeEnd = byterange.offset + byterange.length - 1; + var byterangeEnd; var byterangeStart = byterange.offset; + + if (typeof byterange.offset === 'bigint' || typeof byterange.length === 'bigint') { + byterangeEnd = window.BigInt(byterange.offset) + window.BigInt(byterange.length) - window.BigInt(1); + } else { + byterangeEnd = byterange.offset + byterange.length - 1; + } + return 'bytes=' + byterangeStart + '-' + byterangeEnd; }; /** @@ -42505,14 +43036,16 @@ var masterXml = _ref.masterXml, srcUrl = _ref.srcUrl, clientOffset = _ref.clientOffset, - sidxMapping = _ref.sidxMapping; - var master = parse(masterXml, { + sidxMapping = _ref.sidxMapping, + previousManifest = _ref.previousManifest; + var manifest = parse(masterXml, { manifestUri: srcUrl, clientOffset: clientOffset, - sidxMapping: sidxMapping + sidxMapping: sidxMapping, + previousManifest: previousManifest }); - addPropertiesToMaster(master, srcUrl); - return master; + addPropertiesToMaster(manifest, srcUrl); + return manifest; }; /** * Returns a new master manifest that is the result of merging an updated master manifest @@ -42533,7 +43066,8 @@ var update = mergeOptions(oldMaster, { // These are top level properties that can be updated duration: newMaster.duration, - minimumUpdatePeriod: newMaster.minimumUpdatePeriod + minimumUpdatePeriod: newMaster.minimumUpdatePeriod, + timelineStarts: newMaster.timelineStarts }); // First update the playlists in playlist list for (var i = 0; i < newMaster.playlists.length; i++) { @@ -42543,7 +43077,7 @@ var sidxKey = generateSidxKey(playlist.sidx); // add sidx segments to the playlist if we have all the sidx info already if (sidxMapping && sidxMapping[sidxKey] && sidxMapping[sidxKey].sidx) { - addSidxSegmentsToPlaylist(playlist, sidxMapping[sidxKey].sidx, playlist.sidx.resolvedUri); + addSidxSegmentsToPlaylist$1(playlist, sidxMapping[sidxKey].sidx, playlist.sidx.resolvedUri); } } @@ -42772,7 +43306,7 @@ sidxInfo: playlist.sidx, sidx: sidx }; - addSidxSegmentsToPlaylist(playlist, sidx, playlist.sidx.resolvedUri); + addSidxSegmentsToPlaylist$1(playlist, sidx, playlist.sidx.resolvedUri); return cb(true); }; @@ -43127,13 +43661,14 @@ _proto.handleMaster_ = function handleMaster_() { // clear media request this.mediaRequest_ = null; + var oldMaster = this.masterPlaylistLoader_.master; var newMaster = parseMasterXml({ masterXml: this.masterPlaylistLoader_.masterXml_, srcUrl: this.masterPlaylistLoader_.srcUrl, clientOffset: this.masterPlaylistLoader_.clientOffset_, - sidxMapping: this.masterPlaylistLoader_.sidxMapping_ - }); - var oldMaster = this.masterPlaylistLoader_.master; // if we have an old master to compare the new master against + sidxMapping: this.masterPlaylistLoader_.sidxMapping_, + previousManifest: oldMaster + }); // if we have an old master to compare the new master against if (oldMaster) { newMaster = updateMaster(oldMaster, newMaster, this.masterPlaylistLoader_.sidxMapping_); @@ -43362,7 +43897,7 @@ var getWorkerString = function getWorkerString(fn) { return fn.toString().replace(/^function.+?{/, '').slice(0, -1); }; - /* rollup-plugin-worker-factory start for worker!/Users/gkatsevman/p/http-streaming-release/src/transmuxer-worker.js */ + /* rollup-plugin-worker-factory start for worker!/Users/gsinger/repos/clean/http-streaming/src/transmuxer-worker.js */ var workerCode$1 = transform(getWorkerString(function () { @@ -43517,17 +44052,30 @@ }; var stream = Stream; - /** - * mux.js - * - * Copyright (c) Brightcove - * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE - * - * Functions that generate fragmented MP4s suitable for use with Media - * Source Extensions. - */ + var MAX_UINT32$1 = Math.pow(2, 32); + + var getUint64$2 = function getUint64(uint8) { + var dv = new DataView(uint8.buffer, uint8.byteOffset, uint8.byteLength); + var value; + + if (dv.getBigUint64) { + value = dv.getBigUint64(0); + + if (value < Number.MAX_SAFE_INTEGER) { + return Number(value); + } + + return value; + } + + return dv.getUint32(0) * MAX_UINT32$1 + dv.getUint32(4); + }; - var UINT32_MAX = Math.pow(2, 32) - 1; + var numbers = { + getUint64: getUint64$2, + MAX_UINT32: MAX_UINT32$1 + }; + var MAX_UINT32 = numbers.MAX_UINT32; var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex, trun$1, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS; // pre-calculate constants (function () { @@ -43944,8 +44492,8 @@ 0x00, 0x00, 0x00, 0x00, // default_sample_size 0x00, 0x00, 0x00, 0x00 // default_sample_flags ])); - upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1)); - lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1)); + upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / MAX_UINT32); + lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % MAX_UINT32); trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([0x01, // version 1 0x00, 0x00, 0x00, // flags // baseMediaDecodeTime @@ -49992,17 +50540,18 @@ var findBox_1 = findBox; var toUnsigned$1 = bin.toUnsigned; + var getUint64$1 = numbers.getUint64; var tfdt = function tfdt(data) { var result = { version: data[0], - flags: new Uint8Array(data.subarray(1, 4)), - baseMediaDecodeTime: toUnsigned$1(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]) + flags: new Uint8Array(data.subarray(1, 4)) }; if (result.version === 1) { - result.baseMediaDecodeTime *= Math.pow(2, 32); - result.baseMediaDecodeTime += toUnsigned$1(data[8] << 24 | data[9] << 16 | data[10] << 8 | data[11]); + result.baseMediaDecodeTime = getUint64$1(data.subarray(4)); + } else { + result.baseMediaDecodeTime = toUnsigned$1(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]); } return result; @@ -50178,6 +50727,20 @@ }; var parseTfhd = tfhd; + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + var win; + + if (typeof window !== "undefined") { + win = window; + } else if (typeof commonjsGlobal !== "undefined") { + win = commonjsGlobal; + } else if (typeof self !== "undefined") { + win = self; + } else { + win = {}; + } + + var window_1 = win; var discardEmulationPreventionBytes = captionPacketParser.discardEmulationPreventionBytes; var CaptionStream = captionStream.CaptionStream; /** @@ -50282,7 +50845,7 @@ * the absolute presentation and decode timestamps of each sample. * * @param {Array} truns - The Trun Run boxes to be parsed - * @param {Number} baseMediaDecodeTime - base media decode time from tfdt + * @param {Number|BigInt} baseMediaDecodeTime - base media decode time from tfdt @see ISO-BMFF-12/2015, Section 8.8.12 * @param {Object} tfhd - The parsed Track Fragment Header * @see inspect.parseTfhd @@ -50320,8 +50883,13 @@ sample.compositionTimeOffset = 0; } - sample.pts = currentDts + sample.compositionTimeOffset; - currentDts += sample.duration; + if (typeof currentDts === 'bigint') { + sample.pts = currentDts + window_1.BigInt(sample.compositionTimeOffset); + currentDts += window_1.BigInt(sample.duration); + } else { + sample.pts = currentDts + sample.compositionTimeOffset; + currentDts += sample.duration; + } }); allSamples = allSamples.concat(samples); }); @@ -50636,6 +51204,7 @@ var captionParser = CaptionParser; var toUnsigned = bin.toUnsigned; var toHexString = bin.toHexString; + var getUint64 = numbers.getUint64; var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks, getTimescaleFromMediaHeader; /** * Parses an MP4 initialization segment and extracts the timescale @@ -50702,38 +51271,47 @@ startTime = function startTime(timescale, fragment) { - var trafs, baseTimes, result; // we need info from two childrend of each track fragment box + var trafs; // we need info from two childrend of each track fragment box trafs = findBox_1(fragment, ['moof', 'traf']); // determine the start times for each track - baseTimes = [].concat.apply([], trafs.map(function (traf) { - return findBox_1(traf, ['tfhd']).map(function (tfhd) { - var id, scale, baseTime; // get the track id from the tfhd + var lowestTime = trafs.reduce(function (acc, traf) { + var tfhd = findBox_1(traf, ['tfhd'])[0]; // get the track id from the tfhd - id = toUnsigned(tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]); // assume a 90kHz clock if no timescale was specified + var id = toUnsigned(tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]); // assume a 90kHz clock if no timescale was specified - scale = timescale[id] || 90e3; // get the base media decode time from the tfdt + var scale = timescale[id] || 90e3; // get the base media decode time from the tfdt - baseTime = findBox_1(traf, ['tfdt']).map(function (tfdt) { - var version, result; - version = tfdt[0]; - result = toUnsigned(tfdt[4] << 24 | tfdt[5] << 16 | tfdt[6] << 8 | tfdt[7]); + var tfdt = findBox_1(traf, ['tfdt'])[0]; + var dv = new DataView(tfdt.buffer, tfdt.byteOffset, tfdt.byteLength); + var baseTime; // version 1 is 64 bit - if (version === 1) { - result *= Math.pow(2, 32); - result += toUnsigned(tfdt[8] << 24 | tfdt[9] << 16 | tfdt[10] << 8 | tfdt[11]); - } + if (tfdt[0] === 1) { + baseTime = getUint64(tfdt.subarray(4, 12)); + } else { + baseTime = dv.getUint32(4); + } // convert base time to seconds if it is a valid number. - return result; - })[0]; - baseTime = typeof baseTime === 'number' && !isNaN(baseTime) ? baseTime : Infinity; // convert base time to seconds - return baseTime / scale; - }); - })); // return the minimum + var seconds; + + if (typeof baseTime === 'bigint') { + seconds = baseTime / window_1.BigInt(scale); + } else if (typeof baseTime === 'number' && !isNaN(baseTime)) { + seconds = baseTime / scale; + } + + if (seconds < Number.MAX_SAFE_INTEGER) { + seconds = Number(seconds); + } - result = Math.min.apply(null, baseTimes); - return isFinite(result) ? result : 0; + if (seconds < acc) { + acc = seconds; + } + + return acc; + }, Infinity); + return typeof lowestTime === 'bigint' || isFinite(lowestTime) ? lowestTime : 0; }; /** * Determine the composition start, in seconds, for an MP4 @@ -50793,7 +51371,18 @@ var timescale = timescales[trackId] || 90e3; // return the composition start time, in seconds - return (baseMediaDecodeTime + compositionTimeOffset) / timescale; + if (typeof baseMediaDecodeTime === 'bigint') { + compositionTimeOffset = window_1.BigInt(compositionTimeOffset); + timescale = window_1.BigInt(timescale); + } + + var result = (baseMediaDecodeTime + compositionTimeOffset) / timescale; + + if (typeof result === 'bigint' && result < Number.MAX_SAFE_INTEGER) { + result = Number(result); + } + + return result; }; /** * Find the trackIds of the video tracks in this source. @@ -52114,7 +52703,7 @@ }; })); var TransmuxWorker = factory(workerCode$1); - /* rollup-plugin-worker-factory end for worker!/Users/gkatsevman/p/http-streaming-release/src/transmuxer-worker.js */ + /* rollup-plugin-worker-factory end for worker!/Users/gsinger/repos/clean/http-streaming/src/transmuxer-worker.js */ var handleData_ = function handleData_(event, transmuxedData, callback) { var _event$data$segment = event.data.segment, @@ -54963,10 +55552,36 @@ return false; }; - var mediaDuration = function mediaDuration(audioTimingInfo, videoTimingInfo) { - var audioDuration = audioTimingInfo && typeof audioTimingInfo.start === 'number' && typeof audioTimingInfo.end === 'number' ? audioTimingInfo.end - audioTimingInfo.start : 0; - var videoDuration = videoTimingInfo && typeof videoTimingInfo.start === 'number' && typeof videoTimingInfo.end === 'number' ? videoTimingInfo.end - videoTimingInfo.start : 0; - return Math.max(audioDuration, videoDuration); + var mediaDuration = function mediaDuration(timingInfos) { + var maxDuration = 0; + ['video', 'audio'].forEach(function (type) { + var typeTimingInfo = timingInfos[type + "TimingInfo"]; + + if (!typeTimingInfo) { + return; + } + + var start = typeTimingInfo.start, + end = typeTimingInfo.end; + var duration; + + if (typeof start === 'bigint' || typeof end === 'bigint') { + duration = window.BigInt(end) - window.BigInt(start); + } else if (typeof start === 'number' && typeof end === 'number') { + duration = end - start; + } + + if (typeof duration !== 'undefined' && duration > maxDuration) { + maxDuration = duration; + } + }); // convert back to a number if it is lower than MAX_SAFE_INTEGER + // as we only need BigInt when we are above that. + + if (typeof maxDuration === 'bigint' && maxDuration < Number.MAX_SAFE_INTEGER) { + maxDuration = Number(maxDuration); + } + + return maxDuration; }; var segmentTooLong = function segmentTooLong(_ref3) { @@ -54999,7 +55614,10 @@ return null; } - var segmentDuration = mediaDuration(segmentInfo.audioTimingInfo, segmentInfo.videoTimingInfo); // Don't report if we lack information. + var segmentDuration = mediaDuration({ + audioTimingInfo: segmentInfo.audioTimingInfo, + videoTimingInfo: segmentInfo.videoTimingInfo + }); // Don't report if we lack information. // // If the segment has a duration of 0 it is either a lack of information or a // metadata only segment and shouldn't be reported here. @@ -59845,7 +60463,7 @@ return TimelineChangeController; }(videojs.EventTarget); - /* rollup-plugin-worker-factory start for worker!/Users/gkatsevman/p/http-streaming-release/src/decrypter-worker.js */ + /* rollup-plugin-worker-factory start for worker!/Users/gsinger/repos/clean/http-streaming/src/decrypter-worker.js */ var workerCode = transform(getWorkerString(function () { @@ -60525,7 +61143,7 @@ }; })); var Decrypter = factory(workerCode); - /* rollup-plugin-worker-factory end for worker!/Users/gkatsevman/p/http-streaming-release/src/decrypter-worker.js */ + /* rollup-plugin-worker-factory end for worker!/Users/gsinger/repos/clean/http-streaming/src/decrypter-worker.js */ /** * Convert the properties of an HLS track into an audioTrackKind. @@ -62918,10 +63536,27 @@ }; _proto.onSyncInfoUpdate_ = function onSyncInfoUpdate_() { - var audioSeekable; // If we have two source buffers and only one is created then the seekable range will be incorrect. - // We should wait until all source buffers are created. + var audioSeekable; // TODO check for creation of both source buffers before updating seekable + // + // A fix was made to this function where a check for + // this.sourceUpdater_.hasCreatedSourceBuffers + // was added to ensure that both source buffers were created before seekable was + // updated. However, it originally had a bug where it was checking for a true and + // returning early instead of checking for false. Setting it to check for false to + // return early though created other issues. A call to play() would check for seekable + // end without verifying that a seekable range was present. In addition, even checking + // for that didn't solve some issues, as handleFirstPlay is sometimes worked around + // due to a media update calling load on the segment loaders, skipping a seek to live, + // thereby starting live streams at the beginning of the stream rather than at the end. + // + // This conditional should be fixed to wait for the creation of two source buffers at + // the same time as the other sections of code are fixed to properly seek to live and + // not throw an error due to checking for a seekable end when no seekable range exists. + // + // For now, fall back to the older behavior, with the understanding that the seekable + // range may not be completely correct, leading to a suboptimal initial live point. - if (!this.masterPlaylistLoader_ || this.sourceUpdater_.hasCreatedSourceBuffers()) { + if (!this.masterPlaylistLoader_) { return; } @@ -64256,10 +64891,6 @@ return; } - if(player == null){ - return; - } - seekTo = player.duration() !== Infinity && player.currentTime() || 0; player.one('loadedmetadata', loadedMetadataHandler); player.src(sourceObj); @@ -64347,9 +64978,9 @@ initPlugin(this, options); }; - var version$4 = "2.12.0"; - var version$3 = "5.14.1"; - var version$2 = "0.19.2"; + var version$4 = "2.13.1"; + var version$3 = "6.0.1"; + var version$2 = "0.21.0"; var version$1 = "4.7.0"; var version = "3.1.2"; var Vhs = { @@ -65306,6 +65937,43 @@ this.mediaSourceUrl_ = window.URL.createObjectURL(this.masterPlaylistController_.mediaSource); this.tech_.src(this.mediaSourceUrl_); + }; + + _proto.createKeySessions_ = function createKeySessions_() { + var _this4 = this; + + var audioPlaylistLoader = this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader; + this.logger_('waiting for EME key session creation'); + waitForKeySessionCreation({ + player: this.player_, + sourceKeySystems: this.source_.keySystems, + audioMedia: audioPlaylistLoader && audioPlaylistLoader.media(), + mainPlaylists: this.playlists.master.playlists + }).then(function () { + _this4.logger_('created EME key session'); + + _this4.masterPlaylistController_.sourceUpdater_.initializedEme(); + })["catch"](function (err) { + _this4.logger_('error while creating EME key session', err); + + _this4.player_.error({ + message: 'Failed to initialize media keys for EME', + code: 3 + }); + }); + }; + + _proto.handleWaitingForKey_ = function handleWaitingForKey_() { + // If waitingforkey is fired, it's possible that the data that's necessary to retrieve + // the key is in the manifest. While this should've happened on initial source load, it + // may happen again in live streams where the keys change, and the manifest info + // reflects the update. + // + // Because videojs-contrib-eme compares the PSSH data we send to that of PSSH data it's + // already requested keys for, we don't have to worry about this generating extraneous + // requests. + this.logger_('waitingforkey fired, attempting to create any new key sessions'); + this.createKeySessions_(); } /** * If necessary and EME is available, sets up EME options and waits for key session @@ -65318,7 +65986,7 @@ ; _proto.setupEme_ = function setupEme_() { - var _this4 = this; + var _this5 = this; var audioPlaylistLoader = this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader; var didSetupEmeOptions = setupEmeOptions({ @@ -65329,13 +65997,15 @@ }); this.player_.tech_.on('keystatuschange', function (e) { if (e.status === 'output-restricted') { - _this4.masterPlaylistController_.blacklistCurrentPlaylist({ - playlist: _this4.masterPlaylistController_.media(), + _this5.masterPlaylistController_.blacklistCurrentPlaylist({ + playlist: _this5.masterPlaylistController_.media(), message: "DRM keystatus changed to " + e.status + ". Playlist will fail to play. Check for HDCP content.", blacklistDuration: Infinity }); } - }); // In IE11 this is too early to initialize media keys, and IE11 does not support + }); + this.handleWaitingForKey_ = this.handleWaitingForKey_.bind(this); + this.player_.tech_.on('waitingforkey', this.handleWaitingForKey_); // In IE11 this is too early to initialize media keys, and IE11 does not support // promises. if (videojs.browser.IE_VERSION === 11 || !didSetupEmeOptions) { @@ -65344,24 +66014,7 @@ return; } - this.logger_('waiting for EME key session creation'); - waitForKeySessionCreation({ - player: this.player_, - sourceKeySystems: this.source_.keySystems, - audioMedia: audioPlaylistLoader && audioPlaylistLoader.media(), - mainPlaylists: this.playlists.master.playlists - }).then(function () { - _this4.logger_('created EME key session'); - - _this4.masterPlaylistController_.sourceUpdater_.initializedEme(); - })["catch"](function (err) { - _this4.logger_('error while creating EME key session', err); - - _this4.player_.error({ - message: 'Failed to initialize media keys for EME', - code: 3 - }); - }); + this.createKeySessions_(); } /** * Initializes the quality levels and sets listeners to update them. @@ -65372,7 +66025,7 @@ ; _proto.setupQualityLevels_ = function setupQualityLevels_() { - var _this5 = this; + var _this6 = this; var player = videojs.players[this.tech_.options_.playerId]; // if there isn't a player or there isn't a qualityLevels plugin // or qualityLevels_ listeners have already been setup, do nothing. @@ -65383,10 +66036,10 @@ this.qualityLevels_ = player.qualityLevels(); this.masterPlaylistController_.on('selectedinitialmedia', function () { - handleVhsLoadedMetadata(_this5.qualityLevels_, _this5); + handleVhsLoadedMetadata(_this6.qualityLevels_, _this6); }); this.playlists.on('mediachange', function () { - handleVhsMediaChange(_this5.qualityLevels_, _this5.playlists); + handleVhsMediaChange(_this6.qualityLevels_, _this6.playlists); }); } /** @@ -65485,6 +66138,10 @@ this.mediaSourceUrl_ = null; } + if (this.tech_) { + this.tech_.off('waitingforkey', this.handleWaitingForKey_); + } + _Component.prototype.dispose.call(this); }; @@ -65567,11 +66224,17 @@ } var _videojs$mergeOptions = videojs.mergeOptions(videojs.options, options), - _videojs$mergeOptions2 = _videojs$mergeOptions.vhs.overrideNative, - overrideNative = _videojs$mergeOptions2 === void 0 ? !videojs.browser.IS_ANY_SAFARI : _videojs$mergeOptions2; - + _videojs$mergeOptions2 = _videojs$mergeOptions.vhs; + + _videojs$mergeOptions2 = _videojs$mergeOptions2 === void 0 ? {} : _videojs$mergeOptions2; + var _videojs$mergeOptions3 = _videojs$mergeOptions2.overrideNative, + overrideNative = _videojs$mergeOptions3 === void 0 ? !videojs.browser.IS_ANY_SAFARI : _videojs$mergeOptions3, + _videojs$mergeOptions4 = _videojs$mergeOptions.hls; + _videojs$mergeOptions4 = _videojs$mergeOptions4 === void 0 ? {} : _videojs$mergeOptions4; + var _videojs$mergeOptions5 = _videojs$mergeOptions4.overrideNative, + legacyOverrideNative = _videojs$mergeOptions5 === void 0 ? false : _videojs$mergeOptions5; var supportedType = simpleTypeFromSourceType(type); - var canUseMsePlayback = supportedType && (!Vhs.supportsTypeNatively(supportedType) || overrideNative); + var canUseMsePlayback = supportedType && (!Vhs.supportsTypeNatively(supportedType) || legacyOverrideNative || overrideNative); return canUseMsePlayback ? 'maybe' : ''; } }; @@ -65631,4 +66294,4 @@ return videojs; -}))); +}))); \ No newline at end of file -- 2.54.0