hls.js 1.5.6 → 1.5.7-0.canary.10014

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 (68) 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 +2074 -1165
  5. package/dist/hls.js.d.ts +65 -50
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1147 -858
  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 +983 -695
  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 +1756 -862
  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 +21 -21
  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 +20 -8
  25. package/src/controller/base-stream-controller.ts +149 -33
  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 +27 -6
  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 +12 -18
  36. package/src/controller/stream-controller.ts +24 -31
  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/loader/playlist-loader.ts +4 -5
  60. package/src/remux/mp4-generator.ts +196 -1
  61. package/src/remux/mp4-remuxer.ts +23 -7
  62. package/src/task-loop.ts +5 -2
  63. package/src/types/component-api.ts +2 -0
  64. package/src/types/demuxer.ts +3 -0
  65. package/src/types/events.ts +4 -0
  66. package/src/utils/codecs.ts +33 -4
  67. package/src/utils/encryption-methods-util.ts +21 -0
  68. package/src/utils/logger.ts +54 -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,61 +466,6 @@
476
466
  return ErrorDetails;
477
467
  }({});
478
468
 
479
- var noop = function noop() {};
480
- var fakeLogger = {
481
- trace: noop,
482
- debug: noop,
483
- log: noop,
484
- warn: noop,
485
- info: noop,
486
- error: noop
487
- };
488
- var exportedLogger = fakeLogger;
489
-
490
- // let lastCallTime;
491
- // function formatMsgWithTimeInfo(type, msg) {
492
- // const now = Date.now();
493
- // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
494
- // lastCallTime = now;
495
- // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
496
- // return msg;
497
- // }
498
-
499
- function consolePrintFn(type) {
500
- var func = self.console[type];
501
- if (func) {
502
- return func.bind(self.console, "[" + type + "] >");
503
- }
504
- return noop;
505
- }
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
- });
513
- }
514
- function enableLogs(debugConfig, id) {
515
- // check that console is available
516
- if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') {
517
- exportLoggerFunctions(debugConfig,
518
- // Remove out from list here to hard-disable a log-level
519
- // 'trace',
520
- 'debug', 'log', 'info', 'warn', 'error');
521
- // Some browsers don't allow to use bind on console object anyway
522
- // fallback to default if needed
523
- try {
524
- exportedLogger.log("Debug logs enabled for \"" + id + "\" in hls.js version " + "1.5.6");
525
- } catch (e) {
526
- exportedLogger = fakeLogger;
527
- }
528
- } else {
529
- exportedLogger = fakeLogger;
530
- }
531
- }
532
- var logger = exportedLogger;
533
-
534
469
  var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
535
470
  var ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g;
536
471
 
@@ -619,6 +554,77 @@
619
554
  return AttrList;
620
555
  }();
621
556
 
557
+ var Logger = function Logger(label, logger) {
558
+ this.trace = void 0;
559
+ this.debug = void 0;
560
+ this.log = void 0;
561
+ this.warn = void 0;
562
+ this.info = void 0;
563
+ this.error = void 0;
564
+ var lb = "[" + label + "]:";
565
+ this.trace = noop;
566
+ this.debug = logger.debug.bind(null, lb);
567
+ this.log = logger.log.bind(null, lb);
568
+ this.warn = logger.warn.bind(null, lb);
569
+ this.info = logger.info.bind(null, lb);
570
+ this.error = logger.error.bind(null, lb);
571
+ };
572
+ var noop = function noop() {};
573
+ var fakeLogger = {
574
+ trace: noop,
575
+ debug: noop,
576
+ log: noop,
577
+ warn: noop,
578
+ info: noop,
579
+ error: noop
580
+ };
581
+ function createLogger() {
582
+ return _extends({}, fakeLogger);
583
+ }
584
+
585
+ // let lastCallTime;
586
+ // function formatMsgWithTimeInfo(type, msg) {
587
+ // const now = Date.now();
588
+ // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
589
+ // lastCallTime = now;
590
+ // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
591
+ // return msg;
592
+ // }
593
+
594
+ function consolePrintFn(type, id) {
595
+ var func = self.console[type];
596
+ return func ? func.bind(self.console, (id ? '[' + id + '] ' : '') + "[" + type + "] >") : noop;
597
+ }
598
+ function getLoggerFn(key, debugConfig, id) {
599
+ return debugConfig[key] ? debugConfig[key].bind(debugConfig) : consolePrintFn(key, id);
600
+ }
601
+ var exportedLogger = createLogger();
602
+ function enableLogs(debugConfig, context, id) {
603
+ // check that console is available
604
+ var newLogger = createLogger();
605
+ if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') {
606
+ var keys = [
607
+ // Remove out from list here to hard-disable a log-level
608
+ // 'trace',
609
+ 'debug', 'log', 'info', 'warn', 'error'];
610
+ keys.forEach(function (key) {
611
+ newLogger[key] = getLoggerFn(key, debugConfig, id);
612
+ });
613
+ // Some browsers don't allow to use bind on console object anyway
614
+ // fallback to default if needed
615
+ try {
616
+ newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.5.7-0.canary.10014");
617
+ } catch (e) {
618
+ /* log fn threw an exception. All logger methods are no-ops. */
619
+ return createLogger();
620
+ }
621
+ }
622
+ // global exported logger uses the log methods from last call to `enableLogs`
623
+ _extends(exportedLogger, newLogger);
624
+ return newLogger;
625
+ }
626
+ var logger = exportedLogger;
627
+
622
628
  // Avoid exporting const enum so that these values can be inlined
623
629
 
624
630
  function isDateRangeCueAttribute(attrName) {
@@ -1173,10 +1179,30 @@
1173
1179
  return LevelDetails;
1174
1180
  }();
1175
1181
 
1182
+ var DecrypterAesMode = {
1183
+ cbc: 0,
1184
+ ctr: 1
1185
+ };
1186
+
1187
+ function isFullSegmentEncryption(method) {
1188
+ return method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR';
1189
+ }
1190
+ function getAesModeFromFullSegmentMethod(method) {
1191
+ switch (method) {
1192
+ case 'AES-128':
1193
+ case 'AES-256':
1194
+ return DecrypterAesMode.cbc;
1195
+ case 'AES-256-CTR':
1196
+ return DecrypterAesMode.ctr;
1197
+ default:
1198
+ throw new Error("invalid full segment method " + method);
1199
+ }
1200
+ }
1201
+
1176
1202
  // This file is inserted as a shim for modules which we do not want to include into the distro.
1177
1203
  // This replacement is done in the "alias" plugin of the rollup config.
1178
1204
  var empty = undefined;
1179
- var Cues = /*@__PURE__*/getDefaultExportFromCjs(empty);
1205
+ var HevcVideoParser = /*@__PURE__*/getDefaultExportFromCjs(empty);
1180
1206
 
1181
1207
  function sliceUint8(array, start, end) {
1182
1208
  // @ts-expect-error This polyfills IE11 usage of Uint8Array slice.
@@ -2628,13 +2654,13 @@
2628
2654
  this.keyFormatVersions = formatversions;
2629
2655
  this.iv = iv;
2630
2656
  this.encrypted = method ? method !== 'NONE' : false;
2631
- this.isCommonEncryption = this.encrypted && method !== 'AES-128';
2657
+ this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method);
2632
2658
  }
2633
2659
  var _proto = LevelKey.prototype;
2634
2660
  _proto.isSupported = function isSupported() {
2635
2661
  // If it's Segment encryption or No encryption, just select that key system
2636
2662
  if (this.method) {
2637
- if (this.method === 'AES-128' || this.method === 'NONE') {
2663
+ if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
2638
2664
  return true;
2639
2665
  }
2640
2666
  if (this.keyFormat === 'identity') {
@@ -2648,14 +2674,13 @@
2648
2674
  if (!this.encrypted || !this.uri) {
2649
2675
  return null;
2650
2676
  }
2651
- if (this.method === 'AES-128' && this.uri && !this.iv) {
2677
+ if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
2652
2678
  if (typeof sn !== 'number') {
2653
2679
  // We are fetching decryption data for a initialization segment
2654
- // If the segment was encrypted with AES-128
2680
+ // If the segment was encrypted with AES-128/256
2655
2681
  // It must have an IV defined. We cannot substitute the Segment Number in.
2656
- if (this.method === 'AES-128' && !this.iv) {
2657
- logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
2658
- }
2682
+ logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
2683
+
2659
2684
  // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
2660
2685
  sn = 0;
2661
2686
  }
@@ -2817,23 +2842,28 @@
2817
2842
  if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
2818
2843
  return CODEC_COMPATIBLE_NAMES[lowerCaseCodec];
2819
2844
  }
2820
-
2821
- // Idealy fLaC and Opus would be first (spec-compliant) but
2822
- // some browsers will report that fLaC is supported then fail.
2823
- // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
2824
2845
  var codecsToCheck = {
2846
+ // Idealy fLaC and Opus would be first (spec-compliant) but
2847
+ // some browsers will report that fLaC is supported then fail.
2848
+ // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
2825
2849
  flac: ['flac', 'fLaC', 'FLAC'],
2826
- opus: ['opus', 'Opus']
2850
+ opus: ['opus', 'Opus'],
2851
+ // Replace audio codec info if browser does not support mp4a.40.34,
2852
+ // and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
2853
+ 'mp4a.40.34': ['mp3']
2827
2854
  }[lowerCaseCodec];
2828
2855
  for (var i = 0; i < codecsToCheck.length; i++) {
2856
+ var _getMediaSource;
2829
2857
  if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) {
2830
2858
  CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
2831
2859
  return codecsToCheck[i];
2860
+ } else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) {
2861
+ return '';
2832
2862
  }
2833
2863
  }
2834
2864
  return lowerCaseCodec;
2835
2865
  }
2836
- var AUDIO_CODEC_REGEXP = /flac|opus/i;
2866
+ var AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
2837
2867
  function getCodecCompatibleName(codec, preferManagedMediaSource) {
2838
2868
  if (preferManagedMediaSource === void 0) {
2839
2869
  preferManagedMediaSource = true;
@@ -2861,6 +2891,18 @@
2861
2891
  }
2862
2892
  return codec;
2863
2893
  }
2894
+ function getM2TSSupportedAudioTypes(preferManagedMediaSource) {
2895
+ var MediaSource = getMediaSource(preferManagedMediaSource) || {
2896
+ isTypeSupported: function isTypeSupported() {
2897
+ return false;
2898
+ }
2899
+ };
2900
+ return {
2901
+ mpeg: MediaSource.isTypeSupported('audio/mpeg'),
2902
+ mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
2903
+ ac3: false
2904
+ };
2905
+ }
2864
2906
 
2865
2907
  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;
2866
2908
  var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
@@ -3661,10 +3703,10 @@
3661
3703
  var loaderContext = loader.context;
3662
3704
  if (loaderContext && loaderContext.url === context.url && loaderContext.level === context.level) {
3663
3705
  // same URL can't overlap
3664
- logger.trace('[playlist-loader]: playlist request ongoing');
3706
+ this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
3665
3707
  return;
3666
3708
  }
3667
- logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
3709
+ this.hls.logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
3668
3710
  loader.abort();
3669
3711
  }
3670
3712
 
@@ -3774,7 +3816,7 @@
3774
3816
  // alt audio rendition in which quality levels (main)
3775
3817
  // contains both audio+video. but with mixed audio track not signaled
3776
3818
  if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
3777
- logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
3819
+ this.hls.logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
3778
3820
  audioTracks.unshift({
3779
3821
  type: 'main',
3780
3822
  name: 'main',
@@ -3874,7 +3916,7 @@
3874
3916
  message += " id: " + context.id + " group-id: \"" + context.groupId + "\"";
3875
3917
  }
3876
3918
  var error = new Error(message);
3877
- logger.warn("[playlist-loader]: " + message);
3919
+ this.hls.logger.warn("[playlist-loader]: " + message);
3878
3920
  var details = ErrorDetails.UNKNOWN;
3879
3921
  var fatal = false;
3880
3922
  var loader = this.getInternalLoader(context);
@@ -4435,8 +4477,43 @@
4435
4477
  this.currentTime = 0;
4436
4478
  this.stallCount = 0;
4437
4479
  this._latency = null;
4438
- this.timeupdateHandler = function () {
4439
- return _this.timeupdate();
4480
+ this.onTimeupdate = function () {
4481
+ var media = _this.media,
4482
+ levelDetails = _this.levelDetails;
4483
+ if (!media || !levelDetails) {
4484
+ return;
4485
+ }
4486
+ _this.currentTime = media.currentTime;
4487
+ var latency = _this.computeLatency();
4488
+ if (latency === null) {
4489
+ return;
4490
+ }
4491
+ _this._latency = latency;
4492
+
4493
+ // Adapt playbackRate to meet target latency in low-latency mode
4494
+ var _this$config = _this.config,
4495
+ lowLatencyMode = _this$config.lowLatencyMode,
4496
+ maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
4497
+ if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4498
+ return;
4499
+ }
4500
+ var targetLatency = _this.targetLatency;
4501
+ if (targetLatency === null) {
4502
+ return;
4503
+ }
4504
+ var distanceFromTarget = latency - targetLatency;
4505
+ // Only adjust playbackRate when within one target duration of targetLatency
4506
+ // and more than one second from under-buffering.
4507
+ // Playback further than one target duration from target can be considered DVR playback.
4508
+ var liveMinLatencyDuration = Math.min(_this.maxLatency, targetLatency + levelDetails.targetduration);
4509
+ var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4510
+ if (inLiveRange && distanceFromTarget > 0.05 && _this.forwardBufferLength > 1) {
4511
+ var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4512
+ var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - _this.edgeStalled)) * 20) / 20;
4513
+ media.playbackRate = Math.min(max, Math.max(1, rate));
4514
+ } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4515
+ media.playbackRate = 1;
4516
+ }
4440
4517
  };
4441
4518
  this.hls = hls;
4442
4519
  this.config = hls.config;
@@ -4448,7 +4525,7 @@
4448
4525
  this.onMediaDetaching();
4449
4526
  this.levelDetails = null;
4450
4527
  // @ts-ignore
4451
- this.hls = this.timeupdateHandler = null;
4528
+ this.hls = null;
4452
4529
  };
4453
4530
  _proto.registerListeners = function registerListeners() {
4454
4531
  this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
@@ -4466,11 +4543,11 @@
4466
4543
  };
4467
4544
  _proto.onMediaAttached = function onMediaAttached(event, data) {
4468
4545
  this.media = data.media;
4469
- this.media.addEventListener('timeupdate', this.timeupdateHandler);
4546
+ this.media.addEventListener('timeupdate', this.onTimeupdate);
4470
4547
  };
4471
4548
  _proto.onMediaDetaching = function onMediaDetaching() {
4472
4549
  if (this.media) {
4473
- this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4550
+ this.media.removeEventListener('timeupdate', this.onTimeupdate);
4474
4551
  this.media = null;
4475
4552
  }
4476
4553
  };
@@ -4483,10 +4560,10 @@
4483
4560
  var details = _ref.details;
4484
4561
  this.levelDetails = details;
4485
4562
  if (details.advanced) {
4486
- this.timeupdate();
4563
+ this.onTimeupdate();
4487
4564
  }
4488
4565
  if (!details.live && this.media) {
4489
- this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4566
+ this.media.removeEventListener('timeupdate', this.onTimeupdate);
4490
4567
  }
4491
4568
  };
4492
4569
  _proto.onError = function onError(event, data) {
@@ -4496,45 +4573,7 @@
4496
4573
  }
4497
4574
  this.stallCount++;
4498
4575
  if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
4499
- logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency');
4500
- }
4501
- };
4502
- _proto.timeupdate = function timeupdate() {
4503
- var media = this.media,
4504
- levelDetails = this.levelDetails;
4505
- if (!media || !levelDetails) {
4506
- return;
4507
- }
4508
- this.currentTime = media.currentTime;
4509
- var latency = this.computeLatency();
4510
- if (latency === null) {
4511
- return;
4512
- }
4513
- this._latency = latency;
4514
-
4515
- // Adapt playbackRate to meet target latency in low-latency mode
4516
- var _this$config = this.config,
4517
- lowLatencyMode = _this$config.lowLatencyMode,
4518
- maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
4519
- if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4520
- return;
4521
- }
4522
- var targetLatency = this.targetLatency;
4523
- if (targetLatency === null) {
4524
- return;
4525
- }
4526
- var distanceFromTarget = latency - targetLatency;
4527
- // Only adjust playbackRate when within one target duration of targetLatency
4528
- // and more than one second from under-buffering.
4529
- // Playback further than one target duration from target can be considered DVR playback.
4530
- var liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
4531
- var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4532
- if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
4533
- var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4534
- var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
4535
- media.playbackRate = Math.min(max, Math.max(1, rate));
4536
- } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4537
- media.playbackRate = 1;
4576
+ this.hls.logger.warn('[latency-controller]: Stall detected, adjusting target latency');
4538
4577
  }
4539
4578
  };
4540
4579
  _proto.estimateLiveEdge = function estimateLiveEdge() {
@@ -5442,19 +5481,17 @@
5442
5481
  MoveAllAlternatesMatchingHDCP: 2,
5443
5482
  SwitchToSDR: 4
5444
5483
  }; // Reserved for future use
5445
- var ErrorController = /*#__PURE__*/function () {
5484
+ var ErrorController = /*#__PURE__*/function (_Logger) {
5485
+ _inheritsLoose(ErrorController, _Logger);
5446
5486
  function ErrorController(hls) {
5447
- this.hls = void 0;
5448
- this.playlistError = 0;
5449
- this.penalizedRenditions = {};
5450
- this.log = void 0;
5451
- this.warn = void 0;
5452
- this.error = void 0;
5453
- this.hls = hls;
5454
- this.log = logger.log.bind(logger, "[info]:");
5455
- this.warn = logger.warn.bind(logger, "[warning]:");
5456
- this.error = logger.error.bind(logger, "[error]:");
5457
- this.registerListeners();
5487
+ var _this;
5488
+ _this = _Logger.call(this, 'error-controller', hls.logger) || this;
5489
+ _this.hls = void 0;
5490
+ _this.playlistError = 0;
5491
+ _this.penalizedRenditions = {};
5492
+ _this.hls = hls;
5493
+ _this.registerListeners();
5494
+ return _this;
5458
5495
  }
5459
5496
  var _proto = ErrorController.prototype;
5460
5497
  _proto.registerListeners = function registerListeners() {
@@ -5810,19 +5847,19 @@
5810
5847
  }
5811
5848
  };
5812
5849
  return ErrorController;
5813
- }();
5850
+ }(Logger);
5814
5851
 
5815
- var BasePlaylistController = /*#__PURE__*/function () {
5852
+ var BasePlaylistController = /*#__PURE__*/function (_Logger) {
5853
+ _inheritsLoose(BasePlaylistController, _Logger);
5816
5854
  function BasePlaylistController(hls, logPrefix) {
5817
- this.hls = void 0;
5818
- this.timer = -1;
5819
- this.requestScheduled = -1;
5820
- this.canLoad = false;
5821
- this.log = void 0;
5822
- this.warn = void 0;
5823
- this.log = logger.log.bind(logger, logPrefix + ":");
5824
- this.warn = logger.warn.bind(logger, logPrefix + ":");
5825
- this.hls = hls;
5855
+ var _this;
5856
+ _this = _Logger.call(this, logPrefix, hls.logger) || this;
5857
+ _this.hls = void 0;
5858
+ _this.timer = -1;
5859
+ _this.requestScheduled = -1;
5860
+ _this.canLoad = false;
5861
+ _this.hls = hls;
5862
+ return _this;
5826
5863
  }
5827
5864
  var _proto = BasePlaylistController.prototype;
5828
5865
  _proto.destroy = function destroy() {
@@ -5855,7 +5892,7 @@
5855
5892
  try {
5856
5893
  uri = new self.URL(attr.URI, previous.url).href;
5857
5894
  } catch (error) {
5858
- logger.warn("Could not construct new URL for Rendition Report: " + error);
5895
+ this.warn("Could not construct new URL for Rendition Report: " + error);
5859
5896
  uri = attr.URI || '';
5860
5897
  }
5861
5898
  // Use exact match. Otherwise, the last partial match, if any, will be used
@@ -5894,7 +5931,7 @@
5894
5931
  return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist);
5895
5932
  };
5896
5933
  _proto.playlistLoaded = function playlistLoaded(index, data, previousDetails) {
5897
- var _this = this;
5934
+ var _this2 = this;
5898
5935
  var details = data.details,
5899
5936
  stats = data.stats;
5900
5937
 
@@ -5941,7 +5978,12 @@
5941
5978
  var cdnAge = lastAdvanced + details.ageHeader;
5942
5979
  var currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5);
5943
5980
  if (currentGoal > 0) {
5944
- if (previousDetails && currentGoal > previousDetails.tuneInGoal) {
5981
+ if (cdnAge > details.targetduration * 3) {
5982
+ // Omit segment and part directives when the last response was more than 3 target durations ago,
5983
+ this.log("Playlist last advanced " + lastAdvanced.toFixed(2) + "s ago. Omitting segment and part directives.");
5984
+ msn = undefined;
5985
+ part = undefined;
5986
+ } else if (previousDetails != null && previousDetails.tuneInGoal && cdnAge - details.partTarget > previousDetails.tuneInGoal) {
5945
5987
  // If we attempted to get the next or latest playlist update, but currentGoal increased,
5946
5988
  // then we either can't catchup, or the "age" header cannot be trusted.
5947
5989
  this.warn("CDN Tune-in goal increased from: " + previousDetails.tuneInGoal + " to: " + currentGoal + " with playlist age: " + details.age);
@@ -5999,7 +6041,7 @@
5999
6041
  // );
6000
6042
 
6001
6043
  this.timer = self.setTimeout(function () {
6002
- return _this.loadPlaylist(deliveryDirectives);
6044
+ return _this2.loadPlaylist(deliveryDirectives);
6003
6045
  }, estimatedTimeUntilUpdate);
6004
6046
  } else {
6005
6047
  this.clearTimer();
@@ -6015,7 +6057,7 @@
6015
6057
  return new HlsUrlParameters(msn, part, skip);
6016
6058
  };
6017
6059
  _proto.checkRetry = function checkRetry(errorEvent) {
6018
- var _this2 = this;
6060
+ var _this3 = this;
6019
6061
  var errorDetails = errorEvent.details;
6020
6062
  var isTimeout = isTimeoutError(errorEvent);
6021
6063
  var errorAction = errorEvent.errorAction;
@@ -6039,7 +6081,7 @@
6039
6081
  var delay = getRetryDelay(retryConfig, retryCount);
6040
6082
  // Schedule level/track reload
6041
6083
  this.timer = self.setTimeout(function () {
6042
- return _this2.loadPlaylist();
6084
+ return _this3.loadPlaylist();
6043
6085
  }, delay);
6044
6086
  this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" in " + delay + "ms");
6045
6087
  }
@@ -6050,7 +6092,7 @@
6050
6092
  return retry;
6051
6093
  };
6052
6094
  return BasePlaylistController;
6053
- }();
6095
+ }(Logger);
6054
6096
 
6055
6097
  /*
6056
6098
  * compute an Exponential Weighted moving average
@@ -6424,30 +6466,33 @@
6424
6466
  }, {});
6425
6467
  }
6426
6468
 
6427
- var AbrController = /*#__PURE__*/function () {
6469
+ var AbrController = /*#__PURE__*/function (_Logger) {
6470
+ _inheritsLoose(AbrController, _Logger);
6428
6471
  function AbrController(_hls) {
6429
- var _this = this;
6430
- this.hls = void 0;
6431
- this.lastLevelLoadSec = 0;
6432
- this.lastLoadedFragLevel = -1;
6433
- this.firstSelection = -1;
6434
- this._nextAutoLevel = -1;
6435
- this.nextAutoLevelKey = '';
6436
- this.audioTracksByGroup = null;
6437
- this.codecTiers = null;
6438
- this.timer = -1;
6439
- this.fragCurrent = null;
6440
- this.partCurrent = null;
6441
- this.bitrateTestDelay = 0;
6442
- this.bwEstimator = void 0;
6472
+ var _this;
6473
+ _this = _Logger.call(this, 'abr', _hls.logger) || this;
6474
+ _this.hls = void 0;
6475
+ _this.lastLevelLoadSec = 0;
6476
+ _this.lastLoadedFragLevel = -1;
6477
+ _this.firstSelection = -1;
6478
+ _this._nextAutoLevel = -1;
6479
+ _this.nextAutoLevelKey = '';
6480
+ _this.audioTracksByGroup = null;
6481
+ _this.codecTiers = null;
6482
+ _this.timer = -1;
6483
+ _this.fragCurrent = null;
6484
+ _this.partCurrent = null;
6485
+ _this.bitrateTestDelay = 0;
6486
+ _this.bwEstimator = void 0;
6443
6487
  /*
6444
6488
  This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
6445
6489
  quickly enough to prevent underbuffering
6446
6490
  */
6447
- this._abandonRulesCheck = function () {
6448
- var frag = _this.fragCurrent,
6449
- part = _this.partCurrent,
6450
- hls = _this.hls;
6491
+ _this._abandonRulesCheck = function () {
6492
+ var _assertThisInitialize = _assertThisInitialized(_this),
6493
+ frag = _assertThisInitialize.fragCurrent,
6494
+ part = _assertThisInitialize.partCurrent,
6495
+ hls = _assertThisInitialize.hls;
6451
6496
  var autoLevelEnabled = hls.autoLevelEnabled,
6452
6497
  media = hls.media;
6453
6498
  if (!frag || !media) {
@@ -6536,21 +6581,22 @@
6536
6581
  _this.resetEstimator(nextLoadLevelBitrate);
6537
6582
  }
6538
6583
  _this.clearTimer();
6539
- 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");
6584
+ _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");
6540
6585
  hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, {
6541
6586
  frag: frag,
6542
6587
  part: part,
6543
6588
  stats: stats
6544
6589
  });
6545
6590
  };
6546
- this.hls = _hls;
6547
- this.bwEstimator = this.initEstimator();
6548
- this.registerListeners();
6591
+ _this.hls = _hls;
6592
+ _this.bwEstimator = _this.initEstimator();
6593
+ _this.registerListeners();
6594
+ return _this;
6549
6595
  }
6550
6596
  var _proto = AbrController.prototype;
6551
6597
  _proto.resetEstimator = function resetEstimator(abrEwmaDefaultEstimate) {
6552
6598
  if (abrEwmaDefaultEstimate) {
6553
- logger.log("setting initial bwe to " + abrEwmaDefaultEstimate);
6599
+ this.log("setting initial bwe to " + abrEwmaDefaultEstimate);
6554
6600
  this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
6555
6601
  }
6556
6602
  this.firstSelection = -1;
@@ -6795,13 +6841,13 @@
6795
6841
  // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
6796
6842
  var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
6797
6843
  maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
6798
- logger.info("[abr] bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6844
+ this.info("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6799
6845
  // don't use conservative factor on bitrate test
6800
6846
  bwFactor = bwUpFactor = 1;
6801
6847
  }
6802
6848
  }
6803
6849
  var bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor);
6804
- logger.info("[abr] " + (bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6850
+ this.info((bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6805
6851
  if (bestLevel > -1) {
6806
6852
  return bestLevel;
6807
6853
  }
@@ -6869,7 +6915,7 @@
6869
6915
  currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
6870
6916
  currentFrameRate = minFramerate;
6871
6917
  currentBw = Math.max(currentBw, minBitrate);
6872
- logger.log("[abr] picked start tier " + JSON.stringify(startTier));
6918
+ this.log("picked start tier " + JSON.stringify(startTier));
6873
6919
  } else {
6874
6920
  currentCodecSet = level == null ? void 0 : level.codecSet;
6875
6921
  currentVideoRange = level == null ? void 0 : level.videoRange;
@@ -6922,9 +6968,9 @@
6922
6968
  var forcedAutoLevel = _this2.forcedAutoLevel;
6923
6969
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
6924
6970
  if (levelsSkipped.length) {
6925
- 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);
6971
+ _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);
6926
6972
  }
6927
- 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);
6973
+ _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);
6928
6974
  }
6929
6975
  if (firstSelection) {
6930
6976
  _this2.firstSelection = i;
@@ -6958,7 +7004,7 @@
6958
7004
  }
6959
7005
  var firstLevel = this.hls.firstLevel;
6960
7006
  var clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
6961
- logger.warn("[abr] Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
7007
+ this.warn("Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
6962
7008
  return clamped;
6963
7009
  }
6964
7010
  }, {
@@ -7011,7 +7057,7 @@
7011
7057
  }
7012
7058
  }]);
7013
7059
  return AbrController;
7014
- }();
7060
+ }(Logger);
7015
7061
 
7016
7062
  /**
7017
7063
  * Provides methods dealing with buffer length retrieval for example.
@@ -7232,57 +7278,57 @@
7232
7278
  }();
7233
7279
 
7234
7280
  var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
7235
- var BufferController = /*#__PURE__*/function () {
7281
+ var BufferController = /*#__PURE__*/function (_Logger) {
7282
+ _inheritsLoose(BufferController, _Logger);
7236
7283
  function BufferController(hls) {
7237
- var _this = this;
7284
+ var _this;
7285
+ _this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
7238
7286
  // The level details used to determine duration, target-duration and live
7239
- this.details = null;
7287
+ _this.details = null;
7240
7288
  // cache the self generated object url to detect hijack of video tag
7241
- this._objectUrl = null;
7289
+ _this._objectUrl = null;
7242
7290
  // A queue of buffer operations which require the SourceBuffer to not be updating upon execution
7243
- this.operationQueue = void 0;
7291
+ _this.operationQueue = void 0;
7244
7292
  // References to event listeners for each SourceBuffer, so that they can be referenced for event removal
7245
- this.listeners = void 0;
7246
- this.hls = void 0;
7293
+ _this.listeners = void 0;
7294
+ _this.hls = void 0;
7247
7295
  // The number of BUFFER_CODEC events received before any sourceBuffers are created
7248
- this.bufferCodecEventsExpected = 0;
7296
+ _this.bufferCodecEventsExpected = 0;
7249
7297
  // The total number of BUFFER_CODEC events received
7250
- this._bufferCodecEventsTotal = 0;
7298
+ _this._bufferCodecEventsTotal = 0;
7251
7299
  // A reference to the attached media element
7252
- this.media = null;
7300
+ _this.media = null;
7253
7301
  // A reference to the active media source
7254
- this.mediaSource = null;
7302
+ _this.mediaSource = null;
7255
7303
  // Last MP3 audio chunk appended
7256
- this.lastMpegAudioChunk = null;
7257
- this.appendSource = void 0;
7304
+ _this.lastMpegAudioChunk = null;
7305
+ _this.appendSource = void 0;
7258
7306
  // counters
7259
- this.appendErrors = {
7307
+ _this.appendErrors = {
7260
7308
  audio: 0,
7261
7309
  video: 0,
7262
7310
  audiovideo: 0
7263
7311
  };
7264
- this.tracks = {};
7265
- this.pendingTracks = {};
7266
- this.sourceBuffer = void 0;
7267
- this.log = void 0;
7268
- this.warn = void 0;
7269
- this.error = void 0;
7270
- this._onEndStreaming = function (event) {
7312
+ _this.tracks = {};
7313
+ _this.pendingTracks = {};
7314
+ _this.sourceBuffer = void 0;
7315
+ _this._onEndStreaming = function (event) {
7271
7316
  if (!_this.hls) {
7272
7317
  return;
7273
7318
  }
7274
7319
  _this.hls.pauseBuffering();
7275
7320
  };
7276
- this._onStartStreaming = function (event) {
7321
+ _this._onStartStreaming = function (event) {
7277
7322
  if (!_this.hls) {
7278
7323
  return;
7279
7324
  }
7280
7325
  _this.hls.resumeBuffering();
7281
7326
  };
7282
7327
  // Keep as arrow functions so that we can directly reference these functions directly as event listeners
7283
- this._onMediaSourceOpen = function () {
7284
- var media = _this.media,
7285
- mediaSource = _this.mediaSource;
7328
+ _this._onMediaSourceOpen = function () {
7329
+ var _assertThisInitialize = _assertThisInitialized(_this),
7330
+ media = _assertThisInitialize.media,
7331
+ mediaSource = _assertThisInitialize.mediaSource;
7286
7332
  _this.log('Media source opened');
7287
7333
  if (media) {
7288
7334
  media.removeEventListener('emptied', _this._onMediaEmptied);
@@ -7298,27 +7344,25 @@
7298
7344
  }
7299
7345
  _this.checkPendingTracks();
7300
7346
  };
7301
- this._onMediaSourceClose = function () {
7347
+ _this._onMediaSourceClose = function () {
7302
7348
  _this.log('Media source closed');
7303
7349
  };
7304
- this._onMediaSourceEnded = function () {
7350
+ _this._onMediaSourceEnded = function () {
7305
7351
  _this.log('Media source ended');
7306
7352
  };
7307
- this._onMediaEmptied = function () {
7308
- var mediaSrc = _this.mediaSrc,
7309
- _objectUrl = _this._objectUrl;
7353
+ _this._onMediaEmptied = function () {
7354
+ var _assertThisInitialize2 = _assertThisInitialized(_this),
7355
+ mediaSrc = _assertThisInitialize2.mediaSrc,
7356
+ _objectUrl = _assertThisInitialize2._objectUrl;
7310
7357
  if (mediaSrc !== _objectUrl) {
7311
- logger.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7358
+ _this.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7312
7359
  }
7313
7360
  };
7314
- this.hls = hls;
7315
- var logPrefix = '[buffer-controller]';
7316
- this.appendSource = hls.config.preferManagedMediaSource;
7317
- this.log = logger.log.bind(logger, logPrefix);
7318
- this.warn = logger.warn.bind(logger, logPrefix);
7319
- this.error = logger.error.bind(logger, logPrefix);
7320
- this._initSourceBuffer();
7321
- this.registerListeners();
7361
+ _this.hls = hls;
7362
+ _this.appendSource = hls.config.preferManagedMediaSource;
7363
+ _this._initSourceBuffer();
7364
+ _this.registerListeners();
7365
+ return _this;
7322
7366
  }
7323
7367
  var _proto = BufferController.prototype;
7324
7368
  _proto.hasSourceTypes = function hasSourceTypes() {
@@ -7330,6 +7374,12 @@
7330
7374
  this.lastMpegAudioChunk = null;
7331
7375
  // @ts-ignore
7332
7376
  this.hls = null;
7377
+ // @ts-ignore
7378
+ this._onMediaSourceOpen = this._onMediaSourceClose = null;
7379
+ // @ts-ignore
7380
+ this._onMediaSourceEnded = null;
7381
+ // @ts-ignore
7382
+ this._onStartStreaming = this._onEndStreaming = null;
7333
7383
  };
7334
7384
  _proto.registerListeners = function registerListeners() {
7335
7385
  var hls = this.hls;
@@ -7487,6 +7537,7 @@
7487
7537
  _this2.resetBuffer(type);
7488
7538
  });
7489
7539
  this._initSourceBuffer();
7540
+ this.hls.resumeBuffering();
7490
7541
  };
7491
7542
  _proto.resetBuffer = function resetBuffer(type) {
7492
7543
  var sb = this.sourceBuffer[type];
@@ -8190,7 +8241,7 @@
8190
8241
  }
8191
8242
  }]);
8192
8243
  return BufferController;
8193
- }();
8244
+ }(Logger);
8194
8245
  function removeSourceChildren(node) {
8195
8246
  var sourceChildren = node.querySelectorAll('source');
8196
8247
  [].slice.call(sourceChildren).forEach(function (source) {
@@ -8314,7 +8365,7 @@
8314
8365
  var hls = this.hls;
8315
8366
  var maxLevel = this.getMaxLevel(levels.length - 1);
8316
8367
  if (maxLevel !== this.autoLevelCapping) {
8317
- logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8368
+ hls.logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8318
8369
  }
8319
8370
  hls.autoLevelCapping = maxLevel;
8320
8371
  if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
@@ -8504,10 +8555,10 @@
8504
8555
  totalDroppedFrames: droppedFrames
8505
8556
  });
8506
8557
  if (droppedFPS > 0) {
8507
- // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8558
+ // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8508
8559
  if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
8509
8560
  var currentLevel = hls.currentLevel;
8510
- logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8561
+ hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8511
8562
  if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
8512
8563
  currentLevel = currentLevel - 1;
8513
8564
  hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
@@ -8541,26 +8592,28 @@
8541
8592
  }();
8542
8593
 
8543
8594
  var PATHWAY_PENALTY_DURATION_MS = 300000;
8544
- var ContentSteeringController = /*#__PURE__*/function () {
8595
+ var ContentSteeringController = /*#__PURE__*/function (_Logger) {
8596
+ _inheritsLoose(ContentSteeringController, _Logger);
8545
8597
  function ContentSteeringController(hls) {
8546
- this.hls = void 0;
8547
- this.log = void 0;
8548
- this.loader = null;
8549
- this.uri = null;
8550
- this.pathwayId = '.';
8551
- this.pathwayPriority = null;
8552
- this.timeToLoad = 300;
8553
- this.reloadTimer = -1;
8554
- this.updated = 0;
8555
- this.started = false;
8556
- this.enabled = true;
8557
- this.levels = null;
8558
- this.audioTracks = null;
8559
- this.subtitleTracks = null;
8560
- this.penalizedPathways = {};
8561
- this.hls = hls;
8562
- this.log = logger.log.bind(logger, "[content-steering]:");
8563
- this.registerListeners();
8598
+ var _this;
8599
+ _this = _Logger.call(this, 'content-steering', hls.logger) || this;
8600
+ _this.hls = void 0;
8601
+ _this.loader = null;
8602
+ _this.uri = null;
8603
+ _this.pathwayId = '.';
8604
+ _this.pathwayPriority = null;
8605
+ _this.timeToLoad = 300;
8606
+ _this.reloadTimer = -1;
8607
+ _this.updated = 0;
8608
+ _this.started = false;
8609
+ _this.enabled = true;
8610
+ _this.levels = null;
8611
+ _this.audioTracks = null;
8612
+ _this.subtitleTracks = null;
8613
+ _this.penalizedPathways = {};
8614
+ _this.hls = hls;
8615
+ _this.registerListeners();
8616
+ return _this;
8564
8617
  }
8565
8618
  var _proto = ContentSteeringController.prototype;
8566
8619
  _proto.registerListeners = function registerListeners() {
@@ -8681,7 +8734,7 @@
8681
8734
  errorAction.resolved = this.pathwayId !== errorPathway;
8682
8735
  }
8683
8736
  if (!errorAction.resolved) {
8684
- 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));
8737
+ 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));
8685
8738
  }
8686
8739
  }
8687
8740
  };
@@ -8761,7 +8814,7 @@
8761
8814
  return defaultPathway;
8762
8815
  };
8763
8816
  _proto.clonePathways = function clonePathways(pathwayClones) {
8764
- var _this = this;
8817
+ var _this2 = this;
8765
8818
  var levels = this.levels;
8766
8819
  if (!levels) {
8767
8820
  return;
@@ -8777,7 +8830,7 @@
8777
8830
  })) {
8778
8831
  return;
8779
8832
  }
8780
- var clonedVariants = _this.getLevelsForPathway(baseId).map(function (baseLevel) {
8833
+ var clonedVariants = _this2.getLevelsForPathway(baseId).map(function (baseLevel) {
8781
8834
  var attributes = new AttrList(baseLevel.attrs);
8782
8835
  attributes['PATHWAY-ID'] = cloneId;
8783
8836
  var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId;
@@ -8814,12 +8867,12 @@
8814
8867
  return clonedLevel;
8815
8868
  });
8816
8869
  levels.push.apply(levels, clonedVariants);
8817
- cloneRenditionGroups(_this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8818
- cloneRenditionGroups(_this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8870
+ cloneRenditionGroups(_this2.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8871
+ cloneRenditionGroups(_this2.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8819
8872
  });
8820
8873
  };
8821
8874
  _proto.loadSteeringManifest = function loadSteeringManifest(uri) {
8822
- var _this2 = this;
8875
+ var _this3 = this;
8823
8876
  var config = this.hls.config;
8824
8877
  var Loader = config.loader;
8825
8878
  if (this.loader) {
@@ -8854,87 +8907,87 @@
8854
8907
  };
8855
8908
  var callbacks = {
8856
8909
  onSuccess: function onSuccess(response, stats, context, networkDetails) {
8857
- _this2.log("Loaded steering manifest: \"" + url + "\"");
8910
+ _this3.log("Loaded steering manifest: \"" + url + "\"");
8858
8911
  var steeringData = response.data;
8859
- if (steeringData.VERSION !== 1) {
8860
- _this2.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8912
+ if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) {
8913
+ _this3.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8861
8914
  return;
8862
8915
  }
8863
- _this2.updated = performance.now();
8864
- _this2.timeToLoad = steeringData.TTL;
8916
+ _this3.updated = performance.now();
8917
+ _this3.timeToLoad = steeringData.TTL;
8865
8918
  var reloadUri = steeringData['RELOAD-URI'],
8866
8919
  pathwayClones = steeringData['PATHWAY-CLONES'],
8867
8920
  pathwayPriority = steeringData['PATHWAY-PRIORITY'];
8868
8921
  if (reloadUri) {
8869
8922
  try {
8870
- _this2.uri = new self.URL(reloadUri, url).href;
8923
+ _this3.uri = new self.URL(reloadUri, url).href;
8871
8924
  } catch (error) {
8872
- _this2.enabled = false;
8873
- _this2.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8925
+ _this3.enabled = false;
8926
+ _this3.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8874
8927
  return;
8875
8928
  }
8876
8929
  }
8877
- _this2.scheduleRefresh(_this2.uri || context.url);
8930
+ _this3.scheduleRefresh(_this3.uri || context.url);
8878
8931
  if (pathwayClones) {
8879
- _this2.clonePathways(pathwayClones);
8932
+ _this3.clonePathways(pathwayClones);
8880
8933
  }
8881
8934
  var loadedSteeringData = {
8882
8935
  steeringManifest: steeringData,
8883
8936
  url: url.toString()
8884
8937
  };
8885
- _this2.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8938
+ _this3.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8886
8939
  if (pathwayPriority) {
8887
- _this2.updatePathwayPriority(pathwayPriority);
8940
+ _this3.updatePathwayPriority(pathwayPriority);
8888
8941
  }
8889
8942
  },
8890
8943
  onError: function onError(error, context, networkDetails, stats) {
8891
- _this2.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8892
- _this2.stopLoad();
8944
+ _this3.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8945
+ _this3.stopLoad();
8893
8946
  if (error.code === 410) {
8894
- _this2.enabled = false;
8895
- _this2.log("Steering manifest " + context.url + " no longer available");
8947
+ _this3.enabled = false;
8948
+ _this3.log("Steering manifest " + context.url + " no longer available");
8896
8949
  return;
8897
8950
  }
8898
- var ttl = _this2.timeToLoad * 1000;
8951
+ var ttl = _this3.timeToLoad * 1000;
8899
8952
  if (error.code === 429) {
8900
- var loader = _this2.loader;
8953
+ var loader = _this3.loader;
8901
8954
  if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {
8902
8955
  var retryAfter = loader.getResponseHeader('Retry-After');
8903
8956
  if (retryAfter) {
8904
8957
  ttl = parseFloat(retryAfter) * 1000;
8905
8958
  }
8906
8959
  }
8907
- _this2.log("Steering manifest " + context.url + " rate limited");
8960
+ _this3.log("Steering manifest " + context.url + " rate limited");
8908
8961
  return;
8909
8962
  }
8910
- _this2.scheduleRefresh(_this2.uri || context.url, ttl);
8963
+ _this3.scheduleRefresh(_this3.uri || context.url, ttl);
8911
8964
  },
8912
8965
  onTimeout: function onTimeout(stats, context, networkDetails) {
8913
- _this2.log("Timeout loading steering manifest (" + context.url + ")");
8914
- _this2.scheduleRefresh(_this2.uri || context.url);
8966
+ _this3.log("Timeout loading steering manifest (" + context.url + ")");
8967
+ _this3.scheduleRefresh(_this3.uri || context.url);
8915
8968
  }
8916
8969
  };
8917
8970
  this.log("Requesting steering manifest: " + url);
8918
8971
  this.loader.load(context, loaderConfig, callbacks);
8919
8972
  };
8920
8973
  _proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) {
8921
- var _this3 = this;
8974
+ var _this4 = this;
8922
8975
  if (ttlMs === void 0) {
8923
8976
  ttlMs = this.timeToLoad * 1000;
8924
8977
  }
8925
8978
  this.clearTimeout();
8926
8979
  this.reloadTimer = self.setTimeout(function () {
8927
- var _this3$hls;
8928
- var media = (_this3$hls = _this3.hls) == null ? void 0 : _this3$hls.media;
8980
+ var _this4$hls;
8981
+ var media = (_this4$hls = _this4.hls) == null ? void 0 : _this4$hls.media;
8929
8982
  if (media && !media.ended) {
8930
- _this3.loadSteeringManifest(uri);
8983
+ _this4.loadSteeringManifest(uri);
8931
8984
  return;
8932
8985
  }
8933
- _this3.scheduleRefresh(uri, _this3.timeToLoad * 1000);
8986
+ _this4.scheduleRefresh(uri, _this4.timeToLoad * 1000);
8934
8987
  }, ttlMs);
8935
8988
  };
8936
8989
  return ContentSteeringController;
8937
- }();
8990
+ }(Logger);
8938
8991
  function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {
8939
8992
  if (!tracks) {
8940
8993
  return;
@@ -9770,7 +9823,7 @@
9770
9823
  });
9771
9824
  function timelineConfig() {
9772
9825
  return {
9773
- cueHandler: Cues,
9826
+ cueHandler: HevcVideoParser,
9774
9827
  // used by timeline-controller
9775
9828
  enableWebVTT: false,
9776
9829
  // used by timeline-controller
@@ -9801,7 +9854,7 @@
9801
9854
  /**
9802
9855
  * @ignore
9803
9856
  */
9804
- function mergeConfig(defaultConfig, userConfig) {
9857
+ function mergeConfig(defaultConfig, userConfig, logger) {
9805
9858
  if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
9806
9859
  throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
9807
9860
  }
@@ -9871,7 +9924,7 @@
9871
9924
  /**
9872
9925
  * @ignore
9873
9926
  */
9874
- function enableStreamingMode(config) {
9927
+ function enableStreamingMode(config, logger) {
9875
9928
  var currentLoader = config.loader;
9876
9929
  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
9877
9930
  // If a developer has configured their own loader, respect that choice
@@ -9888,12 +9941,11 @@
9888
9941
  }
9889
9942
  }
9890
9943
 
9891
- var chromeOrFirefox;
9892
9944
  var LevelController = /*#__PURE__*/function (_BasePlaylistControll) {
9893
9945
  _inheritsLoose(LevelController, _BasePlaylistControll);
9894
9946
  function LevelController(hls, contentSteeringController) {
9895
9947
  var _this;
9896
- _this = _BasePlaylistControll.call(this, hls, '[level-controller]') || this;
9948
+ _this = _BasePlaylistControll.call(this, hls, 'level-controller') || this;
9897
9949
  _this._levels = [];
9898
9950
  _this._firstLevel = -1;
9899
9951
  _this._maxAutoLevel = -1;
@@ -9962,21 +10014,13 @@
9962
10014
  var videoCodecFound = false;
9963
10015
  var audioCodecFound = false;
9964
10016
  data.levels.forEach(function (levelParsed) {
9965
- var _audioCodec, _videoCodec;
10017
+ var _videoCodec;
9966
10018
  var attributes = levelParsed.attrs;
9967
-
9968
- // erase audio codec info if browser does not support mp4a.40.34.
9969
- // demuxer will autodetect codec and fallback to mpeg/audio
9970
10019
  var audioCodec = levelParsed.audioCodec,
9971
10020
  videoCodec = levelParsed.videoCodec;
9972
- if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) {
9973
- chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));
9974
- if (chromeOrFirefox) {
9975
- levelParsed.audioCodec = audioCodec = undefined;
9976
- }
9977
- }
9978
10021
  if (audioCodec) {
9979
- levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource);
10022
+ // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
10023
+ levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
9980
10024
  }
9981
10025
  if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
9982
10026
  videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
@@ -10208,7 +10252,12 @@
10208
10252
  if (curLevel.fragmentError === 0) {
10209
10253
  curLevel.loadError = 0;
10210
10254
  }
10211
- this.playlistLoaded(level, data, curLevel.details);
10255
+ // Ignore matching details populated by loading a Media Playlist directly
10256
+ var previousDetails = curLevel.details;
10257
+ if (previousDetails === data.details && previousDetails.advanced) {
10258
+ previousDetails = undefined;
10259
+ }
10260
+ this.playlistLoaded(level, data, previousDetails);
10212
10261
  } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
10213
10262
  // received a delta playlist update that cannot be merged
10214
10263
  details.deltaUpdateFailed = true;
@@ -11123,8 +11172,8 @@
11123
11172
  var _frag$decryptdata;
11124
11173
  var byteRangeStart = start;
11125
11174
  var byteRangeEnd = end;
11126
- if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {
11127
- // MAP segment encrypted with method 'AES-128', when served with HTTP Range,
11175
+ if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) {
11176
+ // MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
11128
11177
  // has the unencrypted size specified in the range.
11129
11178
  // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
11130
11179
  var fragmentLen = end - start;
@@ -11157,6 +11206,9 @@
11157
11206
  (part ? part : frag).stats.aborted = true;
11158
11207
  return new LoadError(errorData);
11159
11208
  }
11209
+ function isMethodFullSegmentAesCbc(method) {
11210
+ return method === 'AES-128' || method === 'AES-256';
11211
+ }
11160
11212
  var LoadError = /*#__PURE__*/function (_Error) {
11161
11213
  _inheritsLoose(LoadError, _Error);
11162
11214
  function LoadError(data) {
@@ -11313,6 +11365,8 @@
11313
11365
  }
11314
11366
  return this.loadKeyEME(keyInfo, frag);
11315
11367
  case 'AES-128':
11368
+ case 'AES-256':
11369
+ case 'AES-256-CTR':
11316
11370
  return this.loadKeyHTTP(keyInfo, frag);
11317
11371
  default:
11318
11372
  return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\"")));
@@ -11446,13 +11500,17 @@
11446
11500
  * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
11447
11501
  * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
11448
11502
  */
11449
- var TaskLoop = /*#__PURE__*/function () {
11450
- function TaskLoop() {
11451
- this._boundTick = void 0;
11452
- this._tickTimer = null;
11453
- this._tickInterval = null;
11454
- this._tickCallCount = 0;
11455
- this._boundTick = this.tick.bind(this);
11503
+ var TaskLoop = /*#__PURE__*/function (_Logger) {
11504
+ _inheritsLoose(TaskLoop, _Logger);
11505
+ function TaskLoop(label, logger) {
11506
+ var _this;
11507
+ _this = _Logger.call(this, label, logger) || this;
11508
+ _this._boundTick = void 0;
11509
+ _this._tickTimer = null;
11510
+ _this._tickInterval = null;
11511
+ _this._tickCallCount = 0;
11512
+ _this._boundTick = _this.tick.bind(_assertThisInitialized(_this));
11513
+ return _this;
11456
11514
  }
11457
11515
  var _proto = TaskLoop.prototype;
11458
11516
  _proto.destroy = function destroy() {
@@ -11538,7 +11596,7 @@
11538
11596
  */;
11539
11597
  _proto.doTick = function doTick() {};
11540
11598
  return TaskLoop;
11541
- }();
11599
+ }(Logger);
11542
11600
 
11543
11601
  var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) {
11544
11602
  if (size === void 0) {
@@ -11724,37 +11782,65 @@
11724
11782
  }
11725
11783
 
11726
11784
  var AESCrypto = /*#__PURE__*/function () {
11727
- function AESCrypto(subtle, iv) {
11785
+ function AESCrypto(subtle, iv, aesMode) {
11728
11786
  this.subtle = void 0;
11729
11787
  this.aesIV = void 0;
11788
+ this.aesMode = void 0;
11730
11789
  this.subtle = subtle;
11731
11790
  this.aesIV = iv;
11791
+ this.aesMode = aesMode;
11732
11792
  }
11733
11793
  var _proto = AESCrypto.prototype;
11734
11794
  _proto.decrypt = function decrypt(data, key) {
11735
- return this.subtle.decrypt({
11736
- name: 'AES-CBC',
11737
- iv: this.aesIV
11738
- }, key, data);
11795
+ switch (this.aesMode) {
11796
+ case DecrypterAesMode.cbc:
11797
+ return this.subtle.decrypt({
11798
+ name: 'AES-CBC',
11799
+ iv: this.aesIV
11800
+ }, key, data);
11801
+ case DecrypterAesMode.ctr:
11802
+ return this.subtle.decrypt({
11803
+ name: 'AES-CTR',
11804
+ counter: this.aesIV,
11805
+ length: 64
11806
+ },
11807
+ //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
11808
+ key, data);
11809
+ default:
11810
+ throw new Error("[AESCrypto] invalid aes mode " + this.aesMode);
11811
+ }
11739
11812
  };
11740
11813
  return AESCrypto;
11741
11814
  }();
11742
11815
 
11743
11816
  var FastAESKey = /*#__PURE__*/function () {
11744
- function FastAESKey(subtle, key) {
11817
+ function FastAESKey(subtle, key, aesMode) {
11745
11818
  this.subtle = void 0;
11746
11819
  this.key = void 0;
11820
+ this.aesMode = void 0;
11747
11821
  this.subtle = subtle;
11748
11822
  this.key = key;
11823
+ this.aesMode = aesMode;
11749
11824
  }
11750
11825
  var _proto = FastAESKey.prototype;
11751
11826
  _proto.expandKey = function expandKey() {
11827
+ var subtleAlgoName = getSubtleAlgoName(this.aesMode);
11752
11828
  return this.subtle.importKey('raw', this.key, {
11753
- name: 'AES-CBC'
11829
+ name: subtleAlgoName
11754
11830
  }, false, ['encrypt', 'decrypt']);
11755
11831
  };
11756
11832
  return FastAESKey;
11757
11833
  }();
11834
+ function getSubtleAlgoName(aesMode) {
11835
+ switch (aesMode) {
11836
+ case DecrypterAesMode.cbc:
11837
+ return 'AES-CBC';
11838
+ case DecrypterAesMode.ctr:
11839
+ return 'AES-CTR';
11840
+ default:
11841
+ throw new Error("[FastAESKey] invalid aes mode " + aesMode);
11842
+ }
11843
+ }
11758
11844
 
11759
11845
  // PKCS7
11760
11846
  function removePadding(array) {
@@ -12007,7 +12093,8 @@
12007
12093
  this.currentIV = null;
12008
12094
  this.currentResult = null;
12009
12095
  this.useSoftware = void 0;
12010
- this.useSoftware = config.enableSoftwareAES;
12096
+ this.enableSoftwareAES = void 0;
12097
+ this.enableSoftwareAES = config.enableSoftwareAES;
12011
12098
  this.removePKCS7Padding = removePKCS7Padding;
12012
12099
  // built in decryptor expects PKCS7 padding
12013
12100
  if (removePKCS7Padding) {
@@ -12020,9 +12107,7 @@
12020
12107
  /* no-op */
12021
12108
  }
12022
12109
  }
12023
- if (this.subtle === null) {
12024
- this.useSoftware = true;
12025
- }
12110
+ this.useSoftware = this.subtle === null;
12026
12111
  }
12027
12112
  var _proto = Decrypter.prototype;
12028
12113
  _proto.destroy = function destroy() {
@@ -12059,11 +12144,11 @@
12059
12144
  this.softwareDecrypter = null;
12060
12145
  }
12061
12146
  };
12062
- _proto.decrypt = function decrypt(data, key, iv) {
12147
+ _proto.decrypt = function decrypt(data, key, iv, aesMode) {
12063
12148
  var _this = this;
12064
12149
  if (this.useSoftware) {
12065
12150
  return new Promise(function (resolve, reject) {
12066
- _this.softwareDecrypt(new Uint8Array(data), key, iv);
12151
+ _this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
12067
12152
  var decryptResult = _this.flush();
12068
12153
  if (decryptResult) {
12069
12154
  resolve(decryptResult.buffer);
@@ -12072,16 +12157,20 @@
12072
12157
  }
12073
12158
  });
12074
12159
  }
12075
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
12160
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
12076
12161
  }
12077
12162
 
12078
12163
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
12079
12164
  // data is handled in the flush() call
12080
12165
  ;
12081
- _proto.softwareDecrypt = function softwareDecrypt(data, key, iv) {
12166
+ _proto.softwareDecrypt = function softwareDecrypt(data, key, iv, aesMode) {
12082
12167
  var currentIV = this.currentIV,
12083
12168
  currentResult = this.currentResult,
12084
12169
  remainderData = this.remainderData;
12170
+ if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
12171
+ logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
12172
+ return null;
12173
+ }
12085
12174
  this.logOnce('JS AES decrypt');
12086
12175
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
12087
12176
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -12114,12 +12203,12 @@
12114
12203
  }
12115
12204
  return result;
12116
12205
  };
12117
- _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv) {
12206
+ _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv, aesMode) {
12118
12207
  var _this2 = this;
12119
12208
  var subtle = this.subtle;
12120
12209
  if (this.key !== key || !this.fastAesKey) {
12121
12210
  this.key = key;
12122
- this.fastAesKey = new FastAESKey(subtle, key);
12211
+ this.fastAesKey = new FastAESKey(subtle, key, aesMode);
12123
12212
  }
12124
12213
  return this.fastAesKey.expandKey().then(function (aesKey) {
12125
12214
  // decrypt using web crypto
@@ -12127,22 +12216,25 @@
12127
12216
  return Promise.reject(new Error('web crypto not initialized'));
12128
12217
  }
12129
12218
  _this2.logOnce('WebCrypto AES decrypt');
12130
- var crypto = new AESCrypto(subtle, new Uint8Array(iv));
12219
+ var crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
12131
12220
  return crypto.decrypt(data.buffer, aesKey);
12132
12221
  }).catch(function (err) {
12133
12222
  logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message);
12134
- return _this2.onWebCryptoError(data, key, iv);
12223
+ return _this2.onWebCryptoError(data, key, iv, aesMode);
12135
12224
  });
12136
12225
  };
12137
- _proto.onWebCryptoError = function onWebCryptoError(data, key, iv) {
12138
- this.useSoftware = true;
12139
- this.logEnabled = true;
12140
- this.softwareDecrypt(data, key, iv);
12141
- var decryptResult = this.flush();
12142
- if (decryptResult) {
12143
- return decryptResult.buffer;
12226
+ _proto.onWebCryptoError = function onWebCryptoError(data, key, iv, aesMode) {
12227
+ var enableSoftwareAES = this.enableSoftwareAES;
12228
+ if (enableSoftwareAES) {
12229
+ this.useSoftware = true;
12230
+ this.logEnabled = true;
12231
+ this.softwareDecrypt(data, key, iv, aesMode);
12232
+ var decryptResult = this.flush();
12233
+ if (decryptResult) {
12234
+ return decryptResult.buffer;
12235
+ }
12144
12236
  }
12145
- throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
12237
+ throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data');
12146
12238
  };
12147
12239
  _proto.getValidChunk = function getValidChunk(data) {
12148
12240
  var currentChunk = data;
@@ -12196,7 +12288,7 @@
12196
12288
  _inheritsLoose(BaseStreamController, _TaskLoop);
12197
12289
  function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
12198
12290
  var _this;
12199
- _this = _TaskLoop.call(this) || this;
12291
+ _this = _TaskLoop.call(this, logPrefix, hls.logger) || this;
12200
12292
  _this.hls = void 0;
12201
12293
  _this.fragPrevious = null;
12202
12294
  _this.fragCurrent = null;
@@ -12221,30 +12313,101 @@
12221
12313
  _this.startFragRequested = false;
12222
12314
  _this.decrypter = void 0;
12223
12315
  _this.initPTS = [];
12224
- _this.onvseeking = null;
12225
- _this.onvended = null;
12226
- _this.logPrefix = '';
12227
- _this.log = void 0;
12228
- _this.warn = void 0;
12316
+ _this.buffering = true;
12317
+ _this.loadingParts = false;
12318
+ _this.onMediaSeeking = function () {
12319
+ var _assertThisInitialize = _assertThisInitialized(_this),
12320
+ config = _assertThisInitialize.config,
12321
+ fragCurrent = _assertThisInitialize.fragCurrent,
12322
+ media = _assertThisInitialize.media,
12323
+ mediaBuffer = _assertThisInitialize.mediaBuffer,
12324
+ state = _assertThisInitialize.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
+ if (!_this.loadingParts) {
12354
+ var bufferEnd = Math.max(bufferInfo.end, currentTime);
12355
+ var shouldLoadParts = _this.shouldLoadParts(_this.getLevelDetails(), bufferEnd);
12356
+ if (shouldLoadParts) {
12357
+ _this.log("LL-Part loading ON after seeking to " + currentTime.toFixed(2) + " with buffer @" + bufferEnd.toFixed(2));
12358
+ _this.loadingParts = shouldLoadParts;
12359
+ }
12360
+ }
12361
+ }
12362
+
12363
+ // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12364
+ if (!_this.loadedmetadata && !bufferInfo.len) {
12365
+ _this.nextLoadPosition = _this.startPosition = currentTime;
12366
+ }
12367
+
12368
+ // Async tick to speed up processing
12369
+ _this.tickImmediate();
12370
+ };
12371
+ _this.onMediaEnded = function () {
12372
+ // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12373
+ _this.startPosition = _this.lastCurrentTime = 0;
12374
+ if (_this.playlistType === PlaylistLevelType.MAIN) {
12375
+ _this.hls.trigger(Events.MEDIA_ENDED, {
12376
+ stalled: false
12377
+ });
12378
+ }
12379
+ };
12229
12380
  _this.playlistType = playlistType;
12230
- _this.logPrefix = logPrefix;
12231
- _this.log = logger.log.bind(logger, logPrefix + ":");
12232
- _this.warn = logger.warn.bind(logger, logPrefix + ":");
12233
12381
  _this.hls = hls;
12234
12382
  _this.fragmentLoader = new FragmentLoader(hls.config);
12235
12383
  _this.keyLoader = keyLoader;
12236
12384
  _this.fragmentTracker = fragmentTracker;
12237
12385
  _this.config = hls.config;
12238
12386
  _this.decrypter = new Decrypter(hls.config);
12239
- hls.on(Events.MANIFEST_LOADED, _this.onManifestLoaded, _assertThisInitialized(_this));
12240
12387
  return _this;
12241
12388
  }
12242
12389
  var _proto = BaseStreamController.prototype;
12243
- _proto.doTick = function doTick() {
12244
- this.onTickEnd();
12245
- };
12246
- _proto.onTickEnd = function onTickEnd() {}
12247
-
12390
+ _proto.registerListeners = function registerListeners() {
12391
+ var hls = this.hls;
12392
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12393
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12394
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12395
+ hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12396
+ hls.on(Events.ERROR, this.onError, this);
12397
+ };
12398
+ _proto.unregisterListeners = function unregisterListeners() {
12399
+ var hls = this.hls;
12400
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12401
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12402
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12403
+ hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12404
+ hls.off(Events.ERROR, this.onError, this);
12405
+ };
12406
+ _proto.doTick = function doTick() {
12407
+ this.onTickEnd();
12408
+ };
12409
+ _proto.onTickEnd = function onTickEnd() {}
12410
+
12248
12411
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
12249
12412
  ;
12250
12413
  _proto.startLoad = function startLoad(startPosition) {};
@@ -12263,6 +12426,12 @@
12263
12426
  this.clearNextTick();
12264
12427
  this.state = State.STOPPED;
12265
12428
  };
12429
+ _proto.pauseBuffering = function pauseBuffering() {
12430
+ this.buffering = false;
12431
+ };
12432
+ _proto.resumeBuffering = function resumeBuffering() {
12433
+ this.buffering = true;
12434
+ };
12266
12435
  _proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) {
12267
12436
  // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
12268
12437
  // of nothing loading/loaded return false
@@ -12293,10 +12462,8 @@
12293
12462
  };
12294
12463
  _proto.onMediaAttached = function onMediaAttached(event, data) {
12295
12464
  var media = this.media = this.mediaBuffer = data.media;
12296
- this.onvseeking = this.onMediaSeeking.bind(this);
12297
- this.onvended = this.onMediaEnded.bind(this);
12298
- media.addEventListener('seeking', this.onvseeking);
12299
- media.addEventListener('ended', this.onvended);
12465
+ media.addEventListener('seeking', this.onMediaSeeking);
12466
+ media.addEventListener('ended', this.onMediaEnded);
12300
12467
  var config = this.config;
12301
12468
  if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
12302
12469
  this.startLoad(config.startPosition);
@@ -12310,10 +12477,9 @@
12310
12477
  }
12311
12478
 
12312
12479
  // remove video listeners
12313
- if (media && this.onvseeking && this.onvended) {
12314
- media.removeEventListener('seeking', this.onvseeking);
12315
- media.removeEventListener('ended', this.onvended);
12316
- this.onvseeking = this.onvended = null;
12480
+ if (media) {
12481
+ media.removeEventListener('seeking', this.onMediaSeeking);
12482
+ media.removeEventListener('ended', this.onMediaEnded);
12317
12483
  }
12318
12484
  if (this.keyLoader) {
12319
12485
  this.keyLoader.detach();
@@ -12323,54 +12489,8 @@
12323
12489
  this.fragmentTracker.removeAllFragments();
12324
12490
  this.stopLoad();
12325
12491
  };
12326
- _proto.onMediaSeeking = function onMediaSeeking() {
12327
- var config = this.config,
12328
- fragCurrent = this.fragCurrent,
12329
- media = this.media,
12330
- mediaBuffer = this.mediaBuffer,
12331
- state = this.state;
12332
- var currentTime = media ? media.currentTime : 0;
12333
- var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12334
- this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12335
- if (this.state === State.ENDED) {
12336
- this.resetLoadingState();
12337
- } else if (fragCurrent) {
12338
- // Seeking while frag load is in progress
12339
- var tolerance = config.maxFragLookUpTolerance;
12340
- var fragStartOffset = fragCurrent.start - tolerance;
12341
- var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12342
- // if seeking out of buffered range or into new one
12343
- if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12344
- var pastFragment = currentTime > fragEndOffset;
12345
- // if the seek position is outside the current fragment range
12346
- if (currentTime < fragStartOffset || pastFragment) {
12347
- if (pastFragment && fragCurrent.loader) {
12348
- this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12349
- fragCurrent.abortRequests();
12350
- this.resetLoadingState();
12351
- }
12352
- this.fragPrevious = null;
12353
- }
12354
- }
12355
- }
12356
- if (media) {
12357
- // Remove gap fragments
12358
- this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
12359
- this.lastCurrentTime = currentTime;
12360
- }
12361
-
12362
- // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12363
- if (!this.loadedmetadata && !bufferInfo.len) {
12364
- this.nextLoadPosition = this.startPosition = currentTime;
12365
- }
12366
-
12367
- // Async tick to speed up processing
12368
- this.tickImmediate();
12369
- };
12370
- _proto.onMediaEnded = function onMediaEnded() {
12371
- // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12372
- this.startPosition = this.lastCurrentTime = 0;
12373
- };
12492
+ _proto.onManifestLoading = function onManifestLoading() {};
12493
+ _proto.onError = function onError(event, data) {};
12374
12494
  _proto.onManifestLoaded = function onManifestLoaded(event, data) {
12375
12495
  this.startTimeOffset = data.startTimeOffset;
12376
12496
  this.initPTS = [];
@@ -12380,7 +12500,7 @@
12380
12500
  this.stopLoad();
12381
12501
  _TaskLoop.prototype.onHandlerDestroying.call(this);
12382
12502
  // @ts-ignore
12383
- this.hls = null;
12503
+ this.hls = this.onMediaSeeking = this.onMediaEnded = null;
12384
12504
  };
12385
12505
  _proto.onHandlerDestroyed = function onHandlerDestroyed() {
12386
12506
  this.state = State.STOPPED;
@@ -12510,10 +12630,10 @@
12510
12630
  var decryptData = frag.decryptdata;
12511
12631
 
12512
12632
  // check to see if the payload needs to be decrypted
12513
- if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
12633
+ if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
12514
12634
  var startTime = self.performance.now();
12515
12635
  // decrypt init segment data
12516
- return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(function (err) {
12636
+ return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(function (err) {
12517
12637
  hls.trigger(Events.ERROR, {
12518
12638
  type: ErrorTypes.MEDIA_ERROR,
12519
12639
  details: ErrorDetails.FRAG_DECRYPT_ERROR,
@@ -12626,7 +12746,7 @@
12626
12746
  }
12627
12747
  var keyLoadingPromise = null;
12628
12748
  if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
12629
- this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + " " + frag.level);
12749
+ this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + " " + frag.level);
12630
12750
  this.state = State.KEY_LOADING;
12631
12751
  this.fragCurrent = frag;
12632
12752
  keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) {
@@ -12647,8 +12767,16 @@
12647
12767
  } else if (!frag.encrypted && details.encryptedFragments.length) {
12648
12768
  this.keyLoader.loadClear(frag, details.encryptedFragments);
12649
12769
  }
12770
+ var fragPrevious = this.fragPrevious;
12771
+ if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) {
12772
+ var shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
12773
+ if (shouldLoadParts !== this.loadingParts) {
12774
+ this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " loading sn " + (fragPrevious == null ? void 0 : fragPrevious.sn) + "->" + frag.sn);
12775
+ this.loadingParts = shouldLoadParts;
12776
+ }
12777
+ }
12650
12778
  targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
12651
- if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
12779
+ if (this.loadingParts && frag.sn !== 'initSegment') {
12652
12780
  var partList = details.partList;
12653
12781
  if (partList && progressCallback) {
12654
12782
  if (targetBufferTime > frag.end && details.fragmentHint) {
@@ -12657,7 +12785,7 @@
12657
12785
  var partIndex = this.getNextPart(partList, frag, targetBufferTime);
12658
12786
  if (partIndex > -1) {
12659
12787
  var part = partList[partIndex];
12660
- 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)));
12788
+ 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)));
12661
12789
  this.nextLoadPosition = part.start + part.duration;
12662
12790
  this.state = State.FRAG_LOADING;
12663
12791
  var _result;
@@ -12690,7 +12818,14 @@
12690
12818
  }
12691
12819
  }
12692
12820
  }
12693
- 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)));
12821
+ if (frag.sn !== 'initSegment' && this.loadingParts) {
12822
+ this.log("LL-Part loading OFF after next part miss @" + targetBufferTime.toFixed(2));
12823
+ this.loadingParts = false;
12824
+ } else if (!frag.url) {
12825
+ // Selected fragment hint for part but not loading parts
12826
+ return Promise.resolve(null);
12827
+ }
12828
+ 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)));
12694
12829
  // Don't update nextLoadPosition for fragments which are not buffered
12695
12830
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
12696
12831
  this.nextLoadPosition = frag.start + frag.duration;
@@ -12792,8 +12927,36 @@
12792
12927
  if (part) {
12793
12928
  part.stats.parsing.end = now;
12794
12929
  }
12930
+ // See if part loading should be disabled/enabled based on buffer and playback position.
12931
+ if (frag.sn !== 'initSegment') {
12932
+ var levelDetails = this.getLevelDetails();
12933
+ var loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
12934
+ var shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
12935
+ if (shouldLoadParts !== this.loadingParts) {
12936
+ this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " after parsing segment ending @" + frag.end.toFixed(2));
12937
+ this.loadingParts = shouldLoadParts;
12938
+ }
12939
+ }
12795
12940
  this.updateLevelTiming(frag, part, level, chunkMeta.partial);
12796
12941
  };
12942
+ _proto.shouldLoadParts = function shouldLoadParts(details, bufferEnd) {
12943
+ if (this.config.lowLatencyMode) {
12944
+ if (!details) {
12945
+ return this.loadingParts;
12946
+ }
12947
+ if (details != null && details.partList) {
12948
+ var _details$fragmentHint;
12949
+ // Buffer must be ahead of first part + duration of parts after last segment
12950
+ // and playback must be at or past segment adjacent to part list
12951
+ var firstPart = details.partList[0];
12952
+ var safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0);
12953
+ if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) {
12954
+ return true;
12955
+ }
12956
+ }
12957
+ }
12958
+ return false;
12959
+ };
12797
12960
  _proto.getCurrentContext = function getCurrentContext(chunkMeta) {
12798
12961
  var levels = this.levels,
12799
12962
  fragCurrent = this.fragCurrent;
@@ -12928,7 +13091,8 @@
12928
13091
  // find fragment index, contiguous with end of buffer position
12929
13092
  var config = this.config;
12930
13093
  var start = fragments[0].start;
12931
- var frag;
13094
+ var canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
13095
+ var frag = null;
12932
13096
  if (levelDetails.live) {
12933
13097
  var initialLiveManifestSize = config.initialLiveManifestSize;
12934
13098
  if (fragLen < initialLiveManifestSize) {
@@ -12940,6 +13104,10 @@
12940
13104
  // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
12941
13105
  // we get the fragment matching that start time
12942
13106
  if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) {
13107
+ if (canLoadParts && !this.loadingParts) {
13108
+ this.log("LL-Part loading ON for initial live fragment");
13109
+ this.loadingParts = true;
13110
+ }
12943
13111
  frag = this.getInitialLiveFragment(levelDetails, fragments);
12944
13112
  this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;
12945
13113
  }
@@ -12950,7 +13118,7 @@
12950
13118
 
12951
13119
  // If we haven't run into any special cases already, just load the fragment most closely matching the requested position
12952
13120
  if (!frag) {
12953
- var end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd;
13121
+ var end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd;
12954
13122
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
12955
13123
  }
12956
13124
  return this.mapToInitFragWhenRequired(frag);
@@ -13064,7 +13232,7 @@
13064
13232
  var fragmentHint = levelDetails.fragmentHint;
13065
13233
  var tolerance = config.maxFragLookUpTolerance;
13066
13234
  var partList = levelDetails.partList;
13067
- var loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint);
13235
+ var loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint);
13068
13236
  if (loadingParts && fragmentHint && !this.bitrateTest) {
13069
13237
  // Include incomplete fragment with parts at end
13070
13238
  fragments = fragments.concat(fragmentHint);
@@ -13251,7 +13419,7 @@
13251
13419
  errorAction.resolved = true;
13252
13420
  }
13253
13421
  } else {
13254
- logger.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13422
+ this.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13255
13423
  return;
13256
13424
  }
13257
13425
  } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
@@ -13641,6 +13809,7 @@
13641
13809
  */
13642
13810
  function getAudioConfig(observer, data, offset, audioCodec) {
13643
13811
  var adtsObjectType;
13812
+ var originalAdtsObjectType;
13644
13813
  var adtsExtensionSamplingIndex;
13645
13814
  var adtsChannelConfig;
13646
13815
  var config;
@@ -13648,7 +13817,7 @@
13648
13817
  var manifestCodec = audioCodec;
13649
13818
  var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
13650
13819
  // byte 2
13651
- adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13820
+ adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13652
13821
  var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
13653
13822
  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
13654
13823
  var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex);
@@ -13665,8 +13834,8 @@
13665
13834
  // byte 3
13666
13835
  adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
13667
13836
  logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex);
13668
- // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
13669
- if (/firefox/i.test(userAgent)) {
13837
+ // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
13838
+ if (/firefox|palemoon/i.test(userAgent)) {
13670
13839
  if (adtsSamplingIndex >= 6) {
13671
13840
  adtsObjectType = 5;
13672
13841
  config = new Array(4);
@@ -13760,6 +13929,7 @@
13760
13929
  samplerate: adtsSamplingRates[adtsSamplingIndex],
13761
13930
  channelCount: adtsChannelConfig,
13762
13931
  codec: 'mp4a.40.' + adtsObjectType,
13932
+ parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
13763
13933
  manifestCodec: manifestCodec
13764
13934
  };
13765
13935
  }
@@ -13814,7 +13984,8 @@
13814
13984
  track.channelCount = config.channelCount;
13815
13985
  track.codec = config.codec;
13816
13986
  track.manifestCodec = config.manifestCodec;
13817
- logger.log("parsed codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13987
+ track.parsedCodec = config.parsedCodec;
13988
+ logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13818
13989
  }
13819
13990
  }
13820
13991
  function getFrameDuration(samplerate) {
@@ -14294,6 +14465,110 @@
14294
14465
  logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
14295
14466
  }
14296
14467
  };
14468
+ _proto.parseNALu = function parseNALu(track, array) {
14469
+ var len = array.byteLength;
14470
+ var state = track.naluState || 0;
14471
+ var lastState = state;
14472
+ var units = [];
14473
+ var i = 0;
14474
+ var value;
14475
+ var overflow;
14476
+ var unitType;
14477
+ var lastUnitStart = -1;
14478
+ var lastUnitType = 0;
14479
+ // logger.log('PES:' + Hex.hexDump(array));
14480
+
14481
+ if (state === -1) {
14482
+ // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14483
+ lastUnitStart = 0;
14484
+ // NALu type is value read from offset 0
14485
+ lastUnitType = this.getNALuType(array, 0);
14486
+ state = 0;
14487
+ i = 1;
14488
+ }
14489
+ while (i < len) {
14490
+ value = array[i++];
14491
+ // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14492
+ if (!state) {
14493
+ state = value ? 0 : 1;
14494
+ continue;
14495
+ }
14496
+ if (state === 1) {
14497
+ state = value ? 0 : 2;
14498
+ continue;
14499
+ }
14500
+ // here we have state either equal to 2 or 3
14501
+ if (!value) {
14502
+ state = 3;
14503
+ } else if (value === 1) {
14504
+ overflow = i - state - 1;
14505
+ if (lastUnitStart >= 0) {
14506
+ var unit = {
14507
+ data: array.subarray(lastUnitStart, overflow),
14508
+ type: lastUnitType
14509
+ };
14510
+ // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14511
+ units.push(unit);
14512
+ } else {
14513
+ // lastUnitStart is undefined => this is the first start code found in this PES packet
14514
+ // first check if start code delimiter is overlapping between 2 PES packets,
14515
+ // ie it started in last packet (lastState not zero)
14516
+ // and ended at the beginning of this PES packet (i <= 4 - lastState)
14517
+ var lastUnit = this.getLastNalUnit(track.samples);
14518
+ if (lastUnit) {
14519
+ if (lastState && i <= 4 - lastState) {
14520
+ // start delimiter overlapping between PES packets
14521
+ // strip start delimiter bytes from the end of last NAL unit
14522
+ // check if lastUnit had a state different from zero
14523
+ if (lastUnit.state) {
14524
+ // strip last bytes
14525
+ lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14526
+ }
14527
+ }
14528
+ // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14529
+
14530
+ if (overflow > 0) {
14531
+ // logger.log('first NALU found with overflow:' + overflow);
14532
+ lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14533
+ lastUnit.state = 0;
14534
+ }
14535
+ }
14536
+ }
14537
+ // check if we can read unit type
14538
+ if (i < len) {
14539
+ unitType = this.getNALuType(array, i);
14540
+ // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14541
+ lastUnitStart = i;
14542
+ lastUnitType = unitType;
14543
+ state = 0;
14544
+ } else {
14545
+ // not enough byte to read unit type. let's read it on next PES parsing
14546
+ state = -1;
14547
+ }
14548
+ } else {
14549
+ state = 0;
14550
+ }
14551
+ }
14552
+ if (lastUnitStart >= 0 && state >= 0) {
14553
+ var _unit = {
14554
+ data: array.subarray(lastUnitStart, len),
14555
+ type: lastUnitType,
14556
+ state: state
14557
+ };
14558
+ units.push(_unit);
14559
+ // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14560
+ }
14561
+ // no NALu found
14562
+ if (units.length === 0) {
14563
+ // append pes.data to previous NAL unit
14564
+ var _lastUnit = this.getLastNalUnit(track.samples);
14565
+ if (_lastUnit) {
14566
+ _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14567
+ }
14568
+ }
14569
+ track.naluState = state;
14570
+ return units;
14571
+ };
14297
14572
  return BaseVideoParser;
14298
14573
  }();
14299
14574
 
@@ -14448,189 +14723,6 @@
14448
14723
  ;
14449
14724
  _proto.readUInt = function readUInt() {
14450
14725
  return this.readBits(32);
14451
- }
14452
-
14453
- /**
14454
- * Advance the ExpGolomb decoder past a scaling list. The scaling
14455
- * list is optionally transmitted as part of a sequence parameter
14456
- * set and is not relevant to transmuxing.
14457
- * @param count the number of entries in this scaling list
14458
- * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14459
- */;
14460
- _proto.skipScalingList = function skipScalingList(count) {
14461
- var lastScale = 8;
14462
- var nextScale = 8;
14463
- var deltaScale;
14464
- for (var j = 0; j < count; j++) {
14465
- if (nextScale !== 0) {
14466
- deltaScale = this.readEG();
14467
- nextScale = (lastScale + deltaScale + 256) % 256;
14468
- }
14469
- lastScale = nextScale === 0 ? lastScale : nextScale;
14470
- }
14471
- }
14472
-
14473
- /**
14474
- * Read a sequence parameter set and return some interesting video
14475
- * properties. A sequence parameter set is the H264 metadata that
14476
- * describes the properties of upcoming video frames.
14477
- * @returns an object with configuration parsed from the
14478
- * sequence parameter set, including the dimensions of the
14479
- * associated video frames.
14480
- */;
14481
- _proto.readSPS = function readSPS() {
14482
- var frameCropLeftOffset = 0;
14483
- var frameCropRightOffset = 0;
14484
- var frameCropTopOffset = 0;
14485
- var frameCropBottomOffset = 0;
14486
- var numRefFramesInPicOrderCntCycle;
14487
- var scalingListCount;
14488
- var i;
14489
- var readUByte = this.readUByte.bind(this);
14490
- var readBits = this.readBits.bind(this);
14491
- var readUEG = this.readUEG.bind(this);
14492
- var readBoolean = this.readBoolean.bind(this);
14493
- var skipBits = this.skipBits.bind(this);
14494
- var skipEG = this.skipEG.bind(this);
14495
- var skipUEG = this.skipUEG.bind(this);
14496
- var skipScalingList = this.skipScalingList.bind(this);
14497
- readUByte();
14498
- var profileIdc = readUByte(); // profile_idc
14499
- readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14500
- skipBits(3); // reserved_zero_3bits u(3),
14501
- readUByte(); // level_idc u(8)
14502
- skipUEG(); // seq_parameter_set_id
14503
- // some profiles have more optional data we don't need
14504
- if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14505
- var chromaFormatIdc = readUEG();
14506
- if (chromaFormatIdc === 3) {
14507
- skipBits(1);
14508
- } // separate_colour_plane_flag
14509
-
14510
- skipUEG(); // bit_depth_luma_minus8
14511
- skipUEG(); // bit_depth_chroma_minus8
14512
- skipBits(1); // qpprime_y_zero_transform_bypass_flag
14513
- if (readBoolean()) {
14514
- // seq_scaling_matrix_present_flag
14515
- scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14516
- for (i = 0; i < scalingListCount; i++) {
14517
- if (readBoolean()) {
14518
- // seq_scaling_list_present_flag[ i ]
14519
- if (i < 6) {
14520
- skipScalingList(16);
14521
- } else {
14522
- skipScalingList(64);
14523
- }
14524
- }
14525
- }
14526
- }
14527
- }
14528
- skipUEG(); // log2_max_frame_num_minus4
14529
- var picOrderCntType = readUEG();
14530
- if (picOrderCntType === 0) {
14531
- readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14532
- } else if (picOrderCntType === 1) {
14533
- skipBits(1); // delta_pic_order_always_zero_flag
14534
- skipEG(); // offset_for_non_ref_pic
14535
- skipEG(); // offset_for_top_to_bottom_field
14536
- numRefFramesInPicOrderCntCycle = readUEG();
14537
- for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14538
- skipEG();
14539
- } // offset_for_ref_frame[ i ]
14540
- }
14541
- skipUEG(); // max_num_ref_frames
14542
- skipBits(1); // gaps_in_frame_num_value_allowed_flag
14543
- var picWidthInMbsMinus1 = readUEG();
14544
- var picHeightInMapUnitsMinus1 = readUEG();
14545
- var frameMbsOnlyFlag = readBits(1);
14546
- if (frameMbsOnlyFlag === 0) {
14547
- skipBits(1);
14548
- } // mb_adaptive_frame_field_flag
14549
-
14550
- skipBits(1); // direct_8x8_inference_flag
14551
- if (readBoolean()) {
14552
- // frame_cropping_flag
14553
- frameCropLeftOffset = readUEG();
14554
- frameCropRightOffset = readUEG();
14555
- frameCropTopOffset = readUEG();
14556
- frameCropBottomOffset = readUEG();
14557
- }
14558
- var pixelRatio = [1, 1];
14559
- if (readBoolean()) {
14560
- // vui_parameters_present_flag
14561
- if (readBoolean()) {
14562
- // aspect_ratio_info_present_flag
14563
- var aspectRatioIdc = readUByte();
14564
- switch (aspectRatioIdc) {
14565
- case 1:
14566
- pixelRatio = [1, 1];
14567
- break;
14568
- case 2:
14569
- pixelRatio = [12, 11];
14570
- break;
14571
- case 3:
14572
- pixelRatio = [10, 11];
14573
- break;
14574
- case 4:
14575
- pixelRatio = [16, 11];
14576
- break;
14577
- case 5:
14578
- pixelRatio = [40, 33];
14579
- break;
14580
- case 6:
14581
- pixelRatio = [24, 11];
14582
- break;
14583
- case 7:
14584
- pixelRatio = [20, 11];
14585
- break;
14586
- case 8:
14587
- pixelRatio = [32, 11];
14588
- break;
14589
- case 9:
14590
- pixelRatio = [80, 33];
14591
- break;
14592
- case 10:
14593
- pixelRatio = [18, 11];
14594
- break;
14595
- case 11:
14596
- pixelRatio = [15, 11];
14597
- break;
14598
- case 12:
14599
- pixelRatio = [64, 33];
14600
- break;
14601
- case 13:
14602
- pixelRatio = [160, 99];
14603
- break;
14604
- case 14:
14605
- pixelRatio = [4, 3];
14606
- break;
14607
- case 15:
14608
- pixelRatio = [3, 2];
14609
- break;
14610
- case 16:
14611
- pixelRatio = [2, 1];
14612
- break;
14613
- case 255:
14614
- {
14615
- pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
14616
- break;
14617
- }
14618
- }
14619
- }
14620
- }
14621
- return {
14622
- width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
14623
- height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
14624
- pixelRatio: pixelRatio
14625
- };
14626
- };
14627
- _proto.readSliceType = function readSliceType() {
14628
- // skip NALu type
14629
- this.readUByte();
14630
- // discard first_mb_in_slice
14631
- this.readUEG();
14632
- // return slice_type
14633
- return this.readUEG();
14634
14726
  };
14635
14727
  return ExpGolomb;
14636
14728
  }();
@@ -14641,9 +14733,9 @@
14641
14733
  return _BaseVideoParser.apply(this, arguments) || this;
14642
14734
  }
14643
14735
  var _proto = AvcVideoParser.prototype;
14644
- _proto.parseAVCPES = function parseAVCPES(track, textTrack, pes, last, duration) {
14736
+ _proto.parsePES = function parsePES(track, textTrack, pes, last, duration) {
14645
14737
  var _this = this;
14646
- var units = this.parseAVCNALu(track, pes.data);
14738
+ var units = this.parseNALu(track, pes.data);
14647
14739
  var VideoSample = this.VideoSample;
14648
14740
  var push;
14649
14741
  var spsfound = false;
@@ -14668,7 +14760,7 @@
14668
14760
  // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
14669
14761
  if (spsfound && data.length > 4) {
14670
14762
  // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
14671
- var sliceType = new ExpGolomb(data).readSliceType();
14763
+ var sliceType = _this.readSliceType(data);
14672
14764
  // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
14673
14765
  // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
14674
14766
  // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
@@ -14722,8 +14814,7 @@
14722
14814
  push = true;
14723
14815
  spsfound = true;
14724
14816
  var sps = unit.data;
14725
- var expGolombDecoder = new ExpGolomb(sps);
14726
- var config = expGolombDecoder.readSPS();
14817
+ var config = _this.readSPS(sps);
14727
14818
  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]) {
14728
14819
  track.width = config.width;
14729
14820
  track.height = config.height;
@@ -14779,109 +14870,192 @@
14779
14870
  this.VideoSample = null;
14780
14871
  }
14781
14872
  };
14782
- _proto.parseAVCNALu = function parseAVCNALu(track, array) {
14783
- var len = array.byteLength;
14784
- var state = track.naluState || 0;
14785
- var lastState = state;
14786
- var units = [];
14787
- var i = 0;
14788
- var value;
14789
- var overflow;
14790
- var unitType;
14791
- var lastUnitStart = -1;
14792
- var lastUnitType = 0;
14793
- // logger.log('PES:' + Hex.hexDump(array));
14873
+ _proto.getNALuType = function getNALuType(data, offset) {
14874
+ return data[offset] & 0x1f;
14875
+ };
14876
+ _proto.readSliceType = function readSliceType(data) {
14877
+ var eg = new ExpGolomb(data);
14878
+ // skip NALu type
14879
+ eg.readUByte();
14880
+ // discard first_mb_in_slice
14881
+ eg.readUEG();
14882
+ // return slice_type
14883
+ return eg.readUEG();
14884
+ }
14794
14885
 
14795
- if (state === -1) {
14796
- // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14797
- lastUnitStart = 0;
14798
- // NALu type is value read from offset 0
14799
- lastUnitType = array[0] & 0x1f;
14800
- state = 0;
14801
- i = 1;
14802
- }
14803
- while (i < len) {
14804
- value = array[i++];
14805
- // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14806
- if (!state) {
14807
- state = value ? 0 : 1;
14808
- continue;
14809
- }
14810
- if (state === 1) {
14811
- state = value ? 0 : 2;
14812
- continue;
14886
+ /**
14887
+ * The scaling list is optionally transmitted as part of a sequence parameter
14888
+ * set and is not relevant to transmuxing.
14889
+ * @param count the number of entries in this scaling list
14890
+ * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14891
+ */;
14892
+ _proto.skipScalingList = function skipScalingList(count, reader) {
14893
+ var lastScale = 8;
14894
+ var nextScale = 8;
14895
+ var deltaScale;
14896
+ for (var j = 0; j < count; j++) {
14897
+ if (nextScale !== 0) {
14898
+ deltaScale = reader.readEG();
14899
+ nextScale = (lastScale + deltaScale + 256) % 256;
14813
14900
  }
14814
- // here we have state either equal to 2 or 3
14815
- if (!value) {
14816
- state = 3;
14817
- } else if (value === 1) {
14818
- overflow = i - state - 1;
14819
- if (lastUnitStart >= 0) {
14820
- var unit = {
14821
- data: array.subarray(lastUnitStart, overflow),
14822
- type: lastUnitType
14823
- };
14824
- // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14825
- units.push(unit);
14826
- } else {
14827
- // lastUnitStart is undefined => this is the first start code found in this PES packet
14828
- // first check if start code delimiter is overlapping between 2 PES packets,
14829
- // ie it started in last packet (lastState not zero)
14830
- // and ended at the beginning of this PES packet (i <= 4 - lastState)
14831
- var lastUnit = this.getLastNalUnit(track.samples);
14832
- if (lastUnit) {
14833
- if (lastState && i <= 4 - lastState) {
14834
- // start delimiter overlapping between PES packets
14835
- // strip start delimiter bytes from the end of last NAL unit
14836
- // check if lastUnit had a state different from zero
14837
- if (lastUnit.state) {
14838
- // strip last bytes
14839
- lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14840
- }
14841
- }
14842
- // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14901
+ lastScale = nextScale === 0 ? lastScale : nextScale;
14902
+ }
14903
+ }
14843
14904
 
14844
- if (overflow > 0) {
14845
- // logger.log('first NALU found with overflow:' + overflow);
14846
- lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14847
- lastUnit.state = 0;
14848
- }
14849
- }
14850
- }
14851
- // check if we can read unit type
14852
- if (i < len) {
14853
- unitType = array[i] & 0x1f;
14854
- // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14855
- lastUnitStart = i;
14856
- lastUnitType = unitType;
14857
- state = 0;
14858
- } else {
14859
- // not enough byte to read unit type. let's read it on next PES parsing
14860
- state = -1;
14905
+ /**
14906
+ * Read a sequence parameter set and return some interesting video
14907
+ * properties. A sequence parameter set is the H264 metadata that
14908
+ * describes the properties of upcoming video frames.
14909
+ * @returns an object with configuration parsed from the
14910
+ * sequence parameter set, including the dimensions of the
14911
+ * associated video frames.
14912
+ */;
14913
+ _proto.readSPS = function readSPS(sps) {
14914
+ var eg = new ExpGolomb(sps);
14915
+ var frameCropLeftOffset = 0;
14916
+ var frameCropRightOffset = 0;
14917
+ var frameCropTopOffset = 0;
14918
+ var frameCropBottomOffset = 0;
14919
+ var numRefFramesInPicOrderCntCycle;
14920
+ var scalingListCount;
14921
+ var i;
14922
+ var readUByte = eg.readUByte.bind(eg);
14923
+ var readBits = eg.readBits.bind(eg);
14924
+ var readUEG = eg.readUEG.bind(eg);
14925
+ var readBoolean = eg.readBoolean.bind(eg);
14926
+ var skipBits = eg.skipBits.bind(eg);
14927
+ var skipEG = eg.skipEG.bind(eg);
14928
+ var skipUEG = eg.skipUEG.bind(eg);
14929
+ var skipScalingList = this.skipScalingList.bind(this);
14930
+ readUByte();
14931
+ var profileIdc = readUByte(); // profile_idc
14932
+ readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14933
+ skipBits(3); // reserved_zero_3bits u(3),
14934
+ readUByte(); // level_idc u(8)
14935
+ skipUEG(); // seq_parameter_set_id
14936
+ // some profiles have more optional data we don't need
14937
+ if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14938
+ var chromaFormatIdc = readUEG();
14939
+ if (chromaFormatIdc === 3) {
14940
+ skipBits(1);
14941
+ } // separate_colour_plane_flag
14942
+
14943
+ skipUEG(); // bit_depth_luma_minus8
14944
+ skipUEG(); // bit_depth_chroma_minus8
14945
+ skipBits(1); // qpprime_y_zero_transform_bypass_flag
14946
+ if (readBoolean()) {
14947
+ // seq_scaling_matrix_present_flag
14948
+ scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14949
+ for (i = 0; i < scalingListCount; i++) {
14950
+ if (readBoolean()) {
14951
+ // seq_scaling_list_present_flag[ i ]
14952
+ if (i < 6) {
14953
+ skipScalingList(16, eg);
14954
+ } else {
14955
+ skipScalingList(64, eg);
14956
+ }
14957
+ }
14861
14958
  }
14862
- } else {
14863
- state = 0;
14864
14959
  }
14865
14960
  }
14866
- if (lastUnitStart >= 0 && state >= 0) {
14867
- var _unit = {
14868
- data: array.subarray(lastUnitStart, len),
14869
- type: lastUnitType,
14870
- state: state
14871
- };
14872
- units.push(_unit);
14873
- // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14961
+ skipUEG(); // log2_max_frame_num_minus4
14962
+ var picOrderCntType = readUEG();
14963
+ if (picOrderCntType === 0) {
14964
+ readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14965
+ } else if (picOrderCntType === 1) {
14966
+ skipBits(1); // delta_pic_order_always_zero_flag
14967
+ skipEG(); // offset_for_non_ref_pic
14968
+ skipEG(); // offset_for_top_to_bottom_field
14969
+ numRefFramesInPicOrderCntCycle = readUEG();
14970
+ for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14971
+ skipEG();
14972
+ } // offset_for_ref_frame[ i ]
14874
14973
  }
14875
- // no NALu found
14876
- if (units.length === 0) {
14877
- // append pes.data to previous NAL unit
14878
- var _lastUnit = this.getLastNalUnit(track.samples);
14879
- if (_lastUnit) {
14880
- _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14974
+ skipUEG(); // max_num_ref_frames
14975
+ skipBits(1); // gaps_in_frame_num_value_allowed_flag
14976
+ var picWidthInMbsMinus1 = readUEG();
14977
+ var picHeightInMapUnitsMinus1 = readUEG();
14978
+ var frameMbsOnlyFlag = readBits(1);
14979
+ if (frameMbsOnlyFlag === 0) {
14980
+ skipBits(1);
14981
+ } // mb_adaptive_frame_field_flag
14982
+
14983
+ skipBits(1); // direct_8x8_inference_flag
14984
+ if (readBoolean()) {
14985
+ // frame_cropping_flag
14986
+ frameCropLeftOffset = readUEG();
14987
+ frameCropRightOffset = readUEG();
14988
+ frameCropTopOffset = readUEG();
14989
+ frameCropBottomOffset = readUEG();
14990
+ }
14991
+ var pixelRatio = [1, 1];
14992
+ if (readBoolean()) {
14993
+ // vui_parameters_present_flag
14994
+ if (readBoolean()) {
14995
+ // aspect_ratio_info_present_flag
14996
+ var aspectRatioIdc = readUByte();
14997
+ switch (aspectRatioIdc) {
14998
+ case 1:
14999
+ pixelRatio = [1, 1];
15000
+ break;
15001
+ case 2:
15002
+ pixelRatio = [12, 11];
15003
+ break;
15004
+ case 3:
15005
+ pixelRatio = [10, 11];
15006
+ break;
15007
+ case 4:
15008
+ pixelRatio = [16, 11];
15009
+ break;
15010
+ case 5:
15011
+ pixelRatio = [40, 33];
15012
+ break;
15013
+ case 6:
15014
+ pixelRatio = [24, 11];
15015
+ break;
15016
+ case 7:
15017
+ pixelRatio = [20, 11];
15018
+ break;
15019
+ case 8:
15020
+ pixelRatio = [32, 11];
15021
+ break;
15022
+ case 9:
15023
+ pixelRatio = [80, 33];
15024
+ break;
15025
+ case 10:
15026
+ pixelRatio = [18, 11];
15027
+ break;
15028
+ case 11:
15029
+ pixelRatio = [15, 11];
15030
+ break;
15031
+ case 12:
15032
+ pixelRatio = [64, 33];
15033
+ break;
15034
+ case 13:
15035
+ pixelRatio = [160, 99];
15036
+ break;
15037
+ case 14:
15038
+ pixelRatio = [4, 3];
15039
+ break;
15040
+ case 15:
15041
+ pixelRatio = [3, 2];
15042
+ break;
15043
+ case 16:
15044
+ pixelRatio = [2, 1];
15045
+ break;
15046
+ case 255:
15047
+ {
15048
+ pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
15049
+ break;
15050
+ }
15051
+ }
14881
15052
  }
14882
15053
  }
14883
- track.naluState = state;
14884
- return units;
15054
+ return {
15055
+ width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
15056
+ height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
15057
+ pixelRatio: pixelRatio
15058
+ };
14885
15059
  };
14886
15060
  return AvcVideoParser;
14887
15061
  }(BaseVideoParser);
@@ -14901,7 +15075,7 @@
14901
15075
  }
14902
15076
  var _proto = SampleAesDecrypter.prototype;
14903
15077
  _proto.decryptBuffer = function decryptBuffer(encryptedData) {
14904
- return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
15078
+ return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc);
14905
15079
  }
14906
15080
 
14907
15081
  // AAC - encrypt all full 16 bytes blocks starting from offset 16
@@ -15020,7 +15194,7 @@
15020
15194
  this.observer = observer;
15021
15195
  this.config = config;
15022
15196
  this.typeSupported = typeSupported;
15023
- this.videoParser = new AvcVideoParser();
15197
+ this.videoParser = null;
15024
15198
  }
15025
15199
  TSDemuxer.probe = function probe(data) {
15026
15200
  var syncOffset = TSDemuxer.syncOffset(data);
@@ -15190,7 +15364,16 @@
15190
15364
  case videoPid:
15191
15365
  if (stt) {
15192
15366
  if (videoData && (pes = parsePES(videoData))) {
15193
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
15367
+ if (this.videoParser === null) {
15368
+ switch (videoTrack.segmentCodec) {
15369
+ case 'avc':
15370
+ this.videoParser = new AvcVideoParser();
15371
+ break;
15372
+ }
15373
+ }
15374
+ if (this.videoParser !== null) {
15375
+ this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
15376
+ }
15194
15377
  }
15195
15378
  videoData = {
15196
15379
  data: [],
@@ -15348,8 +15531,17 @@
15348
15531
  // try to parse last PES packets
15349
15532
  var pes;
15350
15533
  if (videoData && (pes = parsePES(videoData))) {
15351
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
15352
- videoTrack.pesData = null;
15534
+ if (this.videoParser === null) {
15535
+ switch (videoTrack.segmentCodec) {
15536
+ case 'avc':
15537
+ this.videoParser = new AvcVideoParser();
15538
+ break;
15539
+ }
15540
+ }
15541
+ if (this.videoParser !== null) {
15542
+ this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
15543
+ videoTrack.pesData = null;
15544
+ }
15353
15545
  } else {
15354
15546
  // either avcData null or PES truncated, keep it for next frag parsing
15355
15547
  videoTrack.pesData = videoData;
@@ -15651,7 +15843,10 @@
15651
15843
  logger.warn('Unsupported EC-3 in M2TS found');
15652
15844
  break;
15653
15845
  case 0x24:
15654
- logger.warn('Unsupported HEVC in M2TS found');
15846
+ // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
15847
+ {
15848
+ logger.warn('Unsupported HEVC in M2TS found');
15849
+ }
15655
15850
  break;
15656
15851
  }
15657
15852
  // move to the next table entry
@@ -15879,6 +16074,8 @@
15879
16074
  avc1: [],
15880
16075
  // codingname
15881
16076
  avcC: [],
16077
+ hvc1: [],
16078
+ hvcC: [],
15882
16079
  btrt: [],
15883
16080
  dinf: [],
15884
16081
  dref: [],
@@ -16306,8 +16503,10 @@
16306
16503
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
16307
16504
  }
16308
16505
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
16309
- } else {
16506
+ } else if (track.segmentCodec === 'avc') {
16310
16507
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
16508
+ } else {
16509
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
16311
16510
  }
16312
16511
  };
16313
16512
  MP4.tkhd = function tkhd(track) {
@@ -16445,6 +16644,84 @@
16445
16644
  var result = appendUint8Array(MP4.FTYP, movie);
16446
16645
  return result;
16447
16646
  };
16647
+ MP4.hvc1 = function hvc1(track) {
16648
+ var ps = track.params;
16649
+ var units = [track.vps, track.sps, track.pps];
16650
+ var NALuLengthSize = 4;
16651
+ 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]);
16652
+
16653
+ // compute hvcC size in bytes
16654
+ var length = config.length;
16655
+ for (var i = 0; i < units.length; i += 1) {
16656
+ length += 3;
16657
+ for (var j = 0; j < units[i].length; j += 1) {
16658
+ length += 2 + units[i][j].length;
16659
+ }
16660
+ }
16661
+ var hvcC = new Uint8Array(length);
16662
+ hvcC.set(config, 0);
16663
+ length = config.length;
16664
+ // append parameter set units: one vps, one or more sps and pps
16665
+ var iMax = units.length - 1;
16666
+ for (var _i = 0; _i < units.length; _i += 1) {
16667
+ hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length);
16668
+ length += 3;
16669
+ for (var _j = 0; _j < units[_i].length; _j += 1) {
16670
+ hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length);
16671
+ length += 2;
16672
+ hvcC.set(units[_i][_j], length);
16673
+ length += units[_i][_j].length;
16674
+ }
16675
+ }
16676
+ var hvcc = MP4.box(MP4.types.hvcC, hvcC);
16677
+ var width = track.width;
16678
+ var height = track.height;
16679
+ var hSpacing = track.pixelRatio[0];
16680
+ var vSpacing = track.pixelRatio[1];
16681
+ return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
16682
+ // reserved
16683
+ 0x00, 0x00, 0x00,
16684
+ // reserved
16685
+ 0x00, 0x01,
16686
+ // data_reference_index
16687
+ 0x00, 0x00,
16688
+ // pre_defined
16689
+ 0x00, 0x00,
16690
+ // reserved
16691
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16692
+ // pre_defined
16693
+ width >> 8 & 0xff, width & 0xff,
16694
+ // width
16695
+ height >> 8 & 0xff, height & 0xff,
16696
+ // height
16697
+ 0x00, 0x48, 0x00, 0x00,
16698
+ // horizresolution
16699
+ 0x00, 0x48, 0x00, 0x00,
16700
+ // vertresolution
16701
+ 0x00, 0x00, 0x00, 0x00,
16702
+ // reserved
16703
+ 0x00, 0x01,
16704
+ // frame_count
16705
+ 0x12, 0x64, 0x61, 0x69, 0x6c,
16706
+ // dailymotion/hls.js
16707
+ 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,
16708
+ // compressorname
16709
+ 0x00, 0x18,
16710
+ // depth = 24
16711
+ 0x11, 0x11]),
16712
+ // pre_defined = -1
16713
+ hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
16714
+ // bufferSizeDB
16715
+ 0x00, 0x2d, 0xc6, 0xc0,
16716
+ // maxBitrate
16717
+ 0x00, 0x2d, 0xc6, 0xc0])),
16718
+ // avgBitrate
16719
+ MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
16720
+ // hSpacing
16721
+ hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
16722
+ // vSpacing
16723
+ vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
16724
+ };
16448
16725
  return MP4;
16449
16726
  }();
16450
16727
  MP4.types = void 0;
@@ -16831,9 +17108,9 @@
16831
17108
  var foundOverlap = delta < -1;
16832
17109
  if (foundHole || foundOverlap) {
16833
17110
  if (foundHole) {
16834
- logger.warn("AVC: " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
17111
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
16835
17112
  } else {
16836
- logger.warn("AVC: " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
17113
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
16837
17114
  }
16838
17115
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
16839
17116
  firstDTS = nextAvcDts;
@@ -16842,12 +17119,24 @@
16842
17119
  inputSamples[0].dts = firstDTS;
16843
17120
  inputSamples[0].pts = firstPTS;
16844
17121
  } else {
17122
+ var isPTSOrderRetained = true;
16845
17123
  for (var _i = 0; _i < inputSamples.length; _i++) {
16846
- if (inputSamples[_i].dts > firstPTS) {
17124
+ if (inputSamples[_i].dts > firstPTS && isPTSOrderRetained) {
16847
17125
  break;
16848
17126
  }
17127
+ var prevPTS = inputSamples[_i].pts;
16849
17128
  inputSamples[_i].dts -= delta;
16850
17129
  inputSamples[_i].pts -= delta;
17130
+
17131
+ // check to see if this sample's PTS order has changed
17132
+ // relative to the next one
17133
+ if (_i < inputSamples.length - 1) {
17134
+ var nextSamplePTS = inputSamples[_i + 1].pts;
17135
+ var currentSamplePTS = inputSamples[_i].pts;
17136
+ var currentOrder = nextSamplePTS <= currentSamplePTS;
17137
+ var prevOrder = nextSamplePTS <= prevPTS;
17138
+ isPTSOrderRetained = currentOrder == prevOrder;
17139
+ }
16851
17140
  }
16852
17141
  }
16853
17142
  logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms");
@@ -16995,7 +17284,7 @@
16995
17284
  }
16996
17285
  }
16997
17286
  }
16998
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
17287
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
16999
17288
  mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
17000
17289
  this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
17001
17290
  this.videoSampleDuration = mp4SampleDuration;
@@ -17130,7 +17419,7 @@
17130
17419
  logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap.");
17131
17420
  for (var j = 0; j < missing; j++) {
17132
17421
  var newStamp = Math.max(nextPts, 0);
17133
- var fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17422
+ var fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17134
17423
  if (!fillFrame) {
17135
17424
  logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
17136
17425
  fillFrame = sample.unit.subarray();
@@ -17258,7 +17547,7 @@
17258
17547
  // samples count of this segment's duration
17259
17548
  var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
17260
17549
  // silent frame
17261
- var silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17550
+ var silentFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17262
17551
  logger.warn('[mp4-remuxer]: remux empty Audio');
17263
17552
  // Can't remux if we can't generate a silent frame...
17264
17553
  if (!silentFrame) {
@@ -17645,13 +17934,15 @@
17645
17934
  duration = transmuxConfig.duration,
17646
17935
  initSegmentData = transmuxConfig.initSegmentData;
17647
17936
  var keyData = getEncryptionType(uintData, decryptdata);
17648
- if (keyData && keyData.method === 'AES-128') {
17937
+ if (keyData && isFullSegmentEncryption(keyData.method)) {
17649
17938
  var decrypter = this.getDecrypter();
17939
+ var aesMode = getAesModeFromFullSegmentMethod(keyData.method);
17940
+
17650
17941
  // Software decryption is synchronous; webCrypto is not
17651
17942
  if (decrypter.isSync()) {
17652
17943
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
17653
17944
  // data is handled in the flush() call
17654
- var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
17945
+ var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode);
17655
17946
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
17656
17947
  var loadingParts = chunkMeta.part > -1;
17657
17948
  if (loadingParts) {
@@ -17663,7 +17954,7 @@
17663
17954
  }
17664
17955
  uintData = new Uint8Array(decryptedData);
17665
17956
  } else {
17666
- this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(function (decryptedData) {
17957
+ this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(function (decryptedData) {
17667
17958
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
17668
17959
  // the decrypted data has been transmuxed
17669
17960
  var result = _this.push(decryptedData, null, chunkMeta);
@@ -18284,7 +18575,7 @@
18284
18575
  observer.on(Events.ERROR, forwardMessage);
18285
18576
 
18286
18577
  // forward logger events to main thread
18287
- var forwardWorkerLogs = function forwardWorkerLogs() {
18578
+ var forwardWorkerLogs = function forwardWorkerLogs(logger) {
18288
18579
  var _loop = function _loop(logFn) {
18289
18580
  var func = function func(message) {
18290
18581
  forwardMessage('workerLog', {
@@ -18305,8 +18596,8 @@
18305
18596
  {
18306
18597
  var config = JSON.parse(data.config);
18307
18598
  self.transmuxer = new Transmuxer(observer, data.typeSupported, config, data.vendor, data.id);
18308
- enableLogs(config.debug, data.id);
18309
- forwardWorkerLogs();
18599
+ var logger = enableLogs(config.debug, data.id);
18600
+ forwardWorkerLogs(logger);
18310
18601
  forwardMessage('init', null);
18311
18602
  break;
18312
18603
  }
@@ -18480,16 +18771,7 @@
18480
18771
  this.observer = new EventEmitter();
18481
18772
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
18482
18773
  this.observer.on(Events.ERROR, forwardMessage);
18483
- var MediaSource = getMediaSource(config.preferManagedMediaSource) || {
18484
- isTypeSupported: function isTypeSupported() {
18485
- return false;
18486
- }
18487
- };
18488
- var m2tsTypeSupported = {
18489
- mpeg: MediaSource.isTypeSupported('audio/mpeg'),
18490
- mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
18491
- ac3: false
18492
- };
18774
+ var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
18493
18775
 
18494
18776
  // navigator.vendor is not always available in Web Worker
18495
18777
  // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
@@ -18746,21 +19028,26 @@
18746
19028
  var MAX_START_GAP_JUMP = 2.0;
18747
19029
  var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
18748
19030
  var SKIP_BUFFER_RANGE_START = 0.05;
18749
- var GapController = /*#__PURE__*/function () {
19031
+ var GapController = /*#__PURE__*/function (_Logger) {
19032
+ _inheritsLoose(GapController, _Logger);
18750
19033
  function GapController(config, media, fragmentTracker, hls) {
18751
- this.config = void 0;
18752
- this.media = null;
18753
- this.fragmentTracker = void 0;
18754
- this.hls = void 0;
18755
- this.nudgeRetry = 0;
18756
- this.stallReported = false;
18757
- this.stalled = null;
18758
- this.moved = false;
18759
- this.seeking = false;
18760
- this.config = config;
18761
- this.media = media;
18762
- this.fragmentTracker = fragmentTracker;
18763
- this.hls = hls;
19034
+ var _this;
19035
+ _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
19036
+ _this.config = void 0;
19037
+ _this.media = null;
19038
+ _this.fragmentTracker = void 0;
19039
+ _this.hls = void 0;
19040
+ _this.nudgeRetry = 0;
19041
+ _this.stallReported = false;
19042
+ _this.stalled = null;
19043
+ _this.moved = false;
19044
+ _this.seeking = false;
19045
+ _this.ended = 0;
19046
+ _this.config = config;
19047
+ _this.media = media;
19048
+ _this.fragmentTracker = fragmentTracker;
19049
+ _this.hls = hls;
19050
+ return _this;
18764
19051
  }
18765
19052
  var _proto = GapController.prototype;
18766
19053
  _proto.destroy = function destroy() {
@@ -18775,7 +19062,7 @@
18775
19062
  *
18776
19063
  * @param lastCurrentTime - Previously read playhead position
18777
19064
  */;
18778
- _proto.poll = function poll(lastCurrentTime, activeFrag) {
19065
+ _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
18779
19066
  var config = this.config,
18780
19067
  media = this.media,
18781
19068
  stalled = this.stalled;
@@ -18790,6 +19077,7 @@
18790
19077
 
18791
19078
  // The playhead is moving, no-op
18792
19079
  if (currentTime !== lastCurrentTime) {
19080
+ this.ended = 0;
18793
19081
  this.moved = true;
18794
19082
  if (!seeking) {
18795
19083
  this.nudgeRetry = 0;
@@ -18798,7 +19086,7 @@
18798
19086
  // The playhead is now moving, but was previously stalled
18799
19087
  if (this.stallReported) {
18800
19088
  var _stalledDuration = self.performance.now() - stalled;
18801
- logger.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
19089
+ this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
18802
19090
  this.stallReported = false;
18803
19091
  }
18804
19092
  this.stalled = null;
@@ -18834,7 +19122,6 @@
18834
19122
  // Skip start gaps if we haven't played, but the last poll detected the start of a stall
18835
19123
  // The addition poll gives the browser a chance to jump the gap for us
18836
19124
  if (!this.moved && this.stalled !== null) {
18837
- var _level$details;
18838
19125
  // There is no playable buffer (seeked, waiting for buffer)
18839
19126
  var isBuffered = bufferInfo.len > 0;
18840
19127
  if (!isBuffered && !nextStart) {
@@ -18846,9 +19133,8 @@
18846
19133
  // When joining a live stream with audio tracks, account for live playlist window sliding by allowing
18847
19134
  // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
18848
19135
  // that begins over 1 target duration after the video start position.
18849
- var level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null;
18850
- var isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live;
18851
- var maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;
19136
+ var isLive = !!(levelDetails != null && levelDetails.live);
19137
+ var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
18852
19138
  var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
18853
19139
  if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
18854
19140
  if (!media.paused) {
@@ -18866,6 +19152,17 @@
18866
19152
  }
18867
19153
  var stalledDuration = tnow - stalled;
18868
19154
  if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
19155
+ // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
19156
+ if (state === State.ENDED && !(levelDetails && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
19157
+ if (stalledDuration < 1000 || this.ended) {
19158
+ return;
19159
+ }
19160
+ this.ended = currentTime;
19161
+ this.hls.trigger(Events.MEDIA_ENDED, {
19162
+ stalled: true
19163
+ });
19164
+ return;
19165
+ }
18869
19166
  // Report stalling after trying to fix
18870
19167
  this._reportStall(bufferInfo);
18871
19168
  if (!this.media) {
@@ -18907,7 +19204,7 @@
18907
19204
  // needs to cross some sort of threshold covering all source-buffers content
18908
19205
  // to start playing properly.
18909
19206
  if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
18910
- logger.warn('Trying to nudge playhead over buffer-hole');
19207
+ this.warn('Trying to nudge playhead over buffer-hole');
18911
19208
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
18912
19209
  // We only try to jump the hole if it's under the configured size
18913
19210
  // Reset stalled so to rearm watchdog timer
@@ -18929,7 +19226,7 @@
18929
19226
  // Report stalled error once
18930
19227
  this.stallReported = true;
18931
19228
  var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
18932
- logger.warn(error.message);
19229
+ this.warn(error.message);
18933
19230
  hls.trigger(Events.ERROR, {
18934
19231
  type: ErrorTypes.MEDIA_ERROR,
18935
19232
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -18993,7 +19290,7 @@
18993
19290
  }
18994
19291
  }
18995
19292
  var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
18996
- logger.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
19293
+ this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
18997
19294
  this.moved = true;
18998
19295
  this.stalled = null;
18999
19296
  media.currentTime = targetTime;
@@ -19032,7 +19329,7 @@
19032
19329
  var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
19033
19330
  // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
19034
19331
  var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
19035
- logger.warn(error.message);
19332
+ this.warn(error.message);
19036
19333
  media.currentTime = targetTime;
19037
19334
  hls.trigger(Events.ERROR, {
19038
19335
  type: ErrorTypes.MEDIA_ERROR,
@@ -19042,7 +19339,7 @@
19042
19339
  });
19043
19340
  } else {
19044
19341
  var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
19045
- logger.error(_error.message);
19342
+ this.error(_error.message);
19046
19343
  hls.trigger(Events.ERROR, {
19047
19344
  type: ErrorTypes.MEDIA_ERROR,
19048
19345
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -19052,14 +19349,14 @@
19052
19349
  }
19053
19350
  };
19054
19351
  return GapController;
19055
- }();
19352
+ }(Logger);
19056
19353
 
19057
19354
  var TICK_INTERVAL = 100; // how often to tick in ms
19058
19355
  var StreamController = /*#__PURE__*/function (_BaseStreamController) {
19059
19356
  _inheritsLoose(StreamController, _BaseStreamController);
19060
19357
  function StreamController(hls, fragmentTracker, keyLoader) {
19061
19358
  var _this;
19062
- _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN) || this;
19359
+ _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
19063
19360
  _this.audioCodecSwap = false;
19064
19361
  _this.gapController = null;
19065
19362
  _this.level = -1;
@@ -19067,27 +19364,43 @@
19067
19364
  _this.altAudio = false;
19068
19365
  _this.audioOnly = false;
19069
19366
  _this.fragPlaying = null;
19070
- _this.onvplaying = null;
19071
- _this.onvseeked = null;
19072
19367
  _this.fragLastKbps = 0;
19073
19368
  _this.couldBacktrack = false;
19074
19369
  _this.backtrackFragment = null;
19075
19370
  _this.audioCodecSwitch = false;
19076
19371
  _this.videoBuffer = null;
19077
- _this._registerListeners();
19372
+ _this.onMediaPlaying = function () {
19373
+ // tick to speed up FRAG_CHANGED triggering
19374
+ _this.tick();
19375
+ };
19376
+ _this.onMediaSeeked = function () {
19377
+ var media = _this.media;
19378
+ var currentTime = media ? media.currentTime : null;
19379
+ if (isFiniteNumber(currentTime)) {
19380
+ _this.log("Media seeked to " + currentTime.toFixed(3));
19381
+ }
19382
+
19383
+ // If seeked was issued before buffer was appended do not tick immediately
19384
+ var bufferInfo = _this.getMainFwdBufferInfo();
19385
+ if (bufferInfo === null || bufferInfo.len === 0) {
19386
+ _this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19387
+ return;
19388
+ }
19389
+
19390
+ // tick to speed up FRAG_CHANGED triggering
19391
+ _this.tick();
19392
+ };
19393
+ _this.registerListeners();
19078
19394
  return _this;
19079
19395
  }
19080
19396
  var _proto = StreamController.prototype;
19081
- _proto._registerListeners = function _registerListeners() {
19397
+ _proto.registerListeners = function registerListeners() {
19398
+ _BaseStreamController.prototype.registerListeners.call(this);
19082
19399
  var hls = this.hls;
19083
- hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19084
- hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19085
- hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19086
19400
  hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19087
19401
  hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
19088
19402
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19089
19403
  hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19090
- hls.on(Events.ERROR, this.onError, this);
19091
19404
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19092
19405
  hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19093
19406
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19095,15 +19408,12 @@
19095
19408
  hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
19096
19409
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19097
19410
  };
19098
- _proto._unregisterListeners = function _unregisterListeners() {
19411
+ _proto.unregisterListeners = function unregisterListeners() {
19412
+ _BaseStreamController.prototype.unregisterListeners.call(this);
19099
19413
  var hls = this.hls;
19100
- hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19101
- hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19102
- hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19103
19414
  hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19104
19415
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19105
19416
  hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19106
- hls.off(Events.ERROR, this.onError, this);
19107
19417
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19108
19418
  hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19109
19419
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19112,7 +19422,9 @@
19112
19422
  hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19113
19423
  };
19114
19424
  _proto.onHandlerDestroying = function onHandlerDestroying() {
19115
- this._unregisterListeners();
19425
+ // @ts-ignore
19426
+ this.onMediaPlaying = this.onMediaSeeked = null;
19427
+ this.unregisterListeners();
19116
19428
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
19117
19429
  };
19118
19430
  _proto.startLoad = function startLoad(startPosition) {
@@ -19427,18 +19739,15 @@
19427
19739
  _proto.onMediaAttached = function onMediaAttached(event, data) {
19428
19740
  _BaseStreamController.prototype.onMediaAttached.call(this, event, data);
19429
19741
  var media = data.media;
19430
- this.onvplaying = this.onMediaPlaying.bind(this);
19431
- this.onvseeked = this.onMediaSeeked.bind(this);
19432
- media.addEventListener('playing', this.onvplaying);
19433
- media.addEventListener('seeked', this.onvseeked);
19742
+ media.addEventListener('playing', this.onMediaPlaying);
19743
+ media.addEventListener('seeked', this.onMediaSeeked);
19434
19744
  this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
19435
19745
  };
19436
19746
  _proto.onMediaDetaching = function onMediaDetaching() {
19437
19747
  var media = this.media;
19438
- if (media && this.onvplaying && this.onvseeked) {
19439
- media.removeEventListener('playing', this.onvplaying);
19440
- media.removeEventListener('seeked', this.onvseeked);
19441
- this.onvplaying = this.onvseeked = null;
19748
+ if (media) {
19749
+ media.removeEventListener('playing', this.onMediaPlaying);
19750
+ media.removeEventListener('seeked', this.onMediaSeeked);
19442
19751
  this.videoBuffer = null;
19443
19752
  }
19444
19753
  this.fragPlaying = null;
@@ -19448,27 +19757,6 @@
19448
19757
  }
19449
19758
  _BaseStreamController.prototype.onMediaDetaching.call(this);
19450
19759
  };
19451
- _proto.onMediaPlaying = function onMediaPlaying() {
19452
- // tick to speed up FRAG_CHANGED triggering
19453
- this.tick();
19454
- };
19455
- _proto.onMediaSeeked = function onMediaSeeked() {
19456
- var media = this.media;
19457
- var currentTime = media ? media.currentTime : null;
19458
- if (isFiniteNumber(currentTime)) {
19459
- this.log("Media seeked to " + currentTime.toFixed(3));
19460
- }
19461
-
19462
- // If seeked was issued before buffer was appended do not tick immediately
19463
- var bufferInfo = this.getMainFwdBufferInfo();
19464
- if (bufferInfo === null || bufferInfo.len === 0) {
19465
- this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19466
- return;
19467
- }
19468
-
19469
- // tick to speed up FRAG_CHANGED triggering
19470
- this.tick();
19471
- };
19472
19760
  _proto.onManifestLoading = function onManifestLoading() {
19473
19761
  // reset buffer on manifest loading
19474
19762
  this.log('Trigger BUFFER_RESET');
@@ -19749,8 +20037,10 @@
19749
20037
  }
19750
20038
  if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
19751
20039
  // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
19752
- var activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
19753
- gapController.poll(this.lastCurrentTime, activeFrag);
20040
+ var state = this.state;
20041
+ var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
20042
+ var levelDetails = this.getLevelDetails();
20043
+ gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
19754
20044
  }
19755
20045
  this.lastCurrentTime = media.currentTime;
19756
20046
  };
@@ -20215,9 +20505,12 @@
20215
20505
  * The configuration object provided on player instantiation.
20216
20506
  */
20217
20507
  this.userConfig = void 0;
20508
+ /**
20509
+ * The logger functions used by this player instance, configured on player instantiation.
20510
+ */
20511
+ this.logger = void 0;
20218
20512
  this.coreComponents = void 0;
20219
20513
  this.networkControllers = void 0;
20220
- this.started = false;
20221
20514
  this._emitter = new EventEmitter();
20222
20515
  this._autoLevelCapping = -1;
20223
20516
  this._maxHdcpLevel = null;
@@ -20234,11 +20527,11 @@
20234
20527
  this._media = null;
20235
20528
  this.url = null;
20236
20529
  this.triggeringException = void 0;
20237
- enableLogs(userConfig.debug || false, 'Hls instance');
20238
- var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
20530
+ var logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance');
20531
+ var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
20239
20532
  this.userConfig = userConfig;
20240
20533
  if (config.progressive) {
20241
- enableStreamingMode(config);
20534
+ enableStreamingMode(config, logger);
20242
20535
  }
20243
20536
 
20244
20537
  // core controllers and network loaders
@@ -20346,7 +20639,7 @@
20346
20639
  try {
20347
20640
  return this.emit(event, event, eventObject);
20348
20641
  } catch (error) {
20349
- logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20642
+ this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20350
20643
  // Prevent recursion in error event handlers that throw #5497
20351
20644
  if (!this.triggeringException) {
20352
20645
  this.triggeringException = true;
@@ -20372,7 +20665,7 @@
20372
20665
  * Dispose of the instance
20373
20666
  */;
20374
20667
  _proto.destroy = function destroy() {
20375
- logger.log('destroy');
20668
+ this.logger.log('destroy');
20376
20669
  this.trigger(Events.DESTROYING, undefined);
20377
20670
  this.detachMedia();
20378
20671
  this.removeAllListeners();
@@ -20397,7 +20690,7 @@
20397
20690
  * Attaches Hls.js to a media element
20398
20691
  */;
20399
20692
  _proto.attachMedia = function attachMedia(media) {
20400
- logger.log('attachMedia');
20693
+ this.logger.log('attachMedia');
20401
20694
  this._media = media;
20402
20695
  this.trigger(Events.MEDIA_ATTACHING, {
20403
20696
  media: media
@@ -20408,7 +20701,7 @@
20408
20701
  * Detach Hls.js from the media
20409
20702
  */;
20410
20703
  _proto.detachMedia = function detachMedia() {
20411
- logger.log('detachMedia');
20704
+ this.logger.log('detachMedia');
20412
20705
  this.trigger(Events.MEDIA_DETACHING, undefined);
20413
20706
  this._media = null;
20414
20707
  }
@@ -20425,7 +20718,7 @@
20425
20718
  });
20426
20719
  this._autoLevelCapping = -1;
20427
20720
  this._maxHdcpLevel = null;
20428
- logger.log("loadSource:" + loadingSource);
20721
+ this.logger.log("loadSource:" + loadingSource);
20429
20722
  if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
20430
20723
  this.detachMedia();
20431
20724
  this.attachMedia(media);
@@ -20447,8 +20740,7 @@
20447
20740
  if (startPosition === void 0) {
20448
20741
  startPosition = -1;
20449
20742
  }
20450
- logger.log("startLoad(" + startPosition + ")");
20451
- this.started = true;
20743
+ this.logger.log("startLoad(" + startPosition + ")");
20452
20744
  this.networkControllers.forEach(function (controller) {
20453
20745
  controller.startLoad(startPosition);
20454
20746
  });
@@ -20458,34 +20750,31 @@
20458
20750
  * Stop loading of any stream data.
20459
20751
  */;
20460
20752
  _proto.stopLoad = function stopLoad() {
20461
- logger.log('stopLoad');
20462
- this.started = false;
20753
+ this.logger.log('stopLoad');
20463
20754
  this.networkControllers.forEach(function (controller) {
20464
20755
  controller.stopLoad();
20465
20756
  });
20466
20757
  }
20467
20758
 
20468
20759
  /**
20469
- * Resumes stream controller segment loading if previously started.
20760
+ * Resumes stream controller segment loading after `pauseBuffering` has been called.
20470
20761
  */;
20471
20762
  _proto.resumeBuffering = function resumeBuffering() {
20472
- if (this.started) {
20473
- this.networkControllers.forEach(function (controller) {
20474
- if ('fragmentLoader' in controller) {
20475
- controller.startLoad(-1);
20476
- }
20477
- });
20478
- }
20763
+ this.networkControllers.forEach(function (controller) {
20764
+ if (controller.resumeBuffering) {
20765
+ controller.resumeBuffering();
20766
+ }
20767
+ });
20479
20768
  }
20480
20769
 
20481
20770
  /**
20482
- * Stops stream controller segment loading without changing 'started' state like stopLoad().
20771
+ * Prevents stream controller from loading new segments until `resumeBuffering` is called.
20483
20772
  * This allows for media buffering to be paused without interupting playlist loading.
20484
20773
  */;
20485
20774
  _proto.pauseBuffering = function pauseBuffering() {
20486
20775
  this.networkControllers.forEach(function (controller) {
20487
- if ('fragmentLoader' in controller) {
20488
- controller.stopLoad();
20776
+ if (controller.pauseBuffering) {
20777
+ controller.pauseBuffering();
20489
20778
  }
20490
20779
  });
20491
20780
  }
@@ -20494,7 +20783,7 @@
20494
20783
  * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
20495
20784
  */;
20496
20785
  _proto.swapAudioCodec = function swapAudioCodec() {
20497
- logger.log('swapAudioCodec');
20786
+ this.logger.log('swapAudioCodec');
20498
20787
  this.streamController.swapAudioCodec();
20499
20788
  }
20500
20789
 
@@ -20505,7 +20794,7 @@
20505
20794
  * Automatic recovery of media-errors by this process is configurable.
20506
20795
  */;
20507
20796
  _proto.recoverMediaError = function recoverMediaError() {
20508
- logger.log('recoverMediaError');
20797
+ this.logger.log('recoverMediaError');
20509
20798
  var media = this._media;
20510
20799
  this.detachMedia();
20511
20800
  if (media) {
@@ -20560,7 +20849,7 @@
20560
20849
  * 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.
20561
20850
  */,
20562
20851
  set: function set(newLevel) {
20563
- logger.log("set currentLevel:" + newLevel);
20852
+ this.logger.log("set currentLevel:" + newLevel);
20564
20853
  this.levelController.manualLevel = newLevel;
20565
20854
  this.streamController.immediateLevelSwitch();
20566
20855
  }
@@ -20581,7 +20870,7 @@
20581
20870
  * @param newLevel - Pass -1 for automatic level selection
20582
20871
  */,
20583
20872
  set: function set(newLevel) {
20584
- logger.log("set nextLevel:" + newLevel);
20873
+ this.logger.log("set nextLevel:" + newLevel);
20585
20874
  this.levelController.manualLevel = newLevel;
20586
20875
  this.streamController.nextLevelSwitch();
20587
20876
  }
@@ -20602,7 +20891,7 @@
20602
20891
  * @param newLevel - Pass -1 for automatic level selection
20603
20892
  */,
20604
20893
  set: function set(newLevel) {
20605
- logger.log("set loadLevel:" + newLevel);
20894
+ this.logger.log("set loadLevel:" + newLevel);
20606
20895
  this.levelController.manualLevel = newLevel;
20607
20896
  }
20608
20897
 
@@ -20637,7 +20926,7 @@
20637
20926
  * Sets "first-level", see getter.
20638
20927
  */,
20639
20928
  set: function set(newLevel) {
20640
- logger.log("set firstLevel:" + newLevel);
20929
+ this.logger.log("set firstLevel:" + newLevel);
20641
20930
  this.levelController.firstLevel = newLevel;
20642
20931
  }
20643
20932
 
@@ -20664,7 +20953,7 @@
20664
20953
  * (determined from download of first segment)
20665
20954
  */,
20666
20955
  set: function set(newLevel) {
20667
- logger.log("set startLevel:" + newLevel);
20956
+ this.logger.log("set startLevel:" + newLevel);
20668
20957
  // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
20669
20958
  if (newLevel !== -1) {
20670
20959
  newLevel = Math.max(newLevel, this.minAutoLevel);
@@ -20717,7 +21006,7 @@
20717
21006
  */
20718
21007
  function set(newLevel) {
20719
21008
  if (this._autoLevelCapping !== newLevel) {
20720
- logger.log("set autoLevelCapping:" + newLevel);
21009
+ this.logger.log("set autoLevelCapping:" + newLevel);
20721
21010
  this._autoLevelCapping = newLevel;
20722
21011
  this.levelController.checkMaxAutoUpdated();
20723
21012
  }
@@ -21042,7 +21331,7 @@
21042
21331
  * Get the video-dev/hls.js package version.
21043
21332
  */
21044
21333
  function get() {
21045
- return "1.5.6";
21334
+ return "1.5.7-0.canary.10014";
21046
21335
  }
21047
21336
  }, {
21048
21337
  key: "Events",