git-tfs-id: [http://tfs.userrendszerhaz.hu:8080/tfs/DefaultCollection]$/MediaCube...
authorSweidan Omar <TFS\sweidan.omar>
Wed, 30 Mar 2022 08:49:32 +0000 (08:49 +0000)
committerSweidan Omar <TFS\sweidan.omar>
Wed, 30 Mar 2022 08:49:32 +0000 (08:49 +0000)
server/user.mediacube.gui/js/video.js

index 3a6f9d6898cb2d75d6c1609f8d190259fd19f3ab..5151e7e42f2e311118cb7ced4f1e73a4ae2eb6ee 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * @license
- * Video.js 7.17.0 <http://videojs.com/>
+ * Video.js 7.18.1 <http://videojs.com/>
  * Copyright Brightcove, Inc. <https://www.brightcove.com/>
  * Available under Apache License Version 2.0
  * <https://github.com/videojs/video.js/blob/main/LICENSE>
@@ -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
 
   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
    *
     _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);
       }
     }
     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
     _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;
     }
     /**
       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`.
      * @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
 
 
       event.stopPropagation();
-      this.player_.scrubbing(true);
       this.videoWasPlaying = !this.player_.paused();
       this.player_.pause();
 
      *
      * @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;
 
     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')
         }));
       }
 
       // 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.
     _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.
       // 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
       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() {
     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;
     }
    * @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
     ;
 
     _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);
     ;
 
     _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);
         });
       }
 
     ;
 
     _proto.play_ = function play_(callback) {
-      var _this10 = this;
+      var _this11 = this;
 
       if (callback === void 0) {
         callback = silencePromise;
 
       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
     };
 
     _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.
 
         if (promise) {
           promise.then(function () {
-            return _this11.isFullscreen(true);
+            return _this12.isFullscreen(true);
           }, function () {
-            return _this11.isFullscreen(false);
+            return _this12.isFullscreen(false);
           });
         }
 
     };
 
     _proto.exitFullscreenHelper_ = function exitFullscreenHelper_() {
-      var _this12 = this;
+      var _this13 = this;
 
       if (this.fsApi_.requestFullscreen) {
         var promise = document[this.fsApi_.exitFullscreen]();
           // 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);
           }));
         }
 
     ;
 
     _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
         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
     ;
 
     _proto.handleSrc_ = function handleSrc_(source, isRetry) {
-      var _this14 = this;
+      var _this15 = this;
 
       // getter usage
       if (typeof source === 'undefined') {
       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)
           // 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);
         };
       }
     }
     ;
 
     _proto.src_ = function src_(source) {
-      var _this15 = this;
+      var _this16 = this;
 
       var sourceTech = this.selectSource([source]);
 
 
         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
     ;
 
     _proto.reset = function reset() {
-      var _this16 = this;
+      var _this17 = this;
 
       var PromiseClass = this.options_.Promise || window.Promise;
 
       } else {
         var playPromise = this.play();
         silencePromise(playPromise.then(function () {
-          return _this16.doReset_();
+          return _this17.doReset_();
         }));
       }
     };
     ;
 
     _proto.error = function error(err) {
-      var _this17 = this;
+      var _this18 = this;
 
       if (err === undefined) {
         return this.error_ || null;
 
 
       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;
         }
       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;
     }
     ;
 
     _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;
     ;
 
     _proto.loadMedia = function loadMedia(media, ready) {
-      var _this19 = this;
+      var _this20 = this;
 
       if (!media || typeof media !== 'object') {
         return;
 
       if (Array.isArray(textTracks)) {
         textTracks.forEach(function (tt) {
-          return _this19.addRemoteTextTrack(tt, false);
+          return _this20.addRemoteTextTrack(tt, false);
         });
       }
 
     })();
   });
 
-  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;
     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);
     }
       // 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);
       }
     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);
+    });
   };
 
   /**
 
   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';
       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',
 
     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
       };
     }
   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;
   };
   /**
           _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);
    */
 
   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
       };
     };
   };
         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) {
       }
     } 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];
   };
   /**
    */
 
 
-  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
 
       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
 
       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,
       }
 
       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);
   };
       // 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;
     }
 
     for (var i in playlists) {
-      playlists[i] = addSidxSegmentsToPlaylist$1(playlists[i], sidxMapping);
+      playlists[i] = addSidxSegmentsToPlaylist(playlists[i], sidxMapping);
     }
 
     return playlists;
 
     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,
       }, _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) {
     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
       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
     };
   };
 
         };
       }
 
-      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') {
         };
       }
 
-      a[label].playlists.push(addSidxSegmentsToPlaylist$1(formatVttPlaylist(playlist), sidxMapping));
+      a[label].playlists.push(addSidxSegmentsToPlaylist(formatVttPlaylist(playlist), sidxMapping));
       return a;
     }, {});
   };
 
     var attributes = _ref3.attributes,
         segments = _ref3.segments,
-        sidx = _ref3.sidx;
+        sidx = _ref3.sidx,
+        discontinuityStarts = _ref3.discontinuityStarts;
     var playlist = {
       attributes: (_attributes2 = {
         NAME: attributes.id,
       }, _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) {
     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
         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);
     }
 
     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;
   };
   /**
         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);
         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;
 
         number: attributes.startNumber || 1,
         duration: attributes.sourceDuration,
         time: 0,
-        timeline: attributes.periodIndex
+        timeline: attributes.periodStart
       }];
     }
 
    * @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
    */
   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
       });
 
 
     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) {
 
     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
 
   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 = {
       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;
     }
 
   };
   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
   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;
   };
   /**
     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
     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++) {
         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);
         }
       }
 
           sidxInfo: playlist.sidx,
           sidx: sidx
         };
-        addSidxSegmentsToPlaylist(playlist, sidx, playlist.sidx.resolvedUri);
+        addSidxSegmentsToPlaylist$1(playlist, sidx, playlist.sidx.resolvedUri);
         return cb(true);
       };
 
     _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_);
   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 () {
     };
 
     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 () {
       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
 
     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;
     };
 
     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;
     /**
       * the absolute presentation and decode timestamps of each sample.
       *
       * @param {Array<Uint8Array>} 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
             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);
       });
     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
 
 
     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
 
       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.
     };
   }));
   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,
     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) {
       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.
 
     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 () {
     };
   }));
   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.
     };
 
     _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;
       }
 
         return;
       }
 
-         if(player == null){
-               return;
-         }
-         
       seekTo = player.duration() !== Infinity && player.currentTime() || 0;
       player.one('loadedmetadata', loadedMetadataHandler);
       player.src(sourceObj);
     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 = {
 
       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
     ;
 
     _proto.setupEme_ = function setupEme_() {
-      var _this4 = this;
+      var _this5 = this;
 
       var audioPlaylistLoader = this.masterPlaylistController_.mediaTypes_.AUDIO.activePlaylistLoader;
       var didSetupEmeOptions = setupEmeOptions({
       });
       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) {
         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.
     ;
 
     _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.
 
       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);
       });
     }
     /**
         this.mediaSourceUrl_ = null;
       }
 
+      if (this.tech_) {
+        this.tech_.off('waitingforkey', this.handleWaitingForKey_);
+      }
+
       _Component.prototype.dispose.call(this);
     };
 
       }
 
       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' : '';
     }
   };
 
   return videojs;
 
-})));
+})));
\ No newline at end of file