hls.js 1.5.14-0.canary.10517 → 1.5.14

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.
Files changed (107) hide show
  1. package/README.md +3 -4
  2. package/dist/hls-demo.js +38 -41
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +2903 -4542
  5. package/dist/hls.js.d.ts +112 -186
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +2284 -3295
  8. package/dist/hls.light.js.map +1 -1
  9. package/dist/hls.light.min.js +1 -1
  10. package/dist/hls.light.min.js.map +1 -1
  11. package/dist/hls.light.mjs +1804 -2817
  12. package/dist/hls.light.mjs.map +1 -1
  13. package/dist/hls.min.js +1 -1
  14. package/dist/hls.min.js.map +1 -1
  15. package/dist/hls.mjs +4652 -6293
  16. package/dist/hls.mjs.map +1 -1
  17. package/dist/hls.worker.js +1 -1
  18. package/dist/hls.worker.js.map +1 -1
  19. package/package.json +38 -38
  20. package/src/config.ts +2 -5
  21. package/src/controller/abr-controller.ts +25 -39
  22. package/src/controller/audio-stream-controller.ts +136 -156
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +10 -27
  25. package/src/controller/base-stream-controller.ts +107 -263
  26. package/src/controller/buffer-controller.ts +98 -252
  27. package/src/controller/buffer-operation-queue.ts +19 -16
  28. package/src/controller/cap-level-controller.ts +2 -3
  29. package/src/controller/cmcd-controller.ts +14 -51
  30. package/src/controller/content-steering-controller.ts +15 -29
  31. package/src/controller/eme-controller.ts +23 -10
  32. package/src/controller/error-controller.ts +22 -28
  33. package/src/controller/fps-controller.ts +3 -8
  34. package/src/controller/fragment-finders.ts +16 -44
  35. package/src/controller/fragment-tracker.ts +25 -58
  36. package/src/controller/gap-controller.ts +16 -43
  37. package/src/controller/id3-track-controller.ts +35 -45
  38. package/src/controller/latency-controller.ts +13 -18
  39. package/src/controller/level-controller.ts +19 -37
  40. package/src/controller/stream-controller.ts +83 -100
  41. package/src/controller/subtitle-stream-controller.ts +47 -35
  42. package/src/controller/subtitle-track-controller.ts +3 -5
  43. package/src/controller/timeline-controller.ts +22 -20
  44. package/src/crypt/aes-crypto.ts +2 -21
  45. package/src/crypt/decrypter.ts +16 -32
  46. package/src/crypt/fast-aes-key.ts +5 -28
  47. package/src/demux/audio/aacdemuxer.ts +5 -5
  48. package/src/demux/audio/ac3-demuxer.ts +4 -5
  49. package/src/demux/audio/adts.ts +4 -9
  50. package/src/demux/audio/base-audio-demuxer.ts +14 -16
  51. package/src/demux/audio/mp3demuxer.ts +3 -4
  52. package/src/demux/audio/mpegaudio.ts +1 -1
  53. package/src/demux/id3.ts +411 -0
  54. package/src/demux/inject-worker.ts +4 -38
  55. package/src/demux/mp4demuxer.ts +7 -7
  56. package/src/demux/sample-aes.ts +0 -2
  57. package/src/demux/transmuxer-interface.ts +83 -106
  58. package/src/demux/transmuxer-worker.ts +77 -111
  59. package/src/demux/transmuxer.ts +22 -46
  60. package/src/demux/tsdemuxer.ts +62 -122
  61. package/src/demux/video/avc-video-parser.ts +121 -210
  62. package/src/demux/video/base-video-parser.ts +2 -135
  63. package/src/demux/video/exp-golomb.ts +208 -0
  64. package/src/errors.ts +0 -2
  65. package/src/events.ts +1 -8
  66. package/src/exports-named.ts +1 -1
  67. package/src/hls.ts +48 -97
  68. package/src/loader/date-range.ts +5 -71
  69. package/src/loader/fragment-loader.ts +21 -23
  70. package/src/loader/fragment.ts +4 -8
  71. package/src/loader/key-loader.ts +1 -3
  72. package/src/loader/level-details.ts +6 -6
  73. package/src/loader/level-key.ts +9 -10
  74. package/src/loader/m3u8-parser.ts +144 -138
  75. package/src/loader/playlist-loader.ts +7 -5
  76. package/src/remux/mp4-generator.ts +1 -196
  77. package/src/remux/mp4-remuxer.ts +84 -55
  78. package/src/remux/passthrough-remuxer.ts +8 -23
  79. package/src/task-loop.ts +2 -5
  80. package/src/types/component-api.ts +1 -3
  81. package/src/types/demuxer.ts +0 -3
  82. package/src/types/events.ts +6 -19
  83. package/src/types/fragment-tracker.ts +2 -2
  84. package/src/types/general.ts +6 -0
  85. package/src/types/media-playlist.ts +1 -9
  86. package/src/types/remuxer.ts +1 -1
  87. package/src/utils/attr-list.ts +9 -96
  88. package/src/utils/buffer-helper.ts +31 -12
  89. package/src/utils/cea-608-parser.ts +3 -1
  90. package/src/utils/codecs.ts +5 -34
  91. package/src/utils/discontinuities.ts +47 -21
  92. package/src/utils/fetch-loader.ts +1 -1
  93. package/src/utils/hdr.ts +7 -4
  94. package/src/utils/imsc1-ttml-parser.ts +1 -1
  95. package/src/utils/keysystem-util.ts +6 -1
  96. package/src/utils/level-helper.ts +44 -71
  97. package/src/utils/logger.ts +23 -58
  98. package/src/utils/mp4-tools.ts +3 -5
  99. package/src/utils/rendition-helper.ts +74 -100
  100. package/src/utils/variable-substitution.ts +19 -0
  101. package/src/utils/webvtt-parser.ts +12 -2
  102. package/src/crypt/decrypter-aes-mode.ts +0 -4
  103. package/src/demux/video/hevc-video-parser.ts +0 -749
  104. package/src/utils/encryption-methods-util.ts +0 -21
  105. package/src/utils/hash.ts +0 -10
  106. package/src/utils/utf8-utils.ts +0 -18
  107. package/src/version.ts +0 -1
@@ -1,6 +1,6 @@
1
1
  import { buildAbsoluteURL } from 'url-toolkit';
2
2
  import { DateRange } from './date-range';
3
- import { Fragment, MediaFragment, Part } from './fragment';
3
+ import { Fragment, Part } from './fragment';
4
4
  import { LevelDetails } from './level-details';
5
5
  import { LevelKey } from './level-key';
6
6
  import { AttrList } from '../utils/attr-list';
@@ -10,6 +10,7 @@ import {
10
10
  hasVariableReferences,
11
11
  importVariableDefinition,
12
12
  substituteVariables,
13
+ substituteVariablesInAttributes,
13
14
  } from '../utils/variable-substitution';
14
15
  import { isCodecType } from '../utils/codecs';
15
16
  import type { CodecType } from '../utils/codecs';
@@ -119,7 +120,21 @@ export default class M3U8Parser {
119
120
  while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) {
120
121
  if (result[1]) {
121
122
  // '#EXT-X-STREAM-INF' is found, parse level tag in group 1
122
- const attrs = new AttrList(result[1], parsed) as LevelAttributes;
123
+ const attrs = new AttrList(result[1]) as LevelAttributes;
124
+ if (__USE_VARIABLE_SUBSTITUTION__) {
125
+ substituteVariablesInAttributes(parsed, attrs, [
126
+ 'CODECS',
127
+ 'SUPPLEMENTAL-CODECS',
128
+ 'ALLOWED-CPC',
129
+ 'PATHWAY-ID',
130
+ 'STABLE-VARIANT-ID',
131
+ 'AUDIO',
132
+ 'VIDEO',
133
+ 'SUBTITLES',
134
+ 'CLOSED-CAPTIONS',
135
+ 'NAME',
136
+ ]);
137
+ }
123
138
  const uri = __USE_VARIABLE_SUBSTITUTION__
124
139
  ? substituteVariables(parsed, result[2])
125
140
  : result[2];
@@ -151,7 +166,15 @@ export default class M3U8Parser {
151
166
  switch (tag) {
152
167
  case 'SESSION-DATA': {
153
168
  // #EXT-X-SESSION-DATA
154
- const sessionAttrs = new AttrList(attributes, parsed);
169
+ const sessionAttrs = new AttrList(attributes);
170
+ if (__USE_VARIABLE_SUBSTITUTION__) {
171
+ substituteVariablesInAttributes(parsed, sessionAttrs, [
172
+ 'DATA-ID',
173
+ 'LANGUAGE',
174
+ 'VALUE',
175
+ 'URI',
176
+ ]);
177
+ }
155
178
  const dataId = sessionAttrs['DATA-ID'];
156
179
  if (dataId) {
157
180
  if (parsed.sessionData === null) {
@@ -179,14 +202,26 @@ export default class M3U8Parser {
179
202
  case 'DEFINE': {
180
203
  // #EXT-X-DEFINE
181
204
  if (__USE_VARIABLE_SUBSTITUTION__) {
182
- const variableAttributes = new AttrList(attributes, parsed);
205
+ const variableAttributes = new AttrList(attributes);
206
+ substituteVariablesInAttributes(parsed, variableAttributes, [
207
+ 'NAME',
208
+ 'VALUE',
209
+ 'QUERYPARAM',
210
+ ]);
183
211
  addVariableDefinition(parsed, variableAttributes, baseurl);
184
212
  }
185
213
  break;
186
214
  }
187
215
  case 'CONTENT-STEERING': {
188
216
  // #EXT-X-CONTENT-STEERING
189
- const contentSteeringAttributes = new AttrList(attributes, parsed);
217
+ const contentSteeringAttributes = new AttrList(attributes);
218
+ if (__USE_VARIABLE_SUBSTITUTION__) {
219
+ substituteVariablesInAttributes(
220
+ parsed,
221
+ contentSteeringAttributes,
222
+ ['SERVER-URI', 'PATHWAY-ID'],
223
+ );
224
+ }
190
225
  parsed.contentSteering = {
191
226
  uri: M3U8Parser.resolve(
192
227
  contentSteeringAttributes['SERVER-URI'],
@@ -243,13 +278,26 @@ export default class M3U8Parser {
243
278
  let id = 0;
244
279
  MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0;
245
280
  while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) {
246
- const attrs = new AttrList(result[1], parsed) as MediaAttributes;
281
+ const attrs = new AttrList(result[1]) as MediaAttributes;
247
282
  const type = attrs.TYPE;
248
283
  if (type) {
249
284
  const groups: (typeof groupsByType)[keyof typeof groupsByType] =
250
285
  groupsByType[type];
251
286
  const medias: MediaPlaylist[] = results[type] || [];
252
287
  results[type] = medias;
288
+ if (__USE_VARIABLE_SUBSTITUTION__) {
289
+ substituteVariablesInAttributes(parsed, attrs, [
290
+ 'URI',
291
+ 'GROUP-ID',
292
+ 'LANGUAGE',
293
+ 'ASSOC-LANGUAGE',
294
+ 'STABLE-RENDITION-ID',
295
+ 'NAME',
296
+ 'INSTREAM-ID',
297
+ 'CHARACTERISTICS',
298
+ 'CHANNELS',
299
+ ]);
300
+ }
253
301
  const lang = attrs.LANGUAGE;
254
302
  const assocLang = attrs['ASSOC-LANGUAGE'];
255
303
  const channels = attrs.CHANNELS;
@@ -307,7 +355,6 @@ export default class M3U8Parser {
307
355
  ): LevelDetails {
308
356
  const level = new LevelDetails(baseurl);
309
357
  const fragments: M3U8ParserFragments = level.fragments;
310
- const programDateTimes: MediaFragment[] = [];
311
358
  // The most recent init segment seen (applies to all subsequent segments)
312
359
  let currentInitSegment: Fragment | null = null;
313
360
  let currentSN = 0;
@@ -373,11 +420,7 @@ export default class M3U8Parser {
373
420
  frag.relurl = __USE_VARIABLE_SUBSTITUTION__
374
421
  ? substituteVariables(level, uri)
375
422
  : uri;
376
- assignProgramDateTime(
377
- frag as MediaFragment,
378
- prevFrag as MediaFragment,
379
- programDateTimes,
380
- );
423
+ assignProgramDateTime(frag, prevFrag);
381
424
  prevFrag = frag;
382
425
  totalduration += frag.duration;
383
426
  currentSN++;
@@ -425,19 +468,19 @@ export default class M3U8Parser {
425
468
  currentSN = level.startSN = parseInt(value1);
426
469
  break;
427
470
  case 'SKIP': {
428
- if (level.skippedSegments) {
429
- level.playlistParsingError = new Error(
430
- `#EXT-X-SKIP MUST NOT appear more than once in a Playlist`,
431
- );
471
+ const skipAttrs = new AttrList(value1);
472
+ if (__USE_VARIABLE_SUBSTITUTION__) {
473
+ substituteVariablesInAttributes(level, skipAttrs, [
474
+ 'RECENTLY-REMOVED-DATERANGES',
475
+ ]);
432
476
  }
433
- const skipAttrs = new AttrList(value1, level);
434
477
  const skippedSegments =
435
478
  skipAttrs.decimalInteger('SKIPPED-SEGMENTS');
436
479
  if (Number.isFinite(skippedSegments)) {
437
- level.skippedSegments += skippedSegments;
480
+ level.skippedSegments = skippedSegments;
438
481
  // This will result in fragments[] containing undefined values, which we will fill in with `mergeDetails`
439
482
  for (let i = skippedSegments; i--; ) {
440
- fragments.push(null);
483
+ fragments.unshift(null);
441
484
  }
442
485
  currentSN += skippedSegments;
443
486
  }
@@ -445,9 +488,8 @@ export default class M3U8Parser {
445
488
  'RECENTLY-REMOVED-DATERANGES',
446
489
  );
447
490
  if (recentlyRemovedDateranges) {
448
- level.recentlyRemovedDateranges = (
449
- level.recentlyRemovedDateranges || []
450
- ).concat(recentlyRemovedDateranges.split('\t'));
491
+ level.recentlyRemovedDateranges =
492
+ recentlyRemovedDateranges.split('\t');
451
493
  }
452
494
  break;
453
495
  }
@@ -480,13 +522,27 @@ export default class M3U8Parser {
480
522
  frag.tagList.push([tag, value1]);
481
523
  break;
482
524
  case 'DATERANGE': {
483
- const dateRangeAttr = new AttrList(value1, level);
525
+ const dateRangeAttr = new AttrList(value1);
526
+ if (__USE_VARIABLE_SUBSTITUTION__) {
527
+ substituteVariablesInAttributes(level, dateRangeAttr, [
528
+ 'ID',
529
+ 'CLASS',
530
+ 'START-DATE',
531
+ 'END-DATE',
532
+ 'SCTE35-CMD',
533
+ 'SCTE35-OUT',
534
+ 'SCTE35-IN',
535
+ ]);
536
+ substituteVariablesInAttributes(
537
+ level,
538
+ dateRangeAttr,
539
+ dateRangeAttr.clientAttrs,
540
+ );
541
+ }
484
542
  const dateRange = new DateRange(
485
543
  dateRangeAttr,
486
544
  level.dateRanges[dateRangeAttr.ID],
487
- level.dateRangeTagCount,
488
545
  );
489
- level.dateRangeTagCount++;
490
546
  if (dateRange.isValid || level.skippedSegments) {
491
547
  level.dateRanges[dateRange.id] = dateRange;
492
548
  } else {
@@ -498,7 +554,13 @@ export default class M3U8Parser {
498
554
  }
499
555
  case 'DEFINE': {
500
556
  if (__USE_VARIABLE_SUBSTITUTION__) {
501
- const variableAttributes = new AttrList(value1, level);
557
+ const variableAttributes = new AttrList(value1);
558
+ substituteVariablesInAttributes(level, variableAttributes, [
559
+ 'NAME',
560
+ 'VALUE',
561
+ 'IMPORT',
562
+ 'QUERYPARAM',
563
+ ]);
502
564
  if ('IMPORT' in variableAttributes) {
503
565
  importVariableDefinition(
504
566
  level,
@@ -538,7 +600,13 @@ export default class M3U8Parser {
538
600
  level.startTimeOffset = parseStartTimeOffset(value1);
539
601
  break;
540
602
  case 'MAP': {
541
- const mapAttrs = new AttrList(value1, level);
603
+ const mapAttrs = new AttrList(value1);
604
+ if (__USE_VARIABLE_SUBSTITUTION__) {
605
+ substituteVariablesInAttributes(level, mapAttrs, [
606
+ 'BYTERANGE',
607
+ 'URI',
608
+ ]);
609
+ }
542
610
  if (frag.duration) {
543
611
  // Initial segment tag is after segment duration tag.
544
612
  // #EXTINF: 6.0
@@ -567,7 +635,6 @@ export default class M3U8Parser {
567
635
  currentInitSegment = frag;
568
636
  createNextFrag = true;
569
637
  }
570
- currentInitSegment.cc = discontinuityCounter;
571
638
  break;
572
639
  }
573
640
  case 'SERVER-CONTROL': {
@@ -600,10 +667,16 @@ export default class M3U8Parser {
600
667
  const previousFragmentPart =
601
668
  currentPart > 0 ? partList[partList.length - 1] : undefined;
602
669
  const index = currentPart++;
603
- const partAttrs = new AttrList(value1, level);
670
+ const partAttrs = new AttrList(value1);
671
+ if (__USE_VARIABLE_SUBSTITUTION__) {
672
+ substituteVariablesInAttributes(level, partAttrs, [
673
+ 'BYTERANGE',
674
+ 'URI',
675
+ ]);
676
+ }
604
677
  const part = new Part(
605
678
  partAttrs,
606
- frag as MediaFragment,
679
+ frag,
607
680
  baseurl,
608
681
  index,
609
682
  previousFragmentPart,
@@ -613,12 +686,20 @@ export default class M3U8Parser {
613
686
  break;
614
687
  }
615
688
  case 'PRELOAD-HINT': {
616
- const preloadHintAttrs = new AttrList(value1, level);
689
+ const preloadHintAttrs = new AttrList(value1);
690
+ if (__USE_VARIABLE_SUBSTITUTION__) {
691
+ substituteVariablesInAttributes(level, preloadHintAttrs, ['URI']);
692
+ }
617
693
  level.preloadHint = preloadHintAttrs;
618
694
  break;
619
695
  }
620
696
  case 'RENDITION-REPORT': {
621
- const renditionReportAttrs = new AttrList(value1, level);
697
+ const renditionReportAttrs = new AttrList(value1);
698
+ if (__USE_VARIABLE_SUBSTITUTION__) {
699
+ substituteVariablesInAttributes(level, renditionReportAttrs, [
700
+ 'URI',
701
+ ]);
702
+ }
622
703
  level.renditionReports = level.renditionReports || [];
623
704
  level.renditionReports.push(renditionReportAttrs);
624
705
  break;
@@ -633,16 +714,12 @@ export default class M3U8Parser {
633
714
  fragments.pop();
634
715
  totalduration -= prevFrag.duration;
635
716
  if (level.partList) {
636
- level.fragmentHint = prevFrag as MediaFragment;
717
+ level.fragmentHint = prevFrag;
637
718
  }
638
719
  } else if (level.partList) {
639
- assignProgramDateTime(
640
- frag as MediaFragment,
641
- prevFrag as MediaFragment,
642
- programDateTimes,
643
- );
720
+ assignProgramDateTime(frag, prevFrag);
644
721
  frag.cc = discontinuityCounter;
645
- level.fragmentHint = frag as MediaFragment;
722
+ level.fragmentHint = frag;
646
723
  if (levelkeys) {
647
724
  setFragLevelKeys(frag, levelkeys, level);
648
725
  }
@@ -661,21 +738,6 @@ export default class M3U8Parser {
661
738
  if (firstFragment) {
662
739
  level.startCC = firstFragment.cc;
663
740
  }
664
- /**
665
- * Backfill any missing PDT values
666
- * "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after
667
- * one or more Media Segment URIs, the client SHOULD extrapolate
668
- * backward from that tag (using EXTINF durations and/or media
669
- * timestamps) to associate dates with those segments."
670
- * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs
671
- * computed.
672
- */
673
- if (firstPdtIndex > 0) {
674
- backfillProgramDateTimes(fragments, firstPdtIndex);
675
- if (firstFragment) {
676
- programDateTimes.unshift(firstFragment as MediaFragment);
677
- }
678
- }
679
741
  } else {
680
742
  level.endSN = 0;
681
743
  level.startCC = 0;
@@ -684,83 +746,23 @@ export default class M3U8Parser {
684
746
  totalduration += level.fragmentHint.duration;
685
747
  }
686
748
  level.totalduration = totalduration;
687
- if (programDateTimes.length && level.dateRangeTagCount && firstFragment) {
688
- mapDateRanges(programDateTimes, level);
689
- }
690
-
691
749
  level.endCC = discontinuityCounter;
692
750
 
693
- return level;
694
- }
695
- }
696
-
697
- export function mapDateRanges(
698
- programDateTimes: MediaFragment[],
699
- details: LevelDetails,
700
- ) {
701
- // Make sure DateRanges are mapped to a ProgramDateTime tag that applies a date to a segment that overlaps with its start date
702
- const programDateTimeCount = programDateTimes.length;
703
- const lastProgramDateTime = programDateTimes[programDateTimeCount - 1];
704
- const playlistEnd = details.live ? Infinity : details.totalduration;
705
- const dateRangeIds = Object.keys(details.dateRanges);
706
- for (let i = dateRangeIds.length; i--; ) {
707
- const dateRange = details.dateRanges[dateRangeIds[i]];
708
- const startDateTime = dateRange.startDate.getTime();
709
- dateRange.tagAnchor = lastProgramDateTime;
710
- for (let j = programDateTimeCount; j--; ) {
711
- const fragIndex = findFragmentWithStartDate(
712
- details,
713
- startDateTime,
714
- programDateTimes,
715
- j,
716
- playlistEnd,
717
- );
718
- if (fragIndex !== -1) {
719
- dateRange.tagAnchor = details.fragments[fragIndex];
720
- break;
721
- }
751
+ /**
752
+ * Backfill any missing PDT values
753
+ * "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after
754
+ * one or more Media Segment URIs, the client SHOULD extrapolate
755
+ * backward from that tag (using EXTINF durations and/or media
756
+ * timestamps) to associate dates with those segments."
757
+ * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs
758
+ * computed.
759
+ */
760
+ if (firstPdtIndex > 0) {
761
+ backfillProgramDateTimes(fragments, firstPdtIndex);
722
762
  }
723
- }
724
- }
725
763
 
726
- function findFragmentWithStartDate(
727
- details: LevelDetails,
728
- startDateTime: number,
729
- programDateTimes: MediaFragment[],
730
- index: number,
731
- endTime: number,
732
- ): number {
733
- const pdtFragment = programDateTimes[index];
734
- if (pdtFragment) {
735
- // find matching range between PDT tags
736
- const durationBetweenPdt =
737
- (programDateTimes[index + 1]?.start || endTime) - pdtFragment.start;
738
- const pdtStart = pdtFragment.programDateTime as number;
739
- if (
740
- (startDateTime >= pdtStart || index === 0) &&
741
- startDateTime <= pdtStart + durationBetweenPdt * 1000
742
- ) {
743
- // map to fragment with date-time range
744
- const startIndex = programDateTimes[index].sn - details.startSN;
745
- const fragments = details.fragments;
746
- if (fragments.length > programDateTimes.length) {
747
- const endSegment =
748
- programDateTimes[index + 1] || fragments[fragments.length - 1];
749
- const endIndex = endSegment.sn - details.startSN;
750
- for (let i = endIndex; i > startIndex; i--) {
751
- const fragStartDateTime = fragments[i].programDateTime as number;
752
- if (
753
- startDateTime >= fragStartDateTime &&
754
- startDateTime < fragStartDateTime + fragments[i].duration * 1000
755
- ) {
756
- return i;
757
- }
758
- }
759
- }
760
- return startIndex;
761
- }
764
+ return level;
762
765
  }
763
- return -1;
764
766
  }
765
767
 
766
768
  function parseKey(
@@ -769,7 +771,16 @@ function parseKey(
769
771
  parsed: ParsedMultivariantPlaylist | LevelDetails,
770
772
  ): LevelKey {
771
773
  // https://tools.ietf.org/html/rfc8216#section-4.3.2.4
772
- const keyAttrs = new AttrList(keyTagAttributes, parsed);
774
+ const keyAttrs = new AttrList(keyTagAttributes);
775
+ if (__USE_VARIABLE_SUBSTITUTION__) {
776
+ substituteVariablesInAttributes(parsed, keyAttrs, [
777
+ 'KEYFORMAT',
778
+ 'KEYFORMATVERSIONS',
779
+ 'URI',
780
+ 'IV',
781
+ 'URI',
782
+ ]);
783
+ }
773
784
  const decryptmethod = keyAttrs.METHOD ?? '';
774
785
  const decrypturi = keyAttrs.URI;
775
786
  const decryptiv = keyAttrs.hexadecimalInteger('IV');
@@ -853,22 +864,17 @@ function backfillProgramDateTimes(
853
864
  }
854
865
  }
855
866
 
856
- export function assignProgramDateTime(
857
- frag: MediaFragment,
858
- prevFrag: MediaFragment | null,
859
- programDateTimes: MediaFragment[],
860
- ) {
867
+ function assignProgramDateTime(frag, prevFrag) {
861
868
  if (frag.rawProgramDateTime) {
862
869
  frag.programDateTime = Date.parse(frag.rawProgramDateTime);
863
- if (!Number.isFinite(frag.programDateTime)) {
864
- frag.programDateTime = null;
865
- frag.rawProgramDateTime = null;
866
- return;
867
- }
868
- programDateTimes.push(frag);
869
870
  } else if (prevFrag?.programDateTime) {
870
871
  frag.programDateTime = prevFrag.endProgramDateTime;
871
872
  }
873
+
874
+ if (!Number.isFinite(frag.programDateTime)) {
875
+ frag.programDateTime = null;
876
+ frag.rawProgramDateTime = null;
877
+ }
872
878
  }
873
879
 
874
880
  function setInitSegment(
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { Events } from '../events';
10
10
  import { ErrorDetails, ErrorTypes } from '../errors';
11
+ import { logger } from '../utils/logger';
11
12
  import M3U8Parser from './m3u8-parser';
12
13
  import type { LevelParsed, VariableMap } from '../types/level';
13
14
  import type {
@@ -220,10 +221,10 @@ class PlaylistLoader implements NetworkComponentAPI {
220
221
  loaderContext.level === context.level
221
222
  ) {
222
223
  // same URL can't overlap
223
- this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
224
+ logger.trace('[playlist-loader]: playlist request ongoing');
224
225
  return;
225
226
  }
226
- this.hls.logger.log(
227
+ logger.log(
227
228
  `[playlist-loader]: aborting previous loader for type: ${context.type}`,
228
229
  );
229
230
  loader.abort();
@@ -407,7 +408,7 @@ class PlaylistLoader implements NetworkComponentAPI {
407
408
  levels[0].audioCodec &&
408
409
  !levels[0].attrs.AUDIO
409
410
  ) {
410
- this.hls.logger.log(
411
+ logger.log(
411
412
  '[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one',
412
413
  );
413
414
  audioTracks.unshift({
@@ -452,6 +453,7 @@ class PlaylistLoader implements NetworkComponentAPI {
452
453
  const { id, level, type } = context;
453
454
 
454
455
  const url = getResponseUrl(response, context);
456
+ const levelUrlId = 0;
455
457
  const levelId = Number.isFinite(level as number)
456
458
  ? (level as number)
457
459
  : Number.isFinite(id as number)
@@ -463,7 +465,7 @@ class PlaylistLoader implements NetworkComponentAPI {
463
465
  url,
464
466
  levelId,
465
467
  levelType,
466
- 0,
468
+ levelUrlId,
467
469
  this.variableList,
468
470
  );
469
471
 
@@ -553,7 +555,7 @@ class PlaylistLoader implements NetworkComponentAPI {
553
555
  message += ` id: ${context.id} group-id: "${context.groupId}"`;
554
556
  }
555
557
  const error = new Error(message);
556
- this.hls.logger.warn(`[playlist-loader]: ${message}`);
558
+ logger.warn(`[playlist-loader]: ${message}`);
557
559
  let details = ErrorDetails.UNKNOWN;
558
560
  let fatal = false;
559
561