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

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