hls.js 1.5.4 → 1.5.5-0.canary.9978

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 (67) hide show
  1. package/README.md +1 -0
  2. package/dist/hls-demo.js +10 -0
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +1935 -1094
  5. package/dist/hls.js.d.ts +63 -50
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1059 -838
  8. package/dist/hls.light.js.map +1 -1
  9. package/dist/hls.light.min.js +1 -1
  10. package/dist/hls.light.min.js.map +1 -1
  11. package/dist/hls.light.mjs +846 -626
  12. package/dist/hls.light.mjs.map +1 -1
  13. package/dist/hls.min.js +1 -1
  14. package/dist/hls.min.js.map +1 -1
  15. package/dist/hls.mjs +1640 -814
  16. package/dist/hls.mjs.map +1 -1
  17. package/dist/hls.worker.js +1 -1
  18. package/dist/hls.worker.js.map +1 -1
  19. package/package.json +18 -18
  20. package/src/config.ts +3 -2
  21. package/src/controller/abr-controller.ts +21 -20
  22. package/src/controller/audio-stream-controller.ts +15 -16
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +7 -7
  25. package/src/controller/base-stream-controller.ts +56 -29
  26. package/src/controller/buffer-controller.ts +11 -11
  27. package/src/controller/cap-level-controller.ts +1 -2
  28. package/src/controller/cmcd-controller.ts +25 -3
  29. package/src/controller/content-steering-controller.ts +8 -6
  30. package/src/controller/eme-controller.ts +9 -22
  31. package/src/controller/error-controller.ts +6 -8
  32. package/src/controller/fps-controller.ts +2 -3
  33. package/src/controller/gap-controller.ts +43 -16
  34. package/src/controller/latency-controller.ts +9 -11
  35. package/src/controller/level-controller.ts +5 -17
  36. package/src/controller/stream-controller.ts +25 -32
  37. package/src/controller/subtitle-stream-controller.ts +13 -14
  38. package/src/controller/subtitle-track-controller.ts +5 -3
  39. package/src/controller/timeline-controller.ts +23 -30
  40. package/src/crypt/aes-crypto.ts +21 -2
  41. package/src/crypt/decrypter-aes-mode.ts +4 -0
  42. package/src/crypt/decrypter.ts +32 -18
  43. package/src/crypt/fast-aes-key.ts +24 -5
  44. package/src/demux/audio/adts.ts +9 -4
  45. package/src/demux/sample-aes.ts +2 -0
  46. package/src/demux/transmuxer-interface.ts +4 -12
  47. package/src/demux/transmuxer-worker.ts +4 -4
  48. package/src/demux/transmuxer.ts +16 -3
  49. package/src/demux/tsdemuxer.ts +71 -37
  50. package/src/demux/video/avc-video-parser.ts +208 -119
  51. package/src/demux/video/base-video-parser.ts +134 -2
  52. package/src/demux/video/exp-golomb.ts +0 -208
  53. package/src/demux/video/hevc-video-parser.ts +746 -0
  54. package/src/events.ts +7 -0
  55. package/src/hls.ts +42 -34
  56. package/src/loader/fragment-loader.ts +9 -2
  57. package/src/loader/key-loader.ts +2 -0
  58. package/src/loader/level-key.ts +10 -9
  59. package/src/remux/mp4-generator.ts +196 -1
  60. package/src/remux/mp4-remuxer.ts +23 -7
  61. package/src/task-loop.ts +5 -2
  62. package/src/types/component-api.ts +2 -0
  63. package/src/types/demuxer.ts +3 -0
  64. package/src/types/events.ts +4 -0
  65. package/src/utils/codecs.ts +33 -4
  66. package/src/utils/encryption-methods-util.ts +21 -0
  67. package/src/utils/logger.ts +53 -24
package/dist/hls.light.js CHANGED
@@ -5,6 +5,21 @@
5
5
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Hls = factory());
6
6
  })(this, (function () { 'use strict';
7
7
 
8
+ function _construct(t, e, r) {
9
+ if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments);
10
+ var o = [null];
11
+ o.push.apply(o, e);
12
+ var p = new (t.bind.apply(t, o))();
13
+ return r && _setPrototypeOf(p, r.prototype), p;
14
+ }
15
+ function _isNativeReflectConstruct() {
16
+ try {
17
+ var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
18
+ } catch (t) {}
19
+ return (_isNativeReflectConstruct = function () {
20
+ return !!t;
21
+ })();
22
+ }
8
23
  function ownKeys(e, r) {
9
24
  var t = Object.keys(e);
10
25
  if (Object.getOwnPropertySymbols) {
@@ -103,32 +118,6 @@
103
118
  };
104
119
  return _setPrototypeOf(o, p);
105
120
  }
106
- function _isNativeReflectConstruct() {
107
- if (typeof Reflect === "undefined" || !Reflect.construct) return false;
108
- if (Reflect.construct.sham) return false;
109
- if (typeof Proxy === "function") return true;
110
- try {
111
- Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
112
- return true;
113
- } catch (e) {
114
- return false;
115
- }
116
- }
117
- function _construct(Parent, args, Class) {
118
- if (_isNativeReflectConstruct()) {
119
- _construct = Reflect.construct.bind();
120
- } else {
121
- _construct = function _construct(Parent, args, Class) {
122
- var a = [null];
123
- a.push.apply(a, args);
124
- var Constructor = Function.bind.apply(Parent, a);
125
- var instance = new Constructor();
126
- if (Class) _setPrototypeOf(instance, Class.prototype);
127
- return instance;
128
- };
129
- }
130
- return _construct.apply(null, arguments);
131
- }
132
121
  function _isNativeFunction(fn) {
133
122
  try {
134
123
  return Function.toString.call(fn).indexOf("[native code]") !== -1;
@@ -363,6 +352,7 @@
363
352
  Events["MEDIA_ATTACHED"] = "hlsMediaAttached";
364
353
  Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
365
354
  Events["MEDIA_DETACHED"] = "hlsMediaDetached";
355
+ Events["MEDIA_ENDED"] = "hlsMediaEnded";
366
356
  Events["BUFFER_RESET"] = "hlsBufferReset";
367
357
  Events["BUFFER_CODECS"] = "hlsBufferCodecs";
368
358
  Events["BUFFER_CREATED"] = "hlsBufferCreated";
@@ -476,6 +466,21 @@
476
466
  return ErrorDetails;
477
467
  }({});
478
468
 
469
+ var Logger = function Logger(label, logger) {
470
+ this.trace = void 0;
471
+ this.debug = void 0;
472
+ this.log = void 0;
473
+ this.warn = void 0;
474
+ this.info = void 0;
475
+ this.error = void 0;
476
+ var lb = "[" + label + "]:";
477
+ this.trace = noop;
478
+ this.debug = logger.debug.bind(null, lb);
479
+ this.log = logger.log.bind(null, lb);
480
+ this.warn = logger.warn.bind(null, lb);
481
+ this.info = logger.info.bind(null, lb);
482
+ this.error = logger.error.bind(null, lb);
483
+ };
479
484
  var noop = function noop() {};
480
485
  var fakeLogger = {
481
486
  trace: noop,
@@ -485,7 +490,9 @@
485
490
  info: noop,
486
491
  error: noop
487
492
  };
488
- var exportedLogger = fakeLogger;
493
+ function createLogger() {
494
+ return _extends({}, fakeLogger);
495
+ }
489
496
 
490
497
  // let lastCallTime;
491
498
  // function formatMsgWithTimeInfo(type, msg) {
@@ -496,38 +503,36 @@
496
503
  // return msg;
497
504
  // }
498
505
 
499
- function consolePrintFn(type) {
506
+ function consolePrintFn(type, id) {
500
507
  var func = self.console[type];
501
- if (func) {
502
- return func.bind(self.console, "[" + type + "] >");
503
- }
504
- return noop;
508
+ return func ? func.bind(self.console, (id ? '[' + id + '] ' : '') + "[" + type + "] >") : noop;
505
509
  }
506
- function exportLoggerFunctions(debugConfig) {
507
- for (var _len = arguments.length, functions = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
508
- functions[_key - 1] = arguments[_key];
509
- }
510
- functions.forEach(function (type) {
511
- exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
512
- });
510
+ function getLoggerFn(key, debugConfig, id) {
511
+ return debugConfig[key] ? debugConfig[key].bind(debugConfig) : consolePrintFn(key, id);
513
512
  }
514
- function enableLogs(debugConfig, id) {
513
+ var exportedLogger = createLogger();
514
+ function enableLogs(debugConfig, context, id) {
515
515
  // check that console is available
516
+ var newLogger = createLogger();
516
517
  if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') {
517
- exportLoggerFunctions(debugConfig,
518
+ var keys = [
518
519
  // Remove out from list here to hard-disable a log-level
519
520
  // 'trace',
520
- 'debug', 'log', 'info', 'warn', 'error');
521
+ 'debug', 'log', 'info', 'warn', 'error'];
522
+ keys.forEach(function (key) {
523
+ newLogger[key] = getLoggerFn(key, debugConfig, id);
524
+ });
521
525
  // Some browsers don't allow to use bind on console object anyway
522
526
  // fallback to default if needed
523
527
  try {
524
- exportedLogger.log("Debug logs enabled for \"" + id + "\" in hls.js version " + "1.5.4");
528
+ newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.5.5-0.canary.9978");
525
529
  } catch (e) {
526
- exportedLogger = fakeLogger;
530
+ /* log fn threw an exception. All logger methods are no-ops. */
531
+ return createLogger();
527
532
  }
528
- } else {
529
- exportedLogger = fakeLogger;
530
533
  }
534
+ exportedLogger = newLogger;
535
+ return newLogger;
531
536
  }
532
537
  var logger = exportedLogger;
533
538
 
@@ -1173,10 +1178,30 @@
1173
1178
  return LevelDetails;
1174
1179
  }();
1175
1180
 
1181
+ var DecrypterAesMode = {
1182
+ cbc: 0,
1183
+ ctr: 1
1184
+ };
1185
+
1186
+ function isFullSegmentEncryption(method) {
1187
+ return method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR';
1188
+ }
1189
+ function getAesModeFromFullSegmentMethod(method) {
1190
+ switch (method) {
1191
+ case 'AES-128':
1192
+ case 'AES-256':
1193
+ return DecrypterAesMode.cbc;
1194
+ case 'AES-256-CTR':
1195
+ return DecrypterAesMode.ctr;
1196
+ default:
1197
+ throw new Error("invalid full segment method " + method);
1198
+ }
1199
+ }
1200
+
1176
1201
  // This file is inserted as a shim for modules which we do not want to include into the distro.
1177
1202
  // This replacement is done in the "alias" plugin of the rollup config.
1178
1203
  var empty = undefined;
1179
- var Cues = /*@__PURE__*/getDefaultExportFromCjs(empty);
1204
+ var HevcVideoParser = /*@__PURE__*/getDefaultExportFromCjs(empty);
1180
1205
 
1181
1206
  function sliceUint8(array, start, end) {
1182
1207
  // @ts-expect-error This polyfills IE11 usage of Uint8Array slice.
@@ -2626,13 +2651,13 @@
2626
2651
  this.keyFormatVersions = formatversions;
2627
2652
  this.iv = iv;
2628
2653
  this.encrypted = method ? method !== 'NONE' : false;
2629
- this.isCommonEncryption = this.encrypted && method !== 'AES-128';
2654
+ this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method);
2630
2655
  }
2631
2656
  var _proto = LevelKey.prototype;
2632
2657
  _proto.isSupported = function isSupported() {
2633
2658
  // If it's Segment encryption or No encryption, just select that key system
2634
2659
  if (this.method) {
2635
- if (this.method === 'AES-128' || this.method === 'NONE') {
2660
+ if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
2636
2661
  return true;
2637
2662
  }
2638
2663
  if (this.keyFormat === 'identity') {
@@ -2646,14 +2671,13 @@
2646
2671
  if (!this.encrypted || !this.uri) {
2647
2672
  return null;
2648
2673
  }
2649
- if (this.method === 'AES-128' && this.uri && !this.iv) {
2674
+ if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
2650
2675
  if (typeof sn !== 'number') {
2651
2676
  // We are fetching decryption data for a initialization segment
2652
- // If the segment was encrypted with AES-128
2677
+ // If the segment was encrypted with AES-128/256
2653
2678
  // It must have an IV defined. We cannot substitute the Segment Number in.
2654
- if (this.method === 'AES-128' && !this.iv) {
2655
- logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
2656
- }
2679
+ logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
2680
+
2657
2681
  // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
2658
2682
  sn = 0;
2659
2683
  }
@@ -2815,23 +2839,28 @@
2815
2839
  if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
2816
2840
  return CODEC_COMPATIBLE_NAMES[lowerCaseCodec];
2817
2841
  }
2818
-
2819
- // Idealy fLaC and Opus would be first (spec-compliant) but
2820
- // some browsers will report that fLaC is supported then fail.
2821
- // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
2822
2842
  var codecsToCheck = {
2843
+ // Idealy fLaC and Opus would be first (spec-compliant) but
2844
+ // some browsers will report that fLaC is supported then fail.
2845
+ // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
2823
2846
  flac: ['flac', 'fLaC', 'FLAC'],
2824
- opus: ['opus', 'Opus']
2847
+ opus: ['opus', 'Opus'],
2848
+ // Replace audio codec info if browser does not support mp4a.40.34,
2849
+ // and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
2850
+ 'mp4a.40.34': ['mp3']
2825
2851
  }[lowerCaseCodec];
2826
2852
  for (var i = 0; i < codecsToCheck.length; i++) {
2853
+ var _getMediaSource;
2827
2854
  if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) {
2828
2855
  CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
2829
2856
  return codecsToCheck[i];
2857
+ } else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) {
2858
+ return '';
2830
2859
  }
2831
2860
  }
2832
2861
  return lowerCaseCodec;
2833
2862
  }
2834
- var AUDIO_CODEC_REGEXP = /flac|opus/i;
2863
+ var AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
2835
2864
  function getCodecCompatibleName(codec, preferManagedMediaSource) {
2836
2865
  if (preferManagedMediaSource === void 0) {
2837
2866
  preferManagedMediaSource = true;
@@ -2859,6 +2888,18 @@
2859
2888
  }
2860
2889
  return codec;
2861
2890
  }
2891
+ function getM2TSSupportedAudioTypes(preferManagedMediaSource) {
2892
+ var MediaSource = getMediaSource(preferManagedMediaSource) || {
2893
+ isTypeSupported: function isTypeSupported() {
2894
+ return false;
2895
+ }
2896
+ };
2897
+ return {
2898
+ mpeg: MediaSource.isTypeSupported('audio/mpeg'),
2899
+ mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
2900
+ ac3: false
2901
+ };
2902
+ }
2862
2903
 
2863
2904
  var 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;
2864
2905
  var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
@@ -4433,8 +4474,43 @@
4433
4474
  this.currentTime = 0;
4434
4475
  this.stallCount = 0;
4435
4476
  this._latency = null;
4436
- this.timeupdateHandler = function () {
4437
- return _this.timeupdate();
4477
+ this.onTimeupdate = function () {
4478
+ var media = _this.media,
4479
+ levelDetails = _this.levelDetails;
4480
+ if (!media || !levelDetails) {
4481
+ return;
4482
+ }
4483
+ _this.currentTime = media.currentTime;
4484
+ var latency = _this.computeLatency();
4485
+ if (latency === null) {
4486
+ return;
4487
+ }
4488
+ _this._latency = latency;
4489
+
4490
+ // Adapt playbackRate to meet target latency in low-latency mode
4491
+ var _this$config = _this.config,
4492
+ lowLatencyMode = _this$config.lowLatencyMode,
4493
+ maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
4494
+ if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4495
+ return;
4496
+ }
4497
+ var targetLatency = _this.targetLatency;
4498
+ if (targetLatency === null) {
4499
+ return;
4500
+ }
4501
+ var distanceFromTarget = latency - targetLatency;
4502
+ // Only adjust playbackRate when within one target duration of targetLatency
4503
+ // and more than one second from under-buffering.
4504
+ // Playback further than one target duration from target can be considered DVR playback.
4505
+ var liveMinLatencyDuration = Math.min(_this.maxLatency, targetLatency + levelDetails.targetduration);
4506
+ var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4507
+ if (inLiveRange && distanceFromTarget > 0.05 && _this.forwardBufferLength > 1) {
4508
+ var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4509
+ var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - _this.edgeStalled)) * 20) / 20;
4510
+ media.playbackRate = Math.min(max, Math.max(1, rate));
4511
+ } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4512
+ media.playbackRate = 1;
4513
+ }
4438
4514
  };
4439
4515
  this.hls = hls;
4440
4516
  this.config = hls.config;
@@ -4446,7 +4522,7 @@
4446
4522
  this.onMediaDetaching();
4447
4523
  this.levelDetails = null;
4448
4524
  // @ts-ignore
4449
- this.hls = this.timeupdateHandler = null;
4525
+ this.hls = null;
4450
4526
  };
4451
4527
  _proto.registerListeners = function registerListeners() {
4452
4528
  this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
@@ -4464,11 +4540,11 @@
4464
4540
  };
4465
4541
  _proto.onMediaAttached = function onMediaAttached(event, data) {
4466
4542
  this.media = data.media;
4467
- this.media.addEventListener('timeupdate', this.timeupdateHandler);
4543
+ this.media.addEventListener('timeupdate', this.onTimeupdate);
4468
4544
  };
4469
4545
  _proto.onMediaDetaching = function onMediaDetaching() {
4470
4546
  if (this.media) {
4471
- this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4547
+ this.media.removeEventListener('timeupdate', this.onTimeupdate);
4472
4548
  this.media = null;
4473
4549
  }
4474
4550
  };
@@ -4481,10 +4557,10 @@
4481
4557
  var details = _ref.details;
4482
4558
  this.levelDetails = details;
4483
4559
  if (details.advanced) {
4484
- this.timeupdate();
4560
+ this.onTimeupdate();
4485
4561
  }
4486
4562
  if (!details.live && this.media) {
4487
- this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4563
+ this.media.removeEventListener('timeupdate', this.onTimeupdate);
4488
4564
  }
4489
4565
  };
4490
4566
  _proto.onError = function onError(event, data) {
@@ -4494,45 +4570,7 @@
4494
4570
  }
4495
4571
  this.stallCount++;
4496
4572
  if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
4497
- logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency');
4498
- }
4499
- };
4500
- _proto.timeupdate = function timeupdate() {
4501
- var media = this.media,
4502
- levelDetails = this.levelDetails;
4503
- if (!media || !levelDetails) {
4504
- return;
4505
- }
4506
- this.currentTime = media.currentTime;
4507
- var latency = this.computeLatency();
4508
- if (latency === null) {
4509
- return;
4510
- }
4511
- this._latency = latency;
4512
-
4513
- // Adapt playbackRate to meet target latency in low-latency mode
4514
- var _this$config = this.config,
4515
- lowLatencyMode = _this$config.lowLatencyMode,
4516
- maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
4517
- if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4518
- return;
4519
- }
4520
- var targetLatency = this.targetLatency;
4521
- if (targetLatency === null) {
4522
- return;
4523
- }
4524
- var distanceFromTarget = latency - targetLatency;
4525
- // Only adjust playbackRate when within one target duration of targetLatency
4526
- // and more than one second from under-buffering.
4527
- // Playback further than one target duration from target can be considered DVR playback.
4528
- var liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
4529
- var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4530
- if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
4531
- var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4532
- var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
4533
- media.playbackRate = Math.min(max, Math.max(1, rate));
4534
- } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4535
- media.playbackRate = 1;
4573
+ this.hls.logger.warn('[latency-controller]: Stall detected, adjusting target latency');
4536
4574
  }
4537
4575
  };
4538
4576
  _proto.estimateLiveEdge = function estimateLiveEdge() {
@@ -5440,19 +5478,17 @@
5440
5478
  MoveAllAlternatesMatchingHDCP: 2,
5441
5479
  SwitchToSDR: 4
5442
5480
  }; // Reserved for future use
5443
- var ErrorController = /*#__PURE__*/function () {
5481
+ var ErrorController = /*#__PURE__*/function (_Logger) {
5482
+ _inheritsLoose(ErrorController, _Logger);
5444
5483
  function ErrorController(hls) {
5445
- this.hls = void 0;
5446
- this.playlistError = 0;
5447
- this.penalizedRenditions = {};
5448
- this.log = void 0;
5449
- this.warn = void 0;
5450
- this.error = void 0;
5451
- this.hls = hls;
5452
- this.log = logger.log.bind(logger, "[info]:");
5453
- this.warn = logger.warn.bind(logger, "[warning]:");
5454
- this.error = logger.error.bind(logger, "[error]:");
5455
- this.registerListeners();
5484
+ var _this;
5485
+ _this = _Logger.call(this, 'error-controller', hls.logger) || this;
5486
+ _this.hls = void 0;
5487
+ _this.playlistError = 0;
5488
+ _this.penalizedRenditions = {};
5489
+ _this.hls = hls;
5490
+ _this.registerListeners();
5491
+ return _this;
5456
5492
  }
5457
5493
  var _proto = ErrorController.prototype;
5458
5494
  _proto.registerListeners = function registerListeners() {
@@ -5808,19 +5844,19 @@
5808
5844
  }
5809
5845
  };
5810
5846
  return ErrorController;
5811
- }();
5847
+ }(Logger);
5812
5848
 
5813
- var BasePlaylistController = /*#__PURE__*/function () {
5849
+ var BasePlaylistController = /*#__PURE__*/function (_Logger) {
5850
+ _inheritsLoose(BasePlaylistController, _Logger);
5814
5851
  function BasePlaylistController(hls, logPrefix) {
5815
- this.hls = void 0;
5816
- this.timer = -1;
5817
- this.requestScheduled = -1;
5818
- this.canLoad = false;
5819
- this.log = void 0;
5820
- this.warn = void 0;
5821
- this.log = logger.log.bind(logger, logPrefix + ":");
5822
- this.warn = logger.warn.bind(logger, logPrefix + ":");
5823
- this.hls = hls;
5852
+ var _this;
5853
+ _this = _Logger.call(this, logPrefix, hls.logger) || this;
5854
+ _this.hls = void 0;
5855
+ _this.timer = -1;
5856
+ _this.requestScheduled = -1;
5857
+ _this.canLoad = false;
5858
+ _this.hls = hls;
5859
+ return _this;
5824
5860
  }
5825
5861
  var _proto = BasePlaylistController.prototype;
5826
5862
  _proto.destroy = function destroy() {
@@ -5853,7 +5889,7 @@
5853
5889
  try {
5854
5890
  uri = new self.URL(attr.URI, previous.url).href;
5855
5891
  } catch (error) {
5856
- logger.warn("Could not construct new URL for Rendition Report: " + error);
5892
+ this.warn("Could not construct new URL for Rendition Report: " + error);
5857
5893
  uri = attr.URI || '';
5858
5894
  }
5859
5895
  // Use exact match. Otherwise, the last partial match, if any, will be used
@@ -5892,7 +5928,7 @@
5892
5928
  return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist);
5893
5929
  };
5894
5930
  _proto.playlistLoaded = function playlistLoaded(index, data, previousDetails) {
5895
- var _this = this;
5931
+ var _this2 = this;
5896
5932
  var details = data.details,
5897
5933
  stats = data.stats;
5898
5934
 
@@ -5997,7 +6033,7 @@
5997
6033
  // );
5998
6034
 
5999
6035
  this.timer = self.setTimeout(function () {
6000
- return _this.loadPlaylist(deliveryDirectives);
6036
+ return _this2.loadPlaylist(deliveryDirectives);
6001
6037
  }, estimatedTimeUntilUpdate);
6002
6038
  } else {
6003
6039
  this.clearTimer();
@@ -6013,7 +6049,7 @@
6013
6049
  return new HlsUrlParameters(msn, part, skip);
6014
6050
  };
6015
6051
  _proto.checkRetry = function checkRetry(errorEvent) {
6016
- var _this2 = this;
6052
+ var _this3 = this;
6017
6053
  var errorDetails = errorEvent.details;
6018
6054
  var isTimeout = isTimeoutError(errorEvent);
6019
6055
  var errorAction = errorEvent.errorAction;
@@ -6037,7 +6073,7 @@
6037
6073
  var delay = getRetryDelay(retryConfig, retryCount);
6038
6074
  // Schedule level/track reload
6039
6075
  this.timer = self.setTimeout(function () {
6040
- return _this2.loadPlaylist();
6076
+ return _this3.loadPlaylist();
6041
6077
  }, delay);
6042
6078
  this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" in " + delay + "ms");
6043
6079
  }
@@ -6048,7 +6084,7 @@
6048
6084
  return retry;
6049
6085
  };
6050
6086
  return BasePlaylistController;
6051
- }();
6087
+ }(Logger);
6052
6088
 
6053
6089
  /*
6054
6090
  * compute an Exponential Weighted moving average
@@ -6422,30 +6458,33 @@
6422
6458
  }, {});
6423
6459
  }
6424
6460
 
6425
- var AbrController = /*#__PURE__*/function () {
6461
+ var AbrController = /*#__PURE__*/function (_Logger) {
6462
+ _inheritsLoose(AbrController, _Logger);
6426
6463
  function AbrController(_hls) {
6427
- var _this = this;
6428
- this.hls = void 0;
6429
- this.lastLevelLoadSec = 0;
6430
- this.lastLoadedFragLevel = -1;
6431
- this.firstSelection = -1;
6432
- this._nextAutoLevel = -1;
6433
- this.nextAutoLevelKey = '';
6434
- this.audioTracksByGroup = null;
6435
- this.codecTiers = null;
6436
- this.timer = -1;
6437
- this.fragCurrent = null;
6438
- this.partCurrent = null;
6439
- this.bitrateTestDelay = 0;
6440
- this.bwEstimator = void 0;
6464
+ var _this;
6465
+ _this = _Logger.call(this, 'abr', _hls.logger) || this;
6466
+ _this.hls = void 0;
6467
+ _this.lastLevelLoadSec = 0;
6468
+ _this.lastLoadedFragLevel = -1;
6469
+ _this.firstSelection = -1;
6470
+ _this._nextAutoLevel = -1;
6471
+ _this.nextAutoLevelKey = '';
6472
+ _this.audioTracksByGroup = null;
6473
+ _this.codecTiers = null;
6474
+ _this.timer = -1;
6475
+ _this.fragCurrent = null;
6476
+ _this.partCurrent = null;
6477
+ _this.bitrateTestDelay = 0;
6478
+ _this.bwEstimator = void 0;
6441
6479
  /*
6442
6480
  This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
6443
6481
  quickly enough to prevent underbuffering
6444
6482
  */
6445
- this._abandonRulesCheck = function () {
6446
- var frag = _this.fragCurrent,
6447
- part = _this.partCurrent,
6448
- hls = _this.hls;
6483
+ _this._abandonRulesCheck = function () {
6484
+ var _assertThisInitialize = _assertThisInitialized(_this),
6485
+ frag = _assertThisInitialize.fragCurrent,
6486
+ part = _assertThisInitialize.partCurrent,
6487
+ hls = _assertThisInitialize.hls;
6449
6488
  var autoLevelEnabled = hls.autoLevelEnabled,
6450
6489
  media = hls.media;
6451
6490
  if (!frag || !media) {
@@ -6534,21 +6573,22 @@
6534
6573
  _this.resetEstimator(nextLoadLevelBitrate);
6535
6574
  }
6536
6575
  _this.clearTimer();
6537
- logger.warn("[abr] Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " is loading too slowly;\n Time to underbuffer: " + bufferStarvationDelay.toFixed(3) + " s\n Estimated load time for current fragment: " + fragLoadedDelay.toFixed(3) + " s\n Estimated load time for down switch fragment: " + fragLevelNextLoadedDelay.toFixed(3) + " s\n TTFB estimate: " + (ttfb | 0) + " ms\n Current BW estimate: " + (isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown') + " bps\n New BW estimate: " + (_this.getBwEstimate() | 0) + " bps\n Switching to level " + nextLoadLevel + " @ " + (nextLoadLevelBitrate | 0) + " bps");
6576
+ _this.warn("Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " is loading too slowly;\n Time to underbuffer: " + bufferStarvationDelay.toFixed(3) + " s\n Estimated load time for current fragment: " + fragLoadedDelay.toFixed(3) + " s\n Estimated load time for down switch fragment: " + fragLevelNextLoadedDelay.toFixed(3) + " s\n TTFB estimate: " + (ttfb | 0) + " ms\n Current BW estimate: " + (isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown') + " bps\n New BW estimate: " + (_this.getBwEstimate() | 0) + " bps\n Switching to level " + nextLoadLevel + " @ " + (nextLoadLevelBitrate | 0) + " bps");
6538
6577
  hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, {
6539
6578
  frag: frag,
6540
6579
  part: part,
6541
6580
  stats: stats
6542
6581
  });
6543
6582
  };
6544
- this.hls = _hls;
6545
- this.bwEstimator = this.initEstimator();
6546
- this.registerListeners();
6583
+ _this.hls = _hls;
6584
+ _this.bwEstimator = _this.initEstimator();
6585
+ _this.registerListeners();
6586
+ return _this;
6547
6587
  }
6548
6588
  var _proto = AbrController.prototype;
6549
6589
  _proto.resetEstimator = function resetEstimator(abrEwmaDefaultEstimate) {
6550
6590
  if (abrEwmaDefaultEstimate) {
6551
- logger.log("setting initial bwe to " + abrEwmaDefaultEstimate);
6591
+ this.log("setting initial bwe to " + abrEwmaDefaultEstimate);
6552
6592
  this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
6553
6593
  }
6554
6594
  this.firstSelection = -1;
@@ -6800,13 +6840,13 @@
6800
6840
  // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
6801
6841
  var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
6802
6842
  maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
6803
- logger.info("[abr] bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6843
+ this.info("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6804
6844
  // don't use conservative factor on bitrate test
6805
6845
  bwFactor = bwUpFactor = 1;
6806
6846
  }
6807
6847
  }
6808
6848
  var bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor);
6809
- logger.info("[abr] " + (bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6849
+ this.info((bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6810
6850
  if (bestLevel > -1) {
6811
6851
  return bestLevel;
6812
6852
  }
@@ -6862,7 +6902,7 @@
6862
6902
  currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
6863
6903
  currentFrameRate = minFramerate;
6864
6904
  currentBw = Math.max(currentBw, minBitrate);
6865
- logger.log("[abr] picked start tier " + JSON.stringify(startTier));
6905
+ this.log("picked start tier " + JSON.stringify(startTier));
6866
6906
  } else {
6867
6907
  currentCodecSet = level == null ? void 0 : level.codecSet;
6868
6908
  currentVideoRange = level == null ? void 0 : level.videoRange;
@@ -6915,9 +6955,9 @@
6915
6955
  var forcedAutoLevel = _this2.forcedAutoLevel;
6916
6956
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
6917
6957
  if (levelsSkipped.length) {
6918
- 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);
6958
+ _this2.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);
6919
6959
  }
6920
- 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);
6960
+ _this2.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);
6921
6961
  }
6922
6962
  if (firstSelection) {
6923
6963
  _this2.firstSelection = i;
@@ -6951,7 +6991,7 @@
6951
6991
  }
6952
6992
  var firstLevel = this.hls.firstLevel;
6953
6993
  var clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
6954
- logger.warn("[abr] Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
6994
+ this.warn("Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
6955
6995
  return clamped;
6956
6996
  }
6957
6997
  }, {
@@ -7004,7 +7044,7 @@
7004
7044
  }
7005
7045
  }]);
7006
7046
  return AbrController;
7007
- }();
7047
+ }(Logger);
7008
7048
 
7009
7049
  /**
7010
7050
  * Provides methods dealing with buffer length retrieval for example.
@@ -7225,57 +7265,57 @@
7225
7265
  }();
7226
7266
 
7227
7267
  var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
7228
- var BufferController = /*#__PURE__*/function () {
7268
+ var BufferController = /*#__PURE__*/function (_Logger) {
7269
+ _inheritsLoose(BufferController, _Logger);
7229
7270
  function BufferController(hls) {
7230
- var _this = this;
7271
+ var _this;
7272
+ _this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
7231
7273
  // The level details used to determine duration, target-duration and live
7232
- this.details = null;
7274
+ _this.details = null;
7233
7275
  // cache the self generated object url to detect hijack of video tag
7234
- this._objectUrl = null;
7276
+ _this._objectUrl = null;
7235
7277
  // A queue of buffer operations which require the SourceBuffer to not be updating upon execution
7236
- this.operationQueue = void 0;
7278
+ _this.operationQueue = void 0;
7237
7279
  // References to event listeners for each SourceBuffer, so that they can be referenced for event removal
7238
- this.listeners = void 0;
7239
- this.hls = void 0;
7280
+ _this.listeners = void 0;
7281
+ _this.hls = void 0;
7240
7282
  // The number of BUFFER_CODEC events received before any sourceBuffers are created
7241
- this.bufferCodecEventsExpected = 0;
7283
+ _this.bufferCodecEventsExpected = 0;
7242
7284
  // The total number of BUFFER_CODEC events received
7243
- this._bufferCodecEventsTotal = 0;
7285
+ _this._bufferCodecEventsTotal = 0;
7244
7286
  // A reference to the attached media element
7245
- this.media = null;
7287
+ _this.media = null;
7246
7288
  // A reference to the active media source
7247
- this.mediaSource = null;
7289
+ _this.mediaSource = null;
7248
7290
  // Last MP3 audio chunk appended
7249
- this.lastMpegAudioChunk = null;
7250
- this.appendSource = void 0;
7291
+ _this.lastMpegAudioChunk = null;
7292
+ _this.appendSource = void 0;
7251
7293
  // counters
7252
- this.appendErrors = {
7294
+ _this.appendErrors = {
7253
7295
  audio: 0,
7254
7296
  video: 0,
7255
7297
  audiovideo: 0
7256
7298
  };
7257
- this.tracks = {};
7258
- this.pendingTracks = {};
7259
- this.sourceBuffer = void 0;
7260
- this.log = void 0;
7261
- this.warn = void 0;
7262
- this.error = void 0;
7263
- this._onEndStreaming = function (event) {
7299
+ _this.tracks = {};
7300
+ _this.pendingTracks = {};
7301
+ _this.sourceBuffer = void 0;
7302
+ _this._onEndStreaming = function (event) {
7264
7303
  if (!_this.hls) {
7265
7304
  return;
7266
7305
  }
7267
7306
  _this.hls.pauseBuffering();
7268
7307
  };
7269
- this._onStartStreaming = function (event) {
7308
+ _this._onStartStreaming = function (event) {
7270
7309
  if (!_this.hls) {
7271
7310
  return;
7272
7311
  }
7273
7312
  _this.hls.resumeBuffering();
7274
7313
  };
7275
7314
  // Keep as arrow functions so that we can directly reference these functions directly as event listeners
7276
- this._onMediaSourceOpen = function () {
7277
- var media = _this.media,
7278
- mediaSource = _this.mediaSource;
7315
+ _this._onMediaSourceOpen = function () {
7316
+ var _assertThisInitialize = _assertThisInitialized(_this),
7317
+ media = _assertThisInitialize.media,
7318
+ mediaSource = _assertThisInitialize.mediaSource;
7279
7319
  _this.log('Media source opened');
7280
7320
  if (media) {
7281
7321
  media.removeEventListener('emptied', _this._onMediaEmptied);
@@ -7291,27 +7331,25 @@
7291
7331
  }
7292
7332
  _this.checkPendingTracks();
7293
7333
  };
7294
- this._onMediaSourceClose = function () {
7334
+ _this._onMediaSourceClose = function () {
7295
7335
  _this.log('Media source closed');
7296
7336
  };
7297
- this._onMediaSourceEnded = function () {
7337
+ _this._onMediaSourceEnded = function () {
7298
7338
  _this.log('Media source ended');
7299
7339
  };
7300
- this._onMediaEmptied = function () {
7301
- var mediaSrc = _this.mediaSrc,
7302
- _objectUrl = _this._objectUrl;
7340
+ _this._onMediaEmptied = function () {
7341
+ var _assertThisInitialize2 = _assertThisInitialized(_this),
7342
+ mediaSrc = _assertThisInitialize2.mediaSrc,
7343
+ _objectUrl = _assertThisInitialize2._objectUrl;
7303
7344
  if (mediaSrc !== _objectUrl) {
7304
- logger.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7345
+ _this.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7305
7346
  }
7306
7347
  };
7307
- this.hls = hls;
7308
- var logPrefix = '[buffer-controller]';
7309
- this.appendSource = hls.config.preferManagedMediaSource;
7310
- this.log = logger.log.bind(logger, logPrefix);
7311
- this.warn = logger.warn.bind(logger, logPrefix);
7312
- this.error = logger.error.bind(logger, logPrefix);
7313
- this._initSourceBuffer();
7314
- this.registerListeners();
7348
+ _this.hls = hls;
7349
+ _this.appendSource = hls.config.preferManagedMediaSource;
7350
+ _this._initSourceBuffer();
7351
+ _this.registerListeners();
7352
+ return _this;
7315
7353
  }
7316
7354
  var _proto = BufferController.prototype;
7317
7355
  _proto.hasSourceTypes = function hasSourceTypes() {
@@ -7323,6 +7361,12 @@
7323
7361
  this.lastMpegAudioChunk = null;
7324
7362
  // @ts-ignore
7325
7363
  this.hls = null;
7364
+ // @ts-ignore
7365
+ this._onMediaSourceOpen = this._onMediaSourceClose = null;
7366
+ // @ts-ignore
7367
+ this._onMediaSourceEnded = null;
7368
+ // @ts-ignore
7369
+ this._onStartStreaming = this._onEndStreaming = null;
7326
7370
  };
7327
7371
  _proto.registerListeners = function registerListeners() {
7328
7372
  var hls = this.hls;
@@ -7480,6 +7524,7 @@
7480
7524
  _this2.resetBuffer(type);
7481
7525
  });
7482
7526
  this._initSourceBuffer();
7527
+ this.hls.resumeBuffering();
7483
7528
  };
7484
7529
  _proto.resetBuffer = function resetBuffer(type) {
7485
7530
  var sb = this.sourceBuffer[type];
@@ -8183,7 +8228,7 @@
8183
8228
  }
8184
8229
  }]);
8185
8230
  return BufferController;
8186
- }();
8231
+ }(Logger);
8187
8232
  function removeSourceChildren(node) {
8188
8233
  var sourceChildren = node.querySelectorAll('source');
8189
8234
  [].slice.call(sourceChildren).forEach(function (source) {
@@ -8307,7 +8352,7 @@
8307
8352
  var hls = this.hls;
8308
8353
  var maxLevel = this.getMaxLevel(levels.length - 1);
8309
8354
  if (maxLevel !== this.autoLevelCapping) {
8310
- logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8355
+ hls.logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8311
8356
  }
8312
8357
  hls.autoLevelCapping = maxLevel;
8313
8358
  if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
@@ -8497,10 +8542,10 @@
8497
8542
  totalDroppedFrames: droppedFrames
8498
8543
  });
8499
8544
  if (droppedFPS > 0) {
8500
- // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8545
+ // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8501
8546
  if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
8502
8547
  var currentLevel = hls.currentLevel;
8503
- logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8548
+ hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8504
8549
  if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
8505
8550
  currentLevel = currentLevel - 1;
8506
8551
  hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
@@ -8534,26 +8579,28 @@
8534
8579
  }();
8535
8580
 
8536
8581
  var PATHWAY_PENALTY_DURATION_MS = 300000;
8537
- var ContentSteeringController = /*#__PURE__*/function () {
8582
+ var ContentSteeringController = /*#__PURE__*/function (_Logger) {
8583
+ _inheritsLoose(ContentSteeringController, _Logger);
8538
8584
  function ContentSteeringController(hls) {
8539
- this.hls = void 0;
8540
- this.log = void 0;
8541
- this.loader = null;
8542
- this.uri = null;
8543
- this.pathwayId = '.';
8544
- this.pathwayPriority = null;
8545
- this.timeToLoad = 300;
8546
- this.reloadTimer = -1;
8547
- this.updated = 0;
8548
- this.started = false;
8549
- this.enabled = true;
8550
- this.levels = null;
8551
- this.audioTracks = null;
8552
- this.subtitleTracks = null;
8553
- this.penalizedPathways = {};
8554
- this.hls = hls;
8555
- this.log = logger.log.bind(logger, "[content-steering]:");
8556
- this.registerListeners();
8585
+ var _this;
8586
+ _this = _Logger.call(this, 'content-steering', hls.logger) || this;
8587
+ _this.hls = void 0;
8588
+ _this.loader = null;
8589
+ _this.uri = null;
8590
+ _this.pathwayId = '.';
8591
+ _this.pathwayPriority = null;
8592
+ _this.timeToLoad = 300;
8593
+ _this.reloadTimer = -1;
8594
+ _this.updated = 0;
8595
+ _this.started = false;
8596
+ _this.enabled = true;
8597
+ _this.levels = null;
8598
+ _this.audioTracks = null;
8599
+ _this.subtitleTracks = null;
8600
+ _this.penalizedPathways = {};
8601
+ _this.hls = hls;
8602
+ _this.registerListeners();
8603
+ return _this;
8557
8604
  }
8558
8605
  var _proto = ContentSteeringController.prototype;
8559
8606
  _proto.registerListeners = function registerListeners() {
@@ -8674,7 +8721,7 @@
8674
8721
  errorAction.resolved = this.pathwayId !== errorPathway;
8675
8722
  }
8676
8723
  if (!errorAction.resolved) {
8677
- 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));
8724
+ 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));
8678
8725
  }
8679
8726
  }
8680
8727
  };
@@ -8754,7 +8801,7 @@
8754
8801
  return defaultPathway;
8755
8802
  };
8756
8803
  _proto.clonePathways = function clonePathways(pathwayClones) {
8757
- var _this = this;
8804
+ var _this2 = this;
8758
8805
  var levels = this.levels;
8759
8806
  if (!levels) {
8760
8807
  return;
@@ -8770,7 +8817,7 @@
8770
8817
  })) {
8771
8818
  return;
8772
8819
  }
8773
- var clonedVariants = _this.getLevelsForPathway(baseId).map(function (baseLevel) {
8820
+ var clonedVariants = _this2.getLevelsForPathway(baseId).map(function (baseLevel) {
8774
8821
  var attributes = new AttrList(baseLevel.attrs);
8775
8822
  attributes['PATHWAY-ID'] = cloneId;
8776
8823
  var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId;
@@ -8807,12 +8854,12 @@
8807
8854
  return clonedLevel;
8808
8855
  });
8809
8856
  levels.push.apply(levels, clonedVariants);
8810
- cloneRenditionGroups(_this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8811
- cloneRenditionGroups(_this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8857
+ cloneRenditionGroups(_this2.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8858
+ cloneRenditionGroups(_this2.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8812
8859
  });
8813
8860
  };
8814
8861
  _proto.loadSteeringManifest = function loadSteeringManifest(uri) {
8815
- var _this2 = this;
8862
+ var _this3 = this;
8816
8863
  var config = this.hls.config;
8817
8864
  var Loader = config.loader;
8818
8865
  if (this.loader) {
@@ -8847,87 +8894,87 @@
8847
8894
  };
8848
8895
  var callbacks = {
8849
8896
  onSuccess: function onSuccess(response, stats, context, networkDetails) {
8850
- _this2.log("Loaded steering manifest: \"" + url + "\"");
8897
+ _this3.log("Loaded steering manifest: \"" + url + "\"");
8851
8898
  var steeringData = response.data;
8852
- if (steeringData.VERSION !== 1) {
8853
- _this2.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8899
+ if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) {
8900
+ _this3.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8854
8901
  return;
8855
8902
  }
8856
- _this2.updated = performance.now();
8857
- _this2.timeToLoad = steeringData.TTL;
8903
+ _this3.updated = performance.now();
8904
+ _this3.timeToLoad = steeringData.TTL;
8858
8905
  var reloadUri = steeringData['RELOAD-URI'],
8859
8906
  pathwayClones = steeringData['PATHWAY-CLONES'],
8860
8907
  pathwayPriority = steeringData['PATHWAY-PRIORITY'];
8861
8908
  if (reloadUri) {
8862
8909
  try {
8863
- _this2.uri = new self.URL(reloadUri, url).href;
8910
+ _this3.uri = new self.URL(reloadUri, url).href;
8864
8911
  } catch (error) {
8865
- _this2.enabled = false;
8866
- _this2.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8912
+ _this3.enabled = false;
8913
+ _this3.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8867
8914
  return;
8868
8915
  }
8869
8916
  }
8870
- _this2.scheduleRefresh(_this2.uri || context.url);
8917
+ _this3.scheduleRefresh(_this3.uri || context.url);
8871
8918
  if (pathwayClones) {
8872
- _this2.clonePathways(pathwayClones);
8919
+ _this3.clonePathways(pathwayClones);
8873
8920
  }
8874
8921
  var loadedSteeringData = {
8875
8922
  steeringManifest: steeringData,
8876
8923
  url: url.toString()
8877
8924
  };
8878
- _this2.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8925
+ _this3.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8879
8926
  if (pathwayPriority) {
8880
- _this2.updatePathwayPriority(pathwayPriority);
8927
+ _this3.updatePathwayPriority(pathwayPriority);
8881
8928
  }
8882
8929
  },
8883
8930
  onError: function onError(error, context, networkDetails, stats) {
8884
- _this2.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8885
- _this2.stopLoad();
8931
+ _this3.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8932
+ _this3.stopLoad();
8886
8933
  if (error.code === 410) {
8887
- _this2.enabled = false;
8888
- _this2.log("Steering manifest " + context.url + " no longer available");
8934
+ _this3.enabled = false;
8935
+ _this3.log("Steering manifest " + context.url + " no longer available");
8889
8936
  return;
8890
8937
  }
8891
- var ttl = _this2.timeToLoad * 1000;
8938
+ var ttl = _this3.timeToLoad * 1000;
8892
8939
  if (error.code === 429) {
8893
- var loader = _this2.loader;
8940
+ var loader = _this3.loader;
8894
8941
  if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {
8895
8942
  var retryAfter = loader.getResponseHeader('Retry-After');
8896
8943
  if (retryAfter) {
8897
8944
  ttl = parseFloat(retryAfter) * 1000;
8898
8945
  }
8899
8946
  }
8900
- _this2.log("Steering manifest " + context.url + " rate limited");
8947
+ _this3.log("Steering manifest " + context.url + " rate limited");
8901
8948
  return;
8902
8949
  }
8903
- _this2.scheduleRefresh(_this2.uri || context.url, ttl);
8950
+ _this3.scheduleRefresh(_this3.uri || context.url, ttl);
8904
8951
  },
8905
8952
  onTimeout: function onTimeout(stats, context, networkDetails) {
8906
- _this2.log("Timeout loading steering manifest (" + context.url + ")");
8907
- _this2.scheduleRefresh(_this2.uri || context.url);
8953
+ _this3.log("Timeout loading steering manifest (" + context.url + ")");
8954
+ _this3.scheduleRefresh(_this3.uri || context.url);
8908
8955
  }
8909
8956
  };
8910
8957
  this.log("Requesting steering manifest: " + url);
8911
8958
  this.loader.load(context, loaderConfig, callbacks);
8912
8959
  };
8913
8960
  _proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) {
8914
- var _this3 = this;
8961
+ var _this4 = this;
8915
8962
  if (ttlMs === void 0) {
8916
8963
  ttlMs = this.timeToLoad * 1000;
8917
8964
  }
8918
8965
  this.clearTimeout();
8919
8966
  this.reloadTimer = self.setTimeout(function () {
8920
- var _this3$hls;
8921
- var media = (_this3$hls = _this3.hls) == null ? void 0 : _this3$hls.media;
8967
+ var _this4$hls;
8968
+ var media = (_this4$hls = _this4.hls) == null ? void 0 : _this4$hls.media;
8922
8969
  if (media && !media.ended) {
8923
- _this3.loadSteeringManifest(uri);
8970
+ _this4.loadSteeringManifest(uri);
8924
8971
  return;
8925
8972
  }
8926
- _this3.scheduleRefresh(uri, _this3.timeToLoad * 1000);
8973
+ _this4.scheduleRefresh(uri, _this4.timeToLoad * 1000);
8927
8974
  }, ttlMs);
8928
8975
  };
8929
8976
  return ContentSteeringController;
8930
- }();
8977
+ }(Logger);
8931
8978
  function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {
8932
8979
  if (!tracks) {
8933
8980
  return;
@@ -9763,7 +9810,7 @@
9763
9810
  });
9764
9811
  function timelineConfig() {
9765
9812
  return {
9766
- cueHandler: Cues,
9813
+ cueHandler: HevcVideoParser,
9767
9814
  // used by timeline-controller
9768
9815
  enableWebVTT: false,
9769
9816
  // used by timeline-controller
@@ -9794,7 +9841,7 @@
9794
9841
  /**
9795
9842
  * @ignore
9796
9843
  */
9797
- function mergeConfig(defaultConfig, userConfig) {
9844
+ function mergeConfig(defaultConfig, userConfig, logger) {
9798
9845
  if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
9799
9846
  throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
9800
9847
  }
@@ -9864,7 +9911,7 @@
9864
9911
  /**
9865
9912
  * @ignore
9866
9913
  */
9867
- function enableStreamingMode(config) {
9914
+ function enableStreamingMode(config, logger) {
9868
9915
  var currentLoader = config.loader;
9869
9916
  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
9870
9917
  // If a developer has configured their own loader, respect that choice
@@ -9881,12 +9928,11 @@
9881
9928
  }
9882
9929
  }
9883
9930
 
9884
- var chromeOrFirefox;
9885
9931
  var LevelController = /*#__PURE__*/function (_BasePlaylistControll) {
9886
9932
  _inheritsLoose(LevelController, _BasePlaylistControll);
9887
9933
  function LevelController(hls, contentSteeringController) {
9888
9934
  var _this;
9889
- _this = _BasePlaylistControll.call(this, hls, '[level-controller]') || this;
9935
+ _this = _BasePlaylistControll.call(this, hls, 'level-controller') || this;
9890
9936
  _this._levels = [];
9891
9937
  _this._firstLevel = -1;
9892
9938
  _this._maxAutoLevel = -1;
@@ -9955,21 +10001,13 @@
9955
10001
  var videoCodecFound = false;
9956
10002
  var audioCodecFound = false;
9957
10003
  data.levels.forEach(function (levelParsed) {
9958
- var _audioCodec, _videoCodec;
10004
+ var _videoCodec;
9959
10005
  var attributes = levelParsed.attrs;
9960
-
9961
- // erase audio codec info if browser does not support mp4a.40.34.
9962
- // demuxer will autodetect codec and fallback to mpeg/audio
9963
10006
  var audioCodec = levelParsed.audioCodec,
9964
10007
  videoCodec = levelParsed.videoCodec;
9965
- if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) {
9966
- chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));
9967
- if (chromeOrFirefox) {
9968
- levelParsed.audioCodec = audioCodec = undefined;
9969
- }
9970
- }
9971
10008
  if (audioCodec) {
9972
- levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource);
10009
+ // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
10010
+ levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
9973
10011
  }
9974
10012
  if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
9975
10013
  videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
@@ -11116,8 +11154,8 @@
11116
11154
  var _frag$decryptdata;
11117
11155
  var byteRangeStart = start;
11118
11156
  var byteRangeEnd = end;
11119
- if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {
11120
- // MAP segment encrypted with method 'AES-128', when served with HTTP Range,
11157
+ if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) {
11158
+ // MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
11121
11159
  // has the unencrypted size specified in the range.
11122
11160
  // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
11123
11161
  var fragmentLen = end - start;
@@ -11150,6 +11188,9 @@
11150
11188
  (part ? part : frag).stats.aborted = true;
11151
11189
  return new LoadError(errorData);
11152
11190
  }
11191
+ function isMethodFullSegmentAesCbc(method) {
11192
+ return method === 'AES-128' || method === 'AES-256';
11193
+ }
11153
11194
  var LoadError = /*#__PURE__*/function (_Error) {
11154
11195
  _inheritsLoose(LoadError, _Error);
11155
11196
  function LoadError(data) {
@@ -11306,6 +11347,8 @@
11306
11347
  }
11307
11348
  return this.loadKeyEME(keyInfo, frag);
11308
11349
  case 'AES-128':
11350
+ case 'AES-256':
11351
+ case 'AES-256-CTR':
11309
11352
  return this.loadKeyHTTP(keyInfo, frag);
11310
11353
  default:
11311
11354
  return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\"")));
@@ -11439,13 +11482,17 @@
11439
11482
  * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
11440
11483
  * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
11441
11484
  */
11442
- var TaskLoop = /*#__PURE__*/function () {
11443
- function TaskLoop() {
11444
- this._boundTick = void 0;
11445
- this._tickTimer = null;
11446
- this._tickInterval = null;
11447
- this._tickCallCount = 0;
11448
- this._boundTick = this.tick.bind(this);
11485
+ var TaskLoop = /*#__PURE__*/function (_Logger) {
11486
+ _inheritsLoose(TaskLoop, _Logger);
11487
+ function TaskLoop(label, logger) {
11488
+ var _this;
11489
+ _this = _Logger.call(this, label, logger) || this;
11490
+ _this._boundTick = void 0;
11491
+ _this._tickTimer = null;
11492
+ _this._tickInterval = null;
11493
+ _this._tickCallCount = 0;
11494
+ _this._boundTick = _this.tick.bind(_assertThisInitialized(_this));
11495
+ return _this;
11449
11496
  }
11450
11497
  var _proto = TaskLoop.prototype;
11451
11498
  _proto.destroy = function destroy() {
@@ -11531,7 +11578,7 @@
11531
11578
  */;
11532
11579
  _proto.doTick = function doTick() {};
11533
11580
  return TaskLoop;
11534
- }();
11581
+ }(Logger);
11535
11582
 
11536
11583
  var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) {
11537
11584
  if (size === void 0) {
@@ -11717,37 +11764,65 @@
11717
11764
  }
11718
11765
 
11719
11766
  var AESCrypto = /*#__PURE__*/function () {
11720
- function AESCrypto(subtle, iv) {
11767
+ function AESCrypto(subtle, iv, aesMode) {
11721
11768
  this.subtle = void 0;
11722
11769
  this.aesIV = void 0;
11770
+ this.aesMode = void 0;
11723
11771
  this.subtle = subtle;
11724
11772
  this.aesIV = iv;
11773
+ this.aesMode = aesMode;
11725
11774
  }
11726
11775
  var _proto = AESCrypto.prototype;
11727
11776
  _proto.decrypt = function decrypt(data, key) {
11728
- return this.subtle.decrypt({
11729
- name: 'AES-CBC',
11730
- iv: this.aesIV
11731
- }, key, data);
11777
+ switch (this.aesMode) {
11778
+ case DecrypterAesMode.cbc:
11779
+ return this.subtle.decrypt({
11780
+ name: 'AES-CBC',
11781
+ iv: this.aesIV
11782
+ }, key, data);
11783
+ case DecrypterAesMode.ctr:
11784
+ return this.subtle.decrypt({
11785
+ name: 'AES-CTR',
11786
+ counter: this.aesIV,
11787
+ length: 64
11788
+ },
11789
+ //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
11790
+ key, data);
11791
+ default:
11792
+ throw new Error("[AESCrypto] invalid aes mode " + this.aesMode);
11793
+ }
11732
11794
  };
11733
11795
  return AESCrypto;
11734
11796
  }();
11735
11797
 
11736
11798
  var FastAESKey = /*#__PURE__*/function () {
11737
- function FastAESKey(subtle, key) {
11799
+ function FastAESKey(subtle, key, aesMode) {
11738
11800
  this.subtle = void 0;
11739
11801
  this.key = void 0;
11802
+ this.aesMode = void 0;
11740
11803
  this.subtle = subtle;
11741
11804
  this.key = key;
11805
+ this.aesMode = aesMode;
11742
11806
  }
11743
11807
  var _proto = FastAESKey.prototype;
11744
11808
  _proto.expandKey = function expandKey() {
11809
+ var subtleAlgoName = getSubtleAlgoName(this.aesMode);
11745
11810
  return this.subtle.importKey('raw', this.key, {
11746
- name: 'AES-CBC'
11811
+ name: subtleAlgoName
11747
11812
  }, false, ['encrypt', 'decrypt']);
11748
11813
  };
11749
11814
  return FastAESKey;
11750
11815
  }();
11816
+ function getSubtleAlgoName(aesMode) {
11817
+ switch (aesMode) {
11818
+ case DecrypterAesMode.cbc:
11819
+ return 'AES-CBC';
11820
+ case DecrypterAesMode.ctr:
11821
+ return 'AES-CTR';
11822
+ default:
11823
+ throw new Error("[FastAESKey] invalid aes mode " + aesMode);
11824
+ }
11825
+ }
11751
11826
 
11752
11827
  // PKCS7
11753
11828
  function removePadding(array) {
@@ -12000,7 +12075,8 @@
12000
12075
  this.currentIV = null;
12001
12076
  this.currentResult = null;
12002
12077
  this.useSoftware = void 0;
12003
- this.useSoftware = config.enableSoftwareAES;
12078
+ this.enableSoftwareAES = void 0;
12079
+ this.enableSoftwareAES = config.enableSoftwareAES;
12004
12080
  this.removePKCS7Padding = removePKCS7Padding;
12005
12081
  // built in decryptor expects PKCS7 padding
12006
12082
  if (removePKCS7Padding) {
@@ -12013,9 +12089,7 @@
12013
12089
  /* no-op */
12014
12090
  }
12015
12091
  }
12016
- if (this.subtle === null) {
12017
- this.useSoftware = true;
12018
- }
12092
+ this.useSoftware = this.subtle === null;
12019
12093
  }
12020
12094
  var _proto = Decrypter.prototype;
12021
12095
  _proto.destroy = function destroy() {
@@ -12052,11 +12126,11 @@
12052
12126
  this.softwareDecrypter = null;
12053
12127
  }
12054
12128
  };
12055
- _proto.decrypt = function decrypt(data, key, iv) {
12129
+ _proto.decrypt = function decrypt(data, key, iv, aesMode) {
12056
12130
  var _this = this;
12057
12131
  if (this.useSoftware) {
12058
12132
  return new Promise(function (resolve, reject) {
12059
- _this.softwareDecrypt(new Uint8Array(data), key, iv);
12133
+ _this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
12060
12134
  var decryptResult = _this.flush();
12061
12135
  if (decryptResult) {
12062
12136
  resolve(decryptResult.buffer);
@@ -12065,16 +12139,20 @@
12065
12139
  }
12066
12140
  });
12067
12141
  }
12068
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
12142
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
12069
12143
  }
12070
12144
 
12071
12145
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
12072
12146
  // data is handled in the flush() call
12073
12147
  ;
12074
- _proto.softwareDecrypt = function softwareDecrypt(data, key, iv) {
12148
+ _proto.softwareDecrypt = function softwareDecrypt(data, key, iv, aesMode) {
12075
12149
  var currentIV = this.currentIV,
12076
12150
  currentResult = this.currentResult,
12077
12151
  remainderData = this.remainderData;
12152
+ if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
12153
+ logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
12154
+ return null;
12155
+ }
12078
12156
  this.logOnce('JS AES decrypt');
12079
12157
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
12080
12158
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -12107,12 +12185,12 @@
12107
12185
  }
12108
12186
  return result;
12109
12187
  };
12110
- _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv) {
12188
+ _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv, aesMode) {
12111
12189
  var _this2 = this;
12112
12190
  var subtle = this.subtle;
12113
12191
  if (this.key !== key || !this.fastAesKey) {
12114
12192
  this.key = key;
12115
- this.fastAesKey = new FastAESKey(subtle, key);
12193
+ this.fastAesKey = new FastAESKey(subtle, key, aesMode);
12116
12194
  }
12117
12195
  return this.fastAesKey.expandKey().then(function (aesKey) {
12118
12196
  // decrypt using web crypto
@@ -12120,22 +12198,25 @@
12120
12198
  return Promise.reject(new Error('web crypto not initialized'));
12121
12199
  }
12122
12200
  _this2.logOnce('WebCrypto AES decrypt');
12123
- var crypto = new AESCrypto(subtle, new Uint8Array(iv));
12201
+ var crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
12124
12202
  return crypto.decrypt(data.buffer, aesKey);
12125
12203
  }).catch(function (err) {
12126
12204
  logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message);
12127
- return _this2.onWebCryptoError(data, key, iv);
12205
+ return _this2.onWebCryptoError(data, key, iv, aesMode);
12128
12206
  });
12129
12207
  };
12130
- _proto.onWebCryptoError = function onWebCryptoError(data, key, iv) {
12131
- this.useSoftware = true;
12132
- this.logEnabled = true;
12133
- this.softwareDecrypt(data, key, iv);
12134
- var decryptResult = this.flush();
12135
- if (decryptResult) {
12136
- return decryptResult.buffer;
12208
+ _proto.onWebCryptoError = function onWebCryptoError(data, key, iv, aesMode) {
12209
+ var enableSoftwareAES = this.enableSoftwareAES;
12210
+ if (enableSoftwareAES) {
12211
+ this.useSoftware = true;
12212
+ this.logEnabled = true;
12213
+ this.softwareDecrypt(data, key, iv, aesMode);
12214
+ var decryptResult = this.flush();
12215
+ if (decryptResult) {
12216
+ return decryptResult.buffer;
12217
+ }
12137
12218
  }
12138
- throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
12219
+ throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data');
12139
12220
  };
12140
12221
  _proto.getValidChunk = function getValidChunk(data) {
12141
12222
  var currentChunk = data;
@@ -12189,7 +12270,7 @@
12189
12270
  _inheritsLoose(BaseStreamController, _TaskLoop);
12190
12271
  function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
12191
12272
  var _this;
12192
- _this = _TaskLoop.call(this) || this;
12273
+ _this = _TaskLoop.call(this, logPrefix, hls.logger) || this;
12193
12274
  _this.hls = void 0;
12194
12275
  _this.fragPrevious = null;
12195
12276
  _this.fragCurrent = null;
@@ -12214,25 +12295,87 @@
12214
12295
  _this.startFragRequested = false;
12215
12296
  _this.decrypter = void 0;
12216
12297
  _this.initPTS = [];
12217
- _this.onvseeking = null;
12218
- _this.onvended = null;
12219
- _this.logPrefix = '';
12220
- _this.log = void 0;
12221
- _this.warn = void 0;
12298
+ _this.buffering = true;
12299
+ _this.onMediaSeeking = function () {
12300
+ var _assertThisInitialize = _assertThisInitialized(_this),
12301
+ config = _assertThisInitialize.config,
12302
+ fragCurrent = _assertThisInitialize.fragCurrent,
12303
+ media = _assertThisInitialize.media,
12304
+ mediaBuffer = _assertThisInitialize.mediaBuffer,
12305
+ state = _assertThisInitialize.state;
12306
+ var currentTime = media ? media.currentTime : 0;
12307
+ var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12308
+ _this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12309
+ if (_this.state === State.ENDED) {
12310
+ _this.resetLoadingState();
12311
+ } else if (fragCurrent) {
12312
+ // Seeking while frag load is in progress
12313
+ var tolerance = config.maxFragLookUpTolerance;
12314
+ var fragStartOffset = fragCurrent.start - tolerance;
12315
+ var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12316
+ // if seeking out of buffered range or into new one
12317
+ if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12318
+ var pastFragment = currentTime > fragEndOffset;
12319
+ // if the seek position is outside the current fragment range
12320
+ if (currentTime < fragStartOffset || pastFragment) {
12321
+ if (pastFragment && fragCurrent.loader) {
12322
+ _this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12323
+ fragCurrent.abortRequests();
12324
+ _this.resetLoadingState();
12325
+ }
12326
+ _this.fragPrevious = null;
12327
+ }
12328
+ }
12329
+ }
12330
+ if (media) {
12331
+ // Remove gap fragments
12332
+ _this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, _this.playlistType, true);
12333
+ _this.lastCurrentTime = currentTime;
12334
+ }
12335
+
12336
+ // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12337
+ if (!_this.loadedmetadata && !bufferInfo.len) {
12338
+ _this.nextLoadPosition = _this.startPosition = currentTime;
12339
+ }
12340
+
12341
+ // Async tick to speed up processing
12342
+ _this.tickImmediate();
12343
+ };
12344
+ _this.onMediaEnded = function () {
12345
+ // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12346
+ _this.startPosition = _this.lastCurrentTime = 0;
12347
+ if (_this.playlistType === PlaylistLevelType.MAIN) {
12348
+ _this.hls.trigger(Events.MEDIA_ENDED, {
12349
+ stalled: false
12350
+ });
12351
+ }
12352
+ };
12222
12353
  _this.playlistType = playlistType;
12223
- _this.logPrefix = logPrefix;
12224
- _this.log = logger.log.bind(logger, logPrefix + ":");
12225
- _this.warn = logger.warn.bind(logger, logPrefix + ":");
12226
12354
  _this.hls = hls;
12227
12355
  _this.fragmentLoader = new FragmentLoader(hls.config);
12228
12356
  _this.keyLoader = keyLoader;
12229
12357
  _this.fragmentTracker = fragmentTracker;
12230
12358
  _this.config = hls.config;
12231
12359
  _this.decrypter = new Decrypter(hls.config);
12232
- hls.on(Events.MANIFEST_LOADED, _this.onManifestLoaded, _assertThisInitialized(_this));
12233
12360
  return _this;
12234
12361
  }
12235
12362
  var _proto = BaseStreamController.prototype;
12363
+ _proto.registerListeners = function registerListeners() {
12364
+ var hls = this.hls;
12365
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12366
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12367
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12368
+ hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12369
+ hls.on(Events.ERROR, this.onError, this);
12370
+ };
12371
+ _proto.unregisterListeners = function unregisterListeners() {
12372
+ var hls = this.hls;
12373
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12374
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12375
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12376
+ hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12377
+ hls.off(Events.ERROR, this.onError, this);
12378
+ };
12236
12379
  _proto.doTick = function doTick() {
12237
12380
  this.onTickEnd();
12238
12381
  };
@@ -12256,6 +12399,12 @@
12256
12399
  this.clearNextTick();
12257
12400
  this.state = State.STOPPED;
12258
12401
  };
12402
+ _proto.pauseBuffering = function pauseBuffering() {
12403
+ this.buffering = false;
12404
+ };
12405
+ _proto.resumeBuffering = function resumeBuffering() {
12406
+ this.buffering = true;
12407
+ };
12259
12408
  _proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) {
12260
12409
  // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
12261
12410
  // of nothing loading/loaded return false
@@ -12286,10 +12435,8 @@
12286
12435
  };
12287
12436
  _proto.onMediaAttached = function onMediaAttached(event, data) {
12288
12437
  var media = this.media = this.mediaBuffer = data.media;
12289
- this.onvseeking = this.onMediaSeeking.bind(this);
12290
- this.onvended = this.onMediaEnded.bind(this);
12291
- media.addEventListener('seeking', this.onvseeking);
12292
- media.addEventListener('ended', this.onvended);
12438
+ media.addEventListener('seeking', this.onMediaSeeking);
12439
+ media.addEventListener('ended', this.onMediaEnded);
12293
12440
  var config = this.config;
12294
12441
  if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
12295
12442
  this.startLoad(config.startPosition);
@@ -12303,10 +12450,9 @@
12303
12450
  }
12304
12451
 
12305
12452
  // remove video listeners
12306
- if (media && this.onvseeking && this.onvended) {
12307
- media.removeEventListener('seeking', this.onvseeking);
12308
- media.removeEventListener('ended', this.onvended);
12309
- this.onvseeking = this.onvended = null;
12453
+ if (media) {
12454
+ media.removeEventListener('seeking', this.onMediaSeeking);
12455
+ media.removeEventListener('ended', this.onMediaEnded);
12310
12456
  }
12311
12457
  if (this.keyLoader) {
12312
12458
  this.keyLoader.detach();
@@ -12316,54 +12462,8 @@
12316
12462
  this.fragmentTracker.removeAllFragments();
12317
12463
  this.stopLoad();
12318
12464
  };
12319
- _proto.onMediaSeeking = function onMediaSeeking() {
12320
- var config = this.config,
12321
- fragCurrent = this.fragCurrent,
12322
- media = this.media,
12323
- mediaBuffer = this.mediaBuffer,
12324
- state = this.state;
12325
- var currentTime = media ? media.currentTime : 0;
12326
- var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12327
- this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12328
- if (this.state === State.ENDED) {
12329
- this.resetLoadingState();
12330
- } else if (fragCurrent) {
12331
- // Seeking while frag load is in progress
12332
- var tolerance = config.maxFragLookUpTolerance;
12333
- var fragStartOffset = fragCurrent.start - tolerance;
12334
- var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12335
- // if seeking out of buffered range or into new one
12336
- if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12337
- var pastFragment = currentTime > fragEndOffset;
12338
- // if the seek position is outside the current fragment range
12339
- if (currentTime < fragStartOffset || pastFragment) {
12340
- if (pastFragment && fragCurrent.loader) {
12341
- this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12342
- fragCurrent.abortRequests();
12343
- this.resetLoadingState();
12344
- }
12345
- this.fragPrevious = null;
12346
- }
12347
- }
12348
- }
12349
- if (media) {
12350
- // Remove gap fragments
12351
- this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
12352
- this.lastCurrentTime = currentTime;
12353
- }
12354
-
12355
- // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12356
- if (!this.loadedmetadata && !bufferInfo.len) {
12357
- this.nextLoadPosition = this.startPosition = currentTime;
12358
- }
12359
-
12360
- // Async tick to speed up processing
12361
- this.tickImmediate();
12362
- };
12363
- _proto.onMediaEnded = function onMediaEnded() {
12364
- // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12365
- this.startPosition = this.lastCurrentTime = 0;
12366
- };
12465
+ _proto.onManifestLoading = function onManifestLoading() {};
12466
+ _proto.onError = function onError(event, data) {};
12367
12467
  _proto.onManifestLoaded = function onManifestLoaded(event, data) {
12368
12468
  this.startTimeOffset = data.startTimeOffset;
12369
12469
  this.initPTS = [];
@@ -12373,7 +12473,7 @@
12373
12473
  this.stopLoad();
12374
12474
  _TaskLoop.prototype.onHandlerDestroying.call(this);
12375
12475
  // @ts-ignore
12376
- this.hls = null;
12476
+ this.hls = this.onMediaSeeking = this.onMediaEnded = null;
12377
12477
  };
12378
12478
  _proto.onHandlerDestroyed = function onHandlerDestroyed() {
12379
12479
  this.state = State.STOPPED;
@@ -12503,10 +12603,10 @@
12503
12603
  var decryptData = frag.decryptdata;
12504
12604
 
12505
12605
  // check to see if the payload needs to be decrypted
12506
- if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
12606
+ if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
12507
12607
  var startTime = self.performance.now();
12508
12608
  // decrypt init segment data
12509
- return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(function (err) {
12609
+ return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(function (err) {
12510
12610
  hls.trigger(Events.ERROR, {
12511
12611
  type: ErrorTypes.MEDIA_ERROR,
12512
12612
  details: ErrorDetails.FRAG_DECRYPT_ERROR,
@@ -12619,7 +12719,7 @@
12619
12719
  }
12620
12720
  var keyLoadingPromise = null;
12621
12721
  if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
12622
- this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + " " + frag.level);
12722
+ this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + " " + frag.level);
12623
12723
  this.state = State.KEY_LOADING;
12624
12724
  this.fragCurrent = frag;
12625
12725
  keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) {
@@ -12650,7 +12750,7 @@
12650
12750
  var partIndex = this.getNextPart(partList, frag, targetBufferTime);
12651
12751
  if (partIndex > -1) {
12652
12752
  var part = partList[partIndex];
12653
- 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)));
12753
+ 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)));
12654
12754
  this.nextLoadPosition = part.start + part.duration;
12655
12755
  this.state = State.FRAG_LOADING;
12656
12756
  var _result;
@@ -12683,7 +12783,7 @@
12683
12783
  }
12684
12784
  }
12685
12785
  }
12686
- 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)));
12786
+ 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)));
12687
12787
  // Don't update nextLoadPosition for fragments which are not buffered
12688
12788
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
12689
12789
  this.nextLoadPosition = frag.start + frag.duration;
@@ -13244,7 +13344,7 @@
13244
13344
  errorAction.resolved = true;
13245
13345
  }
13246
13346
  } else {
13247
- logger.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13347
+ this.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13248
13348
  return;
13249
13349
  }
13250
13350
  } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
@@ -13634,6 +13734,7 @@
13634
13734
  */
13635
13735
  function getAudioConfig(observer, data, offset, audioCodec) {
13636
13736
  var adtsObjectType;
13737
+ var originalAdtsObjectType;
13637
13738
  var adtsExtensionSamplingIndex;
13638
13739
  var adtsChannelConfig;
13639
13740
  var config;
@@ -13641,7 +13742,7 @@
13641
13742
  var manifestCodec = audioCodec;
13642
13743
  var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
13643
13744
  // byte 2
13644
- adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13745
+ adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13645
13746
  var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
13646
13747
  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
13647
13748
  var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex);
@@ -13658,8 +13759,8 @@
13658
13759
  // byte 3
13659
13760
  adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
13660
13761
  logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex);
13661
- // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
13662
- if (/firefox/i.test(userAgent)) {
13762
+ // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
13763
+ if (/firefox|palemoon/i.test(userAgent)) {
13663
13764
  if (adtsSamplingIndex >= 6) {
13664
13765
  adtsObjectType = 5;
13665
13766
  config = new Array(4);
@@ -13753,6 +13854,7 @@
13753
13854
  samplerate: adtsSamplingRates[adtsSamplingIndex],
13754
13855
  channelCount: adtsChannelConfig,
13755
13856
  codec: 'mp4a.40.' + adtsObjectType,
13857
+ parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
13756
13858
  manifestCodec: manifestCodec
13757
13859
  };
13758
13860
  }
@@ -13807,7 +13909,8 @@
13807
13909
  track.channelCount = config.channelCount;
13808
13910
  track.codec = config.codec;
13809
13911
  track.manifestCodec = config.manifestCodec;
13810
- logger.log("parsed codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13912
+ track.parsedCodec = config.parsedCodec;
13913
+ logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13811
13914
  }
13812
13915
  }
13813
13916
  function getFrameDuration(samplerate) {
@@ -14287,36 +14390,140 @@
14287
14390
  logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
14288
14391
  }
14289
14392
  };
14290
- return BaseVideoParser;
14291
- }();
14292
-
14293
- /**
14294
- * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
14295
- */
14296
-
14297
- var ExpGolomb = /*#__PURE__*/function () {
14298
- function ExpGolomb(data) {
14299
- this.data = void 0;
14300
- this.bytesAvailable = void 0;
14301
- this.word = void 0;
14302
- this.bitsAvailable = void 0;
14303
- this.data = data;
14304
- // the number of bytes left to examine in this.data
14305
- this.bytesAvailable = data.byteLength;
14306
- // the current word being examined
14307
- this.word = 0; // :uint
14308
- // the number of bits left to examine in the current word
14309
- this.bitsAvailable = 0; // :uint
14310
- }
14393
+ _proto.parseNALu = function parseNALu(track, array) {
14394
+ var len = array.byteLength;
14395
+ var state = track.naluState || 0;
14396
+ var lastState = state;
14397
+ var units = [];
14398
+ var i = 0;
14399
+ var value;
14400
+ var overflow;
14401
+ var unitType;
14402
+ var lastUnitStart = -1;
14403
+ var lastUnitType = 0;
14404
+ // logger.log('PES:' + Hex.hexDump(array));
14311
14405
 
14312
- // ():void
14313
- var _proto = ExpGolomb.prototype;
14314
- _proto.loadWord = function loadWord() {
14315
- var data = this.data;
14316
- var bytesAvailable = this.bytesAvailable;
14317
- var position = data.byteLength - bytesAvailable;
14318
- var workingBytes = new Uint8Array(4);
14319
- var availableBytes = Math.min(4, bytesAvailable);
14406
+ if (state === -1) {
14407
+ // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14408
+ lastUnitStart = 0;
14409
+ // NALu type is value read from offset 0
14410
+ lastUnitType = this.getNALuType(array, 0);
14411
+ state = 0;
14412
+ i = 1;
14413
+ }
14414
+ while (i < len) {
14415
+ value = array[i++];
14416
+ // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14417
+ if (!state) {
14418
+ state = value ? 0 : 1;
14419
+ continue;
14420
+ }
14421
+ if (state === 1) {
14422
+ state = value ? 0 : 2;
14423
+ continue;
14424
+ }
14425
+ // here we have state either equal to 2 or 3
14426
+ if (!value) {
14427
+ state = 3;
14428
+ } else if (value === 1) {
14429
+ overflow = i - state - 1;
14430
+ if (lastUnitStart >= 0) {
14431
+ var unit = {
14432
+ data: array.subarray(lastUnitStart, overflow),
14433
+ type: lastUnitType
14434
+ };
14435
+ // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14436
+ units.push(unit);
14437
+ } else {
14438
+ // lastUnitStart is undefined => this is the first start code found in this PES packet
14439
+ // first check if start code delimiter is overlapping between 2 PES packets,
14440
+ // ie it started in last packet (lastState not zero)
14441
+ // and ended at the beginning of this PES packet (i <= 4 - lastState)
14442
+ var lastUnit = this.getLastNalUnit(track.samples);
14443
+ if (lastUnit) {
14444
+ if (lastState && i <= 4 - lastState) {
14445
+ // start delimiter overlapping between PES packets
14446
+ // strip start delimiter bytes from the end of last NAL unit
14447
+ // check if lastUnit had a state different from zero
14448
+ if (lastUnit.state) {
14449
+ // strip last bytes
14450
+ lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14451
+ }
14452
+ }
14453
+ // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14454
+
14455
+ if (overflow > 0) {
14456
+ // logger.log('first NALU found with overflow:' + overflow);
14457
+ lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14458
+ lastUnit.state = 0;
14459
+ }
14460
+ }
14461
+ }
14462
+ // check if we can read unit type
14463
+ if (i < len) {
14464
+ unitType = this.getNALuType(array, i);
14465
+ // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14466
+ lastUnitStart = i;
14467
+ lastUnitType = unitType;
14468
+ state = 0;
14469
+ } else {
14470
+ // not enough byte to read unit type. let's read it on next PES parsing
14471
+ state = -1;
14472
+ }
14473
+ } else {
14474
+ state = 0;
14475
+ }
14476
+ }
14477
+ if (lastUnitStart >= 0 && state >= 0) {
14478
+ var _unit = {
14479
+ data: array.subarray(lastUnitStart, len),
14480
+ type: lastUnitType,
14481
+ state: state
14482
+ };
14483
+ units.push(_unit);
14484
+ // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14485
+ }
14486
+ // no NALu found
14487
+ if (units.length === 0) {
14488
+ // append pes.data to previous NAL unit
14489
+ var _lastUnit = this.getLastNalUnit(track.samples);
14490
+ if (_lastUnit) {
14491
+ _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14492
+ }
14493
+ }
14494
+ track.naluState = state;
14495
+ return units;
14496
+ };
14497
+ return BaseVideoParser;
14498
+ }();
14499
+
14500
+ /**
14501
+ * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
14502
+ */
14503
+
14504
+ var ExpGolomb = /*#__PURE__*/function () {
14505
+ function ExpGolomb(data) {
14506
+ this.data = void 0;
14507
+ this.bytesAvailable = void 0;
14508
+ this.word = void 0;
14509
+ this.bitsAvailable = void 0;
14510
+ this.data = data;
14511
+ // the number of bytes left to examine in this.data
14512
+ this.bytesAvailable = data.byteLength;
14513
+ // the current word being examined
14514
+ this.word = 0; // :uint
14515
+ // the number of bits left to examine in the current word
14516
+ this.bitsAvailable = 0; // :uint
14517
+ }
14518
+
14519
+ // ():void
14520
+ var _proto = ExpGolomb.prototype;
14521
+ _proto.loadWord = function loadWord() {
14522
+ var data = this.data;
14523
+ var bytesAvailable = this.bytesAvailable;
14524
+ var position = data.byteLength - bytesAvailable;
14525
+ var workingBytes = new Uint8Array(4);
14526
+ var availableBytes = Math.min(4, bytesAvailable);
14320
14527
  if (availableBytes === 0) {
14321
14528
  throw new Error('no bytes available');
14322
14529
  }
@@ -14441,189 +14648,6 @@
14441
14648
  ;
14442
14649
  _proto.readUInt = function readUInt() {
14443
14650
  return this.readBits(32);
14444
- }
14445
-
14446
- /**
14447
- * Advance the ExpGolomb decoder past a scaling list. The scaling
14448
- * list is optionally transmitted as part of a sequence parameter
14449
- * set and is not relevant to transmuxing.
14450
- * @param count the number of entries in this scaling list
14451
- * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14452
- */;
14453
- _proto.skipScalingList = function skipScalingList(count) {
14454
- var lastScale = 8;
14455
- var nextScale = 8;
14456
- var deltaScale;
14457
- for (var j = 0; j < count; j++) {
14458
- if (nextScale !== 0) {
14459
- deltaScale = this.readEG();
14460
- nextScale = (lastScale + deltaScale + 256) % 256;
14461
- }
14462
- lastScale = nextScale === 0 ? lastScale : nextScale;
14463
- }
14464
- }
14465
-
14466
- /**
14467
- * Read a sequence parameter set and return some interesting video
14468
- * properties. A sequence parameter set is the H264 metadata that
14469
- * describes the properties of upcoming video frames.
14470
- * @returns an object with configuration parsed from the
14471
- * sequence parameter set, including the dimensions of the
14472
- * associated video frames.
14473
- */;
14474
- _proto.readSPS = function readSPS() {
14475
- var frameCropLeftOffset = 0;
14476
- var frameCropRightOffset = 0;
14477
- var frameCropTopOffset = 0;
14478
- var frameCropBottomOffset = 0;
14479
- var numRefFramesInPicOrderCntCycle;
14480
- var scalingListCount;
14481
- var i;
14482
- var readUByte = this.readUByte.bind(this);
14483
- var readBits = this.readBits.bind(this);
14484
- var readUEG = this.readUEG.bind(this);
14485
- var readBoolean = this.readBoolean.bind(this);
14486
- var skipBits = this.skipBits.bind(this);
14487
- var skipEG = this.skipEG.bind(this);
14488
- var skipUEG = this.skipUEG.bind(this);
14489
- var skipScalingList = this.skipScalingList.bind(this);
14490
- readUByte();
14491
- var profileIdc = readUByte(); // profile_idc
14492
- readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14493
- skipBits(3); // reserved_zero_3bits u(3),
14494
- readUByte(); // level_idc u(8)
14495
- skipUEG(); // seq_parameter_set_id
14496
- // some profiles have more optional data we don't need
14497
- if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14498
- var chromaFormatIdc = readUEG();
14499
- if (chromaFormatIdc === 3) {
14500
- skipBits(1);
14501
- } // separate_colour_plane_flag
14502
-
14503
- skipUEG(); // bit_depth_luma_minus8
14504
- skipUEG(); // bit_depth_chroma_minus8
14505
- skipBits(1); // qpprime_y_zero_transform_bypass_flag
14506
- if (readBoolean()) {
14507
- // seq_scaling_matrix_present_flag
14508
- scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14509
- for (i = 0; i < scalingListCount; i++) {
14510
- if (readBoolean()) {
14511
- // seq_scaling_list_present_flag[ i ]
14512
- if (i < 6) {
14513
- skipScalingList(16);
14514
- } else {
14515
- skipScalingList(64);
14516
- }
14517
- }
14518
- }
14519
- }
14520
- }
14521
- skipUEG(); // log2_max_frame_num_minus4
14522
- var picOrderCntType = readUEG();
14523
- if (picOrderCntType === 0) {
14524
- readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14525
- } else if (picOrderCntType === 1) {
14526
- skipBits(1); // delta_pic_order_always_zero_flag
14527
- skipEG(); // offset_for_non_ref_pic
14528
- skipEG(); // offset_for_top_to_bottom_field
14529
- numRefFramesInPicOrderCntCycle = readUEG();
14530
- for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14531
- skipEG();
14532
- } // offset_for_ref_frame[ i ]
14533
- }
14534
- skipUEG(); // max_num_ref_frames
14535
- skipBits(1); // gaps_in_frame_num_value_allowed_flag
14536
- var picWidthInMbsMinus1 = readUEG();
14537
- var picHeightInMapUnitsMinus1 = readUEG();
14538
- var frameMbsOnlyFlag = readBits(1);
14539
- if (frameMbsOnlyFlag === 0) {
14540
- skipBits(1);
14541
- } // mb_adaptive_frame_field_flag
14542
-
14543
- skipBits(1); // direct_8x8_inference_flag
14544
- if (readBoolean()) {
14545
- // frame_cropping_flag
14546
- frameCropLeftOffset = readUEG();
14547
- frameCropRightOffset = readUEG();
14548
- frameCropTopOffset = readUEG();
14549
- frameCropBottomOffset = readUEG();
14550
- }
14551
- var pixelRatio = [1, 1];
14552
- if (readBoolean()) {
14553
- // vui_parameters_present_flag
14554
- if (readBoolean()) {
14555
- // aspect_ratio_info_present_flag
14556
- var aspectRatioIdc = readUByte();
14557
- switch (aspectRatioIdc) {
14558
- case 1:
14559
- pixelRatio = [1, 1];
14560
- break;
14561
- case 2:
14562
- pixelRatio = [12, 11];
14563
- break;
14564
- case 3:
14565
- pixelRatio = [10, 11];
14566
- break;
14567
- case 4:
14568
- pixelRatio = [16, 11];
14569
- break;
14570
- case 5:
14571
- pixelRatio = [40, 33];
14572
- break;
14573
- case 6:
14574
- pixelRatio = [24, 11];
14575
- break;
14576
- case 7:
14577
- pixelRatio = [20, 11];
14578
- break;
14579
- case 8:
14580
- pixelRatio = [32, 11];
14581
- break;
14582
- case 9:
14583
- pixelRatio = [80, 33];
14584
- break;
14585
- case 10:
14586
- pixelRatio = [18, 11];
14587
- break;
14588
- case 11:
14589
- pixelRatio = [15, 11];
14590
- break;
14591
- case 12:
14592
- pixelRatio = [64, 33];
14593
- break;
14594
- case 13:
14595
- pixelRatio = [160, 99];
14596
- break;
14597
- case 14:
14598
- pixelRatio = [4, 3];
14599
- break;
14600
- case 15:
14601
- pixelRatio = [3, 2];
14602
- break;
14603
- case 16:
14604
- pixelRatio = [2, 1];
14605
- break;
14606
- case 255:
14607
- {
14608
- pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
14609
- break;
14610
- }
14611
- }
14612
- }
14613
- }
14614
- return {
14615
- width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
14616
- height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
14617
- pixelRatio: pixelRatio
14618
- };
14619
- };
14620
- _proto.readSliceType = function readSliceType() {
14621
- // skip NALu type
14622
- this.readUByte();
14623
- // discard first_mb_in_slice
14624
- this.readUEG();
14625
- // return slice_type
14626
- return this.readUEG();
14627
14651
  };
14628
14652
  return ExpGolomb;
14629
14653
  }();
@@ -14634,9 +14658,9 @@
14634
14658
  return _BaseVideoParser.apply(this, arguments) || this;
14635
14659
  }
14636
14660
  var _proto = AvcVideoParser.prototype;
14637
- _proto.parseAVCPES = function parseAVCPES(track, textTrack, pes, last, duration) {
14661
+ _proto.parsePES = function parsePES(track, textTrack, pes, last, duration) {
14638
14662
  var _this = this;
14639
- var units = this.parseAVCNALu(track, pes.data);
14663
+ var units = this.parseNALu(track, pes.data);
14640
14664
  var VideoSample = this.VideoSample;
14641
14665
  var push;
14642
14666
  var spsfound = false;
@@ -14661,7 +14685,7 @@
14661
14685
  // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
14662
14686
  if (spsfound && data.length > 4) {
14663
14687
  // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
14664
- var sliceType = new ExpGolomb(data).readSliceType();
14688
+ var sliceType = _this.readSliceType(data);
14665
14689
  // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
14666
14690
  // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
14667
14691
  // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
@@ -14715,8 +14739,7 @@
14715
14739
  push = true;
14716
14740
  spsfound = true;
14717
14741
  var sps = unit.data;
14718
- var expGolombDecoder = new ExpGolomb(sps);
14719
- var config = expGolombDecoder.readSPS();
14742
+ var config = _this.readSPS(sps);
14720
14743
  if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) {
14721
14744
  track.width = config.width;
14722
14745
  track.height = config.height;
@@ -14772,109 +14795,192 @@
14772
14795
  this.VideoSample = null;
14773
14796
  }
14774
14797
  };
14775
- _proto.parseAVCNALu = function parseAVCNALu(track, array) {
14776
- var len = array.byteLength;
14777
- var state = track.naluState || 0;
14778
- var lastState = state;
14779
- var units = [];
14780
- var i = 0;
14781
- var value;
14782
- var overflow;
14783
- var unitType;
14784
- var lastUnitStart = -1;
14785
- var lastUnitType = 0;
14786
- // logger.log('PES:' + Hex.hexDump(array));
14798
+ _proto.getNALuType = function getNALuType(data, offset) {
14799
+ return data[offset] & 0x1f;
14800
+ };
14801
+ _proto.readSliceType = function readSliceType(data) {
14802
+ var eg = new ExpGolomb(data);
14803
+ // skip NALu type
14804
+ eg.readUByte();
14805
+ // discard first_mb_in_slice
14806
+ eg.readUEG();
14807
+ // return slice_type
14808
+ return eg.readUEG();
14809
+ }
14787
14810
 
14788
- if (state === -1) {
14789
- // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14790
- lastUnitStart = 0;
14791
- // NALu type is value read from offset 0
14792
- lastUnitType = array[0] & 0x1f;
14793
- state = 0;
14794
- i = 1;
14795
- }
14796
- while (i < len) {
14797
- value = array[i++];
14798
- // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14799
- if (!state) {
14800
- state = value ? 0 : 1;
14801
- continue;
14802
- }
14803
- if (state === 1) {
14804
- state = value ? 0 : 2;
14805
- continue;
14811
+ /**
14812
+ * The scaling list is optionally transmitted as part of a sequence parameter
14813
+ * set and is not relevant to transmuxing.
14814
+ * @param count the number of entries in this scaling list
14815
+ * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14816
+ */;
14817
+ _proto.skipScalingList = function skipScalingList(count, reader) {
14818
+ var lastScale = 8;
14819
+ var nextScale = 8;
14820
+ var deltaScale;
14821
+ for (var j = 0; j < count; j++) {
14822
+ if (nextScale !== 0) {
14823
+ deltaScale = reader.readEG();
14824
+ nextScale = (lastScale + deltaScale + 256) % 256;
14806
14825
  }
14807
- // here we have state either equal to 2 or 3
14808
- if (!value) {
14809
- state = 3;
14810
- } else if (value === 1) {
14811
- overflow = i - state - 1;
14812
- if (lastUnitStart >= 0) {
14813
- var unit = {
14814
- data: array.subarray(lastUnitStart, overflow),
14815
- type: lastUnitType
14816
- };
14817
- // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14818
- units.push(unit);
14819
- } else {
14820
- // lastUnitStart is undefined => this is the first start code found in this PES packet
14821
- // first check if start code delimiter is overlapping between 2 PES packets,
14822
- // ie it started in last packet (lastState not zero)
14823
- // and ended at the beginning of this PES packet (i <= 4 - lastState)
14824
- var lastUnit = this.getLastNalUnit(track.samples);
14825
- if (lastUnit) {
14826
- if (lastState && i <= 4 - lastState) {
14827
- // start delimiter overlapping between PES packets
14828
- // strip start delimiter bytes from the end of last NAL unit
14829
- // check if lastUnit had a state different from zero
14830
- if (lastUnit.state) {
14831
- // strip last bytes
14832
- lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14833
- }
14834
- }
14835
- // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14826
+ lastScale = nextScale === 0 ? lastScale : nextScale;
14827
+ }
14828
+ }
14836
14829
 
14837
- if (overflow > 0) {
14838
- // logger.log('first NALU found with overflow:' + overflow);
14839
- lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14840
- lastUnit.state = 0;
14841
- }
14842
- }
14843
- }
14844
- // check if we can read unit type
14845
- if (i < len) {
14846
- unitType = array[i] & 0x1f;
14847
- // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14848
- lastUnitStart = i;
14849
- lastUnitType = unitType;
14850
- state = 0;
14851
- } else {
14852
- // not enough byte to read unit type. let's read it on next PES parsing
14853
- state = -1;
14830
+ /**
14831
+ * Read a sequence parameter set and return some interesting video
14832
+ * properties. A sequence parameter set is the H264 metadata that
14833
+ * describes the properties of upcoming video frames.
14834
+ * @returns an object with configuration parsed from the
14835
+ * sequence parameter set, including the dimensions of the
14836
+ * associated video frames.
14837
+ */;
14838
+ _proto.readSPS = function readSPS(sps) {
14839
+ var eg = new ExpGolomb(sps);
14840
+ var frameCropLeftOffset = 0;
14841
+ var frameCropRightOffset = 0;
14842
+ var frameCropTopOffset = 0;
14843
+ var frameCropBottomOffset = 0;
14844
+ var numRefFramesInPicOrderCntCycle;
14845
+ var scalingListCount;
14846
+ var i;
14847
+ var readUByte = eg.readUByte.bind(eg);
14848
+ var readBits = eg.readBits.bind(eg);
14849
+ var readUEG = eg.readUEG.bind(eg);
14850
+ var readBoolean = eg.readBoolean.bind(eg);
14851
+ var skipBits = eg.skipBits.bind(eg);
14852
+ var skipEG = eg.skipEG.bind(eg);
14853
+ var skipUEG = eg.skipUEG.bind(eg);
14854
+ var skipScalingList = this.skipScalingList.bind(this);
14855
+ readUByte();
14856
+ var profileIdc = readUByte(); // profile_idc
14857
+ readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14858
+ skipBits(3); // reserved_zero_3bits u(3),
14859
+ readUByte(); // level_idc u(8)
14860
+ skipUEG(); // seq_parameter_set_id
14861
+ // some profiles have more optional data we don't need
14862
+ if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14863
+ var chromaFormatIdc = readUEG();
14864
+ if (chromaFormatIdc === 3) {
14865
+ skipBits(1);
14866
+ } // separate_colour_plane_flag
14867
+
14868
+ skipUEG(); // bit_depth_luma_minus8
14869
+ skipUEG(); // bit_depth_chroma_minus8
14870
+ skipBits(1); // qpprime_y_zero_transform_bypass_flag
14871
+ if (readBoolean()) {
14872
+ // seq_scaling_matrix_present_flag
14873
+ scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14874
+ for (i = 0; i < scalingListCount; i++) {
14875
+ if (readBoolean()) {
14876
+ // seq_scaling_list_present_flag[ i ]
14877
+ if (i < 6) {
14878
+ skipScalingList(16, eg);
14879
+ } else {
14880
+ skipScalingList(64, eg);
14881
+ }
14882
+ }
14854
14883
  }
14855
- } else {
14856
- state = 0;
14857
14884
  }
14858
14885
  }
14859
- if (lastUnitStart >= 0 && state >= 0) {
14860
- var _unit = {
14861
- data: array.subarray(lastUnitStart, len),
14862
- type: lastUnitType,
14863
- state: state
14864
- };
14865
- units.push(_unit);
14866
- // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14886
+ skipUEG(); // log2_max_frame_num_minus4
14887
+ var picOrderCntType = readUEG();
14888
+ if (picOrderCntType === 0) {
14889
+ readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14890
+ } else if (picOrderCntType === 1) {
14891
+ skipBits(1); // delta_pic_order_always_zero_flag
14892
+ skipEG(); // offset_for_non_ref_pic
14893
+ skipEG(); // offset_for_top_to_bottom_field
14894
+ numRefFramesInPicOrderCntCycle = readUEG();
14895
+ for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14896
+ skipEG();
14897
+ } // offset_for_ref_frame[ i ]
14867
14898
  }
14868
- // no NALu found
14869
- if (units.length === 0) {
14870
- // append pes.data to previous NAL unit
14871
- var _lastUnit = this.getLastNalUnit(track.samples);
14872
- if (_lastUnit) {
14873
- _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14899
+ skipUEG(); // max_num_ref_frames
14900
+ skipBits(1); // gaps_in_frame_num_value_allowed_flag
14901
+ var picWidthInMbsMinus1 = readUEG();
14902
+ var picHeightInMapUnitsMinus1 = readUEG();
14903
+ var frameMbsOnlyFlag = readBits(1);
14904
+ if (frameMbsOnlyFlag === 0) {
14905
+ skipBits(1);
14906
+ } // mb_adaptive_frame_field_flag
14907
+
14908
+ skipBits(1); // direct_8x8_inference_flag
14909
+ if (readBoolean()) {
14910
+ // frame_cropping_flag
14911
+ frameCropLeftOffset = readUEG();
14912
+ frameCropRightOffset = readUEG();
14913
+ frameCropTopOffset = readUEG();
14914
+ frameCropBottomOffset = readUEG();
14915
+ }
14916
+ var pixelRatio = [1, 1];
14917
+ if (readBoolean()) {
14918
+ // vui_parameters_present_flag
14919
+ if (readBoolean()) {
14920
+ // aspect_ratio_info_present_flag
14921
+ var aspectRatioIdc = readUByte();
14922
+ switch (aspectRatioIdc) {
14923
+ case 1:
14924
+ pixelRatio = [1, 1];
14925
+ break;
14926
+ case 2:
14927
+ pixelRatio = [12, 11];
14928
+ break;
14929
+ case 3:
14930
+ pixelRatio = [10, 11];
14931
+ break;
14932
+ case 4:
14933
+ pixelRatio = [16, 11];
14934
+ break;
14935
+ case 5:
14936
+ pixelRatio = [40, 33];
14937
+ break;
14938
+ case 6:
14939
+ pixelRatio = [24, 11];
14940
+ break;
14941
+ case 7:
14942
+ pixelRatio = [20, 11];
14943
+ break;
14944
+ case 8:
14945
+ pixelRatio = [32, 11];
14946
+ break;
14947
+ case 9:
14948
+ pixelRatio = [80, 33];
14949
+ break;
14950
+ case 10:
14951
+ pixelRatio = [18, 11];
14952
+ break;
14953
+ case 11:
14954
+ pixelRatio = [15, 11];
14955
+ break;
14956
+ case 12:
14957
+ pixelRatio = [64, 33];
14958
+ break;
14959
+ case 13:
14960
+ pixelRatio = [160, 99];
14961
+ break;
14962
+ case 14:
14963
+ pixelRatio = [4, 3];
14964
+ break;
14965
+ case 15:
14966
+ pixelRatio = [3, 2];
14967
+ break;
14968
+ case 16:
14969
+ pixelRatio = [2, 1];
14970
+ break;
14971
+ case 255:
14972
+ {
14973
+ pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
14974
+ break;
14975
+ }
14976
+ }
14874
14977
  }
14875
14978
  }
14876
- track.naluState = state;
14877
- return units;
14979
+ return {
14980
+ width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
14981
+ height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
14982
+ pixelRatio: pixelRatio
14983
+ };
14878
14984
  };
14879
14985
  return AvcVideoParser;
14880
14986
  }(BaseVideoParser);
@@ -14894,7 +15000,7 @@
14894
15000
  }
14895
15001
  var _proto = SampleAesDecrypter.prototype;
14896
15002
  _proto.decryptBuffer = function decryptBuffer(encryptedData) {
14897
- return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
15003
+ return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc);
14898
15004
  }
14899
15005
 
14900
15006
  // AAC - encrypt all full 16 bytes blocks starting from offset 16
@@ -15013,7 +15119,7 @@
15013
15119
  this.observer = observer;
15014
15120
  this.config = config;
15015
15121
  this.typeSupported = typeSupported;
15016
- this.videoParser = new AvcVideoParser();
15122
+ this.videoParser = null;
15017
15123
  }
15018
15124
  TSDemuxer.probe = function probe(data) {
15019
15125
  var syncOffset = TSDemuxer.syncOffset(data);
@@ -15183,7 +15289,16 @@
15183
15289
  case videoPid:
15184
15290
  if (stt) {
15185
15291
  if (videoData && (pes = parsePES(videoData))) {
15186
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
15292
+ if (this.videoParser === null) {
15293
+ switch (videoTrack.segmentCodec) {
15294
+ case 'avc':
15295
+ this.videoParser = new AvcVideoParser();
15296
+ break;
15297
+ }
15298
+ }
15299
+ if (this.videoParser !== null) {
15300
+ this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
15301
+ }
15187
15302
  }
15188
15303
  videoData = {
15189
15304
  data: [],
@@ -15341,8 +15456,17 @@
15341
15456
  // try to parse last PES packets
15342
15457
  var pes;
15343
15458
  if (videoData && (pes = parsePES(videoData))) {
15344
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
15345
- videoTrack.pesData = null;
15459
+ if (this.videoParser === null) {
15460
+ switch (videoTrack.segmentCodec) {
15461
+ case 'avc':
15462
+ this.videoParser = new AvcVideoParser();
15463
+ break;
15464
+ }
15465
+ }
15466
+ if (this.videoParser !== null) {
15467
+ this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
15468
+ videoTrack.pesData = null;
15469
+ }
15346
15470
  } else {
15347
15471
  // either avcData null or PES truncated, keep it for next frag parsing
15348
15472
  videoTrack.pesData = videoData;
@@ -15644,7 +15768,10 @@
15644
15768
  logger.warn('Unsupported EC-3 in M2TS found');
15645
15769
  break;
15646
15770
  case 0x24:
15647
- logger.warn('Unsupported HEVC in M2TS found');
15771
+ // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
15772
+ {
15773
+ logger.warn('Unsupported HEVC in M2TS found');
15774
+ }
15648
15775
  break;
15649
15776
  }
15650
15777
  // move to the next table entry
@@ -15872,6 +15999,8 @@
15872
15999
  avc1: [],
15873
16000
  // codingname
15874
16001
  avcC: [],
16002
+ hvc1: [],
16003
+ hvcC: [],
15875
16004
  btrt: [],
15876
16005
  dinf: [],
15877
16006
  dref: [],
@@ -16299,8 +16428,10 @@
16299
16428
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
16300
16429
  }
16301
16430
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
16302
- } else {
16431
+ } else if (track.segmentCodec === 'avc') {
16303
16432
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
16433
+ } else {
16434
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
16304
16435
  }
16305
16436
  };
16306
16437
  MP4.tkhd = function tkhd(track) {
@@ -16438,6 +16569,84 @@
16438
16569
  var result = appendUint8Array(MP4.FTYP, movie);
16439
16570
  return result;
16440
16571
  };
16572
+ MP4.hvc1 = function hvc1(track) {
16573
+ var ps = track.params;
16574
+ var units = [track.vps, track.sps, track.pps];
16575
+ var NALuLengthSize = 4;
16576
+ var config = new Uint8Array([0x01, ps.general_profile_space << 6 | (ps.general_tier_flag ? 32 : 0) | ps.general_profile_idc, ps.general_profile_compatibility_flags[0], ps.general_profile_compatibility_flags[1], ps.general_profile_compatibility_flags[2], ps.general_profile_compatibility_flags[3], ps.general_constraint_indicator_flags[0], ps.general_constraint_indicator_flags[1], ps.general_constraint_indicator_flags[2], ps.general_constraint_indicator_flags[3], ps.general_constraint_indicator_flags[4], ps.general_constraint_indicator_flags[5], ps.general_level_idc, 240 | ps.min_spatial_segmentation_idc >> 8, 255 & ps.min_spatial_segmentation_idc, 252 | ps.parallelismType, 252 | ps.chroma_format_idc, 248 | ps.bit_depth_luma_minus8, 248 | ps.bit_depth_chroma_minus8, 0x00, parseInt(ps.frame_rate.fps), NALuLengthSize - 1 | ps.temporal_id_nested << 2 | ps.num_temporal_layers << 3 | (ps.frame_rate.fixed ? 64 : 0), units.length]);
16577
+
16578
+ // compute hvcC size in bytes
16579
+ var length = config.length;
16580
+ for (var i = 0; i < units.length; i += 1) {
16581
+ length += 3;
16582
+ for (var j = 0; j < units[i].length; j += 1) {
16583
+ length += 2 + units[i][j].length;
16584
+ }
16585
+ }
16586
+ var hvcC = new Uint8Array(length);
16587
+ hvcC.set(config, 0);
16588
+ length = config.length;
16589
+ // append parameter set units: one vps, one or more sps and pps
16590
+ var iMax = units.length - 1;
16591
+ for (var _i = 0; _i < units.length; _i += 1) {
16592
+ hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length);
16593
+ length += 3;
16594
+ for (var _j = 0; _j < units[_i].length; _j += 1) {
16595
+ hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length);
16596
+ length += 2;
16597
+ hvcC.set(units[_i][_j], length);
16598
+ length += units[_i][_j].length;
16599
+ }
16600
+ }
16601
+ var hvcc = MP4.box(MP4.types.hvcC, hvcC);
16602
+ var width = track.width;
16603
+ var height = track.height;
16604
+ var hSpacing = track.pixelRatio[0];
16605
+ var vSpacing = track.pixelRatio[1];
16606
+ return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
16607
+ // reserved
16608
+ 0x00, 0x00, 0x00,
16609
+ // reserved
16610
+ 0x00, 0x01,
16611
+ // data_reference_index
16612
+ 0x00, 0x00,
16613
+ // pre_defined
16614
+ 0x00, 0x00,
16615
+ // reserved
16616
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16617
+ // pre_defined
16618
+ width >> 8 & 0xff, width & 0xff,
16619
+ // width
16620
+ height >> 8 & 0xff, height & 0xff,
16621
+ // height
16622
+ 0x00, 0x48, 0x00, 0x00,
16623
+ // horizresolution
16624
+ 0x00, 0x48, 0x00, 0x00,
16625
+ // vertresolution
16626
+ 0x00, 0x00, 0x00, 0x00,
16627
+ // reserved
16628
+ 0x00, 0x01,
16629
+ // frame_count
16630
+ 0x12, 0x64, 0x61, 0x69, 0x6c,
16631
+ // dailymotion/hls.js
16632
+ 0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16633
+ // compressorname
16634
+ 0x00, 0x18,
16635
+ // depth = 24
16636
+ 0x11, 0x11]),
16637
+ // pre_defined = -1
16638
+ hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
16639
+ // bufferSizeDB
16640
+ 0x00, 0x2d, 0xc6, 0xc0,
16641
+ // maxBitrate
16642
+ 0x00, 0x2d, 0xc6, 0xc0])),
16643
+ // avgBitrate
16644
+ MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
16645
+ // hSpacing
16646
+ hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
16647
+ // vSpacing
16648
+ vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
16649
+ };
16441
16650
  return MP4;
16442
16651
  }();
16443
16652
  MP4.types = void 0;
@@ -16824,9 +17033,9 @@
16824
17033
  var foundOverlap = delta < -1;
16825
17034
  if (foundHole || foundOverlap) {
16826
17035
  if (foundHole) {
16827
- logger.warn("AVC: " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
17036
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
16828
17037
  } else {
16829
- logger.warn("AVC: " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
17038
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
16830
17039
  }
16831
17040
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
16832
17041
  firstDTS = nextAvcDts;
@@ -16835,12 +17044,24 @@
16835
17044
  inputSamples[0].dts = firstDTS;
16836
17045
  inputSamples[0].pts = firstPTS;
16837
17046
  } else {
17047
+ var isPTSOrderRetained = true;
16838
17048
  for (var _i = 0; _i < inputSamples.length; _i++) {
16839
- if (inputSamples[_i].dts > firstPTS) {
17049
+ if (inputSamples[_i].dts > firstPTS && isPTSOrderRetained) {
16840
17050
  break;
16841
17051
  }
17052
+ var prevPTS = inputSamples[_i].pts;
16842
17053
  inputSamples[_i].dts -= delta;
16843
17054
  inputSamples[_i].pts -= delta;
17055
+
17056
+ // check to see if this sample's PTS order has changed
17057
+ // relative to the next one
17058
+ if (_i < inputSamples.length - 1) {
17059
+ var nextSamplePTS = inputSamples[_i + 1].pts;
17060
+ var currentSamplePTS = inputSamples[_i].pts;
17061
+ var currentOrder = nextSamplePTS <= currentSamplePTS;
17062
+ var prevOrder = nextSamplePTS <= prevPTS;
17063
+ isPTSOrderRetained = currentOrder == prevOrder;
17064
+ }
16844
17065
  }
16845
17066
  }
16846
17067
  logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms");
@@ -16988,7 +17209,7 @@
16988
17209
  }
16989
17210
  }
16990
17211
  }
16991
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
17212
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
16992
17213
  mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
16993
17214
  this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
16994
17215
  this.videoSampleDuration = mp4SampleDuration;
@@ -17123,7 +17344,7 @@
17123
17344
  logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap.");
17124
17345
  for (var j = 0; j < missing; j++) {
17125
17346
  var newStamp = Math.max(nextPts, 0);
17126
- var fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17347
+ var fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17127
17348
  if (!fillFrame) {
17128
17349
  logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
17129
17350
  fillFrame = sample.unit.subarray();
@@ -17251,7 +17472,7 @@
17251
17472
  // samples count of this segment's duration
17252
17473
  var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
17253
17474
  // silent frame
17254
- var silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17475
+ var silentFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17255
17476
  logger.warn('[mp4-remuxer]: remux empty Audio');
17256
17477
  // Can't remux if we can't generate a silent frame...
17257
17478
  if (!silentFrame) {
@@ -17638,13 +17859,15 @@
17638
17859
  duration = transmuxConfig.duration,
17639
17860
  initSegmentData = transmuxConfig.initSegmentData;
17640
17861
  var keyData = getEncryptionType(uintData, decryptdata);
17641
- if (keyData && keyData.method === 'AES-128') {
17862
+ if (keyData && isFullSegmentEncryption(keyData.method)) {
17642
17863
  var decrypter = this.getDecrypter();
17864
+ var aesMode = getAesModeFromFullSegmentMethod(keyData.method);
17865
+
17643
17866
  // Software decryption is synchronous; webCrypto is not
17644
17867
  if (decrypter.isSync()) {
17645
17868
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
17646
17869
  // data is handled in the flush() call
17647
- var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
17870
+ var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode);
17648
17871
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
17649
17872
  var loadingParts = chunkMeta.part > -1;
17650
17873
  if (loadingParts) {
@@ -17656,7 +17879,7 @@
17656
17879
  }
17657
17880
  uintData = new Uint8Array(decryptedData);
17658
17881
  } else {
17659
- this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(function (decryptedData) {
17882
+ this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(function (decryptedData) {
17660
17883
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
17661
17884
  // the decrypted data has been transmuxed
17662
17885
  var result = _this.push(decryptedData, null, chunkMeta);
@@ -18277,7 +18500,7 @@
18277
18500
  observer.on(Events.ERROR, forwardMessage);
18278
18501
 
18279
18502
  // forward logger events to main thread
18280
- var forwardWorkerLogs = function forwardWorkerLogs() {
18503
+ var forwardWorkerLogs = function forwardWorkerLogs(logger) {
18281
18504
  var _loop = function _loop(logFn) {
18282
18505
  var func = function func(message) {
18283
18506
  forwardMessage('workerLog', {
@@ -18298,8 +18521,8 @@
18298
18521
  {
18299
18522
  var config = JSON.parse(data.config);
18300
18523
  self.transmuxer = new Transmuxer(observer, data.typeSupported, config, data.vendor, data.id);
18301
- enableLogs(config.debug, data.id);
18302
- forwardWorkerLogs();
18524
+ var logger = enableLogs(config.debug, data.id);
18525
+ forwardWorkerLogs(logger);
18303
18526
  forwardMessage('init', null);
18304
18527
  break;
18305
18528
  }
@@ -18473,16 +18696,7 @@
18473
18696
  this.observer = new EventEmitter();
18474
18697
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
18475
18698
  this.observer.on(Events.ERROR, forwardMessage);
18476
- var MediaSource = getMediaSource(config.preferManagedMediaSource) || {
18477
- isTypeSupported: function isTypeSupported() {
18478
- return false;
18479
- }
18480
- };
18481
- var m2tsTypeSupported = {
18482
- mpeg: MediaSource.isTypeSupported('audio/mpeg'),
18483
- mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
18484
- ac3: false
18485
- };
18699
+ var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
18486
18700
 
18487
18701
  // navigator.vendor is not always available in Web Worker
18488
18702
  // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
@@ -18739,21 +18953,26 @@
18739
18953
  var MAX_START_GAP_JUMP = 2.0;
18740
18954
  var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
18741
18955
  var SKIP_BUFFER_RANGE_START = 0.05;
18742
- var GapController = /*#__PURE__*/function () {
18956
+ var GapController = /*#__PURE__*/function (_Logger) {
18957
+ _inheritsLoose(GapController, _Logger);
18743
18958
  function GapController(config, media, fragmentTracker, hls) {
18744
- this.config = void 0;
18745
- this.media = null;
18746
- this.fragmentTracker = void 0;
18747
- this.hls = void 0;
18748
- this.nudgeRetry = 0;
18749
- this.stallReported = false;
18750
- this.stalled = null;
18751
- this.moved = false;
18752
- this.seeking = false;
18753
- this.config = config;
18754
- this.media = media;
18755
- this.fragmentTracker = fragmentTracker;
18756
- this.hls = hls;
18959
+ var _this;
18960
+ _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
18961
+ _this.config = void 0;
18962
+ _this.media = null;
18963
+ _this.fragmentTracker = void 0;
18964
+ _this.hls = void 0;
18965
+ _this.nudgeRetry = 0;
18966
+ _this.stallReported = false;
18967
+ _this.stalled = null;
18968
+ _this.moved = false;
18969
+ _this.seeking = false;
18970
+ _this.ended = 0;
18971
+ _this.config = config;
18972
+ _this.media = media;
18973
+ _this.fragmentTracker = fragmentTracker;
18974
+ _this.hls = hls;
18975
+ return _this;
18757
18976
  }
18758
18977
  var _proto = GapController.prototype;
18759
18978
  _proto.destroy = function destroy() {
@@ -18768,7 +18987,7 @@
18768
18987
  *
18769
18988
  * @param lastCurrentTime - Previously read playhead position
18770
18989
  */;
18771
- _proto.poll = function poll(lastCurrentTime, activeFrag) {
18990
+ _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
18772
18991
  var config = this.config,
18773
18992
  media = this.media,
18774
18993
  stalled = this.stalled;
@@ -18783,6 +19002,7 @@
18783
19002
 
18784
19003
  // The playhead is moving, no-op
18785
19004
  if (currentTime !== lastCurrentTime) {
19005
+ this.ended = 0;
18786
19006
  this.moved = true;
18787
19007
  if (!seeking) {
18788
19008
  this.nudgeRetry = 0;
@@ -18791,7 +19011,7 @@
18791
19011
  // The playhead is now moving, but was previously stalled
18792
19012
  if (this.stallReported) {
18793
19013
  var _stalledDuration = self.performance.now() - stalled;
18794
- logger.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
19014
+ this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
18795
19015
  this.stallReported = false;
18796
19016
  }
18797
19017
  this.stalled = null;
@@ -18827,7 +19047,6 @@
18827
19047
  // Skip start gaps if we haven't played, but the last poll detected the start of a stall
18828
19048
  // The addition poll gives the browser a chance to jump the gap for us
18829
19049
  if (!this.moved && this.stalled !== null) {
18830
- var _level$details;
18831
19050
  // There is no playable buffer (seeked, waiting for buffer)
18832
19051
  var isBuffered = bufferInfo.len > 0;
18833
19052
  if (!isBuffered && !nextStart) {
@@ -18839,9 +19058,8 @@
18839
19058
  // When joining a live stream with audio tracks, account for live playlist window sliding by allowing
18840
19059
  // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
18841
19060
  // that begins over 1 target duration after the video start position.
18842
- var level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null;
18843
- var isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live;
18844
- var maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;
19061
+ var isLive = !!(levelDetails != null && levelDetails.live);
19062
+ var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
18845
19063
  var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
18846
19064
  if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
18847
19065
  if (!media.paused) {
@@ -18859,6 +19077,17 @@
18859
19077
  }
18860
19078
  var stalledDuration = tnow - stalled;
18861
19079
  if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
19080
+ // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
19081
+ if (state === State.ENDED && !(levelDetails && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
19082
+ if (stalledDuration < 1000 || this.ended) {
19083
+ return;
19084
+ }
19085
+ this.ended = currentTime;
19086
+ this.hls.trigger(Events.MEDIA_ENDED, {
19087
+ stalled: true
19088
+ });
19089
+ return;
19090
+ }
18862
19091
  // Report stalling after trying to fix
18863
19092
  this._reportStall(bufferInfo);
18864
19093
  if (!this.media) {
@@ -18900,7 +19129,7 @@
18900
19129
  // needs to cross some sort of threshold covering all source-buffers content
18901
19130
  // to start playing properly.
18902
19131
  if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
18903
- logger.warn('Trying to nudge playhead over buffer-hole');
19132
+ this.warn('Trying to nudge playhead over buffer-hole');
18904
19133
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
18905
19134
  // We only try to jump the hole if it's under the configured size
18906
19135
  // Reset stalled so to rearm watchdog timer
@@ -18922,7 +19151,7 @@
18922
19151
  // Report stalled error once
18923
19152
  this.stallReported = true;
18924
19153
  var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
18925
- logger.warn(error.message);
19154
+ this.warn(error.message);
18926
19155
  hls.trigger(Events.ERROR, {
18927
19156
  type: ErrorTypes.MEDIA_ERROR,
18928
19157
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -18986,7 +19215,7 @@
18986
19215
  }
18987
19216
  }
18988
19217
  var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
18989
- logger.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
19218
+ this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
18990
19219
  this.moved = true;
18991
19220
  this.stalled = null;
18992
19221
  media.currentTime = targetTime;
@@ -19025,7 +19254,7 @@
19025
19254
  var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
19026
19255
  // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
19027
19256
  var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
19028
- logger.warn(error.message);
19257
+ this.warn(error.message);
19029
19258
  media.currentTime = targetTime;
19030
19259
  hls.trigger(Events.ERROR, {
19031
19260
  type: ErrorTypes.MEDIA_ERROR,
@@ -19035,7 +19264,7 @@
19035
19264
  });
19036
19265
  } else {
19037
19266
  var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
19038
- logger.error(_error.message);
19267
+ this.error(_error.message);
19039
19268
  hls.trigger(Events.ERROR, {
19040
19269
  type: ErrorTypes.MEDIA_ERROR,
19041
19270
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -19045,14 +19274,14 @@
19045
19274
  }
19046
19275
  };
19047
19276
  return GapController;
19048
- }();
19277
+ }(Logger);
19049
19278
 
19050
19279
  var TICK_INTERVAL = 100; // how often to tick in ms
19051
19280
  var StreamController = /*#__PURE__*/function (_BaseStreamController) {
19052
19281
  _inheritsLoose(StreamController, _BaseStreamController);
19053
19282
  function StreamController(hls, fragmentTracker, keyLoader) {
19054
19283
  var _this;
19055
- _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN) || this;
19284
+ _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
19056
19285
  _this.audioCodecSwap = false;
19057
19286
  _this.gapController = null;
19058
19287
  _this.level = -1;
@@ -19060,27 +19289,43 @@
19060
19289
  _this.altAudio = false;
19061
19290
  _this.audioOnly = false;
19062
19291
  _this.fragPlaying = null;
19063
- _this.onvplaying = null;
19064
- _this.onvseeked = null;
19065
19292
  _this.fragLastKbps = 0;
19066
19293
  _this.couldBacktrack = false;
19067
19294
  _this.backtrackFragment = null;
19068
19295
  _this.audioCodecSwitch = false;
19069
19296
  _this.videoBuffer = null;
19070
- _this._registerListeners();
19297
+ _this.onMediaPlaying = function () {
19298
+ // tick to speed up FRAG_CHANGED triggering
19299
+ _this.tick();
19300
+ };
19301
+ _this.onMediaSeeked = function () {
19302
+ var media = _this.media;
19303
+ var currentTime = media ? media.currentTime : null;
19304
+ if (isFiniteNumber(currentTime)) {
19305
+ _this.log("Media seeked to " + currentTime.toFixed(3));
19306
+ }
19307
+
19308
+ // If seeked was issued before buffer was appended do not tick immediately
19309
+ var bufferInfo = _this.getMainFwdBufferInfo();
19310
+ if (bufferInfo === null || bufferInfo.len === 0) {
19311
+ _this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19312
+ return;
19313
+ }
19314
+
19315
+ // tick to speed up FRAG_CHANGED triggering
19316
+ _this.tick();
19317
+ };
19318
+ _this.registerListeners();
19071
19319
  return _this;
19072
19320
  }
19073
19321
  var _proto = StreamController.prototype;
19074
- _proto._registerListeners = function _registerListeners() {
19322
+ _proto.registerListeners = function registerListeners() {
19323
+ _BaseStreamController.prototype.registerListeners.call(this);
19075
19324
  var hls = this.hls;
19076
- hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19077
- hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19078
- hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19079
19325
  hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19080
19326
  hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
19081
19327
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19082
19328
  hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19083
- hls.on(Events.ERROR, this.onError, this);
19084
19329
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19085
19330
  hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19086
19331
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19088,15 +19333,12 @@
19088
19333
  hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
19089
19334
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19090
19335
  };
19091
- _proto._unregisterListeners = function _unregisterListeners() {
19336
+ _proto.unregisterListeners = function unregisterListeners() {
19337
+ _BaseStreamController.prototype.unregisterListeners.call(this);
19092
19338
  var hls = this.hls;
19093
- hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19094
- hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19095
- hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19096
19339
  hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19097
19340
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19098
19341
  hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19099
- hls.off(Events.ERROR, this.onError, this);
19100
19342
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19101
19343
  hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19102
19344
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19105,7 +19347,9 @@
19105
19347
  hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19106
19348
  };
19107
19349
  _proto.onHandlerDestroying = function onHandlerDestroying() {
19108
- this._unregisterListeners();
19350
+ // @ts-ignore
19351
+ this.onMediaPlaying = this.onMediaSeeked = null;
19352
+ this.unregisterListeners();
19109
19353
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
19110
19354
  };
19111
19355
  _proto.startLoad = function startLoad(startPosition) {
@@ -19215,7 +19459,7 @@
19215
19459
  if (this.altAudio && this.audioOnly) {
19216
19460
  return;
19217
19461
  }
19218
- if (!(levels != null && levels[level])) {
19462
+ if (!this.buffering || !(levels != null && levels[level])) {
19219
19463
  return;
19220
19464
  }
19221
19465
  var levelInfo = levels[level];
@@ -19421,18 +19665,15 @@
19421
19665
  _proto.onMediaAttached = function onMediaAttached(event, data) {
19422
19666
  _BaseStreamController.prototype.onMediaAttached.call(this, event, data);
19423
19667
  var media = data.media;
19424
- this.onvplaying = this.onMediaPlaying.bind(this);
19425
- this.onvseeked = this.onMediaSeeked.bind(this);
19426
- media.addEventListener('playing', this.onvplaying);
19427
- media.addEventListener('seeked', this.onvseeked);
19668
+ media.addEventListener('playing', this.onMediaPlaying);
19669
+ media.addEventListener('seeked', this.onMediaSeeked);
19428
19670
  this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
19429
19671
  };
19430
19672
  _proto.onMediaDetaching = function onMediaDetaching() {
19431
19673
  var media = this.media;
19432
- if (media && this.onvplaying && this.onvseeked) {
19433
- media.removeEventListener('playing', this.onvplaying);
19434
- media.removeEventListener('seeked', this.onvseeked);
19435
- this.onvplaying = this.onvseeked = null;
19674
+ if (media) {
19675
+ media.removeEventListener('playing', this.onMediaPlaying);
19676
+ media.removeEventListener('seeked', this.onMediaSeeked);
19436
19677
  this.videoBuffer = null;
19437
19678
  }
19438
19679
  this.fragPlaying = null;
@@ -19442,27 +19683,6 @@
19442
19683
  }
19443
19684
  _BaseStreamController.prototype.onMediaDetaching.call(this);
19444
19685
  };
19445
- _proto.onMediaPlaying = function onMediaPlaying() {
19446
- // tick to speed up FRAG_CHANGED triggering
19447
- this.tick();
19448
- };
19449
- _proto.onMediaSeeked = function onMediaSeeked() {
19450
- var media = this.media;
19451
- var currentTime = media ? media.currentTime : null;
19452
- if (isFiniteNumber(currentTime)) {
19453
- this.log("Media seeked to " + currentTime.toFixed(3));
19454
- }
19455
-
19456
- // If seeked was issued before buffer was appended do not tick immediately
19457
- var bufferInfo = this.getMainFwdBufferInfo();
19458
- if (bufferInfo === null || bufferInfo.len === 0) {
19459
- this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19460
- return;
19461
- }
19462
-
19463
- // tick to speed up FRAG_CHANGED triggering
19464
- this.tick();
19465
- };
19466
19686
  _proto.onManifestLoading = function onManifestLoading() {
19467
19687
  // reset buffer on manifest loading
19468
19688
  this.log('Trigger BUFFER_RESET');
@@ -19743,8 +19963,10 @@
19743
19963
  }
19744
19964
  if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
19745
19965
  // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
19746
- var activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
19747
- gapController.poll(this.lastCurrentTime, activeFrag);
19966
+ var state = this.state;
19967
+ var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
19968
+ var levelDetails = this.getLevelDetails();
19969
+ gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
19748
19970
  }
19749
19971
  this.lastCurrentTime = media.currentTime;
19750
19972
  };
@@ -20209,9 +20431,12 @@
20209
20431
  * The configuration object provided on player instantiation.
20210
20432
  */
20211
20433
  this.userConfig = void 0;
20434
+ /**
20435
+ * The logger functions used by this player instance, configured on player instantiation.
20436
+ */
20437
+ this.logger = void 0;
20212
20438
  this.coreComponents = void 0;
20213
20439
  this.networkControllers = void 0;
20214
- this.started = false;
20215
20440
  this._emitter = new EventEmitter();
20216
20441
  this._autoLevelCapping = -1;
20217
20442
  this._maxHdcpLevel = null;
@@ -20228,11 +20453,11 @@
20228
20453
  this._media = null;
20229
20454
  this.url = null;
20230
20455
  this.triggeringException = void 0;
20231
- enableLogs(userConfig.debug || false, 'Hls instance');
20232
- var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
20456
+ var logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance');
20457
+ var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
20233
20458
  this.userConfig = userConfig;
20234
20459
  if (config.progressive) {
20235
- enableStreamingMode(config);
20460
+ enableStreamingMode(config, logger);
20236
20461
  }
20237
20462
 
20238
20463
  // core controllers and network loaders
@@ -20340,7 +20565,7 @@
20340
20565
  try {
20341
20566
  return this.emit(event, event, eventObject);
20342
20567
  } catch (error) {
20343
- logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20568
+ this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20344
20569
  // Prevent recursion in error event handlers that throw #5497
20345
20570
  if (!this.triggeringException) {
20346
20571
  this.triggeringException = true;
@@ -20366,7 +20591,7 @@
20366
20591
  * Dispose of the instance
20367
20592
  */;
20368
20593
  _proto.destroy = function destroy() {
20369
- logger.log('destroy');
20594
+ this.logger.log('destroy');
20370
20595
  this.trigger(Events.DESTROYING, undefined);
20371
20596
  this.detachMedia();
20372
20597
  this.removeAllListeners();
@@ -20391,7 +20616,7 @@
20391
20616
  * Attaches Hls.js to a media element
20392
20617
  */;
20393
20618
  _proto.attachMedia = function attachMedia(media) {
20394
- logger.log('attachMedia');
20619
+ this.logger.log('attachMedia');
20395
20620
  this._media = media;
20396
20621
  this.trigger(Events.MEDIA_ATTACHING, {
20397
20622
  media: media
@@ -20402,7 +20627,7 @@
20402
20627
  * Detach Hls.js from the media
20403
20628
  */;
20404
20629
  _proto.detachMedia = function detachMedia() {
20405
- logger.log('detachMedia');
20630
+ this.logger.log('detachMedia');
20406
20631
  this.trigger(Events.MEDIA_DETACHING, undefined);
20407
20632
  this._media = null;
20408
20633
  }
@@ -20419,7 +20644,7 @@
20419
20644
  });
20420
20645
  this._autoLevelCapping = -1;
20421
20646
  this._maxHdcpLevel = null;
20422
- logger.log("loadSource:" + loadingSource);
20647
+ this.logger.log("loadSource:" + loadingSource);
20423
20648
  if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
20424
20649
  this.detachMedia();
20425
20650
  this.attachMedia(media);
@@ -20441,8 +20666,7 @@
20441
20666
  if (startPosition === void 0) {
20442
20667
  startPosition = -1;
20443
20668
  }
20444
- logger.log("startLoad(" + startPosition + ")");
20445
- this.started = true;
20669
+ this.logger.log("startLoad(" + startPosition + ")");
20446
20670
  this.networkControllers.forEach(function (controller) {
20447
20671
  controller.startLoad(startPosition);
20448
20672
  });
@@ -20452,34 +20676,31 @@
20452
20676
  * Stop loading of any stream data.
20453
20677
  */;
20454
20678
  _proto.stopLoad = function stopLoad() {
20455
- logger.log('stopLoad');
20456
- this.started = false;
20679
+ this.logger.log('stopLoad');
20457
20680
  this.networkControllers.forEach(function (controller) {
20458
20681
  controller.stopLoad();
20459
20682
  });
20460
20683
  }
20461
20684
 
20462
20685
  /**
20463
- * Resumes stream controller segment loading if previously started.
20686
+ * Resumes stream controller segment loading after `pauseBuffering` has been called.
20464
20687
  */;
20465
20688
  _proto.resumeBuffering = function resumeBuffering() {
20466
- if (this.started) {
20467
- this.networkControllers.forEach(function (controller) {
20468
- if ('fragmentLoader' in controller) {
20469
- controller.startLoad(-1);
20470
- }
20471
- });
20472
- }
20689
+ this.networkControllers.forEach(function (controller) {
20690
+ if (controller.resumeBuffering) {
20691
+ controller.resumeBuffering();
20692
+ }
20693
+ });
20473
20694
  }
20474
20695
 
20475
20696
  /**
20476
- * Stops stream controller segment loading without changing 'started' state like stopLoad().
20697
+ * Prevents stream controller from loading new segments until `resumeBuffering` is called.
20477
20698
  * This allows for media buffering to be paused without interupting playlist loading.
20478
20699
  */;
20479
20700
  _proto.pauseBuffering = function pauseBuffering() {
20480
20701
  this.networkControllers.forEach(function (controller) {
20481
- if ('fragmentLoader' in controller) {
20482
- controller.stopLoad();
20702
+ if (controller.pauseBuffering) {
20703
+ controller.pauseBuffering();
20483
20704
  }
20484
20705
  });
20485
20706
  }
@@ -20488,7 +20709,7 @@
20488
20709
  * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
20489
20710
  */;
20490
20711
  _proto.swapAudioCodec = function swapAudioCodec() {
20491
- logger.log('swapAudioCodec');
20712
+ this.logger.log('swapAudioCodec');
20492
20713
  this.streamController.swapAudioCodec();
20493
20714
  }
20494
20715
 
@@ -20499,7 +20720,7 @@
20499
20720
  * Automatic recovery of media-errors by this process is configurable.
20500
20721
  */;
20501
20722
  _proto.recoverMediaError = function recoverMediaError() {
20502
- logger.log('recoverMediaError');
20723
+ this.logger.log('recoverMediaError');
20503
20724
  var media = this._media;
20504
20725
  this.detachMedia();
20505
20726
  if (media) {
@@ -20554,7 +20775,7 @@
20554
20775
  * 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.
20555
20776
  */,
20556
20777
  set: function set(newLevel) {
20557
- logger.log("set currentLevel:" + newLevel);
20778
+ this.logger.log("set currentLevel:" + newLevel);
20558
20779
  this.levelController.manualLevel = newLevel;
20559
20780
  this.streamController.immediateLevelSwitch();
20560
20781
  }
@@ -20575,7 +20796,7 @@
20575
20796
  * @param newLevel - Pass -1 for automatic level selection
20576
20797
  */,
20577
20798
  set: function set(newLevel) {
20578
- logger.log("set nextLevel:" + newLevel);
20799
+ this.logger.log("set nextLevel:" + newLevel);
20579
20800
  this.levelController.manualLevel = newLevel;
20580
20801
  this.streamController.nextLevelSwitch();
20581
20802
  }
@@ -20596,7 +20817,7 @@
20596
20817
  * @param newLevel - Pass -1 for automatic level selection
20597
20818
  */,
20598
20819
  set: function set(newLevel) {
20599
- logger.log("set loadLevel:" + newLevel);
20820
+ this.logger.log("set loadLevel:" + newLevel);
20600
20821
  this.levelController.manualLevel = newLevel;
20601
20822
  }
20602
20823
 
@@ -20631,7 +20852,7 @@
20631
20852
  * Sets "first-level", see getter.
20632
20853
  */,
20633
20854
  set: function set(newLevel) {
20634
- logger.log("set firstLevel:" + newLevel);
20855
+ this.logger.log("set firstLevel:" + newLevel);
20635
20856
  this.levelController.firstLevel = newLevel;
20636
20857
  }
20637
20858
 
@@ -20658,7 +20879,7 @@
20658
20879
  * (determined from download of first segment)
20659
20880
  */,
20660
20881
  set: function set(newLevel) {
20661
- logger.log("set startLevel:" + newLevel);
20882
+ this.logger.log("set startLevel:" + newLevel);
20662
20883
  // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
20663
20884
  if (newLevel !== -1) {
20664
20885
  newLevel = Math.max(newLevel, this.minAutoLevel);
@@ -20711,7 +20932,7 @@
20711
20932
  */
20712
20933
  function set(newLevel) {
20713
20934
  if (this._autoLevelCapping !== newLevel) {
20714
- logger.log("set autoLevelCapping:" + newLevel);
20935
+ this.logger.log("set autoLevelCapping:" + newLevel);
20715
20936
  this._autoLevelCapping = newLevel;
20716
20937
  this.levelController.checkMaxAutoUpdated();
20717
20938
  }
@@ -21036,7 +21257,7 @@
21036
21257
  * Get the video-dev/hls.js package version.
21037
21258
  */
21038
21259
  function get() {
21039
- return "1.5.4";
21260
+ return "1.5.5-0.canary.9978";
21040
21261
  }
21041
21262
  }, {
21042
21263
  key: "Events",