hls.js 1.4.6 → 1.4.8

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
@@ -402,7 +402,7 @@ function enableLogs(debugConfig, id) {
402
402
  // Some browsers don't allow to use bind on console object anyway
403
403
  // fallback to default if needed
404
404
  try {
405
- exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.4.6"}`);
405
+ exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.4.8"}`);
406
406
  } catch (e) {
407
407
  exportedLogger = fakeLogger;
408
408
  }
@@ -9320,10 +9320,8 @@ class BaseStreamController extends TaskLoop {
9320
9320
  retryConfig
9321
9321
  } = errorAction || {};
9322
9322
  if (errorAction && action === NetworkErrorAction.RetryRequest && retryConfig) {
9323
- if (!this.loadedmetadata) {
9324
- this.startFragRequested = false;
9325
- this.nextLoadPosition = this.startPosition;
9326
- }
9323
+ var _this$levelLastLoaded;
9324
+ this.resetStartWhenNotLoaded((_this$levelLastLoaded = this.levelLastLoaded) != null ? _this$levelLastLoaded : frag.level);
9327
9325
  const delay = getRetryDelay(retryConfig, retryCount);
9328
9326
  this.warn(`Fragment ${frag.sn} of ${filterType} ${frag.level} errored with ${data.details}, retrying loading ${retryCount + 1}/${retryConfig.maxNumRetry} in ${delay}ms`);
9329
9327
  errorAction.resolved = true;
@@ -9339,6 +9337,8 @@ class BaseStreamController extends TaskLoop {
9339
9337
  } else {
9340
9338
  logger.warn(`${data.details} reached or exceeded max retry (${retryCount})`);
9341
9339
  }
9340
+ } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
9341
+ this.state = State.WAITING_LEVEL;
9342
9342
  } else {
9343
9343
  this.state = State.ERROR;
9344
9344
  }
@@ -9421,9 +9421,10 @@ class BaseStreamController extends TaskLoop {
9421
9421
  }
9422
9422
  }
9423
9423
  resetWhenMissingContext(chunkMeta) {
9424
+ var _this$levelLastLoaded2;
9424
9425
  this.warn(`The loading context changed while buffering fragment ${chunkMeta.sn} of level ${chunkMeta.level}. This chunk will not be buffered.`);
9425
9426
  this.removeUnbufferedFrags();
9426
- this.resetStartWhenNotLoaded(chunkMeta.level);
9427
+ this.resetStartWhenNotLoaded((_this$levelLastLoaded2 = this.levelLastLoaded) != null ? _this$levelLastLoaded2 : chunkMeta.level);
9427
9428
  this.resetLoadingState();
9428
9429
  }
9429
9430
  removeUnbufferedFrags(start = 0) {
@@ -9495,7 +9496,10 @@ class BaseStreamController extends TaskLoop {
9495
9496
  }
9496
9497
  recoverWorkerError(data) {
9497
9498
  if (data.event === 'demuxerWorker') {
9499
+ var _ref, _this$levelLastLoaded3, _this$fragCurrent3;
9500
+ this.fragmentTracker.removeAllFragments();
9498
9501
  this.resetTransmuxer();
9502
+ this.resetStartWhenNotLoaded((_ref = (_this$levelLastLoaded3 = this.levelLastLoaded) != null ? _this$levelLastLoaded3 : (_this$fragCurrent3 = this.fragCurrent) == null ? void 0 : _this$fragCurrent3.level) != null ? _ref : 0);
9499
9503
  this.resetLoadingState();
9500
9504
  }
9501
9505
  }
@@ -15695,6 +15699,7 @@ class StreamController extends BaseStreamController {
15695
15699
  }
15696
15700
 
15697
15701
  // Avoid buffering if backtracking this fragment
15702
+ const notFirstFragment = frag.sn !== (details == null ? void 0 : details.startSN);
15698
15703
  if (video && remuxResult.independent !== false) {
15699
15704
  if (details) {
15700
15705
  const {
@@ -15720,7 +15725,7 @@ class StreamController extends BaseStreamController {
15720
15725
  const bufferInfo = this.getMainFwdBufferInfo();
15721
15726
  const targetBufferTime = (bufferInfo ? bufferInfo.end : this.getLoadPosition()) + this.config.maxBufferHole;
15722
15727
  const startTime = video.firstKeyFramePTS ? video.firstKeyFramePTS : startPTS;
15723
- if (targetBufferTime < startTime - this.config.maxBufferHole) {
15728
+ if (notFirstFragment && targetBufferTime < startTime - this.config.maxBufferHole) {
15724
15729
  this.backtrack(frag);
15725
15730
  return;
15726
15731
  }
@@ -15734,7 +15739,7 @@ class StreamController extends BaseStreamController {
15734
15739
  }
15735
15740
  this.bufferFragmentData(video, frag, part, chunkMeta);
15736
15741
  }
15737
- } else if (remuxResult.independent === false) {
15742
+ } else if (notFirstFragment && remuxResult.independent === false) {
15738
15743
  this.backtrack(frag);
15739
15744
  return;
15740
15745
  }
@@ -17605,16 +17610,7 @@ class SubtitleStreamController extends BaseStreamController {
17605
17610
  endOffset
17606
17611
  } = data;
17607
17612
  if (startOffset === 0 && endOffset !== Number.POSITIVE_INFINITY) {
17608
- const {
17609
- currentTrackId,
17610
- levels
17611
- } = this;
17612
- if (!levels.length || !levels[currentTrackId] || !levels[currentTrackId].details) {
17613
- return;
17614
- }
17615
- const trackDetails = levels[currentTrackId].details;
17616
- const targetDuration = trackDetails.targetduration;
17617
- const endOffsetSubtitles = endOffset - targetDuration;
17613
+ const endOffsetSubtitles = endOffset - 1;
17618
17614
  if (endOffsetSubtitles <= 0) {
17619
17615
  return;
17620
17616
  }
@@ -17810,21 +17806,18 @@ class SubtitleStreamController extends BaseStreamController {
17810
17806
  if (!levels.length || !track || !track.details) {
17811
17807
  return;
17812
17808
  }
17813
-
17814
- // Expand range of subs loaded by one target-duration in either direction to make up for misaligned playlists
17815
- const trackDetails = track.details;
17816
- const targetDuration = trackDetails.targetduration;
17817
17809
  const {
17818
17810
  config
17819
17811
  } = this;
17820
17812
  const currentTime = this.getLoadPosition();
17821
- const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime - targetDuration, config.maxBufferHole);
17813
+ const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime, config.maxBufferHole);
17822
17814
  const {
17823
17815
  end: targetBufferTime,
17824
17816
  len: bufferLen
17825
17817
  } = bufferedInfo;
17826
17818
  const mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN);
17827
- const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + targetDuration;
17819
+ const trackDetails = track.details;
17820
+ const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + trackDetails.levelTargetDuration;
17828
17821
  if (bufferLen > maxBufLen) {
17829
17822
  return;
17830
17823
  }
@@ -17834,10 +17827,9 @@ class SubtitleStreamController extends BaseStreamController {
17834
17827
  let foundFrag = null;
17835
17828
  const fragPrevious = this.fragPrevious;
17836
17829
  if (targetBufferTime < end) {
17837
- const {
17838
- maxFragLookUpTolerance
17839
- } = config;
17840
- foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), maxFragLookUpTolerance);
17830
+ const tolerance = config.maxFragLookUpTolerance;
17831
+ const lookupTolerance = targetBufferTime > end - tolerance ? 0 : tolerance;
17832
+ foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), lookupTolerance);
17841
17833
  if (!foundFrag && fragPrevious && fragPrevious.start < fragments[0].start) {
17842
17834
  foundFrag = fragments[0];
17843
17835
  }
@@ -17848,6 +17840,14 @@ class SubtitleStreamController extends BaseStreamController {
17848
17840
  return;
17849
17841
  }
17850
17842
  foundFrag = this.mapToInitFragWhenRequired(foundFrag);
17843
+ if (foundFrag.sn !== 'initSegment') {
17844
+ // Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment
17845
+ const curSNIdx = foundFrag.sn - trackDetails.startSN;
17846
+ const prevFrag = fragments[curSNIdx - 1];
17847
+ if (prevFrag && prevFrag.cc === foundFrag.cc && this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED) {
17848
+ foundFrag = prevFrag;
17849
+ }
17850
+ }
17851
17851
  if (this.fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED) {
17852
17852
  // only load if fragment is not loaded
17853
17853
  this.loadFragment(foundFrag, track, targetBufferTime);
@@ -21191,7 +21191,7 @@ function parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, er
21191
21191
  // Uint8Array.prototype.reduce is not implemented in IE11
21192
21192
  const vttLines = utf8ArrayToStr(new Uint8Array(vttByteArray)).trim().replace(LINEBREAKS, '\n').split('\n');
21193
21193
  const cues = [];
21194
- const init90kHz = toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale);
21194
+ const init90kHz = initPTS ? toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale) : 0;
21195
21195
  let cueTime = '00:00.000';
21196
21196
  let timestampMapMPEGTS = 0;
21197
21197
  let timestampMapLOCAL = 0;
@@ -21215,6 +21215,10 @@ function parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, er
21215
21215
  }
21216
21216
  }
21217
21217
  if (webVttMpegTsMapOffset) {
21218
+ if (!initPTS) {
21219
+ parsingError = new Error('Missing initPTS for VTT MPEGTS');
21220
+ return;
21221
+ }
21218
21222
  // If we have MPEGTS, offset = presentation time + discontinuity offset
21219
21223
  cueOffset = webVttMpegTsMapOffset - vttCCs.presentationOffset;
21220
21224
  }
@@ -21710,7 +21714,7 @@ class TimelineController {
21710
21714
  this.captionsTracks = {};
21711
21715
  this.nonNativeCaptionsTracks = {};
21712
21716
  this.textTracks = [];
21713
- this.unparsedVttFrags = this.unparsedVttFrags || [];
21717
+ this.unparsedVttFrags = [];
21714
21718
  this.initPTS = [];
21715
21719
  if (this.cea608Parser1 && this.cea608Parser2) {
21716
21720
  this.cea608Parser1.reset();
@@ -21854,26 +21858,9 @@ class TimelineController {
21854
21858
  frag,
21855
21859
  payload
21856
21860
  } = data;
21857
- const {
21858
- initPTS,
21859
- unparsedVttFrags
21860
- } = this;
21861
21861
  if (frag.type === PlaylistLevelType.SUBTITLE) {
21862
21862
  // If fragment is subtitle type, parse as WebVTT.
21863
21863
  if (payload.byteLength) {
21864
- // We need an initial synchronisation PTS. Store fragments as long as none has arrived.
21865
- if (!initPTS[frag.cc]) {
21866
- unparsedVttFrags.push(data);
21867
- if (initPTS.length) {
21868
- // finish unsuccessfully, otherwise the subtitle-stream-controller could be blocked from loading new frags.
21869
- this.hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21870
- success: false,
21871
- frag,
21872
- error: new Error('Missing initial subtitle PTS')
21873
- });
21874
- }
21875
- return;
21876
- }
21877
21864
  const decryptData = frag.decryptdata;
21878
21865
  // fragment after decryption has a stats object
21879
21866
  const decrypted = ('stats' in data);
@@ -21892,7 +21879,7 @@ class TimelineController {
21892
21879
  if (trackPlaylistMedia && trackPlaylistMedia.textCodec === IMSC1_CODEC) {
21893
21880
  this._parseIMSC1(frag, payload);
21894
21881
  } else {
21895
- this._parseVTTs(frag, payload, vttCCs);
21882
+ this._parseVTTs(data);
21896
21883
  }
21897
21884
  }
21898
21885
  } else {
@@ -21922,21 +21909,43 @@ class TimelineController {
21922
21909
  });
21923
21910
  });
21924
21911
  }
21925
- _parseVTTs(frag, payload, vttCCs) {
21912
+ _parseVTTs(data) {
21926
21913
  var _frag$initSegment;
21914
+ const {
21915
+ frag,
21916
+ payload
21917
+ } = data;
21918
+ // We need an initial synchronisation PTS. Store fragments as long as none has arrived
21919
+ const {
21920
+ initPTS,
21921
+ unparsedVttFrags
21922
+ } = this;
21923
+ const maxAvCC = initPTS.length - 1;
21924
+ if (!initPTS[frag.cc] && maxAvCC === -1) {
21925
+ unparsedVttFrags.push(data);
21926
+ return;
21927
+ }
21927
21928
  const hls = this.hls;
21928
21929
  // Parse the WebVTT file contents.
21929
21930
  const payloadWebVTT = (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload)) : payload;
21930
- parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], vttCCs, frag.cc, frag.start, cues => {
21931
+ parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], this.vttCCs, frag.cc, frag.start, cues => {
21931
21932
  this._appendCues(cues, frag.level);
21932
21933
  hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21933
21934
  success: true,
21934
21935
  frag: frag
21935
21936
  });
21936
21937
  }, error => {
21937
- this._fallbackToIMSC1(frag, payload);
21938
+ const missingInitPTS = error.message === 'Missing initPTS for VTT MPEGTS';
21939
+ if (missingInitPTS) {
21940
+ unparsedVttFrags.push(data);
21941
+ } else {
21942
+ this._fallbackToIMSC1(frag, payload);
21943
+ }
21938
21944
  // Something went wrong while parsing. Trigger event with success false.
21939
21945
  logger.log(`Failed to parse VTT cue: ${error}`);
21946
+ if (missingInitPTS && maxAvCC > frag.cc) {
21947
+ return;
21948
+ }
21940
21949
  hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21941
21950
  success: false,
21942
21951
  frag: frag,
@@ -21986,10 +21995,6 @@ class TimelineController {
21986
21995
  frag
21987
21996
  } = data;
21988
21997
  if (frag.type === PlaylistLevelType.SUBTITLE) {
21989
- if (!this.initPTS[frag.cc]) {
21990
- this.unparsedVttFrags.push(data);
21991
- return;
21992
- }
21993
21998
  this.onFragLoaded(Events.FRAG_LOADED, data);
21994
21999
  }
21995
22000
  }
@@ -24122,6 +24127,7 @@ class XhrLoader {
24122
24127
  const stats = this.stats;
24123
24128
  stats.loading.first = 0;
24124
24129
  stats.loaded = 0;
24130
+ stats.aborted = false;
24125
24131
  const xhrSetup = this.xhrSetup;
24126
24132
  if (xhrSetup) {
24127
24133
  Promise.resolve().then(() => {
@@ -24993,7 +24999,7 @@ class Hls {
24993
24999
  * Get the video-dev/hls.js package version.
24994
25000
  */
24995
25001
  static get version() {
24996
- return "1.4.6";
25002
+ return "1.4.8";
24997
25003
  }
24998
25004
 
24999
25005
  /**