hls.js 1.5.7 → 1.5.8-0.canary.10044

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 (72) hide show
  1. package/README.md +2 -1
  2. package/dist/hls-demo.js +10 -0
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +2314 -1298
  5. package/dist/hls.js.d.ts +97 -84
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1486 -1075
  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 +1195 -789
  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 +1979 -982
  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 +22 -22
  20. package/src/config.ts +3 -2
  21. package/src/controller/abr-controller.ts +24 -20
  22. package/src/controller/audio-stream-controller.ts +68 -74
  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 +157 -36
  26. package/src/controller/buffer-controller.ts +203 -67
  27. package/src/controller/buffer-operation-queue.ts +16 -19
  28. package/src/controller/cap-level-controller.ts +2 -2
  29. package/src/controller/cmcd-controller.ts +27 -6
  30. package/src/controller/content-steering-controller.ts +8 -6
  31. package/src/controller/eme-controller.ts +9 -22
  32. package/src/controller/error-controller.ts +6 -8
  33. package/src/controller/fps-controller.ts +2 -3
  34. package/src/controller/fragment-tracker.ts +15 -11
  35. package/src/controller/gap-controller.ts +43 -16
  36. package/src/controller/latency-controller.ts +9 -11
  37. package/src/controller/level-controller.ts +12 -18
  38. package/src/controller/stream-controller.ts +36 -31
  39. package/src/controller/subtitle-stream-controller.ts +28 -40
  40. package/src/controller/subtitle-track-controller.ts +5 -3
  41. package/src/controller/timeline-controller.ts +23 -30
  42. package/src/crypt/aes-crypto.ts +21 -2
  43. package/src/crypt/decrypter-aes-mode.ts +4 -0
  44. package/src/crypt/decrypter.ts +32 -18
  45. package/src/crypt/fast-aes-key.ts +24 -5
  46. package/src/demux/audio/adts.ts +9 -4
  47. package/src/demux/sample-aes.ts +2 -0
  48. package/src/demux/transmuxer-interface.ts +4 -12
  49. package/src/demux/transmuxer-worker.ts +4 -4
  50. package/src/demux/transmuxer.ts +16 -3
  51. package/src/demux/tsdemuxer.ts +71 -37
  52. package/src/demux/video/avc-video-parser.ts +208 -119
  53. package/src/demux/video/base-video-parser.ts +134 -2
  54. package/src/demux/video/exp-golomb.ts +0 -208
  55. package/src/demux/video/hevc-video-parser.ts +746 -0
  56. package/src/events.ts +7 -0
  57. package/src/hls.ts +49 -37
  58. package/src/loader/fragment-loader.ts +9 -2
  59. package/src/loader/key-loader.ts +2 -0
  60. package/src/loader/level-key.ts +10 -9
  61. package/src/loader/playlist-loader.ts +4 -5
  62. package/src/remux/mp4-generator.ts +196 -1
  63. package/src/remux/mp4-remuxer.ts +23 -7
  64. package/src/task-loop.ts +5 -2
  65. package/src/types/component-api.ts +2 -0
  66. package/src/types/demuxer.ts +3 -0
  67. package/src/types/events.ts +4 -0
  68. package/src/utils/buffer-helper.ts +12 -31
  69. package/src/utils/codecs.ts +34 -5
  70. package/src/utils/encryption-methods-util.ts +21 -0
  71. package/src/utils/logger.ts +54 -24
  72. package/src/utils/mp4-tools.ts +4 -2
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.7");
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.8-0.canary.10044");
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.
@@ -1811,7 +1837,7 @@
1811
1837
  {
1812
1838
  var codecBox = findBox(sampleEntries, [fourCC])[0];
1813
1839
  var esdsBox = findBox(codecBox.subarray(28), ['esds'])[0];
1814
- if (esdsBox && esdsBox.length > 12) {
1840
+ if (esdsBox && esdsBox.length > 7) {
1815
1841
  var i = 4;
1816
1842
  // ES Descriptor tag
1817
1843
  if (esdsBox[i++] !== 0x03) {
@@ -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;
@@ -6765,6 +6813,9 @@
6765
6813
  var fragCurrent = this.fragCurrent,
6766
6814
  partCurrent = this.partCurrent,
6767
6815
  hls = this.hls;
6816
+ if (hls.levels.length <= 1) {
6817
+ return hls.loadLevel;
6818
+ }
6768
6819
  var maxAutoLevel = hls.maxAutoLevel,
6769
6820
  config = hls.config,
6770
6821
  minAutoLevel = hls.minAutoLevel;
@@ -6795,13 +6846,13 @@
6795
6846
  // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
6796
6847
  var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
6797
6848
  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");
6849
+ this.info("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms");
6799
6850
  // don't use conservative factor on bitrate test
6800
6851
  bwFactor = bwUpFactor = 1;
6801
6852
  }
6802
6853
  }
6803
6854
  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);
6855
+ this.info((bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel);
6805
6856
  if (bestLevel > -1) {
6806
6857
  return bestLevel;
6807
6858
  }
@@ -6869,7 +6920,7 @@
6869
6920
  currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0];
6870
6921
  currentFrameRate = minFramerate;
6871
6922
  currentBw = Math.max(currentBw, minBitrate);
6872
- logger.log("[abr] picked start tier " + JSON.stringify(startTier));
6923
+ this.log("picked start tier " + JSON.stringify(startTier));
6873
6924
  } else {
6874
6925
  currentCodecSet = level == null ? void 0 : level.codecSet;
6875
6926
  currentVideoRange = level == null ? void 0 : level.videoRange;
@@ -6922,9 +6973,9 @@
6922
6973
  var forcedAutoLevel = _this2.forcedAutoLevel;
6923
6974
  if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) {
6924
6975
  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);
6976
+ _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
6977
  }
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);
6978
+ _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
6979
  }
6929
6980
  if (firstSelection) {
6930
6981
  _this2.firstSelection = i;
@@ -6958,7 +7009,7 @@
6958
7009
  }
6959
7010
  var firstLevel = this.hls.firstLevel;
6960
7011
  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);
7012
+ this.warn("Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped);
6962
7013
  return clamped;
6963
7014
  }
6964
7015
  }, {
@@ -7011,7 +7062,7 @@
7011
7062
  }
7012
7063
  }]);
7013
7064
  return AbrController;
7014
- }();
7065
+ }(Logger);
7015
7066
 
7016
7067
  /**
7017
7068
  * Provides methods dealing with buffer length retrieval for example.
@@ -7036,40 +7087,29 @@
7036
7087
  * Return true if `media`'s buffered include `position`
7037
7088
  */
7038
7089
  BufferHelper.isBuffered = function isBuffered(media, position) {
7039
- try {
7040
- if (media) {
7041
- var buffered = BufferHelper.getBuffered(media);
7042
- for (var i = 0; i < buffered.length; i++) {
7043
- if (position >= buffered.start(i) && position <= buffered.end(i)) {
7044
- return true;
7045
- }
7090
+ if (media) {
7091
+ var buffered = BufferHelper.getBuffered(media);
7092
+ for (var i = buffered.length; i--;) {
7093
+ if (position >= buffered.start(i) && position <= buffered.end(i)) {
7094
+ return true;
7046
7095
  }
7047
7096
  }
7048
- } catch (error) {
7049
- // this is to catch
7050
- // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
7051
- // This SourceBuffer has been removed from the parent media source
7052
7097
  }
7053
7098
  return false;
7054
7099
  };
7055
7100
  BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) {
7056
- try {
7057
- if (media) {
7058
- var vbuffered = BufferHelper.getBuffered(media);
7101
+ if (media) {
7102
+ var vbuffered = BufferHelper.getBuffered(media);
7103
+ if (vbuffered.length) {
7059
7104
  var buffered = [];
7060
- var i;
7061
- for (i = 0; i < vbuffered.length; i++) {
7105
+ for (var i = 0; i < vbuffered.length; i++) {
7062
7106
  buffered.push({
7063
7107
  start: vbuffered.start(i),
7064
7108
  end: vbuffered.end(i)
7065
7109
  });
7066
7110
  }
7067
- return this.bufferedInfo(buffered, pos, maxHoleDuration);
7111
+ return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
7068
7112
  }
7069
- } catch (error) {
7070
- // this is to catch
7071
- // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer':
7072
- // This SourceBuffer has been removed from the parent media source
7073
7113
  }
7074
7114
  return {
7075
7115
  len: 0,
@@ -7082,12 +7122,7 @@
7082
7122
  pos = Math.max(0, pos);
7083
7123
  // sort on buffer.start/smaller end (IE does not always return sorted buffered range)
7084
7124
  buffered.sort(function (a, b) {
7085
- var diff = a.start - b.start;
7086
- if (diff) {
7087
- return diff;
7088
- } else {
7089
- return b.end - a.end;
7090
- }
7125
+ return a.start - b.start || b.end - a.end;
7091
7126
  });
7092
7127
  var buffered2 = [];
7093
7128
  if (maxHoleDuration) {
@@ -7155,7 +7190,7 @@
7155
7190
  */;
7156
7191
  BufferHelper.getBuffered = function getBuffered(media) {
7157
7192
  try {
7158
- return media.buffered;
7193
+ return media.buffered || noopBuffered;
7159
7194
  } catch (e) {
7160
7195
  logger.log('failed to get media.buffered', e);
7161
7196
  return noopBuffered;
@@ -7182,24 +7217,23 @@
7182
7217
  this.executeNext(type);
7183
7218
  }
7184
7219
  };
7185
- _proto.insertAbort = function insertAbort(operation, type) {
7186
- var queue = this.queues[type];
7187
- queue.unshift(operation);
7188
- this.executeNext(type);
7189
- };
7190
7220
  _proto.appendBlocker = function appendBlocker(type) {
7191
- var execute;
7192
- var promise = new Promise(function (resolve) {
7193
- execute = resolve;
7221
+ var _this = this;
7222
+ return new Promise(function (resolve) {
7223
+ var operation = {
7224
+ execute: resolve,
7225
+ onStart: function onStart() {},
7226
+ onComplete: function onComplete() {},
7227
+ onError: function onError() {}
7228
+ };
7229
+ _this.append(operation, type);
7194
7230
  });
7195
- var operation = {
7196
- execute: execute,
7197
- onStart: function onStart() {},
7198
- onComplete: function onComplete() {},
7199
- onError: function onError() {}
7200
- };
7201
- this.append(operation, type);
7202
- return promise;
7231
+ };
7232
+ _proto.unblockAudio = function unblockAudio(op) {
7233
+ var queue = this.queues.audio;
7234
+ if (queue[0] === op) {
7235
+ this.shiftAndExecuteNext('audio');
7236
+ }
7203
7237
  };
7204
7238
  _proto.executeNext = function executeNext(type) {
7205
7239
  var queue = this.queues[type];
@@ -7232,61 +7266,69 @@
7232
7266
  }();
7233
7267
 
7234
7268
  var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
7235
- var BufferController = /*#__PURE__*/function () {
7236
- function BufferController(hls) {
7237
- var _this = this;
7269
+ var BufferController = /*#__PURE__*/function (_Logger) {
7270
+ _inheritsLoose(BufferController, _Logger);
7271
+ function BufferController(hls, fragmentTracker) {
7272
+ var _this;
7273
+ _this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
7238
7274
  // The level details used to determine duration, target-duration and live
7239
- this.details = null;
7275
+ _this.details = null;
7240
7276
  // cache the self generated object url to detect hijack of video tag
7241
- this._objectUrl = null;
7277
+ _this._objectUrl = null;
7242
7278
  // A queue of buffer operations which require the SourceBuffer to not be updating upon execution
7243
- this.operationQueue = void 0;
7279
+ _this.operationQueue = void 0;
7244
7280
  // 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;
7281
+ _this.listeners = void 0;
7282
+ _this.hls = void 0;
7283
+ _this.fragmentTracker = void 0;
7247
7284
  // The number of BUFFER_CODEC events received before any sourceBuffers are created
7248
- this.bufferCodecEventsExpected = 0;
7285
+ _this.bufferCodecEventsExpected = 0;
7249
7286
  // The total number of BUFFER_CODEC events received
7250
- this._bufferCodecEventsTotal = 0;
7287
+ _this._bufferCodecEventsTotal = 0;
7251
7288
  // A reference to the attached media element
7252
- this.media = null;
7289
+ _this.media = null;
7253
7290
  // A reference to the active media source
7254
- this.mediaSource = null;
7291
+ _this.mediaSource = null;
7255
7292
  // Last MP3 audio chunk appended
7256
- this.lastMpegAudioChunk = null;
7257
- this.appendSource = void 0;
7293
+ _this.lastMpegAudioChunk = null;
7294
+ // Audio fragment blocked from appending until corresponding video appends or context changes
7295
+ _this.blockedAudioAppend = null;
7296
+ // Keep track of video append position for unblocking audio
7297
+ _this.lastVideoAppendEnd = 0;
7298
+ _this.appendSource = void 0;
7258
7299
  // counters
7259
- this.appendErrors = {
7300
+ _this.appendErrors = {
7260
7301
  audio: 0,
7261
7302
  video: 0,
7262
7303
  audiovideo: 0
7263
7304
  };
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) {
7305
+ _this.tracks = {};
7306
+ _this.pendingTracks = {};
7307
+ _this.sourceBuffer = void 0;
7308
+ _this._onEndStreaming = function (event) {
7271
7309
  if (!_this.hls) {
7272
7310
  return;
7273
7311
  }
7274
7312
  _this.hls.pauseBuffering();
7275
7313
  };
7276
- this._onStartStreaming = function (event) {
7314
+ _this._onStartStreaming = function (event) {
7277
7315
  if (!_this.hls) {
7278
7316
  return;
7279
7317
  }
7280
7318
  _this.hls.resumeBuffering();
7281
7319
  };
7282
7320
  // 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;
7321
+ _this._onMediaSourceOpen = function () {
7322
+ var _assertThisInitialize = _assertThisInitialized(_this),
7323
+ media = _assertThisInitialize.media,
7324
+ mediaSource = _assertThisInitialize.mediaSource;
7286
7325
  _this.log('Media source opened');
7287
7326
  if (media) {
7288
7327
  media.removeEventListener('emptied', _this._onMediaEmptied);
7289
- _this.updateMediaElementDuration();
7328
+ var durationAndRange = _this.getDurationAndRange();
7329
+ if (durationAndRange) {
7330
+ _this.updateMediaSource(durationAndRange);
7331
+ }
7290
7332
  _this.hls.trigger(Events.MEDIA_ATTACHED, {
7291
7333
  media: media,
7292
7334
  mediaSource: mediaSource
@@ -7298,27 +7340,26 @@
7298
7340
  }
7299
7341
  _this.checkPendingTracks();
7300
7342
  };
7301
- this._onMediaSourceClose = function () {
7343
+ _this._onMediaSourceClose = function () {
7302
7344
  _this.log('Media source closed');
7303
7345
  };
7304
- this._onMediaSourceEnded = function () {
7346
+ _this._onMediaSourceEnded = function () {
7305
7347
  _this.log('Media source ended');
7306
7348
  };
7307
- this._onMediaEmptied = function () {
7308
- var mediaSrc = _this.mediaSrc,
7309
- _objectUrl = _this._objectUrl;
7349
+ _this._onMediaEmptied = function () {
7350
+ var _assertThisInitialize2 = _assertThisInitialized(_this),
7351
+ mediaSrc = _assertThisInitialize2.mediaSrc,
7352
+ _objectUrl = _assertThisInitialize2._objectUrl;
7310
7353
  if (mediaSrc !== _objectUrl) {
7311
- logger.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7354
+ _this.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")");
7312
7355
  }
7313
7356
  };
7314
- this.hls = hls;
7315
- var logPrefix = '[buffer-controller]';
7316
- this.appendSource = hls.config.preferManagedMediaSource && typeof self !== 'undefined' && self.ManagedMediaSource;
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();
7357
+ _this.hls = hls;
7358
+ _this.fragmentTracker = fragmentTracker;
7359
+ _this.appendSource = hls.config.preferManagedMediaSource && typeof self !== 'undefined' && self.ManagedMediaSource;
7360
+ _this._initSourceBuffer();
7361
+ _this.registerListeners();
7362
+ return _this;
7322
7363
  }
7323
7364
  var _proto = BufferController.prototype;
7324
7365
  _proto.hasSourceTypes = function hasSourceTypes() {
@@ -7329,7 +7370,13 @@
7329
7370
  this.details = null;
7330
7371
  this.lastMpegAudioChunk = null;
7331
7372
  // @ts-ignore
7332
- this.hls = null;
7373
+ this.hls = this.fragmentTracker = null;
7374
+ // @ts-ignore
7375
+ this._onMediaSourceOpen = this._onMediaSourceClose = null;
7376
+ // @ts-ignore
7377
+ this._onMediaSourceEnded = null;
7378
+ // @ts-ignore
7379
+ this._onStartStreaming = this._onEndStreaming = null;
7333
7380
  };
7334
7381
  _proto.registerListeners = function registerListeners() {
7335
7382
  var hls = this.hls;
@@ -7375,6 +7422,8 @@
7375
7422
  audiovideo: 0
7376
7423
  };
7377
7424
  this.lastMpegAudioChunk = null;
7425
+ this.blockedAudioAppend = null;
7426
+ this.lastVideoAppendEnd = 0;
7378
7427
  };
7379
7428
  _proto.onManifestLoading = function onManifestLoading() {
7380
7429
  this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
@@ -7491,6 +7540,7 @@
7491
7540
  _this2.resetBuffer(type);
7492
7541
  });
7493
7542
  this._initSourceBuffer();
7543
+ this.hls.resumeBuffering();
7494
7544
  };
7495
7545
  _proto.resetBuffer = function resetBuffer(type) {
7496
7546
  var sb = this.sourceBuffer[type];
@@ -7515,9 +7565,10 @@
7515
7565
  var trackNames = Object.keys(data);
7516
7566
  trackNames.forEach(function (trackName) {
7517
7567
  if (sourceBufferCount) {
7568
+ var _track$buffer;
7518
7569
  // check if SourceBuffer codec needs to change
7519
7570
  var track = _this3.tracks[trackName];
7520
- if (track && typeof track.buffer.changeType === 'function') {
7571
+ if (track && typeof ((_track$buffer = track.buffer) == null ? void 0 : _track$buffer.changeType) === 'function') {
7521
7572
  var _trackCodec;
7522
7573
  var _data$trackName = data[trackName],
7523
7574
  id = _data$trackName.id,
@@ -7585,17 +7636,52 @@
7585
7636
  };
7586
7637
  operationQueue.append(operation, type, !!this.pendingTracks[type]);
7587
7638
  };
7639
+ _proto.blockAudio = function blockAudio(partOrFrag) {
7640
+ var _this$fragmentTracker,
7641
+ _this5 = this;
7642
+ var pStart = partOrFrag.start;
7643
+ var pTime = pStart + partOrFrag.duration * 0.05;
7644
+ var atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true;
7645
+ if (atGap) {
7646
+ return;
7647
+ }
7648
+ var op = {
7649
+ execute: function execute() {
7650
+ var _this5$fragmentTracke;
7651
+ if (_this5.lastVideoAppendEnd > pTime || _this5.sourceBuffer.video && BufferHelper.isBuffered(_this5.sourceBuffer.video, pTime) || ((_this5$fragmentTracke = _this5.fragmentTracker.getAppendedFrag(pTime, PlaylistLevelType.MAIN)) == null ? void 0 : _this5$fragmentTracke.gap) === true) {
7652
+ _this5.blockedAudioAppend = null;
7653
+ _this5.operationQueue.shiftAndExecuteNext('audio');
7654
+ }
7655
+ },
7656
+ onStart: function onStart() {},
7657
+ onComplete: function onComplete() {},
7658
+ onError: function onError() {}
7659
+ };
7660
+ this.blockedAudioAppend = {
7661
+ op: op,
7662
+ frag: partOrFrag
7663
+ };
7664
+ this.operationQueue.append(op, 'audio', true);
7665
+ };
7666
+ _proto.unblockAudio = function unblockAudio() {
7667
+ var blockedAudioAppend = this.blockedAudioAppend;
7668
+ if (blockedAudioAppend) {
7669
+ this.blockedAudioAppend = null;
7670
+ this.operationQueue.unblockAudio(blockedAudioAppend.op);
7671
+ }
7672
+ };
7588
7673
  _proto.onBufferAppending = function onBufferAppending(event, eventData) {
7589
- var _this5 = this;
7590
- var hls = this.hls,
7591
- operationQueue = this.operationQueue,
7674
+ var _this6 = this;
7675
+ var operationQueue = this.operationQueue,
7592
7676
  tracks = this.tracks;
7593
7677
  var data = eventData.data,
7594
7678
  type = eventData.type,
7679
+ parent = eventData.parent,
7595
7680
  frag = eventData.frag,
7596
7681
  part = eventData.part,
7597
7682
  chunkMeta = eventData.chunkMeta;
7598
7683
  var chunkStats = chunkMeta.buffering[type];
7684
+ var sn = frag.sn;
7599
7685
  var bufferAppendingStart = self.performance.now();
7600
7686
  chunkStats.start = bufferAppendingStart;
7601
7687
  var fragBuffering = frag.stats.buffering;
@@ -7618,21 +7704,50 @@
7618
7704
  checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;
7619
7705
  this.lastMpegAudioChunk = chunkMeta;
7620
7706
  }
7621
- var fragStart = frag.start;
7707
+
7708
+ // Block audio append until overlapping video append
7709
+ var videoSb = this.sourceBuffer.video;
7710
+ if (videoSb && sn !== 'initSegment') {
7711
+ var partOrFrag = part || frag;
7712
+ var blockedAudioAppend = this.blockedAudioAppend;
7713
+ if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) {
7714
+ var pStart = partOrFrag.start;
7715
+ var pTime = pStart + partOrFrag.duration * 0.05;
7716
+ var vbuffered = videoSb.buffered;
7717
+ var vappending = this.operationQueue.current('video');
7718
+ if (!vbuffered.length && !vappending) {
7719
+ // wait for video before appending audio
7720
+ this.blockAudio(partOrFrag);
7721
+ } else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) {
7722
+ // audio is ahead of video
7723
+ this.blockAudio(partOrFrag);
7724
+ }
7725
+ } else if (type === 'video') {
7726
+ var videoAppendEnd = partOrFrag.end;
7727
+ if (blockedAudioAppend) {
7728
+ var audioStart = blockedAudioAppend.frag.start;
7729
+ if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) {
7730
+ this.unblockAudio();
7731
+ }
7732
+ }
7733
+ this.lastVideoAppendEnd = videoAppendEnd;
7734
+ }
7735
+ }
7736
+ var fragStart = (part || frag).start;
7622
7737
  var operation = {
7623
7738
  execute: function execute() {
7624
7739
  chunkStats.executeStart = self.performance.now();
7625
7740
  if (checkTimestampOffset) {
7626
- var sb = _this5.sourceBuffer[type];
7741
+ var sb = _this6.sourceBuffer[type];
7627
7742
  if (sb) {
7628
7743
  var delta = fragStart - sb.timestampOffset;
7629
7744
  if (Math.abs(delta) >= 0.1) {
7630
- _this5.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + frag.sn + ")");
7745
+ _this6.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + sn + ")");
7631
7746
  sb.timestampOffset = fragStart;
7632
7747
  }
7633
7748
  }
7634
7749
  }
7635
- _this5.appendExecutor(data, type);
7750
+ _this6.appendExecutor(data, type);
7636
7751
  },
7637
7752
  onStart: function onStart() {
7638
7753
  // logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`);
@@ -7647,19 +7762,19 @@
7647
7762
  if (partBuffering && partBuffering.first === 0) {
7648
7763
  partBuffering.first = end;
7649
7764
  }
7650
- var sourceBuffer = _this5.sourceBuffer;
7765
+ var sourceBuffer = _this6.sourceBuffer;
7651
7766
  var timeRanges = {};
7652
7767
  for (var _type in sourceBuffer) {
7653
7768
  timeRanges[_type] = BufferHelper.getBuffered(sourceBuffer[_type]);
7654
7769
  }
7655
- _this5.appendErrors[type] = 0;
7770
+ _this6.appendErrors[type] = 0;
7656
7771
  if (type === 'audio' || type === 'video') {
7657
- _this5.appendErrors.audiovideo = 0;
7772
+ _this6.appendErrors.audiovideo = 0;
7658
7773
  } else {
7659
- _this5.appendErrors.audio = 0;
7660
- _this5.appendErrors.video = 0;
7774
+ _this6.appendErrors.audio = 0;
7775
+ _this6.appendErrors.video = 0;
7661
7776
  }
7662
- _this5.hls.trigger(Events.BUFFER_APPENDED, {
7777
+ _this6.hls.trigger(Events.BUFFER_APPENDED, {
7663
7778
  type: type,
7664
7779
  frag: frag,
7665
7780
  part: part,
@@ -7687,51 +7802,57 @@
7687
7802
  // let's stop appending any segments, and report BUFFER_FULL_ERROR error
7688
7803
  event.details = ErrorDetails.BUFFER_FULL_ERROR;
7689
7804
  } else {
7690
- var appendErrorCount = ++_this5.appendErrors[type];
7805
+ var appendErrorCount = ++_this6.appendErrors[type];
7691
7806
  event.details = ErrorDetails.BUFFER_APPEND_ERROR;
7692
7807
  /* with UHD content, we could get loop of quota exceeded error until
7693
7808
  browser is able to evict some data from sourcebuffer. Retrying can help recover.
7694
7809
  */
7695
- _this5.warn("Failed " + appendErrorCount + "/" + hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer");
7696
- if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
7810
+ _this6.warn("Failed " + appendErrorCount + "/" + _this6.hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer");
7811
+ if (appendErrorCount >= _this6.hls.config.appendErrorMaxRetry) {
7697
7812
  event.fatal = true;
7698
7813
  }
7699
7814
  }
7700
- hls.trigger(Events.ERROR, event);
7815
+ _this6.hls.trigger(Events.ERROR, event);
7701
7816
  }
7702
7817
  };
7703
7818
  operationQueue.append(operation, type, !!this.pendingTracks[type]);
7704
7819
  };
7705
- _proto.onBufferFlushing = function onBufferFlushing(event, data) {
7706
- var _this6 = this;
7707
- var operationQueue = this.operationQueue;
7708
- var flushOperation = function flushOperation(type) {
7709
- return {
7710
- execute: _this6.removeExecutor.bind(_this6, type, data.startOffset, data.endOffset),
7711
- onStart: function onStart() {
7712
- // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
7713
- },
7714
- onComplete: function onComplete() {
7715
- // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
7716
- _this6.hls.trigger(Events.BUFFER_FLUSHED, {
7717
- type: type
7718
- });
7719
- },
7720
- onError: function onError(error) {
7721
- _this6.warn("Failed to remove from " + type + " SourceBuffer", error);
7722
- }
7723
- };
7820
+ _proto.getFlushOp = function getFlushOp(type, start, end) {
7821
+ var _this7 = this;
7822
+ return {
7823
+ execute: function execute() {
7824
+ _this7.removeExecutor(type, start, end);
7825
+ },
7826
+ onStart: function onStart() {
7827
+ // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
7828
+ },
7829
+ onComplete: function onComplete() {
7830
+ // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
7831
+ _this7.hls.trigger(Events.BUFFER_FLUSHED, {
7832
+ type: type
7833
+ });
7834
+ },
7835
+ onError: function onError(error) {
7836
+ _this7.warn("Failed to remove from " + type + " SourceBuffer", error);
7837
+ }
7724
7838
  };
7725
- if (data.type) {
7726
- operationQueue.append(flushOperation(data.type), data.type);
7839
+ };
7840
+ _proto.onBufferFlushing = function onBufferFlushing(event, data) {
7841
+ var _this8 = this;
7842
+ var operationQueue = this.operationQueue;
7843
+ var type = data.type,
7844
+ startOffset = data.startOffset,
7845
+ endOffset = data.endOffset;
7846
+ if (type) {
7847
+ operationQueue.append(this.getFlushOp(type, startOffset, endOffset), type);
7727
7848
  } else {
7728
- this.getSourceBufferTypes().forEach(function (type) {
7729
- operationQueue.append(flushOperation(type), type);
7849
+ this.getSourceBufferTypes().forEach(function (sbType) {
7850
+ operationQueue.append(_this8.getFlushOp(sbType, startOffset, endOffset), sbType);
7730
7851
  });
7731
7852
  }
7732
7853
  };
7733
7854
  _proto.onFragParsed = function onFragParsed(event, data) {
7734
- var _this7 = this;
7855
+ var _this9 = this;
7735
7856
  var frag = data.frag,
7736
7857
  part = data.part;
7737
7858
  var buffersAppendedTo = [];
@@ -7753,7 +7874,7 @@
7753
7874
  part.stats.buffering.end = now;
7754
7875
  }
7755
7876
  var stats = part ? part.stats : frag.stats;
7756
- _this7.hls.trigger(Events.FRAG_BUFFERED, {
7877
+ _this9.hls.trigger(Events.FRAG_BUFFERED, {
7757
7878
  frag: frag,
7758
7879
  part: part,
7759
7880
  stats: stats,
@@ -7773,14 +7894,17 @@
7773
7894
  // an undefined data.type will mark all buffers as EOS.
7774
7895
  ;
7775
7896
  _proto.onBufferEos = function onBufferEos(event, data) {
7776
- var _this8 = this;
7897
+ var _this10 = this;
7898
+ if (data.type === 'video') {
7899
+ this.unblockAudio();
7900
+ }
7777
7901
  var ended = this.getSourceBufferTypes().reduce(function (acc, type) {
7778
- var sb = _this8.sourceBuffer[type];
7902
+ var sb = _this10.sourceBuffer[type];
7779
7903
  if (sb && (!data.type || data.type === type)) {
7780
7904
  sb.ending = true;
7781
7905
  if (!sb.ended) {
7782
7906
  sb.ended = true;
7783
- _this8.log(type + " sourceBuffer now EOS");
7907
+ _this10.log(type + " sourceBuffer now EOS");
7784
7908
  }
7785
7909
  }
7786
7910
  return acc && !!(!sb || sb.ended);
@@ -7788,35 +7912,42 @@
7788
7912
  if (ended) {
7789
7913
  this.log("Queueing mediaSource.endOfStream()");
7790
7914
  this.blockBuffers(function () {
7791
- _this8.getSourceBufferTypes().forEach(function (type) {
7792
- var sb = _this8.sourceBuffer[type];
7915
+ _this10.getSourceBufferTypes().forEach(function (type) {
7916
+ var sb = _this10.sourceBuffer[type];
7793
7917
  if (sb) {
7794
7918
  sb.ending = false;
7795
7919
  }
7796
7920
  });
7797
- var mediaSource = _this8.mediaSource;
7921
+ var mediaSource = _this10.mediaSource;
7798
7922
  if (!mediaSource || mediaSource.readyState !== 'open') {
7799
7923
  if (mediaSource) {
7800
- _this8.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState);
7924
+ _this10.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState);
7801
7925
  }
7802
7926
  return;
7803
7927
  }
7804
- _this8.log("Calling mediaSource.endOfStream()");
7928
+ _this10.log("Calling mediaSource.endOfStream()");
7805
7929
  // Allow this to throw and be caught by the enqueueing function
7806
7930
  mediaSource.endOfStream();
7807
7931
  });
7808
7932
  }
7809
7933
  };
7810
7934
  _proto.onLevelUpdated = function onLevelUpdated(event, _ref) {
7935
+ var _this11 = this;
7811
7936
  var details = _ref.details;
7812
7937
  if (!details.fragments.length) {
7813
7938
  return;
7814
7939
  }
7815
7940
  this.details = details;
7941
+ var durationAndRange = this.getDurationAndRange();
7942
+ if (!durationAndRange) {
7943
+ return;
7944
+ }
7816
7945
  if (this.getSourceBufferTypes().length) {
7817
- this.blockBuffers(this.updateMediaElementDuration.bind(this));
7946
+ this.blockBuffers(function () {
7947
+ return _this11.updateMediaSource(durationAndRange);
7948
+ });
7818
7949
  } else {
7819
- this.updateMediaElementDuration();
7950
+ this.updateMediaSource(durationAndRange);
7820
7951
  }
7821
7952
  };
7822
7953
  _proto.trimBuffers = function trimBuffers() {
@@ -7849,7 +7980,7 @@
7849
7980
  }
7850
7981
  };
7851
7982
  _proto.flushBackBuffer = function flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) {
7852
- var _this9 = this;
7983
+ var _this12 = this;
7853
7984
  var details = this.details,
7854
7985
  sourceBuffer = this.sourceBuffer;
7855
7986
  var sourceBufferTypes = this.getSourceBufferTypes();
@@ -7859,20 +7990,20 @@
7859
7990
  var buffered = BufferHelper.getBuffered(sb);
7860
7991
  // when target buffer start exceeds actual buffer start
7861
7992
  if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) {
7862
- _this9.hls.trigger(Events.BACK_BUFFER_REACHED, {
7993
+ _this12.hls.trigger(Events.BACK_BUFFER_REACHED, {
7863
7994
  bufferEnd: targetBackBufferPosition
7864
7995
  });
7865
7996
 
7866
7997
  // Support for deprecated event:
7867
7998
  if (details != null && details.live) {
7868
- _this9.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {
7999
+ _this12.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {
7869
8000
  bufferEnd: targetBackBufferPosition
7870
8001
  });
7871
8002
  } else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) {
7872
- _this9.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state");
8003
+ _this12.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state");
7873
8004
  return;
7874
8005
  }
7875
- _this9.hls.trigger(Events.BUFFER_FLUSHING, {
8006
+ _this12.hls.trigger(Events.BUFFER_FLUSHING, {
7876
8007
  startOffset: 0,
7877
8008
  endOffset: targetBackBufferPosition,
7878
8009
  type: type
@@ -7882,7 +8013,7 @@
7882
8013
  });
7883
8014
  };
7884
8015
  _proto.flushFrontBuffer = function flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) {
7885
- var _this10 = this;
8016
+ var _this13 = this;
7886
8017
  var sourceBuffer = this.sourceBuffer;
7887
8018
  var sourceBufferTypes = this.getSourceBufferTypes();
7888
8019
  sourceBufferTypes.forEach(function (type) {
@@ -7900,10 +8031,10 @@
7900
8031
  if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) {
7901
8032
  return;
7902
8033
  } else if (sb.ended && currentTime - bufferEnd < 2 * targetDuration) {
7903
- _this10.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state");
8034
+ _this13.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state");
7904
8035
  return;
7905
8036
  }
7906
- _this10.hls.trigger(Events.BUFFER_FLUSHING, {
8037
+ _this13.hls.trigger(Events.BUFFER_FLUSHING, {
7907
8038
  startOffset: bufferStart,
7908
8039
  endOffset: Infinity,
7909
8040
  type: type
@@ -7917,9 +8048,9 @@
7917
8048
  * 'liveDurationInfinity` is set to `true`
7918
8049
  * More details: https://github.com/video-dev/hls.js/issues/355
7919
8050
  */;
7920
- _proto.updateMediaElementDuration = function updateMediaElementDuration() {
8051
+ _proto.getDurationAndRange = function getDurationAndRange() {
7921
8052
  if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
7922
- return;
8053
+ return null;
7923
8054
  }
7924
8055
  var details = this.details,
7925
8056
  hls = this.hls,
@@ -7931,25 +8062,40 @@
7931
8062
  if (details.live && hls.config.liveDurationInfinity) {
7932
8063
  // Override duration to Infinity
7933
8064
  mediaSource.duration = Infinity;
7934
- this.updateSeekableRange(details);
8065
+ var len = details.fragments.length;
8066
+ if (len && details.live && !!mediaSource.setLiveSeekableRange) {
8067
+ var start = Math.max(0, details.fragments[0].start);
8068
+ var end = Math.max(start, start + details.totalduration);
8069
+ return {
8070
+ duration: Infinity,
8071
+ start: start,
8072
+ end: end
8073
+ };
8074
+ }
8075
+ return {
8076
+ duration: Infinity
8077
+ };
7935
8078
  } else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {
7936
- // levelDuration was the last value we set.
7937
- // not using mediaSource.duration as the browser may tweak this value
7938
- // only update Media Source duration if its value increase, this is to avoid
7939
- // flushing already buffered portion when switching between quality level
7940
- this.log("Updating Media Source duration to " + levelDuration.toFixed(3));
7941
- mediaSource.duration = levelDuration;
8079
+ return {
8080
+ duration: levelDuration
8081
+ };
7942
8082
  }
8083
+ return null;
7943
8084
  };
7944
- _proto.updateSeekableRange = function updateSeekableRange(levelDetails) {
7945
- var mediaSource = this.mediaSource;
7946
- var fragments = levelDetails.fragments;
7947
- var len = fragments.length;
7948
- if (len && levelDetails.live && mediaSource != null && mediaSource.setLiveSeekableRange) {
7949
- var start = Math.max(0, fragments[0].start);
7950
- var end = Math.max(start, start + levelDetails.totalduration);
7951
- this.log("Media Source duration is set to " + mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
7952
- mediaSource.setLiveSeekableRange(start, end);
8085
+ _proto.updateMediaSource = function updateMediaSource(_ref2) {
8086
+ var duration = _ref2.duration,
8087
+ start = _ref2.start,
8088
+ end = _ref2.end;
8089
+ if (!this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
8090
+ return;
8091
+ }
8092
+ if (isFiniteNumber(duration)) {
8093
+ this.log("Updating Media Source duration to " + duration.toFixed(3));
8094
+ }
8095
+ this.mediaSource.duration = duration;
8096
+ if (start !== undefined && end !== undefined) {
8097
+ this.log("Media Source duration is set to " + this.mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
8098
+ this.mediaSource.setLiveSeekableRange(start, end);
7953
8099
  }
7954
8100
  };
7955
8101
  _proto.checkPendingTracks = function checkPendingTracks() {
@@ -7988,7 +8134,7 @@
7988
8134
  }
7989
8135
  };
7990
8136
  _proto.createSourceBuffers = function createSourceBuffers(tracks) {
7991
- var _this11 = this;
8137
+ var _this14 = this;
7992
8138
  var sourceBuffer = this.sourceBuffer,
7993
8139
  mediaSource = this.mediaSource;
7994
8140
  if (!mediaSource) {
@@ -8004,30 +8150,30 @@
8004
8150
  var codec = track.levelCodec || track.codec;
8005
8151
  if (codec) {
8006
8152
  if (trackName.slice(0, 5) === 'audio') {
8007
- codec = getCodecCompatibleName(codec, _this11.appendSource);
8153
+ codec = getCodecCompatibleName(codec, _this14.appendSource);
8008
8154
  }
8009
8155
  }
8010
8156
  var mimeType = track.container + ";codecs=" + codec;
8011
- _this11.log("creating sourceBuffer(" + mimeType + ")");
8157
+ _this14.log("creating sourceBuffer(" + mimeType + ")");
8012
8158
  try {
8013
8159
  var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
8014
8160
  var sbName = trackName;
8015
- _this11.addBufferListener(sbName, 'updatestart', _this11._onSBUpdateStart);
8016
- _this11.addBufferListener(sbName, 'updateend', _this11._onSBUpdateEnd);
8017
- _this11.addBufferListener(sbName, 'error', _this11._onSBUpdateError);
8161
+ _this14.addBufferListener(sbName, 'updatestart', _this14._onSBUpdateStart);
8162
+ _this14.addBufferListener(sbName, 'updateend', _this14._onSBUpdateEnd);
8163
+ _this14.addBufferListener(sbName, 'error', _this14._onSBUpdateError);
8018
8164
  // ManagedSourceBuffer bufferedchange event
8019
- if (_this11.appendSource) {
8020
- _this11.addBufferListener(sbName, 'bufferedchange', function (type, event) {
8165
+ if (_this14.appendSource) {
8166
+ _this14.addBufferListener(sbName, 'bufferedchange', function (type, event) {
8021
8167
  // If media was ejected check for a change. Added ranges are redundant with changes on 'updateend' event.
8022
8168
  var removedRanges = event.removedRanges;
8023
8169
  if (removedRanges != null && removedRanges.length) {
8024
- _this11.hls.trigger(Events.BUFFER_FLUSHED, {
8170
+ _this14.hls.trigger(Events.BUFFER_FLUSHED, {
8025
8171
  type: trackName
8026
8172
  });
8027
8173
  }
8028
8174
  });
8029
8175
  }
8030
- _this11.tracks[trackName] = {
8176
+ _this14.tracks[trackName] = {
8031
8177
  buffer: sb,
8032
8178
  codec: codec,
8033
8179
  container: track.container,
@@ -8036,8 +8182,8 @@
8036
8182
  id: track.id
8037
8183
  };
8038
8184
  } catch (err) {
8039
- _this11.error("error while trying to add sourceBuffer: " + err.message);
8040
- _this11.hls.trigger(Events.ERROR, {
8185
+ _this14.error("error while trying to add sourceBuffer: " + err.message);
8186
+ _this14.hls.trigger(Events.ERROR, {
8041
8187
  type: ErrorTypes.MEDIA_ERROR,
8042
8188
  details: ErrorDetails.BUFFER_ADD_CODEC_ERROR,
8043
8189
  fatal: false,
@@ -8125,6 +8271,7 @@
8125
8271
  }
8126
8272
  return;
8127
8273
  }
8274
+ sb.ending = false;
8128
8275
  sb.ended = false;
8129
8276
  sb.appendBuffer(data);
8130
8277
  }
@@ -8134,7 +8281,7 @@
8134
8281
  // upon completion, since we already do it here
8135
8282
  ;
8136
8283
  _proto.blockBuffers = function blockBuffers(onUnblocked, buffers) {
8137
- var _this12 = this;
8284
+ var _this15 = this;
8138
8285
  if (buffers === void 0) {
8139
8286
  buffers = this.getSourceBufferTypes();
8140
8287
  }
@@ -8149,11 +8296,15 @@
8149
8296
  var blockingOperations = buffers.map(function (type) {
8150
8297
  return operationQueue.appendBlocker(type);
8151
8298
  });
8152
- Promise.all(blockingOperations).then(function () {
8299
+ var audioBlocked = buffers.length > 1 && !!this.blockedAudioAppend;
8300
+ if (audioBlocked) {
8301
+ this.unblockAudio();
8302
+ }
8303
+ Promise.all(blockingOperations).then(function (result) {
8153
8304
  // logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
8154
8305
  onUnblocked();
8155
- buffers.forEach(function (type) {
8156
- var sb = _this12.sourceBuffer[type];
8306
+ buffers.forEach(function (type, i) {
8307
+ var sb = _this15.sourceBuffer[type];
8157
8308
  // Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to
8158
8309
  // true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)
8159
8310
  // While this is a workaround, it's probably useful to have around
@@ -8196,7 +8347,7 @@
8196
8347
  }
8197
8348
  }]);
8198
8349
  return BufferController;
8199
- }();
8350
+ }(Logger);
8200
8351
  function removeSourceChildren(node) {
8201
8352
  var sourceChildren = node.querySelectorAll('source');
8202
8353
  [].slice.call(sourceChildren).forEach(function (source) {
@@ -8320,10 +8471,10 @@
8320
8471
  var hls = this.hls;
8321
8472
  var maxLevel = this.getMaxLevel(levels.length - 1);
8322
8473
  if (maxLevel !== this.autoLevelCapping) {
8323
- logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8474
+ hls.logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight);
8324
8475
  }
8325
8476
  hls.autoLevelCapping = maxLevel;
8326
- if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
8477
+ if (hls.autoLevelEnabled && hls.autoLevelCapping > this.autoLevelCapping && this.streamController) {
8327
8478
  // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch
8328
8479
  // usually happen when the user go to the fullscreen mode.
8329
8480
  this.streamController.nextLevelSwitch();
@@ -8510,10 +8661,10 @@
8510
8661
  totalDroppedFrames: droppedFrames
8511
8662
  });
8512
8663
  if (droppedFPS > 0) {
8513
- // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8664
+ // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
8514
8665
  if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
8515
8666
  var currentLevel = hls.currentLevel;
8516
- logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8667
+ hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
8517
8668
  if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
8518
8669
  currentLevel = currentLevel - 1;
8519
8670
  hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, {
@@ -8547,26 +8698,28 @@
8547
8698
  }();
8548
8699
 
8549
8700
  var PATHWAY_PENALTY_DURATION_MS = 300000;
8550
- var ContentSteeringController = /*#__PURE__*/function () {
8701
+ var ContentSteeringController = /*#__PURE__*/function (_Logger) {
8702
+ _inheritsLoose(ContentSteeringController, _Logger);
8551
8703
  function ContentSteeringController(hls) {
8552
- this.hls = void 0;
8553
- this.log = void 0;
8554
- this.loader = null;
8555
- this.uri = null;
8556
- this.pathwayId = '.';
8557
- this.pathwayPriority = null;
8558
- this.timeToLoad = 300;
8559
- this.reloadTimer = -1;
8560
- this.updated = 0;
8561
- this.started = false;
8562
- this.enabled = true;
8563
- this.levels = null;
8564
- this.audioTracks = null;
8565
- this.subtitleTracks = null;
8566
- this.penalizedPathways = {};
8567
- this.hls = hls;
8568
- this.log = logger.log.bind(logger, "[content-steering]:");
8569
- this.registerListeners();
8704
+ var _this;
8705
+ _this = _Logger.call(this, 'content-steering', hls.logger) || this;
8706
+ _this.hls = void 0;
8707
+ _this.loader = null;
8708
+ _this.uri = null;
8709
+ _this.pathwayId = '.';
8710
+ _this.pathwayPriority = null;
8711
+ _this.timeToLoad = 300;
8712
+ _this.reloadTimer = -1;
8713
+ _this.updated = 0;
8714
+ _this.started = false;
8715
+ _this.enabled = true;
8716
+ _this.levels = null;
8717
+ _this.audioTracks = null;
8718
+ _this.subtitleTracks = null;
8719
+ _this.penalizedPathways = {};
8720
+ _this.hls = hls;
8721
+ _this.registerListeners();
8722
+ return _this;
8570
8723
  }
8571
8724
  var _proto = ContentSteeringController.prototype;
8572
8725
  _proto.registerListeners = function registerListeners() {
@@ -8687,7 +8840,7 @@
8687
8840
  errorAction.resolved = this.pathwayId !== errorPathway;
8688
8841
  }
8689
8842
  if (!errorAction.resolved) {
8690
- 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));
8843
+ 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));
8691
8844
  }
8692
8845
  }
8693
8846
  };
@@ -8767,7 +8920,7 @@
8767
8920
  return defaultPathway;
8768
8921
  };
8769
8922
  _proto.clonePathways = function clonePathways(pathwayClones) {
8770
- var _this = this;
8923
+ var _this2 = this;
8771
8924
  var levels = this.levels;
8772
8925
  if (!levels) {
8773
8926
  return;
@@ -8783,7 +8936,7 @@
8783
8936
  })) {
8784
8937
  return;
8785
8938
  }
8786
- var clonedVariants = _this.getLevelsForPathway(baseId).map(function (baseLevel) {
8939
+ var clonedVariants = _this2.getLevelsForPathway(baseId).map(function (baseLevel) {
8787
8940
  var attributes = new AttrList(baseLevel.attrs);
8788
8941
  attributes['PATHWAY-ID'] = cloneId;
8789
8942
  var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId;
@@ -8820,12 +8973,12 @@
8820
8973
  return clonedLevel;
8821
8974
  });
8822
8975
  levels.push.apply(levels, clonedVariants);
8823
- cloneRenditionGroups(_this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8824
- cloneRenditionGroups(_this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8976
+ cloneRenditionGroups(_this2.audioTracks, audioGroupCloneMap, uriReplacement, cloneId);
8977
+ cloneRenditionGroups(_this2.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId);
8825
8978
  });
8826
8979
  };
8827
8980
  _proto.loadSteeringManifest = function loadSteeringManifest(uri) {
8828
- var _this2 = this;
8981
+ var _this3 = this;
8829
8982
  var config = this.hls.config;
8830
8983
  var Loader = config.loader;
8831
8984
  if (this.loader) {
@@ -8860,87 +9013,87 @@
8860
9013
  };
8861
9014
  var callbacks = {
8862
9015
  onSuccess: function onSuccess(response, stats, context, networkDetails) {
8863
- _this2.log("Loaded steering manifest: \"" + url + "\"");
9016
+ _this3.log("Loaded steering manifest: \"" + url + "\"");
8864
9017
  var steeringData = response.data;
8865
- if (steeringData.VERSION !== 1) {
8866
- _this2.log("Steering VERSION " + steeringData.VERSION + " not supported!");
9018
+ if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) {
9019
+ _this3.log("Steering VERSION " + steeringData.VERSION + " not supported!");
8867
9020
  return;
8868
9021
  }
8869
- _this2.updated = performance.now();
8870
- _this2.timeToLoad = steeringData.TTL;
9022
+ _this3.updated = performance.now();
9023
+ _this3.timeToLoad = steeringData.TTL;
8871
9024
  var reloadUri = steeringData['RELOAD-URI'],
8872
9025
  pathwayClones = steeringData['PATHWAY-CLONES'],
8873
9026
  pathwayPriority = steeringData['PATHWAY-PRIORITY'];
8874
9027
  if (reloadUri) {
8875
9028
  try {
8876
- _this2.uri = new self.URL(reloadUri, url).href;
9029
+ _this3.uri = new self.URL(reloadUri, url).href;
8877
9030
  } catch (error) {
8878
- _this2.enabled = false;
8879
- _this2.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
9031
+ _this3.enabled = false;
9032
+ _this3.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri);
8880
9033
  return;
8881
9034
  }
8882
9035
  }
8883
- _this2.scheduleRefresh(_this2.uri || context.url);
9036
+ _this3.scheduleRefresh(_this3.uri || context.url);
8884
9037
  if (pathwayClones) {
8885
- _this2.clonePathways(pathwayClones);
9038
+ _this3.clonePathways(pathwayClones);
8886
9039
  }
8887
9040
  var loadedSteeringData = {
8888
9041
  steeringManifest: steeringData,
8889
9042
  url: url.toString()
8890
9043
  };
8891
- _this2.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
9044
+ _this3.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData);
8892
9045
  if (pathwayPriority) {
8893
- _this2.updatePathwayPriority(pathwayPriority);
9046
+ _this3.updatePathwayPriority(pathwayPriority);
8894
9047
  }
8895
9048
  },
8896
9049
  onError: function onError(error, context, networkDetails, stats) {
8897
- _this2.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
8898
- _this2.stopLoad();
9050
+ _this3.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")");
9051
+ _this3.stopLoad();
8899
9052
  if (error.code === 410) {
8900
- _this2.enabled = false;
8901
- _this2.log("Steering manifest " + context.url + " no longer available");
9053
+ _this3.enabled = false;
9054
+ _this3.log("Steering manifest " + context.url + " no longer available");
8902
9055
  return;
8903
9056
  }
8904
- var ttl = _this2.timeToLoad * 1000;
9057
+ var ttl = _this3.timeToLoad * 1000;
8905
9058
  if (error.code === 429) {
8906
- var loader = _this2.loader;
9059
+ var loader = _this3.loader;
8907
9060
  if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') {
8908
9061
  var retryAfter = loader.getResponseHeader('Retry-After');
8909
9062
  if (retryAfter) {
8910
9063
  ttl = parseFloat(retryAfter) * 1000;
8911
9064
  }
8912
9065
  }
8913
- _this2.log("Steering manifest " + context.url + " rate limited");
9066
+ _this3.log("Steering manifest " + context.url + " rate limited");
8914
9067
  return;
8915
9068
  }
8916
- _this2.scheduleRefresh(_this2.uri || context.url, ttl);
9069
+ _this3.scheduleRefresh(_this3.uri || context.url, ttl);
8917
9070
  },
8918
9071
  onTimeout: function onTimeout(stats, context, networkDetails) {
8919
- _this2.log("Timeout loading steering manifest (" + context.url + ")");
8920
- _this2.scheduleRefresh(_this2.uri || context.url);
9072
+ _this3.log("Timeout loading steering manifest (" + context.url + ")");
9073
+ _this3.scheduleRefresh(_this3.uri || context.url);
8921
9074
  }
8922
9075
  };
8923
9076
  this.log("Requesting steering manifest: " + url);
8924
9077
  this.loader.load(context, loaderConfig, callbacks);
8925
9078
  };
8926
9079
  _proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) {
8927
- var _this3 = this;
9080
+ var _this4 = this;
8928
9081
  if (ttlMs === void 0) {
8929
9082
  ttlMs = this.timeToLoad * 1000;
8930
9083
  }
8931
9084
  this.clearTimeout();
8932
9085
  this.reloadTimer = self.setTimeout(function () {
8933
- var _this3$hls;
8934
- var media = (_this3$hls = _this3.hls) == null ? void 0 : _this3$hls.media;
9086
+ var _this4$hls;
9087
+ var media = (_this4$hls = _this4.hls) == null ? void 0 : _this4$hls.media;
8935
9088
  if (media && !media.ended) {
8936
- _this3.loadSteeringManifest(uri);
9089
+ _this4.loadSteeringManifest(uri);
8937
9090
  return;
8938
9091
  }
8939
- _this3.scheduleRefresh(uri, _this3.timeToLoad * 1000);
9092
+ _this4.scheduleRefresh(uri, _this4.timeToLoad * 1000);
8940
9093
  }, ttlMs);
8941
9094
  };
8942
9095
  return ContentSteeringController;
8943
- }();
9096
+ }(Logger);
8944
9097
  function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) {
8945
9098
  if (!tracks) {
8946
9099
  return;
@@ -9776,7 +9929,7 @@
9776
9929
  });
9777
9930
  function timelineConfig() {
9778
9931
  return {
9779
- cueHandler: Cues,
9932
+ cueHandler: HevcVideoParser,
9780
9933
  // used by timeline-controller
9781
9934
  enableWebVTT: false,
9782
9935
  // used by timeline-controller
@@ -9807,7 +9960,7 @@
9807
9960
  /**
9808
9961
  * @ignore
9809
9962
  */
9810
- function mergeConfig(defaultConfig, userConfig) {
9963
+ function mergeConfig(defaultConfig, userConfig, logger) {
9811
9964
  if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) {
9812
9965
  throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");
9813
9966
  }
@@ -9877,7 +10030,7 @@
9877
10030
  /**
9878
10031
  * @ignore
9879
10032
  */
9880
- function enableStreamingMode(config) {
10033
+ function enableStreamingMode(config, logger) {
9881
10034
  var currentLoader = config.loader;
9882
10035
  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
9883
10036
  // If a developer has configured their own loader, respect that choice
@@ -9894,12 +10047,11 @@
9894
10047
  }
9895
10048
  }
9896
10049
 
9897
- var chromeOrFirefox;
9898
10050
  var LevelController = /*#__PURE__*/function (_BasePlaylistControll) {
9899
10051
  _inheritsLoose(LevelController, _BasePlaylistControll);
9900
10052
  function LevelController(hls, contentSteeringController) {
9901
10053
  var _this;
9902
- _this = _BasePlaylistControll.call(this, hls, '[level-controller]') || this;
10054
+ _this = _BasePlaylistControll.call(this, hls, 'level-controller') || this;
9903
10055
  _this._levels = [];
9904
10056
  _this._firstLevel = -1;
9905
10057
  _this._maxAutoLevel = -1;
@@ -9968,21 +10120,13 @@
9968
10120
  var videoCodecFound = false;
9969
10121
  var audioCodecFound = false;
9970
10122
  data.levels.forEach(function (levelParsed) {
9971
- var _audioCodec, _videoCodec;
10123
+ var _videoCodec;
9972
10124
  var attributes = levelParsed.attrs;
9973
-
9974
- // erase audio codec info if browser does not support mp4a.40.34.
9975
- // demuxer will autodetect codec and fallback to mpeg/audio
9976
10125
  var audioCodec = levelParsed.audioCodec,
9977
10126
  videoCodec = levelParsed.videoCodec;
9978
- if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) {
9979
- chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent));
9980
- if (chromeOrFirefox) {
9981
- levelParsed.audioCodec = audioCodec = undefined;
9982
- }
9983
- }
9984
10127
  if (audioCodec) {
9985
- levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource);
10128
+ // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer
10129
+ levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined;
9986
10130
  }
9987
10131
  if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) {
9988
10132
  videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
@@ -10214,7 +10358,12 @@
10214
10358
  if (curLevel.fragmentError === 0) {
10215
10359
  curLevel.loadError = 0;
10216
10360
  }
10217
- this.playlistLoaded(level, data, curLevel.details);
10361
+ // Ignore matching details populated by loading a Media Playlist directly
10362
+ var previousDetails = curLevel.details;
10363
+ if (previousDetails === data.details && previousDetails.advanced) {
10364
+ previousDetails = undefined;
10365
+ }
10366
+ this.playlistLoaded(level, data, previousDetails);
10218
10367
  } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
10219
10368
  // received a delta playlist update that cannot be merged
10220
10369
  details.deltaUpdateFailed = true;
@@ -10514,11 +10663,14 @@
10514
10663
  * If not found any Fragment, return null
10515
10664
  */;
10516
10665
  _proto.getBufferedFrag = function getBufferedFrag(position, levelType) {
10666
+ return this.getFragAtPos(position, levelType, true);
10667
+ };
10668
+ _proto.getFragAtPos = function getFragAtPos(position, levelType, buffered) {
10517
10669
  var fragments = this.fragments;
10518
10670
  var keys = Object.keys(fragments);
10519
10671
  for (var i = keys.length; i--;) {
10520
10672
  var fragmentEntity = fragments[keys[i]];
10521
- if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {
10673
+ if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) {
10522
10674
  var frag = fragmentEntity.body;
10523
10675
  if (frag.start <= position && position <= frag.end) {
10524
10676
  return frag;
@@ -10768,10 +10920,10 @@
10768
10920
  };
10769
10921
  };
10770
10922
  _proto.onBufferAppended = function onBufferAppended(event, data) {
10771
- var _this3 = this;
10772
10923
  var frag = data.frag,
10773
10924
  part = data.part,
10774
- timeRanges = data.timeRanges;
10925
+ timeRanges = data.timeRanges,
10926
+ type = data.type;
10775
10927
  if (frag.sn === 'initSegment') {
10776
10928
  return;
10777
10929
  }
@@ -10785,10 +10937,8 @@
10785
10937
  }
10786
10938
  // Store the latest timeRanges loaded in the buffer
10787
10939
  this.timeRanges = timeRanges;
10788
- Object.keys(timeRanges).forEach(function (elementaryStream) {
10789
- var timeRange = timeRanges[elementaryStream];
10790
- _this3.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);
10791
- });
10940
+ var timeRange = timeRanges[type];
10941
+ this.detectEvictedFragments(type, timeRange, playlistType, part);
10792
10942
  };
10793
10943
  _proto.onFragBuffered = function onFragBuffered(event, data) {
10794
10944
  this.detectPartialFragments(data);
@@ -10802,12 +10952,12 @@
10802
10952
  return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);
10803
10953
  };
10804
10954
  _proto.removeFragmentsInRange = function removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) {
10805
- var _this4 = this;
10955
+ var _this3 = this;
10806
10956
  if (withGapOnly && !this.hasGaps) {
10807
10957
  return;
10808
10958
  }
10809
10959
  Object.keys(this.fragments).forEach(function (key) {
10810
- var fragmentEntity = _this4.fragments[key];
10960
+ var fragmentEntity = _this3.fragments[key];
10811
10961
  if (!fragmentEntity) {
10812
10962
  return;
10813
10963
  }
@@ -10816,7 +10966,7 @@
10816
10966
  return;
10817
10967
  }
10818
10968
  if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) {
10819
- _this4.removeFragment(frag);
10969
+ _this3.removeFragment(frag);
10820
10970
  }
10821
10971
  });
10822
10972
  };
@@ -11129,8 +11279,8 @@
11129
11279
  var _frag$decryptdata;
11130
11280
  var byteRangeStart = start;
11131
11281
  var byteRangeEnd = end;
11132
- if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') {
11133
- // MAP segment encrypted with method 'AES-128', when served with HTTP Range,
11282
+ if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) {
11283
+ // MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range,
11134
11284
  // has the unencrypted size specified in the range.
11135
11285
  // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
11136
11286
  var fragmentLen = end - start;
@@ -11163,6 +11313,9 @@
11163
11313
  (part ? part : frag).stats.aborted = true;
11164
11314
  return new LoadError(errorData);
11165
11315
  }
11316
+ function isMethodFullSegmentAesCbc(method) {
11317
+ return method === 'AES-128' || method === 'AES-256';
11318
+ }
11166
11319
  var LoadError = /*#__PURE__*/function (_Error) {
11167
11320
  _inheritsLoose(LoadError, _Error);
11168
11321
  function LoadError(data) {
@@ -11319,6 +11472,8 @@
11319
11472
  }
11320
11473
  return this.loadKeyEME(keyInfo, frag);
11321
11474
  case 'AES-128':
11475
+ case 'AES-256':
11476
+ case 'AES-256-CTR':
11322
11477
  return this.loadKeyHTTP(keyInfo, frag);
11323
11478
  default:
11324
11479
  return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\"")));
@@ -11452,13 +11607,17 @@
11452
11607
  * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further
11453
11608
  * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo).
11454
11609
  */
11455
- var TaskLoop = /*#__PURE__*/function () {
11456
- function TaskLoop() {
11457
- this._boundTick = void 0;
11458
- this._tickTimer = null;
11459
- this._tickInterval = null;
11460
- this._tickCallCount = 0;
11461
- this._boundTick = this.tick.bind(this);
11610
+ var TaskLoop = /*#__PURE__*/function (_Logger) {
11611
+ _inheritsLoose(TaskLoop, _Logger);
11612
+ function TaskLoop(label, logger) {
11613
+ var _this;
11614
+ _this = _Logger.call(this, label, logger) || this;
11615
+ _this._boundTick = void 0;
11616
+ _this._tickTimer = null;
11617
+ _this._tickInterval = null;
11618
+ _this._tickCallCount = 0;
11619
+ _this._boundTick = _this.tick.bind(_assertThisInitialized(_this));
11620
+ return _this;
11462
11621
  }
11463
11622
  var _proto = TaskLoop.prototype;
11464
11623
  _proto.destroy = function destroy() {
@@ -11544,7 +11703,7 @@
11544
11703
  */;
11545
11704
  _proto.doTick = function doTick() {};
11546
11705
  return TaskLoop;
11547
- }();
11706
+ }(Logger);
11548
11707
 
11549
11708
  var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) {
11550
11709
  if (size === void 0) {
@@ -11730,37 +11889,65 @@
11730
11889
  }
11731
11890
 
11732
11891
  var AESCrypto = /*#__PURE__*/function () {
11733
- function AESCrypto(subtle, iv) {
11892
+ function AESCrypto(subtle, iv, aesMode) {
11734
11893
  this.subtle = void 0;
11735
11894
  this.aesIV = void 0;
11895
+ this.aesMode = void 0;
11736
11896
  this.subtle = subtle;
11737
11897
  this.aesIV = iv;
11898
+ this.aesMode = aesMode;
11738
11899
  }
11739
11900
  var _proto = AESCrypto.prototype;
11740
11901
  _proto.decrypt = function decrypt(data, key) {
11741
- return this.subtle.decrypt({
11742
- name: 'AES-CBC',
11743
- iv: this.aesIV
11744
- }, key, data);
11902
+ switch (this.aesMode) {
11903
+ case DecrypterAesMode.cbc:
11904
+ return this.subtle.decrypt({
11905
+ name: 'AES-CBC',
11906
+ iv: this.aesIV
11907
+ }, key, data);
11908
+ case DecrypterAesMode.ctr:
11909
+ return this.subtle.decrypt({
11910
+ name: 'AES-CTR',
11911
+ counter: this.aesIV,
11912
+ length: 64
11913
+ },
11914
+ //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
11915
+ key, data);
11916
+ default:
11917
+ throw new Error("[AESCrypto] invalid aes mode " + this.aesMode);
11918
+ }
11745
11919
  };
11746
11920
  return AESCrypto;
11747
11921
  }();
11748
11922
 
11749
11923
  var FastAESKey = /*#__PURE__*/function () {
11750
- function FastAESKey(subtle, key) {
11924
+ function FastAESKey(subtle, key, aesMode) {
11751
11925
  this.subtle = void 0;
11752
11926
  this.key = void 0;
11927
+ this.aesMode = void 0;
11753
11928
  this.subtle = subtle;
11754
11929
  this.key = key;
11930
+ this.aesMode = aesMode;
11755
11931
  }
11756
11932
  var _proto = FastAESKey.prototype;
11757
11933
  _proto.expandKey = function expandKey() {
11934
+ var subtleAlgoName = getSubtleAlgoName(this.aesMode);
11758
11935
  return this.subtle.importKey('raw', this.key, {
11759
- name: 'AES-CBC'
11936
+ name: subtleAlgoName
11760
11937
  }, false, ['encrypt', 'decrypt']);
11761
11938
  };
11762
11939
  return FastAESKey;
11763
11940
  }();
11941
+ function getSubtleAlgoName(aesMode) {
11942
+ switch (aesMode) {
11943
+ case DecrypterAesMode.cbc:
11944
+ return 'AES-CBC';
11945
+ case DecrypterAesMode.ctr:
11946
+ return 'AES-CTR';
11947
+ default:
11948
+ throw new Error("[FastAESKey] invalid aes mode " + aesMode);
11949
+ }
11950
+ }
11764
11951
 
11765
11952
  // PKCS7
11766
11953
  function removePadding(array) {
@@ -12013,7 +12200,8 @@
12013
12200
  this.currentIV = null;
12014
12201
  this.currentResult = null;
12015
12202
  this.useSoftware = void 0;
12016
- this.useSoftware = config.enableSoftwareAES;
12203
+ this.enableSoftwareAES = void 0;
12204
+ this.enableSoftwareAES = config.enableSoftwareAES;
12017
12205
  this.removePKCS7Padding = removePKCS7Padding;
12018
12206
  // built in decryptor expects PKCS7 padding
12019
12207
  if (removePKCS7Padding) {
@@ -12026,9 +12214,7 @@
12026
12214
  /* no-op */
12027
12215
  }
12028
12216
  }
12029
- if (this.subtle === null) {
12030
- this.useSoftware = true;
12031
- }
12217
+ this.useSoftware = this.subtle === null;
12032
12218
  }
12033
12219
  var _proto = Decrypter.prototype;
12034
12220
  _proto.destroy = function destroy() {
@@ -12065,11 +12251,11 @@
12065
12251
  this.softwareDecrypter = null;
12066
12252
  }
12067
12253
  };
12068
- _proto.decrypt = function decrypt(data, key, iv) {
12254
+ _proto.decrypt = function decrypt(data, key, iv, aesMode) {
12069
12255
  var _this = this;
12070
12256
  if (this.useSoftware) {
12071
12257
  return new Promise(function (resolve, reject) {
12072
- _this.softwareDecrypt(new Uint8Array(data), key, iv);
12258
+ _this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
12073
12259
  var decryptResult = _this.flush();
12074
12260
  if (decryptResult) {
12075
12261
  resolve(decryptResult.buffer);
@@ -12078,16 +12264,20 @@
12078
12264
  }
12079
12265
  });
12080
12266
  }
12081
- return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
12267
+ return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
12082
12268
  }
12083
12269
 
12084
12270
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
12085
12271
  // data is handled in the flush() call
12086
12272
  ;
12087
- _proto.softwareDecrypt = function softwareDecrypt(data, key, iv) {
12273
+ _proto.softwareDecrypt = function softwareDecrypt(data, key, iv, aesMode) {
12088
12274
  var currentIV = this.currentIV,
12089
12275
  currentResult = this.currentResult,
12090
12276
  remainderData = this.remainderData;
12277
+ if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
12278
+ logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
12279
+ return null;
12280
+ }
12091
12281
  this.logOnce('JS AES decrypt');
12092
12282
  // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
12093
12283
  // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
@@ -12120,12 +12310,12 @@
12120
12310
  }
12121
12311
  return result;
12122
12312
  };
12123
- _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv) {
12313
+ _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv, aesMode) {
12124
12314
  var _this2 = this;
12125
12315
  var subtle = this.subtle;
12126
12316
  if (this.key !== key || !this.fastAesKey) {
12127
12317
  this.key = key;
12128
- this.fastAesKey = new FastAESKey(subtle, key);
12318
+ this.fastAesKey = new FastAESKey(subtle, key, aesMode);
12129
12319
  }
12130
12320
  return this.fastAesKey.expandKey().then(function (aesKey) {
12131
12321
  // decrypt using web crypto
@@ -12133,22 +12323,25 @@
12133
12323
  return Promise.reject(new Error('web crypto not initialized'));
12134
12324
  }
12135
12325
  _this2.logOnce('WebCrypto AES decrypt');
12136
- var crypto = new AESCrypto(subtle, new Uint8Array(iv));
12326
+ var crypto = new AESCrypto(subtle, new Uint8Array(iv), aesMode);
12137
12327
  return crypto.decrypt(data.buffer, aesKey);
12138
12328
  }).catch(function (err) {
12139
12329
  logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message);
12140
- return _this2.onWebCryptoError(data, key, iv);
12330
+ return _this2.onWebCryptoError(data, key, iv, aesMode);
12141
12331
  });
12142
12332
  };
12143
- _proto.onWebCryptoError = function onWebCryptoError(data, key, iv) {
12144
- this.useSoftware = true;
12145
- this.logEnabled = true;
12146
- this.softwareDecrypt(data, key, iv);
12147
- var decryptResult = this.flush();
12148
- if (decryptResult) {
12149
- return decryptResult.buffer;
12333
+ _proto.onWebCryptoError = function onWebCryptoError(data, key, iv, aesMode) {
12334
+ var enableSoftwareAES = this.enableSoftwareAES;
12335
+ if (enableSoftwareAES) {
12336
+ this.useSoftware = true;
12337
+ this.logEnabled = true;
12338
+ this.softwareDecrypt(data, key, iv, aesMode);
12339
+ var decryptResult = this.flush();
12340
+ if (decryptResult) {
12341
+ return decryptResult.buffer;
12342
+ }
12150
12343
  }
12151
- throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
12344
+ throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data');
12152
12345
  };
12153
12346
  _proto.getValidChunk = function getValidChunk(data) {
12154
12347
  var currentChunk = data;
@@ -12202,7 +12395,7 @@
12202
12395
  _inheritsLoose(BaseStreamController, _TaskLoop);
12203
12396
  function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) {
12204
12397
  var _this;
12205
- _this = _TaskLoop.call(this) || this;
12398
+ _this = _TaskLoop.call(this, logPrefix, hls.logger) || this;
12206
12399
  _this.hls = void 0;
12207
12400
  _this.fragPrevious = null;
12208
12401
  _this.fragCurrent = null;
@@ -12227,25 +12420,96 @@
12227
12420
  _this.startFragRequested = false;
12228
12421
  _this.decrypter = void 0;
12229
12422
  _this.initPTS = [];
12230
- _this.onvseeking = null;
12231
- _this.onvended = null;
12232
- _this.logPrefix = '';
12233
- _this.log = void 0;
12234
- _this.warn = void 0;
12423
+ _this.buffering = true;
12424
+ _this.loadingParts = false;
12425
+ _this.onMediaSeeking = function () {
12426
+ var _assertThisInitialize = _assertThisInitialized(_this),
12427
+ config = _assertThisInitialize.config,
12428
+ fragCurrent = _assertThisInitialize.fragCurrent,
12429
+ media = _assertThisInitialize.media,
12430
+ mediaBuffer = _assertThisInitialize.mediaBuffer,
12431
+ state = _assertThisInitialize.state;
12432
+ var currentTime = media ? media.currentTime : 0;
12433
+ var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12434
+ _this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12435
+ if (_this.state === State.ENDED) {
12436
+ _this.resetLoadingState();
12437
+ } else if (fragCurrent) {
12438
+ // Seeking while frag load is in progress
12439
+ var tolerance = config.maxFragLookUpTolerance;
12440
+ var fragStartOffset = fragCurrent.start - tolerance;
12441
+ var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12442
+ // if seeking out of buffered range or into new one
12443
+ if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12444
+ var pastFragment = currentTime > fragEndOffset;
12445
+ // if the seek position is outside the current fragment range
12446
+ if (currentTime < fragStartOffset || pastFragment) {
12447
+ if (pastFragment && fragCurrent.loader) {
12448
+ _this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12449
+ fragCurrent.abortRequests();
12450
+ _this.resetLoadingState();
12451
+ }
12452
+ _this.fragPrevious = null;
12453
+ }
12454
+ }
12455
+ }
12456
+ if (media) {
12457
+ // Remove gap fragments
12458
+ _this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, _this.playlistType, true);
12459
+ _this.lastCurrentTime = currentTime;
12460
+ if (!_this.loadingParts) {
12461
+ var bufferEnd = Math.max(bufferInfo.end, currentTime);
12462
+ var shouldLoadParts = _this.shouldLoadParts(_this.getLevelDetails(), bufferEnd);
12463
+ if (shouldLoadParts) {
12464
+ _this.log("LL-Part loading ON after seeking to " + currentTime.toFixed(2) + " with buffer @" + bufferEnd.toFixed(2));
12465
+ _this.loadingParts = shouldLoadParts;
12466
+ }
12467
+ }
12468
+ }
12469
+
12470
+ // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12471
+ if (!_this.loadedmetadata && !bufferInfo.len) {
12472
+ _this.nextLoadPosition = _this.startPosition = currentTime;
12473
+ }
12474
+
12475
+ // Async tick to speed up processing
12476
+ _this.tickImmediate();
12477
+ };
12478
+ _this.onMediaEnded = function () {
12479
+ // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12480
+ _this.startPosition = _this.lastCurrentTime = 0;
12481
+ if (_this.playlistType === PlaylistLevelType.MAIN) {
12482
+ _this.hls.trigger(Events.MEDIA_ENDED, {
12483
+ stalled: false
12484
+ });
12485
+ }
12486
+ };
12235
12487
  _this.playlistType = playlistType;
12236
- _this.logPrefix = logPrefix;
12237
- _this.log = logger.log.bind(logger, logPrefix + ":");
12238
- _this.warn = logger.warn.bind(logger, logPrefix + ":");
12239
12488
  _this.hls = hls;
12240
12489
  _this.fragmentLoader = new FragmentLoader(hls.config);
12241
12490
  _this.keyLoader = keyLoader;
12242
12491
  _this.fragmentTracker = fragmentTracker;
12243
12492
  _this.config = hls.config;
12244
12493
  _this.decrypter = new Decrypter(hls.config);
12245
- hls.on(Events.MANIFEST_LOADED, _this.onManifestLoaded, _assertThisInitialized(_this));
12246
12494
  return _this;
12247
12495
  }
12248
12496
  var _proto = BaseStreamController.prototype;
12497
+ _proto.registerListeners = function registerListeners() {
12498
+ var hls = this.hls;
12499
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12500
+ hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12501
+ hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12502
+ hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12503
+ hls.on(Events.ERROR, this.onError, this);
12504
+ };
12505
+ _proto.unregisterListeners = function unregisterListeners() {
12506
+ var hls = this.hls;
12507
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
12508
+ hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
12509
+ hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
12510
+ hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
12511
+ hls.off(Events.ERROR, this.onError, this);
12512
+ };
12249
12513
  _proto.doTick = function doTick() {
12250
12514
  this.onTickEnd();
12251
12515
  };
@@ -12269,6 +12533,12 @@
12269
12533
  this.clearNextTick();
12270
12534
  this.state = State.STOPPED;
12271
12535
  };
12536
+ _proto.pauseBuffering = function pauseBuffering() {
12537
+ this.buffering = false;
12538
+ };
12539
+ _proto.resumeBuffering = function resumeBuffering() {
12540
+ this.buffering = true;
12541
+ };
12272
12542
  _proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) {
12273
12543
  // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
12274
12544
  // of nothing loading/loaded return false
@@ -12299,10 +12569,8 @@
12299
12569
  };
12300
12570
  _proto.onMediaAttached = function onMediaAttached(event, data) {
12301
12571
  var media = this.media = this.mediaBuffer = data.media;
12302
- this.onvseeking = this.onMediaSeeking.bind(this);
12303
- this.onvended = this.onMediaEnded.bind(this);
12304
- media.addEventListener('seeking', this.onvseeking);
12305
- media.addEventListener('ended', this.onvended);
12572
+ media.addEventListener('seeking', this.onMediaSeeking);
12573
+ media.addEventListener('ended', this.onMediaEnded);
12306
12574
  var config = this.config;
12307
12575
  if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
12308
12576
  this.startLoad(config.startPosition);
@@ -12316,10 +12584,9 @@
12316
12584
  }
12317
12585
 
12318
12586
  // remove video listeners
12319
- if (media && this.onvseeking && this.onvended) {
12320
- media.removeEventListener('seeking', this.onvseeking);
12321
- media.removeEventListener('ended', this.onvended);
12322
- this.onvseeking = this.onvended = null;
12587
+ if (media) {
12588
+ media.removeEventListener('seeking', this.onMediaSeeking);
12589
+ media.removeEventListener('ended', this.onMediaEnded);
12323
12590
  }
12324
12591
  if (this.keyLoader) {
12325
12592
  this.keyLoader.detach();
@@ -12329,54 +12596,8 @@
12329
12596
  this.fragmentTracker.removeAllFragments();
12330
12597
  this.stopLoad();
12331
12598
  };
12332
- _proto.onMediaSeeking = function onMediaSeeking() {
12333
- var config = this.config,
12334
- fragCurrent = this.fragCurrent,
12335
- media = this.media,
12336
- mediaBuffer = this.mediaBuffer,
12337
- state = this.state;
12338
- var currentTime = media ? media.currentTime : 0;
12339
- var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole);
12340
- this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state);
12341
- if (this.state === State.ENDED) {
12342
- this.resetLoadingState();
12343
- } else if (fragCurrent) {
12344
- // Seeking while frag load is in progress
12345
- var tolerance = config.maxFragLookUpTolerance;
12346
- var fragStartOffset = fragCurrent.start - tolerance;
12347
- var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
12348
- // if seeking out of buffered range or into new one
12349
- if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) {
12350
- var pastFragment = currentTime > fragEndOffset;
12351
- // if the seek position is outside the current fragment range
12352
- if (currentTime < fragStartOffset || pastFragment) {
12353
- if (pastFragment && fragCurrent.loader) {
12354
- this.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
12355
- fragCurrent.abortRequests();
12356
- this.resetLoadingState();
12357
- }
12358
- this.fragPrevious = null;
12359
- }
12360
- }
12361
- }
12362
- if (media) {
12363
- // Remove gap fragments
12364
- this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true);
12365
- this.lastCurrentTime = currentTime;
12366
- }
12367
-
12368
- // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
12369
- if (!this.loadedmetadata && !bufferInfo.len) {
12370
- this.nextLoadPosition = this.startPosition = currentTime;
12371
- }
12372
-
12373
- // Async tick to speed up processing
12374
- this.tickImmediate();
12375
- };
12376
- _proto.onMediaEnded = function onMediaEnded() {
12377
- // reset startPosition and lastCurrentTime to restart playback @ stream beginning
12378
- this.startPosition = this.lastCurrentTime = 0;
12379
- };
12599
+ _proto.onManifestLoading = function onManifestLoading() {};
12600
+ _proto.onError = function onError(event, data) {};
12380
12601
  _proto.onManifestLoaded = function onManifestLoaded(event, data) {
12381
12602
  this.startTimeOffset = data.startTimeOffset;
12382
12603
  this.initPTS = [];
@@ -12386,7 +12607,7 @@
12386
12607
  this.stopLoad();
12387
12608
  _TaskLoop.prototype.onHandlerDestroying.call(this);
12388
12609
  // @ts-ignore
12389
- this.hls = null;
12610
+ this.hls = this.onMediaSeeking = this.onMediaEnded = null;
12390
12611
  };
12391
12612
  _proto.onHandlerDestroyed = function onHandlerDestroyed() {
12392
12613
  this.state = State.STOPPED;
@@ -12516,10 +12737,10 @@
12516
12737
  var decryptData = frag.decryptdata;
12517
12738
 
12518
12739
  // check to see if the payload needs to be decrypted
12519
- if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') {
12740
+ if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
12520
12741
  var startTime = self.performance.now();
12521
12742
  // decrypt init segment data
12522
- return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(function (err) {
12743
+ return _this3.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(function (err) {
12523
12744
  hls.trigger(Events.ERROR, {
12524
12745
  type: ErrorTypes.MEDIA_ERROR,
12525
12746
  details: ErrorDetails.FRAG_DECRYPT_ERROR,
@@ -12632,7 +12853,7 @@
12632
12853
  }
12633
12854
  var keyLoadingPromise = null;
12634
12855
  if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
12635
- this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.logPrefix === '[stream-controller]' ? 'level' : 'track') + " " + frag.level);
12856
+ this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + (this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track') + " " + frag.level);
12636
12857
  this.state = State.KEY_LOADING;
12637
12858
  this.fragCurrent = frag;
12638
12859
  keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) {
@@ -12653,8 +12874,16 @@
12653
12874
  } else if (!frag.encrypted && details.encryptedFragments.length) {
12654
12875
  this.keyLoader.loadClear(frag, details.encryptedFragments);
12655
12876
  }
12877
+ var fragPrevious = this.fragPrevious;
12878
+ if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) {
12879
+ var shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
12880
+ if (shouldLoadParts !== this.loadingParts) {
12881
+ this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " loading sn " + (fragPrevious == null ? void 0 : fragPrevious.sn) + "->" + frag.sn);
12882
+ this.loadingParts = shouldLoadParts;
12883
+ }
12884
+ }
12656
12885
  targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
12657
- if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
12886
+ if (this.loadingParts && frag.sn !== 'initSegment') {
12658
12887
  var partList = details.partList;
12659
12888
  if (partList && progressCallback) {
12660
12889
  if (targetBufferTime > frag.end && details.fragmentHint) {
@@ -12663,7 +12892,7 @@
12663
12892
  var partIndex = this.getNextPart(partList, frag, targetBufferTime);
12664
12893
  if (partIndex > -1) {
12665
12894
  var part = partList[partIndex];
12666
- 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)));
12895
+ 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)));
12667
12896
  this.nextLoadPosition = part.start + part.duration;
12668
12897
  this.state = State.FRAG_LOADING;
12669
12898
  var _result;
@@ -12696,7 +12925,14 @@
12696
12925
  }
12697
12926
  }
12698
12927
  }
12699
- 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)));
12928
+ if (frag.sn !== 'initSegment' && this.loadingParts) {
12929
+ this.log("LL-Part loading OFF after next part miss @" + targetBufferTime.toFixed(2));
12930
+ this.loadingParts = false;
12931
+ } else if (!frag.url) {
12932
+ // Selected fragment hint for part but not loading parts
12933
+ return Promise.resolve(null);
12934
+ }
12935
+ 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)));
12700
12936
  // Don't update nextLoadPosition for fragments which are not buffered
12701
12937
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
12702
12938
  this.nextLoadPosition = frag.start + frag.duration;
@@ -12798,8 +13034,36 @@
12798
13034
  if (part) {
12799
13035
  part.stats.parsing.end = now;
12800
13036
  }
13037
+ // See if part loading should be disabled/enabled based on buffer and playback position.
13038
+ if (frag.sn !== 'initSegment') {
13039
+ var levelDetails = this.getLevelDetails();
13040
+ var loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
13041
+ var shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
13042
+ if (shouldLoadParts !== this.loadingParts) {
13043
+ this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " after parsing segment ending @" + frag.end.toFixed(2));
13044
+ this.loadingParts = shouldLoadParts;
13045
+ }
13046
+ }
12801
13047
  this.updateLevelTiming(frag, part, level, chunkMeta.partial);
12802
13048
  };
13049
+ _proto.shouldLoadParts = function shouldLoadParts(details, bufferEnd) {
13050
+ if (this.config.lowLatencyMode) {
13051
+ if (!details) {
13052
+ return this.loadingParts;
13053
+ }
13054
+ if (details != null && details.partList) {
13055
+ var _details$fragmentHint;
13056
+ // Buffer must be ahead of first part + duration of parts after last segment
13057
+ // and playback must be at or past segment adjacent to part list
13058
+ var firstPart = details.partList[0];
13059
+ var safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0);
13060
+ if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) {
13061
+ return true;
13062
+ }
13063
+ }
13064
+ }
13065
+ return false;
13066
+ };
12803
13067
  _proto.getCurrentContext = function getCurrentContext(chunkMeta) {
12804
13068
  var levels = this.levels,
12805
13069
  fragCurrent = this.fragCurrent;
@@ -12890,7 +13154,7 @@
12890
13154
  // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
12891
13155
  if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
12892
13156
  var bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
12893
- if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
13157
+ if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) {
12894
13158
  return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
12895
13159
  }
12896
13160
  }
@@ -12934,7 +13198,8 @@
12934
13198
  // find fragment index, contiguous with end of buffer position
12935
13199
  var config = this.config;
12936
13200
  var start = fragments[0].start;
12937
- var frag;
13201
+ var canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
13202
+ var frag = null;
12938
13203
  if (levelDetails.live) {
12939
13204
  var initialLiveManifestSize = config.initialLiveManifestSize;
12940
13205
  if (fragLen < initialLiveManifestSize) {
@@ -12946,6 +13211,10 @@
12946
13211
  // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that
12947
13212
  // we get the fragment matching that start time
12948
13213
  if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) {
13214
+ if (canLoadParts && !this.loadingParts) {
13215
+ this.log("LL-Part loading ON for initial live fragment");
13216
+ this.loadingParts = true;
13217
+ }
12949
13218
  frag = this.getInitialLiveFragment(levelDetails, fragments);
12950
13219
  this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos;
12951
13220
  }
@@ -12956,7 +13225,7 @@
12956
13225
 
12957
13226
  // If we haven't run into any special cases already, just load the fragment most closely matching the requested position
12958
13227
  if (!frag) {
12959
- var end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd;
13228
+ var end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd;
12960
13229
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
12961
13230
  }
12962
13231
  return this.mapToInitFragWhenRequired(frag);
@@ -13070,7 +13339,7 @@
13070
13339
  var fragmentHint = levelDetails.fragmentHint;
13071
13340
  var tolerance = config.maxFragLookUpTolerance;
13072
13341
  var partList = levelDetails.partList;
13073
- var loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint);
13342
+ var loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint);
13074
13343
  if (loadingParts && fragmentHint && !this.bitrateTest) {
13075
13344
  // Include incomplete fragment with parts at end
13076
13345
  fragments = fragments.concat(fragmentHint);
@@ -13257,7 +13526,7 @@
13257
13526
  errorAction.resolved = true;
13258
13527
  }
13259
13528
  } else {
13260
- logger.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13529
+ this.warn(data.details + " reached or exceeded max retry (" + retryCount + ")");
13261
13530
  return;
13262
13531
  }
13263
13532
  } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) {
@@ -13325,7 +13594,9 @@
13325
13594
  this.log('Reset loading state');
13326
13595
  this.fragCurrent = null;
13327
13596
  this.fragPrevious = null;
13328
- this.state = State.IDLE;
13597
+ if (this.state !== State.STOPPED) {
13598
+ this.state = State.IDLE;
13599
+ }
13329
13600
  };
13330
13601
  _proto.resetStartWhenNotLoaded = function resetStartWhenNotLoaded(level) {
13331
13602
  // if loadedmetadata is not set, it means that first frag request failed
@@ -13647,6 +13918,7 @@
13647
13918
  */
13648
13919
  function getAudioConfig(observer, data, offset, audioCodec) {
13649
13920
  var adtsObjectType;
13921
+ var originalAdtsObjectType;
13650
13922
  var adtsExtensionSamplingIndex;
13651
13923
  var adtsChannelConfig;
13652
13924
  var config;
@@ -13654,7 +13926,7 @@
13654
13926
  var manifestCodec = audioCodec;
13655
13927
  var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
13656
13928
  // byte 2
13657
- adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13929
+ adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
13658
13930
  var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
13659
13931
  if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
13660
13932
  var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex);
@@ -13671,8 +13943,8 @@
13671
13943
  // byte 3
13672
13944
  adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
13673
13945
  logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex);
13674
- // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
13675
- if (/firefox/i.test(userAgent)) {
13946
+ // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
13947
+ if (/firefox|palemoon/i.test(userAgent)) {
13676
13948
  if (adtsSamplingIndex >= 6) {
13677
13949
  adtsObjectType = 5;
13678
13950
  config = new Array(4);
@@ -13766,6 +14038,7 @@
13766
14038
  samplerate: adtsSamplingRates[adtsSamplingIndex],
13767
14039
  channelCount: adtsChannelConfig,
13768
14040
  codec: 'mp4a.40.' + adtsObjectType,
14041
+ parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
13769
14042
  manifestCodec: manifestCodec
13770
14043
  };
13771
14044
  }
@@ -13820,7 +14093,8 @@
13820
14093
  track.channelCount = config.channelCount;
13821
14094
  track.codec = config.codec;
13822
14095
  track.manifestCodec = config.manifestCodec;
13823
- logger.log("parsed codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
14096
+ track.parsedCodec = config.parsedCodec;
14097
+ logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount);
13824
14098
  }
13825
14099
  }
13826
14100
  function getFrameDuration(samplerate) {
@@ -14300,72 +14574,176 @@
14300
14574
  logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
14301
14575
  }
14302
14576
  };
14303
- return BaseVideoParser;
14304
- }();
14305
-
14306
- /**
14307
- * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
14308
- */
14309
-
14310
- var ExpGolomb = /*#__PURE__*/function () {
14311
- function ExpGolomb(data) {
14312
- this.data = void 0;
14313
- this.bytesAvailable = void 0;
14314
- this.word = void 0;
14315
- this.bitsAvailable = void 0;
14316
- this.data = data;
14317
- // the number of bytes left to examine in this.data
14318
- this.bytesAvailable = data.byteLength;
14319
- // the current word being examined
14320
- this.word = 0; // :uint
14321
- // the number of bits left to examine in the current word
14322
- this.bitsAvailable = 0; // :uint
14323
- }
14324
-
14325
- // ():void
14326
- var _proto = ExpGolomb.prototype;
14327
- _proto.loadWord = function loadWord() {
14328
- var data = this.data;
14329
- var bytesAvailable = this.bytesAvailable;
14330
- var position = data.byteLength - bytesAvailable;
14331
- var workingBytes = new Uint8Array(4);
14332
- var availableBytes = Math.min(4, bytesAvailable);
14333
- if (availableBytes === 0) {
14334
- throw new Error('no bytes available');
14335
- }
14336
- workingBytes.set(data.subarray(position, position + availableBytes));
14337
- this.word = new DataView(workingBytes.buffer).getUint32(0);
14338
- // track the amount of this.data that has been processed
14339
- this.bitsAvailable = availableBytes * 8;
14340
- this.bytesAvailable -= availableBytes;
14341
- }
14577
+ _proto.parseNALu = function parseNALu(track, array) {
14578
+ var len = array.byteLength;
14579
+ var state = track.naluState || 0;
14580
+ var lastState = state;
14581
+ var units = [];
14582
+ var i = 0;
14583
+ var value;
14584
+ var overflow;
14585
+ var unitType;
14586
+ var lastUnitStart = -1;
14587
+ var lastUnitType = 0;
14588
+ // logger.log('PES:' + Hex.hexDump(array));
14342
14589
 
14343
- // (count:int):void
14344
- ;
14345
- _proto.skipBits = function skipBits(count) {
14346
- var skipBytes; // :int
14347
- count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable);
14348
- if (this.bitsAvailable > count) {
14349
- this.word <<= count;
14350
- this.bitsAvailable -= count;
14351
- } else {
14352
- count -= this.bitsAvailable;
14353
- skipBytes = count >> 3;
14354
- count -= skipBytes << 3;
14355
- this.bytesAvailable -= skipBytes;
14356
- this.loadWord();
14357
- this.word <<= count;
14358
- this.bitsAvailable -= count;
14590
+ if (state === -1) {
14591
+ // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14592
+ lastUnitStart = 0;
14593
+ // NALu type is value read from offset 0
14594
+ lastUnitType = this.getNALuType(array, 0);
14595
+ state = 0;
14596
+ i = 1;
14359
14597
  }
14360
- }
14361
-
14362
- // (size:int):uint
14363
- ;
14364
- _proto.readBits = function readBits(size) {
14365
- var bits = Math.min(this.bitsAvailable, size); // :uint
14366
- var valu = this.word >>> 32 - bits; // :uint
14367
- if (size > 32) {
14368
- logger.error('Cannot read more than 32 bits at a time');
14598
+ while (i < len) {
14599
+ value = array[i++];
14600
+ // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14601
+ if (!state) {
14602
+ state = value ? 0 : 1;
14603
+ continue;
14604
+ }
14605
+ if (state === 1) {
14606
+ state = value ? 0 : 2;
14607
+ continue;
14608
+ }
14609
+ // here we have state either equal to 2 or 3
14610
+ if (!value) {
14611
+ state = 3;
14612
+ } else if (value === 1) {
14613
+ overflow = i - state - 1;
14614
+ if (lastUnitStart >= 0) {
14615
+ var unit = {
14616
+ data: array.subarray(lastUnitStart, overflow),
14617
+ type: lastUnitType
14618
+ };
14619
+ // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14620
+ units.push(unit);
14621
+ } else {
14622
+ // lastUnitStart is undefined => this is the first start code found in this PES packet
14623
+ // first check if start code delimiter is overlapping between 2 PES packets,
14624
+ // ie it started in last packet (lastState not zero)
14625
+ // and ended at the beginning of this PES packet (i <= 4 - lastState)
14626
+ var lastUnit = this.getLastNalUnit(track.samples);
14627
+ if (lastUnit) {
14628
+ if (lastState && i <= 4 - lastState) {
14629
+ // start delimiter overlapping between PES packets
14630
+ // strip start delimiter bytes from the end of last NAL unit
14631
+ // check if lastUnit had a state different from zero
14632
+ if (lastUnit.state) {
14633
+ // strip last bytes
14634
+ lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14635
+ }
14636
+ }
14637
+ // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14638
+
14639
+ if (overflow > 0) {
14640
+ // logger.log('first NALU found with overflow:' + overflow);
14641
+ lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14642
+ lastUnit.state = 0;
14643
+ }
14644
+ }
14645
+ }
14646
+ // check if we can read unit type
14647
+ if (i < len) {
14648
+ unitType = this.getNALuType(array, i);
14649
+ // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14650
+ lastUnitStart = i;
14651
+ lastUnitType = unitType;
14652
+ state = 0;
14653
+ } else {
14654
+ // not enough byte to read unit type. let's read it on next PES parsing
14655
+ state = -1;
14656
+ }
14657
+ } else {
14658
+ state = 0;
14659
+ }
14660
+ }
14661
+ if (lastUnitStart >= 0 && state >= 0) {
14662
+ var _unit = {
14663
+ data: array.subarray(lastUnitStart, len),
14664
+ type: lastUnitType,
14665
+ state: state
14666
+ };
14667
+ units.push(_unit);
14668
+ // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14669
+ }
14670
+ // no NALu found
14671
+ if (units.length === 0) {
14672
+ // append pes.data to previous NAL unit
14673
+ var _lastUnit = this.getLastNalUnit(track.samples);
14674
+ if (_lastUnit) {
14675
+ _lastUnit.data = appendUint8Array(_lastUnit.data, array);
14676
+ }
14677
+ }
14678
+ track.naluState = state;
14679
+ return units;
14680
+ };
14681
+ return BaseVideoParser;
14682
+ }();
14683
+
14684
+ /**
14685
+ * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
14686
+ */
14687
+
14688
+ var ExpGolomb = /*#__PURE__*/function () {
14689
+ function ExpGolomb(data) {
14690
+ this.data = void 0;
14691
+ this.bytesAvailable = void 0;
14692
+ this.word = void 0;
14693
+ this.bitsAvailable = void 0;
14694
+ this.data = data;
14695
+ // the number of bytes left to examine in this.data
14696
+ this.bytesAvailable = data.byteLength;
14697
+ // the current word being examined
14698
+ this.word = 0; // :uint
14699
+ // the number of bits left to examine in the current word
14700
+ this.bitsAvailable = 0; // :uint
14701
+ }
14702
+
14703
+ // ():void
14704
+ var _proto = ExpGolomb.prototype;
14705
+ _proto.loadWord = function loadWord() {
14706
+ var data = this.data;
14707
+ var bytesAvailable = this.bytesAvailable;
14708
+ var position = data.byteLength - bytesAvailable;
14709
+ var workingBytes = new Uint8Array(4);
14710
+ var availableBytes = Math.min(4, bytesAvailable);
14711
+ if (availableBytes === 0) {
14712
+ throw new Error('no bytes available');
14713
+ }
14714
+ workingBytes.set(data.subarray(position, position + availableBytes));
14715
+ this.word = new DataView(workingBytes.buffer).getUint32(0);
14716
+ // track the amount of this.data that has been processed
14717
+ this.bitsAvailable = availableBytes * 8;
14718
+ this.bytesAvailable -= availableBytes;
14719
+ }
14720
+
14721
+ // (count:int):void
14722
+ ;
14723
+ _proto.skipBits = function skipBits(count) {
14724
+ var skipBytes; // :int
14725
+ count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable);
14726
+ if (this.bitsAvailable > count) {
14727
+ this.word <<= count;
14728
+ this.bitsAvailable -= count;
14729
+ } else {
14730
+ count -= this.bitsAvailable;
14731
+ skipBytes = count >> 3;
14732
+ count -= skipBytes << 3;
14733
+ this.bytesAvailable -= skipBytes;
14734
+ this.loadWord();
14735
+ this.word <<= count;
14736
+ this.bitsAvailable -= count;
14737
+ }
14738
+ }
14739
+
14740
+ // (size:int):uint
14741
+ ;
14742
+ _proto.readBits = function readBits(size) {
14743
+ var bits = Math.min(this.bitsAvailable, size); // :uint
14744
+ var valu = this.word >>> 32 - bits; // :uint
14745
+ if (size > 32) {
14746
+ logger.error('Cannot read more than 32 bits at a time');
14369
14747
  }
14370
14748
  this.bitsAvailable -= bits;
14371
14749
  if (this.bitsAvailable > 0) {
@@ -14454,189 +14832,6 @@
14454
14832
  ;
14455
14833
  _proto.readUInt = function readUInt() {
14456
14834
  return this.readBits(32);
14457
- }
14458
-
14459
- /**
14460
- * Advance the ExpGolomb decoder past a scaling list. The scaling
14461
- * list is optionally transmitted as part of a sequence parameter
14462
- * set and is not relevant to transmuxing.
14463
- * @param count the number of entries in this scaling list
14464
- * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14465
- */;
14466
- _proto.skipScalingList = function skipScalingList(count) {
14467
- var lastScale = 8;
14468
- var nextScale = 8;
14469
- var deltaScale;
14470
- for (var j = 0; j < count; j++) {
14471
- if (nextScale !== 0) {
14472
- deltaScale = this.readEG();
14473
- nextScale = (lastScale + deltaScale + 256) % 256;
14474
- }
14475
- lastScale = nextScale === 0 ? lastScale : nextScale;
14476
- }
14477
- }
14478
-
14479
- /**
14480
- * Read a sequence parameter set and return some interesting video
14481
- * properties. A sequence parameter set is the H264 metadata that
14482
- * describes the properties of upcoming video frames.
14483
- * @returns an object with configuration parsed from the
14484
- * sequence parameter set, including the dimensions of the
14485
- * associated video frames.
14486
- */;
14487
- _proto.readSPS = function readSPS() {
14488
- var frameCropLeftOffset = 0;
14489
- var frameCropRightOffset = 0;
14490
- var frameCropTopOffset = 0;
14491
- var frameCropBottomOffset = 0;
14492
- var numRefFramesInPicOrderCntCycle;
14493
- var scalingListCount;
14494
- var i;
14495
- var readUByte = this.readUByte.bind(this);
14496
- var readBits = this.readBits.bind(this);
14497
- var readUEG = this.readUEG.bind(this);
14498
- var readBoolean = this.readBoolean.bind(this);
14499
- var skipBits = this.skipBits.bind(this);
14500
- var skipEG = this.skipEG.bind(this);
14501
- var skipUEG = this.skipUEG.bind(this);
14502
- var skipScalingList = this.skipScalingList.bind(this);
14503
- readUByte();
14504
- var profileIdc = readUByte(); // profile_idc
14505
- readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
14506
- skipBits(3); // reserved_zero_3bits u(3),
14507
- readUByte(); // level_idc u(8)
14508
- skipUEG(); // seq_parameter_set_id
14509
- // some profiles have more optional data we don't need
14510
- if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
14511
- var chromaFormatIdc = readUEG();
14512
- if (chromaFormatIdc === 3) {
14513
- skipBits(1);
14514
- } // separate_colour_plane_flag
14515
-
14516
- skipUEG(); // bit_depth_luma_minus8
14517
- skipUEG(); // bit_depth_chroma_minus8
14518
- skipBits(1); // qpprime_y_zero_transform_bypass_flag
14519
- if (readBoolean()) {
14520
- // seq_scaling_matrix_present_flag
14521
- scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
14522
- for (i = 0; i < scalingListCount; i++) {
14523
- if (readBoolean()) {
14524
- // seq_scaling_list_present_flag[ i ]
14525
- if (i < 6) {
14526
- skipScalingList(16);
14527
- } else {
14528
- skipScalingList(64);
14529
- }
14530
- }
14531
- }
14532
- }
14533
- }
14534
- skipUEG(); // log2_max_frame_num_minus4
14535
- var picOrderCntType = readUEG();
14536
- if (picOrderCntType === 0) {
14537
- readUEG(); // log2_max_pic_order_cnt_lsb_minus4
14538
- } else if (picOrderCntType === 1) {
14539
- skipBits(1); // delta_pic_order_always_zero_flag
14540
- skipEG(); // offset_for_non_ref_pic
14541
- skipEG(); // offset_for_top_to_bottom_field
14542
- numRefFramesInPicOrderCntCycle = readUEG();
14543
- for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
14544
- skipEG();
14545
- } // offset_for_ref_frame[ i ]
14546
- }
14547
- skipUEG(); // max_num_ref_frames
14548
- skipBits(1); // gaps_in_frame_num_value_allowed_flag
14549
- var picWidthInMbsMinus1 = readUEG();
14550
- var picHeightInMapUnitsMinus1 = readUEG();
14551
- var frameMbsOnlyFlag = readBits(1);
14552
- if (frameMbsOnlyFlag === 0) {
14553
- skipBits(1);
14554
- } // mb_adaptive_frame_field_flag
14555
-
14556
- skipBits(1); // direct_8x8_inference_flag
14557
- if (readBoolean()) {
14558
- // frame_cropping_flag
14559
- frameCropLeftOffset = readUEG();
14560
- frameCropRightOffset = readUEG();
14561
- frameCropTopOffset = readUEG();
14562
- frameCropBottomOffset = readUEG();
14563
- }
14564
- var pixelRatio = [1, 1];
14565
- if (readBoolean()) {
14566
- // vui_parameters_present_flag
14567
- if (readBoolean()) {
14568
- // aspect_ratio_info_present_flag
14569
- var aspectRatioIdc = readUByte();
14570
- switch (aspectRatioIdc) {
14571
- case 1:
14572
- pixelRatio = [1, 1];
14573
- break;
14574
- case 2:
14575
- pixelRatio = [12, 11];
14576
- break;
14577
- case 3:
14578
- pixelRatio = [10, 11];
14579
- break;
14580
- case 4:
14581
- pixelRatio = [16, 11];
14582
- break;
14583
- case 5:
14584
- pixelRatio = [40, 33];
14585
- break;
14586
- case 6:
14587
- pixelRatio = [24, 11];
14588
- break;
14589
- case 7:
14590
- pixelRatio = [20, 11];
14591
- break;
14592
- case 8:
14593
- pixelRatio = [32, 11];
14594
- break;
14595
- case 9:
14596
- pixelRatio = [80, 33];
14597
- break;
14598
- case 10:
14599
- pixelRatio = [18, 11];
14600
- break;
14601
- case 11:
14602
- pixelRatio = [15, 11];
14603
- break;
14604
- case 12:
14605
- pixelRatio = [64, 33];
14606
- break;
14607
- case 13:
14608
- pixelRatio = [160, 99];
14609
- break;
14610
- case 14:
14611
- pixelRatio = [4, 3];
14612
- break;
14613
- case 15:
14614
- pixelRatio = [3, 2];
14615
- break;
14616
- case 16:
14617
- pixelRatio = [2, 1];
14618
- break;
14619
- case 255:
14620
- {
14621
- pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
14622
- break;
14623
- }
14624
- }
14625
- }
14626
- }
14627
- return {
14628
- width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
14629
- height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
14630
- pixelRatio: pixelRatio
14631
- };
14632
- };
14633
- _proto.readSliceType = function readSliceType() {
14634
- // skip NALu type
14635
- this.readUByte();
14636
- // discard first_mb_in_slice
14637
- this.readUEG();
14638
- // return slice_type
14639
- return this.readUEG();
14640
14835
  };
14641
14836
  return ExpGolomb;
14642
14837
  }();
@@ -14647,9 +14842,9 @@
14647
14842
  return _BaseVideoParser.apply(this, arguments) || this;
14648
14843
  }
14649
14844
  var _proto = AvcVideoParser.prototype;
14650
- _proto.parseAVCPES = function parseAVCPES(track, textTrack, pes, last, duration) {
14845
+ _proto.parsePES = function parsePES(track, textTrack, pes, last, duration) {
14651
14846
  var _this = this;
14652
- var units = this.parseAVCNALu(track, pes.data);
14847
+ var units = this.parseNALu(track, pes.data);
14653
14848
  var VideoSample = this.VideoSample;
14654
14849
  var push;
14655
14850
  var spsfound = false;
@@ -14674,7 +14869,7 @@
14674
14869
  // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
14675
14870
  if (spsfound && data.length > 4) {
14676
14871
  // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
14677
- var sliceType = new ExpGolomb(data).readSliceType();
14872
+ var sliceType = _this.readSliceType(data);
14678
14873
  // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
14679
14874
  // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
14680
14875
  // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
@@ -14728,8 +14923,7 @@
14728
14923
  push = true;
14729
14924
  spsfound = true;
14730
14925
  var sps = unit.data;
14731
- var expGolombDecoder = new ExpGolomb(sps);
14732
- var config = expGolombDecoder.readSPS();
14926
+ var config = _this.readSPS(sps);
14733
14927
  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]) {
14734
14928
  track.width = config.width;
14735
14929
  track.height = config.height;
@@ -14785,109 +14979,192 @@
14785
14979
  this.VideoSample = null;
14786
14980
  }
14787
14981
  };
14788
- _proto.parseAVCNALu = function parseAVCNALu(track, array) {
14789
- var len = array.byteLength;
14790
- var state = track.naluState || 0;
14791
- var lastState = state;
14792
- var units = [];
14793
- var i = 0;
14794
- var value;
14795
- var overflow;
14796
- var unitType;
14797
- var lastUnitStart = -1;
14798
- var lastUnitType = 0;
14799
- // logger.log('PES:' + Hex.hexDump(array));
14982
+ _proto.getNALuType = function getNALuType(data, offset) {
14983
+ return data[offset] & 0x1f;
14984
+ };
14985
+ _proto.readSliceType = function readSliceType(data) {
14986
+ var eg = new ExpGolomb(data);
14987
+ // skip NALu type
14988
+ eg.readUByte();
14989
+ // discard first_mb_in_slice
14990
+ eg.readUEG();
14991
+ // return slice_type
14992
+ return eg.readUEG();
14993
+ }
14800
14994
 
14801
- if (state === -1) {
14802
- // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14803
- lastUnitStart = 0;
14804
- // NALu type is value read from offset 0
14805
- lastUnitType = array[0] & 0x1f;
14806
- state = 0;
14807
- i = 1;
14808
- }
14809
- while (i < len) {
14810
- value = array[i++];
14811
- // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14812
- if (!state) {
14813
- state = value ? 0 : 1;
14814
- continue;
14815
- }
14816
- if (state === 1) {
14817
- state = value ? 0 : 2;
14818
- continue;
14995
+ /**
14996
+ * The scaling list is optionally transmitted as part of a sequence parameter
14997
+ * set and is not relevant to transmuxing.
14998
+ * @param count the number of entries in this scaling list
14999
+ * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
15000
+ */;
15001
+ _proto.skipScalingList = function skipScalingList(count, reader) {
15002
+ var lastScale = 8;
15003
+ var nextScale = 8;
15004
+ var deltaScale;
15005
+ for (var j = 0; j < count; j++) {
15006
+ if (nextScale !== 0) {
15007
+ deltaScale = reader.readEG();
15008
+ nextScale = (lastScale + deltaScale + 256) % 256;
14819
15009
  }
14820
- // here we have state either equal to 2 or 3
14821
- if (!value) {
14822
- state = 3;
14823
- } else if (value === 1) {
14824
- overflow = i - state - 1;
14825
- if (lastUnitStart >= 0) {
14826
- var unit = {
14827
- data: array.subarray(lastUnitStart, overflow),
14828
- type: lastUnitType
14829
- };
14830
- // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14831
- units.push(unit);
14832
- } else {
14833
- // lastUnitStart is undefined => this is the first start code found in this PES packet
14834
- // first check if start code delimiter is overlapping between 2 PES packets,
14835
- // ie it started in last packet (lastState not zero)
14836
- // and ended at the beginning of this PES packet (i <= 4 - lastState)
14837
- var lastUnit = this.getLastNalUnit(track.samples);
14838
- if (lastUnit) {
14839
- if (lastState && i <= 4 - lastState) {
14840
- // start delimiter overlapping between PES packets
14841
- // strip start delimiter bytes from the end of last NAL unit
14842
- // check if lastUnit had a state different from zero
14843
- if (lastUnit.state) {
14844
- // strip last bytes
14845
- lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14846
- }
14847
- }
14848
- // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
15010
+ lastScale = nextScale === 0 ? lastScale : nextScale;
15011
+ }
15012
+ }
14849
15013
 
14850
- if (overflow > 0) {
14851
- // logger.log('first NALU found with overflow:' + overflow);
14852
- lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14853
- lastUnit.state = 0;
15014
+ /**
15015
+ * Read a sequence parameter set and return some interesting video
15016
+ * properties. A sequence parameter set is the H264 metadata that
15017
+ * describes the properties of upcoming video frames.
15018
+ * @returns an object with configuration parsed from the
15019
+ * sequence parameter set, including the dimensions of the
15020
+ * associated video frames.
15021
+ */;
15022
+ _proto.readSPS = function readSPS(sps) {
15023
+ var eg = new ExpGolomb(sps);
15024
+ var frameCropLeftOffset = 0;
15025
+ var frameCropRightOffset = 0;
15026
+ var frameCropTopOffset = 0;
15027
+ var frameCropBottomOffset = 0;
15028
+ var numRefFramesInPicOrderCntCycle;
15029
+ var scalingListCount;
15030
+ var i;
15031
+ var readUByte = eg.readUByte.bind(eg);
15032
+ var readBits = eg.readBits.bind(eg);
15033
+ var readUEG = eg.readUEG.bind(eg);
15034
+ var readBoolean = eg.readBoolean.bind(eg);
15035
+ var skipBits = eg.skipBits.bind(eg);
15036
+ var skipEG = eg.skipEG.bind(eg);
15037
+ var skipUEG = eg.skipUEG.bind(eg);
15038
+ var skipScalingList = this.skipScalingList.bind(this);
15039
+ readUByte();
15040
+ var profileIdc = readUByte(); // profile_idc
15041
+ readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
15042
+ skipBits(3); // reserved_zero_3bits u(3),
15043
+ readUByte(); // level_idc u(8)
15044
+ skipUEG(); // seq_parameter_set_id
15045
+ // some profiles have more optional data we don't need
15046
+ if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
15047
+ var chromaFormatIdc = readUEG();
15048
+ if (chromaFormatIdc === 3) {
15049
+ skipBits(1);
15050
+ } // separate_colour_plane_flag
15051
+
15052
+ skipUEG(); // bit_depth_luma_minus8
15053
+ skipUEG(); // bit_depth_chroma_minus8
15054
+ skipBits(1); // qpprime_y_zero_transform_bypass_flag
15055
+ if (readBoolean()) {
15056
+ // seq_scaling_matrix_present_flag
15057
+ scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
15058
+ for (i = 0; i < scalingListCount; i++) {
15059
+ if (readBoolean()) {
15060
+ // seq_scaling_list_present_flag[ i ]
15061
+ if (i < 6) {
15062
+ skipScalingList(16, eg);
15063
+ } else {
15064
+ skipScalingList(64, eg);
14854
15065
  }
14855
15066
  }
14856
15067
  }
14857
- // check if we can read unit type
14858
- if (i < len) {
14859
- unitType = array[i] & 0x1f;
14860
- // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14861
- lastUnitStart = i;
14862
- lastUnitType = unitType;
14863
- state = 0;
14864
- } else {
14865
- // not enough byte to read unit type. let's read it on next PES parsing
14866
- state = -1;
14867
- }
14868
- } else {
14869
- state = 0;
14870
15068
  }
14871
15069
  }
14872
- if (lastUnitStart >= 0 && state >= 0) {
14873
- var _unit = {
14874
- data: array.subarray(lastUnitStart, len),
14875
- type: lastUnitType,
14876
- state: state
14877
- };
14878
- units.push(_unit);
14879
- // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
15070
+ skipUEG(); // log2_max_frame_num_minus4
15071
+ var picOrderCntType = readUEG();
15072
+ if (picOrderCntType === 0) {
15073
+ readUEG(); // log2_max_pic_order_cnt_lsb_minus4
15074
+ } else if (picOrderCntType === 1) {
15075
+ skipBits(1); // delta_pic_order_always_zero_flag
15076
+ skipEG(); // offset_for_non_ref_pic
15077
+ skipEG(); // offset_for_top_to_bottom_field
15078
+ numRefFramesInPicOrderCntCycle = readUEG();
15079
+ for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
15080
+ skipEG();
15081
+ } // offset_for_ref_frame[ i ]
15082
+ }
15083
+ skipUEG(); // max_num_ref_frames
15084
+ skipBits(1); // gaps_in_frame_num_value_allowed_flag
15085
+ var picWidthInMbsMinus1 = readUEG();
15086
+ var picHeightInMapUnitsMinus1 = readUEG();
15087
+ var frameMbsOnlyFlag = readBits(1);
15088
+ if (frameMbsOnlyFlag === 0) {
15089
+ skipBits(1);
15090
+ } // mb_adaptive_frame_field_flag
15091
+
15092
+ skipBits(1); // direct_8x8_inference_flag
15093
+ if (readBoolean()) {
15094
+ // frame_cropping_flag
15095
+ frameCropLeftOffset = readUEG();
15096
+ frameCropRightOffset = readUEG();
15097
+ frameCropTopOffset = readUEG();
15098
+ frameCropBottomOffset = readUEG();
14880
15099
  }
14881
- // no NALu found
14882
- if (units.length === 0) {
14883
- // append pes.data to previous NAL unit
14884
- var _lastUnit = this.getLastNalUnit(track.samples);
14885
- if (_lastUnit) {
14886
- _lastUnit.data = appendUint8Array(_lastUnit.data, array);
15100
+ var pixelRatio = [1, 1];
15101
+ if (readBoolean()) {
15102
+ // vui_parameters_present_flag
15103
+ if (readBoolean()) {
15104
+ // aspect_ratio_info_present_flag
15105
+ var aspectRatioIdc = readUByte();
15106
+ switch (aspectRatioIdc) {
15107
+ case 1:
15108
+ pixelRatio = [1, 1];
15109
+ break;
15110
+ case 2:
15111
+ pixelRatio = [12, 11];
15112
+ break;
15113
+ case 3:
15114
+ pixelRatio = [10, 11];
15115
+ break;
15116
+ case 4:
15117
+ pixelRatio = [16, 11];
15118
+ break;
15119
+ case 5:
15120
+ pixelRatio = [40, 33];
15121
+ break;
15122
+ case 6:
15123
+ pixelRatio = [24, 11];
15124
+ break;
15125
+ case 7:
15126
+ pixelRatio = [20, 11];
15127
+ break;
15128
+ case 8:
15129
+ pixelRatio = [32, 11];
15130
+ break;
15131
+ case 9:
15132
+ pixelRatio = [80, 33];
15133
+ break;
15134
+ case 10:
15135
+ pixelRatio = [18, 11];
15136
+ break;
15137
+ case 11:
15138
+ pixelRatio = [15, 11];
15139
+ break;
15140
+ case 12:
15141
+ pixelRatio = [64, 33];
15142
+ break;
15143
+ case 13:
15144
+ pixelRatio = [160, 99];
15145
+ break;
15146
+ case 14:
15147
+ pixelRatio = [4, 3];
15148
+ break;
15149
+ case 15:
15150
+ pixelRatio = [3, 2];
15151
+ break;
15152
+ case 16:
15153
+ pixelRatio = [2, 1];
15154
+ break;
15155
+ case 255:
15156
+ {
15157
+ pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
15158
+ break;
15159
+ }
15160
+ }
14887
15161
  }
14888
15162
  }
14889
- track.naluState = state;
14890
- return units;
15163
+ return {
15164
+ width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
15165
+ height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
15166
+ pixelRatio: pixelRatio
15167
+ };
14891
15168
  };
14892
15169
  return AvcVideoParser;
14893
15170
  }(BaseVideoParser);
@@ -14907,7 +15184,7 @@
14907
15184
  }
14908
15185
  var _proto = SampleAesDecrypter.prototype;
14909
15186
  _proto.decryptBuffer = function decryptBuffer(encryptedData) {
14910
- return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer);
15187
+ return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc);
14911
15188
  }
14912
15189
 
14913
15190
  // AAC - encrypt all full 16 bytes blocks starting from offset 16
@@ -15026,7 +15303,7 @@
15026
15303
  this.observer = observer;
15027
15304
  this.config = config;
15028
15305
  this.typeSupported = typeSupported;
15029
- this.videoParser = new AvcVideoParser();
15306
+ this.videoParser = null;
15030
15307
  }
15031
15308
  TSDemuxer.probe = function probe(data) {
15032
15309
  var syncOffset = TSDemuxer.syncOffset(data);
@@ -15196,7 +15473,16 @@
15196
15473
  case videoPid:
15197
15474
  if (stt) {
15198
15475
  if (videoData && (pes = parsePES(videoData))) {
15199
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
15476
+ if (this.videoParser === null) {
15477
+ switch (videoTrack.segmentCodec) {
15478
+ case 'avc':
15479
+ this.videoParser = new AvcVideoParser();
15480
+ break;
15481
+ }
15482
+ }
15483
+ if (this.videoParser !== null) {
15484
+ this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
15485
+ }
15200
15486
  }
15201
15487
  videoData = {
15202
15488
  data: [],
@@ -15354,8 +15640,17 @@
15354
15640
  // try to parse last PES packets
15355
15641
  var pes;
15356
15642
  if (videoData && (pes = parsePES(videoData))) {
15357
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
15358
- videoTrack.pesData = null;
15643
+ if (this.videoParser === null) {
15644
+ switch (videoTrack.segmentCodec) {
15645
+ case 'avc':
15646
+ this.videoParser = new AvcVideoParser();
15647
+ break;
15648
+ }
15649
+ }
15650
+ if (this.videoParser !== null) {
15651
+ this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
15652
+ videoTrack.pesData = null;
15653
+ }
15359
15654
  } else {
15360
15655
  // either avcData null or PES truncated, keep it for next frag parsing
15361
15656
  videoTrack.pesData = videoData;
@@ -15657,7 +15952,10 @@
15657
15952
  logger.warn('Unsupported EC-3 in M2TS found');
15658
15953
  break;
15659
15954
  case 0x24:
15660
- logger.warn('Unsupported HEVC in M2TS found');
15955
+ // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
15956
+ {
15957
+ logger.warn('Unsupported HEVC in M2TS found');
15958
+ }
15661
15959
  break;
15662
15960
  }
15663
15961
  // move to the next table entry
@@ -15885,6 +16183,8 @@
15885
16183
  avc1: [],
15886
16184
  // codingname
15887
16185
  avcC: [],
16186
+ hvc1: [],
16187
+ hvcC: [],
15888
16188
  btrt: [],
15889
16189
  dinf: [],
15890
16190
  dref: [],
@@ -16312,8 +16612,10 @@
16312
16612
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
16313
16613
  }
16314
16614
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
16315
- } else {
16615
+ } else if (track.segmentCodec === 'avc') {
16316
16616
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
16617
+ } else {
16618
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
16317
16619
  }
16318
16620
  };
16319
16621
  MP4.tkhd = function tkhd(track) {
@@ -16451,6 +16753,84 @@
16451
16753
  var result = appendUint8Array(MP4.FTYP, movie);
16452
16754
  return result;
16453
16755
  };
16756
+ MP4.hvc1 = function hvc1(track) {
16757
+ var ps = track.params;
16758
+ var units = [track.vps, track.sps, track.pps];
16759
+ var NALuLengthSize = 4;
16760
+ 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]);
16761
+
16762
+ // compute hvcC size in bytes
16763
+ var length = config.length;
16764
+ for (var i = 0; i < units.length; i += 1) {
16765
+ length += 3;
16766
+ for (var j = 0; j < units[i].length; j += 1) {
16767
+ length += 2 + units[i][j].length;
16768
+ }
16769
+ }
16770
+ var hvcC = new Uint8Array(length);
16771
+ hvcC.set(config, 0);
16772
+ length = config.length;
16773
+ // append parameter set units: one vps, one or more sps and pps
16774
+ var iMax = units.length - 1;
16775
+ for (var _i = 0; _i < units.length; _i += 1) {
16776
+ hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length);
16777
+ length += 3;
16778
+ for (var _j = 0; _j < units[_i].length; _j += 1) {
16779
+ hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length);
16780
+ length += 2;
16781
+ hvcC.set(units[_i][_j], length);
16782
+ length += units[_i][_j].length;
16783
+ }
16784
+ }
16785
+ var hvcc = MP4.box(MP4.types.hvcC, hvcC);
16786
+ var width = track.width;
16787
+ var height = track.height;
16788
+ var hSpacing = track.pixelRatio[0];
16789
+ var vSpacing = track.pixelRatio[1];
16790
+ return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
16791
+ // reserved
16792
+ 0x00, 0x00, 0x00,
16793
+ // reserved
16794
+ 0x00, 0x01,
16795
+ // data_reference_index
16796
+ 0x00, 0x00,
16797
+ // pre_defined
16798
+ 0x00, 0x00,
16799
+ // reserved
16800
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16801
+ // pre_defined
16802
+ width >> 8 & 0xff, width & 0xff,
16803
+ // width
16804
+ height >> 8 & 0xff, height & 0xff,
16805
+ // height
16806
+ 0x00, 0x48, 0x00, 0x00,
16807
+ // horizresolution
16808
+ 0x00, 0x48, 0x00, 0x00,
16809
+ // vertresolution
16810
+ 0x00, 0x00, 0x00, 0x00,
16811
+ // reserved
16812
+ 0x00, 0x01,
16813
+ // frame_count
16814
+ 0x12, 0x64, 0x61, 0x69, 0x6c,
16815
+ // dailymotion/hls.js
16816
+ 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,
16817
+ // compressorname
16818
+ 0x00, 0x18,
16819
+ // depth = 24
16820
+ 0x11, 0x11]),
16821
+ // pre_defined = -1
16822
+ hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
16823
+ // bufferSizeDB
16824
+ 0x00, 0x2d, 0xc6, 0xc0,
16825
+ // maxBitrate
16826
+ 0x00, 0x2d, 0xc6, 0xc0])),
16827
+ // avgBitrate
16828
+ MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
16829
+ // hSpacing
16830
+ hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
16831
+ // vSpacing
16832
+ vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
16833
+ };
16454
16834
  return MP4;
16455
16835
  }();
16456
16836
  MP4.types = void 0;
@@ -16837,9 +17217,9 @@
16837
17217
  var foundOverlap = delta < -1;
16838
17218
  if (foundHole || foundOverlap) {
16839
17219
  if (foundHole) {
16840
- logger.warn("AVC: " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
17220
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3));
16841
17221
  } else {
16842
- logger.warn("AVC: " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
17222
+ logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3));
16843
17223
  }
16844
17224
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
16845
17225
  firstDTS = nextAvcDts;
@@ -16848,12 +17228,24 @@
16848
17228
  inputSamples[0].dts = firstDTS;
16849
17229
  inputSamples[0].pts = firstPTS;
16850
17230
  } else {
17231
+ var isPTSOrderRetained = true;
16851
17232
  for (var _i = 0; _i < inputSamples.length; _i++) {
16852
- if (inputSamples[_i].dts > firstPTS) {
17233
+ if (inputSamples[_i].dts > firstPTS && isPTSOrderRetained) {
16853
17234
  break;
16854
17235
  }
17236
+ var prevPTS = inputSamples[_i].pts;
16855
17237
  inputSamples[_i].dts -= delta;
16856
17238
  inputSamples[_i].pts -= delta;
17239
+
17240
+ // check to see if this sample's PTS order has changed
17241
+ // relative to the next one
17242
+ if (_i < inputSamples.length - 1) {
17243
+ var nextSamplePTS = inputSamples[_i + 1].pts;
17244
+ var currentSamplePTS = inputSamples[_i].pts;
17245
+ var currentOrder = nextSamplePTS <= currentSamplePTS;
17246
+ var prevOrder = nextSamplePTS <= prevPTS;
17247
+ isPTSOrderRetained = currentOrder == prevOrder;
17248
+ }
16857
17249
  }
16858
17250
  }
16859
17251
  logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms");
@@ -17001,7 +17393,7 @@
17001
17393
  }
17002
17394
  }
17003
17395
  }
17004
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
17396
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
17005
17397
  mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
17006
17398
  this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
17007
17399
  this.videoSampleDuration = mp4SampleDuration;
@@ -17136,7 +17528,7 @@
17136
17528
  logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap.");
17137
17529
  for (var j = 0; j < missing; j++) {
17138
17530
  var newStamp = Math.max(nextPts, 0);
17139
- var fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17531
+ var fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17140
17532
  if (!fillFrame) {
17141
17533
  logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
17142
17534
  fillFrame = sample.unit.subarray();
@@ -17264,7 +17656,7 @@
17264
17656
  // samples count of this segment's duration
17265
17657
  var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration);
17266
17658
  // silent frame
17267
- var silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
17659
+ var silentFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
17268
17660
  logger.warn('[mp4-remuxer]: remux empty Audio');
17269
17661
  // Can't remux if we can't generate a silent frame...
17270
17662
  if (!silentFrame) {
@@ -17651,13 +18043,15 @@
17651
18043
  duration = transmuxConfig.duration,
17652
18044
  initSegmentData = transmuxConfig.initSegmentData;
17653
18045
  var keyData = getEncryptionType(uintData, decryptdata);
17654
- if (keyData && keyData.method === 'AES-128') {
18046
+ if (keyData && isFullSegmentEncryption(keyData.method)) {
17655
18047
  var decrypter = this.getDecrypter();
18048
+ var aesMode = getAesModeFromFullSegmentMethod(keyData.method);
18049
+
17656
18050
  // Software decryption is synchronous; webCrypto is not
17657
18051
  if (decrypter.isSync()) {
17658
18052
  // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
17659
18053
  // data is handled in the flush() call
17660
- var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer);
18054
+ var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode);
17661
18055
  // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
17662
18056
  var loadingParts = chunkMeta.part > -1;
17663
18057
  if (loadingParts) {
@@ -17669,7 +18063,7 @@
17669
18063
  }
17670
18064
  uintData = new Uint8Array(decryptedData);
17671
18065
  } else {
17672
- this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(function (decryptedData) {
18066
+ this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(function (decryptedData) {
17673
18067
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
17674
18068
  // the decrypted data has been transmuxed
17675
18069
  var result = _this.push(decryptedData, null, chunkMeta);
@@ -18290,7 +18684,7 @@
18290
18684
  observer.on(Events.ERROR, forwardMessage);
18291
18685
 
18292
18686
  // forward logger events to main thread
18293
- var forwardWorkerLogs = function forwardWorkerLogs() {
18687
+ var forwardWorkerLogs = function forwardWorkerLogs(logger) {
18294
18688
  var _loop = function _loop(logFn) {
18295
18689
  var func = function func(message) {
18296
18690
  forwardMessage('workerLog', {
@@ -18311,8 +18705,8 @@
18311
18705
  {
18312
18706
  var config = JSON.parse(data.config);
18313
18707
  self.transmuxer = new Transmuxer(observer, data.typeSupported, config, data.vendor, data.id);
18314
- enableLogs(config.debug, data.id);
18315
- forwardWorkerLogs();
18708
+ var logger = enableLogs(config.debug, data.id);
18709
+ forwardWorkerLogs(logger);
18316
18710
  forwardMessage('init', null);
18317
18711
  break;
18318
18712
  }
@@ -18486,16 +18880,7 @@
18486
18880
  this.observer = new EventEmitter();
18487
18881
  this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
18488
18882
  this.observer.on(Events.ERROR, forwardMessage);
18489
- var MediaSource = getMediaSource(config.preferManagedMediaSource) || {
18490
- isTypeSupported: function isTypeSupported() {
18491
- return false;
18492
- }
18493
- };
18494
- var m2tsTypeSupported = {
18495
- mpeg: MediaSource.isTypeSupported('audio/mpeg'),
18496
- mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
18497
- ac3: false
18498
- };
18883
+ var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
18499
18884
 
18500
18885
  // navigator.vendor is not always available in Web Worker
18501
18886
  // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
@@ -18752,21 +19137,26 @@
18752
19137
  var MAX_START_GAP_JUMP = 2.0;
18753
19138
  var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
18754
19139
  var SKIP_BUFFER_RANGE_START = 0.05;
18755
- var GapController = /*#__PURE__*/function () {
19140
+ var GapController = /*#__PURE__*/function (_Logger) {
19141
+ _inheritsLoose(GapController, _Logger);
18756
19142
  function GapController(config, media, fragmentTracker, hls) {
18757
- this.config = void 0;
18758
- this.media = null;
18759
- this.fragmentTracker = void 0;
18760
- this.hls = void 0;
18761
- this.nudgeRetry = 0;
18762
- this.stallReported = false;
18763
- this.stalled = null;
18764
- this.moved = false;
18765
- this.seeking = false;
18766
- this.config = config;
18767
- this.media = media;
18768
- this.fragmentTracker = fragmentTracker;
18769
- this.hls = hls;
19143
+ var _this;
19144
+ _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
19145
+ _this.config = void 0;
19146
+ _this.media = null;
19147
+ _this.fragmentTracker = void 0;
19148
+ _this.hls = void 0;
19149
+ _this.nudgeRetry = 0;
19150
+ _this.stallReported = false;
19151
+ _this.stalled = null;
19152
+ _this.moved = false;
19153
+ _this.seeking = false;
19154
+ _this.ended = 0;
19155
+ _this.config = config;
19156
+ _this.media = media;
19157
+ _this.fragmentTracker = fragmentTracker;
19158
+ _this.hls = hls;
19159
+ return _this;
18770
19160
  }
18771
19161
  var _proto = GapController.prototype;
18772
19162
  _proto.destroy = function destroy() {
@@ -18781,7 +19171,7 @@
18781
19171
  *
18782
19172
  * @param lastCurrentTime - Previously read playhead position
18783
19173
  */;
18784
- _proto.poll = function poll(lastCurrentTime, activeFrag) {
19174
+ _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
18785
19175
  var config = this.config,
18786
19176
  media = this.media,
18787
19177
  stalled = this.stalled;
@@ -18796,6 +19186,7 @@
18796
19186
 
18797
19187
  // The playhead is moving, no-op
18798
19188
  if (currentTime !== lastCurrentTime) {
19189
+ this.ended = 0;
18799
19190
  this.moved = true;
18800
19191
  if (!seeking) {
18801
19192
  this.nudgeRetry = 0;
@@ -18804,7 +19195,7 @@
18804
19195
  // The playhead is now moving, but was previously stalled
18805
19196
  if (this.stallReported) {
18806
19197
  var _stalledDuration = self.performance.now() - stalled;
18807
- logger.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
19198
+ this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
18808
19199
  this.stallReported = false;
18809
19200
  }
18810
19201
  this.stalled = null;
@@ -18840,7 +19231,6 @@
18840
19231
  // Skip start gaps if we haven't played, but the last poll detected the start of a stall
18841
19232
  // The addition poll gives the browser a chance to jump the gap for us
18842
19233
  if (!this.moved && this.stalled !== null) {
18843
- var _level$details;
18844
19234
  // There is no playable buffer (seeked, waiting for buffer)
18845
19235
  var isBuffered = bufferInfo.len > 0;
18846
19236
  if (!isBuffered && !nextStart) {
@@ -18852,9 +19242,8 @@
18852
19242
  // When joining a live stream with audio tracks, account for live playlist window sliding by allowing
18853
19243
  // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
18854
19244
  // that begins over 1 target duration after the video start position.
18855
- var level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null;
18856
- var isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live;
18857
- var maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP;
19245
+ var isLive = !!(levelDetails != null && levelDetails.live);
19246
+ var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
18858
19247
  var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
18859
19248
  if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
18860
19249
  if (!media.paused) {
@@ -18872,6 +19261,17 @@
18872
19261
  }
18873
19262
  var stalledDuration = tnow - stalled;
18874
19263
  if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
19264
+ // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
19265
+ if (state === State.ENDED && !(levelDetails && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
19266
+ if (stalledDuration < 1000 || this.ended) {
19267
+ return;
19268
+ }
19269
+ this.ended = currentTime;
19270
+ this.hls.trigger(Events.MEDIA_ENDED, {
19271
+ stalled: true
19272
+ });
19273
+ return;
19274
+ }
18875
19275
  // Report stalling after trying to fix
18876
19276
  this._reportStall(bufferInfo);
18877
19277
  if (!this.media) {
@@ -18913,7 +19313,7 @@
18913
19313
  // needs to cross some sort of threshold covering all source-buffers content
18914
19314
  // to start playing properly.
18915
19315
  if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
18916
- logger.warn('Trying to nudge playhead over buffer-hole');
19316
+ this.warn('Trying to nudge playhead over buffer-hole');
18917
19317
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
18918
19318
  // We only try to jump the hole if it's under the configured size
18919
19319
  // Reset stalled so to rearm watchdog timer
@@ -18935,7 +19335,7 @@
18935
19335
  // Report stalled error once
18936
19336
  this.stallReported = true;
18937
19337
  var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
18938
- logger.warn(error.message);
19338
+ this.warn(error.message);
18939
19339
  hls.trigger(Events.ERROR, {
18940
19340
  type: ErrorTypes.MEDIA_ERROR,
18941
19341
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -18999,7 +19399,7 @@
18999
19399
  }
19000
19400
  }
19001
19401
  var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
19002
- logger.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
19402
+ this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
19003
19403
  this.moved = true;
19004
19404
  this.stalled = null;
19005
19405
  media.currentTime = targetTime;
@@ -19038,7 +19438,7 @@
19038
19438
  var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
19039
19439
  // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
19040
19440
  var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
19041
- logger.warn(error.message);
19441
+ this.warn(error.message);
19042
19442
  media.currentTime = targetTime;
19043
19443
  hls.trigger(Events.ERROR, {
19044
19444
  type: ErrorTypes.MEDIA_ERROR,
@@ -19048,7 +19448,7 @@
19048
19448
  });
19049
19449
  } else {
19050
19450
  var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
19051
- logger.error(_error.message);
19451
+ this.error(_error.message);
19052
19452
  hls.trigger(Events.ERROR, {
19053
19453
  type: ErrorTypes.MEDIA_ERROR,
19054
19454
  details: ErrorDetails.BUFFER_STALLED_ERROR,
@@ -19058,14 +19458,14 @@
19058
19458
  }
19059
19459
  };
19060
19460
  return GapController;
19061
- }();
19461
+ }(Logger);
19062
19462
 
19063
19463
  var TICK_INTERVAL = 100; // how often to tick in ms
19064
19464
  var StreamController = /*#__PURE__*/function (_BaseStreamController) {
19065
19465
  _inheritsLoose(StreamController, _BaseStreamController);
19066
19466
  function StreamController(hls, fragmentTracker, keyLoader) {
19067
19467
  var _this;
19068
- _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN) || this;
19468
+ _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
19069
19469
  _this.audioCodecSwap = false;
19070
19470
  _this.gapController = null;
19071
19471
  _this.level = -1;
@@ -19073,27 +19473,43 @@
19073
19473
  _this.altAudio = false;
19074
19474
  _this.audioOnly = false;
19075
19475
  _this.fragPlaying = null;
19076
- _this.onvplaying = null;
19077
- _this.onvseeked = null;
19078
19476
  _this.fragLastKbps = 0;
19079
19477
  _this.couldBacktrack = false;
19080
19478
  _this.backtrackFragment = null;
19081
19479
  _this.audioCodecSwitch = false;
19082
19480
  _this.videoBuffer = null;
19083
- _this._registerListeners();
19481
+ _this.onMediaPlaying = function () {
19482
+ // tick to speed up FRAG_CHANGED triggering
19483
+ _this.tick();
19484
+ };
19485
+ _this.onMediaSeeked = function () {
19486
+ var media = _this.media;
19487
+ var currentTime = media ? media.currentTime : null;
19488
+ if (isFiniteNumber(currentTime)) {
19489
+ _this.log("Media seeked to " + currentTime.toFixed(3));
19490
+ }
19491
+
19492
+ // If seeked was issued before buffer was appended do not tick immediately
19493
+ var bufferInfo = _this.getMainFwdBufferInfo();
19494
+ if (bufferInfo === null || bufferInfo.len === 0) {
19495
+ _this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19496
+ return;
19497
+ }
19498
+
19499
+ // tick to speed up FRAG_CHANGED triggering
19500
+ _this.tick();
19501
+ };
19502
+ _this.registerListeners();
19084
19503
  return _this;
19085
19504
  }
19086
19505
  var _proto = StreamController.prototype;
19087
- _proto._registerListeners = function _registerListeners() {
19506
+ _proto.registerListeners = function registerListeners() {
19507
+ _BaseStreamController.prototype.registerListeners.call(this);
19088
19508
  var hls = this.hls;
19089
- hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19090
- hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19091
- hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19092
19509
  hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19093
19510
  hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
19094
19511
  hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19095
19512
  hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19096
- hls.on(Events.ERROR, this.onError, this);
19097
19513
  hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19098
19514
  hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19099
19515
  hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19101,15 +19517,12 @@
19101
19517
  hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
19102
19518
  hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19103
19519
  };
19104
- _proto._unregisterListeners = function _unregisterListeners() {
19520
+ _proto.unregisterListeners = function unregisterListeners() {
19521
+ _BaseStreamController.prototype.unregisterListeners.call(this);
19105
19522
  var hls = this.hls;
19106
- hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
19107
- hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
19108
- hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
19109
19523
  hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
19110
19524
  hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
19111
19525
  hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
19112
- hls.off(Events.ERROR, this.onError, this);
19113
19526
  hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
19114
19527
  hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
19115
19528
  hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
@@ -19118,7 +19531,9 @@
19118
19531
  hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
19119
19532
  };
19120
19533
  _proto.onHandlerDestroying = function onHandlerDestroying() {
19121
- this._unregisterListeners();
19534
+ // @ts-ignore
19535
+ this.onMediaPlaying = this.onMediaSeeked = null;
19536
+ this.unregisterListeners();
19122
19537
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
19123
19538
  };
19124
19539
  _proto.startLoad = function startLoad(startPosition) {
@@ -19210,6 +19625,9 @@
19210
19625
  this.checkFragmentChanged();
19211
19626
  };
19212
19627
  _proto.doTickIdle = function doTickIdle() {
19628
+ if (!this.buffering) {
19629
+ return;
19630
+ }
19213
19631
  var hls = this.hls,
19214
19632
  levelLastLoaded = this.levelLastLoaded,
19215
19633
  levels = this.levels,
@@ -19433,18 +19851,15 @@
19433
19851
  _proto.onMediaAttached = function onMediaAttached(event, data) {
19434
19852
  _BaseStreamController.prototype.onMediaAttached.call(this, event, data);
19435
19853
  var media = data.media;
19436
- this.onvplaying = this.onMediaPlaying.bind(this);
19437
- this.onvseeked = this.onMediaSeeked.bind(this);
19438
- media.addEventListener('playing', this.onvplaying);
19439
- media.addEventListener('seeked', this.onvseeked);
19854
+ media.addEventListener('playing', this.onMediaPlaying);
19855
+ media.addEventListener('seeked', this.onMediaSeeked);
19440
19856
  this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
19441
19857
  };
19442
19858
  _proto.onMediaDetaching = function onMediaDetaching() {
19443
19859
  var media = this.media;
19444
- if (media && this.onvplaying && this.onvseeked) {
19445
- media.removeEventListener('playing', this.onvplaying);
19446
- media.removeEventListener('seeked', this.onvseeked);
19447
- this.onvplaying = this.onvseeked = null;
19860
+ if (media) {
19861
+ media.removeEventListener('playing', this.onMediaPlaying);
19862
+ media.removeEventListener('seeked', this.onMediaSeeked);
19448
19863
  this.videoBuffer = null;
19449
19864
  }
19450
19865
  this.fragPlaying = null;
@@ -19454,27 +19869,6 @@
19454
19869
  }
19455
19870
  _BaseStreamController.prototype.onMediaDetaching.call(this);
19456
19871
  };
19457
- _proto.onMediaPlaying = function onMediaPlaying() {
19458
- // tick to speed up FRAG_CHANGED triggering
19459
- this.tick();
19460
- };
19461
- _proto.onMediaSeeked = function onMediaSeeked() {
19462
- var media = this.media;
19463
- var currentTime = media ? media.currentTime : null;
19464
- if (isFiniteNumber(currentTime)) {
19465
- this.log("Media seeked to " + currentTime.toFixed(3));
19466
- }
19467
-
19468
- // If seeked was issued before buffer was appended do not tick immediately
19469
- var bufferInfo = this.getMainFwdBufferInfo();
19470
- if (bufferInfo === null || bufferInfo.len === 0) {
19471
- this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")");
19472
- return;
19473
- }
19474
-
19475
- // tick to speed up FRAG_CHANGED triggering
19476
- this.tick();
19477
- };
19478
19872
  _proto.onManifestLoading = function onManifestLoading() {
19479
19873
  // reset buffer on manifest loading
19480
19874
  this.log('Trigger BUFFER_RESET');
@@ -19755,8 +20149,10 @@
19755
20149
  }
19756
20150
  if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) {
19757
20151
  // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
19758
- var activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
19759
- gapController.poll(this.lastCurrentTime, activeFrag);
20152
+ var state = this.state;
20153
+ var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
20154
+ var levelDetails = this.getLevelDetails();
20155
+ gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
19760
20156
  }
19761
20157
  this.lastCurrentTime = media.currentTime;
19762
20158
  };
@@ -20122,6 +20518,17 @@
20122
20518
  }
20123
20519
  };
20124
20520
  _createClass(StreamController, [{
20521
+ key: "maxBufferLength",
20522
+ get: function get() {
20523
+ var levels = this.levels,
20524
+ level = this.level;
20525
+ var levelInfo = levels == null ? void 0 : levels[level];
20526
+ if (!levelInfo) {
20527
+ return this.config.maxBufferLength;
20528
+ }
20529
+ return this.getMaxBufferLength(levelInfo.maxBitrate);
20530
+ }
20531
+ }, {
20125
20532
  key: "nextLevel",
20126
20533
  get: function get() {
20127
20534
  var frag = this.nextBufferedFrag;
@@ -20221,9 +20628,12 @@
20221
20628
  * The configuration object provided on player instantiation.
20222
20629
  */
20223
20630
  this.userConfig = void 0;
20631
+ /**
20632
+ * The logger functions used by this player instance, configured on player instantiation.
20633
+ */
20634
+ this.logger = void 0;
20224
20635
  this.coreComponents = void 0;
20225
20636
  this.networkControllers = void 0;
20226
- this.started = false;
20227
20637
  this._emitter = new EventEmitter();
20228
20638
  this._autoLevelCapping = -1;
20229
20639
  this._maxHdcpLevel = null;
@@ -20240,11 +20650,11 @@
20240
20650
  this._media = null;
20241
20651
  this.url = null;
20242
20652
  this.triggeringException = void 0;
20243
- enableLogs(userConfig.debug || false, 'Hls instance');
20244
- var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig);
20653
+ var logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance');
20654
+ var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger);
20245
20655
  this.userConfig = userConfig;
20246
20656
  if (config.progressive) {
20247
- enableStreamingMode(config);
20657
+ enableStreamingMode(config, logger);
20248
20658
  }
20249
20659
 
20250
20660
  // core controllers and network loaders
@@ -20255,7 +20665,9 @@
20255
20665
  ConfigFpsController = config.fpsController;
20256
20666
  var errorController = new ConfigErrorController(this);
20257
20667
  var abrController = this.abrController = new ConfigAbrController(this);
20258
- var bufferController = this.bufferController = new ConfigBufferController(this);
20668
+ // FragmentTracker must be defined before StreamController because the order of event handling is important
20669
+ var fragmentTracker = new FragmentTracker(this);
20670
+ var bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker);
20259
20671
  var capLevelController = this.capLevelController = new ConfigCapLevelController(this);
20260
20672
  var fpsController = new ConfigFpsController(this);
20261
20673
  var playListLoader = new PlaylistLoader(this);
@@ -20264,8 +20676,6 @@
20264
20676
  // ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
20265
20677
  var contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;
20266
20678
  var levelController = this.levelController = new LevelController(this, contentSteering);
20267
- // FragmentTracker must be defined before StreamController because the order of event handling is important
20268
- var fragmentTracker = new FragmentTracker(this);
20269
20679
  var keyLoader = new KeyLoader(this.config);
20270
20680
  var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
20271
20681
 
@@ -20352,7 +20762,7 @@
20352
20762
  try {
20353
20763
  return this.emit(event, event, eventObject);
20354
20764
  } catch (error) {
20355
- logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20765
+ this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
20356
20766
  // Prevent recursion in error event handlers that throw #5497
20357
20767
  if (!this.triggeringException) {
20358
20768
  this.triggeringException = true;
@@ -20378,7 +20788,7 @@
20378
20788
  * Dispose of the instance
20379
20789
  */;
20380
20790
  _proto.destroy = function destroy() {
20381
- logger.log('destroy');
20791
+ this.logger.log('destroy');
20382
20792
  this.trigger(Events.DESTROYING, undefined);
20383
20793
  this.detachMedia();
20384
20794
  this.removeAllListeners();
@@ -20403,7 +20813,7 @@
20403
20813
  * Attaches Hls.js to a media element
20404
20814
  */;
20405
20815
  _proto.attachMedia = function attachMedia(media) {
20406
- logger.log('attachMedia');
20816
+ this.logger.log('attachMedia');
20407
20817
  this._media = media;
20408
20818
  this.trigger(Events.MEDIA_ATTACHING, {
20409
20819
  media: media
@@ -20414,7 +20824,7 @@
20414
20824
  * Detach Hls.js from the media
20415
20825
  */;
20416
20826
  _proto.detachMedia = function detachMedia() {
20417
- logger.log('detachMedia');
20827
+ this.logger.log('detachMedia');
20418
20828
  this.trigger(Events.MEDIA_DETACHING, undefined);
20419
20829
  this._media = null;
20420
20830
  }
@@ -20431,7 +20841,7 @@
20431
20841
  });
20432
20842
  this._autoLevelCapping = -1;
20433
20843
  this._maxHdcpLevel = null;
20434
- logger.log("loadSource:" + loadingSource);
20844
+ this.logger.log("loadSource:" + loadingSource);
20435
20845
  if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
20436
20846
  this.detachMedia();
20437
20847
  this.attachMedia(media);
@@ -20453,8 +20863,7 @@
20453
20863
  if (startPosition === void 0) {
20454
20864
  startPosition = -1;
20455
20865
  }
20456
- logger.log("startLoad(" + startPosition + ")");
20457
- this.started = true;
20866
+ this.logger.log("startLoad(" + startPosition + ")");
20458
20867
  this.networkControllers.forEach(function (controller) {
20459
20868
  controller.startLoad(startPosition);
20460
20869
  });
@@ -20464,34 +20873,31 @@
20464
20873
  * Stop loading of any stream data.
20465
20874
  */;
20466
20875
  _proto.stopLoad = function stopLoad() {
20467
- logger.log('stopLoad');
20468
- this.started = false;
20876
+ this.logger.log('stopLoad');
20469
20877
  this.networkControllers.forEach(function (controller) {
20470
20878
  controller.stopLoad();
20471
20879
  });
20472
20880
  }
20473
20881
 
20474
20882
  /**
20475
- * Resumes stream controller segment loading if previously started.
20883
+ * Resumes stream controller segment loading after `pauseBuffering` has been called.
20476
20884
  */;
20477
20885
  _proto.resumeBuffering = function resumeBuffering() {
20478
- if (this.started) {
20479
- this.networkControllers.forEach(function (controller) {
20480
- if ('fragmentLoader' in controller) {
20481
- controller.startLoad(-1);
20482
- }
20483
- });
20484
- }
20886
+ this.networkControllers.forEach(function (controller) {
20887
+ if (controller.resumeBuffering) {
20888
+ controller.resumeBuffering();
20889
+ }
20890
+ });
20485
20891
  }
20486
20892
 
20487
20893
  /**
20488
- * Stops stream controller segment loading without changing 'started' state like stopLoad().
20894
+ * Prevents stream controller from loading new segments until `resumeBuffering` is called.
20489
20895
  * This allows for media buffering to be paused without interupting playlist loading.
20490
20896
  */;
20491
20897
  _proto.pauseBuffering = function pauseBuffering() {
20492
20898
  this.networkControllers.forEach(function (controller) {
20493
- if ('fragmentLoader' in controller) {
20494
- controller.stopLoad();
20899
+ if (controller.pauseBuffering) {
20900
+ controller.pauseBuffering();
20495
20901
  }
20496
20902
  });
20497
20903
  }
@@ -20500,7 +20906,7 @@
20500
20906
  * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
20501
20907
  */;
20502
20908
  _proto.swapAudioCodec = function swapAudioCodec() {
20503
- logger.log('swapAudioCodec');
20909
+ this.logger.log('swapAudioCodec');
20504
20910
  this.streamController.swapAudioCodec();
20505
20911
  }
20506
20912
 
@@ -20511,7 +20917,7 @@
20511
20917
  * Automatic recovery of media-errors by this process is configurable.
20512
20918
  */;
20513
20919
  _proto.recoverMediaError = function recoverMediaError() {
20514
- logger.log('recoverMediaError');
20920
+ this.logger.log('recoverMediaError');
20515
20921
  var media = this._media;
20516
20922
  this.detachMedia();
20517
20923
  if (media) {
@@ -20566,7 +20972,7 @@
20566
20972
  * 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.
20567
20973
  */,
20568
20974
  set: function set(newLevel) {
20569
- logger.log("set currentLevel:" + newLevel);
20975
+ this.logger.log("set currentLevel:" + newLevel);
20570
20976
  this.levelController.manualLevel = newLevel;
20571
20977
  this.streamController.immediateLevelSwitch();
20572
20978
  }
@@ -20587,7 +20993,7 @@
20587
20993
  * @param newLevel - Pass -1 for automatic level selection
20588
20994
  */,
20589
20995
  set: function set(newLevel) {
20590
- logger.log("set nextLevel:" + newLevel);
20996
+ this.logger.log("set nextLevel:" + newLevel);
20591
20997
  this.levelController.manualLevel = newLevel;
20592
20998
  this.streamController.nextLevelSwitch();
20593
20999
  }
@@ -20608,7 +21014,7 @@
20608
21014
  * @param newLevel - Pass -1 for automatic level selection
20609
21015
  */,
20610
21016
  set: function set(newLevel) {
20611
- logger.log("set loadLevel:" + newLevel);
21017
+ this.logger.log("set loadLevel:" + newLevel);
20612
21018
  this.levelController.manualLevel = newLevel;
20613
21019
  }
20614
21020
 
@@ -20643,7 +21049,7 @@
20643
21049
  * Sets "first-level", see getter.
20644
21050
  */,
20645
21051
  set: function set(newLevel) {
20646
- logger.log("set firstLevel:" + newLevel);
21052
+ this.logger.log("set firstLevel:" + newLevel);
20647
21053
  this.levelController.firstLevel = newLevel;
20648
21054
  }
20649
21055
 
@@ -20670,7 +21076,7 @@
20670
21076
  * (determined from download of first segment)
20671
21077
  */,
20672
21078
  set: function set(newLevel) {
20673
- logger.log("set startLevel:" + newLevel);
21079
+ this.logger.log("set startLevel:" + newLevel);
20674
21080
  // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
20675
21081
  if (newLevel !== -1) {
20676
21082
  newLevel = Math.max(newLevel, this.minAutoLevel);
@@ -20723,7 +21129,7 @@
20723
21129
  */
20724
21130
  function set(newLevel) {
20725
21131
  if (this._autoLevelCapping !== newLevel) {
20726
- logger.log("set autoLevelCapping:" + newLevel);
21132
+ this.logger.log("set autoLevelCapping:" + newLevel);
20727
21133
  this._autoLevelCapping = newLevel;
20728
21134
  this.levelController.checkMaxAutoUpdated();
20729
21135
  }
@@ -20866,6 +21272,11 @@
20866
21272
  get: function get() {
20867
21273
  return this.streamController.getMainFwdBufferInfo();
20868
21274
  }
21275
+ }, {
21276
+ key: "maxBufferLength",
21277
+ get: function get() {
21278
+ return this.streamController.maxBufferLength;
21279
+ }
20869
21280
  }, {
20870
21281
  key: "allAudioTracks",
20871
21282
  get: function get() {
@@ -21048,7 +21459,7 @@
21048
21459
  * Get the video-dev/hls.js package version.
21049
21460
  */
21050
21461
  function get() {
21051
- return "1.5.7";
21462
+ return "1.5.8-0.canary.10044";
21052
21463
  }
21053
21464
  }, {
21054
21465
  key: "Events",