hls.js 1.5.5-0.canary.9984 → 1.5.5-0.canary.9986

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.mjs CHANGED
@@ -512,7 +512,7 @@ function enableLogs(debugConfig, context, id) {
512
512
  // Some browsers don't allow to use bind on console object anyway
513
513
  // fallback to default if needed
514
514
  try {
515
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.5-0.canary.9984"}`);
515
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.5-0.canary.9986"}`);
516
516
  } catch (e) {
517
517
  /* log fn threw an exception. All logger methods are no-ops. */
518
518
  return createLogger();
@@ -6197,7 +6197,12 @@ class BasePlaylistController extends Logger {
6197
6197
  const cdnAge = lastAdvanced + details.ageHeader;
6198
6198
  let currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5);
6199
6199
  if (currentGoal > 0) {
6200
- if (previousDetails && currentGoal > previousDetails.tuneInGoal) {
6200
+ if (cdnAge > details.targetduration * 3) {
6201
+ // Omit segment and part directives when the last response was more than 3 target durations ago,
6202
+ this.log(`Playlist last advanced ${lastAdvanced.toFixed(2)}s ago. Omitting segment and part directives.`);
6203
+ msn = undefined;
6204
+ part = undefined;
6205
+ } else if (previousDetails != null && previousDetails.tuneInGoal && cdnAge - details.partTarget > previousDetails.tuneInGoal) {
6201
6206
  // If we attempted to get the next or latest playlist update, but currentGoal increased,
6202
6207
  // then we either can't catchup, or the "age" header cannot be trusted.
6203
6208
  this.warn(`CDN Tune-in goal increased from: ${previousDetails.tuneInGoal} to: ${currentGoal} with playlist age: ${details.age}`);
@@ -9182,6 +9187,7 @@ class BaseStreamController extends TaskLoop {
9182
9187
  this.decrypter = void 0;
9183
9188
  this.initPTS = [];
9184
9189
  this.buffering = true;
9190
+ this.loadingParts = false;
9185
9191
  this.onMediaSeeking = () => {
9186
9192
  const {
9187
9193
  config,
@@ -9218,6 +9224,14 @@ class BaseStreamController extends TaskLoop {
9218
9224
  // Remove gap fragments
9219
9225
  this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
9220
9226
  this.lastCurrentTime = currentTime;
9227
+ if (!this.loadingParts) {
9228
+ const bufferEnd = Math.max(bufferInfo.end, currentTime);
9229
+ const shouldLoadParts = this.shouldLoadParts(this.getLevelDetails(), bufferEnd);
9230
+ if (shouldLoadParts) {
9231
+ this.log(`LL-Part loading ON after seeking to ${currentTime.toFixed(2)} with buffer @${bufferEnd.toFixed(2)}`);
9232
+ this.loadingParts = shouldLoadParts;
9233
+ }
9234
+ }
9221
9235
  }
9222
9236
 
9223
9237
  // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
@@ -9628,8 +9642,16 @@ class BaseStreamController extends TaskLoop {
9628
9642
  } else if (!frag.encrypted && details.encryptedFragments.length) {
9629
9643
  this.keyLoader.loadClear(frag, details.encryptedFragments);
9630
9644
  }
9645
+ const fragPrevious = this.fragPrevious;
9646
+ if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) {
9647
+ const shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
9648
+ if (shouldLoadParts !== this.loadingParts) {
9649
+ this.log(`LL-Part loading ${shouldLoadParts ? 'ON' : 'OFF'} loading sn ${fragPrevious == null ? void 0 : fragPrevious.sn}->${frag.sn}`);
9650
+ this.loadingParts = shouldLoadParts;
9651
+ }
9652
+ }
9631
9653
  targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
9632
- if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
9654
+ if (this.loadingParts && frag.sn !== 'initSegment') {
9633
9655
  const partList = details.partList;
9634
9656
  if (partList && progressCallback) {
9635
9657
  if (targetBufferTime > frag.end && details.fragmentHint) {
@@ -9667,6 +9689,13 @@ class BaseStreamController extends TaskLoop {
9667
9689
  }
9668
9690
  }
9669
9691
  }
9692
+ if (frag.sn !== 'initSegment' && this.loadingParts) {
9693
+ this.log(`LL-Part loading OFF after next part miss @${targetBufferTime.toFixed(2)}`);
9694
+ this.loadingParts = false;
9695
+ } else if (!frag.url) {
9696
+ // Selected fragment hint for part but not loading parts
9697
+ return Promise.resolve(null);
9698
+ }
9670
9699
  this.log(`Loading fragment ${frag.sn} cc: ${frag.cc} ${details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''}${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
9671
9700
  // Don't update nextLoadPosition for fragments which are not buffered
9672
9701
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
@@ -9765,8 +9794,36 @@ class BaseStreamController extends TaskLoop {
9765
9794
  if (part) {
9766
9795
  part.stats.parsing.end = now;
9767
9796
  }
9797
+ // See if part loading should be disabled/enabled based on buffer and playback position.
9798
+ if (frag.sn !== 'initSegment') {
9799
+ const levelDetails = this.getLevelDetails();
9800
+ const loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
9801
+ const shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
9802
+ if (shouldLoadParts !== this.loadingParts) {
9803
+ this.log(`LL-Part loading ${shouldLoadParts ? 'ON' : 'OFF'} after parsing segment ending @${frag.end.toFixed(2)}`);
9804
+ this.loadingParts = shouldLoadParts;
9805
+ }
9806
+ }
9768
9807
  this.updateLevelTiming(frag, part, level, chunkMeta.partial);
9769
9808
  }
9809
+ shouldLoadParts(details, bufferEnd) {
9810
+ if (this.config.lowLatencyMode) {
9811
+ if (!details) {
9812
+ return this.loadingParts;
9813
+ }
9814
+ if (details != null && details.partList) {
9815
+ var _details$fragmentHint;
9816
+ // Buffer must be ahead of first part + duration of parts after last segment
9817
+ // and playback must be at or past segment adjacent to part list
9818
+ const firstPart = details.partList[0];
9819
+ const safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0);
9820
+ if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) {
9821
+ return true;
9822
+ }
9823
+ }
9824
+ }
9825
+ return false;
9826
+ }
9770
9827
  getCurrentContext(chunkMeta) {
9771
9828
  const {
9772
9829
  levels,
@@ -9915,7 +9972,8 @@ class BaseStreamController extends TaskLoop {
9915
9972
  config
9916
9973
  } = this;
9917
9974
  const start = fragments[0].start;
9918
- let frag;
9975
+ const canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
9976
+ let frag = null;
9919
9977
  if (levelDetails.live) {
9920
9978
  const initialLiveManifestSize = config.initialLiveManifestSize;
9921
9979
  if (fragLen < initialLiveManifestSize) {
@@ -9927,6 +9985,10 @@ class BaseStreamController extends TaskLoop {
9927
9985
  // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
9928
9986
  // we get the fragment matching that start time
9929
9987
  if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) {
9988
+ if (canLoadParts && !this.loadingParts) {
9989
+ this.log(`LL-Part loading ON for initial live fragment`);
9990
+ this.loadingParts = true;
9991
+ }
9930
9992
  frag = this.getInitialLiveFragment(levelDetails, fragments);
9931
9993
  this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;
9932
9994
  }
@@ -9937,7 +9999,7 @@ class BaseStreamController extends TaskLoop {
9937
9999
 
9938
10000
  // If we haven't run into any special cases already, just load the fragment most closely matching the requested position
9939
10001
  if (!frag) {
9940
- const end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd;
10002
+ const end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd;
9941
10003
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
9942
10004
  }
9943
10005
  return this.mapToInitFragWhenRequired(frag);
@@ -10059,7 +10121,7 @@ class BaseStreamController extends TaskLoop {
10059
10121
  } = levelDetails;
10060
10122
  const tolerance = config.maxFragLookUpTolerance;
10061
10123
  const partList = levelDetails.partList;
10062
- const loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint);
10124
+ const loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint);
10063
10125
  if (loadingParts && fragmentHint && !this.bitrateTest) {
10064
10126
  // Include incomplete fragment with parts at end
10065
10127
  fragments = fragments.concat(fragmentHint);
@@ -26658,7 +26720,12 @@ class LevelController extends BasePlaylistController {
26658
26720
  if (curLevel.fragmentError === 0) {
26659
26721
  curLevel.loadError = 0;
26660
26722
  }
26661
- this.playlistLoaded(level, data, curLevel.details);
26723
+ // Ignore matching details populated by loading a Media Playlist directly
26724
+ let previousDetails = curLevel.details;
26725
+ if (previousDetails === data.details && previousDetails.advanced) {
26726
+ previousDetails = undefined;
26727
+ }
26728
+ this.playlistLoaded(level, data, previousDetails);
26662
26729
  } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
26663
26730
  // received a delta playlist update that cannot be merged
26664
26731
  details.deltaUpdateFailed = true;
@@ -28527,7 +28594,7 @@ class Hls {
28527
28594
  * Get the video-dev/hls.js package version.
28528
28595
  */
28529
28596
  static get version() {
28530
- return "1.5.5-0.canary.9984";
28597
+ return "1.5.5-0.canary.9986";
28531
28598
  }
28532
28599
 
28533
28600
  /**