hls.js 1.6.0-rc.1.0.canary.11071 → 1.6.0-rc.1.0.canary.11074

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.
@@ -402,7 +402,7 @@ function enableLogs(debugConfig, context, 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
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-rc.1.0.canary.11071"}`);
405
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-rc.1.0.canary.11074"}`);
406
406
  } catch (e) {
407
407
  /* log fn threw an exception. All logger methods are no-ops. */
408
408
  return createLogger();
@@ -4642,7 +4642,7 @@ const LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*
4642
4642
  // segment URI, group 3 => the URI (note newline is not eaten)
4643
4643
  /#.*/.source // All other non-segment oriented tags will match with all groups empty
4644
4644
  ].join('|'), 'g');
4645
- const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#(EXTM3U)/.source, /#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\r?\n?/.source].join('|'));
4645
+ const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\r?\n?/.source].join('|'));
4646
4646
  class M3U8Parser {
4647
4647
  static findGroup(groups, mediaGroupId) {
4648
4648
  for (let i = 0; i < groups.length; i++) {
@@ -4832,6 +4832,7 @@ class M3U8Parser {
4832
4832
  return results;
4833
4833
  }
4834
4834
  static parseLevelPlaylist(string, baseurl, id, type, levelUrlId, multivariantVariableList) {
4835
+ var _LEVEL_PLAYLIST_REGEX;
4835
4836
  const base = {
4836
4837
  url: baseurl
4837
4838
  };
@@ -4853,9 +4854,14 @@ class M3U8Parser {
4853
4854
  let firstPdtIndex = -1;
4854
4855
  let createNextFrag = false;
4855
4856
  let nextByteRange = null;
4857
+ let serverControlAttrs;
4856
4858
  LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0;
4857
4859
  level.m3u8 = string;
4858
4860
  level.hasVariableRefs = false;
4861
+ if (((_LEVEL_PLAYLIST_REGEX = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) == null ? void 0 : _LEVEL_PLAYLIST_REGEX[0]) !== '#EXTM3U') {
4862
+ level.playlistParsingError = new Error('Missing format identifier #EXTM3U');
4863
+ return level;
4864
+ }
4859
4865
  while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) {
4860
4866
  if (createNextFrag) {
4861
4867
  createNextFrag = false;
@@ -4944,15 +4950,23 @@ class M3U8Parser {
4944
4950
  }
4945
4951
  break;
4946
4952
  case 'PLAYLIST-TYPE':
4953
+ if (level.type) {
4954
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
4955
+ }
4947
4956
  level.type = value1.toUpperCase();
4948
4957
  break;
4949
4958
  case 'MEDIA-SEQUENCE':
4959
+ if (level.startSN !== 0) {
4960
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
4961
+ } else if (fragments.length > 0) {
4962
+ assignMustAppearBeforeSegmentsError(level, tag, result);
4963
+ }
4950
4964
  currentSN = level.startSN = parseInt(value1);
4951
4965
  break;
4952
4966
  case 'SKIP':
4953
4967
  {
4954
4968
  if (level.skippedSegments) {
4955
- level.playlistParsingError = new Error(`#EXT-X-SKIP MUST NOT appear more than once in a Playlist`);
4969
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
4956
4970
  }
4957
4971
  const skipAttrs = new AttrList(value1, level);
4958
4972
  const skippedSegments = skipAttrs.decimalInteger('SKIPPED-SEGMENTS');
@@ -4971,15 +4985,23 @@ class M3U8Parser {
4971
4985
  break;
4972
4986
  }
4973
4987
  case 'TARGETDURATION':
4988
+ if (level.targetduration !== 0) {
4989
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
4990
+ }
4974
4991
  level.targetduration = Math.max(parseInt(value1), 1);
4975
4992
  break;
4976
4993
  case 'VERSION':
4994
+ if (level.version !== null) {
4995
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
4996
+ }
4977
4997
  level.version = parseInt(value1);
4978
4998
  break;
4979
4999
  case 'INDEPENDENT-SEGMENTS':
4980
- case 'EXTM3U':
4981
5000
  break;
4982
5001
  case 'ENDLIST':
5002
+ if (!level.live) {
5003
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
5004
+ }
4983
5005
  level.live = false;
4984
5006
  break;
4985
5007
  case '#':
@@ -5023,6 +5045,11 @@ class M3U8Parser {
5023
5045
  break;
5024
5046
  }
5025
5047
  case 'DISCONTINUITY-SEQUENCE':
5048
+ if (level.startCC !== 0) {
5049
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
5050
+ } else if (fragments.length > 0) {
5051
+ assignMustAppearBeforeSegmentsError(level, tag, result);
5052
+ }
5026
5053
  level.startCC = discontinuityCounter = parseInt(value1);
5027
5054
  break;
5028
5055
  case 'KEY':
@@ -5081,7 +5108,10 @@ class M3U8Parser {
5081
5108
  }
5082
5109
  case 'SERVER-CONTROL':
5083
5110
  {
5084
- const serverControlAttrs = new AttrList(value1);
5111
+ if (serverControlAttrs) {
5112
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
5113
+ }
5114
+ serverControlAttrs = new AttrList(value1);
5085
5115
  level.canBlockReload = serverControlAttrs.bool('CAN-BLOCK-RELOAD');
5086
5116
  level.canSkipUntil = serverControlAttrs.optionalFloat('CAN-SKIP-UNTIL', 0);
5087
5117
  level.canSkipDateRanges = level.canSkipUntil > 0 && serverControlAttrs.bool('CAN-SKIP-DATERANGES');
@@ -5091,6 +5121,9 @@ class M3U8Parser {
5091
5121
  }
5092
5122
  case 'PART-INF':
5093
5123
  {
5124
+ if (level.partTarget) {
5125
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
5126
+ }
5094
5127
  const partInfAttrs = new AttrList(value1);
5095
5128
  level.partTarget = partInfAttrs.decimalFloatingPoint('PART-TARGET');
5096
5129
  break;
@@ -5142,6 +5175,9 @@ class M3U8Parser {
5142
5175
  setFragLevelKeys(frag, levelkeys, level);
5143
5176
  }
5144
5177
  }
5178
+ if (!level.targetduration) {
5179
+ level.playlistParsingError = new Error(`#EXT-X-TARGETDURATION is required`);
5180
+ }
5145
5181
  const fragmentLength = fragments.length;
5146
5182
  const firstFragment = fragments[0];
5147
5183
  const lastFragment = fragments[fragmentLength - 1];
@@ -5319,6 +5355,12 @@ function setFragLevelKeys(frag, levelkeys, level) {
5319
5355
  encryptedFragments.push(frag);
5320
5356
  }
5321
5357
  }
5358
+ function assignMultipleMediaPlaylistTagOccuranceError(level, tag, result) {
5359
+ level.playlistParsingError = new Error(`#EXT-X-${tag} must not appear more than once (${result[0]})`);
5360
+ }
5361
+ function assignMustAppearBeforeSegmentsError(level, tag, result) {
5362
+ level.playlistParsingError = new Error(`#EXT-X-${tag} must appear before the first Media Segment (${result[0]})`);
5363
+ }
5322
5364
 
5323
5365
  function updateFromToPTS(fragFrom, fragTo) {
5324
5366
  const fragToPTS = fragTo.startPTS;
@@ -5606,9 +5648,24 @@ function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) {
5606
5648
  }
5607
5649
  if (oldFrag && newFrag) {
5608
5650
  intersectionFn(oldFrag, newFrag, i, newFrags);
5651
+ if (oldFrag.url && oldFrag.url !== newFrag.url) {
5652
+ newDetails.playlistParsingError = getSequenceError(`media sequence mismatch ${newFrag.sn}:`, oldDetails, newDetails, oldFrag, newFrag);
5653
+ return;
5654
+ } else if (oldFrag.cc !== newFrag.cc) {
5655
+ newDetails.playlistParsingError = getSequenceError(`discontinuity sequence mismatch (${oldFrag.cc}!=${newFrag.cc})`, oldDetails, newDetails, oldFrag, newFrag);
5656
+ return;
5657
+ }
5609
5658
  }
5610
5659
  }
5611
5660
  }
5661
+ function getSequenceError(message, oldDetails, newDetails, oldFrag, newFrag) {
5662
+ return new Error(`${message} ${newFrag.url}
5663
+ Playlist starting @${oldDetails.startSN}
5664
+ ${oldDetails.m3u8}
5665
+
5666
+ Playlist starting @${newDetails.startSN}
5667
+ ${newDetails.m3u8}`);
5668
+ }
5612
5669
  function adjustSliding(oldDetails, newDetails, matchingStableVariantOrRendition = true) {
5613
5670
  const delta = newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN;
5614
5671
  const oldFragments = oldDetails.fragments;
@@ -5821,6 +5878,31 @@ class BasePlaylistController extends Logger {
5821
5878
  // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments
5822
5879
  if (previousDetails && details.fragments.length > 0) {
5823
5880
  mergeDetails(previousDetails, details);
5881
+ const error = details.playlistParsingError;
5882
+ if (error) {
5883
+ this.warn(error);
5884
+ const hls = this.hls;
5885
+ if (!hls.config.ignorePlaylistParsingErrors) {
5886
+ var _details$fragments$;
5887
+ const {
5888
+ networkDetails
5889
+ } = data;
5890
+ hls.trigger(Events.ERROR, {
5891
+ type: ErrorTypes.NETWORK_ERROR,
5892
+ details: ErrorDetails.LEVEL_PARSING_ERROR,
5893
+ fatal: false,
5894
+ url: details.url,
5895
+ error,
5896
+ reason: error.message,
5897
+ level: data.level || undefined,
5898
+ parent: (_details$fragments$ = details.fragments[0]) == null ? void 0 : _details$fragments$.type,
5899
+ networkDetails,
5900
+ stats
5901
+ });
5902
+ return;
5903
+ }
5904
+ details.playlistParsingError = null;
5905
+ }
5824
5906
  }
5825
5907
  if (details.requestScheduled === -1) {
5826
5908
  details.requestScheduled = stats.loading.start;
@@ -17380,6 +17462,7 @@ const hlsDefaultConfig = _objectSpread2(_objectSpread2({
17380
17462
  // used by fps-controller
17381
17463
  appendErrorMaxRetry: 3,
17382
17464
  // used by buffer-controller
17465
+ ignorePlaylistParsingErrors: false,
17383
17466
  loader: XhrLoader,
17384
17467
  // loader: FetchLoader,
17385
17468
  fLoader: undefined,
@@ -19524,7 +19607,7 @@ function assignTrackIdsByGroup(tracks) {
19524
19607
  });
19525
19608
  }
19526
19609
 
19527
- const version = "1.6.0-rc.1.0.canary.11071";
19610
+ const version = "1.6.0-rc.1.0.canary.11074";
19528
19611
 
19529
19612
  // ensure the worker ends up in the bundle
19530
19613
  // If the worker should not be included this gets aliased to empty.js
@@ -21944,21 +22027,25 @@ class PlaylistLoader {
21944
22027
  }
21945
22028
  const error = levelDetails.playlistParsingError;
21946
22029
  if (error) {
21947
- hls.trigger(Events.ERROR, {
21948
- type: ErrorTypes.NETWORK_ERROR,
21949
- details: ErrorDetails.LEVEL_PARSING_ERROR,
21950
- fatal: false,
21951
- url,
21952
- error,
21953
- reason: error.message,
21954
- response,
21955
- context,
21956
- level: levelIndex,
21957
- parent,
21958
- networkDetails,
21959
- stats
21960
- });
21961
- return;
22030
+ this.hls.logger.warn(error);
22031
+ if (!hls.config.ignorePlaylistParsingErrors) {
22032
+ hls.trigger(Events.ERROR, {
22033
+ type: ErrorTypes.NETWORK_ERROR,
22034
+ details: ErrorDetails.LEVEL_PARSING_ERROR,
22035
+ fatal: false,
22036
+ url,
22037
+ error,
22038
+ reason: error.message,
22039
+ response,
22040
+ context,
22041
+ level: levelIndex,
22042
+ parent,
22043
+ networkDetails,
22044
+ stats
22045
+ });
22046
+ return;
22047
+ }
22048
+ levelDetails.playlistParsingError = null;
21962
22049
  }
21963
22050
  if (levelDetails.live && loader) {
21964
22051
  if (loader.getCacheAge) {