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
package/dist/hls.mjs CHANGED
@@ -369,23 +369,6 @@ let ErrorDetails = /*#__PURE__*/function (ErrorDetails) {
369
369
  return ErrorDetails;
370
370
  }({});
371
371
 
372
- class Logger {
373
- constructor(label, logger) {
374
- this.trace = void 0;
375
- this.debug = void 0;
376
- this.log = void 0;
377
- this.warn = void 0;
378
- this.info = void 0;
379
- this.error = void 0;
380
- const lb = `[${label}]:`;
381
- this.trace = noop;
382
- this.debug = logger.debug.bind(null, lb);
383
- this.log = logger.log.bind(null, lb);
384
- this.warn = logger.warn.bind(null, lb);
385
- this.info = logger.info.bind(null, lb);
386
- this.error = logger.error.bind(null, lb);
387
- }
388
- }
389
372
  const noop = function noop() {};
390
373
  const fakeLogger = {
391
374
  trace: noop,
@@ -395,9 +378,7 @@ const fakeLogger = {
395
378
  info: noop,
396
379
  error: noop
397
380
  };
398
- function createLogger() {
399
- return _extends({}, fakeLogger);
400
- }
381
+ let exportedLogger = fakeLogger;
401
382
 
402
383
  // let lastCallTime;
403
384
  // function formatMsgWithTimeInfo(type, msg) {
@@ -408,36 +389,35 @@ function createLogger() {
408
389
  // return msg;
409
390
  // }
410
391
 
411
- function consolePrintFn(type, id) {
392
+ function consolePrintFn(type) {
412
393
  const func = self.console[type];
413
- return func ? func.bind(self.console, `${id ? '[' + id + '] ' : ''}[${type}] >`) : noop;
394
+ if (func) {
395
+ return func.bind(self.console, `[${type}] >`);
396
+ }
397
+ return noop;
414
398
  }
415
- function getLoggerFn(key, debugConfig, id) {
416
- return debugConfig[key] ? debugConfig[key].bind(debugConfig) : consolePrintFn(key, id);
399
+ function exportLoggerFunctions(debugConfig, ...functions) {
400
+ functions.forEach(function (type) {
401
+ exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
402
+ });
417
403
  }
418
- let exportedLogger = createLogger();
419
- function enableLogs(debugConfig, context, id) {
404
+ function enableLogs(debugConfig, id) {
420
405
  // check that console is available
421
- const newLogger = createLogger();
422
406
  if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') {
423
- const keys = [
407
+ exportLoggerFunctions(debugConfig,
424
408
  // Remove out from list here to hard-disable a log-level
425
409
  // 'trace',
426
- 'debug', 'log', 'info', 'warn', 'error'];
427
- keys.forEach(key => {
428
- newLogger[key] = getLoggerFn(key, debugConfig, id);
429
- });
410
+ 'debug', 'log', 'info', 'warn', 'error');
430
411
  // Some browsers don't allow to use bind on console object anyway
431
412
  // fallback to default if needed
432
413
  try {
433
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.2-0.canary.9924"}`);
414
+ exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.5.2"}`);
434
415
  } catch (e) {
435
- /* log fn threw an exception. All logger methods are no-ops. */
436
- return createLogger();
416
+ exportedLogger = fakeLogger;
437
417
  }
418
+ } else {
419
+ exportedLogger = fakeLogger;
438
420
  }
439
- exportedLogger = newLogger;
440
- return newLogger;
441
421
  }
442
422
  const logger = exportedLogger;
443
423
 
@@ -1056,26 +1036,6 @@ function strToUtf8array(str) {
1056
1036
  return Uint8Array.from(unescape(encodeURIComponent(str)), c => c.charCodeAt(0));
1057
1037
  }
1058
1038
 
1059
- var DecrypterAesMode = {
1060
- cbc: 0,
1061
- ctr: 1
1062
- };
1063
-
1064
- function isFullSegmentEncryption(method) {
1065
- return method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR';
1066
- }
1067
- function getAesModeFromFullSegmentMethod(method) {
1068
- switch (method) {
1069
- case 'AES-128':
1070
- case 'AES-256':
1071
- return DecrypterAesMode.cbc;
1072
- case 'AES-256-CTR':
1073
- return DecrypterAesMode.ctr;
1074
- default:
1075
- throw new Error(`invalid full segment method ${method}`);
1076
- }
1077
- }
1078
-
1079
1039
  /** returns `undefined` is `self` is missing, e.g. in node */
1080
1040
  const optionalSelf = typeof self !== 'undefined' ? self : undefined;
1081
1041
 
@@ -2714,12 +2674,12 @@ class LevelKey {
2714
2674
  this.keyFormatVersions = formatversions;
2715
2675
  this.iv = iv;
2716
2676
  this.encrypted = method ? method !== 'NONE' : false;
2717
- this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method);
2677
+ this.isCommonEncryption = this.encrypted && method !== 'AES-128';
2718
2678
  }
2719
2679
  isSupported() {
2720
2680
  // If it's Segment encryption or No encryption, just select that key system
2721
2681
  if (this.method) {
2722
- if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
2682
+ if (this.method === 'AES-128' || this.method === 'NONE') {
2723
2683
  return true;
2724
2684
  }
2725
2685
  if (this.keyFormat === 'identity') {
@@ -2741,13 +2701,14 @@ class LevelKey {
2741
2701
  if (!this.encrypted || !this.uri) {
2742
2702
  return null;
2743
2703
  }
2744
- if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
2704
+ if (this.method === 'AES-128' && this.uri && !this.iv) {
2745
2705
  if (typeof sn !== 'number') {
2746
2706
  // We are fetching decryption data for a initialization segment
2747
- // If the segment was encrypted with AES-128/256
2707
+ // If the segment was encrypted with AES-128
2748
2708
  // It must have an IV defined. We cannot substitute the Segment Number in.
2749
- logger.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`);
2750
-
2709
+ if (this.method === 'AES-128' && !this.iv) {
2710
+ logger.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`);
2711
+ }
2751
2712
  // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
2752
2713
  sn = 0;
2753
2714
  }
@@ -3026,28 +2987,23 @@ function getCodecCompatibleNameLower(lowerCaseCodec, preferManagedMediaSource =
3026
2987
  if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
3027
2988
  return CODEC_COMPATIBLE_NAMES[lowerCaseCodec];
3028
2989
  }
2990
+
2991
+ // Idealy fLaC and Opus would be first (spec-compliant) but
2992
+ // some browsers will report that fLaC is supported then fail.
2993
+ // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
3029
2994
  const codecsToCheck = {
3030
- // Idealy fLaC and Opus would be first (spec-compliant) but
3031
- // some browsers will report that fLaC is supported then fail.
3032
- // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
3033
2995
  flac: ['flac', 'fLaC', 'FLAC'],
3034
- opus: ['opus', 'Opus'],
3035
- // Replace audio codec info if browser does not support mp4a.40.34,
3036
- // and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
3037
- 'mp4a.40.34': ['mp3']
2996
+ opus: ['opus', 'Opus']
3038
2997
  }[lowerCaseCodec];
3039
2998
  for (let i = 0; i < codecsToCheck.length; i++) {
3040
- var _getMediaSource;
3041
2999
  if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) {
3042
3000
  CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
3043
3001
  return codecsToCheck[i];
3044
- } else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) {
3045
- return '';
3046
3002
  }
3047
3003
  }
3048
3004
  return lowerCaseCodec;
3049
3005
  }
3050
- const AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
3006
+ const AUDIO_CODEC_REGEXP = /flac|opus/i;
3051
3007
  function getCodecCompatibleName(codec, preferManagedMediaSource = true) {
3052
3008
  return codec.replace(AUDIO_CODEC_REGEXP, m => getCodecCompatibleNameLower(m.toLowerCase(), preferManagedMediaSource));
3053
3009
  }
@@ -3070,16 +3026,6 @@ function convertAVC1ToAVCOTI(codec) {
3070
3026
  }
3071
3027
  return codec;
3072
3028
  }
3073
- function getM2TSSupportedAudioTypes(preferManagedMediaSource) {
3074
- const MediaSource = getMediaSource(preferManagedMediaSource) || {
3075
- isTypeSupported: () => false
3076
- };
3077
- return {
3078
- mpeg: MediaSource.isTypeSupported('audio/mpeg'),
3079
- mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
3080
- ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
3081
- };
3082
- }
3083
3029
 
3084
3030
  const MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g;
3085
3031
  const MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
@@ -4746,47 +4692,7 @@ class LatencyController {
4746
4692
  this.currentTime = 0;
4747
4693
  this.stallCount = 0;
4748
4694
  this._latency = null;
4749
- this.onTimeupdate = () => {
4750
- const {
4751
- media,
4752
- levelDetails
4753
- } = this;
4754
- if (!media || !levelDetails) {
4755
- return;
4756
- }
4757
- this.currentTime = media.currentTime;
4758
- const latency = this.computeLatency();
4759
- if (latency === null) {
4760
- return;
4761
- }
4762
- this._latency = latency;
4763
-
4764
- // Adapt playbackRate to meet target latency in low-latency mode
4765
- const {
4766
- lowLatencyMode,
4767
- maxLiveSyncPlaybackRate
4768
- } = this.config;
4769
- if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4770
- return;
4771
- }
4772
- const targetLatency = this.targetLatency;
4773
- if (targetLatency === null) {
4774
- return;
4775
- }
4776
- const distanceFromTarget = latency - targetLatency;
4777
- // Only adjust playbackRate when within one target duration of targetLatency
4778
- // and more than one second from under-buffering.
4779
- // Playback further than one target duration from target can be considered DVR playback.
4780
- const liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
4781
- const inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4782
- if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
4783
- const max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4784
- const rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
4785
- media.playbackRate = Math.min(max, Math.max(1, rate));
4786
- } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4787
- media.playbackRate = 1;
4788
- }
4789
- };
4695
+ this.timeupdateHandler = () => this.timeupdate();
4790
4696
  this.hls = hls;
4791
4697
  this.config = hls.config;
4792
4698
  this.registerListeners();
@@ -4878,7 +4784,7 @@ class LatencyController {
4878
4784
  this.onMediaDetaching();
4879
4785
  this.levelDetails = null;
4880
4786
  // @ts-ignore
4881
- this.hls = null;
4787
+ this.hls = this.timeupdateHandler = null;
4882
4788
  }
4883
4789
  registerListeners() {
4884
4790
  this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
@@ -4896,11 +4802,11 @@ class LatencyController {
4896
4802
  }
4897
4803
  onMediaAttached(event, data) {
4898
4804
  this.media = data.media;
4899
- this.media.addEventListener('timeupdate', this.onTimeupdate);
4805
+ this.media.addEventListener('timeupdate', this.timeupdateHandler);
4900
4806
  }
4901
4807
  onMediaDetaching() {
4902
4808
  if (this.media) {
4903
- this.media.removeEventListener('timeupdate', this.onTimeupdate);
4809
+ this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4904
4810
  this.media = null;
4905
4811
  }
4906
4812
  }
@@ -4914,10 +4820,10 @@ class LatencyController {
4914
4820
  }) {
4915
4821
  this.levelDetails = details;
4916
4822
  if (details.advanced) {
4917
- this.onTimeupdate();
4823
+ this.timeupdate();
4918
4824
  }
4919
4825
  if (!details.live && this.media) {
4920
- this.media.removeEventListener('timeupdate', this.onTimeupdate);
4826
+ this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4921
4827
  }
4922
4828
  }
4923
4829
  onError(event, data) {
@@ -4927,7 +4833,48 @@ class LatencyController {
4927
4833
  }
4928
4834
  this.stallCount++;
4929
4835
  if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
4930
- this.hls.logger.warn('[latency-controller]: Stall detected, adjusting target latency');
4836
+ logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency');
4837
+ }
4838
+ }
4839
+ timeupdate() {
4840
+ const {
4841
+ media,
4842
+ levelDetails
4843
+ } = this;
4844
+ if (!media || !levelDetails) {
4845
+ return;
4846
+ }
4847
+ this.currentTime = media.currentTime;
4848
+ const latency = this.computeLatency();
4849
+ if (latency === null) {
4850
+ return;
4851
+ }
4852
+ this._latency = latency;
4853
+
4854
+ // Adapt playbackRate to meet target latency in low-latency mode
4855
+ const {
4856
+ lowLatencyMode,
4857
+ maxLiveSyncPlaybackRate
4858
+ } = this.config;
4859
+ if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4860
+ return;
4861
+ }
4862
+ const targetLatency = this.targetLatency;
4863
+ if (targetLatency === null) {
4864
+ return;
4865
+ }
4866
+ const distanceFromTarget = latency - targetLatency;
4867
+ // Only adjust playbackRate when within one target duration of targetLatency
4868
+ // and more than one second from under-buffering.
4869
+ // Playback further than one target duration from target can be considered DVR playback.
4870
+ const liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
4871
+ const inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4872
+ if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
4873
+ const max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4874
+ const rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
4875
+ media.playbackRate = Math.min(max, Math.max(1, rate));
4876
+ } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4877
+ media.playbackRate = 1;
4931
4878
  }
4932
4879
  }
4933
4880
  estimateLiveEdge() {
@@ -5699,13 +5646,18 @@ var ErrorActionFlags = {
5699
5646
  MoveAllAlternatesMatchingHDCP: 2,
5700
5647
  SwitchToSDR: 4
5701
5648
  }; // Reserved for future use
5702
- class ErrorController extends Logger {
5649
+ class ErrorController {
5703
5650
  constructor(hls) {
5704
- super('error-controller', hls.logger);
5705
5651
  this.hls = void 0;
5706
5652
  this.playlistError = 0;
5707
5653
  this.penalizedRenditions = {};
5654
+ this.log = void 0;
5655
+ this.warn = void 0;
5656
+ this.error = void 0;
5708
5657
  this.hls = hls;
5658
+ this.log = logger.log.bind(logger, `[info]:`);
5659
+ this.warn = logger.warn.bind(logger, `[warning]:`);
5660
+ this.error = logger.error.bind(logger, `[error]:`);
5709
5661
  this.registerListeners();
5710
5662
  }
5711
5663
  registerListeners() {
@@ -6057,13 +6009,16 @@ class ErrorController extends Logger {
6057
6009
  }
6058
6010
  }
6059
6011
 
6060
- class BasePlaylistController extends Logger {
6012
+ class BasePlaylistController {
6061
6013
  constructor(hls, logPrefix) {
6062
- super(logPrefix, hls.logger);
6063
6014
  this.hls = void 0;
6064
6015
  this.timer = -1;
6065
6016
  this.requestScheduled = -1;
6066
6017
  this.canLoad = false;
6018
+ this.log = void 0;
6019
+ this.warn = void 0;
6020
+ this.log = logger.log.bind(logger, `${logPrefix}:`);
6021
+ this.warn = logger.warn.bind(logger, `${logPrefix}:`);
6067
6022
  this.hls = hls;
6068
6023
  }
6069
6024
  destroy() {
@@ -6096,7 +6051,7 @@ class BasePlaylistController extends Logger {
6096
6051
  try {
6097
6052
  uri = new self.URL(attr.URI, previous.url).href;
6098
6053
  } catch (error) {
6099
- this.warn(`Could not construct new URL for Rendition Report: ${error}`);
6054
+ logger.warn(`Could not construct new URL for Rendition Report: ${error}`);
6100
6055
  uri = attr.URI || '';
6101
6056
  }
6102
6057
  // Use exact match. Otherwise, the last partial match, if any, will be used
@@ -6855,9 +6810,8 @@ function searchDownAndUpList(arr, searchIndex, predicate) {
6855
6810
  return -1;
6856
6811
  }
6857
6812
 
6858
- class AbrController extends Logger {
6813
+ class AbrController {
6859
6814
  constructor(_hls) {
6860
- super('abr', _hls.logger);
6861
6815
  this.hls = void 0;
6862
6816
  this.lastLevelLoadSec = 0;
6863
6817
  this.lastLoadedFragLevel = -1;
@@ -6923,7 +6877,7 @@ class AbrController extends Logger {
6923
6877
  const bwEstimate = this.getBwEstimate();
6924
6878
  const levels = hls.levels;
6925
6879
  const level = levels[frag.level];
6926
- const expectedLen = stats.total || Math.max(stats.loaded, Math.round(duration * level.maxBitrate / 8));
6880
+ const expectedLen = stats.total || Math.max(stats.loaded, Math.round(duration * level.averageBitrate / 8));
6927
6881
  let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading;
6928
6882
  if (timeStreaming < 1 && loadedFirstByte) {
6929
6883
  timeStreaming = Math.min(timeLoading, stats.loaded * 8 / bwEstimate);
@@ -6966,12 +6920,12 @@ class AbrController extends Logger {
6966
6920
  // If there has been no loading progress, sample TTFB
6967
6921
  this.bwEstimator.sampleTTFB(timeLoading);
6968
6922
  }
6969
- const nextLoadLevelBitrate = levels[nextLoadLevel].bitrate;
6923
+ const nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate;
6970
6924
  if (this.getBwEstimate() * this.hls.config.abrBandWidthUpFactor > nextLoadLevelBitrate) {
6971
6925
  this.resetEstimator(nextLoadLevelBitrate);
6972
6926
  }
6973
6927
  this.clearTimer();
6974
- this.warn(`Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${frag.level} is loading too slowly;
6928
+ logger.warn(`[abr] Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${frag.level} is loading too slowly;
6975
6929
  Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s
6976
6930
  Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s
6977
6931
  Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(3)} s
@@ -6991,7 +6945,7 @@ class AbrController extends Logger {
6991
6945
  }
6992
6946
  resetEstimator(abrEwmaDefaultEstimate) {
6993
6947
  if (abrEwmaDefaultEstimate) {
6994
- this.log(`setting initial bwe to ${abrEwmaDefaultEstimate}`);
6948
+ logger.log(`setting initial bwe to ${abrEwmaDefaultEstimate}`);
6995
6949
  this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
6996
6950
  }
6997
6951
  this.firstSelection = -1;
@@ -7223,7 +7177,7 @@ class AbrController extends Logger {
7223
7177
  }
7224
7178
  const firstLevel = this.hls.firstLevel;
7225
7179
  const clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
7226
- this.warn(`Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`);
7180
+ logger.warn(`[abr] Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`);
7227
7181
  return clamped;
7228
7182
  }
7229
7183
  get forcedAutoLevel() {
@@ -7308,13 +7262,13 @@ class AbrController extends Logger {
7308
7262
  // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
7309
7263
  const maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
7310
7264
  maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
7311
- this.info(`bitrate test took ${Math.round(1000 * bitrateTestDelay)}ms, set first fragment max fetchDuration to ${Math.round(1000 * maxStarvationDelay)} ms`);
7265
+ logger.info(`[abr] bitrate test took ${Math.round(1000 * bitrateTestDelay)}ms, set first fragment max fetchDuration to ${Math.round(1000 * maxStarvationDelay)} ms`);
7312
7266
  // don't use conservative factor on bitrate test
7313
7267
  bwFactor = bwUpFactor = 1;
7314
7268
  }
7315
7269
  }
7316
7270
  const bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor);
7317
- this.info(`${bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'}, optimal quality level ${bestLevel}`);
7271
+ logger.info(`[abr] ${bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'}, optimal quality level ${bestLevel}`);
7318
7272
  if (bestLevel > -1) {
7319
7273
  return bestLevel;
7320
7274
  }
@@ -7376,7 +7330,7 @@ class AbrController extends Logger {
7376
7330
  currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
7377
7331
  currentFrameRate = minFramerate;
7378
7332
  currentBw = Math.max(currentBw, minBitrate);
7379
- this.log(`picked start tier ${JSON.stringify(startTier)}`);
7333
+ logger.log(`[abr] picked start tier ${JSON.stringify(startTier)}`);
7380
7334
  } else {
7381
7335
  currentCodecSet = level == null ? void 0 : level.codecSet;
7382
7336
  currentVideoRange = level == null ? void 0 : level.videoRange;
@@ -7400,11 +7354,11 @@ class AbrController extends Logger {
7400
7354
  const levels = this.hls.levels;
7401
7355
  const index = levels.indexOf(levelInfo);
7402
7356
  if (decodingInfo.error) {
7403
- this.warn(`MediaCapabilities decodingInfo error: "${decodingInfo.error}" for level ${index} ${JSON.stringify(decodingInfo)}`);
7357
+ logger.warn(`[abr] MediaCapabilities decodingInfo error: "${decodingInfo.error}" for level ${index} ${JSON.stringify(decodingInfo)}`);
7404
7358
  } else if (!decodingInfo.supported) {
7405
- this.warn(`Unsupported MediaCapabilities decodingInfo result for level ${index} ${JSON.stringify(decodingInfo)}`);
7359
+ logger.warn(`[abr] Unsupported MediaCapabilities decodingInfo result for level ${index} ${JSON.stringify(decodingInfo)}`);
7406
7360
  if (index > -1 && levels.length > 1) {
7407
- this.log(`Removing unsupported level ${index}`);
7361
+ logger.log(`[abr] Removing unsupported level ${index}`);
7408
7362
  this.hls.removeLevel(index);
7409
7363
  }
7410
7364
  }
@@ -7451,9 +7405,9 @@ class AbrController extends Logger {
7451
7405
  const forcedAutoLevel = this.forcedAutoLevel;
7452
7406
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
7453
7407
  if (levelsSkipped.length) {
7454
- this.trace(`Skipped level(s) ${levelsSkipped.join(',')} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${level.codecs}" ${currentVideoRange}`);
7408
+ logger.trace(`[abr] Skipped level(s) ${levelsSkipped.join(',')} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${level.codecs}" ${currentVideoRange}`);
7455
7409
  }
7456
- this.info(`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${currentCodecSet} videoRange:${currentVideoRange} hls.loadLevel:${loadLevel}`);
7410
+ logger.info(`[abr] switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${currentCodecSet} videoRange:${currentVideoRange} hls.loadLevel:${loadLevel}`);
7457
7411
  }
7458
7412
  if (firstSelection) {
7459
7413
  this.firstSelection = i;
@@ -7503,9 +7457,8 @@ class AbrController extends Logger {
7503
7457
  * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
7504
7458
  * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
7505
7459
  */
7506
- class TaskLoop extends Logger {
7507
- constructor(label, logger) {
7508
- super(label, logger);
7460
+ class TaskLoop {
7461
+ constructor() {
7509
7462
  this._boundTick = void 0;
7510
7463
  this._tickTimer = null;
7511
7464
  this._tickInterval = null;
@@ -8596,8 +8549,8 @@ function createLoaderContext(frag, part = null) {
8596
8549
  var _frag$decryptdata;
8597
8550
  let byteRangeStart = start;
8598
8551
  let byteRangeEnd = end;
8599
- if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) {
8600
- // MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
8552
+ if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {
8553
+ // MAP segment encrypted with method 'AES-128', when served with HTTP Range,
8601
8554
  // has the unencrypted size specified in the range.
8602
8555
  // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
8603
8556
  const fragmentLen = end - start;
@@ -8630,9 +8583,6 @@ function createGapLoadError(frag, part) {
8630
8583
  (part ? part : frag).stats.aborted = true;
8631
8584
  return new LoadError(errorData);
8632
8585
  }
8633
- function isMethodFullSegmentAesCbc(method) {
8634
- return method === 'AES-128' || method === 'AES-256';
8635
- }
8636
8586
  class LoadError extends Error {
8637
8587
  constructor(data) {
8638
8588
  super(data.error.message);
@@ -8642,61 +8592,33 @@ class LoadError extends Error {
8642
8592
  }
8643
8593
 
8644
8594
  class AESCrypto {
8645
- constructor(subtle, iv, aesMode) {
8595
+ constructor(subtle, iv) {
8646
8596
  this.subtle = void 0;
8647
8597
  this.aesIV = void 0;
8648
- this.aesMode = void 0;
8649
8598
  this.subtle = subtle;
8650
8599
  this.aesIV = iv;
8651
- this.aesMode = aesMode;
8652
8600
  }
8653
8601
  decrypt(data, key) {
8654
- switch (this.aesMode) {
8655
- case DecrypterAesMode.cbc:
8656
- return this.subtle.decrypt({
8657
- name: 'AES-CBC',
8658
- iv: this.aesIV
8659
- }, key, data);
8660
- case DecrypterAesMode.ctr:
8661
- return this.subtle.decrypt({
8662
- name: 'AES-CTR',
8663
- counter: this.aesIV,
8664
- length: 64
8665
- },
8666
- //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
8667
- key, data);
8668
- default:
8669
- throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`);
8670
- }
8602
+ return this.subtle.decrypt({
8603
+ name: 'AES-CBC',
8604
+ iv: this.aesIV
8605
+ }, key, data);
8671
8606
  }
8672
8607
  }
8673
8608
 
8674
8609
  class FastAESKey {
8675
- constructor(subtle, key, aesMode) {
8610
+ constructor(subtle, key) {
8676
8611
  this.subtle = void 0;
8677
8612
  this.key = void 0;
8678
- this.aesMode = void 0;
8679
8613
  this.subtle = subtle;
8680
8614
  this.key = key;
8681
- this.aesMode = aesMode;
8682
8615
  }
8683
8616
  expandKey() {
8684
- const subtleAlgoName = getSubtleAlgoName(this.aesMode);
8685
8617
  return this.subtle.importKey('raw', this.key, {
8686
- name: subtleAlgoName
8618
+ name: 'AES-CBC'
8687
8619
  }, false, ['encrypt', 'decrypt']);
8688
8620
  }
8689
8621
  }
8690
- function getSubtleAlgoName(aesMode) {
8691
- switch (aesMode) {
8692
- case DecrypterAesMode.cbc:
8693
- return 'AES-CBC';
8694
- case DecrypterAesMode.ctr:
8695
- return 'AES-CTR';
8696
- default:
8697
- throw new Error(`[FastAESKey] invalid aes mode ${aesMode}`);
8698
- }
8699
- }
8700
8622
 
8701
8623
  // PKCS7
8702
8624
  function removePadding(array) {
@@ -8946,8 +8868,7 @@ class Decrypter {
8946
8868
  this.currentIV = null;
8947
8869
  this.currentResult = null;
8948
8870
  this.useSoftware = void 0;
8949
- this.enableSoftwareAES = void 0;
8950
- this.enableSoftwareAES = config.enableSoftwareAES;
8871
+ this.useSoftware = config.enableSoftwareAES;
8951
8872
  this.removePKCS7Padding = removePKCS7Padding;
8952
8873
  // built in decryptor expects PKCS7 padding
8953
8874
  if (removePKCS7Padding) {
@@ -8960,7 +8881,9 @@ class Decrypter {
8960
8881
  /* no-op */
8961
8882
  }
8962
8883
  }
8963
- this.useSoftware = this.subtle === null;
8884
+ if (this.subtle === null) {
8885
+ this.useSoftware = true;
8886
+ }
8964
8887
  }
8965
8888
  destroy() {
8966
8889
  this.subtle = null;
@@ -8998,10 +8921,10 @@ class Decrypter {
8998
8921
  this.softwareDecrypter = null;
8999
8922
  }
9000
8923
  }
9001
- decrypt(data, key, iv, aesMode) {
8924
+ decrypt(data, key, iv) {
9002
8925
  if (this.useSoftware) {
9003
8926
  return new Promise((resolve, reject) => {
9004
- this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
8927
+ this.softwareDecrypt(new Uint8Array(data), key, iv);
9005
8928
  const decryptResult = this.flush();
9006
8929
  if (decryptResult) {
9007
8930
  resolve(decryptResult.buffer);
@@ -9010,21 +8933,17 @@ class Decrypter {
9010
8933
  }
9011
8934
  });
9012
8935
  }
9013
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
8936
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
9014
8937
  }
9015
8938
 
9016
8939
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
9017
8940
  // data is handled in the flush() call
9018
- softwareDecrypt(data, key, iv, aesMode) {
8941
+ softwareDecrypt(data, key, iv) {
9019
8942
  const {
9020
8943
  currentIV,
9021
8944
  currentResult,
9022
8945
  remainderData
9023
8946
  } = this;
9024
- if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
9025
- logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
9026
- return null;
9027
- }
9028
8947
  this.logOnce('JS AES decrypt');
9029
8948
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
9030
8949
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -9057,11 +8976,11 @@ class Decrypter {
9057
8976
  }
9058
8977
  return result;
9059
8978
  }
9060
- webCryptoDecrypt(data, key, iv, aesMode) {
8979
+ webCryptoDecrypt(data, key, iv) {
9061
8980
  const subtle = this.subtle;
9062
8981
  if (this.key !== key || !this.fastAesKey) {
9063
8982
  this.key = key;
9064
- this.fastAesKey = new FastAESKey(subtle, key, aesMode);
8983
+ this.fastAesKey = new FastAESKey(subtle, key);
9065
8984
  }
9066
8985
  return this.fastAesKey.expandKey().then(aesKey => {
9067
8986
  // decrypt using web crypto
@@ -9069,25 +8988,22 @@ class Decrypter {
9069
8988
  return Promise.reject(new Error('web crypto not initialized'));
9070
8989
  }
9071
8990
  this.logOnce('WebCrypto AES decrypt');
9072
- const crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
8991
+ const crypto = new AESCrypto(subtle, new Uint8Array(iv));
9073
8992
  return crypto.decrypt(data.buffer, aesKey);
9074
8993
  }).catch(err => {
9075
8994
  logger.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`);
9076
- return this.onWebCryptoError(data, key, iv, aesMode);
8995
+ return this.onWebCryptoError(data, key, iv);
9077
8996
  });
9078
8997
  }
9079
- onWebCryptoError(data, key, iv, aesMode) {
9080
- const enableSoftwareAES = this.enableSoftwareAES;
9081
- if (enableSoftwareAES) {
9082
- this.useSoftware = true;
9083
- this.logEnabled = true;
9084
- this.softwareDecrypt(data, key, iv, aesMode);
9085
- const decryptResult = this.flush();
9086
- if (decryptResult) {
9087
- return decryptResult.buffer;
9088
- }
8998
+ onWebCryptoError(data, key, iv) {
8999
+ this.useSoftware = true;
9000
+ this.logEnabled = true;
9001
+ this.softwareDecrypt(data, key, iv);
9002
+ const decryptResult = this.flush();
9003
+ if (decryptResult) {
9004
+ return decryptResult.buffer;
9089
9005
  }
9090
- throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data');
9006
+ throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
9091
9007
  }
9092
9008
  getValidChunk(data) {
9093
9009
  let currentChunk = data;
@@ -9138,7 +9054,7 @@ const State = {
9138
9054
  };
9139
9055
  class BaseStreamController extends TaskLoop {
9140
9056
  constructor(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
9141
- super(logPrefix, hls.logger);
9057
+ super();
9142
9058
  this.hls = void 0;
9143
9059
  this.fragPrevious = null;
9144
9060
  this.fragCurrent = null;
@@ -9163,83 +9079,22 @@ class BaseStreamController extends TaskLoop {
9163
9079
  this.startFragRequested = false;
9164
9080
  this.decrypter = void 0;
9165
9081
  this.initPTS = [];
9166
- this.onMediaSeeking = () => {
9167
- const {
9168
- config,
9169
- fragCurrent,
9170
- media,
9171
- mediaBuffer,
9172
- state
9173
- } = this;
9174
- const currentTime = media ? media.currentTime : 0;
9175
- const bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
9176
- this.log(`media seeking to ${isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime}, state: ${state}`);
9177
- if (this.state === State.ENDED) {
9178
- this.resetLoadingState();
9179
- } else if (fragCurrent) {
9180
- // Seeking while frag load is in progress
9181
- const tolerance = config.maxFragLookUpTolerance;
9182
- const fragStartOffset = fragCurrent.start - tolerance;
9183
- const fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
9184
- // if seeking out of buffered range or into new one
9185
- if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
9186
- const pastFragment = currentTime > fragEndOffset;
9187
- // if the seek position is outside the current fragment range
9188
- if (currentTime < fragStartOffset || pastFragment) {
9189
- if (pastFragment && fragCurrent.loader) {
9190
- this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
9191
- fragCurrent.abortRequests();
9192
- this.resetLoadingState();
9193
- }
9194
- this.fragPrevious = null;
9195
- }
9196
- }
9197
- }
9198
- if (media) {
9199
- // Remove gap fragments
9200
- this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
9201
- this.lastCurrentTime = currentTime;
9202
- }
9203
-
9204
- // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
9205
- if (!this.loadedmetadata && !bufferInfo.len) {
9206
- this.nextLoadPosition = this.startPosition = currentTime;
9207
- }
9208
-
9209
- // Async tick to speed up processing
9210
- this.tickImmediate();
9211
- };
9212
- this.onMediaEnded = () => {
9213
- // reset startPosition and lastCurrentTime to restart playback @ stream beginning
9214
- this.startPosition = this.lastCurrentTime = 0;
9215
- };
9082
+ this.onvseeking = null;
9083
+ this.onvended = null;
9084
+ this.logPrefix = '';
9085
+ this.log = void 0;
9086
+ this.warn = void 0;
9216
9087
  this.playlistType = playlistType;
9088
+ this.logPrefix = logPrefix;
9089
+ this.log = logger.log.bind(logger, `${logPrefix}:`);
9090
+ this.warn = logger.warn.bind(logger, `${logPrefix}:`);
9217
9091
  this.hls = hls;
9218
9092
  this.fragmentLoader = new FragmentLoader(hls.config);
9219
9093
  this.keyLoader = keyLoader;
9220
9094
  this.fragmentTracker = fragmentTracker;
9221
9095
  this.config = hls.config;
9222
9096
  this.decrypter = new Decrypter(hls.config);
9223
- }
9224
- registerListeners() {
9225
- const {
9226
- hls
9227
- } = this;
9228
- hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
9229
- hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
9230
- hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
9231
9097
  hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
9232
- hls.on(Events.ERROR, this.onError, this);
9233
- }
9234
- unregisterListeners() {
9235
- const {
9236
- hls
9237
- } = this;
9238
- hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
9239
- hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
9240
- hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
9241
- hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
9242
- hls.off(Events.ERROR, this.onError, this);
9243
9098
  }
9244
9099
  doTick() {
9245
9100
  this.onTickEnd();
@@ -9293,8 +9148,10 @@ class BaseStreamController extends TaskLoop {
9293
9148
  }
9294
9149
  onMediaAttached(event, data) {
9295
9150
  const media = this.media = this.mediaBuffer = data.media;
9296
- media.addEventListener('seeking', this.onMediaSeeking);
9297
- media.addEventListener('ended', this.onMediaEnded);
9151
+ this.onvseeking = this.onMediaSeeking.bind(this);
9152
+ this.onvended = this.onMediaEnded.bind(this);
9153
+ media.addEventListener('seeking', this.onvseeking);
9154
+ media.addEventListener('ended', this.onvended);
9298
9155
  const config = this.config;
9299
9156
  if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
9300
9157
  this.startLoad(config.startPosition);
@@ -9308,9 +9165,10 @@ class BaseStreamController extends TaskLoop {
9308
9165
  }
9309
9166
 
9310
9167
  // remove video listeners
9311
- if (media) {
9312
- media.removeEventListener('seeking', this.onMediaSeeking);
9313
- media.removeEventListener('ended', this.onMediaEnded);
9168
+ if (media && this.onvseeking && this.onvended) {
9169
+ media.removeEventListener('seeking', this.onvseeking);
9170
+ media.removeEventListener('ended', this.onvended);
9171
+ this.onvseeking = this.onvended = null;
9314
9172
  }
9315
9173
  if (this.keyLoader) {
9316
9174
  this.keyLoader.detach();
@@ -9320,8 +9178,56 @@ class BaseStreamController extends TaskLoop {
9320
9178
  this.fragmentTracker.removeAllFragments();
9321
9179
  this.stopLoad();
9322
9180
  }
9323
- onManifestLoading() {}
9324
- onError(event, data) {}
9181
+ onMediaSeeking() {
9182
+ const {
9183
+ config,
9184
+ fragCurrent,
9185
+ media,
9186
+ mediaBuffer,
9187
+ state
9188
+ } = this;
9189
+ const currentTime = media ? media.currentTime : 0;
9190
+ const bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
9191
+ this.log(`media seeking to ${isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime}, state: ${state}`);
9192
+ if (this.state === State.ENDED) {
9193
+ this.resetLoadingState();
9194
+ } else if (fragCurrent) {
9195
+ // Seeking while frag load is in progress
9196
+ const tolerance = config.maxFragLookUpTolerance;
9197
+ const fragStartOffset = fragCurrent.start - tolerance;
9198
+ const fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
9199
+ // if seeking out of buffered range or into new one
9200
+ if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
9201
+ const pastFragment = currentTime > fragEndOffset;
9202
+ // if the seek position is outside the current fragment range
9203
+ if (currentTime < fragStartOffset || pastFragment) {
9204
+ if (pastFragment && fragCurrent.loader) {
9205
+ this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
9206
+ fragCurrent.abortRequests();
9207
+ this.resetLoadingState();
9208
+ }
9209
+ this.fragPrevious = null;
9210
+ }
9211
+ }
9212
+ }
9213
+ if (media) {
9214
+ // Remove gap fragments
9215
+ this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
9216
+ this.lastCurrentTime = currentTime;
9217
+ }
9218
+
9219
+ // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
9220
+ if (!this.loadedmetadata && !bufferInfo.len) {
9221
+ this.nextLoadPosition = this.startPosition = currentTime;
9222
+ }
9223
+
9224
+ // Async tick to speed up processing
9225
+ this.tickImmediate();
9226
+ }
9227
+ onMediaEnded() {
9228
+ // reset startPosition and lastCurrentTime to restart playback @ stream beginning
9229
+ this.startPosition = this.lastCurrentTime = 0;
9230
+ }
9325
9231
  onManifestLoaded(event, data) {
9326
9232
  this.startTimeOffset = data.startTimeOffset;
9327
9233
  this.initPTS = [];
@@ -9331,7 +9237,7 @@ class BaseStreamController extends TaskLoop {
9331
9237
  this.stopLoad();
9332
9238
  super.onHandlerDestroying();
9333
9239
  // @ts-ignore
9334
- this.hls = this.onMediaSeeking = this.onMediaEnded = null;
9240
+ this.hls = null;
9335
9241
  }
9336
9242
  onHandlerDestroyed() {
9337
9243
  this.state = State.STOPPED;
@@ -9462,10 +9368,10 @@ class BaseStreamController extends TaskLoop {
9462
9368
  const decryptData = frag.decryptdata;
9463
9369
 
9464
9370
  // check to see if the payload needs to be decrypted
9465
- if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
9371
+ if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
9466
9372
  const startTime = self.performance.now();
9467
9373
  // decrypt init segment data
9468
- return this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(err => {
9374
+ return this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(err => {
9469
9375
  hls.trigger(Events.ERROR, {
9470
9376
  type: ErrorTypes.MEDIA_ERROR,
9471
9377
  details: ErrorDetails.FRAG_DECRYPT_ERROR,
@@ -9577,7 +9483,7 @@ class BaseStreamController extends TaskLoop {
9577
9483
  }
9578
9484
  let keyLoadingPromise = null;
9579
9485
  if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
9580
- this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'} ${frag.level}`);
9486
+ this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.logPrefix === '[stream-controller]' ? 'level' : 'track'} ${frag.level}`);
9581
9487
  this.state = State.KEY_LOADING;
9582
9488
  this.fragCurrent = frag;
9583
9489
  keyLoadingPromise = this.keyLoader.load(frag).then(keyLoadedData => {
@@ -9608,7 +9514,7 @@ class BaseStreamController extends TaskLoop {
9608
9514
  const partIndex = this.getNextPart(partList, frag, targetBufferTime);
9609
9515
  if (partIndex > -1) {
9610
9516
  const part = partList[partIndex];
9611
- this.log(`Loading part sn: ${frag.sn} p: ${part.index} cc: ${frag.cc} of playlist [${details.startSN}-${details.endSN}] parts [0-${partIndex}-${partList.length - 1}] ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
9517
+ this.log(`Loading part sn: ${frag.sn} p: ${part.index} cc: ${frag.cc} of playlist [${details.startSN}-${details.endSN}] parts [0-${partIndex}-${partList.length - 1}] ${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
9612
9518
  this.nextLoadPosition = part.start + part.duration;
9613
9519
  this.state = State.FRAG_LOADING;
9614
9520
  let _result;
@@ -9637,7 +9543,7 @@ class BaseStreamController extends TaskLoop {
9637
9543
  }
9638
9544
  }
9639
9545
  }
9640
- this.log(`Loading fragment ${frag.sn} cc: ${frag.cc} ${details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''}${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
9546
+ this.log(`Loading fragment ${frag.sn} cc: ${frag.cc} ${details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''}${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
9641
9547
  // Don't update nextLoadPosition for fragments which are not buffered
9642
9548
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
9643
9549
  this.nextLoadPosition = frag.start + frag.duration;
@@ -10222,7 +10128,7 @@ class BaseStreamController extends TaskLoop {
10222
10128
  errorAction.resolved = true;
10223
10129
  }
10224
10130
  } else {
10225
- this.warn(`${data.details} reached or exceeded max retry (${retryCount})`);
10131
+ logger.warn(`${data.details} reached or exceeded max retry (${retryCount})`);
10226
10132
  return;
10227
10133
  }
10228
10134
  } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
@@ -10631,7 +10537,6 @@ const initPTSFn = (timestamp, timeOffset, initPTS) => {
10631
10537
  */
10632
10538
  function getAudioConfig(observer, data, offset, audioCodec) {
10633
10539
  let adtsObjectType;
10634
- let originalAdtsObjectType;
10635
10540
  let adtsExtensionSamplingIndex;
10636
10541
  let adtsChannelConfig;
10637
10542
  let config;
@@ -10639,7 +10544,7 @@ function getAudioConfig(observer, data, offset, audioCodec) {
10639
10544
  const manifestCodec = audioCodec;
10640
10545
  const adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
10641
10546
  // byte 2
10642
- adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
10547
+ adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
10643
10548
  const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
10644
10549
  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
10645
10550
  const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`);
@@ -10656,8 +10561,8 @@ function getAudioConfig(observer, data, offset, audioCodec) {
10656
10561
  // byte 3
10657
10562
  adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
10658
10563
  logger.log(`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`);
10659
- // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
10660
- if (/firefox|palemoon/i.test(userAgent)) {
10564
+ // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
10565
+ if (/firefox/i.test(userAgent)) {
10661
10566
  if (adtsSamplingIndex >= 6) {
10662
10567
  adtsObjectType = 5;
10663
10568
  config = new Array(4);
@@ -10751,7 +10656,6 @@ function getAudioConfig(observer, data, offset, audioCodec) {
10751
10656
  samplerate: adtsSamplingRates[adtsSamplingIndex],
10752
10657
  channelCount: adtsChannelConfig,
10753
10658
  codec: 'mp4a.40.' + adtsObjectType,
10754
- parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
10755
10659
  manifestCodec
10756
10660
  };
10757
10661
  }
@@ -10806,8 +10710,7 @@ function initTrackConfig(track, observer, data, offset, audioCodec) {
10806
10710
  track.channelCount = config.channelCount;
10807
10711
  track.codec = config.codec;
10808
10712
  track.manifestCodec = config.manifestCodec;
10809
- track.parsedCodec = config.parsedCodec;
10810
- logger.log(`parsed codec:${track.parsedCodec}, codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`);
10713
+ logger.log(`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`);
10811
10714
  }
10812
10715
  }
10813
10716
  function getFrameDuration(samplerate) {
@@ -11983,7 +11886,7 @@ class SampleAesDecrypter {
11983
11886
  });
11984
11887
  }
11985
11888
  decryptBuffer(encryptedData) {
11986
- return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc);
11889
+ return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
11987
11890
  }
11988
11891
 
11989
11892
  // AAC - encrypt all full 16 bytes blocks starting from offset 16
@@ -14227,7 +14130,7 @@ class MP4Remuxer {
14227
14130
  logger.warn(`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${Math.round(1000 * delta / inputTimeScale)} ms gap.`);
14228
14131
  for (let j = 0; j < missing; j++) {
14229
14132
  const newStamp = Math.max(nextPts, 0);
14230
- let fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
14133
+ let fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
14231
14134
  if (!fillFrame) {
14232
14135
  logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
14233
14136
  fillFrame = sample.unit.subarray();
@@ -14355,7 +14258,7 @@ class MP4Remuxer {
14355
14258
  // samples count of this segment's duration
14356
14259
  const nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
14357
14260
  // silent frame
14358
- const silentFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
14261
+ const silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
14359
14262
  logger.warn('[mp4-remuxer]: remux empty Audio');
14360
14263
  // Can't remux if we can't generate a silent frame...
14361
14264
  if (!silentFrame) {
@@ -14749,15 +14652,13 @@ class Transmuxer {
14749
14652
  initSegmentData
14750
14653
  } = transmuxConfig;
14751
14654
  const keyData = getEncryptionType(uintData, decryptdata);
14752
- if (keyData && isFullSegmentEncryption(keyData.method)) {
14655
+ if (keyData && keyData.method === 'AES-128') {
14753
14656
  const decrypter = this.getDecrypter();
14754
- const aesMode = getAesModeFromFullSegmentMethod(keyData.method);
14755
-
14756
14657
  // Software decryption is synchronous; webCrypto is not
14757
14658
  if (decrypter.isSync()) {
14758
14659
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
14759
14660
  // data is handled in the flush() call
14760
- let decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode);
14661
+ let decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
14761
14662
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
14762
14663
  const loadingParts = chunkMeta.part > -1;
14763
14664
  if (loadingParts) {
@@ -14769,7 +14670,7 @@ class Transmuxer {
14769
14670
  }
14770
14671
  uintData = new Uint8Array(decryptedData);
14771
14672
  } else {
14772
- this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(decryptedData => {
14673
+ this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(decryptedData => {
14773
14674
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
14774
14675
  // the decrypted data has been transmuxed
14775
14676
  const result = this.push(decryptedData, null, chunkMeta);
@@ -15423,7 +15324,14 @@ class TransmuxerInterface {
15423
15324
  this.observer = new EventEmitter();
15424
15325
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
15425
15326
  this.observer.on(Events.ERROR, forwardMessage);
15426
- const m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
15327
+ const MediaSource = getMediaSource(config.preferManagedMediaSource) || {
15328
+ isTypeSupported: () => false
15329
+ };
15330
+ const m2tsTypeSupported = {
15331
+ mpeg: MediaSource.isTypeSupported('audio/mpeg'),
15332
+ mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
15333
+ ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
15334
+ };
15427
15335
 
15428
15336
  // navigator.vendor is not always available in Web Worker
15429
15337
  // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
@@ -15711,7 +15619,7 @@ const TICK_INTERVAL$2 = 100; // how often to tick in ms
15711
15619
 
15712
15620
  class AudioStreamController extends BaseStreamController {
15713
15621
  constructor(hls, fragmentTracker, keyLoader) {
15714
- super(hls, fragmentTracker, keyLoader, 'audio-stream-controller', PlaylistLevelType.AUDIO);
15622
+ super(hls, fragmentTracker, keyLoader, '[audio-stream-controller]', PlaylistLevelType.AUDIO);
15715
15623
  this.videoBuffer = null;
15716
15624
  this.videoTrackCC = -1;
15717
15625
  this.waitingVideoCC = -1;
@@ -15723,24 +15631,27 @@ class AudioStreamController extends BaseStreamController {
15723
15631
  this.flushing = false;
15724
15632
  this.bufferFlushed = false;
15725
15633
  this.cachedTrackLoadedData = null;
15726
- this.registerListeners();
15634
+ this._registerListeners();
15727
15635
  }
15728
15636
  onHandlerDestroying() {
15729
- this.unregisterListeners();
15637
+ this._unregisterListeners();
15730
15638
  super.onHandlerDestroying();
15731
15639
  this.mainDetails = null;
15732
15640
  this.bufferedTrack = null;
15733
15641
  this.switchingTrack = null;
15734
15642
  }
15735
- registerListeners() {
15736
- super.registerListeners();
15643
+ _registerListeners() {
15737
15644
  const {
15738
15645
  hls
15739
15646
  } = this;
15647
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
15648
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
15649
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
15740
15650
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
15741
15651
  hls.on(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this);
15742
15652
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
15743
15653
  hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);
15654
+ hls.on(Events.ERROR, this.onError, this);
15744
15655
  hls.on(Events.BUFFER_RESET, this.onBufferReset, this);
15745
15656
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
15746
15657
  hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
@@ -15748,18 +15659,18 @@ class AudioStreamController extends BaseStreamController {
15748
15659
  hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);
15749
15660
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
15750
15661
  }
15751
- unregisterListeners() {
15662
+ _unregisterListeners() {
15752
15663
  const {
15753
15664
  hls
15754
15665
  } = this;
15755
- if (!hls) {
15756
- return;
15757
- }
15758
- super.unregisterListeners();
15666
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
15667
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
15668
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
15759
15669
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
15760
15670
  hls.off(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this);
15761
15671
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
15762
15672
  hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);
15673
+ hls.off(Events.ERROR, this.onError, this);
15763
15674
  hls.off(Events.BUFFER_RESET, this.onBufferReset, this);
15764
15675
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
15765
15676
  hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
@@ -16491,7 +16402,7 @@ class AudioStreamController extends BaseStreamController {
16491
16402
 
16492
16403
  class AudioTrackController extends BasePlaylistController {
16493
16404
  constructor(hls) {
16494
- super(hls, 'audio-track-controller');
16405
+ super(hls, '[audio-track-controller]');
16495
16406
  this.tracks = [];
16496
16407
  this.groupIds = null;
16497
16408
  this.tracksInGroup = [];
@@ -16810,23 +16721,26 @@ const TICK_INTERVAL$1 = 500; // how often to tick in ms
16810
16721
 
16811
16722
  class SubtitleStreamController extends BaseStreamController {
16812
16723
  constructor(hls, fragmentTracker, keyLoader) {
16813
- super(hls, fragmentTracker, keyLoader, 'subtitle-stream-controller', PlaylistLevelType.SUBTITLE);
16724
+ super(hls, fragmentTracker, keyLoader, '[subtitle-stream-controller]', PlaylistLevelType.SUBTITLE);
16814
16725
  this.currentTrackId = -1;
16815
16726
  this.tracksBuffered = [];
16816
16727
  this.mainDetails = null;
16817
- this.registerListeners();
16728
+ this._registerListeners();
16818
16729
  }
16819
16730
  onHandlerDestroying() {
16820
- this.unregisterListeners();
16731
+ this._unregisterListeners();
16821
16732
  super.onHandlerDestroying();
16822
16733
  this.mainDetails = null;
16823
16734
  }
16824
- registerListeners() {
16825
- super.registerListeners();
16735
+ _registerListeners() {
16826
16736
  const {
16827
16737
  hls
16828
16738
  } = this;
16739
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
16740
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
16741
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
16829
16742
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
16743
+ hls.on(Events.ERROR, this.onError, this);
16830
16744
  hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
16831
16745
  hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
16832
16746
  hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
@@ -16834,12 +16748,15 @@ class SubtitleStreamController extends BaseStreamController {
16834
16748
  hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
16835
16749
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
16836
16750
  }
16837
- unregisterListeners() {
16838
- super.unregisterListeners();
16751
+ _unregisterListeners() {
16839
16752
  const {
16840
16753
  hls
16841
16754
  } = this;
16755
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
16756
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
16757
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
16842
16758
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
16759
+ hls.off(Events.ERROR, this.onError, this);
16843
16760
  hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
16844
16761
  hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
16845
16762
  hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
@@ -17066,10 +16983,10 @@ class SubtitleStreamController extends BaseStreamController {
17066
16983
  return;
17067
16984
  }
17068
16985
  // check to see if the payload needs to be decrypted
17069
- if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
16986
+ if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
17070
16987
  const startTime = performance.now();
17071
16988
  // decrypt the subtitles
17072
- this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(err => {
16989
+ this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(err => {
17073
16990
  hls.trigger(Events.ERROR, {
17074
16991
  type: ErrorTypes.MEDIA_ERROR,
17075
16992
  details: ErrorDetails.FRAG_DECRYPT_ERROR,
@@ -17203,7 +17120,7 @@ class BufferableInstance {
17203
17120
 
17204
17121
  class SubtitleTrackController extends BasePlaylistController {
17205
17122
  constructor(hls) {
17206
- super(hls, 'subtitle-track-controller');
17123
+ super(hls, '[subtitle-track-controller]');
17207
17124
  this.media = null;
17208
17125
  this.tracks = [];
17209
17126
  this.groupIds = null;
@@ -17212,10 +17129,10 @@ class SubtitleTrackController extends BasePlaylistController {
17212
17129
  this.currentTrack = null;
17213
17130
  this.selectDefaultTrack = true;
17214
17131
  this.queuedDefaultTrack = -1;
17132
+ this.asyncPollTrackChange = () => this.pollTrackChange(0);
17215
17133
  this.useTextTrackPolling = false;
17216
17134
  this.subtitlePollingInterval = -1;
17217
17135
  this._subtitleDisplay = true;
17218
- this.asyncPollTrackChange = () => this.pollTrackChange(0);
17219
17136
  this.onTextTracksChanged = () => {
17220
17137
  if (!this.useTextTrackPolling) {
17221
17138
  self.clearInterval(this.subtitlePollingInterval);
@@ -17249,7 +17166,6 @@ class SubtitleTrackController extends BasePlaylistController {
17249
17166
  this.tracks.length = 0;
17250
17167
  this.tracksInGroup.length = 0;
17251
17168
  this.currentTrack = null;
17252
- // @ts-ignore
17253
17169
  this.onTextTracksChanged = this.asyncPollTrackChange = null;
17254
17170
  super.destroy();
17255
17171
  }
@@ -17710,9 +17626,8 @@ class BufferOperationQueue {
17710
17626
  }
17711
17627
 
17712
17628
  const VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
17713
- class BufferController extends Logger {
17629
+ class BufferController {
17714
17630
  constructor(hls) {
17715
- super('buffer-controller', hls.logger);
17716
17631
  // The level details used to determine duration, target-duration and live
17717
17632
  this.details = null;
17718
17633
  // cache the self generated object url to detect hijack of video tag
@@ -17742,6 +17657,9 @@ class BufferController extends Logger {
17742
17657
  this.tracks = {};
17743
17658
  this.pendingTracks = {};
17744
17659
  this.sourceBuffer = void 0;
17660
+ this.log = void 0;
17661
+ this.warn = void 0;
17662
+ this.error = void 0;
17745
17663
  this._onEndStreaming = event => {
17746
17664
  if (!this.hls) {
17747
17665
  return;
@@ -17787,11 +17705,15 @@ class BufferController extends Logger {
17787
17705
  _objectUrl
17788
17706
  } = this;
17789
17707
  if (mediaSrc !== _objectUrl) {
17790
- this.error(`Media element src was set while attaching MediaSource (${_objectUrl} > ${mediaSrc})`);
17708
+ logger.error(`Media element src was set while attaching MediaSource (${_objectUrl} > ${mediaSrc})`);
17791
17709
  }
17792
17710
  };
17793
17711
  this.hls = hls;
17712
+ const logPrefix = '[buffer-controller]';
17794
17713
  this.appendSource = hls.config.preferManagedMediaSource;
17714
+ this.log = logger.log.bind(logger, logPrefix);
17715
+ this.warn = logger.warn.bind(logger, logPrefix);
17716
+ this.error = logger.error.bind(logger, logPrefix);
17795
17717
  this._initSourceBuffer();
17796
17718
  this.registerListeners();
17797
17719
  }
@@ -17804,12 +17726,6 @@ class BufferController extends Logger {
17804
17726
  this.lastMpegAudioChunk = null;
17805
17727
  // @ts-ignore
17806
17728
  this.hls = null;
17807
- // @ts-ignore
17808
- this._onMediaSourceOpen = this._onMediaSourceClose = null;
17809
- // @ts-ignore
17810
- this._onMediaSourceEnded = null;
17811
- // @ts-ignore
17812
- this._onStartStreaming = this._onEndStreaming = null;
17813
17729
  }
17814
17730
  registerListeners() {
17815
17731
  const {
@@ -21074,12 +20990,14 @@ class TimelineController {
21074
20990
  this.cea608Parser1 = this.cea608Parser2 = undefined;
21075
20991
  }
21076
20992
  initCea608Parsers() {
21077
- const channel1 = new OutputFilter(this, 'textTrack1');
21078
- const channel2 = new OutputFilter(this, 'textTrack2');
21079
- const channel3 = new OutputFilter(this, 'textTrack3');
21080
- const channel4 = new OutputFilter(this, 'textTrack4');
21081
- this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
21082
- this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);
20993
+ if (this.config.enableCEA708Captions && (!this.cea608Parser1 || !this.cea608Parser2)) {
20994
+ const channel1 = new OutputFilter(this, 'textTrack1');
20995
+ const channel2 = new OutputFilter(this, 'textTrack2');
20996
+ const channel3 = new OutputFilter(this, 'textTrack3');
20997
+ const channel4 = new OutputFilter(this, 'textTrack4');
20998
+ this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
20999
+ this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);
21000
+ }
21083
21001
  }
21084
21002
  addCues(trackName, startTime, endTime, screen, cueRanges) {
21085
21003
  // skip cues which overlap more than 50% with previously parsed time ranges
@@ -21317,7 +21235,7 @@ class TimelineController {
21317
21235
  if (inUseTracks != null && inUseTracks.length) {
21318
21236
  const unusedTextTracks = inUseTracks.filter(t => t !== null).map(t => t.label);
21319
21237
  if (unusedTextTracks.length) {
21320
- this.hls.logger.warn(`Media element contains unused subtitle tracks: ${unusedTextTracks.join(', ')}. Replace media element for each source to clear TextTracks and captions menu.`);
21238
+ logger.warn(`Media element contains unused subtitle tracks: ${unusedTextTracks.join(', ')}. Replace media element for each source to clear TextTracks and captions menu.`);
21321
21239
  }
21322
21240
  }
21323
21241
  } else if (this.tracks.length) {
@@ -21362,23 +21280,26 @@ class TimelineController {
21362
21280
  return level == null ? void 0 : level.attrs['CLOSED-CAPTIONS'];
21363
21281
  }
21364
21282
  onFragLoading(event, data) {
21283
+ this.initCea608Parsers();
21284
+ const {
21285
+ cea608Parser1,
21286
+ cea608Parser2,
21287
+ lastCc,
21288
+ lastSn,
21289
+ lastPartIndex
21290
+ } = this;
21291
+ if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
21292
+ return;
21293
+ }
21365
21294
  // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack
21366
- if (this.enabled && data.frag.type === PlaylistLevelType.MAIN) {
21295
+ if (data.frag.type === PlaylistLevelType.MAIN) {
21367
21296
  var _data$part$index, _data$part;
21368
- const {
21369
- cea608Parser1,
21370
- cea608Parser2,
21371
- lastSn
21372
- } = this;
21373
- if (!cea608Parser1 || !cea608Parser2) {
21374
- return;
21375
- }
21376
21297
  const {
21377
21298
  cc,
21378
21299
  sn
21379
21300
  } = data.frag;
21380
- const partIndex = (_data$part$index = (_data$part = data.part) == null ? void 0 : _data$part.index) != null ? _data$part$index : -1;
21381
- if (!(sn === lastSn + 1 || sn === lastSn && partIndex === this.lastPartIndex + 1 || cc === this.lastCc)) {
21301
+ const partIndex = (_data$part$index = data == null ? void 0 : (_data$part = data.part) == null ? void 0 : _data$part.index) != null ? _data$part$index : -1;
21302
+ if (!(sn === lastSn + 1 || sn === lastSn && partIndex === lastPartIndex + 1 || cc === lastCc)) {
21382
21303
  cea608Parser1.reset();
21383
21304
  cea608Parser2.reset();
21384
21305
  }
@@ -21435,7 +21356,7 @@ class TimelineController {
21435
21356
  frag: frag
21436
21357
  });
21437
21358
  }, error => {
21438
- hls.logger.log(`Failed to parse IMSC1: ${error}`);
21359
+ logger.log(`Failed to parse IMSC1: ${error}`);
21439
21360
  hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
21440
21361
  success: false,
21441
21362
  frag: frag,
@@ -21476,7 +21397,7 @@ class TimelineController {
21476
21397
  this._fallbackToIMSC1(frag, payload);
21477
21398
  }
21478
21399
  // Something went wrong while parsing. Trigger event with success false.
21479
- hls.logger.log(`Failed to parse VTT cue: ${error}`);
21400
+ logger.log(`Failed to parse VTT cue: ${error}`);
21480
21401
  if (missingInitPTS && maxAvCC > frag.cc) {
21481
21402
  return;
21482
21403
  }
@@ -21537,7 +21458,12 @@ class TimelineController {
21537
21458
  this.captionsTracks = {};
21538
21459
  }
21539
21460
  onFragParsingUserdata(event, data) {
21540
- if (!this.enabled || !this.config.enableCEA708Captions) {
21461
+ this.initCea608Parsers();
21462
+ const {
21463
+ cea608Parser1,
21464
+ cea608Parser2
21465
+ } = this;
21466
+ if (!this.enabled || !cea608Parser1 || !cea608Parser2) {
21541
21467
  return;
21542
21468
  }
21543
21469
  const {
@@ -21552,12 +21478,9 @@ class TimelineController {
21552
21478
  for (let i = 0; i < samples.length; i++) {
21553
21479
  const ccBytes = samples[i].bytes;
21554
21480
  if (ccBytes) {
21555
- if (!this.cea608Parser1) {
21556
- this.initCea608Parsers();
21557
- }
21558
21481
  const ccdatas = this.extractCea608Data(ccBytes);
21559
- this.cea608Parser1.addData(samples[i].pts, ccdatas[0]);
21560
- this.cea608Parser2.addData(samples[i].pts, ccdatas[1]);
21482
+ cea608Parser1.addData(samples[i].pts, ccdatas[0]);
21483
+ cea608Parser2.addData(samples[i].pts, ccdatas[1]);
21561
21484
  }
21562
21485
  }
21563
21486
  }
@@ -21753,7 +21676,7 @@ class CapLevelController {
21753
21676
  const hls = this.hls;
21754
21677
  const maxLevel = this.getMaxLevel(levels.length - 1);
21755
21678
  if (maxLevel !== this.autoLevelCapping) {
21756
- hls.logger.log(`Setting autoLevelCapping to ${maxLevel}: ${levels[maxLevel].height}p@${levels[maxLevel].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`);
21679
+ logger.log(`Setting autoLevelCapping to ${maxLevel}: ${levels[maxLevel].height}p@${levels[maxLevel].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`);
21757
21680
  }
21758
21681
  hls.autoLevelCapping = maxLevel;
21759
21682
  if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
@@ -21931,10 +21854,10 @@ class FPSController {
21931
21854
  totalDroppedFrames: droppedFrames
21932
21855
  });
21933
21856
  if (droppedFPS > 0) {
21934
- // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
21857
+ // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
21935
21858
  if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
21936
21859
  let currentLevel = hls.currentLevel;
21937
- hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
21860
+ logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
21938
21861
  if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
21939
21862
  currentLevel = currentLevel - 1;
21940
21863
  hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
@@ -21966,6 +21889,7 @@ class FPSController {
21966
21889
  }
21967
21890
  }
21968
21891
 
21892
+ const LOGGER_PREFIX = '[eme]';
21969
21893
  /**
21970
21894
  * Controller to deal with encrypted media extensions (EME)
21971
21895
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API
@@ -21973,9 +21897,8 @@ class FPSController {
21973
21897
  * @class
21974
21898
  * @constructor
21975
21899
  */
21976
- class EMEController extends Logger {
21900
+ class EMEController {
21977
21901
  constructor(hls) {
21978
- super('eme', hls.logger);
21979
21902
  this.hls = void 0;
21980
21903
  this.config = void 0;
21981
21904
  this.media = null;
@@ -21985,100 +21908,12 @@ class EMEController extends Logger {
21985
21908
  this.mediaKeySessions = [];
21986
21909
  this.keyIdToKeySessionPromise = {};
21987
21910
  this.setMediaKeysQueue = EMEController.CDMCleanupPromise ? [EMEController.CDMCleanupPromise] : [];
21988
- this.onMediaEncrypted = event => {
21989
- const {
21990
- initDataType,
21991
- initData
21992
- } = event;
21993
- this.debug(`"${event.type}" event: init data type: "${initDataType}"`);
21994
-
21995
- // Ignore event when initData is null
21996
- if (initData === null) {
21997
- return;
21998
- }
21999
- let keyId;
22000
- let keySystemDomain;
22001
- if (initDataType === 'sinf' && this.config.drmSystems[KeySystems.FAIRPLAY]) {
22002
- // Match sinf keyId to playlist skd://keyId=
22003
- const json = bin2str(new Uint8Array(initData));
22004
- try {
22005
- const sinf = base64Decode(JSON.parse(json).sinf);
22006
- const tenc = parseSinf(new Uint8Array(sinf));
22007
- if (!tenc) {
22008
- return;
22009
- }
22010
- keyId = tenc.subarray(8, 24);
22011
- keySystemDomain = KeySystems.FAIRPLAY;
22012
- } catch (error) {
22013
- this.warn('Failed to parse sinf "encrypted" event message initData');
22014
- return;
22015
- }
22016
- } else {
22017
- // Support clear-lead key-session creation (otherwise depend on playlist keys)
22018
- const psshInfo = parsePssh(initData);
22019
- if (psshInfo === null) {
22020
- return;
22021
- }
22022
- if (psshInfo.version === 0 && psshInfo.systemId === KeySystemIds.WIDEVINE && psshInfo.data) {
22023
- keyId = psshInfo.data.subarray(8, 24);
22024
- }
22025
- keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);
22026
- }
22027
- if (!keySystemDomain || !keyId) {
22028
- return;
22029
- }
22030
- const keyIdHex = Hex.hexDump(keyId);
22031
- const {
22032
- keyIdToKeySessionPromise,
22033
- mediaKeySessions
22034
- } = this;
22035
- let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex];
22036
- for (let i = 0; i < mediaKeySessions.length; i++) {
22037
- // Match playlist key
22038
- const keyContext = mediaKeySessions[i];
22039
- const decryptdata = keyContext.decryptdata;
22040
- if (decryptdata.pssh || !decryptdata.keyId) {
22041
- continue;
22042
- }
22043
- const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);
22044
- if (keyIdHex === oldKeyIdHex || decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1) {
22045
- keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];
22046
- delete keyIdToKeySessionPromise[oldKeyIdHex];
22047
- decryptdata.pssh = new Uint8Array(initData);
22048
- decryptdata.keyId = keyId;
22049
- keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = keySessionContextPromise.then(() => {
22050
- return this.generateRequestWithPreferredKeySession(keyContext, initDataType, initData, 'encrypted-event-key-match');
22051
- });
22052
- break;
22053
- }
22054
- }
22055
- if (!keySessionContextPromise) {
22056
- // Clear-lead key (not encountered in playlist)
22057
- keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = this.getKeySystemSelectionPromise([keySystemDomain]).then(({
22058
- keySystem,
22059
- mediaKeys
22060
- }) => {
22061
- var _keySystemToKeySystem;
22062
- this.throwIfDestroyed();
22063
- const decryptdata = new LevelKey('ISO-23001-7', keyIdHex, (_keySystemToKeySystem = keySystemDomainToKeySystemFormat(keySystem)) != null ? _keySystemToKeySystem : '');
22064
- decryptdata.pssh = new Uint8Array(initData);
22065
- decryptdata.keyId = keyId;
22066
- return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => {
22067
- this.throwIfDestroyed();
22068
- const keySessionContext = this.createMediaKeySessionContext({
22069
- decryptdata,
22070
- keySystem,
22071
- mediaKeys
22072
- });
22073
- return this.generateRequestWithPreferredKeySession(keySessionContext, initDataType, initData, 'encrypted-event-no-match');
22074
- });
22075
- });
22076
- }
22077
- keySessionContextPromise.catch(error => this.handleError(error));
22078
- };
22079
- this.onWaitingForKey = event => {
22080
- this.log(`"${event.type}" event`);
22081
- };
21911
+ this.onMediaEncrypted = this._onMediaEncrypted.bind(this);
21912
+ this.onWaitingForKey = this._onWaitingForKey.bind(this);
21913
+ this.debug = logger.debug.bind(logger, LOGGER_PREFIX);
21914
+ this.log = logger.log.bind(logger, LOGGER_PREFIX);
21915
+ this.warn = logger.warn.bind(logger, LOGGER_PREFIX);
21916
+ this.error = logger.error.bind(logger, LOGGER_PREFIX);
22082
21917
  this.hls = hls;
22083
21918
  this.config = hls.config;
22084
21919
  this.registerListeners();
@@ -22092,9 +21927,9 @@ class EMEController extends Logger {
22092
21927
  config.licenseXhrSetup = config.licenseResponseCallback = undefined;
22093
21928
  config.drmSystems = config.drmSystemOptions = {};
22094
21929
  // @ts-ignore
22095
- this.hls = this.config = this.keyIdToKeySessionPromise = null;
21930
+ this.hls = this.onMediaEncrypted = this.onWaitingForKey = this.keyIdToKeySessionPromise = null;
22096
21931
  // @ts-ignore
22097
- this.onMediaEncrypted = this.onWaitingForKey = null;
21932
+ this.config = null;
22098
21933
  }
22099
21934
  registerListeners() {
22100
21935
  this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
@@ -22358,6 +22193,100 @@ class EMEController extends Logger {
22358
22193
  }
22359
22194
  return this.attemptKeySystemAccess(keySystemsToAttempt);
22360
22195
  }
22196
+ _onMediaEncrypted(event) {
22197
+ const {
22198
+ initDataType,
22199
+ initData
22200
+ } = event;
22201
+ this.debug(`"${event.type}" event: init data type: "${initDataType}"`);
22202
+
22203
+ // Ignore event when initData is null
22204
+ if (initData === null) {
22205
+ return;
22206
+ }
22207
+ let keyId;
22208
+ let keySystemDomain;
22209
+ if (initDataType === 'sinf' && this.config.drmSystems[KeySystems.FAIRPLAY]) {
22210
+ // Match sinf keyId to playlist skd://keyId=
22211
+ const json = bin2str(new Uint8Array(initData));
22212
+ try {
22213
+ const sinf = base64Decode(JSON.parse(json).sinf);
22214
+ const tenc = parseSinf(new Uint8Array(sinf));
22215
+ if (!tenc) {
22216
+ return;
22217
+ }
22218
+ keyId = tenc.subarray(8, 24);
22219
+ keySystemDomain = KeySystems.FAIRPLAY;
22220
+ } catch (error) {
22221
+ this.warn('Failed to parse sinf "encrypted" event message initData');
22222
+ return;
22223
+ }
22224
+ } else {
22225
+ // Support clear-lead key-session creation (otherwise depend on playlist keys)
22226
+ const psshInfo = parsePssh(initData);
22227
+ if (psshInfo === null) {
22228
+ return;
22229
+ }
22230
+ if (psshInfo.version === 0 && psshInfo.systemId === KeySystemIds.WIDEVINE && psshInfo.data) {
22231
+ keyId = psshInfo.data.subarray(8, 24);
22232
+ }
22233
+ keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId);
22234
+ }
22235
+ if (!keySystemDomain || !keyId) {
22236
+ return;
22237
+ }
22238
+ const keyIdHex = Hex.hexDump(keyId);
22239
+ const {
22240
+ keyIdToKeySessionPromise,
22241
+ mediaKeySessions
22242
+ } = this;
22243
+ let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex];
22244
+ for (let i = 0; i < mediaKeySessions.length; i++) {
22245
+ // Match playlist key
22246
+ const keyContext = mediaKeySessions[i];
22247
+ const decryptdata = keyContext.decryptdata;
22248
+ if (decryptdata.pssh || !decryptdata.keyId) {
22249
+ continue;
22250
+ }
22251
+ const oldKeyIdHex = Hex.hexDump(decryptdata.keyId);
22252
+ if (keyIdHex === oldKeyIdHex || decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1) {
22253
+ keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex];
22254
+ delete keyIdToKeySessionPromise[oldKeyIdHex];
22255
+ decryptdata.pssh = new Uint8Array(initData);
22256
+ decryptdata.keyId = keyId;
22257
+ keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = keySessionContextPromise.then(() => {
22258
+ return this.generateRequestWithPreferredKeySession(keyContext, initDataType, initData, 'encrypted-event-key-match');
22259
+ });
22260
+ break;
22261
+ }
22262
+ }
22263
+ if (!keySessionContextPromise) {
22264
+ // Clear-lead key (not encountered in playlist)
22265
+ keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = this.getKeySystemSelectionPromise([keySystemDomain]).then(({
22266
+ keySystem,
22267
+ mediaKeys
22268
+ }) => {
22269
+ var _keySystemToKeySystem;
22270
+ this.throwIfDestroyed();
22271
+ const decryptdata = new LevelKey('ISO-23001-7', keyIdHex, (_keySystemToKeySystem = keySystemDomainToKeySystemFormat(keySystem)) != null ? _keySystemToKeySystem : '');
22272
+ decryptdata.pssh = new Uint8Array(initData);
22273
+ decryptdata.keyId = keyId;
22274
+ return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => {
22275
+ this.throwIfDestroyed();
22276
+ const keySessionContext = this.createMediaKeySessionContext({
22277
+ decryptdata,
22278
+ keySystem,
22279
+ mediaKeys
22280
+ });
22281
+ return this.generateRequestWithPreferredKeySession(keySessionContext, initDataType, initData, 'encrypted-event-no-match');
22282
+ });
22283
+ });
22284
+ }
22285
+ keySessionContextPromise.catch(error => this.handleError(error));
22286
+ }
22287
+ _onWaitingForKey(event) {
22288
+ this.log(`"${event.type}" event`);
22289
+ }
22361
22290
  attemptSetMediaKeys(keySystem, mediaKeys) {
22362
22291
  const queue = this.setMediaKeysQueue.slice();
22363
22292
  this.log(`Setting media-keys for "${keySystem}"`);
@@ -24090,10 +24019,10 @@ class CMCDController {
24090
24019
  }
24091
24020
 
24092
24021
  const PATHWAY_PENALTY_DURATION_MS = 300000;
24093
- class ContentSteeringController extends Logger {
24022
+ class ContentSteeringController {
24094
24023
  constructor(hls) {
24095
- super('content-steering', hls.logger);
24096
24024
  this.hls = void 0;
24025
+ this.log = void 0;
24097
24026
  this.loader = null;
24098
24027
  this.uri = null;
24099
24028
  this.pathwayId = '.';
@@ -24108,6 +24037,7 @@ class ContentSteeringController extends Logger {
24108
24037
  this.subtitleTracks = null;
24109
24038
  this.penalizedPathways = {};
24110
24039
  this.hls = hls;
24040
+ this.log = logger.log.bind(logger, `[content-steering]:`);
24111
24041
  this.registerListeners();
24112
24042
  }
24113
24043
  registerListeners() {
@@ -24231,7 +24161,7 @@ class ContentSteeringController extends Logger {
24231
24161
  errorAction.resolved = this.pathwayId !== errorPathway;
24232
24162
  }
24233
24163
  if (!errorAction.resolved) {
24234
- this.warn(`Could not resolve ${data.details} ("${data.error.message}") with content-steering for Pathway: ${errorPathway} levels: ${levels ? levels.length : levels} priorities: ${JSON.stringify(pathwayPriority)} penalized: ${JSON.stringify(this.penalizedPathways)}`);
24164
+ logger.warn(`Could not resolve ${data.details} ("${data.error.message}") with content-steering for Pathway: ${errorPathway} levels: ${levels ? levels.length : levels} priorities: ${JSON.stringify(pathwayPriority)} penalized: ${JSON.stringify(this.penalizedPathways)}`);
24235
24165
  }
24236
24166
  }
24237
24167
  }
@@ -24402,7 +24332,7 @@ class ContentSteeringController extends Logger {
24402
24332
  onSuccess: (response, stats, context, networkDetails) => {
24403
24333
  this.log(`Loaded steering manifest: "${url}"`);
24404
24334
  const steeringData = response.data;
24405
- if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) {
24335
+ if (steeringData.VERSION !== 1) {
24406
24336
  this.log(`Steering VERSION ${steeringData.VERSION} not supported!`);
24407
24337
  return;
24408
24338
  }
@@ -25372,7 +25302,7 @@ function timelineConfig() {
25372
25302
  /**
25373
25303
  * @ignore
25374
25304
  */
25375
- function mergeConfig(defaultConfig, userConfig, logger) {
25305
+ function mergeConfig(defaultConfig, userConfig) {
25376
25306
  if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
25377
25307
  throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
25378
25308
  }
@@ -25442,7 +25372,7 @@ function deepCpy(obj) {
25442
25372
  /**
25443
25373
  * @ignore
25444
25374
  */
25445
- function enableStreamingMode(config, logger) {
25375
+ function enableStreamingMode(config) {
25446
25376
  const currentLoader = config.loader;
25447
25377
  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
25448
25378
  // If a developer has configured their own loader, respect that choice
@@ -25459,9 +25389,10 @@ function enableStreamingMode(config, logger) {
25459
25389
  }
25460
25390
  }
25461
25391
 
25392
+ let chromeOrFirefox;
25462
25393
  class LevelController extends BasePlaylistController {
25463
25394
  constructor(hls, contentSteeringController) {
25464
- super(hls, 'level-controller');
25395
+ super(hls, '[level-controller]');
25465
25396
  this._levels = [];
25466
25397
  this._firstLevel = -1;
25467
25398
  this._maxAutoLevel = -1;
@@ -25532,15 +25463,23 @@ class LevelController extends BasePlaylistController {
25532
25463
  let videoCodecFound = false;
25533
25464
  let audioCodecFound = false;
25534
25465
  data.levels.forEach(levelParsed => {
25535
- var _videoCodec;
25466
+ var _audioCodec, _videoCodec;
25536
25467
  const attributes = levelParsed.attrs;
25468
+
25469
+ // erase audio codec info if browser does not support mp4a.40.34.
25470
+ // demuxer will autodetect codec and fallback to mpeg/audio
25537
25471
  let {
25538
25472
  audioCodec,
25539
25473
  videoCodec
25540
25474
  } = levelParsed;
25475
+ if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) {
25476
+ chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));
25477
+ if (chromeOrFirefox) {
25478
+ levelParsed.audioCodec = audioCodec = undefined;
25479
+ }
25480
+ }
25541
25481
  if (audioCodec) {
25542
- // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
25543
- levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
25482
+ levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource);
25544
25483
  }
25545
25484
  if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
25546
25485
  videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
@@ -25665,8 +25604,8 @@ class LevelController extends BasePlaylistController {
25665
25604
  return valueB - valueA;
25666
25605
  }
25667
25606
  }
25668
- if (a.bitrate !== b.bitrate) {
25669
- return a.bitrate - b.bitrate;
25607
+ if (a.averageBitrate !== b.averageBitrate) {
25608
+ return a.averageBitrate - b.averageBitrate;
25670
25609
  }
25671
25610
  return 0;
25672
25611
  });
@@ -26126,8 +26065,6 @@ class KeyLoader {
26126
26065
  }
26127
26066
  return this.loadKeyEME(keyInfo, frag);
26128
26067
  case 'AES-128':
26129
- case 'AES-256':
26130
- case 'AES-256-CTR':
26131
26068
  return this.loadKeyHTTP(keyInfo, frag);
26132
26069
  default:
26133
26070
  return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Key supplied with unsupported METHOD: "${decryptdata.method}"`)));
@@ -26265,9 +26202,8 @@ const STALL_MINIMUM_DURATION_MS = 250;
26265
26202
  const MAX_START_GAP_JUMP = 2.0;
26266
26203
  const SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
26267
26204
  const SKIP_BUFFER_RANGE_START = 0.05;
26268
- class GapController extends Logger {
26205
+ class GapController {
26269
26206
  constructor(config, media, fragmentTracker, hls) {
26270
- super('gap-controller', hls.logger);
26271
26207
  this.config = void 0;
26272
26208
  this.media = null;
26273
26209
  this.fragmentTracker = void 0;
@@ -26321,7 +26257,7 @@ class GapController extends Logger {
26321
26257
  // The playhead is now moving, but was previously stalled
26322
26258
  if (this.stallReported) {
26323
26259
  const _stalledDuration = self.performance.now() - stalled;
26324
- this.warn(`playback not stuck anymore @${currentTime}, after ${Math.round(_stalledDuration)}ms`);
26260
+ logger.warn(`playback not stuck anymore @${currentTime}, after ${Math.round(_stalledDuration)}ms`);
26325
26261
  this.stallReported = false;
26326
26262
  }
26327
26263
  this.stalled = null;
@@ -26432,7 +26368,7 @@ class GapController extends Logger {
26432
26368
  // needs to cross some sort of threshold covering all source-buffers content
26433
26369
  // to start playing properly.
26434
26370
  if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
26435
- this.warn('Trying to nudge playhead over buffer-hole');
26371
+ logger.warn('Trying to nudge playhead over buffer-hole');
26436
26372
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
26437
26373
  // We only try to jump the hole if it's under the configured size
26438
26374
  // Reset stalled so to rearm watchdog timer
@@ -26456,7 +26392,7 @@ class GapController extends Logger {
26456
26392
  // Report stalled error once
26457
26393
  this.stallReported = true;
26458
26394
  const error = new Error(`Playback stalling at @${media.currentTime} due to low buffer (${JSON.stringify(bufferInfo)})`);
26459
- this.warn(error.message);
26395
+ logger.warn(error.message);
26460
26396
  hls.trigger(Events.ERROR, {
26461
26397
  type: ErrorTypes.MEDIA_ERROR,
26462
26398
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -26524,7 +26460,7 @@ class GapController extends Logger {
26524
26460
  }
26525
26461
  }
26526
26462
  const targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
26527
- this.warn(`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`);
26463
+ logger.warn(`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`);
26528
26464
  this.moved = true;
26529
26465
  this.stalled = null;
26530
26466
  media.currentTime = targetTime;
@@ -26565,7 +26501,7 @@ class GapController extends Logger {
26565
26501
  const targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
26566
26502
  // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
26567
26503
  const error = new Error(`Nudging 'currentTime' from ${currentTime} to ${targetTime}`);
26568
- this.warn(error.message);
26504
+ logger.warn(error.message);
26569
26505
  media.currentTime = targetTime;
26570
26506
  hls.trigger(Events.ERROR, {
26571
26507
  type: ErrorTypes.MEDIA_ERROR,
@@ -26575,7 +26511,7 @@ class GapController extends Logger {
26575
26511
  });
26576
26512
  } else {
26577
26513
  const error = new Error(`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`);
26578
- this.error(error.message);
26514
+ logger.error(error.message);
26579
26515
  hls.trigger(Events.ERROR, {
26580
26516
  type: ErrorTypes.MEDIA_ERROR,
26581
26517
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -26590,7 +26526,7 @@ const TICK_INTERVAL = 100; // how often to tick in ms
26590
26526
 
26591
26527
  class StreamController extends BaseStreamController {
26592
26528
  constructor(hls, fragmentTracker, keyLoader) {
26593
- super(hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN);
26529
+ super(hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN);
26594
26530
  this.audioCodecSwap = false;
26595
26531
  this.gapController = null;
26596
26532
  this.level = -1;
@@ -26598,43 +26534,27 @@ class StreamController extends BaseStreamController {
26598
26534
  this.altAudio = false;
26599
26535
  this.audioOnly = false;
26600
26536
  this.fragPlaying = null;
26537
+ this.onvplaying = null;
26538
+ this.onvseeked = null;
26601
26539
  this.fragLastKbps = 0;
26602
26540
  this.couldBacktrack = false;
26603
26541
  this.backtrackFragment = null;
26604
26542
  this.audioCodecSwitch = false;
26605
26543
  this.videoBuffer = null;
26606
- this.onMediaPlaying = () => {
26607
- // tick to speed up FRAG_CHANGED triggering
26608
- this.tick();
26609
- };
26610
- this.onMediaSeeked = () => {
26611
- const media = this.media;
26612
- const currentTime = media ? media.currentTime : null;
26613
- if (isFiniteNumber(currentTime)) {
26614
- this.log(`Media seeked to ${currentTime.toFixed(3)}`);
26615
- }
26616
-
26617
- // If seeked was issued before buffer was appended do not tick immediately
26618
- const bufferInfo = this.getMainFwdBufferInfo();
26619
- if (bufferInfo === null || bufferInfo.len === 0) {
26620
- this.warn(`Main forward buffer length on "seeked" event ${bufferInfo ? bufferInfo.len : 'empty'})`);
26621
- return;
26622
- }
26623
-
26624
- // tick to speed up FRAG_CHANGED triggering
26625
- this.tick();
26626
- };
26627
- this.registerListeners();
26544
+ this._registerListeners();
26628
26545
  }
26629
- registerListeners() {
26630
- super.registerListeners();
26546
+ _registerListeners() {
26631
26547
  const {
26632
26548
  hls
26633
26549
  } = this;
26550
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
26551
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
26552
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
26634
26553
  hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
26635
26554
  hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
26636
26555
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
26637
26556
  hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
26557
+ hls.on(Events.ERROR, this.onError, this);
26638
26558
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
26639
26559
  hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
26640
26560
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -26642,14 +26562,17 @@ class StreamController extends BaseStreamController {
26642
26562
  hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
26643
26563
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
26644
26564
  }
26645
- unregisterListeners() {
26646
- super.unregisterListeners();
26565
+ _unregisterListeners() {
26647
26566
  const {
26648
26567
  hls
26649
26568
  } = this;
26569
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
26570
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
26571
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
26650
26572
  hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
26651
26573
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
26652
26574
  hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
26575
+ hls.off(Events.ERROR, this.onError, this);
26653
26576
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
26654
26577
  hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
26655
26578
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -26658,9 +26581,7 @@ class StreamController extends BaseStreamController {
26658
26581
  hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
26659
26582
  }
26660
26583
  onHandlerDestroying() {
26661
- // @ts-ignore
26662
- this.onMediaPlaying = this.onMediaSeeked = null;
26663
- this.unregisterListeners();
26584
+ this._unregisterListeners();
26664
26585
  super.onHandlerDestroying();
26665
26586
  }
26666
26587
  startLoad(startPosition) {
@@ -26987,17 +26908,20 @@ class StreamController extends BaseStreamController {
26987
26908
  onMediaAttached(event, data) {
26988
26909
  super.onMediaAttached(event, data);
26989
26910
  const media = data.media;
26990
- media.addEventListener('playing', this.onMediaPlaying);
26991
- media.addEventListener('seeked', this.onMediaSeeked);
26911
+ this.onvplaying = this.onMediaPlaying.bind(this);
26912
+ this.onvseeked = this.onMediaSeeked.bind(this);
26913
+ media.addEventListener('playing', this.onvplaying);
26914
+ media.addEventListener('seeked', this.onvseeked);
26992
26915
  this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
26993
26916
  }
26994
26917
  onMediaDetaching() {
26995
26918
  const {
26996
26919
  media
26997
26920
  } = this;
26998
- if (media) {
26999
- media.removeEventListener('playing', this.onMediaPlaying);
27000
- media.removeEventListener('seeked', this.onMediaSeeked);
26921
+ if (media && this.onvplaying && this.onvseeked) {
26922
+ media.removeEventListener('playing', this.onvplaying);
26923
+ media.removeEventListener('seeked', this.onvseeked);
26924
+ this.onvplaying = this.onvseeked = null;
27001
26925
  this.videoBuffer = null;
27002
26926
  }
27003
26927
  this.fragPlaying = null;
@@ -27007,6 +26931,27 @@ class StreamController extends BaseStreamController {
27007
26931
  }
27008
26932
  super.onMediaDetaching();
27009
26933
  }
26934
+ onMediaPlaying() {
26935
+ // tick to speed up FRAG_CHANGED triggering
26936
+ this.tick();
26937
+ }
26938
+ onMediaSeeked() {
26939
+ const media = this.media;
26940
+ const currentTime = media ? media.currentTime : null;
26941
+ if (isFiniteNumber(currentTime)) {
26942
+ this.log(`Media seeked to ${currentTime.toFixed(3)}`);
26943
+ }
26944
+
26945
+ // If seeked was issued before buffer was appended do not tick immediately
26946
+ const bufferInfo = this.getMainFwdBufferInfo();
26947
+ if (bufferInfo === null || bufferInfo.len === 0) {
26948
+ this.warn(`Main forward buffer length on "seeked" event ${bufferInfo ? bufferInfo.len : 'empty'})`);
26949
+ return;
26950
+ }
26951
+
26952
+ // tick to speed up FRAG_CHANGED triggering
26953
+ this.tick();
26954
+ }
27010
26955
  onManifestLoading() {
27011
26956
  // reset buffer on manifest loading
27012
26957
  this.log('Trigger BUFFER_RESET');
@@ -27737,7 +27682,7 @@ class Hls {
27737
27682
  * Get the video-dev/hls.js package version.
27738
27683
  */
27739
27684
  static get version() {
27740
- return "1.5.2-0.canary.9924";
27685
+ return "1.5.2";
27741
27686
  }
27742
27687
 
27743
27688
  /**
@@ -27800,10 +27745,6 @@ class Hls {
27800
27745
  * The configuration object provided on player instantiation.
27801
27746
  */
27802
27747
  this.userConfig = void 0;
27803
- /**
27804
- * The logger functions used by this player instance, configured on player instantiation.
27805
- */
27806
- this.logger = void 0;
27807
27748
  this.coreComponents = void 0;
27808
27749
  this.networkControllers = void 0;
27809
27750
  this.started = false;
@@ -27823,11 +27764,11 @@ class Hls {
27823
27764
  this._media = null;
27824
27765
  this.url = null;
27825
27766
  this.triggeringException = void 0;
27826
- const logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance');
27827
- const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
27767
+ enableLogs(userConfig.debug || false, 'Hls instance');
27768
+ const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
27828
27769
  this.userConfig = userConfig;
27829
27770
  if (config.progressive) {
27830
- enableStreamingMode(config, logger);
27771
+ enableStreamingMode(config);
27831
27772
  }
27832
27773
 
27833
27774
  // core controllers and network loaders
@@ -27926,7 +27867,7 @@ class Hls {
27926
27867
  try {
27927
27868
  return this.emit(event, event, eventObject);
27928
27869
  } catch (error) {
27929
- this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
27870
+ logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
27930
27871
  // Prevent recursion in error event handlers that throw #5497
27931
27872
  if (!this.triggeringException) {
27932
27873
  this.triggeringException = true;
@@ -27952,7 +27893,7 @@ class Hls {
27952
27893
  * Dispose of the instance
27953
27894
  */
27954
27895
  destroy() {
27955
- this.logger.log('destroy');
27896
+ logger.log('destroy');
27956
27897
  this.trigger(Events.DESTROYING, undefined);
27957
27898
  this.detachMedia();
27958
27899
  this.removeAllListeners();
@@ -27973,7 +27914,7 @@ class Hls {
27973
27914
  * Attaches Hls.js to a media element
27974
27915
  */
27975
27916
  attachMedia(media) {
27976
- this.logger.log('attachMedia');
27917
+ logger.log('attachMedia');
27977
27918
  this._media = media;
27978
27919
  this.trigger(Events.MEDIA_ATTACHING, {
27979
27920
  media: media
@@ -27984,7 +27925,7 @@ class Hls {
27984
27925
  * Detach Hls.js from the media
27985
27926
  */
27986
27927
  detachMedia() {
27987
- this.logger.log('detachMedia');
27928
+ logger.log('detachMedia');
27988
27929
  this.trigger(Events.MEDIA_DETACHING, undefined);
27989
27930
  this._media = null;
27990
27931
  }
@@ -28001,7 +27942,7 @@ class Hls {
28001
27942
  });
28002
27943
  this._autoLevelCapping = -1;
28003
27944
  this._maxHdcpLevel = null;
28004
- this.logger.log(`loadSource:${loadingSource}`);
27945
+ logger.log(`loadSource:${loadingSource}`);
28005
27946
  if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
28006
27947
  this.detachMedia();
28007
27948
  this.attachMedia(media);
@@ -28020,7 +27961,7 @@ class Hls {
28020
27961
  * Defaults to -1 (None: starts from earliest point)
28021
27962
  */
28022
27963
  startLoad(startPosition = -1) {
28023
- this.logger.log(`startLoad(${startPosition})`);
27964
+ logger.log(`startLoad(${startPosition})`);
28024
27965
  this.started = true;
28025
27966
  this.networkControllers.forEach(controller => {
28026
27967
  controller.startLoad(startPosition);
@@ -28031,7 +27972,7 @@ class Hls {
28031
27972
  * Stop loading of any stream data.
28032
27973
  */
28033
27974
  stopLoad() {
28034
- this.logger.log('stopLoad');
27975
+ logger.log('stopLoad');
28035
27976
  this.started = false;
28036
27977
  this.networkControllers.forEach(controller => {
28037
27978
  controller.stopLoad();
@@ -28067,7 +28008,7 @@ class Hls {
28067
28008
  * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
28068
28009
  */
28069
28010
  swapAudioCodec() {
28070
- this.logger.log('swapAudioCodec');
28011
+ logger.log('swapAudioCodec');
28071
28012
  this.streamController.swapAudioCodec();
28072
28013
  }
28073
28014
 
@@ -28078,7 +28019,7 @@ class Hls {
28078
28019
  * Automatic recovery of media-errors by this process is configurable.
28079
28020
  */
28080
28021
  recoverMediaError() {
28081
- this.logger.log('recoverMediaError');
28022
+ logger.log('recoverMediaError');
28082
28023
  const media = this._media;
28083
28024
  this.detachMedia();
28084
28025
  if (media) {
@@ -28108,7 +28049,7 @@ class Hls {
28108
28049
  * Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection.
28109
28050
  */
28110
28051
  set currentLevel(newLevel) {
28111
- this.logger.log(`set currentLevel:${newLevel}`);
28052
+ logger.log(`set currentLevel:${newLevel}`);
28112
28053
  this.levelController.manualLevel = newLevel;
28113
28054
  this.streamController.immediateLevelSwitch();
28114
28055
  }
@@ -28127,7 +28068,7 @@ class Hls {
28127
28068
  * @param newLevel - Pass -1 for automatic level selection
28128
28069
  */
28129
28070
  set nextLevel(newLevel) {
28130
- this.logger.log(`set nextLevel:${newLevel}`);
28071
+ logger.log(`set nextLevel:${newLevel}`);
28131
28072
  this.levelController.manualLevel = newLevel;
28132
28073
  this.streamController.nextLevelSwitch();
28133
28074
  }
@@ -28146,7 +28087,7 @@ class Hls {
28146
28087
  * @param newLevel - Pass -1 for automatic level selection
28147
28088
  */
28148
28089
  set loadLevel(newLevel) {
28149
- this.logger.log(`set loadLevel:${newLevel}`);
28090
+ logger.log(`set loadLevel:${newLevel}`);
28150
28091
  this.levelController.manualLevel = newLevel;
28151
28092
  }
28152
28093
 
@@ -28177,7 +28118,7 @@ class Hls {
28177
28118
  * Sets "first-level", see getter.
28178
28119
  */
28179
28120
  set firstLevel(newLevel) {
28180
- this.logger.log(`set firstLevel:${newLevel}`);
28121
+ logger.log(`set firstLevel:${newLevel}`);
28181
28122
  this.levelController.firstLevel = newLevel;
28182
28123
  }
28183
28124
 
@@ -28202,7 +28143,7 @@ class Hls {
28202
28143
  * (determined from download of first segment)
28203
28144
  */
28204
28145
  set startLevel(newLevel) {
28205
- this.logger.log(`set startLevel:${newLevel}`);
28146
+ logger.log(`set startLevel:${newLevel}`);
28206
28147
  // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
28207
28148
  if (newLevel !== -1) {
28208
28149
  newLevel = Math.max(newLevel, this.minAutoLevel);
@@ -28277,7 +28218,7 @@ class Hls {
28277
28218
  */
28278
28219
  set autoLevelCapping(newLevel) {
28279
28220
  if (this._autoLevelCapping !== newLevel) {
28280
- this.logger.log(`set autoLevelCapping:${newLevel}`);
28221
+ logger.log(`set autoLevelCapping:${newLevel}`);
28281
28222
  this._autoLevelCapping = newLevel;
28282
28223
  this.levelController.checkMaxAutoUpdated();
28283
28224
  }