hls.js 1.5.14-0.canary.10515 → 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
@@ -9,16 +9,12 @@ import { PlaylistLevelType } from '../types/loader';
9
9
  import { Level } from '../types/level';
10
10
  import { subtitleOptionsIdentical } from '../utils/media-option-attributes';
11
11
  import { ErrorDetails, ErrorTypes } from '../errors';
12
- import {
13
- isFullSegmentEncryption,
14
- getAesModeFromFullSegmentMethod,
15
- } from '../utils/encryption-methods-util';
16
12
  import type { NetworkComponentAPI } from '../types/component-api';
17
13
  import type Hls from '../hls';
18
14
  import type { FragmentTracker } from './fragment-tracker';
19
15
  import type KeyLoader from '../loader/key-loader';
20
16
  import type { LevelDetails } from '../loader/level-details';
21
- import type { Fragment, MediaFragment } from '../loader/fragment';
17
+ import type { Fragment } from '../loader/fragment';
22
18
  import type {
23
19
  ErrorData,
24
20
  FragLoadedData,
@@ -55,22 +51,25 @@ export class SubtitleStreamController
55
51
  hls,
56
52
  fragmentTracker,
57
53
  keyLoader,
58
- 'subtitle-stream-controller',
54
+ '[subtitle-stream-controller]',
59
55
  PlaylistLevelType.SUBTITLE,
60
56
  );
61
- this.registerListeners();
57
+ this._registerListeners();
62
58
  }
63
59
 
64
60
  protected onHandlerDestroying() {
65
- this.unregisterListeners();
61
+ this._unregisterListeners();
66
62
  super.onHandlerDestroying();
67
63
  this.mainDetails = null;
68
64
  }
69
65
 
70
- protected registerListeners() {
71
- super.registerListeners();
66
+ private _registerListeners() {
72
67
  const { hls } = this;
68
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
69
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
70
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
73
71
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
72
+ hls.on(Events.ERROR, this.onError, this);
74
73
  hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
75
74
  hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
76
75
  hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
@@ -79,10 +78,13 @@ export class SubtitleStreamController
79
78
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
80
79
  }
81
80
 
82
- protected unregisterListeners() {
83
- super.unregisterListeners();
81
+ private _unregisterListeners() {
84
82
  const { hls } = this;
83
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
84
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
85
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
85
86
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
87
+ hls.off(Events.ERROR, this.onError, this);
86
88
  hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
87
89
  hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
88
90
  hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
@@ -105,28 +107,26 @@ export class SubtitleStreamController
105
107
  this.tick();
106
108
  }
107
109
 
108
- protected onManifestLoading() {
109
- super.onManifestLoading();
110
+ onManifestLoading() {
110
111
  this.mainDetails = null;
112
+ this.fragmentTracker.removeAllFragments();
111
113
  }
112
114
 
113
- protected onMediaDetaching(): void {
115
+ onMediaDetaching(): void {
114
116
  this.tracksBuffered = [];
115
117
  super.onMediaDetaching();
116
118
  }
117
119
 
118
- private onLevelLoaded(event: Events.LEVEL_LOADED, data: LevelLoadedData) {
120
+ onLevelLoaded(event: Events.LEVEL_LOADED, data: LevelLoadedData) {
119
121
  this.mainDetails = data.details;
120
122
  }
121
123
 
122
- private onSubtitleFragProcessed(
124
+ onSubtitleFragProcessed(
123
125
  event: Events.SUBTITLE_FRAG_PROCESSED,
124
126
  data: SubtitleFragProcessed,
125
127
  ) {
126
128
  const { frag, success } = data;
127
- if (frag.sn !== 'initSegment') {
128
- this.fragPrevious = frag as MediaFragment;
129
- }
129
+ this.fragPrevious = frag;
130
130
  this.state = State.IDLE;
131
131
  if (!success) {
132
132
  return;
@@ -158,14 +158,11 @@ export class SubtitleStreamController
158
158
  };
159
159
  buffered.push(timeRange);
160
160
  }
161
- this.fragmentTracker.fragBuffered(frag as MediaFragment);
161
+ this.fragmentTracker.fragBuffered(frag);
162
162
  this.fragBufferedComplete(frag, null);
163
163
  }
164
164
 
165
- private onBufferFlushing(
166
- event: Events.BUFFER_FLUSHING,
167
- data: BufferFlushingData,
168
- ) {
165
+ onBufferFlushing(event: Events.BUFFER_FLUSHING, data: BufferFlushingData) {
169
166
  const { startOffset, endOffset } = data;
170
167
  if (startOffset === 0 && endOffset !== Number.POSITIVE_INFINITY) {
171
168
  const endOffsetSubtitles = endOffset - 1;
@@ -194,7 +191,7 @@ export class SubtitleStreamController
194
191
  }
195
192
  }
196
193
 
197
- private onFragBuffered(event: Events.FRAG_BUFFERED, data: FragBufferedData) {
194
+ onFragBuffered(event: Events.FRAG_BUFFERED, data: FragBufferedData) {
198
195
  if (!this.loadedmetadata && data.frag.type === PlaylistLevelType.MAIN) {
199
196
  if (this.media?.buffered.length) {
200
197
  this.loadedmetadata = true;
@@ -203,12 +200,12 @@ export class SubtitleStreamController
203
200
  }
204
201
 
205
202
  // If something goes wrong, proceed to next frag, if we were processing one.
206
- protected onError(event: Events.ERROR, data: ErrorData) {
203
+ onError(event: Events.ERROR, data: ErrorData) {
207
204
  const frag = data.frag;
208
205
 
209
206
  if (frag?.type === PlaylistLevelType.SUBTITLE) {
210
207
  if (data.details === ErrorDetails.FRAG_GAP) {
211
- this.fragmentTracker.fragBuffered(frag as MediaFragment, true);
208
+ this.fragmentTracker.fragBuffered(frag, true);
212
209
  }
213
210
  if (this.fragCurrent) {
214
211
  this.fragCurrent.abortRequests();
@@ -220,7 +217,7 @@ export class SubtitleStreamController
220
217
  }
221
218
 
222
219
  // Got all new subtitle levels.
223
- private onSubtitleTracksUpdated(
220
+ onSubtitleTracksUpdated(
224
221
  event: Events.SUBTITLE_TRACKS_UPDATED,
225
222
  { subtitleTracks }: SubtitleTracksUpdatedData,
226
223
  ) {
@@ -245,7 +242,7 @@ export class SubtitleStreamController
245
242
  this.mediaBuffer = null;
246
243
  }
247
244
 
248
- private onSubtitleTrackSwitch(
245
+ onSubtitleTrackSwitch(
249
246
  event: Events.SUBTITLE_TRACK_SWITCH,
250
247
  data: TrackSwitchedData,
251
248
  ) {
@@ -263,13 +260,13 @@ export class SubtitleStreamController
263
260
  } else {
264
261
  this.mediaBuffer = null;
265
262
  }
266
- if (currentTrack && this.state !== State.STOPPED) {
263
+ if (currentTrack) {
267
264
  this.setInterval(TICK_INTERVAL);
268
265
  }
269
266
  }
270
267
 
271
268
  // Got a new set of subtitle fragments.
272
- private onSubtitleTrackLoaded(
269
+ onSubtitleTrackLoaded(
273
270
  event: Events.SUBTITLE_TRACK_LOADED,
274
271
  data: TrackLoadedData,
275
272
  ) {
@@ -370,7 +367,7 @@ export class SubtitleStreamController
370
367
  payload.byteLength > 0 &&
371
368
  decryptData?.key &&
372
369
  decryptData.iv &&
373
- isFullSegmentEncryption(decryptData.method)
370
+ decryptData.method === 'AES-128'
374
371
  ) {
375
372
  const startTime = performance.now();
376
373
  // decrypt the subtitles
@@ -379,7 +376,6 @@ export class SubtitleStreamController
379
376
  new Uint8Array(payload),
380
377
  decryptData.key.buffer,
381
378
  decryptData.iv.buffer,
382
- getAesModeFromFullSegmentMethod(decryptData.method),
383
379
  )
384
380
  .catch((err) => {
385
381
  hls.trigger(Events.ERROR, {
@@ -430,9 +426,15 @@ export class SubtitleStreamController
430
426
  config.maxBufferHole,
431
427
  );
432
428
  const { end: targetBufferTime, len: bufferLen } = bufferedInfo;
429
+
430
+ const mainBufferInfo = this.getFwdBufferInfo(
431
+ this.media,
432
+ PlaylistLevelType.MAIN,
433
+ );
433
434
  const trackDetails = track.details as LevelDetails;
434
435
  const maxBufLen =
435
- this.hls.maxBufferLength + trackDetails.levelTargetDuration;
436
+ this.getMaxBufferLength(mainBufferInfo?.len) +
437
+ trackDetails.levelTargetDuration;
436
438
 
437
439
  if (bufferLen > maxBufLen) {
438
440
  return;
@@ -488,14 +490,24 @@ export class SubtitleStreamController
488
490
  }
489
491
  }
490
492
 
493
+ protected getMaxBufferLength(mainBufferLength?: number): number {
494
+ const maxConfigBuffer = super.getMaxBufferLength();
495
+ if (!mainBufferLength) {
496
+ return maxConfigBuffer;
497
+ }
498
+ return Math.max(maxConfigBuffer, mainBufferLength);
499
+ }
500
+
491
501
  protected loadFragment(
492
502
  frag: Fragment,
493
503
  level: Level,
494
504
  targetBufferTime: number,
495
505
  ) {
506
+ this.fragCurrent = frag;
496
507
  if (frag.sn === 'initSegment') {
497
508
  this._loadInitSegment(frag, level);
498
509
  } else {
510
+ this.startFragRequested = true;
499
511
  super.loadFragment(frag, level, targetBufferTime);
500
512
  }
501
513
  }
@@ -35,14 +35,13 @@ class SubtitleTrackController extends BasePlaylistController {
35
35
  private currentTrack: MediaPlaylist | null = null;
36
36
  private selectDefaultTrack: boolean = true;
37
37
  private queuedDefaultTrack: number = -1;
38
+ private asyncPollTrackChange: () => void = () => this.pollTrackChange(0);
38
39
  private useTextTrackPolling: boolean = false;
39
40
  private subtitlePollingInterval: number = -1;
40
41
  private _subtitleDisplay: boolean = true;
41
42
 
42
- private asyncPollTrackChange = () => this.pollTrackChange(0);
43
-
44
43
  constructor(hls: Hls) {
45
- super(hls, 'subtitle-track-controller');
44
+ super(hls, '[subtitle-track-controller]');
46
45
  this.registerListeners();
47
46
  }
48
47
 
@@ -51,8 +50,7 @@ class SubtitleTrackController extends BasePlaylistController {
51
50
  this.tracks.length = 0;
52
51
  this.tracksInGroup.length = 0;
53
52
  this.currentTrack = null;
54
- // @ts-ignore
55
- this.onTextTracksChanged = this.asyncPollTrackChange = null;
53
+ this.onTextTracksChanged = this.asyncPollTrackChange = null as any;
56
54
  super.destroy();
57
55
  }
58
56
 
@@ -28,6 +28,7 @@ import type {
28
28
  BufferFlushingData,
29
29
  FragLoadingData,
30
30
  } from '../types/events';
31
+ import { logger } from '../utils/logger';
31
32
  import type Hls from '../hls';
32
33
  import type { ComponentAPI } from '../types/component-api';
33
34
  import type { HlsConfig } from '../config';
@@ -130,17 +131,22 @@ export class TimelineController implements ComponentAPI {
130
131
  hls.off(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this);
131
132
  hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
132
133
  // @ts-ignore
133
- this.hls = this.config = this.media = null;
134
+ this.hls = this.config = null;
134
135
  this.cea608Parser1 = this.cea608Parser2 = undefined;
135
136
  }
136
137
 
137
138
  private initCea608Parsers() {
138
- const channel1 = new OutputFilter(this, 'textTrack1');
139
- const channel2 = new OutputFilter(this, 'textTrack2');
140
- const channel3 = new OutputFilter(this, 'textTrack3');
141
- const channel4 = new OutputFilter(this, 'textTrack4');
142
- this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
143
- this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);
139
+ if (
140
+ this.config.enableCEA708Captions &&
141
+ (!this.cea608Parser1 || !this.cea608Parser2)
142
+ ) {
143
+ const channel1 = new OutputFilter(this, 'textTrack1');
144
+ const channel2 = new OutputFilter(this, 'textTrack2');
145
+ const channel3 = new OutputFilter(this, 'textTrack3');
146
+ const channel4 = new OutputFilter(this, 'textTrack4');
147
+ this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
148
+ this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);
149
+ }
144
150
  }
145
151
 
146
152
  public addCues(
@@ -192,7 +198,7 @@ export class TimelineController implements ComponentAPI {
192
198
  { frag, id, initPTS, timescale }: InitPTSFoundData,
193
199
  ) {
194
200
  const { unparsedVttFrags } = this;
195
- if (id === PlaylistLevelType.MAIN) {
201
+ if (id === 'main') {
196
202
  this.initPTS[frag.cc] = { baseTime: initPTS, timescale };
197
203
  }
198
204
 
@@ -215,8 +221,6 @@ export class TimelineController implements ComponentAPI {
215
221
  canReuseVttTextTrack(textTrack, {
216
222
  name: label,
217
223
  lang: language,
218
- characteristics:
219
- 'transcribes-spoken-dialog,describes-music-and-sound',
220
224
  attrs: {} as any,
221
225
  })
222
226
  ) {
@@ -305,7 +309,6 @@ export class TimelineController implements ComponentAPI {
305
309
  delete captionsTracks[trackName];
306
310
  });
307
311
  this.nonNativeCaptionsTracks = {};
308
- this.media = null;
309
312
  }
310
313
 
311
314
  private onManifestLoading() {
@@ -407,7 +410,7 @@ export class TimelineController implements ComponentAPI {
407
410
  .filter((t) => t !== null)
408
411
  .map((t) => (t as TextTrack).label);
409
412
  if (unusedTextTracks.length) {
410
- this.hls.logger.warn(
413
+ logger.warn(
411
414
  `Media element contains unused subtitle tracks: ${unusedTextTracks.join(
412
415
  ', ',
413
416
  )}. Replace media element for each source to clear TextTracks and captions menu.`,
@@ -542,7 +545,7 @@ export class TimelineController implements ComponentAPI {
542
545
  });
543
546
  },
544
547
  (error) => {
545
- hls.logger.log(`Failed to parse IMSC1: ${error}`);
548
+ logger.log(`Failed to parse IMSC1: ${error}`);
546
549
  hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
547
550
  success: false,
548
551
  frag: frag,
@@ -589,7 +592,7 @@ export class TimelineController implements ComponentAPI {
589
592
  this._fallbackToIMSC1(frag, payload);
590
593
  }
591
594
  // Something went wrong while parsing. Trigger event with success false.
592
- hls.logger.log(`Failed to parse VTT cue: ${error}`);
595
+ logger.log(`Failed to parse VTT cue: ${error}`);
593
596
  if (missingInitPTS && maxAvCC > frag.cc) {
594
597
  return;
595
598
  }
@@ -661,7 +664,9 @@ export class TimelineController implements ComponentAPI {
661
664
  event: Events.FRAG_PARSING_USERDATA,
662
665
  data: FragParsingUserdataData,
663
666
  ) {
664
- if (!this.enabled || !this.config.enableCEA708Captions) {
667
+ this.initCea608Parsers();
668
+ const { cea608Parser1, cea608Parser2 } = this;
669
+ if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
665
670
  return;
666
671
  }
667
672
  const { frag, samples } = data;
@@ -676,12 +681,9 @@ export class TimelineController implements ComponentAPI {
676
681
  for (let i = 0; i < samples.length; i++) {
677
682
  const ccBytes = samples[i].bytes;
678
683
  if (ccBytes) {
679
- if (!this.cea608Parser1) {
680
- this.initCea608Parsers();
681
- }
682
684
  const ccdatas = this.extractCea608Data(ccBytes);
683
- this.cea608Parser1!.addData(samples[i].pts, ccdatas[0]);
684
- this.cea608Parser2!.addData(samples[i].pts, ccdatas[1]);
685
+ cea608Parser1.addData(samples[i].pts, ccdatas[0]);
686
+ cea608Parser2.addData(samples[i].pts, ccdatas[1]);
685
687
  }
686
688
  }
687
689
  }
@@ -1,32 +1,13 @@
1
- import { DecrypterAesMode } from './decrypter-aes-mode';
2
-
3
1
  export default class AESCrypto {
4
2
  private subtle: SubtleCrypto;
5
3
  private aesIV: Uint8Array;
6
- private aesMode: DecrypterAesMode;
7
4
 
8
- constructor(subtle: SubtleCrypto, iv: Uint8Array, aesMode: DecrypterAesMode) {
5
+ constructor(subtle: SubtleCrypto, iv: Uint8Array) {
9
6
  this.subtle = subtle;
10
7
  this.aesIV = iv;
11
- this.aesMode = aesMode;
12
8
  }
13
9
 
14
10
  decrypt(data: ArrayBuffer, key: CryptoKey) {
15
- switch (this.aesMode) {
16
- case DecrypterAesMode.cbc:
17
- return this.subtle.decrypt(
18
- { name: 'AES-CBC', iv: this.aesIV },
19
- key,
20
- data,
21
- );
22
- case DecrypterAesMode.ctr:
23
- return this.subtle.decrypt(
24
- { name: 'AES-CTR', counter: this.aesIV, length: 64 }, //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
25
- key,
26
- data,
27
- );
28
- default:
29
- throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`);
30
- }
11
+ return this.subtle.decrypt({ name: 'AES-CBC', iv: this.aesIV }, key, data);
31
12
  }
32
13
  }
@@ -4,7 +4,6 @@ import AESDecryptor, { removePadding } from './aes-decryptor';
4
4
  import { logger } from '../utils/logger';
5
5
  import { appendUint8Array } from '../utils/mp4-tools';
6
6
  import { sliceUint8 } from '../utils/typed-array';
7
- import { DecrypterAesMode } from './decrypter-aes-mode';
8
7
  import type { HlsConfig } from '../config';
9
8
 
10
9
  const CHUNK_SIZE = 16; // 16 bytes, 128 bits
@@ -20,10 +19,9 @@ export default class Decrypter {
20
19
  private currentIV: ArrayBuffer | null = null;
21
20
  private currentResult: ArrayBuffer | null = null;
22
21
  private useSoftware: boolean;
23
- private enableSoftwareAES: boolean;
24
22
 
25
23
  constructor(config: HlsConfig, { removePKCS7Padding = true } = {}) {
26
- this.enableSoftwareAES = config.enableSoftwareAES;
24
+ this.useSoftware = config.enableSoftwareAES;
27
25
  this.removePKCS7Padding = removePKCS7Padding;
28
26
  // built in decryptor expects PKCS7 padding
29
27
  if (removePKCS7Padding) {
@@ -38,6 +36,7 @@ export default class Decrypter {
38
36
  /* no-op */
39
37
  }
40
38
  }
39
+
41
40
  this.useSoftware = !this.subtle;
42
41
  }
43
42
 
@@ -82,11 +81,10 @@ export default class Decrypter {
82
81
  data: Uint8Array | ArrayBuffer,
83
82
  key: ArrayBuffer,
84
83
  iv: ArrayBuffer,
85
- aesMode: DecrypterAesMode,
86
84
  ): Promise<ArrayBuffer> {
87
85
  if (this.useSoftware) {
88
86
  return new Promise((resolve, reject) => {
89
- this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
87
+ this.softwareDecrypt(new Uint8Array(data), key, iv);
90
88
  const decryptResult = this.flush();
91
89
  if (decryptResult) {
92
90
  resolve(decryptResult.buffer);
@@ -95,7 +93,7 @@ export default class Decrypter {
95
93
  }
96
94
  });
97
95
  }
98
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
96
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
99
97
  }
100
98
 
101
99
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
@@ -104,13 +102,8 @@ export default class Decrypter {
104
102
  data: Uint8Array,
105
103
  key: ArrayBuffer,
106
104
  iv: ArrayBuffer,
107
- aesMode: DecrypterAesMode,
108
105
  ): ArrayBuffer | null {
109
106
  const { currentIV, currentResult, remainderData } = this;
110
- if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
111
- logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
112
- return null;
113
- }
114
107
  this.logOnce('JS AES decrypt');
115
108
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
116
109
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -153,24 +146,23 @@ export default class Decrypter {
153
146
  data: Uint8Array,
154
147
  key: ArrayBuffer,
155
148
  iv: ArrayBuffer,
156
- aesMode: DecrypterAesMode,
157
149
  ): Promise<ArrayBuffer> {
158
150
  if (this.key !== key || !this.fastAesKey) {
159
151
  if (!this.subtle) {
160
- return Promise.resolve(this.onWebCryptoError(data, key, iv, aesMode));
152
+ return Promise.resolve(this.onWebCryptoError(data, key, iv));
161
153
  }
162
154
  this.key = key;
163
- this.fastAesKey = new FastAESKey(this.subtle, key, aesMode);
155
+ this.fastAesKey = new FastAESKey(this.subtle, key);
164
156
  }
165
157
  return this.fastAesKey
166
158
  .expandKey()
167
- .then((aesKey: CryptoKey) => {
159
+ .then((aesKey) => {
168
160
  // decrypt using web crypto
169
161
  if (!this.subtle) {
170
162
  return Promise.reject(new Error('web crypto not initialized'));
171
163
  }
172
164
  this.logOnce('WebCrypto AES decrypt');
173
- const crypto = new AESCrypto(this.subtle, new Uint8Array(iv), aesMode);
165
+ const crypto = new AESCrypto(this.subtle, new Uint8Array(iv));
174
166
  return crypto.decrypt(data.buffer, aesKey);
175
167
  })
176
168
  .catch((err) => {
@@ -178,7 +170,7 @@ export default class Decrypter {
178
170
  `[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`,
179
171
  );
180
172
 
181
- return this.onWebCryptoError(data, key, iv, aesMode);
173
+ return this.onWebCryptoError(data, key, iv);
182
174
  });
183
175
  }
184
176
 
@@ -186,23 +178,15 @@ export default class Decrypter {
186
178
  data: Uint8Array,
187
179
  key: ArrayBuffer,
188
180
  iv: ArrayBuffer,
189
- aesMode: DecrypterAesMode,
190
181
  ): ArrayBuffer | never {
191
- const enableSoftwareAES = this.enableSoftwareAES;
192
- if (enableSoftwareAES) {
193
- this.useSoftware = true;
194
- this.logEnabled = true;
195
- this.softwareDecrypt(data, key, iv, aesMode);
196
- const decryptResult = this.flush();
197
- if (decryptResult) {
198
- return decryptResult.buffer;
199
- }
182
+ this.useSoftware = true;
183
+ this.logEnabled = true;
184
+ this.softwareDecrypt(data, key, iv);
185
+ const decryptResult = this.flush();
186
+ if (decryptResult) {
187
+ return decryptResult.buffer;
200
188
  }
201
- throw new Error(
202
- 'WebCrypto' +
203
- (enableSoftwareAES ? ' and softwareDecrypt' : '') +
204
- ': failed to decrypt data',
205
- );
189
+ throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
206
190
  }
207
191
 
208
192
  private getValidChunk(data: Uint8Array): Uint8Array {
@@ -1,39 +1,16 @@
1
- import { DecrypterAesMode } from './decrypter-aes-mode';
2
-
3
1
  export default class FastAESKey {
4
2
  private subtle: SubtleCrypto;
5
3
  private key: ArrayBuffer;
6
- private aesMode: DecrypterAesMode;
7
4
 
8
- constructor(
9
- subtle: SubtleCrypto,
10
- key: ArrayBuffer,
11
- aesMode: DecrypterAesMode,
12
- ) {
5
+ constructor(subtle: SubtleCrypto, key: ArrayBuffer) {
13
6
  this.subtle = subtle;
14
7
  this.key = key;
15
- this.aesMode = aesMode;
16
8
  }
17
9
 
18
10
  expandKey() {
19
- const subtleAlgoName = getSubtleAlgoName(this.aesMode);
20
- return this.subtle.importKey(
21
- 'raw',
22
- this.key,
23
- { name: subtleAlgoName },
24
- false,
25
- ['encrypt', 'decrypt'],
26
- );
27
- }
28
- }
29
-
30
- function getSubtleAlgoName(aesMode: DecrypterAesMode) {
31
- switch (aesMode) {
32
- case DecrypterAesMode.cbc:
33
- return 'AES-CBC';
34
- case DecrypterAesMode.ctr:
35
- return 'AES-CTR';
36
- default:
37
- throw new Error(`[FastAESKey] invalid aes mode ${aesMode}`);
11
+ return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, [
12
+ 'encrypt',
13
+ 'decrypt',
14
+ ]);
38
15
  }
39
16
  }
@@ -4,16 +4,16 @@
4
4
  import BaseAudioDemuxer from './base-audio-demuxer';
5
5
  import * as ADTS from './adts';
6
6
  import * as MpegAudio from './mpegaudio';
7
- import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
7
+ import { logger } from '../../utils/logger';
8
+ import * as ID3 from '../id3';
8
9
  import type { HlsEventEmitter } from '../../events';
9
10
  import type { HlsConfig } from '../../config';
10
- import type { ILogger } from '../../utils/logger';
11
11
 
12
12
  class AACDemuxer extends BaseAudioDemuxer {
13
13
  private readonly observer: HlsEventEmitter;
14
14
  private readonly config: HlsConfig;
15
15
 
16
- constructor(observer: HlsEventEmitter, config) {
16
+ constructor(observer, config) {
17
17
  super();
18
18
  this.observer = observer;
19
19
  this.config = config;
@@ -42,7 +42,7 @@ class AACDemuxer extends BaseAudioDemuxer {
42
42
  }
43
43
 
44
44
  // Source for probe info - https://wiki.multimedia.cx/index.php?title=ADTS
45
- static probe(data: Uint8Array | undefined, logger: ILogger): boolean {
45
+ static probe(data: Uint8Array | undefined): boolean {
46
46
  if (!data) {
47
47
  return false;
48
48
  }
@@ -51,7 +51,7 @@ class AACDemuxer extends BaseAudioDemuxer {
51
51
  // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
52
52
  // Layer bits (position 14 and 15) in header should be always 0 for ADTS
53
53
  // More info https://wiki.multimedia.cx/index.php?title=ADTS
54
- const id3Data = getId3Data(data, 0);
54
+ const id3Data = ID3.getID3Data(data, 0);
55
55
  let offset = id3Data?.length || 0;
56
56
 
57
57
  if (MpegAudio.probe(data, offset)) {
@@ -1,6 +1,5 @@
1
1
  import BaseAudioDemuxer from './base-audio-demuxer';
2
- import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
3
- import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
2
+ import { getID3Data, getTimeStamp } from '../id3';
4
3
  import { getAudioBSID } from './dolby';
5
4
  import type { HlsEventEmitter } from '../../events';
6
5
  import type { AudioFrame, DemuxedAudioTrack } from '../../types/demuxer';
@@ -8,7 +7,7 @@ import type { AudioFrame, DemuxedAudioTrack } from '../../types/demuxer';
8
7
  export class AC3Demuxer extends BaseAudioDemuxer {
9
8
  private readonly observer: HlsEventEmitter;
10
9
 
11
- constructor(observer: HlsEventEmitter) {
10
+ constructor(observer) {
12
11
  super();
13
12
  this.observer = observer;
14
13
  }
@@ -62,7 +61,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
62
61
  return false;
63
62
  }
64
63
 
65
- const id3Data = getId3Data(data, 0);
64
+ const id3Data = getID3Data(data, 0);
66
65
  if (!id3Data) {
67
66
  return false;
68
67
  }
@@ -72,7 +71,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
72
71
  if (
73
72
  data[offset] === 0x0b &&
74
73
  data[offset + 1] === 0x77 &&
75
- getId3Timestamp(id3Data) !== undefined &&
74
+ getTimeStamp(id3Data) !== undefined &&
76
75
  // check the bsid to confirm ac-3
77
76
  getAudioBSID(data, offset) < 16
78
77
  ) {