hls.js 1.5.2-0.canary.9924 → 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 (57) hide show
  1. package/dist/hls-demo.js +0 -5
  2. package/dist/hls-demo.js.map +1 -1
  3. package/dist/hls.js +686 -762
  4. package/dist/hls.js.d.ts +47 -49
  5. package/dist/hls.js.map +1 -1
  6. package/dist/hls.light.js +471 -563
  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 +329 -409
  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 +500 -559
  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/config.ts +2 -3
  20. package/src/controller/abr-controller.ts +22 -23
  21. package/src/controller/audio-stream-controller.ts +14 -11
  22. package/src/controller/audio-track-controller.ts +1 -1
  23. package/src/controller/base-playlist-controller.ts +7 -7
  24. package/src/controller/base-stream-controller.ts +29 -42
  25. package/src/controller/buffer-controller.ts +11 -10
  26. package/src/controller/cap-level-controller.ts +2 -1
  27. package/src/controller/content-steering-controller.ts +6 -8
  28. package/src/controller/eme-controller.ts +22 -9
  29. package/src/controller/error-controller.ts +8 -6
  30. package/src/controller/fps-controller.ts +3 -2
  31. package/src/controller/gap-controller.ts +10 -16
  32. package/src/controller/latency-controller.ts +11 -9
  33. package/src/controller/level-controller.ts +19 -8
  34. package/src/controller/stream-controller.ts +29 -20
  35. package/src/controller/subtitle-stream-controller.ts +14 -13
  36. package/src/controller/subtitle-track-controller.ts +3 -5
  37. package/src/controller/timeline-controller.ts +30 -23
  38. package/src/crypt/aes-crypto.ts +2 -21
  39. package/src/crypt/decrypter.ts +18 -32
  40. package/src/crypt/fast-aes-key.ts +5 -24
  41. package/src/demux/audio/adts.ts +4 -9
  42. package/src/demux/sample-aes.ts +0 -2
  43. package/src/demux/transmuxer-interface.ts +12 -4
  44. package/src/demux/transmuxer-worker.ts +4 -4
  45. package/src/demux/transmuxer.ts +3 -16
  46. package/src/demux/tsdemuxer.ts +17 -12
  47. package/src/hls.ts +20 -32
  48. package/src/loader/fragment-loader.ts +2 -9
  49. package/src/loader/key-loader.ts +0 -2
  50. package/src/loader/level-key.ts +9 -10
  51. package/src/remux/mp4-remuxer.ts +3 -4
  52. package/src/task-loop.ts +2 -5
  53. package/src/types/demuxer.ts +0 -1
  54. package/src/utils/codecs.ts +4 -33
  55. package/src/utils/logger.ts +24 -53
  56. package/src/crypt/decrypter-aes-mode.ts +0 -4
  57. package/src/utils/encryption-methods-util.ts +0 -21
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { Events } from '../events';
7
7
  import { ErrorTypes, ErrorDetails } from '../errors';
8
- import { Logger } from '../utils/logger';
8
+ import { logger } from '../utils/logger';
9
9
  import {
10
10
  getKeySystemsForConfig,
11
11
  getSupportedMediaKeySystemConfigurations,
@@ -41,6 +41,9 @@ import type {
41
41
  LoaderConfiguration,
42
42
  LoaderContext,
43
43
  } from '../types/loader';
44
+
45
+ const LOGGER_PREFIX = '[eme]';
46
+
44
47
  interface KeySystemAccessPromises {
45
48
  keySystemAccess: Promise<MediaKeySystemAccess>;
46
49
  mediaKeys?: Promise<MediaKeys>;
@@ -65,7 +68,7 @@ export interface MediaKeySessionContext {
65
68
  * @class
66
69
  * @constructor
67
70
  */
68
- class EMEController extends Logger implements ComponentAPI {
71
+ class EMEController implements ComponentAPI {
69
72
  public static CDMCleanupPromise: Promise<void> | void;
70
73
 
71
74
  private readonly hls: Hls;
@@ -87,9 +90,15 @@ class EMEController extends Logger implements ComponentAPI {
87
90
  private setMediaKeysQueue: Promise<void>[] = EMEController.CDMCleanupPromise
88
91
  ? [EMEController.CDMCleanupPromise]
89
92
  : [];
93
+ private onMediaEncrypted = this._onMediaEncrypted.bind(this);
94
+ private onWaitingForKey = this._onWaitingForKey.bind(this);
95
+
96
+ private debug: (msg: any) => void = logger.debug.bind(logger, LOGGER_PREFIX);
97
+ private log: (msg: any) => void = logger.log.bind(logger, LOGGER_PREFIX);
98
+ private warn: (msg: any) => void = logger.warn.bind(logger, LOGGER_PREFIX);
99
+ private error: (msg: any) => void = logger.error.bind(logger, LOGGER_PREFIX);
90
100
 
91
101
  constructor(hls: Hls) {
92
- super('eme', hls.logger);
93
102
  this.hls = hls;
94
103
  this.config = hls.config;
95
104
  this.registerListeners();
@@ -104,9 +113,13 @@ class EMEController extends Logger implements ComponentAPI {
104
113
  config.licenseXhrSetup = config.licenseResponseCallback = undefined;
105
114
  config.drmSystems = config.drmSystemOptions = {};
106
115
  // @ts-ignore
107
- this.hls = this.config = this.keyIdToKeySessionPromise = null;
116
+ this.hls =
117
+ this.onMediaEncrypted =
118
+ this.onWaitingForKey =
119
+ this.keyIdToKeySessionPromise =
120
+ null as any;
108
121
  // @ts-ignore
109
- this.onMediaEncrypted = this.onWaitingForKey = null;
122
+ this.config = null;
110
123
  }
111
124
 
112
125
  private registerListeners() {
@@ -510,7 +523,7 @@ class EMEController extends Logger implements ComponentAPI {
510
523
  return this.attemptKeySystemAccess(keySystemsToAttempt);
511
524
  }
512
525
 
513
- private onMediaEncrypted = (event: MediaEncryptedEvent) => {
526
+ private _onMediaEncrypted(event: MediaEncryptedEvent) {
514
527
  const { initDataType, initData } = event;
515
528
  this.debug(`"${event.type}" event: init data type: "${initDataType}"`);
516
529
 
@@ -626,11 +639,11 @@ class EMEController extends Logger implements ComponentAPI {
626
639
  );
627
640
  }
628
641
  keySessionContextPromise.catch((error) => this.handleError(error));
629
- };
642
+ }
630
643
 
631
- private onWaitingForKey = (event: Event) => {
644
+ private _onWaitingForKey(event: Event) {
632
645
  this.log(`"${event.type}" event`);
633
- };
646
+ }
634
647
 
635
648
  private attemptSetMediaKeys(
636
649
  keySystem: KeySystems,
@@ -8,7 +8,7 @@ import {
8
8
  } from '../utils/error-helper';
9
9
  import { findFragmentByPTS } from './fragment-finders';
10
10
  import { HdcpLevel, HdcpLevels } from '../types/level';
11
- import { Logger } from '../utils/logger';
11
+ import { logger } from '../utils/logger';
12
12
  import type Hls from '../hls';
13
13
  import type { RetryConfig } from '../config';
14
14
  import type { NetworkComponentAPI } from '../types/component-api';
@@ -50,17 +50,19 @@ type PenalizedRendition = {
50
50
 
51
51
  type PenalizedRenditions = { [key: number]: PenalizedRendition };
52
52
 
53
- export default class ErrorController
54
- extends Logger
55
- implements NetworkComponentAPI
56
- {
53
+ export default class ErrorController implements NetworkComponentAPI {
57
54
  private readonly hls: Hls;
58
55
  private playlistError: number = 0;
59
56
  private penalizedRenditions: PenalizedRenditions = {};
57
+ private log: (msg: any) => void;
58
+ private warn: (msg: any) => void;
59
+ private error: (msg: any) => void;
60
60
 
61
61
  constructor(hls: Hls) {
62
- super('error-controller', hls.logger);
63
62
  this.hls = hls;
63
+ this.log = logger.log.bind(logger, `[info]:`);
64
+ this.warn = logger.warn.bind(logger, `[warning]:`);
65
+ this.error = logger.error.bind(logger, `[error]:`);
64
66
  this.registerListeners();
65
67
  }
66
68
 
@@ -1,4 +1,5 @@
1
1
  import { Events } from '../events';
2
+ import { logger } from '../utils/logger';
2
3
  import type { ComponentAPI } from '../types/component-api';
3
4
  import type Hls from '../hls';
4
5
  import type { MediaAttachingData } from '../types/events';
@@ -83,13 +84,13 @@ class FPSController implements ComponentAPI {
83
84
  totalDroppedFrames: droppedFrames,
84
85
  });
85
86
  if (droppedFPS > 0) {
86
- // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
87
+ // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
87
88
  if (
88
89
  currentDropped >
89
90
  hls.config.fpsDroppedMonitoringThreshold * currentDecoded
90
91
  ) {
91
92
  let currentLevel = hls.currentLevel;
92
- hls.logger.warn(
93
+ logger.warn(
93
94
  'drop FPS ratio greater than max allowed value for currentLevel: ' +
94
95
  currentLevel,
95
96
  );
@@ -1,10 +1,10 @@
1
+ import type { BufferInfo } from '../utils/buffer-helper';
1
2
  import { BufferHelper } from '../utils/buffer-helper';
2
3
  import { ErrorTypes, ErrorDetails } from '../errors';
3
4
  import { PlaylistLevelType } from '../types/loader';
4
5
  import { Events } from '../events';
5
- import { Logger } from '../utils/logger';
6
+ import { logger } from '../utils/logger';
6
7
  import type Hls from '../hls';
7
- import type { BufferInfo } from '../utils/buffer-helper';
8
8
  import type { HlsConfig } from '../config';
9
9
  import type { Fragment } from '../loader/fragment';
10
10
  import type { FragmentTracker } from './fragment-tracker';
@@ -14,7 +14,7 @@ export const MAX_START_GAP_JUMP = 2.0;
14
14
  export const SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
15
15
  export const SKIP_BUFFER_RANGE_START = 0.05;
16
16
 
17
- export default class GapController extends Logger {
17
+ export default class GapController {
18
18
  private config: HlsConfig;
19
19
  private media: HTMLMediaElement | null = null;
20
20
  private fragmentTracker: FragmentTracker;
@@ -25,13 +25,7 @@ export default class GapController extends Logger {
25
25
  private moved: boolean = false;
26
26
  private seeking: boolean = false;
27
27
 
28
- constructor(
29
- config: HlsConfig,
30
- media: HTMLMediaElement,
31
- fragmentTracker: FragmentTracker,
32
- hls: Hls,
33
- ) {
34
- super('gap-controller', hls.logger);
28
+ constructor(config, media, fragmentTracker, hls) {
35
29
  this.config = config;
36
30
  this.media = media;
37
31
  this.fragmentTracker = fragmentTracker;
@@ -71,7 +65,7 @@ export default class GapController extends Logger {
71
65
  // The playhead is now moving, but was previously stalled
72
66
  if (this.stallReported) {
73
67
  const stalledDuration = self.performance.now() - stalled;
74
- this.warn(
68
+ logger.warn(
75
69
  `playback not stuck anymore @${currentTime}, after ${Math.round(
76
70
  stalledDuration,
77
71
  )}ms`,
@@ -212,7 +206,7 @@ export default class GapController extends Logger {
212
206
  bufferInfo.nextStart - currentTime < config.maxBufferHole)) &&
213
207
  stalledDurationMs > config.highBufferWatchdogPeriod * 1000
214
208
  ) {
215
- this.warn('Trying to nudge playhead over buffer-hole');
209
+ logger.warn('Trying to nudge playhead over buffer-hole');
216
210
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
217
211
  // We only try to jump the hole if it's under the configured size
218
212
  // Reset stalled so to rearm watchdog timer
@@ -236,7 +230,7 @@ export default class GapController extends Logger {
236
230
  media.currentTime
237
231
  } due to low buffer (${JSON.stringify(bufferInfo)})`,
238
232
  );
239
- this.warn(error.message);
233
+ logger.warn(error.message);
240
234
  hls.trigger(Events.ERROR, {
241
235
  type: ErrorTypes.MEDIA_ERROR,
242
236
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -311,7 +305,7 @@ export default class GapController extends Logger {
311
305
  startTime + SKIP_BUFFER_RANGE_START,
312
306
  currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS,
313
307
  );
314
- this.warn(
308
+ logger.warn(
315
309
  `skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`,
316
310
  );
317
311
  this.moved = true;
@@ -354,7 +348,7 @@ export default class GapController extends Logger {
354
348
  const error = new Error(
355
349
  `Nudging 'currentTime' from ${currentTime} to ${targetTime}`,
356
350
  );
357
- this.warn(error.message);
351
+ logger.warn(error.message);
358
352
  media.currentTime = targetTime;
359
353
  hls.trigger(Events.ERROR, {
360
354
  type: ErrorTypes.MEDIA_ERROR,
@@ -366,7 +360,7 @@ export default class GapController extends Logger {
366
360
  const error = new Error(
367
361
  `Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`,
368
362
  );
369
- this.error(error.message);
363
+ logger.error(error.message);
370
364
  hls.trigger(Events.ERROR, {
371
365
  type: ErrorTypes.MEDIA_ERROR,
372
366
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -6,6 +6,7 @@ import type {
6
6
  LevelUpdatedData,
7
7
  MediaAttachingData,
8
8
  } from '../types/events';
9
+ import { logger } from '../utils/logger';
9
10
  import type { ComponentAPI } from '../types/component-api';
10
11
  import type Hls from '../hls';
11
12
  import type { HlsConfig } from '../config';
@@ -18,6 +19,7 @@ export default class LatencyController implements ComponentAPI {
18
19
  private currentTime: number = 0;
19
20
  private stallCount: number = 0;
20
21
  private _latency: number | null = null;
22
+ private timeupdateHandler = () => this.timeupdate();
21
23
 
22
24
  constructor(hls: Hls) {
23
25
  this.hls = hls;
@@ -124,7 +126,7 @@ export default class LatencyController implements ComponentAPI {
124
126
  this.onMediaDetaching();
125
127
  this.levelDetails = null;
126
128
  // @ts-ignore
127
- this.hls = null;
129
+ this.hls = this.timeupdateHandler = null;
128
130
  }
129
131
 
130
132
  private registerListeners() {
@@ -148,12 +150,12 @@ export default class LatencyController implements ComponentAPI {
148
150
  data: MediaAttachingData,
149
151
  ) {
150
152
  this.media = data.media;
151
- this.media.addEventListener('timeupdate', this.onTimeupdate);
153
+ this.media.addEventListener('timeupdate', this.timeupdateHandler);
152
154
  }
153
155
 
154
156
  private onMediaDetaching() {
155
157
  if (this.media) {
156
- this.media.removeEventListener('timeupdate', this.onTimeupdate);
158
+ this.media.removeEventListener('timeupdate', this.timeupdateHandler);
157
159
  this.media = null;
158
160
  }
159
161
  }
@@ -170,10 +172,10 @@ export default class LatencyController implements ComponentAPI {
170
172
  ) {
171
173
  this.levelDetails = details;
172
174
  if (details.advanced) {
173
- this.onTimeupdate();
175
+ this.timeupdate();
174
176
  }
175
177
  if (!details.live && this.media) {
176
- this.media.removeEventListener('timeupdate', this.onTimeupdate);
178
+ this.media.removeEventListener('timeupdate', this.timeupdateHandler);
177
179
  }
178
180
  }
179
181
 
@@ -183,13 +185,13 @@ export default class LatencyController implements ComponentAPI {
183
185
  }
184
186
  this.stallCount++;
185
187
  if (this.levelDetails?.live) {
186
- this.hls.logger.warn(
187
- '[latency-controller]: Stall detected, adjusting target latency',
188
+ logger.warn(
189
+ '[playback-rate-controller]: Stall detected, adjusting target latency',
188
190
  );
189
191
  }
190
192
  }
191
193
 
192
- private onTimeupdate = () => {
194
+ private timeupdate() {
193
195
  const { media, levelDetails } = this;
194
196
  if (!media || !levelDetails) {
195
197
  return;
@@ -240,7 +242,7 @@ export default class LatencyController implements ComponentAPI {
240
242
  } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
241
243
  media.playbackRate = 1;
242
244
  }
243
- };
245
+ }
244
246
 
245
247
  private estimateLiveEdge(): number | null {
246
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;
@@ -44,7 +45,7 @@ export default class LevelController extends BasePlaylistController {
44
45
  hls: Hls,
45
46
  contentSteeringController: ContentSteeringController | null,
46
47
  ) {
47
- super(hls, 'level-controller');
48
+ super(hls, '[level-controller]');
48
49
  this.steering = contentSteeringController;
49
50
  this._registerListeners();
50
51
  }
@@ -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;
@@ -64,15 +66,17 @@ export default class StreamController
64
66
  hls,
65
67
  fragmentTracker,
66
68
  keyLoader,
67
- 'stream-controller',
69
+ '[stream-controller]',
68
70
  PlaylistLevelType.MAIN,
69
71
  );
70
- this.registerListeners();
72
+ this._registerListeners();
71
73
  }
72
74
 
73
- protected registerListeners() {
74
- super.registerListeners();
75
+ private _registerListeners() {
75
76
  const { hls } = this;
77
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
78
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
79
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
76
80
  hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
77
81
  hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
78
82
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
@@ -81,6 +85,7 @@ export default class StreamController
81
85
  this.onFragLoadEmergencyAborted,
82
86
  this,
83
87
  );
88
+ hls.on(Events.ERROR, this.onError, this);
84
89
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
85
90
  hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
86
91
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -89,9 +94,11 @@ export default class StreamController
89
94
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
90
95
  }
91
96
 
92
- protected unregisterListeners() {
93
- super.unregisterListeners();
97
+ protected _unregisterListeners() {
94
98
  const { hls } = this;
99
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
100
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
101
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
95
102
  hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
96
103
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
97
104
  hls.off(
@@ -99,6 +106,7 @@ export default class StreamController
99
106
  this.onFragLoadEmergencyAborted,
100
107
  this,
101
108
  );
109
+ hls.off(Events.ERROR, this.onError, this);
102
110
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
103
111
  hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
104
112
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -108,9 +116,7 @@ export default class StreamController
108
116
  }
109
117
 
110
118
  protected onHandlerDestroying() {
111
- // @ts-ignore
112
- this.onMediaPlaying = this.onMediaSeeked = null;
113
- this.unregisterListeners();
119
+ this._unregisterListeners();
114
120
  super.onHandlerDestroying();
115
121
  }
116
122
 
@@ -509,8 +515,10 @@ export default class StreamController
509
515
  ) {
510
516
  super.onMediaAttached(event, data);
511
517
  const media = data.media;
512
- media.addEventListener('playing', this.onMediaPlaying);
513
- 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);
514
522
  this.gapController = new GapController(
515
523
  this.config,
516
524
  media,
@@ -521,9 +529,10 @@ export default class StreamController
521
529
 
522
530
  protected onMediaDetaching() {
523
531
  const { media } = this;
524
- if (media) {
525
- media.removeEventListener('playing', this.onMediaPlaying);
526
- 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;
527
536
  this.videoBuffer = null;
528
537
  }
529
538
  this.fragPlaying = null;
@@ -534,12 +543,12 @@ export default class StreamController
534
543
  super.onMediaDetaching();
535
544
  }
536
545
 
537
- private onMediaPlaying = () => {
546
+ private onMediaPlaying() {
538
547
  // tick to speed up FRAG_CHANGED triggering
539
548
  this.tick();
540
- };
549
+ }
541
550
 
542
- private onMediaSeeked = () => {
551
+ private onMediaSeeked() {
543
552
  const media = this.media;
544
553
  const currentTime = media ? media.currentTime : null;
545
554
  if (Number.isFinite(currentTime)) {
@@ -559,9 +568,9 @@ export default class StreamController
559
568
 
560
569
  // tick to speed up FRAG_CHANGED triggering
561
570
  this.tick();
562
- };
571
+ }
563
572
 
564
- protected onManifestLoading() {
573
+ private onManifestLoading() {
565
574
  // reset buffer on manifest loading
566
575
  this.log('Trigger BUFFER_RESET');
567
576
  this.hls.trigger(Events.BUFFER_RESET, undefined);
@@ -874,7 +883,7 @@ export default class StreamController
874
883
  this.fragBufferedComplete(frag, part);
875
884
  }
876
885
 
877
- protected onError(event: Events.ERROR, data: ErrorData) {
886
+ private onError(event: Events.ERROR, data: ErrorData) {
878
887
  if (data.fatal) {
879
888
  this.state = State.ERROR;
880
889
  return;
@@ -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';
@@ -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);
@@ -358,7 +360,7 @@ export class SubtitleStreamController
358
360
  payload.byteLength > 0 &&
359
361
  decryptData?.key &&
360
362
  decryptData.iv &&
361
- isFullSegmentEncryption(decryptData.method)
363
+ decryptData.method === 'AES-128'
362
364
  ) {
363
365
  const startTime = performance.now();
364
366
  // decrypt the subtitles
@@ -367,7 +369,6 @@ export class SubtitleStreamController
367
369
  new Uint8Array(payload),
368
370
  decryptData.key.buffer,
369
371
  decryptData.iv.buffer,
370
- getAesModeFromFullSegmentMethod(decryptData.method),
371
372
  )
372
373
  .catch((err) => {
373
374
  hls.trigger(Events.ERROR, {
@@ -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