hls.js 1.5.2-0.canary.9923 → 1.5.2

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 (44) hide show
  1. package/dist/hls-demo.js +0 -5
  2. package/dist/hls-demo.js.map +1 -1
  3. package/dist/hls.js +379 -431
  4. package/dist/hls.js.d.ts +14 -14
  5. package/dist/hls.js.map +1 -1
  6. package/dist/hls.light.js +217 -275
  7. package/dist/hls.light.js.map +1 -1
  8. package/dist/hls.light.min.js +1 -1
  9. package/dist/hls.light.min.js.map +1 -1
  10. package/dist/hls.light.mjs +219 -278
  11. package/dist/hls.light.mjs.map +1 -1
  12. package/dist/hls.min.js +1 -1
  13. package/dist/hls.min.js.map +1 -1
  14. package/dist/hls.mjs +350 -401
  15. package/dist/hls.mjs.map +1 -1
  16. package/dist/hls.worker.js +1 -1
  17. package/dist/hls.worker.js.map +1 -1
  18. package/package.json +9 -9
  19. package/src/controller/abr-controller.ts +2 -2
  20. package/src/controller/base-stream-controller.ts +16 -16
  21. package/src/controller/buffer-controller.ts +0 -6
  22. package/src/controller/eme-controller.ts +12 -6
  23. package/src/controller/latency-controller.ts +8 -7
  24. package/src/controller/level-controller.ts +18 -7
  25. package/src/controller/stream-controller.ts +14 -11
  26. package/src/controller/subtitle-stream-controller.ts +1 -6
  27. package/src/controller/subtitle-track-controller.ts +2 -4
  28. package/src/controller/timeline-controller.ts +26 -20
  29. package/src/crypt/aes-crypto.ts +2 -21
  30. package/src/crypt/decrypter.ts +18 -32
  31. package/src/crypt/fast-aes-key.ts +5 -24
  32. package/src/demux/audio/adts.ts +4 -9
  33. package/src/demux/sample-aes.ts +0 -2
  34. package/src/demux/transmuxer-interface.ts +12 -4
  35. package/src/demux/transmuxer.ts +3 -16
  36. package/src/demux/tsdemuxer.ts +17 -12
  37. package/src/loader/fragment-loader.ts +2 -9
  38. package/src/loader/key-loader.ts +0 -2
  39. package/src/loader/level-key.ts +9 -10
  40. package/src/remux/mp4-remuxer.ts +3 -4
  41. package/src/types/demuxer.ts +0 -1
  42. package/src/utils/codecs.ts +4 -33
  43. package/src/crypt/decrypter-aes-mode.ts +0 -4
  44. package/src/utils/encryption-methods-util.ts +0 -21
package/package.json CHANGED
@@ -67,10 +67,10 @@
67
67
  "@babel/plugin-proposal-object-rest-spread": "7.20.7",
68
68
  "@babel/plugin-proposal-optional-chaining": "7.21.0",
69
69
  "@babel/plugin-transform-object-assign": "7.23.3",
70
- "@babel/preset-env": "7.23.8",
70
+ "@babel/preset-env": "7.23.7",
71
71
  "@babel/preset-typescript": "7.23.3",
72
72
  "@babel/register": "7.23.7",
73
- "@microsoft/api-documenter": "7.23.19",
73
+ "@microsoft/api-documenter": "7.23.16",
74
74
  "@microsoft/api-extractor": "7.39.1",
75
75
  "@rollup/plugin-alias": "5.1.0",
76
76
  "@rollup/plugin-babel": "6.0.4",
@@ -78,17 +78,17 @@
78
78
  "@rollup/plugin-node-resolve": "15.2.3",
79
79
  "@rollup/plugin-replace": "5.0.5",
80
80
  "@rollup/plugin-terser": "0.4.4",
81
- "@rollup/plugin-typescript": "11.1.6",
81
+ "@rollup/plugin-typescript": "11.1.5",
82
82
  "@svta/common-media-library": "0.6.1",
83
83
  "@types/chai": "4.3.11",
84
84
  "@types/chart.js": "2.9.41",
85
85
  "@types/mocha": "10.0.6",
86
86
  "@types/sinon-chai": "3.2.12",
87
- "@typescript-eslint/eslint-plugin": "6.19.1",
88
- "@typescript-eslint/parser": "6.19.1",
87
+ "@typescript-eslint/eslint-plugin": "6.17.0",
88
+ "@typescript-eslint/parser": "6.17.0",
89
89
  "babel-loader": "9.1.3",
90
90
  "babel-plugin-transform-remove-console": "6.9.4",
91
- "chai": "4.4.1",
91
+ "chai": "4.3.10",
92
92
  "chart.js": "2.9.4",
93
93
  "chromedriver": "120.0.1",
94
94
  "doctoc": "2.2.1",
@@ -119,7 +119,7 @@
119
119
  "npm-run-all": "4.1.5",
120
120
  "prettier": "3.1.1",
121
121
  "promise-polyfill": "8.3.0",
122
- "rollup": "4.9.5",
122
+ "rollup": "4.9.4",
123
123
  "rollup-plugin-istanbul": "5.0.0",
124
124
  "sauce-connect-launcher": "1.3.2",
125
125
  "selenium-webdriver": "4.16.0",
@@ -128,7 +128,7 @@
128
128
  "sinon-chai": "3.7.0",
129
129
  "typescript": "5.3.3",
130
130
  "url-toolkit": "2.2.5",
131
- "wrangler": "3.24.0"
131
+ "wrangler": "3.22.4"
132
132
  },
133
- "version": "1.5.2-0.canary.9923"
133
+ "version": "1.5.2"
134
134
  }
@@ -286,7 +286,7 @@ class AbrController implements AbrComponentAPI {
286
286
  const level = levels[frag.level];
287
287
  const expectedLen =
288
288
  stats.total ||
289
- Math.max(stats.loaded, Math.round((duration * level.maxBitrate) / 8));
289
+ Math.max(stats.loaded, Math.round((duration * level.averageBitrate) / 8));
290
290
  let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading;
291
291
  if (timeStreaming < 1 && loadedFirstByte) {
292
292
  timeStreaming = Math.min(timeLoading, (stats.loaded * 8) / bwEstimate);
@@ -346,7 +346,7 @@ class AbrController implements AbrComponentAPI {
346
346
  // If there has been no loading progress, sample TTFB
347
347
  this.bwEstimator.sampleTTFB(timeLoading);
348
348
  }
349
- const nextLoadLevelBitrate = levels[nextLoadLevel].bitrate;
349
+ const nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate;
350
350
  if (
351
351
  this.getBwEstimate() * this.hls.config.abrBandWidthUpFactor >
352
352
  nextLoadLevelBitrate
@@ -7,10 +7,6 @@ import { ErrorDetails, ErrorTypes } from '../errors';
7
7
  import { ChunkMetadata } from '../types/transmuxer';
8
8
  import { appendUint8Array } from '../utils/mp4-tools';
9
9
  import { alignStream } from '../utils/discontinuities';
10
- import {
11
- isFullSegmentEncryption,
12
- getAesModeFromFullSegmentMethod,
13
- } from '../utils/encryption-methods-util';
14
10
  import {
15
11
  findFragmentByPDT,
16
12
  findFragmentByPTS,
@@ -101,6 +97,8 @@ export default class BaseStreamController
101
97
  protected startFragRequested: boolean = false;
102
98
  protected decrypter: Decrypter;
103
99
  protected initPTS: RationalTimestamp[] = [];
100
+ protected onvseeking: EventListener | null = null;
101
+ protected onvended: EventListener | null = null;
104
102
 
105
103
  private readonly logPrefix: string = '';
106
104
  protected log: (msg: any) => void;
@@ -199,8 +197,10 @@ export default class BaseStreamController
199
197
  data: MediaAttachedData,
200
198
  ) {
201
199
  const media = (this.media = this.mediaBuffer = data.media);
202
- media.addEventListener('seeking', this.onMediaSeeking);
203
- media.addEventListener('ended', this.onMediaEnded);
200
+ this.onvseeking = this.onMediaSeeking.bind(this) as EventListener;
201
+ this.onvended = this.onMediaEnded.bind(this) as EventListener;
202
+ media.addEventListener('seeking', this.onvseeking);
203
+ media.addEventListener('ended', this.onvended);
204
204
  const config = this.config;
205
205
  if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
206
206
  this.startLoad(config.startPosition);
@@ -215,9 +215,10 @@ export default class BaseStreamController
215
215
  }
216
216
 
217
217
  // remove video listeners
218
- if (media) {
219
- media.removeEventListener('seeking', this.onMediaSeeking);
220
- media.removeEventListener('ended', this.onMediaEnded);
218
+ if (media && this.onvseeking && this.onvended) {
219
+ media.removeEventListener('seeking', this.onvseeking);
220
+ media.removeEventListener('ended', this.onvended);
221
+ this.onvseeking = this.onvended = null;
221
222
  }
222
223
  if (this.keyLoader) {
223
224
  this.keyLoader.detach();
@@ -228,7 +229,7 @@ export default class BaseStreamController
228
229
  this.stopLoad();
229
230
  }
230
231
 
231
- protected onMediaSeeking = () => {
232
+ protected onMediaSeeking() {
232
233
  const { config, fragCurrent, media, mediaBuffer, state } = this;
233
234
  const currentTime: number = media ? media.currentTime : 0;
234
235
  const bufferInfo = BufferHelper.bufferInfo(
@@ -291,12 +292,12 @@ export default class BaseStreamController
291
292
 
292
293
  // Async tick to speed up processing
293
294
  this.tickImmediate();
294
- };
295
+ }
295
296
 
296
- protected onMediaEnded = () => {
297
+ protected onMediaEnded() {
297
298
  // reset startPosition and lastCurrentTime to restart playback @ stream beginning
298
299
  this.startPosition = this.lastCurrentTime = 0;
299
- };
300
+ }
300
301
 
301
302
  protected onManifestLoaded(
302
303
  event: Events.MANIFEST_LOADED,
@@ -311,7 +312,7 @@ export default class BaseStreamController
311
312
  this.stopLoad();
312
313
  super.onHandlerDestroying();
313
314
  // @ts-ignore
314
- this.hls = this.onMediaSeeking = this.onMediaEnded = null;
315
+ this.hls = null;
315
316
  }
316
317
 
317
318
  protected onHandlerDestroyed() {
@@ -485,7 +486,7 @@ export default class BaseStreamController
485
486
  payload.byteLength > 0 &&
486
487
  decryptData?.key &&
487
488
  decryptData.iv &&
488
- isFullSegmentEncryption(decryptData.method)
489
+ decryptData.method === 'AES-128'
489
490
  ) {
490
491
  const startTime = self.performance.now();
491
492
  // decrypt init segment data
@@ -494,7 +495,6 @@ export default class BaseStreamController
494
495
  new Uint8Array(payload),
495
496
  decryptData.key.buffer,
496
497
  decryptData.iv.buffer,
497
- getAesModeFromFullSegmentMethod(decryptData.method),
498
498
  )
499
499
  .catch((err) => {
500
500
  hls.trigger(Events.ERROR, {
@@ -110,12 +110,6 @@ export default class BufferController implements ComponentAPI {
110
110
  this.lastMpegAudioChunk = null;
111
111
  // @ts-ignore
112
112
  this.hls = null;
113
- // @ts-ignore
114
- this._onMediaSourceOpen = this._onMediaSourceClose = null;
115
- // @ts-ignore
116
- this._onMediaSourceEnded = null;
117
- // @ts-ignore
118
- this._onStartStreaming = this._onEndStreaming = null;
119
113
  }
120
114
 
121
115
  protected registerListeners() {
@@ -90,6 +90,8 @@ class EMEController implements ComponentAPI {
90
90
  private setMediaKeysQueue: Promise<void>[] = EMEController.CDMCleanupPromise
91
91
  ? [EMEController.CDMCleanupPromise]
92
92
  : [];
93
+ private onMediaEncrypted = this._onMediaEncrypted.bind(this);
94
+ private onWaitingForKey = this._onWaitingForKey.bind(this);
93
95
 
94
96
  private debug: (msg: any) => void = logger.debug.bind(logger, LOGGER_PREFIX);
95
97
  private log: (msg: any) => void = logger.log.bind(logger, LOGGER_PREFIX);
@@ -111,9 +113,13 @@ class EMEController implements ComponentAPI {
111
113
  config.licenseXhrSetup = config.licenseResponseCallback = undefined;
112
114
  config.drmSystems = config.drmSystemOptions = {};
113
115
  // @ts-ignore
114
- this.hls = this.config = this.keyIdToKeySessionPromise = null;
116
+ this.hls =
117
+ this.onMediaEncrypted =
118
+ this.onWaitingForKey =
119
+ this.keyIdToKeySessionPromise =
120
+ null as any;
115
121
  // @ts-ignore
116
- this.onMediaEncrypted = this.onWaitingForKey = null;
122
+ this.config = null;
117
123
  }
118
124
 
119
125
  private registerListeners() {
@@ -517,7 +523,7 @@ class EMEController implements ComponentAPI {
517
523
  return this.attemptKeySystemAccess(keySystemsToAttempt);
518
524
  }
519
525
 
520
- private onMediaEncrypted = (event: MediaEncryptedEvent) => {
526
+ private _onMediaEncrypted(event: MediaEncryptedEvent) {
521
527
  const { initDataType, initData } = event;
522
528
  this.debug(`"${event.type}" event: init data type: "${initDataType}"`);
523
529
 
@@ -633,11 +639,11 @@ class EMEController implements ComponentAPI {
633
639
  );
634
640
  }
635
641
  keySessionContextPromise.catch((error) => this.handleError(error));
636
- };
642
+ }
637
643
 
638
- private onWaitingForKey = (event: Event) => {
644
+ private _onWaitingForKey(event: Event) {
639
645
  this.log(`"${event.type}" event`);
640
- };
646
+ }
641
647
 
642
648
  private attemptSetMediaKeys(
643
649
  keySystem: KeySystems,
@@ -19,6 +19,7 @@ export default class LatencyController implements ComponentAPI {
19
19
  private currentTime: number = 0;
20
20
  private stallCount: number = 0;
21
21
  private _latency: number | null = null;
22
+ private timeupdateHandler = () => this.timeupdate();
22
23
 
23
24
  constructor(hls: Hls) {
24
25
  this.hls = hls;
@@ -125,7 +126,7 @@ export default class LatencyController implements ComponentAPI {
125
126
  this.onMediaDetaching();
126
127
  this.levelDetails = null;
127
128
  // @ts-ignore
128
- this.hls = this.onTimeupdate = null;
129
+ this.hls = this.timeupdateHandler = null;
129
130
  }
130
131
 
131
132
  private registerListeners() {
@@ -149,12 +150,12 @@ export default class LatencyController implements ComponentAPI {
149
150
  data: MediaAttachingData,
150
151
  ) {
151
152
  this.media = data.media;
152
- this.media.addEventListener('timeupdate', this.onTimeupdate);
153
+ this.media.addEventListener('timeupdate', this.timeupdateHandler);
153
154
  }
154
155
 
155
156
  private onMediaDetaching() {
156
157
  if (this.media) {
157
- this.media.removeEventListener('timeupdate', this.onTimeupdate);
158
+ this.media.removeEventListener('timeupdate', this.timeupdateHandler);
158
159
  this.media = null;
159
160
  }
160
161
  }
@@ -171,10 +172,10 @@ export default class LatencyController implements ComponentAPI {
171
172
  ) {
172
173
  this.levelDetails = details;
173
174
  if (details.advanced) {
174
- this.onTimeupdate();
175
+ this.timeupdate();
175
176
  }
176
177
  if (!details.live && this.media) {
177
- this.media.removeEventListener('timeupdate', this.onTimeupdate);
178
+ this.media.removeEventListener('timeupdate', this.timeupdateHandler);
178
179
  }
179
180
  }
180
181
 
@@ -190,7 +191,7 @@ export default class LatencyController implements ComponentAPI {
190
191
  }
191
192
  }
192
193
 
193
- private onTimeupdate = () => {
194
+ private timeupdate() {
194
195
  const { media, levelDetails } = this;
195
196
  if (!media || !levelDetails) {
196
197
  return;
@@ -241,7 +242,7 @@ export default class LatencyController implements ComponentAPI {
241
242
  } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
242
243
  media.playbackRate = 1;
243
244
  }
244
- };
245
+ }
245
246
 
246
247
  private estimateLiveEdge(): number | null {
247
248
  const { levelDetails } = this;
@@ -16,7 +16,6 @@ import {
16
16
  codecsSetSelectionPreferenceValue,
17
17
  convertAVC1ToAVCOTI,
18
18
  getCodecCompatibleName,
19
- getM2TSSupportedAudioTypes,
20
19
  videoCodecPreferenceValue,
21
20
  } from '../utils/codecs';
22
21
  import BasePlaylistController from './base-playlist-controller';
@@ -28,6 +27,8 @@ import type Hls from '../hls';
28
27
  import type { HlsUrlParameters, LevelParsed } from '../types/level';
29
28
  import type { MediaPlaylist } from '../types/media-playlist';
30
29
 
30
+ let chromeOrFirefox: boolean;
31
+
31
32
  export default class LevelController extends BasePlaylistController {
32
33
  private _levels: Level[] = [];
33
34
  private _firstLevel: number = -1;
@@ -118,12 +119,22 @@ export default class LevelController extends BasePlaylistController {
118
119
 
119
120
  data.levels.forEach((levelParsed: LevelParsed) => {
120
121
  const attributes = levelParsed.attrs;
122
+
123
+ // erase audio codec info if browser does not support mp4a.40.34.
124
+ // demuxer will autodetect codec and fallback to mpeg/audio
121
125
  let { audioCodec, videoCodec } = levelParsed;
126
+ if (audioCodec?.indexOf('mp4a.40.34') !== -1) {
127
+ chromeOrFirefox ||= /chrome|firefox/i.test(navigator.userAgent);
128
+ if (chromeOrFirefox) {
129
+ levelParsed.audioCodec = audioCodec = undefined;
130
+ }
131
+ }
132
+
122
133
  if (audioCodec) {
123
- // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
124
- levelParsed.audioCodec = audioCodec =
125
- getCodecCompatibleName(audioCodec, preferManagedMediaSource) ||
126
- undefined;
134
+ levelParsed.audioCodec = audioCodec = getCodecCompatibleName(
135
+ audioCodec,
136
+ preferManagedMediaSource,
137
+ );
127
138
  }
128
139
 
129
140
  if (videoCodec?.indexOf('avc1') === 0) {
@@ -296,8 +307,8 @@ export default class LevelController extends BasePlaylistController {
296
307
  return valueB - valueA;
297
308
  }
298
309
  }
299
- if (a.bitrate !== b.bitrate) {
300
- return a.bitrate - b.bitrate;
310
+ if (a.averageBitrate !== b.averageBitrate) {
311
+ return a.averageBitrate - b.averageBitrate;
301
312
  }
302
313
  return 0;
303
314
  });
@@ -49,6 +49,8 @@ export default class StreamController
49
49
  private altAudio: boolean = false;
50
50
  private audioOnly: boolean = false;
51
51
  private fragPlaying: Fragment | null = null;
52
+ private onvplaying: EventListener | null = null;
53
+ private onvseeked: EventListener | null = null;
52
54
  private fragLastKbps: number = 0;
53
55
  private couldBacktrack: boolean = false;
54
56
  private backtrackFragment: Fragment | null = null;
@@ -115,8 +117,6 @@ export default class StreamController
115
117
 
116
118
  protected onHandlerDestroying() {
117
119
  this._unregisterListeners();
118
- // @ts-ignore
119
- this.onMediaPlaying = this.onMediaSeeked = null;
120
120
  super.onHandlerDestroying();
121
121
  }
122
122
 
@@ -515,8 +515,10 @@ export default class StreamController
515
515
  ) {
516
516
  super.onMediaAttached(event, data);
517
517
  const media = data.media;
518
- media.addEventListener('playing', this.onMediaPlaying);
519
- media.addEventListener('seeked', this.onMediaSeeked);
518
+ this.onvplaying = this.onMediaPlaying.bind(this);
519
+ this.onvseeked = this.onMediaSeeked.bind(this);
520
+ media.addEventListener('playing', this.onvplaying as EventListener);
521
+ media.addEventListener('seeked', this.onvseeked as EventListener);
520
522
  this.gapController = new GapController(
521
523
  this.config,
522
524
  media,
@@ -527,9 +529,10 @@ export default class StreamController
527
529
 
528
530
  protected onMediaDetaching() {
529
531
  const { media } = this;
530
- if (media) {
531
- media.removeEventListener('playing', this.onMediaPlaying);
532
- media.removeEventListener('seeked', this.onMediaSeeked);
532
+ if (media && this.onvplaying && this.onvseeked) {
533
+ media.removeEventListener('playing', this.onvplaying);
534
+ media.removeEventListener('seeked', this.onvseeked);
535
+ this.onvplaying = this.onvseeked = null;
533
536
  this.videoBuffer = null;
534
537
  }
535
538
  this.fragPlaying = null;
@@ -540,12 +543,12 @@ export default class StreamController
540
543
  super.onMediaDetaching();
541
544
  }
542
545
 
543
- private onMediaPlaying = () => {
546
+ private onMediaPlaying() {
544
547
  // tick to speed up FRAG_CHANGED triggering
545
548
  this.tick();
546
- };
549
+ }
547
550
 
548
- private onMediaSeeked = () => {
551
+ private onMediaSeeked() {
549
552
  const media = this.media;
550
553
  const currentTime = media ? media.currentTime : null;
551
554
  if (Number.isFinite(currentTime)) {
@@ -565,7 +568,7 @@ export default class StreamController
565
568
 
566
569
  // tick to speed up FRAG_CHANGED triggering
567
570
  this.tick();
568
- };
571
+ }
569
572
 
570
573
  private onManifestLoading() {
571
574
  // reset buffer on manifest loading
@@ -9,10 +9,6 @@ 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';
@@ -364,7 +360,7 @@ export class SubtitleStreamController
364
360
  payload.byteLength > 0 &&
365
361
  decryptData?.key &&
366
362
  decryptData.iv &&
367
- isFullSegmentEncryption(decryptData.method)
363
+ decryptData.method === 'AES-128'
368
364
  ) {
369
365
  const startTime = performance.now();
370
366
  // decrypt the subtitles
@@ -373,7 +369,6 @@ export class SubtitleStreamController
373
369
  new Uint8Array(payload),
374
370
  decryptData.key.buffer,
375
371
  decryptData.iv.buffer,
376
- getAesModeFromFullSegmentMethod(decryptData.method),
377
372
  )
378
373
  .catch((err) => {
379
374
  hls.trigger(Events.ERROR, {
@@ -35,12 +35,11 @@ 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
44
  super(hls, '[subtitle-track-controller]');
46
45
  this.registerListeners();
@@ -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
 
@@ -136,12 +136,17 @@ export class TimelineController implements ComponentAPI {
136
136
  }
137
137
 
138
138
  private initCea608Parsers() {
139
- const channel1 = new OutputFilter(this, 'textTrack1');
140
- const channel2 = new OutputFilter(this, 'textTrack2');
141
- const channel3 = new OutputFilter(this, 'textTrack3');
142
- const channel4 = new OutputFilter(this, 'textTrack4');
143
- this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
144
- 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
+ }
145
150
  }
146
151
 
147
152
  public addCues(
@@ -463,19 +468,21 @@ export class TimelineController implements ComponentAPI {
463
468
  }
464
469
 
465
470
  private onFragLoading(event: Events.FRAG_LOADING, data: FragLoadingData) {
471
+ this.initCea608Parsers();
472
+ const { cea608Parser1, cea608Parser2, lastCc, lastSn, lastPartIndex } =
473
+ this;
474
+ if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
475
+ return;
476
+ }
466
477
  // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack
467
- if (this.enabled && data.frag.type === PlaylistLevelType.MAIN) {
468
- const { cea608Parser1, cea608Parser2, lastSn } = this;
469
- if (!cea608Parser1 || !cea608Parser2) {
470
- return;
471
- }
478
+ if (data.frag.type === PlaylistLevelType.MAIN) {
472
479
  const { cc, sn } = data.frag;
473
- const partIndex = data.part?.index ?? -1;
480
+ const partIndex = data?.part?.index ?? -1;
474
481
  if (
475
482
  !(
476
483
  sn === lastSn + 1 ||
477
- (sn === lastSn && partIndex === this.lastPartIndex + 1) ||
478
- cc === this.lastCc
484
+ (sn === lastSn && partIndex === lastPartIndex + 1) ||
485
+ cc === lastCc
479
486
  )
480
487
  ) {
481
488
  cea608Parser1.reset();
@@ -662,7 +669,9 @@ export class TimelineController implements ComponentAPI {
662
669
  event: Events.FRAG_PARSING_USERDATA,
663
670
  data: FragParsingUserdataData,
664
671
  ) {
665
- if (!this.enabled || !this.config.enableCEA708Captions) {
672
+ this.initCea608Parsers();
673
+ const { cea608Parser1, cea608Parser2 } = this;
674
+ if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
666
675
  return;
667
676
  }
668
677
  const { frag, samples } = data;
@@ -677,12 +686,9 @@ export class TimelineController implements ComponentAPI {
677
686
  for (let i = 0; i < samples.length; i++) {
678
687
  const ccBytes = samples[i].bytes;
679
688
  if (ccBytes) {
680
- if (!this.cea608Parser1) {
681
- this.initCea608Parsers();
682
- }
683
689
  const ccdatas = this.extractCea608Data(ccBytes);
684
- this.cea608Parser1!.addData(samples[i].pts, ccdatas[0]);
685
- this.cea608Parser2!.addData(samples[i].pts, ccdatas[1]);
690
+ cea608Parser1.addData(samples[i].pts, ccdatas[0]);
691
+ cea608Parser2.addData(samples[i].pts, ccdatas[1]);
686
692
  }
687
693
  }
688
694
  }
@@ -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
  }