hls.js 1.6.0-beta.1.0.canary.10803 → 1.6.0-beta.1.0.canary.10806

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.
@@ -400,7 +400,7 @@ function enableLogs(debugConfig, context, id) {
400
400
  // Some browsers don't allow to use bind on console object anyway
401
401
  // fallback to default if needed
402
402
  try {
403
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.1.0.canary.10803"}`);
403
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.1.0.canary.10806"}`);
404
404
  } catch (e) {
405
405
  /* log fn threw an exception. All logger methods are no-ops. */
406
406
  return createLogger();
@@ -1677,7 +1677,7 @@ class AbrController extends Logger {
1677
1677
  const forcedAutoLevel = this.forcedAutoLevel;
1678
1678
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
1679
1679
  if (levelsSkipped.length) {
1680
- this.trace(`Skipped level(s) ${levelsSkipped.join(',')} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${level.codecs}" ${currentVideoRange}`);
1680
+ this.trace(`Skipped level(s) ${levelsSkipped.join(',')} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${currentCodecSet}" ${currentVideoRange}`);
1681
1681
  }
1682
1682
  this.info(`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${levelInfo.codecSet} videoRange:${levelInfo.videoRange} hls.loadLevel:${loadLevel}`);
1683
1683
  }
@@ -3254,6 +3254,21 @@ class LevelDetails {
3254
3254
  }
3255
3255
  return -1;
3256
3256
  }
3257
+ get maxPartIndex() {
3258
+ const partList = this.partList;
3259
+ if (partList) {
3260
+ const lastIndex = this.lastPartIndex;
3261
+ if (lastIndex !== -1) {
3262
+ for (let i = partList.length; i--;) {
3263
+ if (partList[i].index > lastIndex) {
3264
+ return partList[i].index;
3265
+ }
3266
+ }
3267
+ return lastIndex;
3268
+ }
3269
+ }
3270
+ return 0;
3271
+ }
3257
3272
  get lastPartSn() {
3258
3273
  var _this$partList3;
3259
3274
  if ((_this$partList3 = this.partList) != null && _this$partList3.length) {
@@ -5573,12 +5588,14 @@ function findPart(partList, sn, partIndex) {
5573
5588
  }
5574
5589
  function reassignFragmentLevelIndexes(levels) {
5575
5590
  levels.forEach((level, index) => {
5576
- const {
5577
- details
5578
- } = level;
5579
- if (details != null && details.fragments) {
5580
- details.fragments.forEach(fragment => {
5591
+ var _level$details;
5592
+ const fragments = (_level$details = level.details) == null ? void 0 : _level$details.fragments;
5593
+ if (fragments) {
5594
+ fragments.forEach(fragment => {
5581
5595
  fragment.level = index;
5596
+ if (fragment.initSegment) {
5597
+ fragment.initSegment.level = index;
5598
+ }
5582
5599
  });
5583
5600
  }
5584
5601
  });
@@ -5588,8 +5605,8 @@ class BasePlaylistController extends Logger {
5588
5605
  constructor(hls, logPrefix) {
5589
5606
  super(logPrefix, hls.logger);
5590
5607
  this.hls = void 0;
5591
- this.timer = -1;
5592
5608
  this.canLoad = false;
5609
+ this.timer = -1;
5593
5610
  this.hls = hls;
5594
5611
  }
5595
5612
  destroy() {
@@ -5694,11 +5711,23 @@ class BasePlaylistController extends Logger {
5694
5711
  if (details.live || previousDetails != null && previousDetails.live) {
5695
5712
  const levelOrTrack = 'levelInfo' in data ? data.levelInfo : data.track;
5696
5713
  details.reloaded(previousDetails);
5697
- this.log(`live playlist ${index} ${details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED'}`);
5698
5714
  // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments
5699
5715
  if (previousDetails && details.fragments.length > 0) {
5700
5716
  mergeDetails(previousDetails, details);
5701
5717
  }
5718
+ if (details.requestScheduled === -1) {
5719
+ details.requestScheduled = stats.loading.start;
5720
+ }
5721
+ const bufferInfo = this.hls.mainForwardBufferInfo;
5722
+ const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0;
5723
+ const distanceToLiveEdgeMs = (details.edge - position) * 1000;
5724
+ const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs);
5725
+ if (details.requestScheduled + reloadInterval < now) {
5726
+ details.requestScheduled = now;
5727
+ } else {
5728
+ details.requestScheduled += reloadInterval;
5729
+ }
5730
+ this.log(`live playlist ${index} ${details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED'}`);
5702
5731
  if (!this.canLoad || !details.live) {
5703
5732
  return;
5704
5733
  }
@@ -5712,12 +5741,16 @@ class BasePlaylistController extends Logger {
5712
5741
  const endSn = details.endSN;
5713
5742
  const lastPartIndex = details.lastPartIndex;
5714
5743
  const hasParts = lastPartIndex !== -1;
5715
- const lastPart = lastPartSn === endSn;
5716
- // When low latency mode is disabled, we'll skip part requests once the last part index is found
5717
- const nextSnStartIndex = lowLatencyMode ? 0 : lastPartIndex;
5744
+ const atLastPartOfSegment = lastPartSn === endSn;
5718
5745
  if (hasParts) {
5719
- msn = lastPart ? endSn + 1 : lastPartSn;
5720
- part = lastPart ? nextSnStartIndex : lastPartIndex + 1;
5746
+ // When low latency mode is disabled, request the last part of the next segment
5747
+ if (atLastPartOfSegment) {
5748
+ msn = endSn + 1;
5749
+ part = lowLatencyMode ? 0 : lastPartIndex;
5750
+ } else {
5751
+ msn = lastPartSn;
5752
+ part = lowLatencyMode ? lastPartIndex + 1 : details.maxPartIndex;
5753
+ }
5721
5754
  } else {
5722
5755
  msn = endSn + 1;
5723
5756
  }
@@ -5749,35 +5782,24 @@ class BasePlaylistController extends Logger {
5749
5782
  details.tuneInGoal = currentGoal;
5750
5783
  }
5751
5784
  deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part);
5752
- if (lowLatencyMode || !lastPart) {
5785
+ if (lowLatencyMode || !atLastPartOfSegment) {
5786
+ details.requestScheduled = now;
5753
5787
  this.loadingPlaylist(levelOrTrack, deliveryDirectives);
5754
5788
  return;
5755
5789
  }
5756
5790
  } else if (details.canBlockReload || details.canSkipUntil) {
5757
5791
  deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part);
5758
5792
  }
5759
- if (details.requestScheduled === -1) {
5760
- details.requestScheduled = stats.loading.start;
5761
- }
5762
5793
  if (deliveryDirectives && msn !== undefined && details.canBlockReload) {
5763
- details.requestScheduled -= details.partTarget * 1000 || 1000;
5794
+ details.requestScheduled = stats.loading.first + Math.max(reloadInterval - elapsed * 2, reloadInterval / 2);
5764
5795
  }
5765
- const bufferInfo = this.hls.mainForwardBufferInfo;
5766
- const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0;
5767
- const distanceToLiveEdgeMs = (details.edge - position) * 1000;
5768
- const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs);
5769
- if (details.requestScheduled + reloadInterval < now) {
5770
- details.requestScheduled = now;
5771
- } else {
5772
- details.requestScheduled += reloadInterval;
5773
- }
5774
- this.scheduleLoading(levelOrTrack, deliveryDirectives);
5796
+ this.scheduleLoading(levelOrTrack, deliveryDirectives, details);
5775
5797
  } else {
5776
5798
  this.clearTimer();
5777
5799
  }
5778
5800
  }
5779
- scheduleLoading(levelOrTrack, deliveryDirectives) {
5780
- const details = levelOrTrack.details;
5801
+ scheduleLoading(levelOrTrack, deliveryDirectives, updatedDetails) {
5802
+ const details = updatedDetails || levelOrTrack.details;
5781
5803
  if (!details) {
5782
5804
  this.loadingPlaylist(levelOrTrack, deliveryDirectives);
5783
5805
  return;
@@ -5790,22 +5812,7 @@ class BasePlaylistController extends Logger {
5790
5812
  }
5791
5813
  const estimatedTimeUntilUpdate = requestScheduled - now;
5792
5814
  this.log(`reload live playlist ${levelOrTrack.name || levelOrTrack.bitrate + 'bps'} in ${Math.round(estimatedTimeUntilUpdate)} ms`);
5793
- // this.log(
5794
- // `live reload ${details.updated ? 'REFRESHED' : 'MISSED'}
5795
- // reload in ${estimatedTimeUntilUpdate / 1000}
5796
- // round trip ${(stats.loading.end - stats.loading.start) / 1000}
5797
- // diff ${
5798
- // (reloadInterval -
5799
- // (estimatedTimeUntilUpdate +
5800
- // stats.loading.end -
5801
- // stats.loading.start)) /
5802
- // 1000
5803
- // }
5804
- // reload interval ${reloadInterval / 1000}
5805
- // target duration ${details.targetduration}
5806
- // distance to edge ${distanceToLiveEdgeMs / 1000}`
5807
- // );
5808
-
5815
+ this.clearTimer();
5809
5816
  this.timer = self.setTimeout(() => this.loadingPlaylist(levelOrTrack, deliveryDirectives), estimatedTimeUntilUpdate);
5810
5817
  }
5811
5818
  getDeliveryDirectives(details, previousDeliveryDirectives, msn, part) {
@@ -5839,6 +5846,7 @@ class BasePlaylistController extends Logger {
5839
5846
  } else {
5840
5847
  const delay = getRetryDelay(retryConfig, retryCount);
5841
5848
  // Schedule level/track reload
5849
+ this.clearTimer();
5842
5850
  this.timer = self.setTimeout(() => this.loadPlaylist(), delay);
5843
5851
  this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after "${errorDetails}" in ${delay}ms`);
5844
5852
  }
@@ -18488,19 +18496,6 @@ class LevelController extends BasePlaylistController {
18488
18496
  altAudio: !audioOnly && audioTracks.some(t => !!t.url)
18489
18497
  };
18490
18498
  this.hls.trigger(Events.MANIFEST_PARSED, edata);
18491
-
18492
- // Initiate loading after all controllers have received MANIFEST_PARSED
18493
- const {
18494
- config: {
18495
- autoStartLoad,
18496
- startPosition
18497
- },
18498
- forceStartLoad
18499
- } = this.hls;
18500
- if (autoStartLoad || forceStartLoad) {
18501
- this.log(`${autoStartLoad ? 'auto' : 'force'} startLoad with configured startPosition ${startPosition}`);
18502
- this.hls.startLoad(startPosition);
18503
- }
18504
18499
  }
18505
18500
  get levels() {
18506
18501
  if (this._levels.length === 0) {
@@ -18672,8 +18667,8 @@ class LevelController extends BasePlaylistController {
18672
18667
  return;
18673
18668
  }
18674
18669
 
18675
- // only process level loaded events matching with expected level
18676
- if (curLevel === this.currentLevel) {
18670
+ // only process level loaded events matching with expected level or prior to switch when media playlist is loaded directly
18671
+ if (curLevel === this.currentLevel || data.withoutMultiVariant) {
18677
18672
  // reset level load error counter on successful level loaded only if there is no issues with fragments
18678
18673
  if (curLevel.fragmentError === 0) {
18679
18674
  curLevel.loadError = 0;
@@ -18728,6 +18723,9 @@ class LevelController extends BasePlaylistController {
18728
18723
  }
18729
18724
  removeLevel(levelIndex) {
18730
18725
  var _this$currentLevel;
18726
+ if (this._levels.length === 1) {
18727
+ return;
18728
+ }
18731
18729
  const levels = this._levels.filter((level, index) => {
18732
18730
  if (index !== levelIndex) {
18733
18731
  return true;
@@ -18749,6 +18747,14 @@ class LevelController extends BasePlaylistController {
18749
18747
  if (this.currentLevelIndex > -1 && (_this$currentLevel = this.currentLevel) != null && _this$currentLevel.details) {
18750
18748
  this.currentLevelIndex = this.currentLevel.details.fragments[0].level;
18751
18749
  }
18750
+ if (this.manualLevelIndex > -1) {
18751
+ this.manualLevelIndex = this.currentLevelIndex;
18752
+ }
18753
+ const maxLevel = levels.length - 1;
18754
+ this._firstLevel = Math.min(this._firstLevel, maxLevel);
18755
+ if (this._startLevel) {
18756
+ this._startLevel = Math.min(this._startLevel, maxLevel);
18757
+ }
18752
18758
  this.hls.trigger(Events.LEVELS_UPDATED, {
18753
18759
  levels
18754
18760
  });
@@ -19130,7 +19136,7 @@ class GapController extends Logger {
19130
19136
  }
19131
19137
  }
19132
19138
 
19133
- const version = "1.6.0-beta.1.0.canary.10803";
19139
+ const version = "1.6.0-beta.1.0.canary.10806";
19134
19140
 
19135
19141
  // ensure the worker ends up in the bundle
19136
19142
  // If the worker should not be included this gets aliased to empty.js
@@ -20358,6 +20364,9 @@ class StreamController extends BaseStreamController {
20358
20364
  onLevelsUpdated(event, data) {
20359
20365
  if (this.level > -1 && this.fragCurrent) {
20360
20366
  this.level = this.fragCurrent.level;
20367
+ if (this.level === -1) {
20368
+ this.resetWhenMissingContext(this.fragCurrent);
20369
+ }
20361
20370
  }
20362
20371
  this.levels = data.levels;
20363
20372
  }
@@ -21263,7 +21272,7 @@ class PlaylistLoader {
21263
21272
  if (loaderContext && loaderContext.levelOrTrack === context.levelOrTrack && (loaderContext.url === context.url || loaderContext.deliveryDirectives && !context.deliveryDirectives)) {
21264
21273
  // same URL can't overlap, or wait for blocking request
21265
21274
  if (loaderContext.url === context.url) {
21266
- logger.log(`[playlist-loader]: playlist request ongoing`);
21275
+ logger.log(`[playlist-loader]: ignore ${context.url} ongoing request`);
21267
21276
  } else {
21268
21277
  logger.log(`[playlist-loader]: ignore ${context.url} in favor of ${loaderContext.url}`);
21269
21278
  }
@@ -21329,7 +21338,7 @@ class PlaylistLoader {
21329
21338
  return;
21330
21339
  }
21331
21340
  stats.parsing.start = performance.now();
21332
- if (M3U8Parser.isMediaPlaylist(string)) {
21341
+ if (M3U8Parser.isMediaPlaylist(string) || context.type !== PlaylistContextType.MANIFEST) {
21333
21342
  this.handleTrackOrLevelPlaylist(response, stats, context, networkDetails || null, loader);
21334
21343
  } else {
21335
21344
  this.handleMasterPlaylist(response, stats, context, networkDetails);
@@ -21347,6 +21356,22 @@ class PlaylistLoader {
21347
21356
 
21348
21357
  loader.load(context, loaderConfig, loaderCallbacks);
21349
21358
  }
21359
+ checkAutostartLoad() {
21360
+ if (!this.hls) {
21361
+ return;
21362
+ }
21363
+ const {
21364
+ config: {
21365
+ autoStartLoad,
21366
+ startPosition
21367
+ },
21368
+ forceStartLoad
21369
+ } = this.hls;
21370
+ if (autoStartLoad || forceStartLoad) {
21371
+ this.hls.logger.log(`${autoStartLoad ? 'auto' : 'force'} startLoad with configured startPosition ${startPosition}`);
21372
+ this.hls.startLoad(startPosition);
21373
+ }
21374
+ }
21350
21375
  handleMasterPlaylist(response, stats, context, networkDetails) {
21351
21376
  const hls = this.hls;
21352
21377
  const string = response.data;
@@ -21408,6 +21433,7 @@ class PlaylistLoader {
21408
21433
  startTimeOffset,
21409
21434
  variableList
21410
21435
  });
21436
+ this.checkAutostartLoad();
21411
21437
  }
21412
21438
  handleTrackOrLevelPlaylist(response, stats, context, networkDetails, loader) {
21413
21439
  const hls = this.hls;
@@ -21453,6 +21479,9 @@ class PlaylistLoader {
21453
21479
  // extend the context with the new levelDetails property
21454
21480
  context.levelDetails = levelDetails;
21455
21481
  this.handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader);
21482
+ if (type === PlaylistContextType.MANIFEST && (!levelDetails.playlistParsingError || !levelDetails.fragments.length && levelDetails.live)) {
21483
+ this.checkAutostartLoad();
21484
+ }
21456
21485
  }
21457
21486
  handleManifestParsingError(response, context, error, networkDetails, stats) {
21458
21487
  this.hls.trigger(Events.ERROR, {
@@ -21535,7 +21564,7 @@ class PlaylistLoader {
21535
21564
  const parent = mapContextToLevelType(context);
21536
21565
  const levelIndex = typeof context.level === 'number' && parent === PlaylistLevelType.MAIN ? level : undefined;
21537
21566
  if (!levelDetails.fragments.length) {
21538
- const _error = new Error('No Segments found in Playlist');
21567
+ const _error = levelDetails.playlistParsingError = new Error('No Segments found in Playlist');
21539
21568
  hls.trigger(Events.ERROR, {
21540
21569
  type: ErrorTypes.NETWORK_ERROR,
21541
21570
  details: ErrorDetails.LEVEL_EMPTY_ERROR,
@@ -21591,7 +21620,8 @@ class PlaylistLoader {
21591
21620
  id: id || 0,
21592
21621
  stats,
21593
21622
  networkDetails,
21594
- deliveryDirectives
21623
+ deliveryDirectives,
21624
+ withoutMultiVariant: type === PlaylistContextType.MANIFEST
21595
21625
  });
21596
21626
  break;
21597
21627
  case PlaylistContextType.AUDIO_TRACK:
@@ -21811,8 +21841,9 @@ class Hls {
21811
21841
  this.cmcdController = void 0;
21812
21842
  this._media = null;
21813
21843
  this._url = null;
21814
- this.triggeringException = void 0;
21815
21844
  this._sessionId = void 0;
21845
+ this.triggeringException = void 0;
21846
+ this.started = false;
21816
21847
  const logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance', userConfig.assetPlayerId);
21817
21848
  const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
21818
21849
  this.userConfig = userConfig;
@@ -22067,10 +22098,14 @@ class Hls {
22067
22098
  */
22068
22099
  startLoad(startPosition = -1, skipSeekToStartPosition) {
22069
22100
  this.logger.log(`startLoad(${startPosition + (skipSeekToStartPosition ? ', <skip seek to start>' : '')})`);
22101
+ this.started = true;
22070
22102
  this.resumeBuffering();
22071
- this.networkControllers.forEach(controller => {
22072
- controller.startLoad(startPosition, skipSeekToStartPosition);
22073
- });
22103
+ for (let i = 0; i < this.networkControllers.length; i++) {
22104
+ this.networkControllers[i].startLoad(startPosition, skipSeekToStartPosition);
22105
+ if (!this.started || !this.networkControllers) {
22106
+ break;
22107
+ }
22108
+ }
22074
22109
  }
22075
22110
 
22076
22111
  /**
@@ -22078,9 +22113,20 @@ class Hls {
22078
22113
  */
22079
22114
  stopLoad() {
22080
22115
  this.logger.log('stopLoad');
22081
- this.networkControllers.forEach(controller => {
22082
- controller.stopLoad();
22083
- });
22116
+ this.started = false;
22117
+ for (let i = 0; i < this.networkControllers.length; i++) {
22118
+ this.networkControllers[i].stopLoad();
22119
+ if (this.started || !this.networkControllers) {
22120
+ break;
22121
+ }
22122
+ }
22123
+ }
22124
+
22125
+ /**
22126
+ * Returns whether loading, toggled with `startLoad()` and `stopLoad()`, is active or not`.
22127
+ */
22128
+ get loadingEnabled() {
22129
+ return this.started;
22084
22130
  }
22085
22131
 
22086
22132
  /**