hls.js 1.6.0-beta.2.0.canary.10882 → 1.6.0-beta.2.0.canary.10884

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.light.js CHANGED
@@ -756,6 +756,7 @@
756
756
  Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
757
757
  Events["MEDIA_DETACHED"] = "hlsMediaDetached";
758
758
  Events["MEDIA_ENDED"] = "hlsMediaEnded";
759
+ Events["STALL_RESOLVED"] = "hlsStallResolved";
759
760
  Events["BUFFER_RESET"] = "hlsBufferReset";
760
761
  Events["BUFFER_CODECS"] = "hlsBufferCodecs";
761
762
  Events["BUFFER_CREATED"] = "hlsBufferCreated";
@@ -1029,7 +1030,7 @@
1029
1030
  // Some browsers don't allow to use bind on console object anyway
1030
1031
  // fallback to default if needed
1031
1032
  try {
1032
- newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10882");
1033
+ newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10884");
1033
1034
  } catch (e) {
1034
1035
  /* log fn threw an exception. All logger methods are no-ops. */
1035
1036
  return createLogger();
@@ -3621,8 +3622,7 @@
3621
3622
  return {
3622
3623
  len: 0,
3623
3624
  start: pos,
3624
- end: pos,
3625
- nextStart: undefined
3625
+ end: pos
3626
3626
  };
3627
3627
  };
3628
3628
  BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) {
@@ -3687,7 +3687,8 @@
3687
3687
  len: bufferLen,
3688
3688
  start: bufferStart || 0,
3689
3689
  end: bufferEnd || 0,
3690
- nextStart: bufferStartNext
3690
+ nextStart: bufferStartNext,
3691
+ buffered: buffered
3691
3692
  };
3692
3693
  }
3693
3694
 
@@ -5792,8 +5793,6 @@
5792
5793
  this.advancedDateTime = undefined;
5793
5794
  this.updated = true;
5794
5795
  this.advanced = true;
5795
- this.availabilityDelay = undefined;
5796
- // Manifest reload synchronization
5797
5796
  this.misses = 0;
5798
5797
  this.startCC = 0;
5799
5798
  this.startSN = 0;
@@ -5846,7 +5845,6 @@
5846
5845
  } else {
5847
5846
  this.misses = previous.misses + 1;
5848
5847
  }
5849
- this.availabilityDelay = previous.availabilityDelay;
5850
5848
  };
5851
5849
  return _createClass(LevelDetails, [{
5852
5850
  key: "hasProgramDateTime",
@@ -7729,7 +7727,7 @@
7729
7727
  if (!level.live) {
7730
7728
  lastFragment.endList = true;
7731
7729
  }
7732
- if (firstFragment && !level.startCC) {
7730
+ if (firstFragment && level.startCC === undefined) {
7733
7731
  level.startCC = firstFragment.cc;
7734
7732
  }
7735
7733
  /**
@@ -8019,15 +8017,16 @@
8019
8017
  delete oldDetails.fragmentHint.endPTS;
8020
8018
  }
8021
8019
  // check if old/new playlists have fragments in common
8022
- // loop through overlapping SN and update startPTS , cc, and duration if any found
8023
- var ccOffset = 0;
8020
+ // loop through overlapping SN and update startPTS, cc, and duration if any found
8024
8021
  var PTSFrag;
8025
- mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag) {
8026
- if (oldFrag.relurl) {
8027
- // Do not compare CC if the old fragment has no url. This is a level.fragmentHint used by LL-HLS parts.
8028
- // It maybe be off by 1 if it was created before any parts or discontinuity tags were appended to the end
8029
- // of the playlist.
8030
- ccOffset = oldFrag.cc - newFrag.cc;
8022
+ mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag, newFragIndex, newFragments) {
8023
+ if (newDetails.skippedSegments) {
8024
+ if (newFrag.cc !== oldFrag.cc) {
8025
+ var ccOffset = oldFrag.cc - newFrag.cc;
8026
+ for (var _i = newFragIndex; _i < newFragments.length; _i++) {
8027
+ newFragments[_i].cc += ccOffset;
8028
+ }
8029
+ }
8031
8030
  }
8032
8031
  if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) {
8033
8032
  newFrag.setStart(newFrag.startPTS = oldFrag.startPTS);
@@ -8056,7 +8055,8 @@
8056
8055
  currentInitSegment = oldFrag.initSegment;
8057
8056
  }
8058
8057
  });
8059
- var fragmentsToCheck = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments;
8058
+ var newFragments = newDetails.fragments;
8059
+ var fragmentsToCheck = newDetails.fragmentHint ? newFragments.concat(newDetails.fragmentHint) : newFragments;
8060
8060
  if (currentInitSegment) {
8061
8061
  fragmentsToCheck.forEach(function (frag) {
8062
8062
  var _currentInitSegment;
@@ -8066,19 +8066,17 @@
8066
8066
  });
8067
8067
  }
8068
8068
  if (newDetails.skippedSegments) {
8069
- newDetails.deltaUpdateFailed = newDetails.fragments.some(function (frag) {
8069
+ newDetails.deltaUpdateFailed = newFragments.some(function (frag) {
8070
8070
  return !frag;
8071
8071
  });
8072
8072
  if (newDetails.deltaUpdateFailed) {
8073
8073
  logger.warn('[level-helper] Previous playlist missing segments skipped in delta playlist');
8074
- for (var _i = newDetails.skippedSegments; _i--;) {
8075
- newDetails.fragments.shift();
8076
- }
8077
- newDetails.startSN = newDetails.fragments[0].sn;
8078
- if (!newDetails.startCC) {
8079
- newDetails.startCC = newDetails.fragments[0].cc;
8074
+ for (var _i2 = newDetails.skippedSegments; _i2--;) {
8075
+ newFragments.shift();
8080
8076
  }
8077
+ newDetails.startSN = newFragments[0].sn;
8081
8078
  } else {
8079
+ newDetails.endCC = newFragments[newFragments.length - 1].cc;
8082
8080
  if (newDetails.canSkipDateRanges) {
8083
8081
  newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails);
8084
8082
  }
@@ -8086,25 +8084,15 @@
8086
8084
  return frag.rawProgramDateTime;
8087
8085
  });
8088
8086
  if (oldDetails.hasProgramDateTime && !newDetails.hasProgramDateTime) {
8089
- for (var _i2 = 1; _i2 < fragmentsToCheck.length; _i2++) {
8090
- if (fragmentsToCheck[_i2].programDateTime === null) {
8091
- assignProgramDateTime(fragmentsToCheck[_i2], fragmentsToCheck[_i2 - 1], programDateTimes);
8087
+ for (var _i3 = 1; _i3 < fragmentsToCheck.length; _i3++) {
8088
+ if (fragmentsToCheck[_i3].programDateTime === null) {
8089
+ assignProgramDateTime(fragmentsToCheck[_i3], fragmentsToCheck[_i3 - 1], programDateTimes);
8092
8090
  }
8093
8091
  }
8094
8092
  }
8095
8093
  mapDateRanges(programDateTimes, newDetails);
8096
8094
  }
8097
8095
  }
8098
- var newFragments = newDetails.fragments;
8099
- if (ccOffset) {
8100
- logger.warn('discontinuity sliding from playlist, take drift into account');
8101
- for (var _i3 = 0; _i3 < newFragments.length; _i3++) {
8102
- newFragments[_i3].cc += ccOffset;
8103
- }
8104
- }
8105
- if (newDetails.skippedSegments) {
8106
- newDetails.startCC = newDetails.fragments[0].cc;
8107
- }
8108
8096
 
8109
8097
  // Merge parts
8110
8098
  mapPartIntersection(oldDetails.partList, newDetails.partList, function (oldPart, newPart) {
@@ -8200,7 +8188,7 @@
8200
8188
  _newFrag = newDetails.fragments[i] = _oldFrag;
8201
8189
  }
8202
8190
  if (_oldFrag && _newFrag) {
8203
- intersectionFn(_oldFrag, _newFrag);
8191
+ intersectionFn(_oldFrag, _newFrag, i, newFrags);
8204
8192
  }
8205
8193
  }
8206
8194
  }
@@ -14972,6 +14960,7 @@
14972
14960
  progressive: false,
14973
14961
  lowLatencyMode: true,
14974
14962
  cmcd: undefined,
14963
+ detectStallWithCurrentTimeMs: 1250,
14975
14964
  enableDateRangeMetadataCues: true,
14976
14965
  enableEmsgMetadataCues: true,
14977
14966
  enableEmsgKLVMetadata: false,
@@ -19236,25 +19225,23 @@
19236
19225
  }]);
19237
19226
  }(TaskLoop);
19238
19227
 
19239
- var STALL_MINIMUM_DURATION_MS = 250;
19240
19228
  var MAX_START_GAP_JUMP = 2.0;
19241
19229
  var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
19242
19230
  var SKIP_BUFFER_RANGE_START = 0.05;
19243
19231
  var GapController = /*#__PURE__*/function (_Logger) {
19244
- function GapController(config, media, fragmentTracker, hls) {
19232
+ function GapController(media, fragmentTracker, hls) {
19245
19233
  var _this;
19246
19234
  _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
19247
- _this.config = undefined;
19248
19235
  _this.media = null;
19249
- _this.fragmentTracker = undefined;
19250
- _this.hls = undefined;
19236
+ _this.fragmentTracker = null;
19237
+ _this.hls = null;
19251
19238
  _this.nudgeRetry = 0;
19252
19239
  _this.stallReported = false;
19253
19240
  _this.stalled = null;
19254
19241
  _this.moved = false;
19255
19242
  _this.seeking = false;
19256
19243
  _this.ended = 0;
19257
- _this.config = config;
19244
+ _this.waiting = 0;
19258
19245
  _this.media = media;
19259
19246
  _this.fragmentTracker = fragmentTracker;
19260
19247
  _this.hls = hls;
@@ -19263,9 +19250,7 @@
19263
19250
  _inheritsLoose(GapController, _Logger);
19264
19251
  var _proto = GapController.prototype;
19265
19252
  _proto.destroy = function destroy() {
19266
- this.media = null;
19267
- // @ts-ignore
19268
- this.hls = this.fragmentTracker = null;
19253
+ this.media = this.hls = this.fragmentTracker = null;
19269
19254
  }
19270
19255
 
19271
19256
  /**
@@ -19275,10 +19260,10 @@
19275
19260
  * @param lastCurrentTime - Previously read playhead position
19276
19261
  */;
19277
19262
  _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
19278
- var config = this.config,
19279
- media = this.media,
19263
+ var _this$hls;
19264
+ var media = this.media,
19280
19265
  stalled = this.stalled;
19281
- if (media === null) {
19266
+ if (!media) {
19282
19267
  return;
19283
19268
  }
19284
19269
  var currentTime = media.currentTime,
@@ -19296,43 +19281,45 @@
19296
19281
  if (!seeking) {
19297
19282
  this.nudgeRetry = 0;
19298
19283
  }
19299
- if (stalled !== null) {
19300
- // The playhead is now moving, but was previously stalled
19301
- if (this.stallReported) {
19302
- var _stalledDuration = self.performance.now() - stalled;
19303
- this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
19304
- this.stallReported = false;
19305
- }
19306
- this.stalled = null;
19284
+ if (this.waiting === 0) {
19285
+ this.stallResolved(currentTime);
19307
19286
  }
19308
19287
  return;
19309
19288
  }
19310
19289
 
19311
19290
  // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
19312
19291
  if (beginSeek || seeked) {
19313
- this.stalled = null;
19292
+ if (seeked) {
19293
+ this.stallResolved(currentTime);
19294
+ }
19314
19295
  return;
19315
19296
  }
19316
19297
 
19317
19298
  // The playhead should not be moving
19318
- if (media.paused && !seeking || media.ended || media.playbackRate === 0 || !BufferHelper.getBuffered(media).length) {
19299
+ if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
19300
+ this.nudgeRetry = 0;
19301
+ this.stallResolved(currentTime);
19319
19302
  // Fire MEDIA_ENDED to workaround event not being dispatched by browser
19320
- if (!this.ended && media.ended) {
19303
+ if (!this.ended && media.ended && this.hls) {
19321
19304
  this.ended = currentTime || 1;
19322
19305
  this.hls.trigger(Events.MEDIA_ENDED, {
19323
19306
  stalled: false
19324
19307
  });
19325
19308
  }
19309
+ return;
19310
+ }
19311
+ if (!BufferHelper.getBuffered(media).length) {
19326
19312
  this.nudgeRetry = 0;
19327
19313
  return;
19328
19314
  }
19329
19315
  var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
19330
19316
  var nextStart = bufferInfo.nextStart || 0;
19331
- if (seeking) {
19317
+ var fragmentTracker = this.fragmentTracker;
19318
+ if (seeking && fragmentTracker) {
19332
19319
  // Waiting for seeking in a buffered range to complete
19333
19320
  var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
19334
19321
  // Next buffered range is too far ahead to jump to while still seeking
19335
- var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !this.fragmentTracker.getPartialFragment(currentTime);
19322
+ var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
19336
19323
  if (hasEnoughBuffer || noBufferGap) {
19337
19324
  return;
19338
19325
  }
@@ -19342,7 +19329,7 @@
19342
19329
 
19343
19330
  // Skip start gaps if we haven't played, but the last poll detected the start of a stall
19344
19331
  // The addition poll gives the browser a chance to jump the gap for us
19345
- if (!this.moved && this.stalled !== null) {
19332
+ if (!this.moved && this.stalled !== null && fragmentTracker) {
19346
19333
  // There is no playable buffer (seeked, waiting for buffer)
19347
19334
  var isBuffered = bufferInfo.len > 0;
19348
19335
  if (!isBuffered && !nextStart) {
@@ -19356,7 +19343,7 @@
19356
19343
  // that begins over 1 target duration after the video start position.
19357
19344
  var isLive = !!(levelDetails != null && levelDetails.live);
19358
19345
  var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
19359
- var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
19346
+ var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
19360
19347
  if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
19361
19348
  if (!media.paused) {
19362
19349
  this._trySkipBufferHole(partialOrGap);
@@ -19366,16 +19353,27 @@
19366
19353
  }
19367
19354
 
19368
19355
  // Start tracking stall time
19356
+ var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
19357
+ if (!config) {
19358
+ return;
19359
+ }
19360
+ var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
19369
19361
  var tnow = self.performance.now();
19362
+ var tWaiting = this.waiting;
19370
19363
  if (stalled === null) {
19371
- this.stalled = tnow;
19364
+ // Use time of recent "waiting" event
19365
+ if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
19366
+ this.stalled = tWaiting;
19367
+ } else {
19368
+ this.stalled = tnow;
19369
+ }
19372
19370
  return;
19373
19371
  }
19374
19372
  var stalledDuration = tnow - stalled;
19375
- if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
19373
+ if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
19376
19374
  // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
19377
19375
  if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
19378
- if (stalledDuration < 1000 || this.ended) {
19376
+ if (this.ended) {
19379
19377
  return;
19380
19378
  }
19381
19379
  this.ended = currentTime || 1;
@@ -19386,12 +19384,26 @@
19386
19384
  }
19387
19385
  // Report stalling after trying to fix
19388
19386
  this._reportStall(bufferInfo);
19389
- if (!this.media) {
19387
+ if (!this.media || !this.hls) {
19390
19388
  return;
19391
19389
  }
19392
19390
  }
19393
19391
  var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
19394
19392
  this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
19393
+ };
19394
+ _proto.stallResolved = function stallResolved(currentTime) {
19395
+ var stalled = this.stalled;
19396
+ if (stalled && this.hls) {
19397
+ this.stalled = null;
19398
+ // The playhead is now moving, but was previously stalled
19399
+ if (this.stallReported) {
19400
+ var stalledDuration = self.performance.now() - stalled;
19401
+ this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
19402
+ this.stallReported = false;
19403
+ this.waiting = 0;
19404
+ this.hls.trigger(Events.STALL_RESOLVED, {});
19405
+ }
19406
+ }
19395
19407
  }
19396
19408
 
19397
19409
  /**
@@ -19401,10 +19413,11 @@
19401
19413
  * @private
19402
19414
  */;
19403
19415
  _proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
19404
- var config = this.config,
19405
- fragmentTracker = this.fragmentTracker,
19416
+ var _this$hls2;
19417
+ var fragmentTracker = this.fragmentTracker,
19406
19418
  media = this.media;
19407
- if (media === null) {
19419
+ var config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
19420
+ if (!media || !fragmentTracker || !config) {
19408
19421
  return;
19409
19422
  }
19410
19423
  var currentTime = media.currentTime;
@@ -19424,13 +19437,12 @@
19424
19437
  // we may just have to "nudge" the playlist as the browser decoding/rendering engine
19425
19438
  // needs to cross some sort of threshold covering all source-buffers content
19426
19439
  // to start playing properly.
19427
- if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
19440
+ var bufferedRanges = bufferInfo.buffered;
19441
+ if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
19428
19442
  this.warn('Trying to nudge playhead over buffer-hole');
19429
19443
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
19430
19444
  // We only try to jump the hole if it's under the configured size
19431
- // Reset stalled so to rearm watchdog timer
19432
- this.stalled = null;
19433
- this._tryNudgeBuffer();
19445
+ this._tryNudgeBuffer(bufferInfo);
19434
19446
  }
19435
19447
  }
19436
19448
 
@@ -19442,8 +19454,9 @@
19442
19454
  _proto._reportStall = function _reportStall(bufferInfo) {
19443
19455
  var hls = this.hls,
19444
19456
  media = this.media,
19445
- stallReported = this.stallReported;
19446
- if (!stallReported && media) {
19457
+ stallReported = this.stallReported,
19458
+ stalled = this.stalled;
19459
+ if (!stallReported && stalled !== null && media && hls) {
19447
19460
  // Report stalled error once
19448
19461
  this.stallReported = true;
19449
19462
  var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
@@ -19453,7 +19466,11 @@
19453
19466
  details: ErrorDetails.BUFFER_STALLED_ERROR,
19454
19467
  fatal: false,
19455
19468
  error: error,
19456
- buffer: bufferInfo.len
19469
+ buffer: bufferInfo.len,
19470
+ bufferInfo: bufferInfo,
19471
+ stalled: {
19472
+ start: stalled
19473
+ }
19457
19474
  });
19458
19475
  }
19459
19476
  }
@@ -19464,10 +19481,11 @@
19464
19481
  * @private
19465
19482
  */;
19466
19483
  _proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
19467
- var config = this.config,
19468
- hls = this.hls,
19484
+ var _this$hls3;
19485
+ var fragmentTracker = this.fragmentTracker,
19469
19486
  media = this.media;
19470
- if (media === null) {
19487
+ var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
19488
+ if (!media || !fragmentTracker || !config) {
19471
19489
  return 0;
19472
19490
  }
19473
19491
 
@@ -19482,7 +19500,6 @@
19482
19500
  if (gapLength > 0 && (bufferStarved || waiting)) {
19483
19501
  // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
19484
19502
  if (gapLength > config.maxBufferHole) {
19485
- var fragmentTracker = this.fragmentTracker;
19486
19503
  var startGap = false;
19487
19504
  if (currentTime === 0) {
19488
19505
  var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
@@ -19513,17 +19530,18 @@
19513
19530
  var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
19514
19531
  this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
19515
19532
  this.moved = true;
19516
- this.stalled = null;
19517
19533
  media.currentTime = targetTime;
19518
- if (partial && !partial.gap) {
19534
+ if (partial && !partial.gap && this.hls) {
19519
19535
  var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
19520
- hls.trigger(Events.ERROR, {
19536
+ this.hls.trigger(Events.ERROR, {
19521
19537
  type: ErrorTypes.MEDIA_ERROR,
19522
19538
  details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
19523
19539
  fatal: false,
19524
19540
  error: error,
19525
19541
  reason: error.message,
19526
- frag: partial
19542
+ frag: partial,
19543
+ buffer: bufferInfo.len,
19544
+ bufferInfo: bufferInfo
19527
19545
  });
19528
19546
  }
19529
19547
  return targetTime;
@@ -19536,13 +19554,13 @@
19536
19554
  * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
19537
19555
  * @private
19538
19556
  */;
19539
- _proto._tryNudgeBuffer = function _tryNudgeBuffer() {
19540
- var config = this.config,
19541
- hls = this.hls,
19557
+ _proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
19558
+ var hls = this.hls,
19542
19559
  media = this.media,
19543
19560
  nudgeRetry = this.nudgeRetry;
19544
- if (media === null) {
19545
- return;
19561
+ var config = hls == null ? undefined : hls.config;
19562
+ if (!media || !config) {
19563
+ return 0;
19546
19564
  }
19547
19565
  var currentTime = media.currentTime;
19548
19566
  this.nudgeRetry++;
@@ -19556,7 +19574,9 @@
19556
19574
  type: ErrorTypes.MEDIA_ERROR,
19557
19575
  details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
19558
19576
  error: error,
19559
- fatal: false
19577
+ fatal: false,
19578
+ buffer: bufferInfo.len,
19579
+ bufferInfo: bufferInfo
19560
19580
  });
19561
19581
  } else {
19562
19582
  var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
@@ -19565,7 +19585,9 @@
19565
19585
  type: ErrorTypes.MEDIA_ERROR,
19566
19586
  details: ErrorDetails.BUFFER_STALLED_ERROR,
19567
19587
  error: _error,
19568
- fatal: true
19588
+ fatal: true,
19589
+ buffer: bufferInfo.len,
19590
+ bufferInfo: bufferInfo
19569
19591
  });
19570
19592
  }
19571
19593
  };
@@ -19735,7 +19757,7 @@
19735
19757
  return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment;
19736
19758
  }
19737
19759
 
19738
- var version = "1.6.0-beta.2.0.canary.10882";
19760
+ var version = "1.6.0-beta.2.0.canary.10884";
19739
19761
 
19740
19762
  // ensure the worker ends up in the bundle
19741
19763
  // If the worker should not be included this gets aliased to empty.js
@@ -20155,11 +20177,18 @@
20155
20177
  _this.backtrackFragment = null;
20156
20178
  _this.audioCodecSwitch = false;
20157
20179
  _this.videoBuffer = null;
20180
+ _this.onMediaWaiting = function () {
20181
+ var gapController = _this.gapController;
20182
+ if (gapController) {
20183
+ gapController.waiting = self.performance.now();
20184
+ }
20185
+ };
20158
20186
  _this.onMediaPlaying = function () {
20159
20187
  // tick to speed up FRAG_CHANGED triggering
20160
20188
  var gapController = _this.gapController;
20161
20189
  if (gapController) {
20162
20190
  gapController.ended = 0;
20191
+ gapController.waiting = 0;
20163
20192
  }
20164
20193
  _this.tick();
20165
20194
  };
@@ -20214,7 +20243,7 @@
20214
20243
  };
20215
20244
  _proto.onHandlerDestroying = function onHandlerDestroying() {
20216
20245
  // @ts-ignore
20217
- this.onMediaPlaying = this.onMediaSeeked = null;
20246
+ this.onMediaPlaying = this.onMediaSeeked = this.onMediaWaiting = null;
20218
20247
  this.unregisterListeners();
20219
20248
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
20220
20249
  };
@@ -20535,15 +20564,18 @@
20535
20564
  var media = data.media;
20536
20565
  media.removeEventListener('playing', this.onMediaPlaying);
20537
20566
  media.removeEventListener('seeked', this.onMediaSeeked);
20567
+ media.removeEventListener('waiting', this.onMediaWaiting);
20538
20568
  media.addEventListener('playing', this.onMediaPlaying);
20539
20569
  media.addEventListener('seeked', this.onMediaSeeked);
20540
- this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
20570
+ media.addEventListener('waiting', this.onMediaWaiting);
20571
+ this.gapController = new GapController(media, this.fragmentTracker, this.hls);
20541
20572
  };
20542
20573
  _proto.onMediaDetaching = function onMediaDetaching(event, data) {
20543
20574
  var media = this.media;
20544
20575
  if (media) {
20545
20576
  media.removeEventListener('playing', this.onMediaPlaying);
20546
20577
  media.removeEventListener('seeked', this.onMediaSeeked);
20578
+ media.removeEventListener('waiting', this.onMediaWaiting);
20547
20579
  }
20548
20580
  this.videoBuffer = null;
20549
20581
  this.fragPlaying = null;
@@ -20953,7 +20985,7 @@
20953
20985
  var startPosition = this.startPosition;
20954
20986
  // only adjust currentTime if different from startPosition or if startPosition not buffered
20955
20987
  // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
20956
- if (startPosition >= 0) {
20988
+ if (startPosition >= 0 && currentTime < startPosition) {
20957
20989
  if (media.seeking) {
20958
20990
  this.log("could not seek to " + startPosition + ", already seeking at " + currentTime);
20959
20991
  return;