hls.js 1.6.0-beta.2.0.canary.10924 → 1.6.0-beta.2.0.canary.10926

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/hls.js CHANGED
@@ -1059,7 +1059,7 @@
1059
1059
  // Some browsers don't allow to use bind on console object anyway
1060
1060
  // fallback to default if needed
1061
1061
  try {
1062
- newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10924");
1062
+ newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10926");
1063
1063
  } catch (e) {
1064
1064
  /* log fn threw an exception. All logger methods are no-ops. */
1065
1065
  return createLogger();
@@ -2049,8 +2049,8 @@
2049
2049
  return -1;
2050
2050
  }
2051
2051
  function useAlternateAudio(audioTrackUrl, hls) {
2052
- var _hls$levels$hls$loadL;
2053
- return !!audioTrackUrl && audioTrackUrl !== ((_hls$levels$hls$loadL = hls.levels[hls.loadLevel]) == null ? undefined : _hls$levels$hls$loadL.uri);
2052
+ var _hls$loadLevelObj;
2053
+ return !!audioTrackUrl && audioTrackUrl !== ((_hls$loadLevelObj = hls.loadLevelObj) == null ? undefined : _hls$loadLevelObj.uri);
2054
2054
  }
2055
2055
 
2056
2056
  var AbrController = /*#__PURE__*/function (_Logger) {
@@ -2470,8 +2470,8 @@
2470
2470
  }
2471
2471
  // If no matching level found, see if min auto level would be a better option
2472
2472
  var minLevel = hls.levels[minAutoLevel];
2473
- var autoLevel = hls.levels[hls.loadLevel];
2474
- if ((minLevel == null ? undefined : minLevel.bitrate) < (autoLevel == null ? undefined : autoLevel.bitrate)) {
2473
+ var autoLevel = hls.loadLevelObj;
2474
+ if (autoLevel && (minLevel == null ? undefined : minLevel.bitrate) < autoLevel.bitrate) {
2475
2475
  return minAutoLevel;
2476
2476
  }
2477
2477
  // or if bitrate is not lower, continue to use loadLevel
@@ -3086,7 +3086,7 @@
3086
3086
  case ErrorDetails.SUBTITLE_LOAD_ERROR:
3087
3087
  case ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT:
3088
3088
  if (context) {
3089
- var level = hls.levels[hls.loadLevel];
3089
+ var level = hls.loadLevelObj;
3090
3090
  if (level && (context.type === PlaylistContextType.AUDIO_TRACK && level.hasAudioGroup(context.groupId) || context.type === PlaylistContextType.SUBTITLE_TRACK && level.hasSubtitleGroup(context.groupId))) {
3091
3091
  // Perform Pathway switch or Redundant failover if possible for fastest recovery
3092
3092
  // otherwise allow playlist retry count to reach max error retries
@@ -3099,7 +3099,7 @@
3099
3099
  return;
3100
3100
  case ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:
3101
3101
  {
3102
- var _level = hls.levels[hls.loadLevel];
3102
+ var _level = hls.loadLevelObj;
3103
3103
  var restrictedHdcpLevel = _level == null ? undefined : _level.attrs['HDCP-LEVEL'];
3104
3104
  if (restrictedHdcpLevel) {
3105
3105
  data.errorAction = {
@@ -6504,38 +6504,55 @@
6504
6504
  }
6505
6505
  return false;
6506
6506
  };
6507
+ BufferHelper.bufferedRanges = function bufferedRanges(media) {
6508
+ if (media) {
6509
+ var timeRanges = BufferHelper.getBuffered(media);
6510
+ return BufferHelper.timeRangesToArray(timeRanges);
6511
+ }
6512
+ return [];
6513
+ };
6514
+ BufferHelper.timeRangesToArray = function timeRangesToArray(timeRanges) {
6515
+ var buffered = [];
6516
+ for (var i = 0; i < timeRanges.length; i++) {
6517
+ buffered.push({
6518
+ start: timeRanges.start(i),
6519
+ end: timeRanges.end(i)
6520
+ });
6521
+ }
6522
+ return buffered;
6523
+ };
6507
6524
  BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) {
6508
6525
  if (media) {
6509
- var vbuffered = BufferHelper.getBuffered(media);
6510
- if (vbuffered.length) {
6511
- var buffered = [];
6512
- for (var i = 0; i < vbuffered.length; i++) {
6513
- buffered.push({
6514
- start: vbuffered.start(i),
6515
- end: vbuffered.end(i)
6516
- });
6517
- }
6526
+ var buffered = BufferHelper.bufferedRanges(media);
6527
+ if (buffered.length) {
6518
6528
  return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
6519
6529
  }
6520
6530
  }
6521
6531
  return {
6522
6532
  len: 0,
6523
6533
  start: pos,
6524
- end: pos
6534
+ end: pos,
6535
+ bufferedIndex: -1
6525
6536
  };
6526
6537
  };
6527
6538
  BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) {
6528
6539
  pos = Math.max(0, pos);
6529
6540
  // sort on buffer.start/smaller end (IE does not always return sorted buffered range)
6530
- buffered.sort(function (a, b) {
6531
- return a.start - b.start || b.end - a.end;
6532
- });
6541
+ if (buffered.length > 1) {
6542
+ buffered.sort(function (a, b) {
6543
+ return a.start - b.start || b.end - a.end;
6544
+ });
6545
+ }
6546
+ var bufferedIndex = -1;
6533
6547
  var buffered2 = [];
6534
6548
  if (maxHoleDuration) {
6535
6549
  // there might be some small holes between buffer time range
6536
6550
  // consider that holes smaller than maxHoleDuration are irrelevant and build another
6537
6551
  // buffer time range representations that discards those holes
6538
6552
  for (var i = 0; i < buffered.length; i++) {
6553
+ if (pos >= buffered[i].start && pos <= buffered[i].end) {
6554
+ bufferedIndex = i;
6555
+ }
6539
6556
  var buf2len = buffered2.length;
6540
6557
  if (buf2len) {
6541
6558
  var buf2end = buffered2[buf2len - 1].end;
@@ -6561,24 +6578,25 @@
6561
6578
  buffered2 = buffered;
6562
6579
  }
6563
6580
  var bufferLen = 0;
6581
+ var nextStart;
6564
6582
 
6565
- // bufferStartNext can possibly be undefined based on the conditional logic below
6566
- var bufferStartNext;
6567
-
6568
- // bufferStart and bufferEnd are buffer boundaries around current video position
6583
+ // bufferStart and bufferEnd are buffer boundaries around current playback position (pos)
6569
6584
  var bufferStart = pos;
6570
6585
  var bufferEnd = pos;
6571
6586
  for (var _i = 0; _i < buffered2.length; _i++) {
6572
6587
  var start = buffered2[_i].start;
6573
6588
  var end = buffered2[_i].end;
6574
6589
  // logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));
6590
+ if (bufferedIndex === -1 && pos >= start && pos <= end) {
6591
+ bufferedIndex = _i;
6592
+ }
6575
6593
  if (pos + maxHoleDuration >= start && pos < end) {
6576
6594
  // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length
6577
6595
  bufferStart = start;
6578
6596
  bufferEnd = end;
6579
6597
  bufferLen = bufferEnd - pos;
6580
6598
  } else if (pos + maxHoleDuration < start) {
6581
- bufferStartNext = start;
6599
+ nextStart = start;
6582
6600
  break;
6583
6601
  }
6584
6602
  }
@@ -6586,8 +6604,9 @@
6586
6604
  len: bufferLen,
6587
6605
  start: bufferStart || 0,
6588
6606
  end: bufferEnd || 0,
6589
- nextStart: bufferStartNext,
6590
- buffered: buffered
6607
+ nextStart: nextStart,
6608
+ buffered: buffered,
6609
+ bufferedIndex: bufferedIndex
6591
6610
  };
6592
6611
  }
6593
6612
 
@@ -8866,7 +8885,6 @@
8866
8885
  // reset startPosition and lastCurrentTime to restart playback @ stream beginning
8867
8886
  _this.log("setting startPosition to 0 because media ended");
8868
8887
  _this.startPosition = _this.lastCurrentTime = 0;
8869
- _this.triggerEnded();
8870
8888
  };
8871
8889
  _this.playlistType = playlistType;
8872
8890
  _this.hls = hls;
@@ -9015,9 +9033,6 @@
9015
9033
  this.startFragRequested = false;
9016
9034
  };
9017
9035
  _proto.onError = function onError(event, data) {};
9018
- _proto.triggerEnded = function triggerEnded() {
9019
- /* overridden in stream-controller */
9020
- };
9021
9036
  _proto.onManifestLoaded = function onManifestLoaded(event, data) {
9022
9037
  this.startTimeOffset = data.startTimeOffset;
9023
9038
  };
@@ -9614,7 +9629,8 @@
9614
9629
  if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
9615
9630
  var bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
9616
9631
  if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) {
9617
- return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
9632
+ var gapDuration = Math.max(Math.min(bufferInfo.nextStart, bufferedFragAtPos.end) - pos, maxBufferHole);
9633
+ return BufferHelper.bufferInfo(bufferable, pos, gapDuration);
9618
9634
  }
9619
9635
  }
9620
9636
  return bufferInfo;
@@ -10194,6 +10210,14 @@
10194
10210
  get: function get() {
10195
10211
  return this.buffering;
10196
10212
  }
10213
+ }, {
10214
+ key: "inFlightFrag",
10215
+ get: function get() {
10216
+ return {
10217
+ frag: this.fragCurrent,
10218
+ state: this.state
10219
+ };
10220
+ }
10197
10221
  }, {
10198
10222
  key: "state",
10199
10223
  get: function get() {
@@ -16311,7 +16335,7 @@
16311
16335
  return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment;
16312
16336
  }
16313
16337
 
16314
- var version = "1.6.0-beta.2.0.canary.10924";
16338
+ var version = "1.6.0-beta.2.0.canary.10926";
16315
16339
 
16316
16340
  // ensure the worker ends up in the bundle
16317
16341
  // If the worker should not be included this gets aliased to empty.js
@@ -16682,7 +16706,7 @@
16682
16706
  return TransmuxerInterface;
16683
16707
  }();
16684
16708
 
16685
- var TICK_INTERVAL$2 = 100; // how often to tick in ms
16709
+ var TICK_INTERVAL$3 = 100; // how often to tick in ms
16686
16710
  var AudioStreamController = /*#__PURE__*/function (_BaseStreamController) {
16687
16711
  function AudioStreamController(hls, fragmentTracker, keyLoader) {
16688
16712
  var _this;
@@ -16793,7 +16817,7 @@
16793
16817
  }
16794
16818
  var lastCurrentTime = this.lastCurrentTime;
16795
16819
  this.stopLoad();
16796
- this.setInterval(TICK_INTERVAL$2);
16820
+ this.setInterval(TICK_INTERVAL$3);
16797
16821
  if (lastCurrentTime > 0 && startPosition === -1) {
16798
16822
  this.log("Override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3));
16799
16823
  startPosition = lastCurrentTime;
@@ -17025,7 +17049,7 @@
17025
17049
  this.flushAudioIfNeeded(data);
17026
17050
  if (this.state !== State.STOPPED) {
17027
17051
  // switching to audio track, start timer if not already started
17028
- this.setInterval(TICK_INTERVAL$2);
17052
+ this.setInterval(TICK_INTERVAL$3);
17029
17053
  this.state = State.IDLE;
17030
17054
  this.tick();
17031
17055
  }
@@ -18732,7 +18756,6 @@
18732
18756
  var sbTrack = transferredTrack != null && transferredTrack.buffer ? transferredTrack : track;
18733
18757
  var sbCodec = (sbTrack == null ? undefined : sbTrack.pendingCodec) || (sbTrack == null ? undefined : sbTrack.codec);
18734
18758
  var trackLevelCodec = sbTrack == null ? undefined : sbTrack.levelCodec;
18735
- var forceChangeType = !sbTrack || !!_this9.hls.config.assetPlayerId;
18736
18759
  if (!track) {
18737
18760
  track = tracks[trackName] = {
18738
18761
  buffer: undefined,
@@ -18749,7 +18772,7 @@
18749
18772
  var currentCodec = currentCodecFull == null ? undefined : currentCodecFull.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1');
18750
18773
  var trackCodec = pickMostCompleteCodecName(codec, levelCodec);
18751
18774
  var nextCodec = (_trackCodec = trackCodec) == null ? undefined : _trackCodec.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1');
18752
- if (trackCodec && (currentCodec !== nextCodec || forceChangeType)) {
18775
+ if (trackCodec && currentCodecFull && currentCodec !== nextCodec) {
18753
18776
  if (trackName.slice(0, 5) === 'audio') {
18754
18777
  trackCodec = getCodecCompatibleName(trackCodec, _this9.appendSource);
18755
18778
  }
@@ -23839,6 +23862,14 @@
23839
23862
  return AssetListLoader;
23840
23863
  }();
23841
23864
 
23865
+ function addEventListener(el, type, listener) {
23866
+ removeEventListener(el, type, listener);
23867
+ el.addEventListener(type, listener);
23868
+ }
23869
+ function removeEventListener(el, type, listener) {
23870
+ el.removeEventListener(type, listener);
23871
+ }
23872
+
23842
23873
  function playWithCatch(media) {
23843
23874
  media == null ? undefined : media.play().catch(function () {
23844
23875
  /* no-op */
@@ -24154,24 +24185,23 @@
24154
24185
  this.onScheduleUpdate = null;
24155
24186
  };
24156
24187
  _proto.onDestroying = function onDestroying() {
24157
- var media = this.primaryMedia;
24188
+ var media = this.primaryMedia || this.media;
24158
24189
  if (media) {
24159
24190
  this.removeMediaListeners(media);
24160
24191
  }
24161
24192
  };
24162
24193
  _proto.removeMediaListeners = function removeMediaListeners(media) {
24163
- media.removeEventListener('play', this.onPlay);
24164
- media.removeEventListener('pause', this.onPause);
24165
- media.removeEventListener('seeking', this.onSeeking);
24166
- media.removeEventListener('timeupdate', this.onTimeupdate);
24194
+ removeEventListener(media, 'play', this.onPlay);
24195
+ removeEventListener(media, 'pause', this.onPause);
24196
+ removeEventListener(media, 'seeking', this.onSeeking);
24197
+ removeEventListener(media, 'timeupdate', this.onTimeupdate);
24167
24198
  };
24168
24199
  _proto.onMediaAttaching = function onMediaAttaching(event, data) {
24169
24200
  var media = this.media = data.media;
24170
- this.removeMediaListeners(media);
24171
- media.addEventListener('seeking', this.onSeeking);
24172
- media.addEventListener('timeupdate', this.onTimeupdate);
24173
- media.addEventListener('play', this.onPlay);
24174
- media.addEventListener('pause', this.onPause);
24201
+ addEventListener(media, 'seeking', this.onSeeking);
24202
+ addEventListener(media, 'timeupdate', this.onTimeupdate);
24203
+ addEventListener(media, 'play', this.onPlay);
24204
+ addEventListener(media, 'pause', this.onPause);
24175
24205
  };
24176
24206
  _proto.onMediaAttached = function onMediaAttached(event, data) {
24177
24207
  var playingItem = this.playingItem;
@@ -24975,7 +25005,7 @@
24975
25005
  var primary = this.hls;
24976
25006
  var userConfig = primary.userConfig;
24977
25007
  var videoPreference = userConfig.videoPreference;
24978
- var currentLevel = primary.levels[primary.loadLevel] || primary.levels[primary.currentLevel];
25008
+ var currentLevel = primary.loadLevelObj || primary.levels[primary.currentLevel];
24979
25009
  if (videoPreference || currentLevel) {
24980
25010
  videoPreference = _extends({}, videoPreference);
24981
25011
  if (currentLevel.videoCodec) {
@@ -25676,7 +25706,7 @@
25676
25706
  }]);
25677
25707
  }(Logger);
25678
25708
 
25679
- var TICK_INTERVAL$1 = 500; // how often to tick in ms
25709
+ var TICK_INTERVAL$2 = 500; // how often to tick in ms
25680
25710
 
25681
25711
  var SubtitleStreamController = /*#__PURE__*/function (_BaseStreamController) {
25682
25712
  function SubtitleStreamController(hls, fragmentTracker, keyLoader) {
@@ -25718,7 +25748,7 @@
25718
25748
  _proto.startLoad = function startLoad(startPosition) {
25719
25749
  this.stopLoad();
25720
25750
  this.state = State.IDLE;
25721
- this.setInterval(TICK_INTERVAL$1);
25751
+ this.setInterval(TICK_INTERVAL$2);
25722
25752
  this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;
25723
25753
  this.tick();
25724
25754
  };
@@ -25854,7 +25884,7 @@
25854
25884
  this.mediaBuffer = null;
25855
25885
  }
25856
25886
  if (currentTrack && this.state !== State.STOPPED) {
25857
- this.setInterval(TICK_INTERVAL$1);
25887
+ this.setInterval(TICK_INTERVAL$2);
25858
25888
  }
25859
25889
  }
25860
25890
 
@@ -30255,16 +30285,20 @@
30255
30285
  frontBufferFlushThreshold: Infinity,
30256
30286
  maxBufferSize: 60 * 1000 * 1000,
30257
30287
  // used by stream-controller
30258
- maxBufferHole: 0.1,
30288
+ maxFragLookUpTolerance: 0.25,
30259
30289
  // used by stream-controller
30290
+ maxBufferHole: 0.1,
30291
+ // used by stream-controller and gap-controller
30292
+ detectStallWithCurrentTimeMs: 1250,
30293
+ // used by gap-controller
30260
30294
  highBufferWatchdogPeriod: 2,
30261
- // used by stream-controller
30295
+ // used by gap-controller
30262
30296
  nudgeOffset: 0.1,
30263
- // used by stream-controller
30297
+ // used by gap-controller
30264
30298
  nudgeMaxRetry: 3,
30265
- // used by stream-controller
30266
- maxFragLookUpTolerance: 0.25,
30267
- // used by stream-controller
30299
+ // used by gap-controller
30300
+ nudgeOnVideoHole: true,
30301
+ // used by gap-controller
30268
30302
  liveSyncDurationCount: 3,
30269
30303
  // used by latency-controller
30270
30304
  liveSyncOnStallIncrease: 1,
@@ -30363,7 +30397,6 @@
30363
30397
  progressive: false,
30364
30398
  lowLatencyMode: true,
30365
30399
  cmcd: undefined,
30366
- detectStallWithCurrentTimeMs: 1250,
30367
30400
  enableDateRangeMetadataCues: true,
30368
30401
  enableEmsgMetadataCues: true,
30369
30402
  enableEmsgKLVMetadata: false,
@@ -30618,6 +30651,545 @@
30618
30651
  }
30619
30652
  }
30620
30653
 
30654
+ var MAX_START_GAP_JUMP = 2.0;
30655
+ var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
30656
+ var SKIP_BUFFER_RANGE_START = 0.05;
30657
+ var TICK_INTERVAL$1 = 100;
30658
+ var GapController = /*#__PURE__*/function (_TaskLoop) {
30659
+ function GapController(hls, fragmentTracker) {
30660
+ var _this;
30661
+ _this = _TaskLoop.call(this, 'gap-controller', hls.logger) || this;
30662
+ _this.hls = null;
30663
+ _this.fragmentTracker = null;
30664
+ _this.media = null;
30665
+ _this.mediaSource = undefined;
30666
+ _this.nudgeRetry = 0;
30667
+ _this.stallReported = false;
30668
+ _this.stalled = null;
30669
+ _this.moved = false;
30670
+ _this.seeking = false;
30671
+ _this.buffered = {};
30672
+ _this.lastCurrentTime = 0;
30673
+ _this.ended = 0;
30674
+ _this.waiting = 0;
30675
+ _this.onMediaPlaying = function () {
30676
+ _this.ended = 0;
30677
+ _this.waiting = 0;
30678
+ };
30679
+ _this.onMediaWaiting = function () {
30680
+ var _this$media;
30681
+ if ((_this$media = _this.media) != null && _this$media.seeking) {
30682
+ return;
30683
+ }
30684
+ _this.waiting = self.performance.now();
30685
+ _this.tick();
30686
+ };
30687
+ _this.onMediaEnded = function () {
30688
+ if (_this.hls) {
30689
+ var _this$media2;
30690
+ // ended is set when triggering MEDIA_ENDED so that we do not trigger it again on stall or on tick with media.ended
30691
+ _this.ended = ((_this$media2 = _this.media) == null ? undefined : _this$media2.currentTime) || 1;
30692
+ _this.hls.trigger(Events.MEDIA_ENDED, {
30693
+ stalled: false
30694
+ });
30695
+ }
30696
+ };
30697
+ _this.hls = hls;
30698
+ _this.fragmentTracker = fragmentTracker;
30699
+ _this.registerListeners();
30700
+ return _this;
30701
+ }
30702
+ _inheritsLoose(GapController, _TaskLoop);
30703
+ var _proto = GapController.prototype;
30704
+ _proto.registerListeners = function registerListeners() {
30705
+ var hls = this.hls;
30706
+ if (hls) {
30707
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
30708
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
30709
+ hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
30710
+ }
30711
+ };
30712
+ _proto.unregisterListeners = function unregisterListeners() {
30713
+ var hls = this.hls;
30714
+ if (hls) {
30715
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
30716
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
30717
+ hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
30718
+ }
30719
+ };
30720
+ _proto.destroy = function destroy() {
30721
+ _TaskLoop.prototype.destroy.call(this);
30722
+ this.unregisterListeners();
30723
+ this.media = this.hls = this.fragmentTracker = null;
30724
+ this.mediaSource = undefined;
30725
+ };
30726
+ _proto.onMediaAttached = function onMediaAttached(event, data) {
30727
+ this.setInterval(TICK_INTERVAL$1);
30728
+ this.mediaSource = data.mediaSource;
30729
+ var media = this.media = data.media;
30730
+ addEventListener(media, 'playing', this.onMediaPlaying);
30731
+ addEventListener(media, 'waiting', this.onMediaWaiting);
30732
+ addEventListener(media, 'ended', this.onMediaEnded);
30733
+ };
30734
+ _proto.onMediaDetaching = function onMediaDetaching(event, data) {
30735
+ this.clearInterval();
30736
+ var media = this.media;
30737
+ if (media) {
30738
+ removeEventListener(media, 'playing', this.onMediaPlaying);
30739
+ removeEventListener(media, 'waiting', this.onMediaWaiting);
30740
+ removeEventListener(media, 'ended', this.onMediaEnded);
30741
+ this.media = null;
30742
+ }
30743
+ this.mediaSource = undefined;
30744
+ };
30745
+ _proto.onBufferAppended = function onBufferAppended(event, data) {
30746
+ this.buffered = data.timeRanges;
30747
+ };
30748
+ _proto.tick = function tick() {
30749
+ var _this$media3;
30750
+ if (!((_this$media3 = this.media) != null && _this$media3.readyState) || !this.hasBuffered) {
30751
+ return;
30752
+ }
30753
+ var currentTime = this.media.currentTime;
30754
+ this.poll(currentTime, this.lastCurrentTime);
30755
+ this.lastCurrentTime = currentTime;
30756
+ }
30757
+
30758
+ /**
30759
+ * Checks if the playhead is stuck within a gap, and if so, attempts to free it.
30760
+ * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range).
30761
+ *
30762
+ * @param lastCurrentTime - Previously read playhead position
30763
+ */;
30764
+ _proto.poll = function poll(currentTime, lastCurrentTime) {
30765
+ var _this$hls, _this$hls2;
30766
+ var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
30767
+ if (!config) {
30768
+ return;
30769
+ }
30770
+ var media = this.media,
30771
+ stalled = this.stalled;
30772
+ if (!media) {
30773
+ return;
30774
+ }
30775
+ var seeking = media.seeking;
30776
+ var seeked = this.seeking && !seeking;
30777
+ var beginSeek = !this.seeking && seeking;
30778
+ var pausedEndedOrHalted = media.paused && !seeking || media.ended || media.playbackRate === 0;
30779
+ this.seeking = seeking;
30780
+
30781
+ // The playhead is moving, no-op
30782
+ if (currentTime !== lastCurrentTime) {
30783
+ if (lastCurrentTime) {
30784
+ this.ended = 0;
30785
+ }
30786
+ this.moved = true;
30787
+ if (!seeking) {
30788
+ this.nudgeRetry = 0;
30789
+ // When crossing between buffered video time ranges, but not audio, flush pipeline with seek (Chrome)
30790
+ if (config.nudgeOnVideoHole && !pausedEndedOrHalted && currentTime > lastCurrentTime) {
30791
+ this.nudgeOnVideoHole(currentTime, lastCurrentTime);
30792
+ }
30793
+ }
30794
+ if (this.waiting === 0) {
30795
+ this.stallResolved(currentTime);
30796
+ }
30797
+ return;
30798
+ }
30799
+
30800
+ // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
30801
+ if (beginSeek || seeked) {
30802
+ if (seeked) {
30803
+ this.stallResolved(currentTime);
30804
+ }
30805
+ return;
30806
+ }
30807
+
30808
+ // The playhead should not be moving
30809
+ if (pausedEndedOrHalted) {
30810
+ this.nudgeRetry = 0;
30811
+ this.stallResolved(currentTime);
30812
+ // Fire MEDIA_ENDED to workaround event not being dispatched by browser
30813
+ if (!this.ended && media.ended && this.hls) {
30814
+ this.ended = currentTime || 1;
30815
+ this.hls.trigger(Events.MEDIA_ENDED, {
30816
+ stalled: false
30817
+ });
30818
+ }
30819
+ return;
30820
+ }
30821
+ if (!BufferHelper.getBuffered(media).length) {
30822
+ this.nudgeRetry = 0;
30823
+ return;
30824
+ }
30825
+
30826
+ // Resolve stalls at buffer holes using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
30827
+ var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
30828
+ var nextStart = bufferInfo.nextStart || 0;
30829
+ var fragmentTracker = this.fragmentTracker;
30830
+ if (seeking && fragmentTracker && this.hls) {
30831
+ // Is there a fragment loading/parsing/appending before currentTime?
30832
+ var inFlightDependency = getInFlightDependency(this.hls.inFlightFragments, currentTime);
30833
+
30834
+ // Waiting for seeking in a buffered range to complete
30835
+ var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
30836
+ // Next buffered range is too far ahead to jump to while still seeking
30837
+ var noBufferHole = !nextStart || inFlightDependency || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
30838
+ if (hasEnoughBuffer || noBufferHole) {
30839
+ return;
30840
+ }
30841
+ // Reset moved state when seeking to a point in or before a gap/hole
30842
+ this.moved = false;
30843
+ }
30844
+
30845
+ // Skip start gaps if we haven't played, but the last poll detected the start of a stall
30846
+ // The addition poll gives the browser a chance to jump the gap for us
30847
+ var levelDetails = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.latestLevelDetails;
30848
+ if (!this.moved && this.stalled !== null && fragmentTracker) {
30849
+ // There is no playable buffer (seeked, waiting for buffer)
30850
+ var isBuffered = bufferInfo.len > 0;
30851
+ if (!isBuffered && !nextStart) {
30852
+ return;
30853
+ }
30854
+ // Jump start gaps within jump threshold
30855
+ var startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime;
30856
+
30857
+ // When joining a live stream with audio tracks, account for live playlist window sliding by allowing
30858
+ // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
30859
+ // that begins over 1 target duration after the video start position.
30860
+ var isLive = !!(levelDetails != null && levelDetails.live);
30861
+ var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
30862
+ var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
30863
+ if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
30864
+ if (!media.paused) {
30865
+ this._trySkipBufferHole(partialOrGap);
30866
+ }
30867
+ return;
30868
+ }
30869
+ }
30870
+
30871
+ // Start tracking stall time
30872
+ var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
30873
+ var tnow = self.performance.now();
30874
+ var tWaiting = this.waiting;
30875
+ if (stalled === null) {
30876
+ // Use time of recent "waiting" event
30877
+ if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
30878
+ this.stalled = tWaiting;
30879
+ } else {
30880
+ this.stalled = tnow;
30881
+ }
30882
+ return;
30883
+ }
30884
+ var stalledDuration = tnow - stalled;
30885
+ if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
30886
+ var _this$mediaSource;
30887
+ // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
30888
+ if (((_this$mediaSource = this.mediaSource) == null ? undefined : _this$mediaSource.readyState) === 'ended' && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
30889
+ if (this.ended) {
30890
+ return;
30891
+ }
30892
+ this.ended = currentTime || 1;
30893
+ this.hls.trigger(Events.MEDIA_ENDED, {
30894
+ stalled: true
30895
+ });
30896
+ return;
30897
+ }
30898
+ // Report stalling after trying to fix
30899
+ this._reportStall(bufferInfo);
30900
+ if (!this.media || !this.hls) {
30901
+ return;
30902
+ }
30903
+ }
30904
+ var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
30905
+ this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
30906
+ };
30907
+ _proto.stallResolved = function stallResolved(currentTime) {
30908
+ var stalled = this.stalled;
30909
+ if (stalled && this.hls) {
30910
+ this.stalled = null;
30911
+ // The playhead is now moving, but was previously stalled
30912
+ if (this.stallReported) {
30913
+ var stalledDuration = self.performance.now() - stalled;
30914
+ this.log("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
30915
+ this.stallReported = false;
30916
+ this.waiting = 0;
30917
+ this.hls.trigger(Events.STALL_RESOLVED, {});
30918
+ }
30919
+ }
30920
+ };
30921
+ _proto.nudgeOnVideoHole = function nudgeOnVideoHole(currentTime, lastCurrentTime) {
30922
+ var _this$buffered$audio;
30923
+ // Chrome will play one second past a hole in video buffered time ranges without rendering any video from the subsequent range and then stall as long as audio is buffered:
30924
+ // https://github.com/video-dev/hls.js/issues/5631
30925
+ // https://issues.chromium.org/issues/40280613#comment10
30926
+ // Detect the potential for this situation and proactively seek to flush the video pipeline once the playhead passes the start of the video hole.
30927
+ // When there are audio and video buffers and currentTime is past the end of the first video buffered range...
30928
+ var videoSourceBuffered = this.buffered.video;
30929
+ if (this.hls && this.media && this.fragmentTracker && (_this$buffered$audio = this.buffered.audio) != null && _this$buffered$audio.length && videoSourceBuffered && videoSourceBuffered.length > 1 && currentTime > videoSourceBuffered.end(0)) {
30930
+ // and audio is buffered at the playhead
30931
+ var audioBufferInfo = BufferHelper.bufferedInfo(BufferHelper.timeRangesToArray(this.buffered.audio), currentTime, 0);
30932
+ if (audioBufferInfo.len > 1 && lastCurrentTime >= audioBufferInfo.start) {
30933
+ var videoTimes = BufferHelper.timeRangesToArray(videoSourceBuffered);
30934
+ var lastBufferedIndex = BufferHelper.bufferedInfo(videoTimes, lastCurrentTime, 0).bufferedIndex;
30935
+ // nudge when crossing into another video buffered range (hole).
30936
+ if (lastBufferedIndex > -1 && lastBufferedIndex < videoTimes.length - 1) {
30937
+ var bufferedIndex = BufferHelper.bufferedInfo(videoTimes, currentTime, 0).bufferedIndex;
30938
+ var holeStart = videoTimes[lastBufferedIndex].end;
30939
+ var holeEnd = videoTimes[lastBufferedIndex + 1].start;
30940
+ if ((bufferedIndex === -1 || bufferedIndex > lastBufferedIndex) && holeEnd - holeStart < 1 &&
30941
+ // `maxBufferHole` may be too small and setting it to 0 should not disable this feature
30942
+ currentTime - holeStart < 2) {
30943
+ var error = new Error("nudging playhead to flush pipeline after video hole. currentTime: " + currentTime + " hole: " + holeStart + " -> " + holeEnd + " buffered index: " + bufferedIndex);
30944
+ this.warn(error.message);
30945
+ // Magic number to flush the pipeline without interuption to audio playback:
30946
+ this.media.currentTime += 0.000001;
30947
+ var frag = this.fragmentTracker.getPartialFragment(currentTime) || undefined;
30948
+ var bufferInfo = BufferHelper.bufferInfo(this.media, currentTime, 0);
30949
+ this.hls.trigger(Events.ERROR, {
30950
+ type: ErrorTypes.MEDIA_ERROR,
30951
+ details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
30952
+ fatal: false,
30953
+ error: error,
30954
+ reason: error.message,
30955
+ frag: frag,
30956
+ buffer: bufferInfo.len,
30957
+ bufferInfo: bufferInfo
30958
+ });
30959
+ }
30960
+ }
30961
+ }
30962
+ }
30963
+ }
30964
+
30965
+ /**
30966
+ * Detects and attempts to fix known buffer stalling issues.
30967
+ * @param bufferInfo - The properties of the current buffer.
30968
+ * @param stalledDurationMs - The amount of time Hls.js has been stalling for.
30969
+ * @private
30970
+ */;
30971
+ _proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
30972
+ var _this$hls3;
30973
+ var fragmentTracker = this.fragmentTracker,
30974
+ media = this.media;
30975
+ var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
30976
+ if (!media || !fragmentTracker || !config) {
30977
+ return;
30978
+ }
30979
+ var currentTime = media.currentTime;
30980
+ var partial = fragmentTracker.getPartialFragment(currentTime);
30981
+ if (partial) {
30982
+ // Try to skip over the buffer hole caused by a partial fragment
30983
+ // This method isn't limited by the size of the gap between buffered ranges
30984
+ var targetTime = this._trySkipBufferHole(partial);
30985
+ // we return here in this case, meaning
30986
+ // the branch below only executes when we haven't seeked to a new position
30987
+ if (targetTime || !this.media) {
30988
+ return;
30989
+ }
30990
+ }
30991
+
30992
+ // if we haven't had to skip over a buffer hole of a partial fragment
30993
+ // we may just have to "nudge" the playlist as the browser decoding/rendering engine
30994
+ // needs to cross some sort of threshold covering all source-buffers content
30995
+ // to start playing properly.
30996
+ var bufferedRanges = bufferInfo.buffered;
30997
+ if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && (stalledDurationMs > config.highBufferWatchdogPeriod * 1000 || this.waiting)) {
30998
+ this.warn('Trying to nudge playhead over buffer-hole');
30999
+ // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
31000
+ // We only try to jump the hole if it's under the configured size
31001
+ this._tryNudgeBuffer(bufferInfo);
31002
+ }
31003
+ }
31004
+
31005
+ /**
31006
+ * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.
31007
+ * @param bufferLen - The playhead distance from the end of the current buffer segment.
31008
+ * @private
31009
+ */;
31010
+ _proto._reportStall = function _reportStall(bufferInfo) {
31011
+ var hls = this.hls,
31012
+ media = this.media,
31013
+ stallReported = this.stallReported,
31014
+ stalled = this.stalled;
31015
+ if (!stallReported && stalled !== null && media && hls) {
31016
+ // Report stalled error once
31017
+ this.stallReported = true;
31018
+ var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
31019
+ this.warn(error.message);
31020
+ hls.trigger(Events.ERROR, {
31021
+ type: ErrorTypes.MEDIA_ERROR,
31022
+ details: ErrorDetails.BUFFER_STALLED_ERROR,
31023
+ fatal: false,
31024
+ error: error,
31025
+ buffer: bufferInfo.len,
31026
+ bufferInfo: bufferInfo,
31027
+ stalled: {
31028
+ start: stalled
31029
+ }
31030
+ });
31031
+ }
31032
+ }
31033
+
31034
+ /**
31035
+ * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments
31036
+ * @param partial - The partial fragment found at the current time (where playback is stalling).
31037
+ * @private
31038
+ */;
31039
+ _proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
31040
+ var _this$hls4;
31041
+ var fragmentTracker = this.fragmentTracker,
31042
+ media = this.media;
31043
+ var config = (_this$hls4 = this.hls) == null ? undefined : _this$hls4.config;
31044
+ if (!media || !fragmentTracker || !config) {
31045
+ return 0;
31046
+ }
31047
+
31048
+ // Check if currentTime is between unbuffered regions of partial fragments
31049
+ var currentTime = media.currentTime;
31050
+ var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
31051
+ var startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart;
31052
+ if (startTime && this.hls) {
31053
+ var bufferStarved = bufferInfo.len <= config.maxBufferHole;
31054
+ var waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3;
31055
+ var gapLength = startTime - currentTime;
31056
+ if (gapLength > 0 && (bufferStarved || waiting)) {
31057
+ // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
31058
+ if (gapLength > config.maxBufferHole) {
31059
+ var startGap = false;
31060
+ if (currentTime === 0) {
31061
+ var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
31062
+ if (startFrag && startTime < startFrag.end) {
31063
+ startGap = true;
31064
+ }
31065
+ }
31066
+ if (!startGap) {
31067
+ var startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN);
31068
+ if (startProvisioned) {
31069
+ var _this$hls$loadLevelOb;
31070
+ // Do not seek when selected variant playlist is unloaded
31071
+ if (!((_this$hls$loadLevelOb = this.hls.loadLevelObj) != null && _this$hls$loadLevelOb.details)) {
31072
+ return 0;
31073
+ }
31074
+ // Do not seek when required fragments are inflight or appending
31075
+ var inFlightDependency = getInFlightDependency(this.hls.inFlightFragments, startTime);
31076
+ if (inFlightDependency) {
31077
+ return 0;
31078
+ }
31079
+ // Do not seek if we can't walk tracked fragments to end of gap
31080
+ var moreToLoad = false;
31081
+ var pos = startProvisioned.end;
31082
+ while (pos < startTime) {
31083
+ var provisioned = fragmentTracker.getPartialFragment(pos);
31084
+ if (provisioned) {
31085
+ pos += provisioned.duration;
31086
+ } else {
31087
+ moreToLoad = true;
31088
+ break;
31089
+ }
31090
+ }
31091
+ if (moreToLoad) {
31092
+ return 0;
31093
+ }
31094
+ }
31095
+ }
31096
+ }
31097
+ var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
31098
+ this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
31099
+ this.moved = true;
31100
+ media.currentTime = targetTime;
31101
+ if (!(partial != null && partial.gap)) {
31102
+ var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
31103
+ this.hls.trigger(Events.ERROR, {
31104
+ type: ErrorTypes.MEDIA_ERROR,
31105
+ details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
31106
+ fatal: false,
31107
+ error: error,
31108
+ reason: error.message,
31109
+ frag: partial || undefined,
31110
+ buffer: bufferInfo.len,
31111
+ bufferInfo: bufferInfo
31112
+ });
31113
+ }
31114
+ return targetTime;
31115
+ }
31116
+ }
31117
+ return 0;
31118
+ }
31119
+
31120
+ /**
31121
+ * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
31122
+ * @private
31123
+ */;
31124
+ _proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
31125
+ var hls = this.hls,
31126
+ media = this.media,
31127
+ nudgeRetry = this.nudgeRetry;
31128
+ var config = hls == null ? undefined : hls.config;
31129
+ if (!media || !config) {
31130
+ return 0;
31131
+ }
31132
+ var currentTime = media.currentTime;
31133
+ this.nudgeRetry++;
31134
+ if (nudgeRetry < config.nudgeMaxRetry) {
31135
+ var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
31136
+ // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
31137
+ var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
31138
+ this.warn(error.message);
31139
+ media.currentTime = targetTime;
31140
+ hls.trigger(Events.ERROR, {
31141
+ type: ErrorTypes.MEDIA_ERROR,
31142
+ details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
31143
+ error: error,
31144
+ fatal: false,
31145
+ buffer: bufferInfo.len,
31146
+ bufferInfo: bufferInfo
31147
+ });
31148
+ } else {
31149
+ var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
31150
+ this.error(_error.message);
31151
+ hls.trigger(Events.ERROR, {
31152
+ type: ErrorTypes.MEDIA_ERROR,
31153
+ details: ErrorDetails.BUFFER_STALLED_ERROR,
31154
+ error: _error,
31155
+ fatal: true,
31156
+ buffer: bufferInfo.len,
31157
+ bufferInfo: bufferInfo
31158
+ });
31159
+ }
31160
+ };
31161
+ return _createClass(GapController, [{
31162
+ key: "hasBuffered",
31163
+ get: function get() {
31164
+ return Object.keys(this.buffered).length > 0;
31165
+ }
31166
+ }]);
31167
+ }(TaskLoop);
31168
+ function getInFlightDependency(inFlightFragments, currentTime) {
31169
+ var main = inFlight(inFlightFragments.main);
31170
+ if (main && main.start <= currentTime) {
31171
+ return main;
31172
+ }
31173
+ var audio = inFlight(inFlightFragments.audio);
31174
+ if (audio && audio.start <= currentTime) {
31175
+ return audio;
31176
+ }
31177
+ return null;
31178
+ }
31179
+ function inFlight(inFlightData) {
31180
+ if (!inFlightData) {
31181
+ return null;
31182
+ }
31183
+ switch (inFlightData.state) {
31184
+ case State.IDLE:
31185
+ case State.STOPPED:
31186
+ case State.ENDED:
31187
+ case State.ERROR:
31188
+ return null;
31189
+ }
31190
+ return inFlightData.frag;
31191
+ }
31192
+
30621
31193
  var MIN_CUE_DURATION = 0.25;
30622
31194
  function getCueClass() {
30623
31195
  if (typeof self === 'undefined') return undefined;
@@ -31630,6 +32202,11 @@
31630
32202
  }
31631
32203
  return this._levels;
31632
32204
  }
32205
+ }, {
32206
+ key: "loadLevelObj",
32207
+ get: function get() {
32208
+ return this.currentLevel;
32209
+ }
31633
32210
  }, {
31634
32211
  key: "level",
31635
32212
  get: function get() {
@@ -31789,375 +32366,6 @@
31789
32366
  });
31790
32367
  }
31791
32368
 
31792
- var MAX_START_GAP_JUMP = 2.0;
31793
- var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
31794
- var SKIP_BUFFER_RANGE_START = 0.05;
31795
- var GapController = /*#__PURE__*/function (_Logger) {
31796
- function GapController(media, fragmentTracker, hls) {
31797
- var _this;
31798
- _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
31799
- _this.media = null;
31800
- _this.fragmentTracker = null;
31801
- _this.hls = null;
31802
- _this.nudgeRetry = 0;
31803
- _this.stallReported = false;
31804
- _this.stalled = null;
31805
- _this.moved = false;
31806
- _this.seeking = false;
31807
- _this.ended = 0;
31808
- _this.waiting = 0;
31809
- _this.media = media;
31810
- _this.fragmentTracker = fragmentTracker;
31811
- _this.hls = hls;
31812
- return _this;
31813
- }
31814
- _inheritsLoose(GapController, _Logger);
31815
- var _proto = GapController.prototype;
31816
- _proto.destroy = function destroy() {
31817
- this.media = this.hls = this.fragmentTracker = null;
31818
- }
31819
-
31820
- /**
31821
- * Checks if the playhead is stuck within a gap, and if so, attempts to free it.
31822
- * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range).
31823
- *
31824
- * @param lastCurrentTime - Previously read playhead position
31825
- */;
31826
- _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
31827
- var _this$hls;
31828
- var media = this.media,
31829
- stalled = this.stalled;
31830
- if (!media) {
31831
- return;
31832
- }
31833
- var currentTime = media.currentTime,
31834
- seeking = media.seeking;
31835
- var seeked = this.seeking && !seeking;
31836
- var beginSeek = !this.seeking && seeking;
31837
- this.seeking = seeking;
31838
-
31839
- // The playhead is moving, no-op
31840
- if (currentTime !== lastCurrentTime) {
31841
- if (lastCurrentTime) {
31842
- this.ended = 0;
31843
- }
31844
- this.moved = true;
31845
- if (!seeking) {
31846
- this.nudgeRetry = 0;
31847
- }
31848
- if (this.waiting === 0) {
31849
- this.stallResolved(currentTime);
31850
- }
31851
- return;
31852
- }
31853
-
31854
- // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
31855
- if (beginSeek || seeked) {
31856
- if (seeked) {
31857
- this.stallResolved(currentTime);
31858
- }
31859
- return;
31860
- }
31861
-
31862
- // The playhead should not be moving
31863
- if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
31864
- this.nudgeRetry = 0;
31865
- this.stallResolved(currentTime);
31866
- // Fire MEDIA_ENDED to workaround event not being dispatched by browser
31867
- if (!this.ended && media.ended && this.hls) {
31868
- this.ended = currentTime || 1;
31869
- this.hls.trigger(Events.MEDIA_ENDED, {
31870
- stalled: false
31871
- });
31872
- }
31873
- return;
31874
- }
31875
- if (!BufferHelper.getBuffered(media).length) {
31876
- this.nudgeRetry = 0;
31877
- return;
31878
- }
31879
- var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
31880
- var nextStart = bufferInfo.nextStart || 0;
31881
- var fragmentTracker = this.fragmentTracker;
31882
- if (seeking && fragmentTracker) {
31883
- // Waiting for seeking in a buffered range to complete
31884
- var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
31885
- // Next buffered range is too far ahead to jump to while still seeking
31886
- var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
31887
- if (hasEnoughBuffer || noBufferGap) {
31888
- return;
31889
- }
31890
- // Reset moved state when seeking to a point in or before a gap
31891
- this.moved = false;
31892
- }
31893
-
31894
- // Skip start gaps if we haven't played, but the last poll detected the start of a stall
31895
- // The addition poll gives the browser a chance to jump the gap for us
31896
- if (!this.moved && this.stalled !== null && fragmentTracker) {
31897
- // There is no playable buffer (seeked, waiting for buffer)
31898
- var isBuffered = bufferInfo.len > 0;
31899
- if (!isBuffered && !nextStart) {
31900
- return;
31901
- }
31902
- // Jump start gaps within jump threshold
31903
- var startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime;
31904
-
31905
- // When joining a live stream with audio tracks, account for live playlist window sliding by allowing
31906
- // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
31907
- // that begins over 1 target duration after the video start position.
31908
- var isLive = !!(levelDetails != null && levelDetails.live);
31909
- var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
31910
- var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
31911
- if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
31912
- if (!media.paused) {
31913
- this._trySkipBufferHole(partialOrGap);
31914
- }
31915
- return;
31916
- }
31917
- }
31918
-
31919
- // Start tracking stall time
31920
- var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
31921
- if (!config) {
31922
- return;
31923
- }
31924
- var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
31925
- var tnow = self.performance.now();
31926
- var tWaiting = this.waiting;
31927
- if (stalled === null) {
31928
- // Use time of recent "waiting" event
31929
- if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
31930
- this.stalled = tWaiting;
31931
- } else {
31932
- this.stalled = tnow;
31933
- }
31934
- return;
31935
- }
31936
- var stalledDuration = tnow - stalled;
31937
- if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
31938
- // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
31939
- if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
31940
- if (this.ended) {
31941
- return;
31942
- }
31943
- this.ended = currentTime || 1;
31944
- this.hls.trigger(Events.MEDIA_ENDED, {
31945
- stalled: true
31946
- });
31947
- return;
31948
- }
31949
- // Report stalling after trying to fix
31950
- this._reportStall(bufferInfo);
31951
- if (!this.media || !this.hls) {
31952
- return;
31953
- }
31954
- }
31955
- var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
31956
- this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
31957
- };
31958
- _proto.stallResolved = function stallResolved(currentTime) {
31959
- var stalled = this.stalled;
31960
- if (stalled && this.hls) {
31961
- this.stalled = null;
31962
- // The playhead is now moving, but was previously stalled
31963
- if (this.stallReported) {
31964
- var stalledDuration = self.performance.now() - stalled;
31965
- this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
31966
- this.stallReported = false;
31967
- this.waiting = 0;
31968
- this.hls.trigger(Events.STALL_RESOLVED, {});
31969
- }
31970
- }
31971
- }
31972
-
31973
- /**
31974
- * Detects and attempts to fix known buffer stalling issues.
31975
- * @param bufferInfo - The properties of the current buffer.
31976
- * @param stalledDurationMs - The amount of time Hls.js has been stalling for.
31977
- * @private
31978
- */;
31979
- _proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
31980
- var _this$hls2;
31981
- var fragmentTracker = this.fragmentTracker,
31982
- media = this.media;
31983
- var config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
31984
- if (!media || !fragmentTracker || !config) {
31985
- return;
31986
- }
31987
- var currentTime = media.currentTime;
31988
- var partial = fragmentTracker.getPartialFragment(currentTime);
31989
- if (partial) {
31990
- // Try to skip over the buffer hole caused by a partial fragment
31991
- // This method isn't limited by the size of the gap between buffered ranges
31992
- var targetTime = this._trySkipBufferHole(partial);
31993
- // we return here in this case, meaning
31994
- // the branch below only executes when we haven't seeked to a new position
31995
- if (targetTime || !this.media) {
31996
- return;
31997
- }
31998
- }
31999
-
32000
- // if we haven't had to skip over a buffer hole of a partial fragment
32001
- // we may just have to "nudge" the playlist as the browser decoding/rendering engine
32002
- // needs to cross some sort of threshold covering all source-buffers content
32003
- // to start playing properly.
32004
- var bufferedRanges = bufferInfo.buffered;
32005
- if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
32006
- this.warn('Trying to nudge playhead over buffer-hole');
32007
- // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
32008
- // We only try to jump the hole if it's under the configured size
32009
- this._tryNudgeBuffer(bufferInfo);
32010
- }
32011
- }
32012
-
32013
- /**
32014
- * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.
32015
- * @param bufferLen - The playhead distance from the end of the current buffer segment.
32016
- * @private
32017
- */;
32018
- _proto._reportStall = function _reportStall(bufferInfo) {
32019
- var hls = this.hls,
32020
- media = this.media,
32021
- stallReported = this.stallReported,
32022
- stalled = this.stalled;
32023
- if (!stallReported && stalled !== null && media && hls) {
32024
- // Report stalled error once
32025
- this.stallReported = true;
32026
- var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
32027
- this.warn(error.message);
32028
- hls.trigger(Events.ERROR, {
32029
- type: ErrorTypes.MEDIA_ERROR,
32030
- details: ErrorDetails.BUFFER_STALLED_ERROR,
32031
- fatal: false,
32032
- error: error,
32033
- buffer: bufferInfo.len,
32034
- bufferInfo: bufferInfo,
32035
- stalled: {
32036
- start: stalled
32037
- }
32038
- });
32039
- }
32040
- }
32041
-
32042
- /**
32043
- * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments
32044
- * @param partial - The partial fragment found at the current time (where playback is stalling).
32045
- * @private
32046
- */;
32047
- _proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
32048
- var _this$hls3;
32049
- var fragmentTracker = this.fragmentTracker,
32050
- media = this.media;
32051
- var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
32052
- if (!media || !fragmentTracker || !config) {
32053
- return 0;
32054
- }
32055
-
32056
- // Check if currentTime is between unbuffered regions of partial fragments
32057
- var currentTime = media.currentTime;
32058
- var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
32059
- var startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart;
32060
- if (startTime) {
32061
- var bufferStarved = bufferInfo.len <= config.maxBufferHole;
32062
- var waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3;
32063
- var gapLength = startTime - currentTime;
32064
- if (gapLength > 0 && (bufferStarved || waiting)) {
32065
- // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
32066
- if (gapLength > config.maxBufferHole) {
32067
- var startGap = false;
32068
- if (currentTime === 0) {
32069
- var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
32070
- if (startFrag && startTime < startFrag.end) {
32071
- startGap = true;
32072
- }
32073
- }
32074
- if (!startGap) {
32075
- var startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN);
32076
- if (startProvisioned) {
32077
- var moreToLoad = false;
32078
- var pos = startProvisioned.end;
32079
- while (pos < startTime) {
32080
- var provisioned = fragmentTracker.getPartialFragment(pos);
32081
- if (provisioned) {
32082
- pos += provisioned.duration;
32083
- } else {
32084
- moreToLoad = true;
32085
- break;
32086
- }
32087
- }
32088
- if (moreToLoad) {
32089
- return 0;
32090
- }
32091
- }
32092
- }
32093
- }
32094
- var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
32095
- this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
32096
- this.moved = true;
32097
- media.currentTime = targetTime;
32098
- if (partial && !partial.gap && this.hls) {
32099
- var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
32100
- this.hls.trigger(Events.ERROR, {
32101
- type: ErrorTypes.MEDIA_ERROR,
32102
- details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
32103
- fatal: false,
32104
- error: error,
32105
- reason: error.message,
32106
- frag: partial,
32107
- buffer: bufferInfo.len,
32108
- bufferInfo: bufferInfo
32109
- });
32110
- }
32111
- return targetTime;
32112
- }
32113
- }
32114
- return 0;
32115
- }
32116
-
32117
- /**
32118
- * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
32119
- * @private
32120
- */;
32121
- _proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
32122
- var hls = this.hls,
32123
- media = this.media,
32124
- nudgeRetry = this.nudgeRetry;
32125
- var config = hls == null ? undefined : hls.config;
32126
- if (!media || !config) {
32127
- return 0;
32128
- }
32129
- var currentTime = media.currentTime;
32130
- this.nudgeRetry++;
32131
- if (nudgeRetry < config.nudgeMaxRetry) {
32132
- var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
32133
- // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
32134
- var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
32135
- this.warn(error.message);
32136
- media.currentTime = targetTime;
32137
- hls.trigger(Events.ERROR, {
32138
- type: ErrorTypes.MEDIA_ERROR,
32139
- details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
32140
- error: error,
32141
- fatal: false,
32142
- buffer: bufferInfo.len,
32143
- bufferInfo: bufferInfo
32144
- });
32145
- } else {
32146
- var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
32147
- this.error(_error.message);
32148
- hls.trigger(Events.ERROR, {
32149
- type: ErrorTypes.MEDIA_ERROR,
32150
- details: ErrorDetails.BUFFER_STALLED_ERROR,
32151
- error: _error,
32152
- fatal: true,
32153
- buffer: bufferInfo.len,
32154
- bufferInfo: bufferInfo
32155
- });
32156
- }
32157
- };
32158
- return GapController;
32159
- }(Logger);
32160
-
32161
32369
  function getSourceBuffer() {
32162
32370
  return self.SourceBuffer || self.WebKitSourceBuffer;
32163
32371
  }
@@ -32195,7 +32403,6 @@
32195
32403
  var _this;
32196
32404
  _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
32197
32405
  _this.audioCodecSwap = false;
32198
- _this.gapController = null;
32199
32406
  _this.level = -1;
32200
32407
  _this._forceStartLoad = false;
32201
32408
  _this._hasEnoughToStart = false;
@@ -32207,19 +32414,8 @@
32207
32414
  _this.backtrackFragment = null;
32208
32415
  _this.audioCodecSwitch = false;
32209
32416
  _this.videoBuffer = null;
32210
- _this.onMediaWaiting = function () {
32211
- var gapController = _this.gapController;
32212
- if (gapController) {
32213
- gapController.waiting = self.performance.now();
32214
- }
32215
- };
32216
32417
  _this.onMediaPlaying = function () {
32217
32418
  // tick to speed up FRAG_CHANGED triggering
32218
- var gapController = _this.gapController;
32219
- if (gapController) {
32220
- gapController.ended = 0;
32221
- gapController.waiting = 0;
32222
- }
32223
32419
  _this.tick();
32224
32420
  };
32225
32421
  _this.onMediaSeeked = function () {
@@ -32273,7 +32469,7 @@
32273
32469
  };
32274
32470
  _proto.onHandlerDestroying = function onHandlerDestroying() {
32275
32471
  // @ts-ignore
32276
- this.onMediaPlaying = this.onMediaSeeked = this.onMediaWaiting = null;
32472
+ this.onMediaPlaying = this.onMediaSeeked = null;
32277
32473
  this.unregisterListeners();
32278
32474
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
32279
32475
  };
@@ -32362,8 +32558,11 @@
32362
32558
  this.onTickEnd();
32363
32559
  };
32364
32560
  _proto.onTickEnd = function onTickEnd() {
32561
+ var _this$media2;
32365
32562
  _BaseStreamController.prototype.onTickEnd.call(this);
32366
- this.checkBuffer();
32563
+ if ((_this$media2 = this.media) != null && _this$media2.readyState && this.media.seeking === false) {
32564
+ this.lastCurrentTime = this.media.currentTime;
32565
+ }
32367
32566
  this.checkFragmentChanged();
32368
32567
  };
32369
32568
  _proto.doTickIdle = function doTickIdle() {
@@ -32592,27 +32791,17 @@
32592
32791
  _proto.onMediaAttached = function onMediaAttached(event, data) {
32593
32792
  _BaseStreamController.prototype.onMediaAttached.call(this, event, data);
32594
32793
  var media = data.media;
32595
- media.removeEventListener('playing', this.onMediaPlaying);
32596
- media.removeEventListener('seeked', this.onMediaSeeked);
32597
- media.removeEventListener('waiting', this.onMediaWaiting);
32598
- media.addEventListener('playing', this.onMediaPlaying);
32599
- media.addEventListener('seeked', this.onMediaSeeked);
32600
- media.addEventListener('waiting', this.onMediaWaiting);
32601
- this.gapController = new GapController(media, this.fragmentTracker, this.hls);
32794
+ addEventListener(media, 'playing', this.onMediaPlaying);
32795
+ addEventListener(media, 'seeked', this.onMediaSeeked);
32602
32796
  };
32603
32797
  _proto.onMediaDetaching = function onMediaDetaching(event, data) {
32604
32798
  var media = this.media;
32605
32799
  if (media) {
32606
- media.removeEventListener('playing', this.onMediaPlaying);
32607
- media.removeEventListener('seeked', this.onMediaSeeked);
32608
- media.removeEventListener('waiting', this.onMediaWaiting);
32800
+ removeEventListener(media, 'playing', this.onMediaPlaying);
32801
+ removeEventListener(media, 'seeked', this.onMediaSeeked);
32609
32802
  }
32610
32803
  this.videoBuffer = null;
32611
32804
  this.fragPlaying = null;
32612
- if (this.gapController) {
32613
- this.gapController.destroy();
32614
- this.gapController = null;
32615
- }
32616
32805
  _BaseStreamController.prototype.onMediaDetaching.call(this, event, data);
32617
32806
  var transferringMedia = !!data.transferMedia;
32618
32807
  if (transferringMedia) {
@@ -32620,19 +32809,6 @@
32620
32809
  }
32621
32810
  this._hasEnoughToStart = false;
32622
32811
  };
32623
- _proto.triggerEnded = function triggerEnded() {
32624
- var gapController = this.gapController;
32625
- if (gapController) {
32626
- var _this$media2;
32627
- if (gapController.ended) {
32628
- return;
32629
- }
32630
- gapController.ended = ((_this$media2 = this.media) == null ? undefined : _this$media2.currentTime) || 1;
32631
- }
32632
- this.hls.trigger(Events.MEDIA_ENDED, {
32633
- stalled: false
32634
- });
32635
- };
32636
32812
  _proto.onManifestLoading = function onManifestLoading() {
32637
32813
  _BaseStreamController.prototype.onManifestLoading.call(this);
32638
32814
  // reset buffer on manifest loading
@@ -32952,25 +33128,6 @@
32952
33128
  this.recoverWorkerError(data);
32953
33129
  break;
32954
33130
  }
32955
- }
32956
-
32957
- // Checks the health of the buffer and attempts to resolve playback stalls.
32958
- ;
32959
- _proto.checkBuffer = function checkBuffer() {
32960
- var media = this.media,
32961
- gapController = this.gapController;
32962
- if (!media || !gapController || !media.readyState) {
32963
- // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0)
32964
- return;
32965
- }
32966
- if (this._hasEnoughToStart || !BufferHelper.getBuffered(media).length) {
32967
- // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
32968
- var state = this.state;
32969
- var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
32970
- var levelDetails = this.getLevelDetails();
32971
- gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
32972
- }
32973
- this.lastCurrentTime = media.currentTime;
32974
33131
  };
32975
33132
  _proto.onFragLoadEmergencyAborted = function onFragLoadEmergencyAborted() {
32976
33133
  this.state = State.IDLE;
@@ -32986,8 +33143,10 @@
32986
33143
  var type = _ref.type;
32987
33144
  if (type !== ElementaryStreamTypes.AUDIO || !this.altAudio) {
32988
33145
  var mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media;
32989
- this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);
32990
- this.tick();
33146
+ if (mediaBuffer) {
33147
+ this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);
33148
+ this.tick();
33149
+ }
32991
33150
  }
32992
33151
  };
32993
33152
  _proto.onLevelsUpdated = function onLevelsUpdated(event, data) {
@@ -34306,9 +34465,12 @@
34306
34465
  this.latencyController = undefined;
34307
34466
  this.levelController = undefined;
34308
34467
  this.streamController = undefined;
34468
+ this.audioStreamController = undefined;
34469
+ this.subtititleStreamController = undefined;
34309
34470
  this.audioTrackController = undefined;
34310
34471
  this.subtitleTrackController = undefined;
34311
34472
  this.interstitialsController = undefined;
34473
+ this.gapController = undefined;
34312
34474
  this.emeController = undefined;
34313
34475
  this.cmcdController = undefined;
34314
34476
  this._media = null;
@@ -34346,6 +34508,7 @@
34346
34508
  var id3TrackController = new ID3TrackController(this);
34347
34509
  var keyLoader = new KeyLoader(this.config);
34348
34510
  var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
34511
+ var gapController = this.gapController = new GapController(this, fragmentTracker);
34349
34512
 
34350
34513
  // Cap level controller uses streamController to flush the buffer
34351
34514
  capLevelController.setStreamController(streamController);
@@ -34359,17 +34522,17 @@
34359
34522
  networkControllers.splice(1, 0, contentSteering);
34360
34523
  }
34361
34524
  this.networkControllers = networkControllers;
34362
- var coreComponents = [abrController, bufferController, capLevelController, fpsController, id3TrackController, fragmentTracker];
34525
+ var coreComponents = [abrController, bufferController, gapController, capLevelController, fpsController, id3TrackController, fragmentTracker];
34363
34526
  this.audioTrackController = this.createController(config.audioTrackController, networkControllers);
34364
34527
  var AudioStreamControllerClass = config.audioStreamController;
34365
34528
  if (AudioStreamControllerClass) {
34366
- networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
34529
+ networkControllers.push(this.audioStreamController = new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
34367
34530
  }
34368
34531
  // Instantiate subtitleTrackController before SubtitleStreamController to receive level events first
34369
34532
  this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers);
34370
34533
  var SubtitleStreamControllerClass = config.subtitleStreamController;
34371
34534
  if (SubtitleStreamControllerClass) {
34372
- networkControllers.push(new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader));
34535
+ networkControllers.push(this.subtititleStreamController = new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader));
34373
34536
  }
34374
34537
  this.createController(config.timelineController, coreComponents);
34375
34538
  keyLoader.emeController = this.emeController = this.createController(config.emeController, coreComponents);
@@ -34644,11 +34807,10 @@
34644
34807
  }
34645
34808
  });
34646
34809
  }
34647
- }
34648
-
34810
+ };
34649
34811
  /**
34650
34812
  * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
34651
- */;
34813
+ */
34652
34814
  _proto.swapAudioCodec = function swapAudioCodec() {
34653
34815
  this.logger.log('swapAudioCodec');
34654
34816
  this.streamController.swapAudioCodec();
@@ -34746,6 +34908,19 @@
34746
34908
  get: function get() {
34747
34909
  return this.streamController.bufferingEnabled;
34748
34910
  }
34911
+ }, {
34912
+ key: "inFlightFragments",
34913
+ get: function get() {
34914
+ var _inFlightData;
34915
+ var inFlightData = (_inFlightData = {}, _inFlightData[PlaylistLevelType.MAIN] = this.streamController.inFlightFrag, _inFlightData);
34916
+ if (this.audioStreamController) {
34917
+ inFlightData[PlaylistLevelType.AUDIO] = this.audioStreamController.inFlightFrag;
34918
+ }
34919
+ if (this.subtititleStreamController) {
34920
+ inFlightData[PlaylistLevelType.SUBTITLE] = this.subtititleStreamController.inFlightFrag;
34921
+ }
34922
+ return inFlightData;
34923
+ }
34749
34924
  }, {
34750
34925
  key: "sessionId",
34751
34926
  get: function get() {
@@ -34765,12 +34940,25 @@
34765
34940
  var levels = this.levelController.levels;
34766
34941
  return levels ? levels : [];
34767
34942
  }
34943
+
34944
+ /**
34945
+ * @returns LevelDetails of last loaded level (variant) or `null` prior to loading a media playlist.
34946
+ */
34768
34947
  }, {
34769
34948
  key: "latestLevelDetails",
34770
34949
  get: function get() {
34771
34950
  return this.streamController.getLevelDetails() || null;
34772
34951
  }
34773
34952
 
34953
+ /**
34954
+ * @returns Level object of selected level (variant) or `null` prior to selecting a level or once the level is removed.
34955
+ */
34956
+ }, {
34957
+ key: "loadLevelObj",
34958
+ get: function get() {
34959
+ return this.levelController.loadLevelObj;
34960
+ }
34961
+
34774
34962
  /**
34775
34963
  * Index of quality level (variant) currently played
34776
34964
  */