hls.js 1.6.0-beta.1.0.canary.10801 → 1.6.0-beta.1.0.canary.10804

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
@@ -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.10801"}`);
403
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.1.0.canary.10804"}`);
404
404
  } catch (e) {
405
405
  /* log fn threw an exception. All logger methods are no-ops. */
406
406
  return createLogger();
@@ -1953,7 +1953,7 @@ class AbrController extends Logger {
1953
1953
  const forcedAutoLevel = this.forcedAutoLevel;
1954
1954
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
1955
1955
  if (levelsSkipped.length) {
1956
- 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}`);
1956
+ 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}`);
1957
1957
  }
1958
1958
  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}`);
1959
1959
  }
@@ -6397,6 +6397,21 @@ class LevelDetails {
6397
6397
  }
6398
6398
  return -1;
6399
6399
  }
6400
+ get maxPartIndex() {
6401
+ const partList = this.partList;
6402
+ if (partList) {
6403
+ const lastIndex = this.lastPartIndex;
6404
+ if (lastIndex !== -1) {
6405
+ for (let i = partList.length; i--;) {
6406
+ if (partList[i].index > lastIndex) {
6407
+ return partList[i].index;
6408
+ }
6409
+ }
6410
+ return lastIndex;
6411
+ }
6412
+ }
6413
+ return 0;
6414
+ }
6400
6415
  get lastPartSn() {
6401
6416
  var _this$partList3;
6402
6417
  if ((_this$partList3 = this.partList) != null && _this$partList3.length) {
@@ -7834,12 +7849,14 @@ function findPart(partList, sn, partIndex) {
7834
7849
  }
7835
7850
  function reassignFragmentLevelIndexes(levels) {
7836
7851
  levels.forEach((level, index) => {
7837
- const {
7838
- details
7839
- } = level;
7840
- if (details != null && details.fragments) {
7841
- details.fragments.forEach(fragment => {
7852
+ var _level$details;
7853
+ const fragments = (_level$details = level.details) == null ? void 0 : _level$details.fragments;
7854
+ if (fragments) {
7855
+ fragments.forEach(fragment => {
7842
7856
  fragment.level = index;
7857
+ if (fragment.initSegment) {
7858
+ fragment.initSegment.level = index;
7859
+ }
7843
7860
  });
7844
7861
  }
7845
7862
  });
@@ -9813,7 +9830,7 @@ var eventemitter3 = {exports: {}};
9813
9830
  var eventemitter3Exports = eventemitter3.exports;
9814
9831
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
9815
9832
 
9816
- const version = "1.6.0-beta.1.0.canary.10801";
9833
+ const version = "1.6.0-beta.1.0.canary.10804";
9817
9834
 
9818
9835
  // ensure the worker ends up in the bundle
9819
9836
  // If the worker should not be included this gets aliased to empty.js
@@ -16882,8 +16899,8 @@ class BasePlaylistController extends Logger {
16882
16899
  constructor(hls, logPrefix) {
16883
16900
  super(logPrefix, hls.logger);
16884
16901
  this.hls = void 0;
16885
- this.timer = -1;
16886
16902
  this.canLoad = false;
16903
+ this.timer = -1;
16887
16904
  this.hls = hls;
16888
16905
  }
16889
16906
  destroy() {
@@ -16988,11 +17005,23 @@ class BasePlaylistController extends Logger {
16988
17005
  if (details.live || previousDetails != null && previousDetails.live) {
16989
17006
  const levelOrTrack = 'levelInfo' in data ? data.levelInfo : data.track;
16990
17007
  details.reloaded(previousDetails);
16991
- this.log(`live playlist ${index} ${details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED'}`);
16992
17008
  // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments
16993
17009
  if (previousDetails && details.fragments.length > 0) {
16994
17010
  mergeDetails(previousDetails, details);
16995
17011
  }
17012
+ if (details.requestScheduled === -1) {
17013
+ details.requestScheduled = stats.loading.start;
17014
+ }
17015
+ const bufferInfo = this.hls.mainForwardBufferInfo;
17016
+ const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0;
17017
+ const distanceToLiveEdgeMs = (details.edge - position) * 1000;
17018
+ const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs);
17019
+ if (details.requestScheduled + reloadInterval < now) {
17020
+ details.requestScheduled = now;
17021
+ } else {
17022
+ details.requestScheduled += reloadInterval;
17023
+ }
17024
+ this.log(`live playlist ${index} ${details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED'}`);
16996
17025
  if (!this.canLoad || !details.live) {
16997
17026
  return;
16998
17027
  }
@@ -17006,12 +17035,16 @@ class BasePlaylistController extends Logger {
17006
17035
  const endSn = details.endSN;
17007
17036
  const lastPartIndex = details.lastPartIndex;
17008
17037
  const hasParts = lastPartIndex !== -1;
17009
- const lastPart = lastPartSn === endSn;
17010
- // When low latency mode is disabled, we'll skip part requests once the last part index is found
17011
- const nextSnStartIndex = lowLatencyMode ? 0 : lastPartIndex;
17038
+ const atLastPartOfSegment = lastPartSn === endSn;
17012
17039
  if (hasParts) {
17013
- msn = lastPart ? endSn + 1 : lastPartSn;
17014
- part = lastPart ? nextSnStartIndex : lastPartIndex + 1;
17040
+ // When low latency mode is disabled, request the last part of the next segment
17041
+ if (atLastPartOfSegment) {
17042
+ msn = endSn + 1;
17043
+ part = lowLatencyMode ? 0 : lastPartIndex;
17044
+ } else {
17045
+ msn = lastPartSn;
17046
+ part = lowLatencyMode ? lastPartIndex + 1 : details.maxPartIndex;
17047
+ }
17015
17048
  } else {
17016
17049
  msn = endSn + 1;
17017
17050
  }
@@ -17043,35 +17076,24 @@ class BasePlaylistController extends Logger {
17043
17076
  details.tuneInGoal = currentGoal;
17044
17077
  }
17045
17078
  deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part);
17046
- if (lowLatencyMode || !lastPart) {
17079
+ if (lowLatencyMode || !atLastPartOfSegment) {
17080
+ details.requestScheduled = now;
17047
17081
  this.loadingPlaylist(levelOrTrack, deliveryDirectives);
17048
17082
  return;
17049
17083
  }
17050
17084
  } else if (details.canBlockReload || details.canSkipUntil) {
17051
17085
  deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part);
17052
17086
  }
17053
- if (details.requestScheduled === -1) {
17054
- details.requestScheduled = stats.loading.start;
17055
- }
17056
17087
  if (deliveryDirectives && msn !== undefined && details.canBlockReload) {
17057
- details.requestScheduled -= details.partTarget * 1000 || 1000;
17058
- }
17059
- const bufferInfo = this.hls.mainForwardBufferInfo;
17060
- const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0;
17061
- const distanceToLiveEdgeMs = (details.edge - position) * 1000;
17062
- const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs);
17063
- if (details.requestScheduled + reloadInterval < now) {
17064
- details.requestScheduled = now;
17065
- } else {
17066
- details.requestScheduled += reloadInterval;
17088
+ details.requestScheduled = stats.loading.first + Math.max(reloadInterval - elapsed * 2, reloadInterval / 2);
17067
17089
  }
17068
- this.scheduleLoading(levelOrTrack, deliveryDirectives);
17090
+ this.scheduleLoading(levelOrTrack, deliveryDirectives, details);
17069
17091
  } else {
17070
17092
  this.clearTimer();
17071
17093
  }
17072
17094
  }
17073
- scheduleLoading(levelOrTrack, deliveryDirectives) {
17074
- const details = levelOrTrack.details;
17095
+ scheduleLoading(levelOrTrack, deliveryDirectives, updatedDetails) {
17096
+ const details = updatedDetails || levelOrTrack.details;
17075
17097
  if (!details) {
17076
17098
  this.loadingPlaylist(levelOrTrack, deliveryDirectives);
17077
17099
  return;
@@ -17084,22 +17106,7 @@ class BasePlaylistController extends Logger {
17084
17106
  }
17085
17107
  const estimatedTimeUntilUpdate = requestScheduled - now;
17086
17108
  this.log(`reload live playlist ${levelOrTrack.name || levelOrTrack.bitrate + 'bps'} in ${Math.round(estimatedTimeUntilUpdate)} ms`);
17087
- // this.log(
17088
- // `live reload ${details.updated ? 'REFRESHED' : 'MISSED'}
17089
- // reload in ${estimatedTimeUntilUpdate / 1000}
17090
- // round trip ${(stats.loading.end - stats.loading.start) / 1000}
17091
- // diff ${
17092
- // (reloadInterval -
17093
- // (estimatedTimeUntilUpdate +
17094
- // stats.loading.end -
17095
- // stats.loading.start)) /
17096
- // 1000
17097
- // }
17098
- // reload interval ${reloadInterval / 1000}
17099
- // target duration ${details.targetduration}
17100
- // distance to edge ${distanceToLiveEdgeMs / 1000}`
17101
- // );
17102
-
17109
+ this.clearTimer();
17103
17110
  this.timer = self.setTimeout(() => this.loadingPlaylist(levelOrTrack, deliveryDirectives), estimatedTimeUntilUpdate);
17104
17111
  }
17105
17112
  getDeliveryDirectives(details, previousDeliveryDirectives, msn, part) {
@@ -17133,6 +17140,7 @@ class BasePlaylistController extends Logger {
17133
17140
  } else {
17134
17141
  const delay = getRetryDelay(retryConfig, retryCount);
17135
17142
  // Schedule level/track reload
17143
+ this.clearTimer();
17136
17144
  this.timer = self.setTimeout(() => this.loadPlaylist(), delay);
17137
17145
  this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after "${errorDetails}" in ${delay}ms`);
17138
17146
  }
@@ -24448,7 +24456,7 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`);
24448
24456
  (_player$media = player.media) == null ? void 0 : _player$media.play();
24449
24457
  }
24450
24458
  } else if (scheduledItem !== null) {
24451
- this.resumePrimary(scheduledItem, index);
24459
+ this.resumePrimary(scheduledItem, index, currentItem);
24452
24460
  if (this.shouldPlay) {
24453
24461
  var _this$hls$media;
24454
24462
  (_this$hls$media = this.hls.media) == null ? void 0 : _this$hls$media.play();
@@ -24474,12 +24482,15 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`);
24474
24482
  var _this$primaryDetails;
24475
24483
  return !!((_this$primaryDetails = this.primaryDetails) != null && _this$primaryDetails.live);
24476
24484
  }
24477
- resumePrimary(scheduledItem, index) {
24485
+ resumePrimary(scheduledItem, index, fromItem) {
24478
24486
  var _this$detachedData3;
24479
24487
  this.playingItem = scheduledItem;
24480
24488
  this.playingAsset = null;
24481
24489
  this.waitingItem = null;
24482
24490
  this.bufferedToItem(scheduledItem);
24491
+ if (!fromItem) {
24492
+ return;
24493
+ }
24483
24494
  this.log(`resuming ${segmentToString(scheduledItem)}`);
24484
24495
  if (!((_this$detachedData3 = this.detachedData) != null && _this$detachedData3.mediaSource)) {
24485
24496
  let timelinePos = this.timelinePos;
@@ -24537,13 +24548,18 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`);
24537
24548
  } else {
24538
24549
  this.transferMediaTo(hls, media);
24539
24550
  if (skipSeekToStartPosition) {
24540
- hls.startLoad(timelinePos, skipSeekToStartPosition);
24551
+ this.startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition);
24541
24552
  }
24542
24553
  }
24543
24554
  if (!skipSeekToStartPosition) {
24544
24555
  // Set primary position to resume time
24545
24556
  this.timelinePos = timelinePos;
24546
- hls.startLoad(timelinePos, skipSeekToStartPosition);
24557
+ this.startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition);
24558
+ }
24559
+ }
24560
+ startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition) {
24561
+ if (this.hls.loadingEnabled) {
24562
+ this.hls.startLoad(timelinePos, skipSeekToStartPosition);
24547
24563
  }
24548
24564
  }
24549
24565
 
@@ -24789,7 +24805,7 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`);
24789
24805
  preloadPrimary(item) {
24790
24806
  const index = this.findItemIndex(item);
24791
24807
  const timelinePos = this.getPrimaryResumption(item, index);
24792
- this.hls.startLoad(timelinePos);
24808
+ this.startLoadingPrimaryAt(timelinePos);
24793
24809
  }
24794
24810
  bufferedToEvent(item, assetListIndex) {
24795
24811
  const interstitial = item.event;
@@ -24929,7 +24945,7 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`);
24929
24945
  }
24930
24946
  }
24931
24947
  const playerConfig = _objectSpread2(_objectSpread2({}, userConfig), {}, {
24932
- // autoStartLoad: false,
24948
+ autoStartLoad: true,
24933
24949
  startFragPrefetch: true,
24934
24950
  primarySessionId: primary.sessionId,
24935
24951
  assetPlayerId: assetItem.identifier,
@@ -30413,19 +30429,6 @@ class LevelController extends BasePlaylistController {
30413
30429
  altAudio: !audioOnly && audioTracks.some(t => !!t.url)
30414
30430
  };
30415
30431
  this.hls.trigger(Events.MANIFEST_PARSED, edata);
30416
-
30417
- // Initiate loading after all controllers have received MANIFEST_PARSED
30418
- const {
30419
- config: {
30420
- autoStartLoad,
30421
- startPosition
30422
- },
30423
- forceStartLoad
30424
- } = this.hls;
30425
- if (autoStartLoad || forceStartLoad) {
30426
- this.log(`${autoStartLoad ? 'auto' : 'force'} startLoad with configured startPosition ${startPosition}`);
30427
- this.hls.startLoad(startPosition);
30428
- }
30429
30432
  }
30430
30433
  get levels() {
30431
30434
  if (this._levels.length === 0) {
@@ -30597,8 +30600,8 @@ class LevelController extends BasePlaylistController {
30597
30600
  return;
30598
30601
  }
30599
30602
 
30600
- // only process level loaded events matching with expected level
30601
- if (curLevel === this.currentLevel) {
30603
+ // only process level loaded events matching with expected level or prior to switch when media playlist is loaded directly
30604
+ if (curLevel === this.currentLevel || data.withoutMultiVariant) {
30602
30605
  // reset level load error counter on successful level loaded only if there is no issues with fragments
30603
30606
  if (curLevel.fragmentError === 0) {
30604
30607
  curLevel.loadError = 0;
@@ -30653,6 +30656,9 @@ class LevelController extends BasePlaylistController {
30653
30656
  }
30654
30657
  removeLevel(levelIndex) {
30655
30658
  var _this$currentLevel;
30659
+ if (this._levels.length === 1) {
30660
+ return;
30661
+ }
30656
30662
  const levels = this._levels.filter((level, index) => {
30657
30663
  if (index !== levelIndex) {
30658
30664
  return true;
@@ -30674,6 +30680,14 @@ class LevelController extends BasePlaylistController {
30674
30680
  if (this.currentLevelIndex > -1 && (_this$currentLevel = this.currentLevel) != null && _this$currentLevel.details) {
30675
30681
  this.currentLevelIndex = this.currentLevel.details.fragments[0].level;
30676
30682
  }
30683
+ if (this.manualLevelIndex > -1) {
30684
+ this.manualLevelIndex = this.currentLevelIndex;
30685
+ }
30686
+ const maxLevel = levels.length - 1;
30687
+ this._firstLevel = Math.min(this._firstLevel, maxLevel);
30688
+ if (this._startLevel) {
30689
+ this._startLevel = Math.min(this._startLevel, maxLevel);
30690
+ }
30677
30691
  this.hls.trigger(Events.LEVELS_UPDATED, {
30678
30692
  levels
30679
30693
  });
@@ -31901,6 +31915,9 @@ class StreamController extends BaseStreamController {
31901
31915
  onLevelsUpdated(event, data) {
31902
31916
  if (this.level > -1 && this.fragCurrent) {
31903
31917
  this.level = this.fragCurrent.level;
31918
+ if (this.level === -1) {
31919
+ this.resetWhenMissingContext(this.fragCurrent);
31920
+ }
31904
31921
  }
31905
31922
  this.levels = data.levels;
31906
31923
  }
@@ -32806,7 +32823,7 @@ class PlaylistLoader {
32806
32823
  if (loaderContext && loaderContext.levelOrTrack === context.levelOrTrack && (loaderContext.url === context.url || loaderContext.deliveryDirectives && !context.deliveryDirectives)) {
32807
32824
  // same URL can't overlap, or wait for blocking request
32808
32825
  if (loaderContext.url === context.url) {
32809
- logger.log(`[playlist-loader]: playlist request ongoing`);
32826
+ logger.log(`[playlist-loader]: ignore ${context.url} ongoing request`);
32810
32827
  } else {
32811
32828
  logger.log(`[playlist-loader]: ignore ${context.url} in favor of ${loaderContext.url}`);
32812
32829
  }
@@ -32872,7 +32889,7 @@ class PlaylistLoader {
32872
32889
  return;
32873
32890
  }
32874
32891
  stats.parsing.start = performance.now();
32875
- if (M3U8Parser.isMediaPlaylist(string)) {
32892
+ if (M3U8Parser.isMediaPlaylist(string) || context.type !== PlaylistContextType.MANIFEST) {
32876
32893
  this.handleTrackOrLevelPlaylist(response, stats, context, networkDetails || null, loader);
32877
32894
  } else {
32878
32895
  this.handleMasterPlaylist(response, stats, context, networkDetails);
@@ -32890,6 +32907,22 @@ class PlaylistLoader {
32890
32907
 
32891
32908
  loader.load(context, loaderConfig, loaderCallbacks);
32892
32909
  }
32910
+ checkAutostartLoad() {
32911
+ if (!this.hls) {
32912
+ return;
32913
+ }
32914
+ const {
32915
+ config: {
32916
+ autoStartLoad,
32917
+ startPosition
32918
+ },
32919
+ forceStartLoad
32920
+ } = this.hls;
32921
+ if (autoStartLoad || forceStartLoad) {
32922
+ this.hls.logger.log(`${autoStartLoad ? 'auto' : 'force'} startLoad with configured startPosition ${startPosition}`);
32923
+ this.hls.startLoad(startPosition);
32924
+ }
32925
+ }
32893
32926
  handleMasterPlaylist(response, stats, context, networkDetails) {
32894
32927
  const hls = this.hls;
32895
32928
  const string = response.data;
@@ -32951,6 +32984,7 @@ class PlaylistLoader {
32951
32984
  startTimeOffset,
32952
32985
  variableList
32953
32986
  });
32987
+ this.checkAutostartLoad();
32954
32988
  }
32955
32989
  handleTrackOrLevelPlaylist(response, stats, context, networkDetails, loader) {
32956
32990
  const hls = this.hls;
@@ -32996,6 +33030,9 @@ class PlaylistLoader {
32996
33030
  // extend the context with the new levelDetails property
32997
33031
  context.levelDetails = levelDetails;
32998
33032
  this.handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader);
33033
+ if (type === PlaylistContextType.MANIFEST && (!levelDetails.playlistParsingError || !levelDetails.fragments.length && levelDetails.live)) {
33034
+ this.checkAutostartLoad();
33035
+ }
32999
33036
  }
33000
33037
  handleManifestParsingError(response, context, error, networkDetails, stats) {
33001
33038
  this.hls.trigger(Events.ERROR, {
@@ -33078,7 +33115,7 @@ class PlaylistLoader {
33078
33115
  const parent = mapContextToLevelType(context);
33079
33116
  const levelIndex = typeof context.level === 'number' && parent === PlaylistLevelType.MAIN ? level : undefined;
33080
33117
  if (!levelDetails.fragments.length) {
33081
- const _error = new Error('No Segments found in Playlist');
33118
+ const _error = levelDetails.playlistParsingError = new Error('No Segments found in Playlist');
33082
33119
  hls.trigger(Events.ERROR, {
33083
33120
  type: ErrorTypes.NETWORK_ERROR,
33084
33121
  details: ErrorDetails.LEVEL_EMPTY_ERROR,
@@ -33134,7 +33171,8 @@ class PlaylistLoader {
33134
33171
  id: id || 0,
33135
33172
  stats,
33136
33173
  networkDetails,
33137
- deliveryDirectives
33174
+ deliveryDirectives,
33175
+ withoutMultiVariant: type === PlaylistContextType.MANIFEST
33138
33176
  });
33139
33177
  break;
33140
33178
  case PlaylistContextType.AUDIO_TRACK:
@@ -33260,8 +33298,9 @@ class Hls {
33260
33298
  this.cmcdController = void 0;
33261
33299
  this._media = null;
33262
33300
  this._url = null;
33263
- this.triggeringException = void 0;
33264
33301
  this._sessionId = void 0;
33302
+ this.triggeringException = void 0;
33303
+ this.started = false;
33265
33304
  const logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance', userConfig.assetPlayerId);
33266
33305
  const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
33267
33306
  this.userConfig = userConfig;
@@ -33516,10 +33555,14 @@ class Hls {
33516
33555
  */
33517
33556
  startLoad(startPosition = -1, skipSeekToStartPosition) {
33518
33557
  this.logger.log(`startLoad(${startPosition + (skipSeekToStartPosition ? ', <skip seek to start>' : '')})`);
33558
+ this.started = true;
33519
33559
  this.resumeBuffering();
33520
- this.networkControllers.forEach(controller => {
33521
- controller.startLoad(startPosition, skipSeekToStartPosition);
33522
- });
33560
+ for (let i = 0; i < this.networkControllers.length; i++) {
33561
+ this.networkControllers[i].startLoad(startPosition, skipSeekToStartPosition);
33562
+ if (!this.started || !this.networkControllers) {
33563
+ break;
33564
+ }
33565
+ }
33523
33566
  }
33524
33567
 
33525
33568
  /**
@@ -33527,9 +33570,20 @@ class Hls {
33527
33570
  */
33528
33571
  stopLoad() {
33529
33572
  this.logger.log('stopLoad');
33530
- this.networkControllers.forEach(controller => {
33531
- controller.stopLoad();
33532
- });
33573
+ this.started = false;
33574
+ for (let i = 0; i < this.networkControllers.length; i++) {
33575
+ this.networkControllers[i].stopLoad();
33576
+ if (this.started || !this.networkControllers) {
33577
+ break;
33578
+ }
33579
+ }
33580
+ }
33581
+
33582
+ /**
33583
+ * Returns whether loading, toggled with `startLoad()` and `stopLoad()`, is active or not`.
33584
+ */
33585
+ get loadingEnabled() {
33586
+ return this.started;
33533
33587
  }
33534
33588
 
33535
33589
  /**