hls.js 1.4.5 → 1.4.7

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.5"}`);
405
+ exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.4.7"}`);
406
406
  } catch (e) {
407
407
  exportedLogger = fakeLogger;
408
408
  }
@@ -5486,15 +5486,20 @@ class ErrorController {
5486
5486
  var _data$frag, _data$context2;
5487
5487
  // Search for next level to retry
5488
5488
  let nextLevel = -1;
5489
- const levels = hls.levels;
5489
+ const {
5490
+ levels,
5491
+ loadLevel,
5492
+ minAutoLevel,
5493
+ maxAutoLevel
5494
+ } = hls;
5490
5495
  const fragErrorType = (_data$frag = data.frag) == null ? void 0 : _data$frag.type;
5491
5496
  const {
5492
5497
  type: playlistErrorType,
5493
5498
  groupId: playlistErrorGroupId
5494
5499
  } = (_data$context2 = data.context) != null ? _data$context2 : {};
5495
5500
  for (let i = levels.length; i--;) {
5496
- const candidate = (i + hls.loadLevel) % levels.length;
5497
- if (candidate !== hls.loadLevel && levels[candidate].loadError === 0) {
5501
+ const candidate = (i + loadLevel) % levels.length;
5502
+ if (candidate !== loadLevel && candidate >= minAutoLevel && candidate <= maxAutoLevel && levels[candidate].loadError === 0) {
5498
5503
  const levelCandidate = levels[candidate];
5499
5504
  // Skip level switch if GAP tag is found in next level at same position
5500
5505
  if (data.details === ErrorDetails.FRAG_GAP && data.frag) {
@@ -9334,6 +9339,8 @@ class BaseStreamController extends TaskLoop {
9334
9339
  } else {
9335
9340
  logger.warn(`${data.details} reached or exceeded max retry (${retryCount})`);
9336
9341
  }
9342
+ } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
9343
+ this.state = State.WAITING_LEVEL;
9337
9344
  } else {
9338
9345
  this.state = State.ERROR;
9339
9346
  }
@@ -17600,16 +17607,7 @@ class SubtitleStreamController extends BaseStreamController {
17600
17607
  endOffset
17601
17608
  } = data;
17602
17609
  if (startOffset === 0 && endOffset !== Number.POSITIVE_INFINITY) {
17603
- const {
17604
- currentTrackId,
17605
- levels
17606
- } = this;
17607
- if (!levels.length || !levels[currentTrackId] || !levels[currentTrackId].details) {
17608
- return;
17609
- }
17610
- const trackDetails = levels[currentTrackId].details;
17611
- const targetDuration = trackDetails.targetduration;
17612
- const endOffsetSubtitles = endOffset - targetDuration;
17610
+ const endOffsetSubtitles = endOffset - 1;
17613
17611
  if (endOffsetSubtitles <= 0) {
17614
17612
  return;
17615
17613
  }
@@ -17805,21 +17803,18 @@ class SubtitleStreamController extends BaseStreamController {
17805
17803
  if (!levels.length || !track || !track.details) {
17806
17804
  return;
17807
17805
  }
17808
-
17809
- // Expand range of subs loaded by one target-duration in either direction to make up for misaligned playlists
17810
- const trackDetails = track.details;
17811
- const targetDuration = trackDetails.targetduration;
17812
17806
  const {
17813
17807
  config
17814
17808
  } = this;
17815
17809
  const currentTime = this.getLoadPosition();
17816
- const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime - targetDuration, config.maxBufferHole);
17810
+ const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime, config.maxBufferHole);
17817
17811
  const {
17818
17812
  end: targetBufferTime,
17819
17813
  len: bufferLen
17820
17814
  } = bufferedInfo;
17821
17815
  const mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN);
17822
- const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + targetDuration;
17816
+ const trackDetails = track.details;
17817
+ const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + trackDetails.levelTargetDuration;
17823
17818
  if (bufferLen > maxBufLen) {
17824
17819
  return;
17825
17820
  }
@@ -17829,10 +17824,9 @@ class SubtitleStreamController extends BaseStreamController {
17829
17824
  let foundFrag = null;
17830
17825
  const fragPrevious = this.fragPrevious;
17831
17826
  if (targetBufferTime < end) {
17832
- const {
17833
- maxFragLookUpTolerance
17834
- } = config;
17835
- foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), maxFragLookUpTolerance);
17827
+ const tolerance = config.maxFragLookUpTolerance;
17828
+ const lookupTolerance = targetBufferTime > end - tolerance ? 0 : tolerance;
17829
+ foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), lookupTolerance);
17836
17830
  if (!foundFrag && fragPrevious && fragPrevious.start < fragments[0].start) {
17837
17831
  foundFrag = fragments[0];
17838
17832
  }
@@ -17843,6 +17837,14 @@ class SubtitleStreamController extends BaseStreamController {
17843
17837
  return;
17844
17838
  }
17845
17839
  foundFrag = this.mapToInitFragWhenRequired(foundFrag);
17840
+ if (foundFrag.sn !== 'initSegment') {
17841
+ // Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment
17842
+ const curSNIdx = foundFrag.sn - trackDetails.startSN;
17843
+ const prevFrag = fragments[curSNIdx - 1];
17844
+ if (prevFrag && prevFrag.cc === foundFrag.cc && this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED) {
17845
+ foundFrag = prevFrag;
17846
+ }
17847
+ }
17846
17848
  if (this.fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED) {
17847
17849
  // only load if fragment is not loaded
17848
17850
  this.loadFragment(foundFrag, track, targetBufferTime);
@@ -21186,7 +21188,7 @@ function parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, er
21186
21188
  // Uint8Array.prototype.reduce is not implemented in IE11
21187
21189
  const vttLines = utf8ArrayToStr(new Uint8Array(vttByteArray)).trim().replace(LINEBREAKS, '\n').split('\n');
21188
21190
  const cues = [];
21189
- const init90kHz = toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale);
21191
+ const init90kHz = initPTS ? toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale) : 0;
21190
21192
  let cueTime = '00:00.000';
21191
21193
  let timestampMapMPEGTS = 0;
21192
21194
  let timestampMapLOCAL = 0;
@@ -21210,6 +21212,10 @@ function parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, er
21210
21212
  }
21211
21213
  }
21212
21214
  if (webVttMpegTsMapOffset) {
21215
+ if (!initPTS) {
21216
+ parsingError = new Error('Missing initPTS for VTT MPEGTS');
21217
+ return;
21218
+ }
21213
21219
  // If we have MPEGTS, offset = presentation time + discontinuity offset
21214
21220
  cueOffset = webVttMpegTsMapOffset - vttCCs.presentationOffset;
21215
21221
  }
@@ -21705,7 +21711,7 @@ class TimelineController {
21705
21711
  this.captionsTracks = {};
21706
21712
  this.nonNativeCaptionsTracks = {};
21707
21713
  this.textTracks = [];
21708
- this.unparsedVttFrags = this.unparsedVttFrags || [];
21714
+ this.unparsedVttFrags = [];
21709
21715
  this.initPTS = [];
21710
21716
  if (this.cea608Parser1 && this.cea608Parser2) {
21711
21717
  this.cea608Parser1.reset();
@@ -21849,26 +21855,9 @@ class TimelineController {
21849
21855
  frag,
21850
21856
  payload
21851
21857
  } = data;
21852
- const {
21853
- initPTS,
21854
- unparsedVttFrags
21855
- } = this;
21856
21858
  if (frag.type === PlaylistLevelType.SUBTITLE) {
21857
21859
  // If fragment is subtitle type, parse as WebVTT.
21858
21860
  if (payload.byteLength) {
21859
- // We need an initial synchronisation PTS. Store fragments as long as none has arrived.
21860
- if (!initPTS[frag.cc]) {
21861
- unparsedVttFrags.push(data);
21862
- if (initPTS.length) {
21863
- // finish unsuccessfully, otherwise the subtitle-stream-controller could be blocked from loading new frags.
21864
- this.hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21865
- success: false,
21866
- frag,
21867
- error: new Error('Missing initial subtitle PTS')
21868
- });
21869
- }
21870
- return;
21871
- }
21872
21861
  const decryptData = frag.decryptdata;
21873
21862
  // fragment after decryption has a stats object
21874
21863
  const decrypted = ('stats' in data);
@@ -21887,7 +21876,7 @@ class TimelineController {
21887
21876
  if (trackPlaylistMedia && trackPlaylistMedia.textCodec === IMSC1_CODEC) {
21888
21877
  this._parseIMSC1(frag, payload);
21889
21878
  } else {
21890
- this._parseVTTs(frag, payload, vttCCs);
21879
+ this._parseVTTs(data);
21891
21880
  }
21892
21881
  }
21893
21882
  } else {
@@ -21917,21 +21906,43 @@ class TimelineController {
21917
21906
  });
21918
21907
  });
21919
21908
  }
21920
- _parseVTTs(frag, payload, vttCCs) {
21909
+ _parseVTTs(data) {
21921
21910
  var _frag$initSegment;
21911
+ const {
21912
+ frag,
21913
+ payload
21914
+ } = data;
21915
+ // We need an initial synchronisation PTS. Store fragments as long as none has arrived
21916
+ const {
21917
+ initPTS,
21918
+ unparsedVttFrags
21919
+ } = this;
21920
+ const maxAvCC = initPTS.length - 1;
21921
+ if (!initPTS[frag.cc] && maxAvCC === -1) {
21922
+ unparsedVttFrags.push(data);
21923
+ return;
21924
+ }
21922
21925
  const hls = this.hls;
21923
21926
  // Parse the WebVTT file contents.
21924
21927
  const payloadWebVTT = (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload)) : payload;
21925
- parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], vttCCs, frag.cc, frag.start, cues => {
21928
+ parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], this.vttCCs, frag.cc, frag.start, cues => {
21926
21929
  this._appendCues(cues, frag.level);
21927
21930
  hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21928
21931
  success: true,
21929
21932
  frag: frag
21930
21933
  });
21931
21934
  }, error => {
21932
- this._fallbackToIMSC1(frag, payload);
21935
+ const missingInitPTS = error.message === 'Missing initPTS for VTT MPEGTS';
21936
+ if (missingInitPTS) {
21937
+ unparsedVttFrags.push(data);
21938
+ } else {
21939
+ this._fallbackToIMSC1(frag, payload);
21940
+ }
21933
21941
  // Something went wrong while parsing. Trigger event with success false.
21934
21942
  logger.log(`Failed to parse VTT cue: ${error}`);
21943
+ if (missingInitPTS && maxAvCC > frag.cc) {
21944
+ return;
21945
+ }
21935
21946
  hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21936
21947
  success: false,
21937
21948
  frag: frag,
@@ -21981,10 +21992,6 @@ class TimelineController {
21981
21992
  frag
21982
21993
  } = data;
21983
21994
  if (frag.type === PlaylistLevelType.SUBTITLE) {
21984
- if (!this.initPTS[frag.cc]) {
21985
- this.unparsedVttFrags.push(data);
21986
- return;
21987
- }
21988
21995
  this.onFragLoaded(Events.FRAG_LOADED, data);
21989
21996
  }
21990
21997
  }
@@ -24117,6 +24124,7 @@ class XhrLoader {
24117
24124
  const stats = this.stats;
24118
24125
  stats.loading.first = 0;
24119
24126
  stats.loaded = 0;
24127
+ stats.aborted = false;
24120
24128
  const xhrSetup = this.xhrSetup;
24121
24129
  if (xhrSetup) {
24122
24130
  Promise.resolve().then(() => {
@@ -24988,7 +24996,7 @@ class Hls {
24988
24996
  * Get the video-dev/hls.js package version.
24989
24997
  */
24990
24998
  static get version() {
24991
- return "1.4.5";
24999
+ return "1.4.7";
24992
25000
  }
24993
25001
 
24994
25002
  /**