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.mjs CHANGED
@@ -512,7 +512,7 @@ function enableLogs(debugConfig, context, id) {
512
512
  // Some browsers don't allow to use bind on console object anyway
513
513
  // fallback to default if needed
514
514
  try {
515
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.7-0.canary.10014"}`);
515
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.7-0.canary.10016"}`);
516
516
  } catch (e) {
517
517
  /* log fn threw an exception. All logger methods are no-ops. */
518
518
  return createLogger();
@@ -1941,7 +1941,9 @@ function parseStsd(stsd) {
1941
1941
  }
1942
1942
  function skipBERInteger(bytes, i) {
1943
1943
  const limit = i + 5;
1944
- while (bytes[i++] & 0x80 && i < limit) {}
1944
+ while (bytes[i++] & 0x80 && i < limit) {
1945
+ /* do nothing */
1946
+ }
1945
1947
  return i;
1946
1948
  }
1947
1949
  function toHex(x) {
@@ -7290,6 +7292,9 @@ class AbrController extends Logger {
7290
7292
  partCurrent,
7291
7293
  hls
7292
7294
  } = this;
7295
+ if (hls.levels.length <= 1) {
7296
+ return hls.loadLevel;
7297
+ }
7293
7298
  const {
7294
7299
  maxAutoLevel,
7295
7300
  config,
@@ -7700,13 +7705,16 @@ class FragmentTracker {
7700
7705
  * If not found any Fragment, return null
7701
7706
  */
7702
7707
  getBufferedFrag(position, levelType) {
7708
+ return this.getFragAtPos(position, levelType, true);
7709
+ }
7710
+ getFragAtPos(position, levelType, buffered) {
7703
7711
  const {
7704
7712
  fragments
7705
7713
  } = this;
7706
7714
  const keys = Object.keys(fragments);
7707
7715
  for (let i = keys.length; i--;) {
7708
7716
  const fragmentEntity = fragments[keys[i]];
7709
- if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {
7717
+ if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) {
7710
7718
  const frag = fragmentEntity.body;
7711
7719
  if (frag.start <= position && position <= frag.end) {
7712
7720
  return frag;
@@ -7961,7 +7969,8 @@ class FragmentTracker {
7961
7969
  const {
7962
7970
  frag,
7963
7971
  part,
7964
- timeRanges
7972
+ timeRanges,
7973
+ type
7965
7974
  } = data;
7966
7975
  if (frag.sn === 'initSegment') {
7967
7976
  return;
@@ -7976,10 +7985,8 @@ class FragmentTracker {
7976
7985
  }
7977
7986
  // Store the latest timeRanges loaded in the buffer
7978
7987
  this.timeRanges = timeRanges;
7979
- Object.keys(timeRanges).forEach(elementaryStream => {
7980
- const timeRange = timeRanges[elementaryStream];
7981
- this.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);
7982
- });
7988
+ const timeRange = timeRanges[type];
7989
+ this.detectEvictedFragments(type, timeRange, playlistType, part);
7983
7990
  }
7984
7991
  onFragBuffered(event, data) {
7985
7992
  this.detectPartialFragments(data);
@@ -9934,7 +9941,7 @@ class BaseStreamController extends TaskLoop {
9934
9941
  // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
9935
9942
  if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
9936
9943
  const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
9937
- if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
9944
+ if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) {
9938
9945
  return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
9939
9946
  }
9940
9947
  }
@@ -16790,9 +16797,8 @@ class AudioStreamController extends BaseStreamController {
16790
16797
  this.state = State.ENDED;
16791
16798
  return;
16792
16799
  }
16793
- const mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);
16794
16800
  const bufferLen = bufferInfo.len;
16795
- const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len);
16801
+ const maxBufLen = hls.maxBufferLength;
16796
16802
  const fragments = trackDetails.fragments;
16797
16803
  const start = fragments[0].start;
16798
16804
  let targetBufferTime = this.flushing ? this.getLoadPosition() : bufferInfo.end;
@@ -16827,32 +16833,25 @@ class AudioStreamController extends BaseStreamController {
16827
16833
  this.bufferFlushed = true;
16828
16834
  return;
16829
16835
  }
16830
-
16831
- // Buffer audio up to one target duration ahead of main buffer
16832
- const atBufferSyncLimit = mainBufferInfo && frag.start > mainBufferInfo.end + trackDetails.targetduration;
16833
- if (atBufferSyncLimit ||
16834
- // Or wait for main buffer after buffing some audio
16835
- !(mainBufferInfo != null && mainBufferInfo.len) && bufferInfo.len) {
16836
- // Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo
16837
- const mainFrag = this.getAppendedFrag(frag.start, PlaylistLevelType.MAIN);
16838
- if (mainFrag === null) {
16839
- return;
16840
- }
16841
- // Bridge gaps in main buffer
16842
- atGap || (atGap = !!mainFrag.gap || !!atBufferSyncLimit && mainBufferInfo.len === 0);
16843
- if (atBufferSyncLimit && !atGap || atGap && bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {
16844
- return;
16836
+ if (!trackDetails.live || targetBufferTime < this.hls.liveSyncPosition) {
16837
+ // Request audio segments up to one fragment ahead of main buffer
16838
+ const mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);
16839
+ const atBufferSyncLimit = !!mainBufferInfo && frag.start > mainBufferInfo.end + frag.duration;
16840
+ if (atBufferSyncLimit) {
16841
+ // Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo
16842
+ const mainFrag = this.fragmentTracker.getFragAtPos(frag.start, PlaylistLevelType.MAIN);
16843
+ if (mainFrag === null) {
16844
+ return;
16845
+ }
16846
+ // Bridge gaps in main buffer (also prevents loop loading at gaps)
16847
+ atGap || (atGap = !!mainFrag.gap || mainBufferInfo.len === 0);
16848
+ if (!atGap || bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {
16849
+ return;
16850
+ }
16845
16851
  }
16846
16852
  }
16847
16853
  this.loadFragment(frag, levelInfo, targetBufferTime);
16848
16854
  }
16849
- getMaxBufferLength(mainBufferLength) {
16850
- const maxConfigBuffer = super.getMaxBufferLength();
16851
- if (!mainBufferLength) {
16852
- return maxConfigBuffer;
16853
- }
16854
- return Math.min(Math.max(maxConfigBuffer, mainBufferLength), this.config.maxMaxBufferLength);
16855
- }
16856
16855
  onMediaDetaching() {
16857
16856
  this.videoBuffer = null;
16858
16857
  this.bufferFlushed = this.flushing = false;
@@ -17946,9 +17945,8 @@ class SubtitleStreamController extends BaseStreamController {
17946
17945
  end: targetBufferTime,
17947
17946
  len: bufferLen
17948
17947
  } = bufferedInfo;
17949
- const mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN);
17950
17948
  const trackDetails = track.details;
17951
- const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + trackDetails.levelTargetDuration;
17949
+ const maxBufLen = this.hls.maxBufferLength + trackDetails.levelTargetDuration;
17952
17950
  if (bufferLen > maxBufLen) {
17953
17951
  return;
17954
17952
  }
@@ -17985,13 +17983,6 @@ class SubtitleStreamController extends BaseStreamController {
17985
17983
  }
17986
17984
  }
17987
17985
  }
17988
- getMaxBufferLength(mainBufferLength) {
17989
- const maxConfigBuffer = super.getMaxBufferLength();
17990
- if (!mainBufferLength) {
17991
- return maxConfigBuffer;
17992
- }
17993
- return Math.max(maxConfigBuffer, mainBufferLength);
17994
- }
17995
17986
  loadFragment(frag, level, targetBufferTime) {
17996
17987
  this.fragCurrent = frag;
17997
17988
  if (frag.sn === 'initSegment') {
@@ -18489,24 +18480,22 @@ class BufferOperationQueue {
18489
18480
  this.executeNext(type);
18490
18481
  }
18491
18482
  }
18492
- insertAbort(operation, type) {
18493
- const queue = this.queues[type];
18494
- queue.unshift(operation);
18495
- this.executeNext(type);
18496
- }
18497
18483
  appendBlocker(type) {
18498
- let execute;
18499
- const promise = new Promise(resolve => {
18500
- execute = resolve;
18484
+ return new Promise(resolve => {
18485
+ const operation = {
18486
+ execute: resolve,
18487
+ onStart: () => {},
18488
+ onComplete: () => {},
18489
+ onError: () => {}
18490
+ };
18491
+ this.append(operation, type);
18501
18492
  });
18502
- const operation = {
18503
- execute,
18504
- onStart: () => {},
18505
- onComplete: () => {},
18506
- onError: () => {}
18507
- };
18508
- this.append(operation, type);
18509
- return promise;
18493
+ }
18494
+ unblockAudio(op) {
18495
+ const queue = this.queues.audio;
18496
+ if (queue[0] === op) {
18497
+ this.shiftAndExecuteNext('audio');
18498
+ }
18510
18499
  }
18511
18500
  executeNext(type) {
18512
18501
  const queue = this.queues[type];
@@ -18539,7 +18528,7 @@ class BufferOperationQueue {
18539
18528
 
18540
18529
  const VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
18541
18530
  class BufferController extends Logger {
18542
- constructor(hls) {
18531
+ constructor(hls, fragmentTracker) {
18543
18532
  super('buffer-controller', hls.logger);
18544
18533
  // The level details used to determine duration, target-duration and live
18545
18534
  this.details = null;
@@ -18550,6 +18539,7 @@ class BufferController extends Logger {
18550
18539
  // References to event listeners for each SourceBuffer, so that they can be referenced for event removal
18551
18540
  this.listeners = void 0;
18552
18541
  this.hls = void 0;
18542
+ this.fragmentTracker = void 0;
18553
18543
  // The number of BUFFER_CODEC events received before any sourceBuffers are created
18554
18544
  this.bufferCodecEventsExpected = 0;
18555
18545
  // The total number of BUFFER_CODEC events received
@@ -18560,6 +18550,10 @@ class BufferController extends Logger {
18560
18550
  this.mediaSource = null;
18561
18551
  // Last MP3 audio chunk appended
18562
18552
  this.lastMpegAudioChunk = null;
18553
+ // Audio fragment blocked from appending until corresponding video appends or context changes
18554
+ this.blockedAudioAppend = null;
18555
+ // Keep track of video append position for unblocking audio
18556
+ this.lastVideoAppendEnd = 0;
18563
18557
  this.appendSource = void 0;
18564
18558
  // counters
18565
18559
  this.appendErrors = {
@@ -18591,7 +18585,10 @@ class BufferController extends Logger {
18591
18585
  this.log('Media source opened');
18592
18586
  if (media) {
18593
18587
  media.removeEventListener('emptied', this._onMediaEmptied);
18594
- this.updateMediaElementDuration();
18588
+ const durationAndRange = this.getDurationAndRange();
18589
+ if (durationAndRange) {
18590
+ this.updateMediaSource(durationAndRange);
18591
+ }
18595
18592
  this.hls.trigger(Events.MEDIA_ATTACHED, {
18596
18593
  media,
18597
18594
  mediaSource: mediaSource
@@ -18619,6 +18616,7 @@ class BufferController extends Logger {
18619
18616
  }
18620
18617
  };
18621
18618
  this.hls = hls;
18619
+ this.fragmentTracker = fragmentTracker;
18622
18620
  this.appendSource = hls.config.preferManagedMediaSource;
18623
18621
  this._initSourceBuffer();
18624
18622
  this.registerListeners();
@@ -18631,7 +18629,7 @@ class BufferController extends Logger {
18631
18629
  this.details = null;
18632
18630
  this.lastMpegAudioChunk = null;
18633
18631
  // @ts-ignore
18634
- this.hls = null;
18632
+ this.hls = this.fragmentTracker = null;
18635
18633
  // @ts-ignore
18636
18634
  this._onMediaSourceOpen = this._onMediaSourceClose = null;
18637
18635
  // @ts-ignore
@@ -18687,6 +18685,8 @@ class BufferController extends Logger {
18687
18685
  audiovideo: 0
18688
18686
  };
18689
18687
  this.lastMpegAudioChunk = null;
18688
+ this.blockedAudioAppend = null;
18689
+ this.lastVideoAppendEnd = 0;
18690
18690
  }
18691
18691
  onManifestLoading() {
18692
18692
  this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
@@ -18824,9 +18824,10 @@ class BufferController extends Logger {
18824
18824
  const trackNames = Object.keys(data);
18825
18825
  trackNames.forEach(trackName => {
18826
18826
  if (sourceBufferCount) {
18827
+ var _track$buffer;
18827
18828
  // check if SourceBuffer codec needs to change
18828
18829
  const track = this.tracks[trackName];
18829
- if (track && typeof track.buffer.changeType === 'function') {
18830
+ if (track && typeof ((_track$buffer = track.buffer) == null ? void 0 : _track$buffer.changeType) === 'function') {
18830
18831
  var _trackCodec;
18831
18832
  const {
18832
18833
  id,
@@ -18896,20 +18897,54 @@ class BufferController extends Logger {
18896
18897
  };
18897
18898
  operationQueue.append(operation, type, !!this.pendingTracks[type]);
18898
18899
  }
18900
+ blockAudio(partOrFrag) {
18901
+ var _this$fragmentTracker;
18902
+ const pStart = partOrFrag.start;
18903
+ const pTime = pStart + partOrFrag.duration * 0.05;
18904
+ const atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true;
18905
+ if (atGap) {
18906
+ return;
18907
+ }
18908
+ const op = {
18909
+ execute: () => {
18910
+ var _this$fragmentTracker2;
18911
+ if (this.lastVideoAppendEnd > pTime || this.sourceBuffer.video && BufferHelper.isBuffered(this.sourceBuffer.video, pTime) || ((_this$fragmentTracker2 = this.fragmentTracker.getAppendedFrag(pTime, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker2.gap) === true) {
18912
+ this.blockedAudioAppend = null;
18913
+ this.operationQueue.shiftAndExecuteNext('audio');
18914
+ }
18915
+ },
18916
+ onStart: () => {},
18917
+ onComplete: () => {},
18918
+ onError: () => {}
18919
+ };
18920
+ this.blockedAudioAppend = {
18921
+ op,
18922
+ frag: partOrFrag
18923
+ };
18924
+ this.operationQueue.append(op, 'audio', true);
18925
+ }
18926
+ unblockAudio() {
18927
+ const blockedAudioAppend = this.blockedAudioAppend;
18928
+ if (blockedAudioAppend) {
18929
+ this.blockedAudioAppend = null;
18930
+ this.operationQueue.unblockAudio(blockedAudioAppend.op);
18931
+ }
18932
+ }
18899
18933
  onBufferAppending(event, eventData) {
18900
18934
  const {
18901
- hls,
18902
18935
  operationQueue,
18903
18936
  tracks
18904
18937
  } = this;
18905
18938
  const {
18906
18939
  data,
18907
18940
  type,
18941
+ parent,
18908
18942
  frag,
18909
18943
  part,
18910
18944
  chunkMeta
18911
18945
  } = eventData;
18912
18946
  const chunkStats = chunkMeta.buffering[type];
18947
+ const sn = frag.sn;
18913
18948
  const bufferAppendingStart = self.performance.now();
18914
18949
  chunkStats.start = bufferAppendingStart;
18915
18950
  const fragBuffering = frag.stats.buffering;
@@ -18932,7 +18967,36 @@ class BufferController extends Logger {
18932
18967
  checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;
18933
18968
  this.lastMpegAudioChunk = chunkMeta;
18934
18969
  }
18935
- const fragStart = frag.start;
18970
+
18971
+ // Block audio append until overlapping video append
18972
+ const videoSb = this.sourceBuffer.video;
18973
+ if (videoSb && sn !== 'initSegment') {
18974
+ const partOrFrag = part || frag;
18975
+ const blockedAudioAppend = this.blockedAudioAppend;
18976
+ if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) {
18977
+ const pStart = partOrFrag.start;
18978
+ const pTime = pStart + partOrFrag.duration * 0.05;
18979
+ const vbuffered = videoSb.buffered;
18980
+ const vappending = this.operationQueue.current('video');
18981
+ if (!vbuffered.length && !vappending) {
18982
+ // wait for video before appending audio
18983
+ this.blockAudio(partOrFrag);
18984
+ } else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) {
18985
+ // audio is ahead of video
18986
+ this.blockAudio(partOrFrag);
18987
+ }
18988
+ } else if (type === 'video') {
18989
+ const videoAppendEnd = partOrFrag.end;
18990
+ if (blockedAudioAppend) {
18991
+ const audioStart = blockedAudioAppend.frag.start;
18992
+ if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) {
18993
+ this.unblockAudio();
18994
+ }
18995
+ }
18996
+ this.lastVideoAppendEnd = videoAppendEnd;
18997
+ }
18998
+ }
18999
+ const fragStart = (part || frag).start;
18936
19000
  const operation = {
18937
19001
  execute: () => {
18938
19002
  chunkStats.executeStart = self.performance.now();
@@ -18941,7 +19005,7 @@ class BufferController extends Logger {
18941
19005
  if (sb) {
18942
19006
  const delta = fragStart - sb.timestampOffset;
18943
19007
  if (Math.abs(delta) >= 0.1) {
18944
- this.log(`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${frag.sn})`);
19008
+ this.log(`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${sn})`);
18945
19009
  sb.timestampOffset = fragStart;
18946
19010
  }
18947
19011
  }
@@ -19008,22 +19072,21 @@ class BufferController extends Logger {
19008
19072
  /* with UHD content, we could get loop of quota exceeded error until
19009
19073
  browser is able to evict some data from sourcebuffer. Retrying can help recover.
19010
19074
  */
19011
- this.warn(`Failed ${appendErrorCount}/${hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`);
19012
- if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
19075
+ this.warn(`Failed ${appendErrorCount}/${this.hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`);
19076
+ if (appendErrorCount >= this.hls.config.appendErrorMaxRetry) {
19013
19077
  event.fatal = true;
19014
19078
  }
19015
19079
  }
19016
- hls.trigger(Events.ERROR, event);
19080
+ this.hls.trigger(Events.ERROR, event);
19017
19081
  }
19018
19082
  };
19019
19083
  operationQueue.append(operation, type, !!this.pendingTracks[type]);
19020
19084
  }
19021
- onBufferFlushing(event, data) {
19022
- const {
19023
- operationQueue
19024
- } = this;
19025
- const flushOperation = type => ({
19026
- execute: this.removeExecutor.bind(this, type, data.startOffset, data.endOffset),
19085
+ getFlushOp(type, start, end) {
19086
+ return {
19087
+ execute: () => {
19088
+ this.removeExecutor(type, start, end);
19089
+ },
19027
19090
  onStart: () => {
19028
19091
  // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
19029
19092
  },
@@ -19036,12 +19099,22 @@ class BufferController extends Logger {
19036
19099
  onError: error => {
19037
19100
  this.warn(`Failed to remove from ${type} SourceBuffer`, error);
19038
19101
  }
19039
- });
19040
- if (data.type) {
19041
- operationQueue.append(flushOperation(data.type), data.type);
19102
+ };
19103
+ }
19104
+ onBufferFlushing(event, data) {
19105
+ const {
19106
+ operationQueue
19107
+ } = this;
19108
+ const {
19109
+ type,
19110
+ startOffset,
19111
+ endOffset
19112
+ } = data;
19113
+ if (type) {
19114
+ operationQueue.append(this.getFlushOp(type, startOffset, endOffset), type);
19042
19115
  } else {
19043
- this.getSourceBufferTypes().forEach(type => {
19044
- operationQueue.append(flushOperation(type), type);
19116
+ this.getSourceBufferTypes().forEach(sbType => {
19117
+ operationQueue.append(this.getFlushOp(sbType, startOffset, endOffset), sbType);
19045
19118
  });
19046
19119
  }
19047
19120
  }
@@ -19088,6 +19161,9 @@ class BufferController extends Logger {
19088
19161
  // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos()
19089
19162
  // an undefined data.type will mark all buffers as EOS.
19090
19163
  onBufferEos(event, data) {
19164
+ if (data.type === 'video') {
19165
+ this.unblockAudio();
19166
+ }
19091
19167
  const ended = this.getSourceBufferTypes().reduce((acc, type) => {
19092
19168
  const sb = this.sourceBuffer[type];
19093
19169
  if (sb && (!data.type || data.type === type)) {
@@ -19130,10 +19206,14 @@ class BufferController extends Logger {
19130
19206
  return;
19131
19207
  }
19132
19208
  this.details = details;
19209
+ const durationAndRange = this.getDurationAndRange();
19210
+ if (!durationAndRange) {
19211
+ return;
19212
+ }
19133
19213
  if (this.getSourceBufferTypes().length) {
19134
- this.blockBuffers(this.updateMediaElementDuration.bind(this));
19214
+ this.blockBuffers(() => this.updateMediaSource(durationAndRange));
19135
19215
  } else {
19136
- this.updateMediaElementDuration();
19216
+ this.updateMediaSource(durationAndRange);
19137
19217
  }
19138
19218
  }
19139
19219
  trimBuffers() {
@@ -19238,9 +19318,9 @@ class BufferController extends Logger {
19238
19318
  * 'liveDurationInfinity` is set to `true`
19239
19319
  * More details: https://github.com/video-dev/hls.js/issues/355
19240
19320
  */
19241
- updateMediaElementDuration() {
19321
+ getDurationAndRange() {
19242
19322
  if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
19243
- return;
19323
+ return null;
19244
19324
  }
19245
19325
  const {
19246
19326
  details,
@@ -19254,25 +19334,41 @@ class BufferController extends Logger {
19254
19334
  if (details.live && hls.config.liveDurationInfinity) {
19255
19335
  // Override duration to Infinity
19256
19336
  mediaSource.duration = Infinity;
19257
- this.updateSeekableRange(details);
19337
+ const len = details.fragments.length;
19338
+ if (len && details.live && !!mediaSource.setLiveSeekableRange) {
19339
+ const start = Math.max(0, details.fragments[0].start);
19340
+ const end = Math.max(start, start + details.totalduration);
19341
+ return {
19342
+ duration: Infinity,
19343
+ start,
19344
+ end
19345
+ };
19346
+ }
19347
+ return {
19348
+ duration: Infinity
19349
+ };
19258
19350
  } else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {
19259
- // levelDuration was the last value we set.
19260
- // not using mediaSource.duration as the browser may tweak this value
19261
- // only update Media Source duration if its value increase, this is to avoid
19262
- // flushing already buffered portion when switching between quality level
19263
- this.log(`Updating Media Source duration to ${levelDuration.toFixed(3)}`);
19264
- mediaSource.duration = levelDuration;
19351
+ return {
19352
+ duration: levelDuration
19353
+ };
19265
19354
  }
19355
+ return null;
19266
19356
  }
19267
- updateSeekableRange(levelDetails) {
19268
- const mediaSource = this.mediaSource;
19269
- const fragments = levelDetails.fragments;
19270
- const len = fragments.length;
19271
- if (len && levelDetails.live && mediaSource != null && mediaSource.setLiveSeekableRange) {
19272
- const start = Math.max(0, fragments[0].start);
19273
- const end = Math.max(start, start + levelDetails.totalduration);
19274
- this.log(`Media Source duration is set to ${mediaSource.duration}. Setting seekable range to ${start}-${end}.`);
19275
- mediaSource.setLiveSeekableRange(start, end);
19357
+ updateMediaSource({
19358
+ duration,
19359
+ start,
19360
+ end
19361
+ }) {
19362
+ if (!this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
19363
+ return;
19364
+ }
19365
+ if (isFiniteNumber(duration)) {
19366
+ this.log(`Updating Media Source duration to ${duration.toFixed(3)}`);
19367
+ }
19368
+ this.mediaSource.duration = duration;
19369
+ if (start !== undefined && end !== undefined) {
19370
+ this.log(`Media Source duration is set to ${this.mediaSource.duration}. Setting seekable range to ${start}-${end}.`);
19371
+ this.mediaSource.setLiveSeekableRange(start, end);
19276
19372
  }
19277
19373
  }
19278
19374
  checkPendingTracks() {
@@ -19455,6 +19551,7 @@ class BufferController extends Logger {
19455
19551
  }
19456
19552
  return;
19457
19553
  }
19554
+ sb.ending = false;
19458
19555
  sb.ended = false;
19459
19556
  sb.appendBuffer(data);
19460
19557
  }
@@ -19474,10 +19571,14 @@ class BufferController extends Logger {
19474
19571
 
19475
19572
  // logger.debug(`[buffer-controller]: Blocking ${buffers} SourceBuffer`);
19476
19573
  const blockingOperations = buffers.map(type => operationQueue.appendBlocker(type));
19477
- Promise.all(blockingOperations).then(() => {
19574
+ const audioBlocked = buffers.length > 1 && !!this.blockedAudioAppend;
19575
+ if (audioBlocked) {
19576
+ this.unblockAudio();
19577
+ }
19578
+ Promise.all(blockingOperations).then(result => {
19478
19579
  // logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
19479
19580
  onUnblocked();
19480
- buffers.forEach(type => {
19581
+ buffers.forEach((type, i) => {
19481
19582
  const sb = this.sourceBuffer[type];
19482
19583
  // Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to
19483
19584
  // true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)
@@ -28496,6 +28597,17 @@ class StreamController extends BaseStreamController {
28496
28597
  getMainFwdBufferInfo() {
28497
28598
  return this.getFwdBufferInfo(this.mediaBuffer ? this.mediaBuffer : this.media, PlaylistLevelType.MAIN);
28498
28599
  }
28600
+ get maxBufferLength() {
28601
+ const {
28602
+ levels,
28603
+ level
28604
+ } = this;
28605
+ const levelInfo = levels == null ? void 0 : levels[level];
28606
+ if (!levelInfo) {
28607
+ return this.config.maxBufferLength;
28608
+ }
28609
+ return this.getMaxBufferLength(levelInfo.maxBitrate);
28610
+ }
28499
28611
  backtrack(frag) {
28500
28612
  this.couldBacktrack = true;
28501
28613
  // Causes findFragments to backtrack through fragments to find the keyframe
@@ -28601,7 +28713,7 @@ class Hls {
28601
28713
  * Get the video-dev/hls.js package version.
28602
28714
  */
28603
28715
  static get version() {
28604
- return "1.5.7-0.canary.10014";
28716
+ return "1.5.7-0.canary.10016";
28605
28717
  }
28606
28718
 
28607
28719
  /**
@@ -28703,7 +28815,9 @@ class Hls {
28703
28815
  } = config;
28704
28816
  const errorController = new ConfigErrorController(this);
28705
28817
  const abrController = this.abrController = new ConfigAbrController(this);
28706
- const bufferController = this.bufferController = new ConfigBufferController(this);
28818
+ // FragmentTracker must be defined before StreamController because the order of event handling is important
28819
+ const fragmentTracker = new FragmentTracker(this);
28820
+ const bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker);
28707
28821
  const capLevelController = this.capLevelController = new ConfigCapLevelController(this);
28708
28822
  const fpsController = new ConfigFpsController(this);
28709
28823
  const playListLoader = new PlaylistLoader(this);
@@ -28712,8 +28826,6 @@ class Hls {
28712
28826
  // ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
28713
28827
  const contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;
28714
28828
  const levelController = this.levelController = new LevelController(this, contentSteering);
28715
- // FragmentTracker must be defined before StreamController because the order of event handling is important
28716
- const fragmentTracker = new FragmentTracker(this);
28717
28829
  const keyLoader = new KeyLoader(this.config);
28718
28830
  const streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
28719
28831
 
@@ -29241,6 +29353,9 @@ class Hls {
29241
29353
  get mainForwardBufferInfo() {
29242
29354
  return this.streamController.getMainFwdBufferInfo();
29243
29355
  }
29356
+ get maxBufferLength() {
29357
+ return this.streamController.maxBufferLength;
29358
+ }
29244
29359
 
29245
29360
  /**
29246
29361
  * Find and select the best matching audio track, making a level switch when a Group change is necessary.