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

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.
package/dist/hls.js CHANGED
@@ -644,7 +644,7 @@
644
644
  // Some browsers don't allow to use bind on console object anyway
645
645
  // fallback to default if needed
646
646
  try {
647
- newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.5.7-0.canary.10014");
647
+ newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.5.7-0.canary.10016");
648
648
  } catch (e) {
649
649
  /* log fn threw an exception. All logger methods are no-ops. */
650
650
  return createLogger();
@@ -2149,7 +2149,9 @@
2149
2149
  }
2150
2150
  function skipBERInteger(bytes, i) {
2151
2151
  var limit = i + 5;
2152
- while (bytes[i++] & 0x80 && i < limit) {}
2152
+ while (bytes[i++] & 0x80 && i < limit) {
2153
+ /* do nothing */
2154
+ }
2153
2155
  return i;
2154
2156
  }
2155
2157
  function toHex(x) {
@@ -7565,6 +7567,9 @@
7565
7567
  var fragCurrent = this.fragCurrent,
7566
7568
  partCurrent = this.partCurrent,
7567
7569
  hls = this.hls;
7570
+ if (hls.levels.length <= 1) {
7571
+ return hls.loadLevel;
7572
+ }
7568
7573
  var maxAutoLevel = hls.maxAutoLevel,
7569
7574
  config = hls.config,
7570
7575
  minAutoLevel = hls.minAutoLevel;
@@ -8033,11 +8038,14 @@
8033
8038
  * If not found any Fragment, return null
8034
8039
  */;
8035
8040
  _proto.getBufferedFrag = function getBufferedFrag(position, levelType) {
8041
+ return this.getFragAtPos(position, levelType, true);
8042
+ };
8043
+ _proto.getFragAtPos = function getFragAtPos(position, levelType, buffered) {
8036
8044
  var fragments = this.fragments;
8037
8045
  var keys = Object.keys(fragments);
8038
8046
  for (var i = keys.length; i--;) {
8039
8047
  var fragmentEntity = fragments[keys[i]];
8040
- if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {
8048
+ if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) {
8041
8049
  var frag = fragmentEntity.body;
8042
8050
  if (frag.start <= position && position <= frag.end) {
8043
8051
  return frag;
@@ -8287,10 +8295,10 @@
8287
8295
  };
8288
8296
  };
8289
8297
  _proto.onBufferAppended = function onBufferAppended(event, data) {
8290
- var _this3 = this;
8291
8298
  var frag = data.frag,
8292
8299
  part = data.part,
8293
- timeRanges = data.timeRanges;
8300
+ timeRanges = data.timeRanges,
8301
+ type = data.type;
8294
8302
  if (frag.sn === 'initSegment') {
8295
8303
  return;
8296
8304
  }
@@ -8304,10 +8312,8 @@
8304
8312
  }
8305
8313
  // Store the latest timeRanges loaded in the buffer
8306
8314
  this.timeRanges = timeRanges;
8307
- Object.keys(timeRanges).forEach(function (elementaryStream) {
8308
- var timeRange = timeRanges[elementaryStream];
8309
- _this3.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);
8310
- });
8315
+ var timeRange = timeRanges[type];
8316
+ this.detectEvictedFragments(type, timeRange, playlistType, part);
8311
8317
  };
8312
8318
  _proto.onFragBuffered = function onFragBuffered(event, data) {
8313
8319
  this.detectPartialFragments(data);
@@ -8321,12 +8327,12 @@
8321
8327
  return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);
8322
8328
  };
8323
8329
  _proto.removeFragmentsInRange = function removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) {
8324
- var _this4 = this;
8330
+ var _this3 = this;
8325
8331
  if (withGapOnly && !this.hasGaps) {
8326
8332
  return;
8327
8333
  }
8328
8334
  Object.keys(this.fragments).forEach(function (key) {
8329
- var fragmentEntity = _this4.fragments[key];
8335
+ var fragmentEntity = _this3.fragments[key];
8330
8336
  if (!fragmentEntity) {
8331
8337
  return;
8332
8338
  }
@@ -8335,7 +8341,7 @@
8335
8341
  return;
8336
8342
  }
8337
8343
  if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) {
8338
- _this4.removeFragment(frag);
8344
+ _this3.removeFragment(frag);
8339
8345
  }
8340
8346
  });
8341
8347
  };
@@ -10297,7 +10303,7 @@
10297
10303
  // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
10298
10304
  if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
10299
10305
  var bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
10300
- if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
10306
+ if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) {
10301
10307
  return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
10302
10308
  }
10303
10309
  }
@@ -17340,9 +17346,8 @@
17340
17346
  this.state = State.ENDED;
17341
17347
  return;
17342
17348
  }
17343
- var mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);
17344
17349
  var bufferLen = bufferInfo.len;
17345
- var maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len);
17350
+ var maxBufLen = hls.maxBufferLength;
17346
17351
  var fragments = trackDetails.fragments;
17347
17352
  var start = fragments[0].start;
17348
17353
  var targetBufferTime = this.flushing ? this.getLoadPosition() : bufferInfo.end;
@@ -17377,32 +17382,25 @@
17377
17382
  this.bufferFlushed = true;
17378
17383
  return;
17379
17384
  }
17380
-
17381
- // Buffer audio up to one target duration ahead of main buffer
17382
- var atBufferSyncLimit = mainBufferInfo && frag.start > mainBufferInfo.end + trackDetails.targetduration;
17383
- if (atBufferSyncLimit ||
17384
- // Or wait for main buffer after buffing some audio
17385
- !(mainBufferInfo != null && mainBufferInfo.len) && bufferInfo.len) {
17386
- // Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo
17387
- var mainFrag = this.getAppendedFrag(frag.start, PlaylistLevelType.MAIN);
17388
- if (mainFrag === null) {
17389
- return;
17390
- }
17391
- // Bridge gaps in main buffer
17392
- atGap || (atGap = !!mainFrag.gap || !!atBufferSyncLimit && mainBufferInfo.len === 0);
17393
- if (atBufferSyncLimit && !atGap || atGap && bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {
17394
- return;
17385
+ if (!trackDetails.live || targetBufferTime < this.hls.liveSyncPosition) {
17386
+ // Request audio segments up to one fragment ahead of main buffer
17387
+ var mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);
17388
+ var atBufferSyncLimit = !!mainBufferInfo && frag.start > mainBufferInfo.end + frag.duration;
17389
+ if (atBufferSyncLimit) {
17390
+ // Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo
17391
+ var mainFrag = this.fragmentTracker.getFragAtPos(frag.start, PlaylistLevelType.MAIN);
17392
+ if (mainFrag === null) {
17393
+ return;
17394
+ }
17395
+ // Bridge gaps in main buffer (also prevents loop loading at gaps)
17396
+ atGap || (atGap = !!mainFrag.gap || mainBufferInfo.len === 0);
17397
+ if (!atGap || bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {
17398
+ return;
17399
+ }
17395
17400
  }
17396
17401
  }
17397
17402
  this.loadFragment(frag, levelInfo, targetBufferTime);
17398
17403
  };
17399
- _proto.getMaxBufferLength = function getMaxBufferLength(mainBufferLength) {
17400
- var maxConfigBuffer = _BaseStreamController.prototype.getMaxBufferLength.call(this);
17401
- if (!mainBufferLength) {
17402
- return maxConfigBuffer;
17403
- }
17404
- return Math.min(Math.max(maxConfigBuffer, mainBufferLength), this.config.maxMaxBufferLength);
17405
- };
17406
17404
  _proto.onMediaDetaching = function onMediaDetaching() {
17407
17405
  this.videoBuffer = null;
17408
17406
  this.bufferFlushed = this.flushing = false;
@@ -18467,9 +18465,8 @@
18467
18465
  var bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime, config.maxBufferHole);
18468
18466
  var targetBufferTime = bufferedInfo.end,
18469
18467
  bufferLen = bufferedInfo.len;
18470
- var mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN);
18471
18468
  var trackDetails = track.details;
18472
- var maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + trackDetails.levelTargetDuration;
18469
+ var maxBufLen = this.hls.maxBufferLength + trackDetails.levelTargetDuration;
18473
18470
  if (bufferLen > maxBufLen) {
18474
18471
  return;
18475
18472
  }
@@ -18506,13 +18503,6 @@
18506
18503
  }
18507
18504
  }
18508
18505
  };
18509
- _proto.getMaxBufferLength = function getMaxBufferLength(mainBufferLength) {
18510
- var maxConfigBuffer = _BaseStreamController.prototype.getMaxBufferLength.call(this);
18511
- if (!mainBufferLength) {
18512
- return maxConfigBuffer;
18513
- }
18514
- return Math.max(maxConfigBuffer, mainBufferLength);
18515
- };
18516
18506
  _proto.loadFragment = function loadFragment(frag, level, targetBufferTime) {
18517
18507
  this.fragCurrent = frag;
18518
18508
  if (frag.sn === 'initSegment') {
@@ -19030,24 +19020,23 @@
19030
19020
  this.executeNext(type);
19031
19021
  }
19032
19022
  };
19033
- _proto.insertAbort = function insertAbort(operation, type) {
19034
- var queue = this.queues[type];
19035
- queue.unshift(operation);
19036
- this.executeNext(type);
19037
- };
19038
19023
  _proto.appendBlocker = function appendBlocker(type) {
19039
- var execute;
19040
- var promise = new Promise(function (resolve) {
19041
- execute = resolve;
19024
+ var _this = this;
19025
+ return new Promise(function (resolve) {
19026
+ var operation = {
19027
+ execute: resolve,
19028
+ onStart: function onStart() {},
19029
+ onComplete: function onComplete() {},
19030
+ onError: function onError() {}
19031
+ };
19032
+ _this.append(operation, type);
19042
19033
  });
19043
- var operation = {
19044
- execute: execute,
19045
- onStart: function onStart() {},
19046
- onComplete: function onComplete() {},
19047
- onError: function onError() {}
19048
- };
19049
- this.append(operation, type);
19050
- return promise;
19034
+ };
19035
+ _proto.unblockAudio = function unblockAudio(op) {
19036
+ var queue = this.queues.audio;
19037
+ if (queue[0] === op) {
19038
+ this.shiftAndExecuteNext('audio');
19039
+ }
19051
19040
  };
19052
19041
  _proto.executeNext = function executeNext(type) {
19053
19042
  var queue = this.queues[type];
@@ -19082,7 +19071,7 @@
19082
19071
  var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
19083
19072
  var BufferController = /*#__PURE__*/function (_Logger) {
19084
19073
  _inheritsLoose(BufferController, _Logger);
19085
- function BufferController(hls) {
19074
+ function BufferController(hls, fragmentTracker) {
19086
19075
  var _this;
19087
19076
  _this = _Logger.call(this, 'buffer-controller', hls.logger) || this;
19088
19077
  // The level details used to determine duration, target-duration and live
@@ -19094,6 +19083,7 @@
19094
19083
  // References to event listeners for each SourceBuffer, so that they can be referenced for event removal
19095
19084
  _this.listeners = void 0;
19096
19085
  _this.hls = void 0;
19086
+ _this.fragmentTracker = void 0;
19097
19087
  // The number of BUFFER_CODEC events received before any sourceBuffers are created
19098
19088
  _this.bufferCodecEventsExpected = 0;
19099
19089
  // The total number of BUFFER_CODEC events received
@@ -19104,6 +19094,10 @@
19104
19094
  _this.mediaSource = null;
19105
19095
  // Last MP3 audio chunk appended
19106
19096
  _this.lastMpegAudioChunk = null;
19097
+ // Audio fragment blocked from appending until corresponding video appends or context changes
19098
+ _this.blockedAudioAppend = null;
19099
+ // Keep track of video append position for unblocking audio
19100
+ _this.lastVideoAppendEnd = 0;
19107
19101
  _this.appendSource = void 0;
19108
19102
  // counters
19109
19103
  _this.appendErrors = {
@@ -19134,7 +19128,10 @@
19134
19128
  _this.log('Media source opened');
19135
19129
  if (media) {
19136
19130
  media.removeEventListener('emptied', _this._onMediaEmptied);
19137
- _this.updateMediaElementDuration();
19131
+ var durationAndRange = _this.getDurationAndRange();
19132
+ if (durationAndRange) {
19133
+ _this.updateMediaSource(durationAndRange);
19134
+ }
19138
19135
  _this.hls.trigger(Events.MEDIA_ATTACHED, {
19139
19136
  media: media,
19140
19137
  mediaSource: mediaSource
@@ -19161,6 +19158,7 @@
19161
19158
  }
19162
19159
  };
19163
19160
  _this.hls = hls;
19161
+ _this.fragmentTracker = fragmentTracker;
19164
19162
  _this.appendSource = hls.config.preferManagedMediaSource;
19165
19163
  _this._initSourceBuffer();
19166
19164
  _this.registerListeners();
@@ -19175,7 +19173,7 @@
19175
19173
  this.details = null;
19176
19174
  this.lastMpegAudioChunk = null;
19177
19175
  // @ts-ignore
19178
- this.hls = null;
19176
+ this.hls = this.fragmentTracker = null;
19179
19177
  // @ts-ignore
19180
19178
  this._onMediaSourceOpen = this._onMediaSourceClose = null;
19181
19179
  // @ts-ignore
@@ -19227,6 +19225,8 @@
19227
19225
  audiovideo: 0
19228
19226
  };
19229
19227
  this.lastMpegAudioChunk = null;
19228
+ this.blockedAudioAppend = null;
19229
+ this.lastVideoAppendEnd = 0;
19230
19230
  };
19231
19231
  _proto.onManifestLoading = function onManifestLoading() {
19232
19232
  this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
@@ -19364,9 +19364,10 @@
19364
19364
  var trackNames = Object.keys(data);
19365
19365
  trackNames.forEach(function (trackName) {
19366
19366
  if (sourceBufferCount) {
19367
+ var _track$buffer;
19367
19368
  // check if SourceBuffer codec needs to change
19368
19369
  var track = _this3.tracks[trackName];
19369
- if (track && typeof track.buffer.changeType === 'function') {
19370
+ if (track && typeof ((_track$buffer = track.buffer) == null ? void 0 : _track$buffer.changeType) === 'function') {
19370
19371
  var _trackCodec;
19371
19372
  var _data$trackName = data[trackName],
19372
19373
  id = _data$trackName.id,
@@ -19434,17 +19435,52 @@
19434
19435
  };
19435
19436
  operationQueue.append(operation, type, !!this.pendingTracks[type]);
19436
19437
  };
19438
+ _proto.blockAudio = function blockAudio(partOrFrag) {
19439
+ var _this$fragmentTracker,
19440
+ _this5 = this;
19441
+ var pStart = partOrFrag.start;
19442
+ var pTime = pStart + partOrFrag.duration * 0.05;
19443
+ var atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true;
19444
+ if (atGap) {
19445
+ return;
19446
+ }
19447
+ var op = {
19448
+ execute: function execute() {
19449
+ var _this5$fragmentTracke;
19450
+ 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) {
19451
+ _this5.blockedAudioAppend = null;
19452
+ _this5.operationQueue.shiftAndExecuteNext('audio');
19453
+ }
19454
+ },
19455
+ onStart: function onStart() {},
19456
+ onComplete: function onComplete() {},
19457
+ onError: function onError() {}
19458
+ };
19459
+ this.blockedAudioAppend = {
19460
+ op: op,
19461
+ frag: partOrFrag
19462
+ };
19463
+ this.operationQueue.append(op, 'audio', true);
19464
+ };
19465
+ _proto.unblockAudio = function unblockAudio() {
19466
+ var blockedAudioAppend = this.blockedAudioAppend;
19467
+ if (blockedAudioAppend) {
19468
+ this.blockedAudioAppend = null;
19469
+ this.operationQueue.unblockAudio(blockedAudioAppend.op);
19470
+ }
19471
+ };
19437
19472
  _proto.onBufferAppending = function onBufferAppending(event, eventData) {
19438
- var _this5 = this;
19439
- var hls = this.hls,
19440
- operationQueue = this.operationQueue,
19473
+ var _this6 = this;
19474
+ var operationQueue = this.operationQueue,
19441
19475
  tracks = this.tracks;
19442
19476
  var data = eventData.data,
19443
19477
  type = eventData.type,
19478
+ parent = eventData.parent,
19444
19479
  frag = eventData.frag,
19445
19480
  part = eventData.part,
19446
19481
  chunkMeta = eventData.chunkMeta;
19447
19482
  var chunkStats = chunkMeta.buffering[type];
19483
+ var sn = frag.sn;
19448
19484
  var bufferAppendingStart = self.performance.now();
19449
19485
  chunkStats.start = bufferAppendingStart;
19450
19486
  var fragBuffering = frag.stats.buffering;
@@ -19467,21 +19503,50 @@
19467
19503
  checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;
19468
19504
  this.lastMpegAudioChunk = chunkMeta;
19469
19505
  }
19470
- var fragStart = frag.start;
19506
+
19507
+ // Block audio append until overlapping video append
19508
+ var videoSb = this.sourceBuffer.video;
19509
+ if (videoSb && sn !== 'initSegment') {
19510
+ var partOrFrag = part || frag;
19511
+ var blockedAudioAppend = this.blockedAudioAppend;
19512
+ if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) {
19513
+ var pStart = partOrFrag.start;
19514
+ var pTime = pStart + partOrFrag.duration * 0.05;
19515
+ var vbuffered = videoSb.buffered;
19516
+ var vappending = this.operationQueue.current('video');
19517
+ if (!vbuffered.length && !vappending) {
19518
+ // wait for video before appending audio
19519
+ this.blockAudio(partOrFrag);
19520
+ } else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) {
19521
+ // audio is ahead of video
19522
+ this.blockAudio(partOrFrag);
19523
+ }
19524
+ } else if (type === 'video') {
19525
+ var videoAppendEnd = partOrFrag.end;
19526
+ if (blockedAudioAppend) {
19527
+ var audioStart = blockedAudioAppend.frag.start;
19528
+ if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) {
19529
+ this.unblockAudio();
19530
+ }
19531
+ }
19532
+ this.lastVideoAppendEnd = videoAppendEnd;
19533
+ }
19534
+ }
19535
+ var fragStart = (part || frag).start;
19471
19536
  var operation = {
19472
19537
  execute: function execute() {
19473
19538
  chunkStats.executeStart = self.performance.now();
19474
19539
  if (checkTimestampOffset) {
19475
- var sb = _this5.sourceBuffer[type];
19540
+ var sb = _this6.sourceBuffer[type];
19476
19541
  if (sb) {
19477
19542
  var delta = fragStart - sb.timestampOffset;
19478
19543
  if (Math.abs(delta) >= 0.1) {
19479
- _this5.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + frag.sn + ")");
19544
+ _this6.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + sn + ")");
19480
19545
  sb.timestampOffset = fragStart;
19481
19546
  }
19482
19547
  }
19483
19548
  }
19484
- _this5.appendExecutor(data, type);
19549
+ _this6.appendExecutor(data, type);
19485
19550
  },
19486
19551
  onStart: function onStart() {
19487
19552
  // logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`);
@@ -19496,19 +19561,19 @@
19496
19561
  if (partBuffering && partBuffering.first === 0) {
19497
19562
  partBuffering.first = end;
19498
19563
  }
19499
- var sourceBuffer = _this5.sourceBuffer;
19564
+ var sourceBuffer = _this6.sourceBuffer;
19500
19565
  var timeRanges = {};
19501
19566
  for (var _type in sourceBuffer) {
19502
19567
  timeRanges[_type] = BufferHelper.getBuffered(sourceBuffer[_type]);
19503
19568
  }
19504
- _this5.appendErrors[type] = 0;
19569
+ _this6.appendErrors[type] = 0;
19505
19570
  if (type === 'audio' || type === 'video') {
19506
- _this5.appendErrors.audiovideo = 0;
19571
+ _this6.appendErrors.audiovideo = 0;
19507
19572
  } else {
19508
- _this5.appendErrors.audio = 0;
19509
- _this5.appendErrors.video = 0;
19573
+ _this6.appendErrors.audio = 0;
19574
+ _this6.appendErrors.video = 0;
19510
19575
  }
19511
- _this5.hls.trigger(Events.BUFFER_APPENDED, {
19576
+ _this6.hls.trigger(Events.BUFFER_APPENDED, {
19512
19577
  type: type,
19513
19578
  frag: frag,
19514
19579
  part: part,
@@ -19536,51 +19601,57 @@
19536
19601
  // let's stop appending any segments, and report BUFFER_FULL_ERROR error
19537
19602
  event.details = ErrorDetails.BUFFER_FULL_ERROR;
19538
19603
  } else {
19539
- var appendErrorCount = ++_this5.appendErrors[type];
19604
+ var appendErrorCount = ++_this6.appendErrors[type];
19540
19605
  event.details = ErrorDetails.BUFFER_APPEND_ERROR;
19541
19606
  /* with UHD content, we could get loop of quota exceeded error until
19542
19607
  browser is able to evict some data from sourcebuffer. Retrying can help recover.
19543
19608
  */
19544
- _this5.warn("Failed " + appendErrorCount + "/" + hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer");
19545
- if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
19609
+ _this6.warn("Failed " + appendErrorCount + "/" + _this6.hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer");
19610
+ if (appendErrorCount >= _this6.hls.config.appendErrorMaxRetry) {
19546
19611
  event.fatal = true;
19547
19612
  }
19548
19613
  }
19549
- hls.trigger(Events.ERROR, event);
19614
+ _this6.hls.trigger(Events.ERROR, event);
19550
19615
  }
19551
19616
  };
19552
19617
  operationQueue.append(operation, type, !!this.pendingTracks[type]);
19553
19618
  };
19619
+ _proto.getFlushOp = function getFlushOp(type, start, end) {
19620
+ var _this7 = this;
19621
+ return {
19622
+ execute: function execute() {
19623
+ _this7.removeExecutor(type, start, end);
19624
+ },
19625
+ onStart: function onStart() {
19626
+ // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
19627
+ },
19628
+ onComplete: function onComplete() {
19629
+ // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
19630
+ _this7.hls.trigger(Events.BUFFER_FLUSHED, {
19631
+ type: type
19632
+ });
19633
+ },
19634
+ onError: function onError(error) {
19635
+ _this7.warn("Failed to remove from " + type + " SourceBuffer", error);
19636
+ }
19637
+ };
19638
+ };
19554
19639
  _proto.onBufferFlushing = function onBufferFlushing(event, data) {
19555
- var _this6 = this;
19640
+ var _this8 = this;
19556
19641
  var operationQueue = this.operationQueue;
19557
- var flushOperation = function flushOperation(type) {
19558
- return {
19559
- execute: _this6.removeExecutor.bind(_this6, type, data.startOffset, data.endOffset),
19560
- onStart: function onStart() {
19561
- // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
19562
- },
19563
- onComplete: function onComplete() {
19564
- // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
19565
- _this6.hls.trigger(Events.BUFFER_FLUSHED, {
19566
- type: type
19567
- });
19568
- },
19569
- onError: function onError(error) {
19570
- _this6.warn("Failed to remove from " + type + " SourceBuffer", error);
19571
- }
19572
- };
19573
- };
19574
- if (data.type) {
19575
- operationQueue.append(flushOperation(data.type), data.type);
19642
+ var type = data.type,
19643
+ startOffset = data.startOffset,
19644
+ endOffset = data.endOffset;
19645
+ if (type) {
19646
+ operationQueue.append(this.getFlushOp(type, startOffset, endOffset), type);
19576
19647
  } else {
19577
- this.getSourceBufferTypes().forEach(function (type) {
19578
- operationQueue.append(flushOperation(type), type);
19648
+ this.getSourceBufferTypes().forEach(function (sbType) {
19649
+ operationQueue.append(_this8.getFlushOp(sbType, startOffset, endOffset), sbType);
19579
19650
  });
19580
19651
  }
19581
19652
  };
19582
19653
  _proto.onFragParsed = function onFragParsed(event, data) {
19583
- var _this7 = this;
19654
+ var _this9 = this;
19584
19655
  var frag = data.frag,
19585
19656
  part = data.part;
19586
19657
  var buffersAppendedTo = [];
@@ -19602,7 +19673,7 @@
19602
19673
  part.stats.buffering.end = now;
19603
19674
  }
19604
19675
  var stats = part ? part.stats : frag.stats;
19605
- _this7.hls.trigger(Events.FRAG_BUFFERED, {
19676
+ _this9.hls.trigger(Events.FRAG_BUFFERED, {
19606
19677
  frag: frag,
19607
19678
  part: part,
19608
19679
  stats: stats,
@@ -19622,14 +19693,17 @@
19622
19693
  // an undefined data.type will mark all buffers as EOS.
19623
19694
  ;
19624
19695
  _proto.onBufferEos = function onBufferEos(event, data) {
19625
- var _this8 = this;
19696
+ var _this10 = this;
19697
+ if (data.type === 'video') {
19698
+ this.unblockAudio();
19699
+ }
19626
19700
  var ended = this.getSourceBufferTypes().reduce(function (acc, type) {
19627
- var sb = _this8.sourceBuffer[type];
19701
+ var sb = _this10.sourceBuffer[type];
19628
19702
  if (sb && (!data.type || data.type === type)) {
19629
19703
  sb.ending = true;
19630
19704
  if (!sb.ended) {
19631
19705
  sb.ended = true;
19632
- _this8.log(type + " sourceBuffer now EOS");
19706
+ _this10.log(type + " sourceBuffer now EOS");
19633
19707
  }
19634
19708
  }
19635
19709
  return acc && !!(!sb || sb.ended);
@@ -19637,35 +19711,42 @@
19637
19711
  if (ended) {
19638
19712
  this.log("Queueing mediaSource.endOfStream()");
19639
19713
  this.blockBuffers(function () {
19640
- _this8.getSourceBufferTypes().forEach(function (type) {
19641
- var sb = _this8.sourceBuffer[type];
19714
+ _this10.getSourceBufferTypes().forEach(function (type) {
19715
+ var sb = _this10.sourceBuffer[type];
19642
19716
  if (sb) {
19643
19717
  sb.ending = false;
19644
19718
  }
19645
19719
  });
19646
- var mediaSource = _this8.mediaSource;
19720
+ var mediaSource = _this10.mediaSource;
19647
19721
  if (!mediaSource || mediaSource.readyState !== 'open') {
19648
19722
  if (mediaSource) {
19649
- _this8.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState);
19723
+ _this10.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState);
19650
19724
  }
19651
19725
  return;
19652
19726
  }
19653
- _this8.log("Calling mediaSource.endOfStream()");
19727
+ _this10.log("Calling mediaSource.endOfStream()");
19654
19728
  // Allow this to throw and be caught by the enqueueing function
19655
19729
  mediaSource.endOfStream();
19656
19730
  });
19657
19731
  }
19658
19732
  };
19659
19733
  _proto.onLevelUpdated = function onLevelUpdated(event, _ref) {
19734
+ var _this11 = this;
19660
19735
  var details = _ref.details;
19661
19736
  if (!details.fragments.length) {
19662
19737
  return;
19663
19738
  }
19664
19739
  this.details = details;
19740
+ var durationAndRange = this.getDurationAndRange();
19741
+ if (!durationAndRange) {
19742
+ return;
19743
+ }
19665
19744
  if (this.getSourceBufferTypes().length) {
19666
- this.blockBuffers(this.updateMediaElementDuration.bind(this));
19745
+ this.blockBuffers(function () {
19746
+ return _this11.updateMediaSource(durationAndRange);
19747
+ });
19667
19748
  } else {
19668
- this.updateMediaElementDuration();
19749
+ this.updateMediaSource(durationAndRange);
19669
19750
  }
19670
19751
  };
19671
19752
  _proto.trimBuffers = function trimBuffers() {
@@ -19698,7 +19779,7 @@
19698
19779
  }
19699
19780
  };
19700
19781
  _proto.flushBackBuffer = function flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) {
19701
- var _this9 = this;
19782
+ var _this12 = this;
19702
19783
  var details = this.details,
19703
19784
  sourceBuffer = this.sourceBuffer;
19704
19785
  var sourceBufferTypes = this.getSourceBufferTypes();
@@ -19708,20 +19789,20 @@
19708
19789
  var buffered = BufferHelper.getBuffered(sb);
19709
19790
  // when target buffer start exceeds actual buffer start
19710
19791
  if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) {
19711
- _this9.hls.trigger(Events.BACK_BUFFER_REACHED, {
19792
+ _this12.hls.trigger(Events.BACK_BUFFER_REACHED, {
19712
19793
  bufferEnd: targetBackBufferPosition
19713
19794
  });
19714
19795
 
19715
19796
  // Support for deprecated event:
19716
19797
  if (details != null && details.live) {
19717
- _this9.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {
19798
+ _this12.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, {
19718
19799
  bufferEnd: targetBackBufferPosition
19719
19800
  });
19720
19801
  } else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) {
19721
- _this9.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state");
19802
+ _this12.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state");
19722
19803
  return;
19723
19804
  }
19724
- _this9.hls.trigger(Events.BUFFER_FLUSHING, {
19805
+ _this12.hls.trigger(Events.BUFFER_FLUSHING, {
19725
19806
  startOffset: 0,
19726
19807
  endOffset: targetBackBufferPosition,
19727
19808
  type: type
@@ -19731,7 +19812,7 @@
19731
19812
  });
19732
19813
  };
19733
19814
  _proto.flushFrontBuffer = function flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) {
19734
- var _this10 = this;
19815
+ var _this13 = this;
19735
19816
  var sourceBuffer = this.sourceBuffer;
19736
19817
  var sourceBufferTypes = this.getSourceBufferTypes();
19737
19818
  sourceBufferTypes.forEach(function (type) {
@@ -19749,10 +19830,10 @@
19749
19830
  if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) {
19750
19831
  return;
19751
19832
  } else if (sb.ended && currentTime - bufferEnd < 2 * targetDuration) {
19752
- _this10.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state");
19833
+ _this13.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state");
19753
19834
  return;
19754
19835
  }
19755
- _this10.hls.trigger(Events.BUFFER_FLUSHING, {
19836
+ _this13.hls.trigger(Events.BUFFER_FLUSHING, {
19756
19837
  startOffset: bufferStart,
19757
19838
  endOffset: Infinity,
19758
19839
  type: type
@@ -19766,9 +19847,9 @@
19766
19847
  * 'liveDurationInfinity` is set to `true`
19767
19848
  * More details: https://github.com/video-dev/hls.js/issues/355
19768
19849
  */;
19769
- _proto.updateMediaElementDuration = function updateMediaElementDuration() {
19850
+ _proto.getDurationAndRange = function getDurationAndRange() {
19770
19851
  if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
19771
- return;
19852
+ return null;
19772
19853
  }
19773
19854
  var details = this.details,
19774
19855
  hls = this.hls,
@@ -19780,25 +19861,40 @@
19780
19861
  if (details.live && hls.config.liveDurationInfinity) {
19781
19862
  // Override duration to Infinity
19782
19863
  mediaSource.duration = Infinity;
19783
- this.updateSeekableRange(details);
19864
+ var len = details.fragments.length;
19865
+ if (len && details.live && !!mediaSource.setLiveSeekableRange) {
19866
+ var start = Math.max(0, details.fragments[0].start);
19867
+ var end = Math.max(start, start + details.totalduration);
19868
+ return {
19869
+ duration: Infinity,
19870
+ start: start,
19871
+ end: end
19872
+ };
19873
+ }
19874
+ return {
19875
+ duration: Infinity
19876
+ };
19784
19877
  } else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {
19785
- // levelDuration was the last value we set.
19786
- // not using mediaSource.duration as the browser may tweak this value
19787
- // only update Media Source duration if its value increase, this is to avoid
19788
- // flushing already buffered portion when switching between quality level
19789
- this.log("Updating Media Source duration to " + levelDuration.toFixed(3));
19790
- mediaSource.duration = levelDuration;
19878
+ return {
19879
+ duration: levelDuration
19880
+ };
19791
19881
  }
19882
+ return null;
19792
19883
  };
19793
- _proto.updateSeekableRange = function updateSeekableRange(levelDetails) {
19794
- var mediaSource = this.mediaSource;
19795
- var fragments = levelDetails.fragments;
19796
- var len = fragments.length;
19797
- if (len && levelDetails.live && mediaSource != null && mediaSource.setLiveSeekableRange) {
19798
- var start = Math.max(0, fragments[0].start);
19799
- var end = Math.max(start, start + levelDetails.totalduration);
19800
- this.log("Media Source duration is set to " + mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
19801
- mediaSource.setLiveSeekableRange(start, end);
19884
+ _proto.updateMediaSource = function updateMediaSource(_ref2) {
19885
+ var duration = _ref2.duration,
19886
+ start = _ref2.start,
19887
+ end = _ref2.end;
19888
+ if (!this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
19889
+ return;
19890
+ }
19891
+ if (isFiniteNumber(duration)) {
19892
+ this.log("Updating Media Source duration to " + duration.toFixed(3));
19893
+ }
19894
+ this.mediaSource.duration = duration;
19895
+ if (start !== undefined && end !== undefined) {
19896
+ this.log("Media Source duration is set to " + this.mediaSource.duration + ". Setting seekable range to " + start + "-" + end + ".");
19897
+ this.mediaSource.setLiveSeekableRange(start, end);
19802
19898
  }
19803
19899
  };
19804
19900
  _proto.checkPendingTracks = function checkPendingTracks() {
@@ -19837,7 +19933,7 @@
19837
19933
  }
19838
19934
  };
19839
19935
  _proto.createSourceBuffers = function createSourceBuffers(tracks) {
19840
- var _this11 = this;
19936
+ var _this14 = this;
19841
19937
  var sourceBuffer = this.sourceBuffer,
19842
19938
  mediaSource = this.mediaSource;
19843
19939
  if (!mediaSource) {
@@ -19853,28 +19949,28 @@
19853
19949
  var codec = track.levelCodec || track.codec;
19854
19950
  if (codec) {
19855
19951
  if (trackName.slice(0, 5) === 'audio') {
19856
- codec = getCodecCompatibleName(codec, _this11.hls.config.preferManagedMediaSource);
19952
+ codec = getCodecCompatibleName(codec, _this14.hls.config.preferManagedMediaSource);
19857
19953
  }
19858
19954
  }
19859
19955
  var mimeType = track.container + ";codecs=" + codec;
19860
- _this11.log("creating sourceBuffer(" + mimeType + ")");
19956
+ _this14.log("creating sourceBuffer(" + mimeType + ")");
19861
19957
  try {
19862
19958
  var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
19863
19959
  var sbName = trackName;
19864
- _this11.addBufferListener(sbName, 'updatestart', _this11._onSBUpdateStart);
19865
- _this11.addBufferListener(sbName, 'updateend', _this11._onSBUpdateEnd);
19866
- _this11.addBufferListener(sbName, 'error', _this11._onSBUpdateError);
19960
+ _this14.addBufferListener(sbName, 'updatestart', _this14._onSBUpdateStart);
19961
+ _this14.addBufferListener(sbName, 'updateend', _this14._onSBUpdateEnd);
19962
+ _this14.addBufferListener(sbName, 'error', _this14._onSBUpdateError);
19867
19963
  // ManagedSourceBuffer bufferedchange event
19868
- _this11.addBufferListener(sbName, 'bufferedchange', function (type, event) {
19964
+ _this14.addBufferListener(sbName, 'bufferedchange', function (type, event) {
19869
19965
  // If media was ejected check for a change. Added ranges are redundant with changes on 'updateend' event.
19870
19966
  var removedRanges = event.removedRanges;
19871
19967
  if (removedRanges != null && removedRanges.length) {
19872
- _this11.hls.trigger(Events.BUFFER_FLUSHED, {
19968
+ _this14.hls.trigger(Events.BUFFER_FLUSHED, {
19873
19969
  type: trackName
19874
19970
  });
19875
19971
  }
19876
19972
  });
19877
- _this11.tracks[trackName] = {
19973
+ _this14.tracks[trackName] = {
19878
19974
  buffer: sb,
19879
19975
  codec: codec,
19880
19976
  container: track.container,
@@ -19883,8 +19979,8 @@
19883
19979
  id: track.id
19884
19980
  };
19885
19981
  } catch (err) {
19886
- _this11.error("error while trying to add sourceBuffer: " + err.message);
19887
- _this11.hls.trigger(Events.ERROR, {
19982
+ _this14.error("error while trying to add sourceBuffer: " + err.message);
19983
+ _this14.hls.trigger(Events.ERROR, {
19888
19984
  type: ErrorTypes.MEDIA_ERROR,
19889
19985
  details: ErrorDetails.BUFFER_ADD_CODEC_ERROR,
19890
19986
  fatal: false,
@@ -19972,6 +20068,7 @@
19972
20068
  }
19973
20069
  return;
19974
20070
  }
20071
+ sb.ending = false;
19975
20072
  sb.ended = false;
19976
20073
  sb.appendBuffer(data);
19977
20074
  }
@@ -19981,7 +20078,7 @@
19981
20078
  // upon completion, since we already do it here
19982
20079
  ;
19983
20080
  _proto.blockBuffers = function blockBuffers(onUnblocked, buffers) {
19984
- var _this12 = this;
20081
+ var _this15 = this;
19985
20082
  if (buffers === void 0) {
19986
20083
  buffers = this.getSourceBufferTypes();
19987
20084
  }
@@ -19996,11 +20093,15 @@
19996
20093
  var blockingOperations = buffers.map(function (type) {
19997
20094
  return operationQueue.appendBlocker(type);
19998
20095
  });
19999
- Promise.all(blockingOperations).then(function () {
20096
+ var audioBlocked = buffers.length > 1 && !!this.blockedAudioAppend;
20097
+ if (audioBlocked) {
20098
+ this.unblockAudio();
20099
+ }
20100
+ Promise.all(blockingOperations).then(function (result) {
20000
20101
  // logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
20001
20102
  onUnblocked();
20002
- buffers.forEach(function (type) {
20003
- var sb = _this12.sourceBuffer[type];
20103
+ buffers.forEach(function (type, i) {
20104
+ var sb = _this15.sourceBuffer[type];
20004
20105
  // Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to
20005
20106
  // true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)
20006
20107
  // While this is a workaround, it's probably useful to have around
@@ -29175,6 +29276,17 @@
29175
29276
  }
29176
29277
  };
29177
29278
  _createClass(StreamController, [{
29279
+ key: "maxBufferLength",
29280
+ get: function get() {
29281
+ var levels = this.levels,
29282
+ level = this.level;
29283
+ var levelInfo = levels == null ? void 0 : levels[level];
29284
+ if (!levelInfo) {
29285
+ return this.config.maxBufferLength;
29286
+ }
29287
+ return this.getMaxBufferLength(levelInfo.maxBitrate);
29288
+ }
29289
+ }, {
29178
29290
  key: "nextLevel",
29179
29291
  get: function get() {
29180
29292
  var frag = this.nextBufferedFrag;
@@ -29311,7 +29423,9 @@
29311
29423
  ConfigFpsController = config.fpsController;
29312
29424
  var errorController = new ConfigErrorController(this);
29313
29425
  var abrController = this.abrController = new ConfigAbrController(this);
29314
- var bufferController = this.bufferController = new ConfigBufferController(this);
29426
+ // FragmentTracker must be defined before StreamController because the order of event handling is important
29427
+ var fragmentTracker = new FragmentTracker(this);
29428
+ var bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker);
29315
29429
  var capLevelController = this.capLevelController = new ConfigCapLevelController(this);
29316
29430
  var fpsController = new ConfigFpsController(this);
29317
29431
  var playListLoader = new PlaylistLoader(this);
@@ -29320,8 +29434,6 @@
29320
29434
  // ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
29321
29435
  var contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;
29322
29436
  var levelController = this.levelController = new LevelController(this, contentSteering);
29323
- // FragmentTracker must be defined before StreamController because the order of event handling is important
29324
- var fragmentTracker = new FragmentTracker(this);
29325
29437
  var keyLoader = new KeyLoader(this.config);
29326
29438
  var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
29327
29439
 
@@ -29918,6 +30030,11 @@
29918
30030
  get: function get() {
29919
30031
  return this.streamController.getMainFwdBufferInfo();
29920
30032
  }
30033
+ }, {
30034
+ key: "maxBufferLength",
30035
+ get: function get() {
30036
+ return this.streamController.maxBufferLength;
30037
+ }
29921
30038
  }, {
29922
30039
  key: "allAudioTracks",
29923
30040
  get: function get() {
@@ -30100,7 +30217,7 @@
30100
30217
  * Get the video-dev/hls.js package version.
30101
30218
  */
30102
30219
  function get() {
30103
- return "1.5.7-0.canary.10014";
30220
+ return "1.5.7-0.canary.10016";
30104
30221
  }
30105
30222
  }, {
30106
30223
  key: "Events",