hls.js 1.5.5 → 1.5.6-0.canary.10001

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 +2075 -1166
  5. package/dist/hls.js.d.ts +65 -50
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1148 -859
  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 +984 -696
  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 +1757 -863
  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 +20 -20
  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 +25 -32
  37. package/src/controller/subtitle-stream-controller.ts +13 -14
  38. package/src/controller/subtitle-track-controller.ts +5 -3
  39. package/src/controller/timeline-controller.ts +23 -30
  40. package/src/crypt/aes-crypto.ts +21 -2
  41. package/src/crypt/decrypter-aes-mode.ts +4 -0
  42. package/src/crypt/decrypter.ts +32 -18
  43. package/src/crypt/fast-aes-key.ts +24 -5
  44. package/src/demux/audio/adts.ts +9 -4
  45. package/src/demux/sample-aes.ts +2 -0
  46. package/src/demux/transmuxer-interface.ts +4 -12
  47. package/src/demux/transmuxer-worker.ts +4 -4
  48. package/src/demux/transmuxer.ts +16 -3
  49. package/src/demux/tsdemuxer.ts +71 -37
  50. package/src/demux/video/avc-video-parser.ts +208 -119
  51. package/src/demux/video/base-video-parser.ts +134 -2
  52. package/src/demux/video/exp-golomb.ts +0 -208
  53. package/src/demux/video/hevc-video-parser.ts +746 -0
  54. package/src/events.ts +7 -0
  55. package/src/hls.ts +42 -34
  56. package/src/loader/fragment-loader.ts +9 -2
  57. package/src/loader/key-loader.ts +2 -0
  58. package/src/loader/level-key.ts +10 -9
  59. package/src/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.5");
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.6-0.canary.10001");
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.
@@ -2626,13 +2652,13 @@
2626
2652
  this.keyFormatVersions = formatversions;
2627
2653
  this.iv = iv;
2628
2654
  this.encrypted = method ? method !== 'NONE' : false;
2629
- this.isCommonEncryption = this.encrypted && method !== 'AES-128';
2655
+ this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method);
2630
2656
  }
2631
2657
  var _proto = LevelKey.prototype;
2632
2658
  _proto.isSupported = function isSupported() {
2633
2659
  // If it's Segment encryption or No encryption, just select that key system
2634
2660
  if (this.method) {
2635
- if (this.method === 'AES-128' || this.method === 'NONE') {
2661
+ if (isFullSegmentEncryption(this.method) || this.method === 'NONE') {
2636
2662
  return true;
2637
2663
  }
2638
2664
  if (this.keyFormat === 'identity') {
@@ -2646,14 +2672,13 @@
2646
2672
  if (!this.encrypted || !this.uri) {
2647
2673
  return null;
2648
2674
  }
2649
- if (this.method === 'AES-128' && this.uri && !this.iv) {
2675
+ if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) {
2650
2676
  if (typeof sn !== 'number') {
2651
2677
  // We are fetching decryption data for a initialization segment
2652
- // If the segment was encrypted with AES-128
2678
+ // If the segment was encrypted with AES-128/256
2653
2679
  // It must have an IV defined. We cannot substitute the Segment Number in.
2654
- if (this.method === 'AES-128' && !this.iv) {
2655
- logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
2656
- }
2680
+ logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue");
2681
+
2657
2682
  // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
2658
2683
  sn = 0;
2659
2684
  }
@@ -2815,23 +2840,28 @@
2815
2840
  if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
2816
2841
  return CODEC_COMPATIBLE_NAMES[lowerCaseCodec];
2817
2842
  }
2818
-
2819
- // Idealy fLaC and Opus would be first (spec-compliant) but
2820
- // some browsers will report that fLaC is supported then fail.
2821
- // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
2822
2843
  var codecsToCheck = {
2844
+ // Idealy fLaC and Opus would be first (spec-compliant) but
2845
+ // some browsers will report that fLaC is supported then fail.
2846
+ // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
2823
2847
  flac: ['flac', 'fLaC', 'FLAC'],
2824
- opus: ['opus', 'Opus']
2848
+ opus: ['opus', 'Opus'],
2849
+ // Replace audio codec info if browser does not support mp4a.40.34,
2850
+ // and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
2851
+ 'mp4a.40.34': ['mp3']
2825
2852
  }[lowerCaseCodec];
2826
2853
  for (var i = 0; i < codecsToCheck.length; i++) {
2854
+ var _getMediaSource;
2827
2855
  if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) {
2828
2856
  CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
2829
2857
  return codecsToCheck[i];
2858
+ } else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) {
2859
+ return '';
2830
2860
  }
2831
2861
  }
2832
2862
  return lowerCaseCodec;
2833
2863
  }
2834
- var AUDIO_CODEC_REGEXP = /flac|opus/i;
2864
+ var AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
2835
2865
  function getCodecCompatibleName(codec, preferManagedMediaSource) {
2836
2866
  if (preferManagedMediaSource === void 0) {
2837
2867
  preferManagedMediaSource = true;
@@ -2859,6 +2889,18 @@
2859
2889
  }
2860
2890
  return codec;
2861
2891
  }
2892
+ function getM2TSSupportedAudioTypes(preferManagedMediaSource) {
2893
+ var MediaSource = getMediaSource(preferManagedMediaSource) || {
2894
+ isTypeSupported: function isTypeSupported() {
2895
+ return false;
2896
+ }
2897
+ };
2898
+ return {
2899
+ mpeg: MediaSource.isTypeSupported('audio/mpeg'),
2900
+ mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
2901
+ ac3: false
2902
+ };
2903
+ }
2862
2904
 
2863
2905
  var MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g;
2864
2906
  var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;
@@ -3659,10 +3701,10 @@
3659
3701
  var loaderContext = loader.context;
3660
3702
  if (loaderContext && loaderContext.url === context.url && loaderContext.level === context.level) {
3661
3703
  // same URL can't overlap
3662
- logger.trace('[playlist-loader]: playlist request ongoing');
3704
+ this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
3663
3705
  return;
3664
3706
  }
3665
- logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
3707
+ this.hls.logger.log("[playlist-loader]: aborting previous loader for type: " + context.type);
3666
3708
  loader.abort();
3667
3709
  }
3668
3710
 
@@ -3772,7 +3814,7 @@
3772
3814
  // alt audio rendition in which quality levels (main)
3773
3815
  // contains both audio+video. but with mixed audio track not signaled
3774
3816
  if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
3775
- logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
3817
+ this.hls.logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one');
3776
3818
  audioTracks.unshift({
3777
3819
  type: 'main',
3778
3820
  name: 'main',
@@ -3872,7 +3914,7 @@
3872
3914
  message += " id: " + context.id + " group-id: \"" + context.groupId + "\"";
3873
3915
  }
3874
3916
  var error = new Error(message);
3875
- logger.warn("[playlist-loader]: " + message);
3917
+ this.hls.logger.warn("[playlist-loader]: " + message);
3876
3918
  var details = ErrorDetails.UNKNOWN;
3877
3919
  var fatal = false;
3878
3920
  var loader = this.getInternalLoader(context);
@@ -4433,8 +4475,43 @@
4433
4475
  this.currentTime = 0;
4434
4476
  this.stallCount = 0;
4435
4477
  this._latency = null;
4436
- this.timeupdateHandler = function () {
4437
- return _this.timeupdate();
4478
+ this.onTimeupdate = function () {
4479
+ var media = _this.media,
4480
+ levelDetails = _this.levelDetails;
4481
+ if (!media || !levelDetails) {
4482
+ return;
4483
+ }
4484
+ _this.currentTime = media.currentTime;
4485
+ var latency = _this.computeLatency();
4486
+ if (latency === null) {
4487
+ return;
4488
+ }
4489
+ _this._latency = latency;
4490
+
4491
+ // Adapt playbackRate to meet target latency in low-latency mode
4492
+ var _this$config = _this.config,
4493
+ lowLatencyMode = _this$config.lowLatencyMode,
4494
+ maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
4495
+ if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4496
+ return;
4497
+ }
4498
+ var targetLatency = _this.targetLatency;
4499
+ if (targetLatency === null) {
4500
+ return;
4501
+ }
4502
+ var distanceFromTarget = latency - targetLatency;
4503
+ // Only adjust playbackRate when within one target duration of targetLatency
4504
+ // and more than one second from under-buffering.
4505
+ // Playback further than one target duration from target can be considered DVR playback.
4506
+ var liveMinLatencyDuration = Math.min(_this.maxLatency, targetLatency + levelDetails.targetduration);
4507
+ var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4508
+ if (inLiveRange && distanceFromTarget > 0.05 && _this.forwardBufferLength > 1) {
4509
+ var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4510
+ var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - _this.edgeStalled)) * 20) / 20;
4511
+ media.playbackRate = Math.min(max, Math.max(1, rate));
4512
+ } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4513
+ media.playbackRate = 1;
4514
+ }
4438
4515
  };
4439
4516
  this.hls = hls;
4440
4517
  this.config = hls.config;
@@ -4446,7 +4523,7 @@
4446
4523
  this.onMediaDetaching();
4447
4524
  this.levelDetails = null;
4448
4525
  // @ts-ignore
4449
- this.hls = this.timeupdateHandler = null;
4526
+ this.hls = null;
4450
4527
  };
4451
4528
  _proto.registerListeners = function registerListeners() {
4452
4529
  this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
@@ -4464,11 +4541,11 @@
4464
4541
  };
4465
4542
  _proto.onMediaAttached = function onMediaAttached(event, data) {
4466
4543
  this.media = data.media;
4467
- this.media.addEventListener('timeupdate', this.timeupdateHandler);
4544
+ this.media.addEventListener('timeupdate', this.onTimeupdate);
4468
4545
  };
4469
4546
  _proto.onMediaDetaching = function onMediaDetaching() {
4470
4547
  if (this.media) {
4471
- this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4548
+ this.media.removeEventListener('timeupdate', this.onTimeupdate);
4472
4549
  this.media = null;
4473
4550
  }
4474
4551
  };
@@ -4481,10 +4558,10 @@
4481
4558
  var details = _ref.details;
4482
4559
  this.levelDetails = details;
4483
4560
  if (details.advanced) {
4484
- this.timeupdate();
4561
+ this.onTimeupdate();
4485
4562
  }
4486
4563
  if (!details.live && this.media) {
4487
- this.media.removeEventListener('timeupdate', this.timeupdateHandler);
4564
+ this.media.removeEventListener('timeupdate', this.onTimeupdate);
4488
4565
  }
4489
4566
  };
4490
4567
  _proto.onError = function onError(event, data) {
@@ -4494,45 +4571,7 @@
4494
4571
  }
4495
4572
  this.stallCount++;
4496
4573
  if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
4497
- logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency');
4498
- }
4499
- };
4500
- _proto.timeupdate = function timeupdate() {
4501
- var media = this.media,
4502
- levelDetails = this.levelDetails;
4503
- if (!media || !levelDetails) {
4504
- return;
4505
- }
4506
- this.currentTime = media.currentTime;
4507
- var latency = this.computeLatency();
4508
- if (latency === null) {
4509
- return;
4510
- }
4511
- this._latency = latency;
4512
-
4513
- // Adapt playbackRate to meet target latency in low-latency mode
4514
- var _this$config = this.config,
4515
- lowLatencyMode = _this$config.lowLatencyMode,
4516
- maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate;
4517
- if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
4518
- return;
4519
- }
4520
- var targetLatency = this.targetLatency;
4521
- if (targetLatency === null) {
4522
- return;
4523
- }
4524
- var distanceFromTarget = latency - targetLatency;
4525
- // Only adjust playbackRate when within one target duration of targetLatency
4526
- // and more than one second from under-buffering.
4527
- // Playback further than one target duration from target can be considered DVR playback.
4528
- var liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
4529
- var inLiveRange = distanceFromTarget < liveMinLatencyDuration;
4530
- if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
4531
- var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate));
4532
- var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
4533
- media.playbackRate = Math.min(max, Math.max(1, rate));
4534
- } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
4535
- media.playbackRate = 1;
4574
+ this.hls.logger.warn('[latency-controller]: Stall detected, adjusting target latency');
4536
4575
  }
4537
4576
  };
4538
4577
  _proto.estimateLiveEdge = function estimateLiveEdge() {
@@ -5440,19 +5479,17 @@
5440
5479
  MoveAllAlternatesMatchingHDCP: 2,
5441
5480
  SwitchToSDR: 4
5442
5481
  }; // Reserved for future use
5443
- var ErrorController = /*#__PURE__*/function () {
5482
+ var ErrorController = /*#__PURE__*/function (_Logger) {
5483
+ _inheritsLoose(ErrorController, _Logger);
5444
5484
  function ErrorController(hls) {
5445
- this.hls = void 0;
5446
- this.playlistError = 0;
5447
- this.penalizedRenditions = {};
5448
- this.log = void 0;
5449
- this.warn = void 0;
5450
- this.error = void 0;
5451
- this.hls = hls;
5452
- this.log = logger.log.bind(logger, "[info]:");
5453
- this.warn = logger.warn.bind(logger, "[warning]:");
5454
- this.error = logger.error.bind(logger, "[error]:");
5455
- this.registerListeners();
5485
+ var _this;
5486
+ _this = _Logger.call(this, 'error-controller', hls.logger) || this;
5487
+ _this.hls = void 0;
5488
+ _this.playlistError = 0;
5489
+ _this.penalizedRenditions = {};
5490
+ _this.hls = hls;
5491
+ _this.registerListeners();
5492
+ return _this;
5456
5493
  }
5457
5494
  var _proto = ErrorController.prototype;
5458
5495
  _proto.registerListeners = function registerListeners() {
@@ -5808,19 +5845,19 @@
5808
5845
  }
5809
5846
  };
5810
5847
  return ErrorController;
5811
- }();
5848
+ }(Logger);
5812
5849
 
5813
- var BasePlaylistController = /*#__PURE__*/function () {
5850
+ var BasePlaylistController = /*#__PURE__*/function (_Logger) {
5851
+ _inheritsLoose(BasePlaylistController, _Logger);
5814
5852
  function BasePlaylistController(hls, logPrefix) {
5815
- this.hls = void 0;
5816
- this.timer = -1;
5817
- this.requestScheduled = -1;
5818
- this.canLoad = false;
5819
- this.log = void 0;
5820
- this.warn = void 0;
5821
- this.log = logger.log.bind(logger, logPrefix + ":");
5822
- this.warn = logger.warn.bind(logger, logPrefix + ":");
5823
- this.hls = hls;
5853
+ var _this;
5854
+ _this = _Logger.call(this, logPrefix, hls.logger) || this;
5855
+ _this.hls = void 0;
5856
+ _this.timer = -1;
5857
+ _this.requestScheduled = -1;
5858
+ _this.canLoad = false;
5859
+ _this.hls = hls;
5860
+ return _this;
5824
5861
  }
5825
5862
  var _proto = BasePlaylistController.prototype;
5826
5863
  _proto.destroy = function destroy() {
@@ -5853,7 +5890,7 @@
5853
5890
  try {
5854
5891
  uri = new self.URL(attr.URI, previous.url).href;
5855
5892
  } catch (error) {
5856
- logger.warn("Could not construct new URL for Rendition Report: " + error);
5893
+ this.warn("Could not construct new URL for Rendition Report: " + error);
5857
5894
  uri = attr.URI || '';
5858
5895
  }
5859
5896
  // Use exact match. Otherwise, the last partial match, if any, will be used
@@ -5892,7 +5929,7 @@
5892
5929
  return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist);
5893
5930
  };
5894
5931
  _proto.playlistLoaded = function playlistLoaded(index, data, previousDetails) {
5895
- var _this = this;
5932
+ var _this2 = this;
5896
5933
  var details = data.details,
5897
5934
  stats = data.stats;
5898
5935
 
@@ -5939,7 +5976,12 @@
5939
5976
  var cdnAge = lastAdvanced + details.ageHeader;
5940
5977
  var currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5);
5941
5978
  if (currentGoal > 0) {
5942
- if (previousDetails && currentGoal > previousDetails.tuneInGoal) {
5979
+ if (cdnAge > details.targetduration * 3) {
5980
+ // Omit segment and part directives when the last response was more than 3 target durations ago,
5981
+ this.log("Playlist last advanced " + lastAdvanced.toFixed(2) + "s ago. Omitting segment and part directives.");
5982
+ msn = undefined;
5983
+ part = undefined;
5984
+ } else if (previousDetails != null && previousDetails.tuneInGoal && cdnAge - details.partTarget > previousDetails.tuneInGoal) {
5943
5985
  // If we attempted to get the next or latest playlist update, but currentGoal increased,
5944
5986
  // then we either can't catchup, or the "age" header cannot be trusted.
5945
5987
  this.warn("CDN Tune-in goal increased from: " + previousDetails.tuneInGoal + " to: " + currentGoal + " with playlist age: " + details.age);
@@ -5997,7 +6039,7 @@
5997
6039
  // );
5998
6040
 
5999
6041
  this.timer = self.setTimeout(function () {
6000
- return _this.loadPlaylist(deliveryDirectives);
6042
+ return _this2.loadPlaylist(deliveryDirectives);
6001
6043
  }, estimatedTimeUntilUpdate);
6002
6044
  } else {
6003
6045
  this.clearTimer();
@@ -6013,7 +6055,7 @@
6013
6055
  return new HlsUrlParameters(msn, part, skip);
6014
6056
  };
6015
6057
  _proto.checkRetry = function checkRetry(errorEvent) {
6016
- var _this2 = this;
6058
+ var _this3 = this;
6017
6059
  var errorDetails = errorEvent.details;
6018
6060
  var isTimeout = isTimeoutError(errorEvent);
6019
6061
  var errorAction = errorEvent.errorAction;
@@ -6037,7 +6079,7 @@
6037
6079
  var delay = getRetryDelay(retryConfig, retryCount);
6038
6080
  // Schedule level/track reload
6039
6081
  this.timer = self.setTimeout(function () {
6040
- return _this2.loadPlaylist();
6082
+ return _this3.loadPlaylist();
6041
6083
  }, delay);
6042
6084
  this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" in " + delay + "ms");
6043
6085
  }
@@ -6048,7 +6090,7 @@
6048
6090
  return retry;
6049
6091
  };
6050
6092
  return BasePlaylistController;
6051
- }();
6093
+ }(Logger);
6052
6094
 
6053
6095
  /*
6054
6096
  * compute an Exponential Weighted moving average
@@ -6422,30 +6464,33 @@
6422
6464
  }, {});
6423
6465
  }
6424
6466
 
6425
- var AbrController = /*#__PURE__*/function () {
6467
+ var AbrController = /*#__PURE__*/function (_Logger) {
6468
+ _inheritsLoose(AbrController, _Logger);
6426
6469
  function AbrController(_hls) {
6427
- var _this = this;
6428
- this.hls = void 0;
6429
- this.lastLevelLoadSec = 0;
6430
- this.lastLoadedFragLevel = -1;
6431
- this.firstSelection = -1;
6432
- this._nextAutoLevel = -1;
6433
- this.nextAutoLevelKey = '';
6434
- this.audioTracksByGroup = null;
6435
- this.codecTiers = null;
6436
- this.timer = -1;
6437
- this.fragCurrent = null;
6438
- this.partCurrent = null;
6439
- this.bitrateTestDelay = 0;
6440
- this.bwEstimator = void 0;
6470
+ var _this;
6471
+ _this = _Logger.call(this, 'abr', _hls.logger) || this;
6472
+ _this.hls = void 0;
6473
+ _this.lastLevelLoadSec = 0;
6474
+ _this.lastLoadedFragLevel = -1;
6475
+ _this.firstSelection = -1;
6476
+ _this._nextAutoLevel = -1;
6477
+ _this.nextAutoLevelKey = '';
6478
+ _this.audioTracksByGroup = null;
6479
+ _this.codecTiers = null;
6480
+ _this.timer = -1;
6481
+ _this.fragCurrent = null;
6482
+ _this.partCurrent = null;
6483
+ _this.bitrateTestDelay = 0;
6484
+ _this.bwEstimator = void 0;
6441
6485
  /*
6442
6486
  This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
6443
6487
  quickly enough to prevent underbuffering
6444
6488
  */
6445
- this._abandonRulesCheck = function () {
6446
- var frag = _this.fragCurrent,
6447
- part = _this.partCurrent,
6448
- hls = _this.hls;
6489
+ _this._abandonRulesCheck = function () {
6490
+ var _assertThisInitialize = _assertThisInitialized(_this),
6491
+ frag = _assertThisInitialize.fragCurrent,
6492
+ part = _assertThisInitialize.partCurrent,
6493
+ hls = _assertThisInitialize.hls;
6449
6494
  var autoLevelEnabled = hls.autoLevelEnabled,
6450
6495
  media = hls.media;
6451
6496
  if (!frag || !media) {
@@ -6534,21 +6579,22 @@
6534
6579
  _this.resetEstimator(nextLoadLevelBitrate);
6535
6580
  }
6536
6581
  _this.clearTimer();
6537
- logger.warn("[abr] Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " is loading too slowly;\n Time to underbuffer: " + bufferStarvationDelay.toFixed(3) + " s\n Estimated load time for current fragment: " + fragLoadedDelay.toFixed(3) + " s\n Estimated load time for down switch fragment: " + fragLevelNextLoadedDelay.toFixed(3) + " s\n TTFB estimate: " + (ttfb | 0) + " ms\n Current BW estimate: " + (isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown') + " bps\n New BW estimate: " + (_this.getBwEstimate() | 0) + " bps\n Switching to level " + nextLoadLevel + " @ " + (nextLoadLevelBitrate | 0) + " bps");
6582
+ _this.warn("Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " is loading too slowly;\n Time to underbuffer: " + bufferStarvationDelay.toFixed(3) + " s\n Estimated load time for current fragment: " + fragLoadedDelay.toFixed(3) + " s\n Estimated load time for down switch fragment: " + fragLevelNextLoadedDelay.toFixed(3) + " s\n TTFB estimate: " + (ttfb | 0) + " ms\n Current BW estimate: " + (isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown') + " bps\n New BW estimate: " + (_this.getBwEstimate() | 0) + " bps\n Switching to level " + nextLoadLevel + " @ " + (nextLoadLevelBitrate | 0) + " bps");
6538
6583
  hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, {
6539
6584
  frag: frag,
6540
6585
  part: part,
6541
6586
  stats: stats
6542
6587
  });
6543
6588
  };
6544
- this.hls = _hls;
6545
- this.bwEstimator = this.initEstimator();
6546
- this.registerListeners();
6589
+ _this.hls = _hls;
6590
+ _this.bwEstimator = _this.initEstimator();
6591
+ _this.registerListeners();
6592
+ return _this;
6547
6593
  }
6548
6594
  var _proto = AbrController.prototype;
6549
6595
  _proto.resetEstimator = function resetEstimator(abrEwmaDefaultEstimate) {
6550
6596
  if (abrEwmaDefaultEstimate) {
6551
- logger.log("setting initial bwe to " + abrEwmaDefaultEstimate);
6597
+ this.log("setting initial bwe to " + abrEwmaDefaultEstimate);
6552
6598
  this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
6553
6599
  }
6554
6600
  this.firstSelection = -1;
@@ -6800,13 +6846,13 @@
6800
6846
  // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
6801
6847
  var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
6802
6848
  maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
6803
- logger.info("[abr] bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6849
+ this.info("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6804
6850
  // don't use conservative factor on bitrate test
6805
6851
  bwFactor = bwUpFactor = 1;
6806
6852
  }
6807
6853
  }
6808
6854
  var bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor);
6809
- logger.info("[abr] " + (bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6855
+ this.info((bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6810
6856
  if (bestLevel > -1) {
6811
6857
  return bestLevel;
6812
6858
  }
@@ -6862,7 +6908,7 @@
6862
6908
  currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
6863
6909
  currentFrameRate = minFramerate;
6864
6910
  currentBw = Math.max(currentBw, minBitrate);
6865
- logger.log("[abr] picked start tier " + JSON.stringify(startTier));
6911
+ this.log("picked start tier " + JSON.stringify(startTier));
6866
6912
  } else {
6867
6913
  currentCodecSet = level == null ? void 0 : level.codecSet;
6868
6914
  currentVideoRange = level == null ? void 0 : level.videoRange;
@@ -6915,9 +6961,9 @@
6915
6961
  var forcedAutoLevel = _this2.forcedAutoLevel;
6916
6962
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
6917
6963
  if (levelsSkipped.length) {
6918
- logger.trace("[abr] Skipped level(s) " + levelsSkipped.join(',') + " of " + maxAutoLevel + " max with CODECS and VIDEO-RANGE:\"" + levels[levelsSkipped[0]].codecs + "\" " + levels[levelsSkipped[0]].videoRange + "; not compatible with \"" + level.codecs + "\" " + currentVideoRange);
6964
+ _this2.trace("Skipped level(s) " + levelsSkipped.join(',') + " of " + maxAutoLevel + " max with CODECS and VIDEO-RANGE:\"" + levels[levelsSkipped[0]].codecs + "\" " + levels[levelsSkipped[0]].videoRange + "; not compatible with \"" + level.codecs + "\" " + currentVideoRange);
6919
6965
  }
6920
- logger.info("[abr] switch candidate:" + selectionBaseLevel + "->" + i + " adjustedbw(" + Math.round(adjustedbw) + ")-bitrate=" + Math.round(adjustedbw - bitrate) + " ttfb:" + ttfbEstimateSec.toFixed(1) + " avgDuration:" + avgDuration.toFixed(1) + " maxFetchDuration:" + maxFetchDuration.toFixed(1) + " fetchDuration:" + fetchDuration.toFixed(1) + " firstSelection:" + firstSelection + " codecSet:" + currentCodecSet + " videoRange:" + currentVideoRange + " hls.loadLevel:" + loadLevel);
6966
+ _this2.info("switch candidate:" + selectionBaseLevel + "->" + i + " adjustedbw(" + Math.round(adjustedbw) + ")-bitrate=" + Math.round(adjustedbw - bitrate) + " ttfb:" + ttfbEstimateSec.toFixed(1) + " avgDuration:" + avgDuration.toFixed(1) + " maxFetchDuration:" + maxFetchDuration.toFixed(1) + " fetchDuration:" + fetchDuration.toFixed(1) + " firstSelection:" + firstSelection + " codecSet:" + currentCodecSet + " videoRange:" + currentVideoRange + " hls.loadLevel:" + loadLevel);
6921
6967
  }
6922
6968
  if (firstSelection) {
6923
6969
  _this2.firstSelection = i;
@@ -6951,7 +6997,7 @@
6951
6997
  }
6952
6998
  var firstLevel = this.hls.firstLevel;
6953
6999
  var clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
6954
- logger.warn("[abr] Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
7000
+ this.warn("Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
6955
7001
  return clamped;
6956
7002
  }
6957
7003
  }, {
@@ -7004,7 +7050,7 @@
7004
7050
  }
7005
7051
  }]);
7006
7052
  return AbrController;
7007
- }();
7053
+ }(Logger);
7008
7054
 
7009
7055
  /**
7010
7056
  * Provides methods dealing with buffer length retrieval for example.
@@ -7225,57 +7271,57 @@
7225
7271
  }();
7226
7272
 
7227
7273
  var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
7228
- var BufferController = /*#__PURE__*/function () {
7274
+ var BufferController = /*#__PURE__*/function (_Logger) {
7275
+ _inheritsLoose(BufferController, _Logger);
7229
7276
  function BufferController(hls) {
7230
- var _this = this;
7277
+ var _this;
7278
+ _this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
7231
7279
  // The level details used to determine duration, target-duration and live
7232
- this.details = null;
7280
+ _this.details = null;
7233
7281
  // cache the self generated object url to detect hijack of video tag
7234
- this._objectUrl = null;
7282
+ _this._objectUrl = null;
7235
7283
  // A queue of buffer operations which require the SourceBuffer to not be updating upon execution
7236
- this.operationQueue = void 0;
7284
+ _this.operationQueue = void 0;
7237
7285
  // References to event listeners for each SourceBuffer, so that they can be referenced for event removal
7238
- this.listeners = void 0;
7239
- this.hls = void 0;
7286
+ _this.listeners = void 0;
7287
+ _this.hls = void 0;
7240
7288
  // The number of BUFFER_CODEC events received before any sourceBuffers are created
7241
- this.bufferCodecEventsExpected = 0;
7289
+ _this.bufferCodecEventsExpected = 0;
7242
7290
  // The total number of BUFFER_CODEC events received
7243
- this._bufferCodecEventsTotal = 0;
7291
+ _this._bufferCodecEventsTotal = 0;
7244
7292
  // A reference to the attached media element
7245
- this.media = null;
7293
+ _this.media = null;
7246
7294
  // A reference to the active media source
7247
- this.mediaSource = null;
7295
+ _this.mediaSource = null;
7248
7296
  // Last MP3 audio chunk appended
7249
- this.lastMpegAudioChunk = null;
7250
- this.appendSource = void 0;
7297
+ _this.lastMpegAudioChunk = null;
7298
+ _this.appendSource = void 0;
7251
7299
  // counters
7252
- this.appendErrors = {
7300
+ _this.appendErrors = {
7253
7301
  audio: 0,
7254
7302
  video: 0,
7255
7303
  audiovideo: 0
7256
7304
  };
7257
- this.tracks = {};
7258
- this.pendingTracks = {};
7259
- this.sourceBuffer = void 0;
7260
- this.log = void 0;
7261
- this.warn = void 0;
7262
- this.error = void 0;
7263
- this._onEndStreaming = function (event) {
7305
+ _this.tracks = {};
7306
+ _this.pendingTracks = {};
7307
+ _this.sourceBuffer = void 0;
7308
+ _this._onEndStreaming = function (event) {
7264
7309
  if (!_this.hls) {
7265
7310
  return;
7266
7311
  }
7267
7312
  _this.hls.pauseBuffering();
7268
7313
  };
7269
- this._onStartStreaming = function (event) {
7314
+ _this._onStartStreaming = function (event) {
7270
7315
  if (!_this.hls) {
7271
7316
  return;
7272
7317
  }
7273
7318
  _this.hls.resumeBuffering();
7274
7319
  };
7275
7320
  // Keep as arrow functions so that we can directly reference these functions directly as event listeners
7276
- this._onMediaSourceOpen = function () {
7277
- var media = _this.media,
7278
- mediaSource = _this.mediaSource;
7321
+ _this._onMediaSourceOpen = function () {
7322
+ var _assertThisInitialize = _assertThisInitialized(_this),
7323
+ media = _assertThisInitialize.media,
7324
+ mediaSource = _assertThisInitialize.mediaSource;
7279
7325
  _this.log('Media source opened');
7280
7326
  if (media) {
7281
7327
  media.removeEventListener('emptied', _this._onMediaEmptied);
@@ -7291,27 +7337,25 @@
7291
7337
  }
7292
7338
  _this.checkPendingTracks();
7293
7339
  };
7294
- this._onMediaSourceClose = function () {
7340
+ _this._onMediaSourceClose = function () {
7295
7341
  _this.log('Media source closed');
7296
7342
  };
7297
- this._onMediaSourceEnded = function () {
7343
+ _this._onMediaSourceEnded = function () {
7298
7344
  _this.log('Media source ended');
7299
7345
  };
7300
- this._onMediaEmptied = function () {
7301
- var mediaSrc = _this.mediaSrc,
7302
- _objectUrl = _this._objectUrl;
7346
+ _this._onMediaEmptied = function () {
7347
+ var _assertThisInitialize2 = _assertThisInitialized(_this),
7348
+ mediaSrc = _assertThisInitialize2.mediaSrc,
7349
+ _objectUrl = _assertThisInitialize2._objectUrl;
7303
7350
  if (mediaSrc !== _objectUrl) {
7304
- logger.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7351
+ _this.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7305
7352
  }
7306
7353
  };
7307
- this.hls = hls;
7308
- var logPrefix = '[buffer-controller]';
7309
- this.appendSource = hls.config.preferManagedMediaSource;
7310
- this.log = logger.log.bind(logger, logPrefix);
7311
- this.warn = logger.warn.bind(logger, logPrefix);
7312
- this.error = logger.error.bind(logger, logPrefix);
7313
- this._initSourceBuffer();
7314
- this.registerListeners();
7354
+ _this.hls = hls;
7355
+ _this.appendSource = hls.config.preferManagedMediaSource;
7356
+ _this._initSourceBuffer();
7357
+ _this.registerListeners();
7358
+ return _this;
7315
7359
  }
7316
7360
  var _proto = BufferController.prototype;
7317
7361
  _proto.hasSourceTypes = function hasSourceTypes() {
@@ -7323,6 +7367,12 @@
7323
7367
  this.lastMpegAudioChunk = null;
7324
7368
  // @ts-ignore
7325
7369
  this.hls = null;
7370
+ // @ts-ignore
7371
+ this._onMediaSourceOpen = this._onMediaSourceClose = null;
7372
+ // @ts-ignore
7373
+ this._onMediaSourceEnded = null;
7374
+ // @ts-ignore
7375
+ this._onStartStreaming = this._onEndStreaming = null;
7326
7376
  };
7327
7377
  _proto.registerListeners = function registerListeners() {
7328
7378
  var hls = this.hls;
@@ -7480,6 +7530,7 @@
7480
7530
  _this2.resetBuffer(type);
7481
7531
  });
7482
7532
  this._initSourceBuffer();
7533
+ this.hls.resumeBuffering();
7483
7534
  };
7484
7535
  _proto.resetBuffer = function resetBuffer(type) {
7485
7536
  var sb = this.sourceBuffer[type];
@@ -8183,7 +8234,7 @@
8183
8234
  }
8184
8235
  }]);
8185
8236
  return BufferController;
8186
- }();
8237
+ }(Logger);
8187
8238
  function removeSourceChildren(node) {
8188
8239
  var sourceChildren = node.querySelectorAll('source');
8189
8240
  [].slice.call(sourceChildren).forEach(function (source) {
@@ -8307,7 +8358,7 @@
8307
8358
  var hls = this.hls;
8308
8359
  var maxLevel = this.getMaxLevel(levels.length - 1);
8309
8360
  if (maxLevel !== this.autoLevelCapping) {
8310
- logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8361
+ hls.logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8311
8362
  }
8312
8363
  hls.autoLevelCapping = maxLevel;
8313
8364
  if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
@@ -8497,10 +8548,10 @@
8497
8548
  totalDroppedFrames: droppedFrames
8498
8549
  });
8499
8550
  if (droppedFPS > 0) {
8500
- // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8551
+ // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8501
8552
  if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
8502
8553
  var currentLevel = hls.currentLevel;
8503
- logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8554
+ hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8504
8555
  if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
8505
8556
  currentLevel = currentLevel - 1;
8506
8557
  hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
@@ -8534,26 +8585,28 @@
8534
8585
  }();
8535
8586
 
8536
8587
  var PATHWAY_PENALTY_DURATION_MS = 300000;
8537
- var ContentSteeringController = /*#__PURE__*/function () {
8588
+ var ContentSteeringController = /*#__PURE__*/function (_Logger) {
8589
+ _inheritsLoose(ContentSteeringController, _Logger);
8538
8590
  function ContentSteeringController(hls) {
8539
- this.hls = void 0;
8540
- this.log = void 0;
8541
- this.loader = null;
8542
- this.uri = null;
8543
- this.pathwayId = '.';
8544
- this.pathwayPriority = null;
8545
- this.timeToLoad = 300;
8546
- this.reloadTimer = -1;
8547
- this.updated = 0;
8548
- this.started = false;
8549
- this.enabled = true;
8550
- this.levels = null;
8551
- this.audioTracks = null;
8552
- this.subtitleTracks = null;
8553
- this.penalizedPathways = {};
8554
- this.hls = hls;
8555
- this.log = logger.log.bind(logger, "[content-steering]:");
8556
- this.registerListeners();
8591
+ var _this;
8592
+ _this = _Logger.call(this, 'content-steering', hls.logger) || this;
8593
+ _this.hls = void 0;
8594
+ _this.loader = null;
8595
+ _this.uri = null;
8596
+ _this.pathwayId = '.';
8597
+ _this.pathwayPriority = null;
8598
+ _this.timeToLoad = 300;
8599
+ _this.reloadTimer = -1;
8600
+ _this.updated = 0;
8601
+ _this.started = false;
8602
+ _this.enabled = true;
8603
+ _this.levels = null;
8604
+ _this.audioTracks = null;
8605
+ _this.subtitleTracks = null;
8606
+ _this.penalizedPathways = {};
8607
+ _this.hls = hls;
8608
+ _this.registerListeners();
8609
+ return _this;
8557
8610
  }
8558
8611
  var _proto = ContentSteeringController.prototype;
8559
8612
  _proto.registerListeners = function registerListeners() {
@@ -8674,7 +8727,7 @@
8674
8727
  errorAction.resolved = this.pathwayId !== errorPathway;
8675
8728
  }
8676
8729
  if (!errorAction.resolved) {
8677
- logger.warn("Could not resolve " + data.details + " (\"" + data.error.message + "\") with content-steering for Pathway: " + errorPathway + " levels: " + (levels ? levels.length : levels) + " priorities: " + JSON.stringify(pathwayPriority) + " penalized: " + JSON.stringify(this.penalizedPathways));
8730
+ this.warn("Could not resolve " + data.details + " (\"" + data.error.message + "\") with content-steering for Pathway: " + errorPathway + " levels: " + (levels ? levels.length : levels) + " priorities: " + JSON.stringify(pathwayPriority) + " penalized: " + JSON.stringify(this.penalizedPathways));
8678
8731
  }
8679
8732
  }
8680
8733
  };
@@ -8754,7 +8807,7 @@
8754
8807
  return defaultPathway;
8755
8808
  };
8756
8809
  _proto.clonePathways = function clonePathways(pathwayClones) {
8757
- var _this = this;
8810
+ var _this2 = this;
8758
8811
  var levels = this.levels;
8759
8812
  if (!levels) {
8760
8813
  return;
@@ -8770,7 +8823,7 @@
8770
8823
  })) {
8771
8824
  return;
8772
8825
  }
8773
- var clonedVariants = _this.getLevelsForPathway(baseId).map(function (baseLevel) {
8826
+ var clonedVariants = _this2.getLevelsForPathway(baseId).map(function (baseLevel) {
8774
8827
  var attributes = new AttrList(baseLevel.attrs);
8775
8828
  attributes['PATHWAY-ID'] = cloneId;
8776
8829
  var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId;
@@ -8807,12 +8860,12 @@
8807
8860
  return clonedLevel;
8808
8861
  });
8809
8862
  levels.push.apply(levels, clonedVariants);
8810
- cloneRenditionGroups(_this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8811
- cloneRenditionGroups(_this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8863
+ cloneRenditionGroups(_this2.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8864
+ cloneRenditionGroups(_this2.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8812
8865
  });
8813
8866
  };
8814
8867
  _proto.loadSteeringManifest = function loadSteeringManifest(uri) {
8815
- var _this2 = this;
8868
+ var _this3 = this;
8816
8869
  var config = this.hls.config;
8817
8870
  var Loader = config.loader;
8818
8871
  if (this.loader) {
@@ -8847,87 +8900,87 @@
8847
8900
  };
8848
8901
  var callbacks = {
8849
8902
  onSuccess: function onSuccess(response, stats, context, networkDetails) {
8850
- _this2.log("Loaded steering manifest: \"" + url + "\"");
8903
+ _this3.log("Loaded steering manifest: \"" + url + "\"");
8851
8904
  var steeringData = response.data;
8852
- if (steeringData.VERSION !== 1) {
8853
- _this2.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8905
+ if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) {
8906
+ _this3.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8854
8907
  return;
8855
8908
  }
8856
- _this2.updated = performance.now();
8857
- _this2.timeToLoad = steeringData.TTL;
8909
+ _this3.updated = performance.now();
8910
+ _this3.timeToLoad = steeringData.TTL;
8858
8911
  var reloadUri = steeringData['RELOAD-URI'],
8859
8912
  pathwayClones = steeringData['PATHWAY-CLONES'],
8860
8913
  pathwayPriority = steeringData['PATHWAY-PRIORITY'];
8861
8914
  if (reloadUri) {
8862
8915
  try {
8863
- _this2.uri = new self.URL(reloadUri, url).href;
8916
+ _this3.uri = new self.URL(reloadUri, url).href;
8864
8917
  } catch (error) {
8865
- _this2.enabled = false;
8866
- _this2.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8918
+ _this3.enabled = false;
8919
+ _this3.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8867
8920
  return;
8868
8921
  }
8869
8922
  }
8870
- _this2.scheduleRefresh(_this2.uri || context.url);
8923
+ _this3.scheduleRefresh(_this3.uri || context.url);
8871
8924
  if (pathwayClones) {
8872
- _this2.clonePathways(pathwayClones);
8925
+ _this3.clonePathways(pathwayClones);
8873
8926
  }
8874
8927
  var loadedSteeringData = {
8875
8928
  steeringManifest: steeringData,
8876
8929
  url: url.toString()
8877
8930
  };
8878
- _this2.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8931
+ _this3.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8879
8932
  if (pathwayPriority) {
8880
- _this2.updatePathwayPriority(pathwayPriority);
8933
+ _this3.updatePathwayPriority(pathwayPriority);
8881
8934
  }
8882
8935
  },
8883
8936
  onError: function onError(error, context, networkDetails, stats) {
8884
- _this2.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8885
- _this2.stopLoad();
8937
+ _this3.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8938
+ _this3.stopLoad();
8886
8939
  if (error.code === 410) {
8887
- _this2.enabled = false;
8888
- _this2.log("Steering manifest " + context.url + " no longer available");
8940
+ _this3.enabled = false;
8941
+ _this3.log("Steering manifest " + context.url + " no longer available");
8889
8942
  return;
8890
8943
  }
8891
- var ttl = _this2.timeToLoad * 1000;
8944
+ var ttl = _this3.timeToLoad * 1000;
8892
8945
  if (error.code === 429) {
8893
- var loader = _this2.loader;
8946
+ var loader = _this3.loader;
8894
8947
  if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {
8895
8948
  var retryAfter = loader.getResponseHeader('Retry-After');
8896
8949
  if (retryAfter) {
8897
8950
  ttl = parseFloat(retryAfter) * 1000;
8898
8951
  }
8899
8952
  }
8900
- _this2.log("Steering manifest " + context.url + " rate limited");
8953
+ _this3.log("Steering manifest " + context.url + " rate limited");
8901
8954
  return;
8902
8955
  }
8903
- _this2.scheduleRefresh(_this2.uri || context.url, ttl);
8956
+ _this3.scheduleRefresh(_this3.uri || context.url, ttl);
8904
8957
  },
8905
8958
  onTimeout: function onTimeout(stats, context, networkDetails) {
8906
- _this2.log("Timeout loading steering manifest (" + context.url + ")");
8907
- _this2.scheduleRefresh(_this2.uri || context.url);
8959
+ _this3.log("Timeout loading steering manifest (" + context.url + ")");
8960
+ _this3.scheduleRefresh(_this3.uri || context.url);
8908
8961
  }
8909
8962
  };
8910
8963
  this.log("Requesting steering manifest: " + url);
8911
8964
  this.loader.load(context, loaderConfig, callbacks);
8912
8965
  };
8913
8966
  _proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) {
8914
- var _this3 = this;
8967
+ var _this4 = this;
8915
8968
  if (ttlMs === void 0) {
8916
8969
  ttlMs = this.timeToLoad * 1000;
8917
8970
  }
8918
8971
  this.clearTimeout();
8919
8972
  this.reloadTimer = self.setTimeout(function () {
8920
- var _this3$hls;
8921
- var media = (_this3$hls = _this3.hls) == null ? void 0 : _this3$hls.media;
8973
+ var _this4$hls;
8974
+ var media = (_this4$hls = _this4.hls) == null ? void 0 : _this4$hls.media;
8922
8975
  if (media && !media.ended) {
8923
- _this3.loadSteeringManifest(uri);
8976
+ _this4.loadSteeringManifest(uri);
8924
8977
  return;
8925
8978
  }
8926
- _this3.scheduleRefresh(uri, _this3.timeToLoad * 1000);
8979
+ _this4.scheduleRefresh(uri, _this4.timeToLoad * 1000);
8927
8980
  }, ttlMs);
8928
8981
  };
8929
8982
  return ContentSteeringController;
8930
- }();
8983
+ }(Logger);
8931
8984
  function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {
8932
8985
  if (!tracks) {
8933
8986
  return;
@@ -9763,7 +9816,7 @@
9763
9816
  });
9764
9817
  function timelineConfig() {
9765
9818
  return {
9766
- cueHandler: Cues,
9819
+ cueHandler: HevcVideoParser,
9767
9820
  // used by timeline-controller
9768
9821
  enableWebVTT: false,
9769
9822
  // used by timeline-controller
@@ -9794,7 +9847,7 @@
9794
9847
  /**
9795
9848
  * @ignore
9796
9849
  */
9797
- function mergeConfig(defaultConfig, userConfig) {
9850
+ function mergeConfig(defaultConfig, userConfig, logger) {
9798
9851
  if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
9799
9852
  throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
9800
9853
  }
@@ -9864,7 +9917,7 @@
9864
9917
  /**
9865
9918
  * @ignore
9866
9919
  */
9867
- function enableStreamingMode(config) {
9920
+ function enableStreamingMode(config, logger) {
9868
9921
  var currentLoader = config.loader;
9869
9922
  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
9870
9923
  // If a developer has configured their own loader, respect that choice
@@ -9881,12 +9934,11 @@
9881
9934
  }
9882
9935
  }
9883
9936
 
9884
- var chromeOrFirefox;
9885
9937
  var LevelController = /*#__PURE__*/function (_BasePlaylistControll) {
9886
9938
  _inheritsLoose(LevelController, _BasePlaylistControll);
9887
9939
  function LevelController(hls, contentSteeringController) {
9888
9940
  var _this;
9889
- _this = _BasePlaylistControll.call(this, hls, '[level-controller]') || this;
9941
+ _this = _BasePlaylistControll.call(this, hls, 'level-controller') || this;
9890
9942
  _this._levels = [];
9891
9943
  _this._firstLevel = -1;
9892
9944
  _this._maxAutoLevel = -1;
@@ -9955,21 +10007,13 @@
9955
10007
  var videoCodecFound = false;
9956
10008
  var audioCodecFound = false;
9957
10009
  data.levels.forEach(function (levelParsed) {
9958
- var _audioCodec, _videoCodec;
10010
+ var _videoCodec;
9959
10011
  var attributes = levelParsed.attrs;
9960
-
9961
- // erase audio codec info if browser does not support mp4a.40.34.
9962
- // demuxer will autodetect codec and fallback to mpeg/audio
9963
10012
  var audioCodec = levelParsed.audioCodec,
9964
10013
  videoCodec = levelParsed.videoCodec;
9965
- if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) {
9966
- chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));
9967
- if (chromeOrFirefox) {
9968
- levelParsed.audioCodec = audioCodec = undefined;
9969
- }
9970
- }
9971
10014
  if (audioCodec) {
9972
- levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource);
10015
+ // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
10016
+ levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
9973
10017
  }
9974
10018
  if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
9975
10019
  videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
@@ -10201,7 +10245,12 @@
10201
10245
  if (curLevel.fragmentError === 0) {
10202
10246
  curLevel.loadError = 0;
10203
10247
  }
10204
- this.playlistLoaded(level, data, curLevel.details);
10248
+ // Ignore matching details populated by loading a Media Playlist directly
10249
+ var previousDetails = curLevel.details;
10250
+ if (previousDetails === data.details && previousDetails.advanced) {
10251
+ previousDetails = undefined;
10252
+ }
10253
+ this.playlistLoaded(level, data, previousDetails);
10205
10254
  } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
10206
10255
  // received a delta playlist update that cannot be merged
10207
10256
  details.deltaUpdateFailed = true;
@@ -11116,8 +11165,8 @@
11116
11165
  var _frag$decryptdata;
11117
11166
  var byteRangeStart = start;
11118
11167
  var byteRangeEnd = end;
11119
- if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {
11120
- // MAP segment encrypted with method 'AES-128', when served with HTTP Range,
11168
+ if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) {
11169
+ // MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
11121
11170
  // has the unencrypted size specified in the range.
11122
11171
  // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
11123
11172
  var fragmentLen = end - start;
@@ -11150,6 +11199,9 @@
11150
11199
  (part ? part : frag).stats.aborted = true;
11151
11200
  return new LoadError(errorData);
11152
11201
  }
11202
+ function isMethodFullSegmentAesCbc(method) {
11203
+ return method === 'AES-128' || method === 'AES-256';
11204
+ }
11153
11205
  var LoadError = /*#__PURE__*/function (_Error) {
11154
11206
  _inheritsLoose(LoadError, _Error);
11155
11207
  function LoadError(data) {
@@ -11306,6 +11358,8 @@
11306
11358
  }
11307
11359
  return this.loadKeyEME(keyInfo, frag);
11308
11360
  case 'AES-128':
11361
+ case 'AES-256':
11362
+ case 'AES-256-CTR':
11309
11363
  return this.loadKeyHTTP(keyInfo, frag);
11310
11364
  default:
11311
11365
  return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\"")));
@@ -11439,13 +11493,17 @@
11439
11493
  * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
11440
11494
  * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
11441
11495
  */
11442
- var TaskLoop = /*#__PURE__*/function () {
11443
- function TaskLoop() {
11444
- this._boundTick = void 0;
11445
- this._tickTimer = null;
11446
- this._tickInterval = null;
11447
- this._tickCallCount = 0;
11448
- this._boundTick = this.tick.bind(this);
11496
+ var TaskLoop = /*#__PURE__*/function (_Logger) {
11497
+ _inheritsLoose(TaskLoop, _Logger);
11498
+ function TaskLoop(label, logger) {
11499
+ var _this;
11500
+ _this = _Logger.call(this, label, logger) || this;
11501
+ _this._boundTick = void 0;
11502
+ _this._tickTimer = null;
11503
+ _this._tickInterval = null;
11504
+ _this._tickCallCount = 0;
11505
+ _this._boundTick = _this.tick.bind(_assertThisInitialized(_this));
11506
+ return _this;
11449
11507
  }
11450
11508
  var _proto = TaskLoop.prototype;
11451
11509
  _proto.destroy = function destroy() {
@@ -11531,7 +11589,7 @@
11531
11589
  */;
11532
11590
  _proto.doTick = function doTick() {};
11533
11591
  return TaskLoop;
11534
- }();
11592
+ }(Logger);
11535
11593
 
11536
11594
  var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) {
11537
11595
  if (size === void 0) {
@@ -11717,37 +11775,65 @@
11717
11775
  }
11718
11776
 
11719
11777
  var AESCrypto = /*#__PURE__*/function () {
11720
- function AESCrypto(subtle, iv) {
11778
+ function AESCrypto(subtle, iv, aesMode) {
11721
11779
  this.subtle = void 0;
11722
11780
  this.aesIV = void 0;
11781
+ this.aesMode = void 0;
11723
11782
  this.subtle = subtle;
11724
11783
  this.aesIV = iv;
11784
+ this.aesMode = aesMode;
11725
11785
  }
11726
11786
  var _proto = AESCrypto.prototype;
11727
11787
  _proto.decrypt = function decrypt(data, key) {
11728
- return this.subtle.decrypt({
11729
- name: 'AES-CBC',
11730
- iv: this.aesIV
11731
- }, key, data);
11788
+ switch (this.aesMode) {
11789
+ case DecrypterAesMode.cbc:
11790
+ return this.subtle.decrypt({
11791
+ name: 'AES-CBC',
11792
+ iv: this.aesIV
11793
+ }, key, data);
11794
+ case DecrypterAesMode.ctr:
11795
+ return this.subtle.decrypt({
11796
+ name: 'AES-CTR',
11797
+ counter: this.aesIV,
11798
+ length: 64
11799
+ },
11800
+ //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
11801
+ key, data);
11802
+ default:
11803
+ throw new Error("[AESCrypto] invalid aes mode " + this.aesMode);
11804
+ }
11732
11805
  };
11733
11806
  return AESCrypto;
11734
11807
  }();
11735
11808
 
11736
11809
  var FastAESKey = /*#__PURE__*/function () {
11737
- function FastAESKey(subtle, key) {
11810
+ function FastAESKey(subtle, key, aesMode) {
11738
11811
  this.subtle = void 0;
11739
11812
  this.key = void 0;
11813
+ this.aesMode = void 0;
11740
11814
  this.subtle = subtle;
11741
11815
  this.key = key;
11816
+ this.aesMode = aesMode;
11742
11817
  }
11743
11818
  var _proto = FastAESKey.prototype;
11744
11819
  _proto.expandKey = function expandKey() {
11820
+ var subtleAlgoName = getSubtleAlgoName(this.aesMode);
11745
11821
  return this.subtle.importKey('raw', this.key, {
11746
- name: 'AES-CBC'
11822
+ name: subtleAlgoName
11747
11823
  }, false, ['encrypt', 'decrypt']);
11748
11824
  };
11749
11825
  return FastAESKey;
11750
11826
  }();
11827
+ function getSubtleAlgoName(aesMode) {
11828
+ switch (aesMode) {
11829
+ case DecrypterAesMode.cbc:
11830
+ return 'AES-CBC';
11831
+ case DecrypterAesMode.ctr:
11832
+ return 'AES-CTR';
11833
+ default:
11834
+ throw new Error("[FastAESKey] invalid aes mode " + aesMode);
11835
+ }
11836
+ }
11751
11837
 
11752
11838
  // PKCS7
11753
11839
  function removePadding(array) {
@@ -12000,7 +12086,8 @@
12000
12086
  this.currentIV = null;
12001
12087
  this.currentResult = null;
12002
12088
  this.useSoftware = void 0;
12003
- this.useSoftware = config.enableSoftwareAES;
12089
+ this.enableSoftwareAES = void 0;
12090
+ this.enableSoftwareAES = config.enableSoftwareAES;
12004
12091
  this.removePKCS7Padding = removePKCS7Padding;
12005
12092
  // built in decryptor expects PKCS7 padding
12006
12093
  if (removePKCS7Padding) {
@@ -12013,9 +12100,7 @@
12013
12100
  /* no-op */
12014
12101
  }
12015
12102
  }
12016
- if (this.subtle === null) {
12017
- this.useSoftware = true;
12018
- }
12103
+ this.useSoftware = this.subtle === null;
12019
12104
  }
12020
12105
  var _proto = Decrypter.prototype;
12021
12106
  _proto.destroy = function destroy() {
@@ -12052,11 +12137,11 @@
12052
12137
  this.softwareDecrypter = null;
12053
12138
  }
12054
12139
  };
12055
- _proto.decrypt = function decrypt(data, key, iv) {
12140
+ _proto.decrypt = function decrypt(data, key, iv, aesMode) {
12056
12141
  var _this = this;
12057
12142
  if (this.useSoftware) {
12058
12143
  return new Promise(function (resolve, reject) {
12059
- _this.softwareDecrypt(new Uint8Array(data), key, iv);
12144
+ _this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
12060
12145
  var decryptResult = _this.flush();
12061
12146
  if (decryptResult) {
12062
12147
  resolve(decryptResult.buffer);
@@ -12065,16 +12150,20 @@
12065
12150
  }
12066
12151
  });
12067
12152
  }
12068
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
12153
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
12069
12154
  }
12070
12155
 
12071
12156
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
12072
12157
  // data is handled in the flush() call
12073
12158
  ;
12074
- _proto.softwareDecrypt = function softwareDecrypt(data, key, iv) {
12159
+ _proto.softwareDecrypt = function softwareDecrypt(data, key, iv, aesMode) {
12075
12160
  var currentIV = this.currentIV,
12076
12161
  currentResult = this.currentResult,
12077
12162
  remainderData = this.remainderData;
12163
+ if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
12164
+ logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
12165
+ return null;
12166
+ }
12078
12167
  this.logOnce('JS AES decrypt');
12079
12168
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
12080
12169
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -12107,12 +12196,12 @@
12107
12196
  }
12108
12197
  return result;
12109
12198
  };
12110
- _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv) {
12199
+ _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv, aesMode) {
12111
12200
  var _this2 = this;
12112
12201
  var subtle = this.subtle;
12113
12202
  if (this.key !== key || !this.fastAesKey) {
12114
12203
  this.key = key;
12115
- this.fastAesKey = new FastAESKey(subtle, key);
12204
+ this.fastAesKey = new FastAESKey(subtle, key, aesMode);
12116
12205
  }
12117
12206
  return this.fastAesKey.expandKey().then(function (aesKey) {
12118
12207
  // decrypt using web crypto
@@ -12120,22 +12209,25 @@
12120
12209
  return Promise.reject(new Error('web crypto not initialized'));
12121
12210
  }
12122
12211
  _this2.logOnce('WebCrypto AES decrypt');
12123
- var crypto = new AESCrypto(subtle, new Uint8Array(iv));
12212
+ var crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
12124
12213
  return crypto.decrypt(data.buffer, aesKey);
12125
12214
  }).catch(function (err) {
12126
12215
  logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message);
12127
- return _this2.onWebCryptoError(data, key, iv);
12216
+ return _this2.onWebCryptoError(data, key, iv, aesMode);
12128
12217
  });
12129
12218
  };
12130
- _proto.onWebCryptoError = function onWebCryptoError(data, key, iv) {
12131
- this.useSoftware = true;
12132
- this.logEnabled = true;
12133
- this.softwareDecrypt(data, key, iv);
12134
- var decryptResult = this.flush();
12135
- if (decryptResult) {
12136
- return decryptResult.buffer;
12219
+ _proto.onWebCryptoError = function onWebCryptoError(data, key, iv, aesMode) {
12220
+ var enableSoftwareAES = this.enableSoftwareAES;
12221
+ if (enableSoftwareAES) {
12222
+ this.useSoftware = true;
12223
+ this.logEnabled = true;
12224
+ this.softwareDecrypt(data, key, iv, aesMode);
12225
+ var decryptResult = this.flush();
12226
+ if (decryptResult) {
12227
+ return decryptResult.buffer;
12228
+ }
12137
12229
  }
12138
- throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
12230
+ throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data');
12139
12231
  };
12140
12232
  _proto.getValidChunk = function getValidChunk(data) {
12141
12233
  var currentChunk = data;
@@ -12189,7 +12281,7 @@
12189
12281
  _inheritsLoose(BaseStreamController, _TaskLoop);
12190
12282
  function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
12191
12283
  var _this;
12192
- _this = _TaskLoop.call(this) || this;
12284
+ _this = _TaskLoop.call(this, logPrefix, hls.logger) || this;
12193
12285
  _this.hls = void 0;
12194
12286
  _this.fragPrevious = null;
12195
12287
  _this.fragCurrent = null;
@@ -12214,30 +12306,101 @@
12214
12306
  _this.startFragRequested = false;
12215
12307
  _this.decrypter = void 0;
12216
12308
  _this.initPTS = [];
12217
- _this.onvseeking = null;
12218
- _this.onvended = null;
12219
- _this.logPrefix = '';
12220
- _this.log = void 0;
12221
- _this.warn = void 0;
12309
+ _this.buffering = true;
12310
+ _this.loadingParts = false;
12311
+ _this.onMediaSeeking = function () {
12312
+ var _assertThisInitialize = _assertThisInitialized(_this),
12313
+ config = _assertThisInitialize.config,
12314
+ fragCurrent = _assertThisInitialize.fragCurrent,
12315
+ media = _assertThisInitialize.media,
12316
+ mediaBuffer = _assertThisInitialize.mediaBuffer,
12317
+ state = _assertThisInitialize.state;
12318
+ var currentTime = media ? media.currentTime : 0;
12319
+ var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12320
+ _this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12321
+ if (_this.state === State.ENDED) {
12322
+ _this.resetLoadingState();
12323
+ } else if (fragCurrent) {
12324
+ // Seeking while frag load is in progress
12325
+ var tolerance = config.maxFragLookUpTolerance;
12326
+ var fragStartOffset = fragCurrent.start - tolerance;
12327
+ var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12328
+ // if seeking out of buffered range or into new one
12329
+ if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12330
+ var pastFragment = currentTime > fragEndOffset;
12331
+ // if the seek position is outside the current fragment range
12332
+ if (currentTime < fragStartOffset || pastFragment) {
12333
+ if (pastFragment && fragCurrent.loader) {
12334
+ _this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12335
+ fragCurrent.abortRequests();
12336
+ _this.resetLoadingState();
12337
+ }
12338
+ _this.fragPrevious = null;
12339
+ }
12340
+ }
12341
+ }
12342
+ if (media) {
12343
+ // Remove gap fragments
12344
+ _this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, _this.playlistType, true);
12345
+ _this.lastCurrentTime = currentTime;
12346
+ if (!_this.loadingParts) {
12347
+ var bufferEnd = Math.max(bufferInfo.end, currentTime);
12348
+ var shouldLoadParts = _this.shouldLoadParts(_this.getLevelDetails(), bufferEnd);
12349
+ if (shouldLoadParts) {
12350
+ _this.log("LL-Part loading ON after seeking to " + currentTime.toFixed(2) + " with buffer @" + bufferEnd.toFixed(2));
12351
+ _this.loadingParts = shouldLoadParts;
12352
+ }
12353
+ }
12354
+ }
12355
+
12356
+ // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12357
+ if (!_this.loadedmetadata && !bufferInfo.len) {
12358
+ _this.nextLoadPosition = _this.startPosition = currentTime;
12359
+ }
12360
+
12361
+ // Async tick to speed up processing
12362
+ _this.tickImmediate();
12363
+ };
12364
+ _this.onMediaEnded = function () {
12365
+ // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12366
+ _this.startPosition = _this.lastCurrentTime = 0;
12367
+ if (_this.playlistType === PlaylistLevelType.MAIN) {
12368
+ _this.hls.trigger(Events.MEDIA_ENDED, {
12369
+ stalled: false
12370
+ });
12371
+ }
12372
+ };
12222
12373
  _this.playlistType = playlistType;
12223
- _this.logPrefix = logPrefix;
12224
- _this.log = logger.log.bind(logger, logPrefix + ":");
12225
- _this.warn = logger.warn.bind(logger, logPrefix + ":");
12226
12374
  _this.hls = hls;
12227
12375
  _this.fragmentLoader = new FragmentLoader(hls.config);
12228
12376
  _this.keyLoader = keyLoader;
12229
12377
  _this.fragmentTracker = fragmentTracker;
12230
12378
  _this.config = hls.config;
12231
12379
  _this.decrypter = new Decrypter(hls.config);
12232
- hls.on(Events.MANIFEST_LOADED, _this.onManifestLoaded, _assertThisInitialized(_this));
12233
12380
  return _this;
12234
12381
  }
12235
12382
  var _proto = BaseStreamController.prototype;
12236
- _proto.doTick = function doTick() {
12237
- this.onTickEnd();
12238
- };
12239
- _proto.onTickEnd = function onTickEnd() {}
12240
-
12383
+ _proto.registerListeners = function registerListeners() {
12384
+ var hls = this.hls;
12385
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12386
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12387
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12388
+ hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12389
+ hls.on(Events.ERROR, this.onError, this);
12390
+ };
12391
+ _proto.unregisterListeners = function unregisterListeners() {
12392
+ var hls = this.hls;
12393
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12394
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12395
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12396
+ hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12397
+ hls.off(Events.ERROR, this.onError, this);
12398
+ };
12399
+ _proto.doTick = function doTick() {
12400
+ this.onTickEnd();
12401
+ };
12402
+ _proto.onTickEnd = function onTickEnd() {}
12403
+
12241
12404
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
12242
12405
  ;
12243
12406
  _proto.startLoad = function startLoad(startPosition) {};
@@ -12256,6 +12419,12 @@
12256
12419
  this.clearNextTick();
12257
12420
  this.state = State.STOPPED;
12258
12421
  };
12422
+ _proto.pauseBuffering = function pauseBuffering() {
12423
+ this.buffering = false;
12424
+ };
12425
+ _proto.resumeBuffering = function resumeBuffering() {
12426
+ this.buffering = true;
12427
+ };
12259
12428
  _proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) {
12260
12429
  // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
12261
12430
  // of nothing loading/loaded return false
@@ -12286,10 +12455,8 @@
12286
12455
  };
12287
12456
  _proto.onMediaAttached = function onMediaAttached(event, data) {
12288
12457
  var media = this.media = this.mediaBuffer = data.media;
12289
- this.onvseeking = this.onMediaSeeking.bind(this);
12290
- this.onvended = this.onMediaEnded.bind(this);
12291
- media.addEventListener('seeking', this.onvseeking);
12292
- media.addEventListener('ended', this.onvended);
12458
+ media.addEventListener('seeking', this.onMediaSeeking);
12459
+ media.addEventListener('ended', this.onMediaEnded);
12293
12460
  var config = this.config;
12294
12461
  if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
12295
12462
  this.startLoad(config.startPosition);
@@ -12303,10 +12470,9 @@
12303
12470
  }
12304
12471
 
12305
12472
  // remove video listeners
12306
- if (media && this.onvseeking && this.onvended) {
12307
- media.removeEventListener('seeking', this.onvseeking);
12308
- media.removeEventListener('ended', this.onvended);
12309
- this.onvseeking = this.onvended = null;
12473
+ if (media) {
12474
+ media.removeEventListener('seeking', this.onMediaSeeking);
12475
+ media.removeEventListener('ended', this.onMediaEnded);
12310
12476
  }
12311
12477
  if (this.keyLoader) {
12312
12478
  this.keyLoader.detach();
@@ -12316,54 +12482,8 @@
12316
12482
  this.fragmentTracker.removeAllFragments();
12317
12483
  this.stopLoad();
12318
12484
  };
12319
- _proto.onMediaSeeking = function onMediaSeeking() {
12320
- var config = this.config,
12321
- fragCurrent = this.fragCurrent,
12322
- media = this.media,
12323
- mediaBuffer = this.mediaBuffer,
12324
- state = this.state;
12325
- var currentTime = media ? media.currentTime : 0;
12326
- var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12327
- this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12328
- if (this.state === State.ENDED) {
12329
- this.resetLoadingState();
12330
- } else if (fragCurrent) {
12331
- // Seeking while frag load is in progress
12332
- var tolerance = config.maxFragLookUpTolerance;
12333
- var fragStartOffset = fragCurrent.start - tolerance;
12334
- var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12335
- // if seeking out of buffered range or into new one
12336
- if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12337
- var pastFragment = currentTime > fragEndOffset;
12338
- // if the seek position is outside the current fragment range
12339
- if (currentTime < fragStartOffset || pastFragment) {
12340
- if (pastFragment && fragCurrent.loader) {
12341
- this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12342
- fragCurrent.abortRequests();
12343
- this.resetLoadingState();
12344
- }
12345
- this.fragPrevious = null;
12346
- }
12347
- }
12348
- }
12349
- if (media) {
12350
- // Remove gap fragments
12351
- this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
12352
- this.lastCurrentTime = currentTime;
12353
- }
12354
-
12355
- // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12356
- if (!this.loadedmetadata && !bufferInfo.len) {
12357
- this.nextLoadPosition = this.startPosition = currentTime;
12358
- }
12359
-
12360
- // Async tick to speed up processing
12361
- this.tickImmediate();
12362
- };
12363
- _proto.onMediaEnded = function onMediaEnded() {
12364
- // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12365
- this.startPosition = this.lastCurrentTime = 0;
12366
- };
12485
+ _proto.onManifestLoading = function onManifestLoading() {};
12486
+ _proto.onError = function onError(event, data) {};
12367
12487
  _proto.onManifestLoaded = function onManifestLoaded(event, data) {
12368
12488
  this.startTimeOffset = data.startTimeOffset;
12369
12489
  this.initPTS = [];
@@ -12373,7 +12493,7 @@
12373
12493
  this.stopLoad();
12374
12494
  _TaskLoop.prototype.onHandlerDestroying.call(this);
12375
12495
  // @ts-ignore
12376
- this.hls = null;
12496
+ this.hls = this.onMediaSeeking = this.onMediaEnded = null;
12377
12497
  };
12378
12498
  _proto.onHandlerDestroyed = function onHandlerDestroyed() {
12379
12499
  this.state = State.STOPPED;
@@ -12503,10 +12623,10 @@
12503
12623
  var decryptData = frag.decryptdata;
12504
12624
 
12505
12625
  // check to see if the payload needs to be decrypted
12506
- if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
12626
+ if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
12507
12627
  var startTime = self.performance.now();
12508
12628
  // decrypt init segment data
12509
- return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(function (err) {
12629
+ return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(function (err) {
12510
12630
  hls.trigger(Events.ERROR, {
12511
12631
  type: ErrorTypes.MEDIA_ERROR,
12512
12632
  details: ErrorDetails.FRAG_DECRYPT_ERROR,
@@ -12619,7 +12739,7 @@
12619
12739
  }
12620
12740
  var keyLoadingPromise = null;
12621
12741
  if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
12622
- this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + " " + frag.level);
12742
+ this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + " " + frag.level);
12623
12743
  this.state = State.KEY_LOADING;
12624
12744
  this.fragCurrent = frag;
12625
12745
  keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) {
@@ -12640,8 +12760,16 @@
12640
12760
  } else if (!frag.encrypted && details.encryptedFragments.length) {
12641
12761
  this.keyLoader.loadClear(frag, details.encryptedFragments);
12642
12762
  }
12763
+ var fragPrevious = this.fragPrevious;
12764
+ if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) {
12765
+ var shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
12766
+ if (shouldLoadParts !== this.loadingParts) {
12767
+ this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " loading sn " + (fragPrevious == null ? void 0 : fragPrevious.sn) + "->" + frag.sn);
12768
+ this.loadingParts = shouldLoadParts;
12769
+ }
12770
+ }
12643
12771
  targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
12644
- if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
12772
+ if (this.loadingParts && frag.sn !== 'initSegment') {
12645
12773
  var partList = details.partList;
12646
12774
  if (partList && progressCallback) {
12647
12775
  if (targetBufferTime > frag.end && details.fragmentHint) {
@@ -12650,7 +12778,7 @@
12650
12778
  var partIndex = this.getNextPart(partList, frag, targetBufferTime);
12651
12779
  if (partIndex > -1) {
12652
12780
  var part = partList[partIndex];
12653
- this.log("Loading part sn: " + frag.sn + " p: " + part.index + " cc: " + frag.cc + " of playlist [" + details.startSN + "-" + details.endSN + "] parts [0-" + partIndex + "-" + (partList.length - 1) + "] " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
12781
+ this.log("Loading part sn: " + frag.sn + " p: " + part.index + " cc: " + frag.cc + " of playlist [" + details.startSN + "-" + details.endSN + "] parts [0-" + partIndex + "-" + (partList.length - 1) + "] " + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
12654
12782
  this.nextLoadPosition = part.start + part.duration;
12655
12783
  this.state = State.FRAG_LOADING;
12656
12784
  var _result;
@@ -12683,7 +12811,14 @@
12683
12811
  }
12684
12812
  }
12685
12813
  }
12686
- this.log("Loading fragment " + frag.sn + " cc: " + frag.cc + " " + (details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : '') + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
12814
+ if (frag.sn !== 'initSegment' && this.loadingParts) {
12815
+ this.log("LL-Part loading OFF after next part miss @" + targetBufferTime.toFixed(2));
12816
+ this.loadingParts = false;
12817
+ } else if (!frag.url) {
12818
+ // Selected fragment hint for part but not loading parts
12819
+ return Promise.resolve(null);
12820
+ }
12821
+ this.log("Loading fragment " + frag.sn + " cc: " + frag.cc + " " + (details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : '') + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3)));
12687
12822
  // Don't update nextLoadPosition for fragments which are not buffered
12688
12823
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
12689
12824
  this.nextLoadPosition = frag.start + frag.duration;
@@ -12785,8 +12920,36 @@
12785
12920
  if (part) {
12786
12921
  part.stats.parsing.end = now;
12787
12922
  }
12923
+ // See if part loading should be disabled/enabled based on buffer and playback position.
12924
+ if (frag.sn !== 'initSegment') {
12925
+ var levelDetails = this.getLevelDetails();
12926
+ var loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
12927
+ var shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
12928
+ if (shouldLoadParts !== this.loadingParts) {
12929
+ this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " after parsing segment ending @" + frag.end.toFixed(2));
12930
+ this.loadingParts = shouldLoadParts;
12931
+ }
12932
+ }
12788
12933
  this.updateLevelTiming(frag, part, level, chunkMeta.partial);
12789
12934
  };
12935
+ _proto.shouldLoadParts = function shouldLoadParts(details, bufferEnd) {
12936
+ if (this.config.lowLatencyMode) {
12937
+ if (!details) {
12938
+ return this.loadingParts;
12939
+ }
12940
+ if (details != null && details.partList) {
12941
+ var _details$fragmentHint;
12942
+ // Buffer must be ahead of first part + duration of parts after last segment
12943
+ // and playback must be at or past segment adjacent to part list
12944
+ var firstPart = details.partList[0];
12945
+ var safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0);
12946
+ if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) {
12947
+ return true;
12948
+ }
12949
+ }
12950
+ }
12951
+ return false;
12952
+ };
12790
12953
  _proto.getCurrentContext = function getCurrentContext(chunkMeta) {
12791
12954
  var levels = this.levels,
12792
12955
  fragCurrent = this.fragCurrent;
@@ -12921,7 +13084,8 @@
12921
13084
  // find fragment index, contiguous with end of buffer position
12922
13085
  var config = this.config;
12923
13086
  var start = fragments[0].start;
12924
- var frag;
13087
+ var canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
13088
+ var frag = null;
12925
13089
  if (levelDetails.live) {
12926
13090
  var initialLiveManifestSize = config.initialLiveManifestSize;
12927
13091
  if (fragLen < initialLiveManifestSize) {
@@ -12933,6 +13097,10 @@
12933
13097
  // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
12934
13098
  // we get the fragment matching that start time
12935
13099
  if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) {
13100
+ if (canLoadParts && !this.loadingParts) {
13101
+ this.log("LL-Part loading ON for initial live fragment");
13102
+ this.loadingParts = true;
13103
+ }
12936
13104
  frag = this.getInitialLiveFragment(levelDetails, fragments);
12937
13105
  this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;
12938
13106
  }
@@ -12943,7 +13111,7 @@
12943
13111
 
12944
13112
  // If we haven't run into any special cases already, just load the fragment most closely matching the requested position
12945
13113
  if (!frag) {
12946
- var end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd;
13114
+ var end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd;
12947
13115
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
12948
13116
  }
12949
13117
  return this.mapToInitFragWhenRequired(frag);
@@ -13057,7 +13225,7 @@
13057
13225
  var fragmentHint = levelDetails.fragmentHint;
13058
13226
  var tolerance = config.maxFragLookUpTolerance;
13059
13227
  var partList = levelDetails.partList;
13060
- var loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint);
13228
+ var loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint);
13061
13229
  if (loadingParts && fragmentHint && !this.bitrateTest) {
13062
13230
  // Include incomplete fragment with parts at end
13063
13231
  fragments = fragments.concat(fragmentHint);
@@ -13244,7 +13412,7 @@
13244
13412
  errorAction.resolved = true;
13245
13413
  }
13246
13414
  } else {
13247
- logger.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13415
+ this.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13248
13416
  return;
13249
13417
  }
13250
13418
  } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
@@ -13634,6 +13802,7 @@
13634
13802
  */
13635
13803
  function getAudioConfig(observer, data, offset, audioCodec) {
13636
13804
  var adtsObjectType;
13805
+ var originalAdtsObjectType;
13637
13806
  var adtsExtensionSamplingIndex;
13638
13807
  var adtsChannelConfig;
13639
13808
  var config;
@@ -13641,7 +13810,7 @@
13641
13810
  var manifestCodec = audioCodec;
13642
13811
  var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
13643
13812
  // byte 2
13644
- adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13813
+ adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13645
13814
  var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
13646
13815
  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
13647
13816
  var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex);
@@ -13658,8 +13827,8 @@
13658
13827
  // byte 3
13659
13828
  adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
13660
13829
  logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex);
13661
- // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
13662
- if (/firefox/i.test(userAgent)) {
13830
+ // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
13831
+ if (/firefox|palemoon/i.test(userAgent)) {
13663
13832
  if (adtsSamplingIndex >= 6) {
13664
13833
  adtsObjectType = 5;
13665
13834
  config = new Array(4);
@@ -13753,6 +13922,7 @@
13753
13922
  samplerate: adtsSamplingRates[adtsSamplingIndex],
13754
13923
  channelCount: adtsChannelConfig,
13755
13924
  codec: 'mp4a.40.' + adtsObjectType,
13925
+ parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
13756
13926
  manifestCodec: manifestCodec
13757
13927
  };
13758
13928
  }
@@ -13807,7 +13977,8 @@
13807
13977
  track.channelCount = config.channelCount;
13808
13978
  track.codec = config.codec;
13809
13979
  track.manifestCodec = config.manifestCodec;
13810
- logger.log("parsed codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13980
+ track.parsedCodec = config.parsedCodec;
13981
+ logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13811
13982
  }
13812
13983
  }
13813
13984
  function getFrameDuration(samplerate) {
@@ -14287,6 +14458,110 @@
14287
14458
  logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
14288
14459
  }
14289
14460
  };
14461
+ _proto.parseNALu = function parseNALu(track, array) {
14462
+ var len = array.byteLength;
14463
+ var state = track.naluState || 0;
14464
+ var lastState = state;
14465
+ var units = [];
14466
+ var i = 0;
14467
+ var value;
14468
+ var overflow;
14469
+ var unitType;
14470
+ var lastUnitStart = -1;
14471
+ var lastUnitType = 0;
14472
+ // logger.log('PES:' + Hex.hexDump(array));
14473
+
14474
+ if (state === -1) {
14475
+ // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14476
+ lastUnitStart = 0;
14477
+ // NALu type is value read from offset 0
14478
+ lastUnitType = this.getNALuType(array, 0);
14479
+ state = 0;
14480
+ i = 1;
14481
+ }
14482
+ while (i < len) {
14483
+ value = array[i++];
14484
+ // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14485
+ if (!state) {
14486
+ state = value ? 0 : 1;
14487
+ continue;
14488
+ }
14489
+ if (state === 1) {
14490
+ state = value ? 0 : 2;
14491
+ continue;
14492
+ }
14493
+ // here we have state either equal to 2 or 3
14494
+ if (!value) {
14495
+ state = 3;
14496
+ } else if (value === 1) {
14497
+ overflow = i - state - 1;
14498
+ if (lastUnitStart >= 0) {
14499
+ var unit = {
14500
+ data: array.subarray(lastUnitStart, overflow),
14501
+ type: lastUnitType
14502
+ };
14503
+ // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14504
+ units.push(unit);
14505
+ } else {
14506
+ // lastUnitStart is undefined => this is the first start code found in this PES packet
14507
+ // first check if start code delimiter is overlapping between 2 PES packets,
14508
+ // ie it started in last packet (lastState not zero)
14509
+ // and ended at the beginning of this PES packet (i <= 4 - lastState)
14510
+ var lastUnit = this.getLastNalUnit(track.samples);
14511
+ if (lastUnit) {
14512
+ if (lastState && i <= 4 - lastState) {
14513
+ // start delimiter overlapping between PES packets
14514
+ // strip start delimiter bytes from the end of last NAL unit
14515
+ // check if lastUnit had a state different from zero
14516
+ if (lastUnit.state) {
14517
+ // strip last bytes
14518
+ lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14519
+ }
14520
+ }
14521
+ // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14522
+
14523
+ if (overflow > 0) {
14524
+ // logger.log('first NALU found with overflow:' + overflow);
14525
+ lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14526
+ lastUnit.state = 0;
14527
+ }
14528
+ }
14529
+ }
14530
+ // check if we can read unit type
14531
+ if (i < len) {
14532
+ unitType = this.getNALuType(array, i);
14533
+ // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14534
+ lastUnitStart = i;
14535
+ lastUnitType = unitType;
14536
+ state = 0;
14537
+ } else {
14538
+ // not enough byte to read unit type. let's read it on next PES parsing
14539
+ state = -1;
14540
+ }
14541
+ } else {
14542
+ state = 0;
14543
+ }
14544
+ }
14545
+ if (lastUnitStart >= 0 && state >= 0) {
14546
+ var _unit = {
14547
+ data: array.subarray(lastUnitStart, len),
14548
+ type: lastUnitType,
14549
+ state: state
14550
+ };
14551
+ units.push(_unit);
14552
+ // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14553
+ }
14554
+ // no NALu found
14555
+ if (units.length === 0) {
14556
+ // append pes.data to previous NAL unit
14557
+ var _lastUnit = this.getLastNalUnit(track.samples);
14558
+ if (_lastUnit) {
14559
+ _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14560
+ }
14561
+ }
14562
+ track.naluState = state;
14563
+ return units;
14564
+ };
14290
14565
  return BaseVideoParser;
14291
14566
  }();
14292
14567
 
@@ -14441,189 +14716,6 @@
14441
14716
  ;
14442
14717
  _proto.readUInt = function readUInt() {
14443
14718
  return this.readBits(32);
14444
- }
14445
-
14446
- /**
14447
- * Advance the ExpGolomb decoder past a scaling list. The scaling
14448
- * list is optionally transmitted as part of a sequence parameter
14449
- * set and is not relevant to transmuxing.
14450
- * @param count the number of entries in this scaling list
14451
- * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14452
- */;
14453
- _proto.skipScalingList = function skipScalingList(count) {
14454
- var lastScale = 8;
14455
- var nextScale = 8;
14456
- var deltaScale;
14457
- for (var j = 0; j < count; j++) {
14458
- if (nextScale !== 0) {
14459
- deltaScale = this.readEG();
14460
- nextScale = (lastScale + deltaScale + 256) % 256;
14461
- }
14462
- lastScale = nextScale === 0 ? lastScale : nextScale;
14463
- }
14464
- }
14465
-
14466
- /**
14467
- * Read a sequence parameter set and return some interesting video
14468
- * properties. A sequence parameter set is the H264 metadata that
14469
- * describes the properties of upcoming video frames.
14470
- * @returns an object with configuration parsed from the
14471
- * sequence parameter set, including the dimensions of the
14472
- * associated video frames.
14473
- */;
14474
- _proto.readSPS = function readSPS() {
14475
- var frameCropLeftOffset = 0;
14476
- var frameCropRightOffset = 0;
14477
- var frameCropTopOffset = 0;
14478
- var frameCropBottomOffset = 0;
14479
- var numRefFramesInPicOrderCntCycle;
14480
- var scalingListCount;
14481
- var i;
14482
- var readUByte = this.readUByte.bind(this);
14483
- var readBits = this.readBits.bind(this);
14484
- var readUEG = this.readUEG.bind(this);
14485
- var readBoolean = this.readBoolean.bind(this);
14486
- var skipBits = this.skipBits.bind(this);
14487
- var skipEG = this.skipEG.bind(this);
14488
- var skipUEG = this.skipUEG.bind(this);
14489
- var skipScalingList = this.skipScalingList.bind(this);
14490
- readUByte();
14491
- var profileIdc = readUByte(); // profile_idc
14492
- readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14493
- skipBits(3); // reserved_zero_3bits u(3),
14494
- readUByte(); // level_idc u(8)
14495
- skipUEG(); // seq_parameter_set_id
14496
- // some profiles have more optional data we don't need
14497
- if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14498
- var chromaFormatIdc = readUEG();
14499
- if (chromaFormatIdc === 3) {
14500
- skipBits(1);
14501
- } // separate_colour_plane_flag
14502
-
14503
- skipUEG(); // bit_depth_luma_minus8
14504
- skipUEG(); // bit_depth_chroma_minus8
14505
- skipBits(1); // qpprime_y_zero_transform_bypass_flag
14506
- if (readBoolean()) {
14507
- // seq_scaling_matrix_present_flag
14508
- scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14509
- for (i = 0; i < scalingListCount; i++) {
14510
- if (readBoolean()) {
14511
- // seq_scaling_list_present_flag[ i ]
14512
- if (i < 6) {
14513
- skipScalingList(16);
14514
- } else {
14515
- skipScalingList(64);
14516
- }
14517
- }
14518
- }
14519
- }
14520
- }
14521
- skipUEG(); // log2_max_frame_num_minus4
14522
- var picOrderCntType = readUEG();
14523
- if (picOrderCntType === 0) {
14524
- readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14525
- } else if (picOrderCntType === 1) {
14526
- skipBits(1); // delta_pic_order_always_zero_flag
14527
- skipEG(); // offset_for_non_ref_pic
14528
- skipEG(); // offset_for_top_to_bottom_field
14529
- numRefFramesInPicOrderCntCycle = readUEG();
14530
- for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14531
- skipEG();
14532
- } // offset_for_ref_frame[ i ]
14533
- }
14534
- skipUEG(); // max_num_ref_frames
14535
- skipBits(1); // gaps_in_frame_num_value_allowed_flag
14536
- var picWidthInMbsMinus1 = readUEG();
14537
- var picHeightInMapUnitsMinus1 = readUEG();
14538
- var frameMbsOnlyFlag = readBits(1);
14539
- if (frameMbsOnlyFlag === 0) {
14540
- skipBits(1);
14541
- } // mb_adaptive_frame_field_flag
14542
-
14543
- skipBits(1); // direct_8x8_inference_flag
14544
- if (readBoolean()) {
14545
- // frame_cropping_flag
14546
- frameCropLeftOffset = readUEG();
14547
- frameCropRightOffset = readUEG();
14548
- frameCropTopOffset = readUEG();
14549
- frameCropBottomOffset = readUEG();
14550
- }
14551
- var pixelRatio = [1, 1];
14552
- if (readBoolean()) {
14553
- // vui_parameters_present_flag
14554
- if (readBoolean()) {
14555
- // aspect_ratio_info_present_flag
14556
- var aspectRatioIdc = readUByte();
14557
- switch (aspectRatioIdc) {
14558
- case 1:
14559
- pixelRatio = [1, 1];
14560
- break;
14561
- case 2:
14562
- pixelRatio = [12, 11];
14563
- break;
14564
- case 3:
14565
- pixelRatio = [10, 11];
14566
- break;
14567
- case 4:
14568
- pixelRatio = [16, 11];
14569
- break;
14570
- case 5:
14571
- pixelRatio = [40, 33];
14572
- break;
14573
- case 6:
14574
- pixelRatio = [24, 11];
14575
- break;
14576
- case 7:
14577
- pixelRatio = [20, 11];
14578
- break;
14579
- case 8:
14580
- pixelRatio = [32, 11];
14581
- break;
14582
- case 9:
14583
- pixelRatio = [80, 33];
14584
- break;
14585
- case 10:
14586
- pixelRatio = [18, 11];
14587
- break;
14588
- case 11:
14589
- pixelRatio = [15, 11];
14590
- break;
14591
- case 12:
14592
- pixelRatio = [64, 33];
14593
- break;
14594
- case 13:
14595
- pixelRatio = [160, 99];
14596
- break;
14597
- case 14:
14598
- pixelRatio = [4, 3];
14599
- break;
14600
- case 15:
14601
- pixelRatio = [3, 2];
14602
- break;
14603
- case 16:
14604
- pixelRatio = [2, 1];
14605
- break;
14606
- case 255:
14607
- {
14608
- pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
14609
- break;
14610
- }
14611
- }
14612
- }
14613
- }
14614
- return {
14615
- width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
14616
- height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
14617
- pixelRatio: pixelRatio
14618
- };
14619
- };
14620
- _proto.readSliceType = function readSliceType() {
14621
- // skip NALu type
14622
- this.readUByte();
14623
- // discard first_mb_in_slice
14624
- this.readUEG();
14625
- // return slice_type
14626
- return this.readUEG();
14627
14719
  };
14628
14720
  return ExpGolomb;
14629
14721
  }();
@@ -14634,9 +14726,9 @@
14634
14726
  return _BaseVideoParser.apply(this, arguments) || this;
14635
14727
  }
14636
14728
  var _proto = AvcVideoParser.prototype;
14637
- _proto.parseAVCPES = function parseAVCPES(track, textTrack, pes, last, duration) {
14729
+ _proto.parsePES = function parsePES(track, textTrack, pes, last, duration) {
14638
14730
  var _this = this;
14639
- var units = this.parseAVCNALu(track, pes.data);
14731
+ var units = this.parseNALu(track, pes.data);
14640
14732
  var VideoSample = this.VideoSample;
14641
14733
  var push;
14642
14734
  var spsfound = false;
@@ -14661,7 +14753,7 @@
14661
14753
  // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
14662
14754
  if (spsfound && data.length > 4) {
14663
14755
  // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
14664
- var sliceType = new ExpGolomb(data).readSliceType();
14756
+ var sliceType = _this.readSliceType(data);
14665
14757
  // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
14666
14758
  // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
14667
14759
  // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
@@ -14715,8 +14807,7 @@
14715
14807
  push = true;
14716
14808
  spsfound = true;
14717
14809
  var sps = unit.data;
14718
- var expGolombDecoder = new ExpGolomb(sps);
14719
- var config = expGolombDecoder.readSPS();
14810
+ var config = _this.readSPS(sps);
14720
14811
  if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) {
14721
14812
  track.width = config.width;
14722
14813
  track.height = config.height;
@@ -14772,109 +14863,192 @@
14772
14863
  this.VideoSample = null;
14773
14864
  }
14774
14865
  };
14775
- _proto.parseAVCNALu = function parseAVCNALu(track, array) {
14776
- var len = array.byteLength;
14777
- var state = track.naluState || 0;
14778
- var lastState = state;
14779
- var units = [];
14780
- var i = 0;
14781
- var value;
14782
- var overflow;
14783
- var unitType;
14784
- var lastUnitStart = -1;
14785
- var lastUnitType = 0;
14786
- // logger.log('PES:' + Hex.hexDump(array));
14866
+ _proto.getNALuType = function getNALuType(data, offset) {
14867
+ return data[offset] & 0x1f;
14868
+ };
14869
+ _proto.readSliceType = function readSliceType(data) {
14870
+ var eg = new ExpGolomb(data);
14871
+ // skip NALu type
14872
+ eg.readUByte();
14873
+ // discard first_mb_in_slice
14874
+ eg.readUEG();
14875
+ // return slice_type
14876
+ return eg.readUEG();
14877
+ }
14787
14878
 
14788
- if (state === -1) {
14789
- // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14790
- lastUnitStart = 0;
14791
- // NALu type is value read from offset 0
14792
- lastUnitType = array[0] & 0x1f;
14793
- state = 0;
14794
- i = 1;
14795
- }
14796
- while (i < len) {
14797
- value = array[i++];
14798
- // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14799
- if (!state) {
14800
- state = value ? 0 : 1;
14801
- continue;
14802
- }
14803
- if (state === 1) {
14804
- state = value ? 0 : 2;
14805
- continue;
14879
+ /**
14880
+ * The scaling list is optionally transmitted as part of a sequence parameter
14881
+ * set and is not relevant to transmuxing.
14882
+ * @param count the number of entries in this scaling list
14883
+ * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14884
+ */;
14885
+ _proto.skipScalingList = function skipScalingList(count, reader) {
14886
+ var lastScale = 8;
14887
+ var nextScale = 8;
14888
+ var deltaScale;
14889
+ for (var j = 0; j < count; j++) {
14890
+ if (nextScale !== 0) {
14891
+ deltaScale = reader.readEG();
14892
+ nextScale = (lastScale + deltaScale + 256) % 256;
14806
14893
  }
14807
- // here we have state either equal to 2 or 3
14808
- if (!value) {
14809
- state = 3;
14810
- } else if (value === 1) {
14811
- overflow = i - state - 1;
14812
- if (lastUnitStart >= 0) {
14813
- var unit = {
14814
- data: array.subarray(lastUnitStart, overflow),
14815
- type: lastUnitType
14816
- };
14817
- // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14818
- units.push(unit);
14819
- } else {
14820
- // lastUnitStart is undefined => this is the first start code found in this PES packet
14821
- // first check if start code delimiter is overlapping between 2 PES packets,
14822
- // ie it started in last packet (lastState not zero)
14823
- // and ended at the beginning of this PES packet (i <= 4 - lastState)
14824
- var lastUnit = this.getLastNalUnit(track.samples);
14825
- if (lastUnit) {
14826
- if (lastState && i <= 4 - lastState) {
14827
- // start delimiter overlapping between PES packets
14828
- // strip start delimiter bytes from the end of last NAL unit
14829
- // check if lastUnit had a state different from zero
14830
- if (lastUnit.state) {
14831
- // strip last bytes
14832
- lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14833
- }
14834
- }
14835
- // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14894
+ lastScale = nextScale === 0 ? lastScale : nextScale;
14895
+ }
14896
+ }
14836
14897
 
14837
- if (overflow > 0) {
14838
- // logger.log('first NALU found with overflow:' + overflow);
14839
- lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14840
- lastUnit.state = 0;
14841
- }
14842
- }
14843
- }
14844
- // check if we can read unit type
14845
- if (i < len) {
14846
- unitType = array[i] & 0x1f;
14847
- // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14848
- lastUnitStart = i;
14849
- lastUnitType = unitType;
14850
- state = 0;
14851
- } else {
14852
- // not enough byte to read unit type. let's read it on next PES parsing
14853
- state = -1;
14898
+ /**
14899
+ * Read a sequence parameter set and return some interesting video
14900
+ * properties. A sequence parameter set is the H264 metadata that
14901
+ * describes the properties of upcoming video frames.
14902
+ * @returns an object with configuration parsed from the
14903
+ * sequence parameter set, including the dimensions of the
14904
+ * associated video frames.
14905
+ */;
14906
+ _proto.readSPS = function readSPS(sps) {
14907
+ var eg = new ExpGolomb(sps);
14908
+ var frameCropLeftOffset = 0;
14909
+ var frameCropRightOffset = 0;
14910
+ var frameCropTopOffset = 0;
14911
+ var frameCropBottomOffset = 0;
14912
+ var numRefFramesInPicOrderCntCycle;
14913
+ var scalingListCount;
14914
+ var i;
14915
+ var readUByte = eg.readUByte.bind(eg);
14916
+ var readBits = eg.readBits.bind(eg);
14917
+ var readUEG = eg.readUEG.bind(eg);
14918
+ var readBoolean = eg.readBoolean.bind(eg);
14919
+ var skipBits = eg.skipBits.bind(eg);
14920
+ var skipEG = eg.skipEG.bind(eg);
14921
+ var skipUEG = eg.skipUEG.bind(eg);
14922
+ var skipScalingList = this.skipScalingList.bind(this);
14923
+ readUByte();
14924
+ var profileIdc = readUByte(); // profile_idc
14925
+ readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14926
+ skipBits(3); // reserved_zero_3bits u(3),
14927
+ readUByte(); // level_idc u(8)
14928
+ skipUEG(); // seq_parameter_set_id
14929
+ // some profiles have more optional data we don't need
14930
+ if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14931
+ var chromaFormatIdc = readUEG();
14932
+ if (chromaFormatIdc === 3) {
14933
+ skipBits(1);
14934
+ } // separate_colour_plane_flag
14935
+
14936
+ skipUEG(); // bit_depth_luma_minus8
14937
+ skipUEG(); // bit_depth_chroma_minus8
14938
+ skipBits(1); // qpprime_y_zero_transform_bypass_flag
14939
+ if (readBoolean()) {
14940
+ // seq_scaling_matrix_present_flag
14941
+ scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14942
+ for (i = 0; i < scalingListCount; i++) {
14943
+ if (readBoolean()) {
14944
+ // seq_scaling_list_present_flag[ i ]
14945
+ if (i < 6) {
14946
+ skipScalingList(16, eg);
14947
+ } else {
14948
+ skipScalingList(64, eg);
14949
+ }
14950
+ }
14854
14951
  }
14855
- } else {
14856
- state = 0;
14857
14952
  }
14858
14953
  }
14859
- if (lastUnitStart >= 0 && state >= 0) {
14860
- var _unit = {
14861
- data: array.subarray(lastUnitStart, len),
14862
- type: lastUnitType,
14863
- state: state
14864
- };
14865
- units.push(_unit);
14866
- // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14954
+ skipUEG(); // log2_max_frame_num_minus4
14955
+ var picOrderCntType = readUEG();
14956
+ if (picOrderCntType === 0) {
14957
+ readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14958
+ } else if (picOrderCntType === 1) {
14959
+ skipBits(1); // delta_pic_order_always_zero_flag
14960
+ skipEG(); // offset_for_non_ref_pic
14961
+ skipEG(); // offset_for_top_to_bottom_field
14962
+ numRefFramesInPicOrderCntCycle = readUEG();
14963
+ for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14964
+ skipEG();
14965
+ } // offset_for_ref_frame[ i ]
14867
14966
  }
14868
- // no NALu found
14869
- if (units.length === 0) {
14870
- // append pes.data to previous NAL unit
14871
- var _lastUnit = this.getLastNalUnit(track.samples);
14872
- if (_lastUnit) {
14873
- _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14967
+ skipUEG(); // max_num_ref_frames
14968
+ skipBits(1); // gaps_in_frame_num_value_allowed_flag
14969
+ var picWidthInMbsMinus1 = readUEG();
14970
+ var picHeightInMapUnitsMinus1 = readUEG();
14971
+ var frameMbsOnlyFlag = readBits(1);
14972
+ if (frameMbsOnlyFlag === 0) {
14973
+ skipBits(1);
14974
+ } // mb_adaptive_frame_field_flag
14975
+
14976
+ skipBits(1); // direct_8x8_inference_flag
14977
+ if (readBoolean()) {
14978
+ // frame_cropping_flag
14979
+ frameCropLeftOffset = readUEG();
14980
+ frameCropRightOffset = readUEG();
14981
+ frameCropTopOffset = readUEG();
14982
+ frameCropBottomOffset = readUEG();
14983
+ }
14984
+ var pixelRatio = [1, 1];
14985
+ if (readBoolean()) {
14986
+ // vui_parameters_present_flag
14987
+ if (readBoolean()) {
14988
+ // aspect_ratio_info_present_flag
14989
+ var aspectRatioIdc = readUByte();
14990
+ switch (aspectRatioIdc) {
14991
+ case 1:
14992
+ pixelRatio = [1, 1];
14993
+ break;
14994
+ case 2:
14995
+ pixelRatio = [12, 11];
14996
+ break;
14997
+ case 3:
14998
+ pixelRatio = [10, 11];
14999
+ break;
15000
+ case 4:
15001
+ pixelRatio = [16, 11];
15002
+ break;
15003
+ case 5:
15004
+ pixelRatio = [40, 33];
15005
+ break;
15006
+ case 6:
15007
+ pixelRatio = [24, 11];
15008
+ break;
15009
+ case 7:
15010
+ pixelRatio = [20, 11];
15011
+ break;
15012
+ case 8:
15013
+ pixelRatio = [32, 11];
15014
+ break;
15015
+ case 9:
15016
+ pixelRatio = [80, 33];
15017
+ break;
15018
+ case 10:
15019
+ pixelRatio = [18, 11];
15020
+ break;
15021
+ case 11:
15022
+ pixelRatio = [15, 11];
15023
+ break;
15024
+ case 12:
15025
+ pixelRatio = [64, 33];
15026
+ break;
15027
+ case 13:
15028
+ pixelRatio = [160, 99];
15029
+ break;
15030
+ case 14:
15031
+ pixelRatio = [4, 3];
15032
+ break;
15033
+ case 15:
15034
+ pixelRatio = [3, 2];
15035
+ break;
15036
+ case 16:
15037
+ pixelRatio = [2, 1];
15038
+ break;
15039
+ case 255:
15040
+ {
15041
+ pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
15042
+ break;
15043
+ }
15044
+ }
14874
15045
  }
14875
15046
  }
14876
- track.naluState = state;
14877
- return units;
15047
+ return {
15048
+ width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
15049
+ height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
15050
+ pixelRatio: pixelRatio
15051
+ };
14878
15052
  };
14879
15053
  return AvcVideoParser;
14880
15054
  }(BaseVideoParser);
@@ -14894,7 +15068,7 @@
14894
15068
  }
14895
15069
  var _proto = SampleAesDecrypter.prototype;
14896
15070
  _proto.decryptBuffer = function decryptBuffer(encryptedData) {
14897
- return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
15071
+ return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc);
14898
15072
  }
14899
15073
 
14900
15074
  // AAC - encrypt all full 16 bytes blocks starting from offset 16
@@ -15013,7 +15187,7 @@
15013
15187
  this.observer = observer;
15014
15188
  this.config = config;
15015
15189
  this.typeSupported = typeSupported;
15016
- this.videoParser = new AvcVideoParser();
15190
+ this.videoParser = null;
15017
15191
  }
15018
15192
  TSDemuxer.probe = function probe(data) {
15019
15193
  var syncOffset = TSDemuxer.syncOffset(data);
@@ -15183,7 +15357,16 @@
15183
15357
  case videoPid:
15184
15358
  if (stt) {
15185
15359
  if (videoData && (pes = parsePES(videoData))) {
15186
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
15360
+ if (this.videoParser === null) {
15361
+ switch (videoTrack.segmentCodec) {
15362
+ case 'avc':
15363
+ this.videoParser = new AvcVideoParser();
15364
+ break;
15365
+ }
15366
+ }
15367
+ if (this.videoParser !== null) {
15368
+ this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
15369
+ }
15187
15370
  }
15188
15371
  videoData = {
15189
15372
  data: [],
@@ -15341,8 +15524,17 @@
15341
15524
  // try to parse last PES packets
15342
15525
  var pes;
15343
15526
  if (videoData && (pes = parsePES(videoData))) {
15344
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
15345
- videoTrack.pesData = null;
15527
+ if (this.videoParser === null) {
15528
+ switch (videoTrack.segmentCodec) {
15529
+ case 'avc':
15530
+ this.videoParser = new AvcVideoParser();
15531
+ break;
15532
+ }
15533
+ }
15534
+ if (this.videoParser !== null) {
15535
+ this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
15536
+ videoTrack.pesData = null;
15537
+ }
15346
15538
  } else {
15347
15539
  // either avcData null or PES truncated, keep it for next frag parsing
15348
15540
  videoTrack.pesData = videoData;
@@ -15644,7 +15836,10 @@
15644
15836
  logger.warn('Unsupported EC-3 in M2TS found');
15645
15837
  break;
15646
15838
  case 0x24:
15647
- logger.warn('Unsupported HEVC in M2TS found');
15839
+ // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
15840
+ {
15841
+ logger.warn('Unsupported HEVC in M2TS found');
15842
+ }
15648
15843
  break;
15649
15844
  }
15650
15845
  // move to the next table entry
@@ -15872,6 +16067,8 @@
15872
16067
  avc1: [],
15873
16068
  // codingname
15874
16069
  avcC: [],
16070
+ hvc1: [],
16071
+ hvcC: [],
15875
16072
  btrt: [],
15876
16073
  dinf: [],
15877
16074
  dref: [],
@@ -16299,8 +16496,10 @@
16299
16496
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
16300
16497
  }
16301
16498
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
16302
- } else {
16499
+ } else if (track.segmentCodec === 'avc') {
16303
16500
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
16501
+ } else {
16502
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
16304
16503
  }
16305
16504
  };
16306
16505
  MP4.tkhd = function tkhd(track) {
@@ -16438,6 +16637,84 @@
16438
16637
  var result = appendUint8Array(MP4.FTYP, movie);
16439
16638
  return result;
16440
16639
  };
16640
+ MP4.hvc1 = function hvc1(track) {
16641
+ var ps = track.params;
16642
+ var units = [track.vps, track.sps, track.pps];
16643
+ var NALuLengthSize = 4;
16644
+ 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]);
16645
+
16646
+ // compute hvcC size in bytes
16647
+ var length = config.length;
16648
+ for (var i = 0; i < units.length; i += 1) {
16649
+ length += 3;
16650
+ for (var j = 0; j < units[i].length; j += 1) {
16651
+ length += 2 + units[i][j].length;
16652
+ }
16653
+ }
16654
+ var hvcC = new Uint8Array(length);
16655
+ hvcC.set(config, 0);
16656
+ length = config.length;
16657
+ // append parameter set units: one vps, one or more sps and pps
16658
+ var iMax = units.length - 1;
16659
+ for (var _i = 0; _i < units.length; _i += 1) {
16660
+ hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length);
16661
+ length += 3;
16662
+ for (var _j = 0; _j < units[_i].length; _j += 1) {
16663
+ hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length);
16664
+ length += 2;
16665
+ hvcC.set(units[_i][_j], length);
16666
+ length += units[_i][_j].length;
16667
+ }
16668
+ }
16669
+ var hvcc = MP4.box(MP4.types.hvcC, hvcC);
16670
+ var width = track.width;
16671
+ var height = track.height;
16672
+ var hSpacing = track.pixelRatio[0];
16673
+ var vSpacing = track.pixelRatio[1];
16674
+ return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
16675
+ // reserved
16676
+ 0x00, 0x00, 0x00,
16677
+ // reserved
16678
+ 0x00, 0x01,
16679
+ // data_reference_index
16680
+ 0x00, 0x00,
16681
+ // pre_defined
16682
+ 0x00, 0x00,
16683
+ // reserved
16684
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16685
+ // pre_defined
16686
+ width >> 8 & 0xff, width & 0xff,
16687
+ // width
16688
+ height >> 8 & 0xff, height & 0xff,
16689
+ // height
16690
+ 0x00, 0x48, 0x00, 0x00,
16691
+ // horizresolution
16692
+ 0x00, 0x48, 0x00, 0x00,
16693
+ // vertresolution
16694
+ 0x00, 0x00, 0x00, 0x00,
16695
+ // reserved
16696
+ 0x00, 0x01,
16697
+ // frame_count
16698
+ 0x12, 0x64, 0x61, 0x69, 0x6c,
16699
+ // dailymotion/hls.js
16700
+ 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,
16701
+ // compressorname
16702
+ 0x00, 0x18,
16703
+ // depth = 24
16704
+ 0x11, 0x11]),
16705
+ // pre_defined = -1
16706
+ hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
16707
+ // bufferSizeDB
16708
+ 0x00, 0x2d, 0xc6, 0xc0,
16709
+ // maxBitrate
16710
+ 0x00, 0x2d, 0xc6, 0xc0])),
16711
+ // avgBitrate
16712
+ MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
16713
+ // hSpacing
16714
+ hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
16715
+ // vSpacing
16716
+ vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
16717
+ };
16441
16718
  return MP4;
16442
16719
  }();
16443
16720
  MP4.types = void 0;
@@ -16824,9 +17101,9 @@
16824
17101
  var foundOverlap = delta < -1;
16825
17102
  if (foundHole || foundOverlap) {
16826
17103
  if (foundHole) {
16827
- logger.warn("AVC: " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
17104
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
16828
17105
  } else {
16829
- logger.warn("AVC: " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
17106
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
16830
17107
  }
16831
17108
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
16832
17109
  firstDTS = nextAvcDts;
@@ -16835,12 +17112,24 @@
16835
17112
  inputSamples[0].dts = firstDTS;
16836
17113
  inputSamples[0].pts = firstPTS;
16837
17114
  } else {
17115
+ var isPTSOrderRetained = true;
16838
17116
  for (var _i = 0; _i < inputSamples.length; _i++) {
16839
- if (inputSamples[_i].dts > firstPTS) {
17117
+ if (inputSamples[_i].dts > firstPTS && isPTSOrderRetained) {
16840
17118
  break;
16841
17119
  }
17120
+ var prevPTS = inputSamples[_i].pts;
16842
17121
  inputSamples[_i].dts -= delta;
16843
17122
  inputSamples[_i].pts -= delta;
17123
+
17124
+ // check to see if this sample's PTS order has changed
17125
+ // relative to the next one
17126
+ if (_i < inputSamples.length - 1) {
17127
+ var nextSamplePTS = inputSamples[_i + 1].pts;
17128
+ var currentSamplePTS = inputSamples[_i].pts;
17129
+ var currentOrder = nextSamplePTS <= currentSamplePTS;
17130
+ var prevOrder = nextSamplePTS <= prevPTS;
17131
+ isPTSOrderRetained = currentOrder == prevOrder;
17132
+ }
16844
17133
  }
16845
17134
  }
16846
17135
  logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms");
@@ -16988,7 +17277,7 @@
16988
17277
  }
16989
17278
  }
16990
17279
  }
16991
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
17280
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
16992
17281
  mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
16993
17282
  this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
16994
17283
  this.videoSampleDuration = mp4SampleDuration;
@@ -17123,7 +17412,7 @@
17123
17412
  logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap.");
17124
17413
  for (var j = 0; j < missing; j++) {
17125
17414
  var newStamp = Math.max(nextPts, 0);
17126
- var fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17415
+ var fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17127
17416
  if (!fillFrame) {
17128
17417
  logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
17129
17418
  fillFrame = sample.unit.subarray();
@@ -17251,7 +17540,7 @@
17251
17540
  // samples count of this segment's duration
17252
17541
  var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
17253
17542
  // silent frame
17254
- var silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17543
+ var silentFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17255
17544
  logger.warn('[mp4-remuxer]: remux empty Audio');
17256
17545
  // Can't remux if we can't generate a silent frame...
17257
17546
  if (!silentFrame) {
@@ -17638,13 +17927,15 @@
17638
17927
  duration = transmuxConfig.duration,
17639
17928
  initSegmentData = transmuxConfig.initSegmentData;
17640
17929
  var keyData = getEncryptionType(uintData, decryptdata);
17641
- if (keyData && keyData.method === 'AES-128') {
17930
+ if (keyData && isFullSegmentEncryption(keyData.method)) {
17642
17931
  var decrypter = this.getDecrypter();
17932
+ var aesMode = getAesModeFromFullSegmentMethod(keyData.method);
17933
+
17643
17934
  // Software decryption is synchronous; webCrypto is not
17644
17935
  if (decrypter.isSync()) {
17645
17936
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
17646
17937
  // data is handled in the flush() call
17647
- var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
17938
+ var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode);
17648
17939
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
17649
17940
  var loadingParts = chunkMeta.part > -1;
17650
17941
  if (loadingParts) {
@@ -17656,7 +17947,7 @@
17656
17947
  }
17657
17948
  uintData = new Uint8Array(decryptedData);
17658
17949
  } else {
17659
- this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(function (decryptedData) {
17950
+ this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(function (decryptedData) {
17660
17951
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
17661
17952
  // the decrypted data has been transmuxed
17662
17953
  var result = _this.push(decryptedData, null, chunkMeta);
@@ -18277,7 +18568,7 @@
18277
18568
  observer.on(Events.ERROR, forwardMessage);
18278
18569
 
18279
18570
  // forward logger events to main thread
18280
- var forwardWorkerLogs = function forwardWorkerLogs() {
18571
+ var forwardWorkerLogs = function forwardWorkerLogs(logger) {
18281
18572
  var _loop = function _loop(logFn) {
18282
18573
  var func = function func(message) {
18283
18574
  forwardMessage('workerLog', {
@@ -18298,8 +18589,8 @@
18298
18589
  {
18299
18590
  var config = JSON.parse(data.config);
18300
18591
  self.transmuxer = new Transmuxer(observer, data.typeSupported, config, data.vendor, data.id);
18301
- enableLogs(config.debug, data.id);
18302
- forwardWorkerLogs();
18592
+ var logger = enableLogs(config.debug, data.id);
18593
+ forwardWorkerLogs(logger);
18303
18594
  forwardMessage('init', null);
18304
18595
  break;
18305
18596
  }
@@ -18473,16 +18764,7 @@
18473
18764
  this.observer = new EventEmitter();
18474
18765
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
18475
18766
  this.observer.on(Events.ERROR, forwardMessage);
18476
- var MediaSource = getMediaSource(config.preferManagedMediaSource) || {
18477
- isTypeSupported: function isTypeSupported() {
18478
- return false;
18479
- }
18480
- };
18481
- var m2tsTypeSupported = {
18482
- mpeg: MediaSource.isTypeSupported('audio/mpeg'),
18483
- mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
18484
- ac3: false
18485
- };
18767
+ var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
18486
18768
 
18487
18769
  // navigator.vendor is not always available in Web Worker
18488
18770
  // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
@@ -18739,21 +19021,26 @@
18739
19021
  var MAX_START_GAP_JUMP = 2.0;
18740
19022
  var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
18741
19023
  var SKIP_BUFFER_RANGE_START = 0.05;
18742
- var GapController = /*#__PURE__*/function () {
19024
+ var GapController = /*#__PURE__*/function (_Logger) {
19025
+ _inheritsLoose(GapController, _Logger);
18743
19026
  function GapController(config, media, fragmentTracker, hls) {
18744
- this.config = void 0;
18745
- this.media = null;
18746
- this.fragmentTracker = void 0;
18747
- this.hls = void 0;
18748
- this.nudgeRetry = 0;
18749
- this.stallReported = false;
18750
- this.stalled = null;
18751
- this.moved = false;
18752
- this.seeking = false;
18753
- this.config = config;
18754
- this.media = media;
18755
- this.fragmentTracker = fragmentTracker;
18756
- this.hls = hls;
19027
+ var _this;
19028
+ _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
19029
+ _this.config = void 0;
19030
+ _this.media = null;
19031
+ _this.fragmentTracker = void 0;
19032
+ _this.hls = void 0;
19033
+ _this.nudgeRetry = 0;
19034
+ _this.stallReported = false;
19035
+ _this.stalled = null;
19036
+ _this.moved = false;
19037
+ _this.seeking = false;
19038
+ _this.ended = 0;
19039
+ _this.config = config;
19040
+ _this.media = media;
19041
+ _this.fragmentTracker = fragmentTracker;
19042
+ _this.hls = hls;
19043
+ return _this;
18757
19044
  }
18758
19045
  var _proto = GapController.prototype;
18759
19046
  _proto.destroy = function destroy() {
@@ -18768,7 +19055,7 @@
18768
19055
  *
18769
19056
  * @param lastCurrentTime - Previously read playhead position
18770
19057
  */;
18771
- _proto.poll = function poll(lastCurrentTime, activeFrag) {
19058
+ _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
18772
19059
  var config = this.config,
18773
19060
  media = this.media,
18774
19061
  stalled = this.stalled;
@@ -18783,6 +19070,7 @@
18783
19070
 
18784
19071
  // The playhead is moving, no-op
18785
19072
  if (currentTime !== lastCurrentTime) {
19073
+ this.ended = 0;
18786
19074
  this.moved = true;
18787
19075
  if (!seeking) {
18788
19076
  this.nudgeRetry = 0;
@@ -18791,7 +19079,7 @@
18791
19079
  // The playhead is now moving, but was previously stalled
18792
19080
  if (this.stallReported) {
18793
19081
  var _stalledDuration = self.performance.now() - stalled;
18794
- logger.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
19082
+ this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
18795
19083
  this.stallReported = false;
18796
19084
  }
18797
19085
  this.stalled = null;
@@ -18827,7 +19115,6 @@
18827
19115
  // Skip start gaps if we haven't played, but the last poll detected the start of a stall
18828
19116
  // The addition poll gives the browser a chance to jump the gap for us
18829
19117
  if (!this.moved && this.stalled !== null) {
18830
- var _level$details;
18831
19118
  // There is no playable buffer (seeked, waiting for buffer)
18832
19119
  var isBuffered = bufferInfo.len > 0;
18833
19120
  if (!isBuffered && !nextStart) {
@@ -18839,9 +19126,8 @@
18839
19126
  // When joining a live stream with audio tracks, account for live playlist window sliding by allowing
18840
19127
  // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
18841
19128
  // that begins over 1 target duration after the video start position.
18842
- var level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null;
18843
- var isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live;
18844
- var maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;
19129
+ var isLive = !!(levelDetails != null && levelDetails.live);
19130
+ var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
18845
19131
  var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
18846
19132
  if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
18847
19133
  if (!media.paused) {
@@ -18859,6 +19145,17 @@
18859
19145
  }
18860
19146
  var stalledDuration = tnow - stalled;
18861
19147
  if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
19148
+ // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
19149
+ if (state === State.ENDED && !(levelDetails && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
19150
+ if (stalledDuration < 1000 || this.ended) {
19151
+ return;
19152
+ }
19153
+ this.ended = currentTime;
19154
+ this.hls.trigger(Events.MEDIA_ENDED, {
19155
+ stalled: true
19156
+ });
19157
+ return;
19158
+ }
18862
19159
  // Report stalling after trying to fix
18863
19160
  this._reportStall(bufferInfo);
18864
19161
  if (!this.media) {
@@ -18900,7 +19197,7 @@
18900
19197
  // needs to cross some sort of threshold covering all source-buffers content
18901
19198
  // to start playing properly.
18902
19199
  if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
18903
- logger.warn('Trying to nudge playhead over buffer-hole');
19200
+ this.warn('Trying to nudge playhead over buffer-hole');
18904
19201
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
18905
19202
  // We only try to jump the hole if it's under the configured size
18906
19203
  // Reset stalled so to rearm watchdog timer
@@ -18922,7 +19219,7 @@
18922
19219
  // Report stalled error once
18923
19220
  this.stallReported = true;
18924
19221
  var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
18925
- logger.warn(error.message);
19222
+ this.warn(error.message);
18926
19223
  hls.trigger(Events.ERROR, {
18927
19224
  type: ErrorTypes.MEDIA_ERROR,
18928
19225
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -18986,7 +19283,7 @@
18986
19283
  }
18987
19284
  }
18988
19285
  var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
18989
- logger.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
19286
+ this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
18990
19287
  this.moved = true;
18991
19288
  this.stalled = null;
18992
19289
  media.currentTime = targetTime;
@@ -19025,7 +19322,7 @@
19025
19322
  var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
19026
19323
  // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
19027
19324
  var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
19028
- logger.warn(error.message);
19325
+ this.warn(error.message);
19029
19326
  media.currentTime = targetTime;
19030
19327
  hls.trigger(Events.ERROR, {
19031
19328
  type: ErrorTypes.MEDIA_ERROR,
@@ -19035,7 +19332,7 @@
19035
19332
  });
19036
19333
  } else {
19037
19334
  var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
19038
- logger.error(_error.message);
19335
+ this.error(_error.message);
19039
19336
  hls.trigger(Events.ERROR, {
19040
19337
  type: ErrorTypes.MEDIA_ERROR,
19041
19338
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -19045,14 +19342,14 @@
19045
19342
  }
19046
19343
  };
19047
19344
  return GapController;
19048
- }();
19345
+ }(Logger);
19049
19346
 
19050
19347
  var TICK_INTERVAL = 100; // how often to tick in ms
19051
19348
  var StreamController = /*#__PURE__*/function (_BaseStreamController) {
19052
19349
  _inheritsLoose(StreamController, _BaseStreamController);
19053
19350
  function StreamController(hls, fragmentTracker, keyLoader) {
19054
19351
  var _this;
19055
- _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN) || this;
19352
+ _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
19056
19353
  _this.audioCodecSwap = false;
19057
19354
  _this.gapController = null;
19058
19355
  _this.level = -1;
@@ -19060,27 +19357,43 @@
19060
19357
  _this.altAudio = false;
19061
19358
  _this.audioOnly = false;
19062
19359
  _this.fragPlaying = null;
19063
- _this.onvplaying = null;
19064
- _this.onvseeked = null;
19065
19360
  _this.fragLastKbps = 0;
19066
19361
  _this.couldBacktrack = false;
19067
19362
  _this.backtrackFragment = null;
19068
19363
  _this.audioCodecSwitch = false;
19069
19364
  _this.videoBuffer = null;
19070
- _this._registerListeners();
19365
+ _this.onMediaPlaying = function () {
19366
+ // tick to speed up FRAG_CHANGED triggering
19367
+ _this.tick();
19368
+ };
19369
+ _this.onMediaSeeked = function () {
19370
+ var media = _this.media;
19371
+ var currentTime = media ? media.currentTime : null;
19372
+ if (isFiniteNumber(currentTime)) {
19373
+ _this.log("Media seeked to " + currentTime.toFixed(3));
19374
+ }
19375
+
19376
+ // If seeked was issued before buffer was appended do not tick immediately
19377
+ var bufferInfo = _this.getMainFwdBufferInfo();
19378
+ if (bufferInfo === null || bufferInfo.len === 0) {
19379
+ _this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19380
+ return;
19381
+ }
19382
+
19383
+ // tick to speed up FRAG_CHANGED triggering
19384
+ _this.tick();
19385
+ };
19386
+ _this.registerListeners();
19071
19387
  return _this;
19072
19388
  }
19073
19389
  var _proto = StreamController.prototype;
19074
- _proto._registerListeners = function _registerListeners() {
19390
+ _proto.registerListeners = function registerListeners() {
19391
+ _BaseStreamController.prototype.registerListeners.call(this);
19075
19392
  var hls = this.hls;
19076
- hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19077
- hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19078
- hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19079
19393
  hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19080
19394
  hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
19081
19395
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19082
19396
  hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19083
- hls.on(Events.ERROR, this.onError, this);
19084
19397
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19085
19398
  hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19086
19399
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19088,15 +19401,12 @@
19088
19401
  hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
19089
19402
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19090
19403
  };
19091
- _proto._unregisterListeners = function _unregisterListeners() {
19404
+ _proto.unregisterListeners = function unregisterListeners() {
19405
+ _BaseStreamController.prototype.unregisterListeners.call(this);
19092
19406
  var hls = this.hls;
19093
- hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19094
- hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19095
- hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19096
19407
  hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19097
19408
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19098
19409
  hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19099
- hls.off(Events.ERROR, this.onError, this);
19100
19410
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19101
19411
  hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19102
19412
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19105,7 +19415,9 @@
19105
19415
  hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19106
19416
  };
19107
19417
  _proto.onHandlerDestroying = function onHandlerDestroying() {
19108
- this._unregisterListeners();
19418
+ // @ts-ignore
19419
+ this.onMediaPlaying = this.onMediaSeeked = null;
19420
+ this.unregisterListeners();
19109
19421
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
19110
19422
  };
19111
19423
  _proto.startLoad = function startLoad(startPosition) {
@@ -19215,7 +19527,7 @@
19215
19527
  if (this.altAudio && this.audioOnly) {
19216
19528
  return;
19217
19529
  }
19218
- if (!(levels != null && levels[level])) {
19530
+ if (!this.buffering || !(levels != null && levels[level])) {
19219
19531
  return;
19220
19532
  }
19221
19533
  var levelInfo = levels[level];
@@ -19421,18 +19733,15 @@
19421
19733
  _proto.onMediaAttached = function onMediaAttached(event, data) {
19422
19734
  _BaseStreamController.prototype.onMediaAttached.call(this, event, data);
19423
19735
  var media = data.media;
19424
- this.onvplaying = this.onMediaPlaying.bind(this);
19425
- this.onvseeked = this.onMediaSeeked.bind(this);
19426
- media.addEventListener('playing', this.onvplaying);
19427
- media.addEventListener('seeked', this.onvseeked);
19736
+ media.addEventListener('playing', this.onMediaPlaying);
19737
+ media.addEventListener('seeked', this.onMediaSeeked);
19428
19738
  this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
19429
19739
  };
19430
19740
  _proto.onMediaDetaching = function onMediaDetaching() {
19431
19741
  var media = this.media;
19432
- if (media && this.onvplaying && this.onvseeked) {
19433
- media.removeEventListener('playing', this.onvplaying);
19434
- media.removeEventListener('seeked', this.onvseeked);
19435
- this.onvplaying = this.onvseeked = null;
19742
+ if (media) {
19743
+ media.removeEventListener('playing', this.onMediaPlaying);
19744
+ media.removeEventListener('seeked', this.onMediaSeeked);
19436
19745
  this.videoBuffer = null;
19437
19746
  }
19438
19747
  this.fragPlaying = null;
@@ -19442,27 +19751,6 @@
19442
19751
  }
19443
19752
  _BaseStreamController.prototype.onMediaDetaching.call(this);
19444
19753
  };
19445
- _proto.onMediaPlaying = function onMediaPlaying() {
19446
- // tick to speed up FRAG_CHANGED triggering
19447
- this.tick();
19448
- };
19449
- _proto.onMediaSeeked = function onMediaSeeked() {
19450
- var media = this.media;
19451
- var currentTime = media ? media.currentTime : null;
19452
- if (isFiniteNumber(currentTime)) {
19453
- this.log("Media seeked to " + currentTime.toFixed(3));
19454
- }
19455
-
19456
- // If seeked was issued before buffer was appended do not tick immediately
19457
- var bufferInfo = this.getMainFwdBufferInfo();
19458
- if (bufferInfo === null || bufferInfo.len === 0) {
19459
- this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19460
- return;
19461
- }
19462
-
19463
- // tick to speed up FRAG_CHANGED triggering
19464
- this.tick();
19465
- };
19466
19754
  _proto.onManifestLoading = function onManifestLoading() {
19467
19755
  // reset buffer on manifest loading
19468
19756
  this.log('Trigger BUFFER_RESET');
@@ -19743,8 +20031,10 @@
19743
20031
  }
19744
20032
  if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
19745
20033
  // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
19746
- var activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
19747
- gapController.poll(this.lastCurrentTime, activeFrag);
20034
+ var state = this.state;
20035
+ var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
20036
+ var levelDetails = this.getLevelDetails();
20037
+ gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
19748
20038
  }
19749
20039
  this.lastCurrentTime = media.currentTime;
19750
20040
  };
@@ -20209,9 +20499,12 @@
20209
20499
  * The configuration object provided on player instantiation.
20210
20500
  */
20211
20501
  this.userConfig = void 0;
20502
+ /**
20503
+ * The logger functions used by this player instance, configured on player instantiation.
20504
+ */
20505
+ this.logger = void 0;
20212
20506
  this.coreComponents = void 0;
20213
20507
  this.networkControllers = void 0;
20214
- this.started = false;
20215
20508
  this._emitter = new EventEmitter();
20216
20509
  this._autoLevelCapping = -1;
20217
20510
  this._maxHdcpLevel = null;
@@ -20228,11 +20521,11 @@
20228
20521
  this._media = null;
20229
20522
  this.url = null;
20230
20523
  this.triggeringException = void 0;
20231
- enableLogs(userConfig.debug || false, 'Hls instance');
20232
- var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
20524
+ var logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance');
20525
+ var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
20233
20526
  this.userConfig = userConfig;
20234
20527
  if (config.progressive) {
20235
- enableStreamingMode(config);
20528
+ enableStreamingMode(config, logger);
20236
20529
  }
20237
20530
 
20238
20531
  // core controllers and network loaders
@@ -20340,7 +20633,7 @@
20340
20633
  try {
20341
20634
  return this.emit(event, event, eventObject);
20342
20635
  } catch (error) {
20343
- logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20636
+ this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20344
20637
  // Prevent recursion in error event handlers that throw #5497
20345
20638
  if (!this.triggeringException) {
20346
20639
  this.triggeringException = true;
@@ -20366,7 +20659,7 @@
20366
20659
  * Dispose of the instance
20367
20660
  */;
20368
20661
  _proto.destroy = function destroy() {
20369
- logger.log('destroy');
20662
+ this.logger.log('destroy');
20370
20663
  this.trigger(Events.DESTROYING, undefined);
20371
20664
  this.detachMedia();
20372
20665
  this.removeAllListeners();
@@ -20391,7 +20684,7 @@
20391
20684
  * Attaches Hls.js to a media element
20392
20685
  */;
20393
20686
  _proto.attachMedia = function attachMedia(media) {
20394
- logger.log('attachMedia');
20687
+ this.logger.log('attachMedia');
20395
20688
  this._media = media;
20396
20689
  this.trigger(Events.MEDIA_ATTACHING, {
20397
20690
  media: media
@@ -20402,7 +20695,7 @@
20402
20695
  * Detach Hls.js from the media
20403
20696
  */;
20404
20697
  _proto.detachMedia = function detachMedia() {
20405
- logger.log('detachMedia');
20698
+ this.logger.log('detachMedia');
20406
20699
  this.trigger(Events.MEDIA_DETACHING, undefined);
20407
20700
  this._media = null;
20408
20701
  }
@@ -20419,7 +20712,7 @@
20419
20712
  });
20420
20713
  this._autoLevelCapping = -1;
20421
20714
  this._maxHdcpLevel = null;
20422
- logger.log("loadSource:" + loadingSource);
20715
+ this.logger.log("loadSource:" + loadingSource);
20423
20716
  if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
20424
20717
  this.detachMedia();
20425
20718
  this.attachMedia(media);
@@ -20441,8 +20734,7 @@
20441
20734
  if (startPosition === void 0) {
20442
20735
  startPosition = -1;
20443
20736
  }
20444
- logger.log("startLoad(" + startPosition + ")");
20445
- this.started = true;
20737
+ this.logger.log("startLoad(" + startPosition + ")");
20446
20738
  this.networkControllers.forEach(function (controller) {
20447
20739
  controller.startLoad(startPosition);
20448
20740
  });
@@ -20452,34 +20744,31 @@
20452
20744
  * Stop loading of any stream data.
20453
20745
  */;
20454
20746
  _proto.stopLoad = function stopLoad() {
20455
- logger.log('stopLoad');
20456
- this.started = false;
20747
+ this.logger.log('stopLoad');
20457
20748
  this.networkControllers.forEach(function (controller) {
20458
20749
  controller.stopLoad();
20459
20750
  });
20460
20751
  }
20461
20752
 
20462
20753
  /**
20463
- * Resumes stream controller segment loading if previously started.
20754
+ * Resumes stream controller segment loading after `pauseBuffering` has been called.
20464
20755
  */;
20465
20756
  _proto.resumeBuffering = function resumeBuffering() {
20466
- if (this.started) {
20467
- this.networkControllers.forEach(function (controller) {
20468
- if ('fragmentLoader' in controller) {
20469
- controller.startLoad(-1);
20470
- }
20471
- });
20472
- }
20757
+ this.networkControllers.forEach(function (controller) {
20758
+ if (controller.resumeBuffering) {
20759
+ controller.resumeBuffering();
20760
+ }
20761
+ });
20473
20762
  }
20474
20763
 
20475
20764
  /**
20476
- * Stops stream controller segment loading without changing 'started' state like stopLoad().
20765
+ * Prevents stream controller from loading new segments until `resumeBuffering` is called.
20477
20766
  * This allows for media buffering to be paused without interupting playlist loading.
20478
20767
  */;
20479
20768
  _proto.pauseBuffering = function pauseBuffering() {
20480
20769
  this.networkControllers.forEach(function (controller) {
20481
- if ('fragmentLoader' in controller) {
20482
- controller.stopLoad();
20770
+ if (controller.pauseBuffering) {
20771
+ controller.pauseBuffering();
20483
20772
  }
20484
20773
  });
20485
20774
  }
@@ -20488,7 +20777,7 @@
20488
20777
  * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
20489
20778
  */;
20490
20779
  _proto.swapAudioCodec = function swapAudioCodec() {
20491
- logger.log('swapAudioCodec');
20780
+ this.logger.log('swapAudioCodec');
20492
20781
  this.streamController.swapAudioCodec();
20493
20782
  }
20494
20783
 
@@ -20499,7 +20788,7 @@
20499
20788
  * Automatic recovery of media-errors by this process is configurable.
20500
20789
  */;
20501
20790
  _proto.recoverMediaError = function recoverMediaError() {
20502
- logger.log('recoverMediaError');
20791
+ this.logger.log('recoverMediaError');
20503
20792
  var media = this._media;
20504
20793
  this.detachMedia();
20505
20794
  if (media) {
@@ -20554,7 +20843,7 @@
20554
20843
  * Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection.
20555
20844
  */,
20556
20845
  set: function set(newLevel) {
20557
- logger.log("set currentLevel:" + newLevel);
20846
+ this.logger.log("set currentLevel:" + newLevel);
20558
20847
  this.levelController.manualLevel = newLevel;
20559
20848
  this.streamController.immediateLevelSwitch();
20560
20849
  }
@@ -20575,7 +20864,7 @@
20575
20864
  * @param newLevel - Pass -1 for automatic level selection
20576
20865
  */,
20577
20866
  set: function set(newLevel) {
20578
- logger.log("set nextLevel:" + newLevel);
20867
+ this.logger.log("set nextLevel:" + newLevel);
20579
20868
  this.levelController.manualLevel = newLevel;
20580
20869
  this.streamController.nextLevelSwitch();
20581
20870
  }
@@ -20596,7 +20885,7 @@
20596
20885
  * @param newLevel - Pass -1 for automatic level selection
20597
20886
  */,
20598
20887
  set: function set(newLevel) {
20599
- logger.log("set loadLevel:" + newLevel);
20888
+ this.logger.log("set loadLevel:" + newLevel);
20600
20889
  this.levelController.manualLevel = newLevel;
20601
20890
  }
20602
20891
 
@@ -20631,7 +20920,7 @@
20631
20920
  * Sets "first-level", see getter.
20632
20921
  */,
20633
20922
  set: function set(newLevel) {
20634
- logger.log("set firstLevel:" + newLevel);
20923
+ this.logger.log("set firstLevel:" + newLevel);
20635
20924
  this.levelController.firstLevel = newLevel;
20636
20925
  }
20637
20926
 
@@ -20658,7 +20947,7 @@
20658
20947
  * (determined from download of first segment)
20659
20948
  */,
20660
20949
  set: function set(newLevel) {
20661
- logger.log("set startLevel:" + newLevel);
20950
+ this.logger.log("set startLevel:" + newLevel);
20662
20951
  // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
20663
20952
  if (newLevel !== -1) {
20664
20953
  newLevel = Math.max(newLevel, this.minAutoLevel);
@@ -20711,7 +21000,7 @@
20711
21000
  */
20712
21001
  function set(newLevel) {
20713
21002
  if (this._autoLevelCapping !== newLevel) {
20714
- logger.log("set autoLevelCapping:" + newLevel);
21003
+ this.logger.log("set autoLevelCapping:" + newLevel);
20715
21004
  this._autoLevelCapping = newLevel;
20716
21005
  this.levelController.checkMaxAutoUpdated();
20717
21006
  }
@@ -21036,7 +21325,7 @@
21036
21325
  * Get the video-dev/hls.js package version.
21037
21326
  */
21038
21327
  function get() {
21039
- return "1.5.5";
21328
+ return "1.5.6-0.canary.10001";
21040
21329
  }
21041
21330
  }, {
21042
21331
  key: "Events",