hls.js 1.6.0-beta.4.0.canary.11033 → 1.6.0-beta.4.0.canary.11034

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.
@@ -207,14 +207,16 @@ class AbrController extends Logger implements AbrComponentAPI {
207
207
  isSwitch: boolean,
208
208
  ): number {
209
209
  const fragLoadSec = timeToFirstByteSec + fragSizeBits / bandwidth;
210
- const playlistLoadSec = isSwitch ? this.lastLevelLoadSec : 0;
210
+ const playlistLoadSec = isSwitch
211
+ ? timeToFirstByteSec + this.lastLevelLoadSec
212
+ : 0;
211
213
  return fragLoadSec + playlistLoadSec;
212
214
  }
213
215
 
214
216
  protected onLevelLoaded(event: Events.LEVEL_LOADED, data: LevelLoadedData) {
215
217
  const config = this.hls.config;
216
218
  const { loading } = data.stats;
217
- const timeLoadingMs = loading.end - loading.start;
219
+ const timeLoadingMs = loading.end - loading.first;
218
220
  if (Number.isFinite(timeLoadingMs)) {
219
221
  this.lastLevelLoadSec = timeLoadingMs / 1000;
220
222
  }
@@ -223,13 +225,16 @@ class AbrController extends Logger implements AbrComponentAPI {
223
225
  } else {
224
226
  this.bwEstimator.update(config.abrEwmaSlowVoD, config.abrEwmaFastVoD);
225
227
  }
228
+ if (this.timer > -1) {
229
+ this._abandonRulesCheck(data.levelInfo);
230
+ }
226
231
  }
227
232
 
228
233
  /*
229
234
  This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
230
235
  quickly enough to prevent underbuffering
231
236
  */
232
- private _abandonRulesCheck = () => {
237
+ private _abandonRulesCheck = (levelLoaded?: Level) => {
233
238
  const { fragCurrent: frag, partCurrent: part, hls } = this;
234
239
  const { autoLevelEnabled, media } = hls;
235
240
  if (!frag || !media) {
@@ -241,11 +246,13 @@ class AbrController extends Logger implements AbrComponentAPI {
241
246
  const duration = part ? part.duration : frag.duration;
242
247
  const timeLoading = now - stats.loading.start;
243
248
  const minAutoLevel = hls.minAutoLevel;
249
+ const loadingFragForLevel = frag.level;
250
+ const currentAutoLevel = this._nextAutoLevel;
244
251
  // If frag loading is aborted, complete, or from lowest level, stop timer and return
245
252
  if (
246
253
  stats.aborted ||
247
254
  (stats.loaded && stats.loaded === stats.total) ||
248
- frag.level <= minAutoLevel
255
+ loadingFragForLevel <= minAutoLevel
249
256
  ) {
250
257
  this.clearTimer();
251
258
  // reset forced auto level value so that next level will be selected
@@ -253,18 +260,24 @@ class AbrController extends Logger implements AbrComponentAPI {
253
260
  return;
254
261
  }
255
262
 
256
- // This check only runs if we're in ABR mode and actually playing
263
+ // This check only runs if we're in ABR mode
264
+ if (!autoLevelEnabled) {
265
+ return;
266
+ }
267
+
268
+ // Must be loading/loaded a new level or be in a playing state
269
+ const fragBlockingSwitch =
270
+ currentAutoLevel > -1 && currentAutoLevel !== loadingFragForLevel;
271
+ const levelChange = !!levelLoaded || fragBlockingSwitch;
257
272
  if (
258
- !autoLevelEnabled ||
259
- media.paused ||
260
- !media.playbackRate ||
261
- !media.readyState
273
+ !levelChange &&
274
+ (media.paused || !media.playbackRate || !media.readyState)
262
275
  ) {
263
276
  return;
264
277
  }
265
278
 
266
279
  const bufferInfo = hls.mainForwardBufferInfo;
267
- if (bufferInfo === null) {
280
+ if (!levelChange && bufferInfo === null) {
268
281
  return;
269
282
  }
270
283
 
@@ -279,14 +292,16 @@ class AbrController extends Logger implements AbrComponentAPI {
279
292
  }
280
293
 
281
294
  // bufferStarvationDelay is an estimate of the amount time (in seconds) it will take to exhaust the buffer
282
- const bufferStarvationDelay = bufferInfo.len / playbackRate;
295
+ const bufferStarvationDelay = bufferInfo
296
+ ? bufferInfo.len / playbackRate
297
+ : 0;
283
298
  const ttfb = stats.loading.first
284
299
  ? stats.loading.first - stats.loading.start
285
300
  : -1;
286
301
  const loadedFirstByte = stats.loaded && ttfb > -1;
287
302
  const bwEstimate: number = this.getBwEstimate();
288
303
  const levels = hls.levels;
289
- const level = levels[frag.level];
304
+ const level = levels[loadingFragForLevel];
290
305
  const expectedLen = Math.max(
291
306
  stats.loaded,
292
307
  Math.round((duration * (frag.bitrate || level.averageBitrate)) / 8),
@@ -309,13 +324,14 @@ class AbrController extends Logger implements AbrComponentAPI {
309
324
  }
310
325
 
311
326
  const bwe = loadRate ? loadRate * 8 : bwEstimate;
312
- const live = this.hls.latestLevelDetails?.live === true;
327
+ const live =
328
+ (levelLoaded?.details || this.hls.latestLevelDetails)?.live === true;
313
329
  const abrBandWidthUpFactor = this.hls.config.abrBandWidthUpFactor;
314
330
  let fragLevelNextLoadedDelay: number = Number.POSITIVE_INFINITY;
315
331
  let nextLoadLevel: number;
316
332
  // Iterate through lower level and try to find the largest one that avoids rebuffering
317
333
  for (
318
- nextLoadLevel = frag.level - 1;
334
+ nextLoadLevel = loadingFragForLevel - 1;
319
335
  nextLoadLevel > minAutoLevel;
320
336
  nextLoadLevel--
321
337
  ) {
@@ -375,7 +391,7 @@ class AbrController extends Logger implements AbrComponentAPI {
375
391
 
376
392
  this.warn(`Fragment ${frag.sn}${
377
393
  part ? ' part ' + part.index : ''
378
- } of level ${frag.level} is loading too slowly;
394
+ } of level ${loadingFragForLevel} is loading too slowly;
379
395
  Fragment duration: ${frag.duration.toFixed(3)}
380
396
  Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s
381
397
  Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s
@@ -392,7 +408,7 @@ class AbrController extends Logger implements AbrComponentAPI {
392
408
  hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel;
393
409
 
394
410
  this.clearTimer();
395
- this.timer = self.setInterval(() => {
411
+ const abortAndSwitch = () => {
396
412
  // Are nextLoadLevel details available or is stream-controller still in "WAITING_LEVEL" state?
397
413
  this.clearTimer();
398
414
  if (
@@ -424,7 +440,15 @@ class AbrController extends Logger implements AbrComponentAPI {
424
440
  this.resetEstimator(this.hls.levels[lowestSwitchLevel].bitrate);
425
441
  }
426
442
  }
427
- }, fragLevelNextLoadedDelay * 1000);
443
+ };
444
+ if (fragBlockingSwitch || fragLoadedDelay > fragLevelNextLoadedDelay * 2) {
445
+ abortAndSwitch();
446
+ } else {
447
+ this.timer = self.setInterval(
448
+ abortAndSwitch,
449
+ fragLevelNextLoadedDelay * 1000,
450
+ );
451
+ }
428
452
 
429
453
  hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { frag, part, stats });
430
454
  };
@@ -1335,7 +1335,7 @@ export default class BaseStreamController
1335
1335
  : liveSyncPosition) || frag.start
1336
1336
  : pos;
1337
1337
  this.log(
1338
- `Setting startPosition to ${startPosition} to match initial live edge. mainStart: ${mainStart} liveSyncPosition: ${liveSyncPosition} frag.start: ${frag?.start}`,
1338
+ `Setting startPosition to ${startPosition} to match start frag at live edge. mainStart: ${mainStart} liveSyncPosition: ${liveSyncPosition} frag.start: ${frag?.start}`,
1339
1339
  );
1340
1340
  this.startPosition = this.nextLoadPosition = startPosition;
1341
1341
  }
@@ -1677,6 +1677,9 @@ export default class BaseStreamController
1677
1677
  // Leave this.startPosition at -1, so that we can use `getInitialLiveFragment` logic when startPosition has
1678
1678
  // not been specified via the config or an as an argument to startLoad (#3736).
1679
1679
  startPosition = this.hls.liveSyncPosition || sliding;
1680
+ this.log(
1681
+ `Setting startPosition to -1 to start at live edge ${startPosition}`,
1682
+ );
1680
1683
  this.startPosition = -1;
1681
1684
  } else {
1682
1685
  this.log(`setting startPosition to 0 by default`);
@@ -503,7 +503,6 @@ export class FragmentTracker implements ComponentAPI {
503
503
 
504
504
  public removeFragment(fragment: Fragment) {
505
505
  const fragKey = getFragmentKey(fragment);
506
- fragment.stats.loaded = 0;
507
506
  fragment.clearElementaryStreamInfo();
508
507
  const activeParts = this.activePartLists[fragment.type];
509
508
  if (activeParts) {
@@ -1374,7 +1374,10 @@ MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
1374
1374
  if (
1375
1375
  !hls.loadingEnabled ||
1376
1376
  !hls.media ||
1377
- Math.abs(hls.media.currentTime - timelinePos) > 0.5
1377
+ Math.abs(
1378
+ (hls.mainForwardBufferInfo?.start || hls.media.currentTime) -
1379
+ timelinePos,
1380
+ ) > 0.5
1378
1381
  ) {
1379
1382
  hls.startLoad(timelinePos, skipSeekToStartPosition);
1380
1383
  } else if (!hls.bufferingEnabled) {
@@ -1784,21 +1787,21 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
1784
1787
  }
1785
1788
  const isInterstitial = this.isInterstitial(item);
1786
1789
  const bufferingPlayer = this.getBufferingPlayer();
1787
- const timeRemaining = bufferingPlayer
1788
- ? bufferingPlayer.remaining
1789
- : bufferingLast
1790
- ? bufferingLast.end - this.timelinePos
1791
- : 0;
1792
- this.log(
1793
- `buffered to boundary ${segmentToString(item)}` +
1794
- (bufferingLast ? ` (${timeRemaining.toFixed(2)} remaining)` : ''),
1795
- );
1796
1790
  this.bufferingItem = item;
1797
1791
  this.bufferedPos = Math.max(
1798
1792
  item.start,
1799
1793
  Math.min(item.end, this.timelinePos),
1800
1794
  );
1801
1795
  if (!this.playbackDisabled) {
1796
+ const timeRemaining = bufferingPlayer
1797
+ ? bufferingPlayer.remaining
1798
+ : bufferingLast
1799
+ ? bufferingLast.end - this.timelinePos
1800
+ : 0;
1801
+ this.log(
1802
+ `buffered to boundary ${segmentToString(item)}` +
1803
+ (bufferingLast ? ` (${timeRemaining.toFixed(2)} remaining)` : ''),
1804
+ );
1802
1805
  if (isInterstitial) {
1803
1806
  // primary fragment loading will exit early in base-stream-controller while `bufferingItem` is set to an Interstitial block
1804
1807
  item.event.assetList.forEach((asset) => {
@@ -733,11 +733,7 @@ export default class StreamController
733
733
  return;
734
734
  }
735
735
  const liveSyncPosition = this.hls.liveSyncPosition;
736
- const mediaCurrentTime = media.currentTime;
737
- const currentTime =
738
- this.hasEnoughToStart || mediaCurrentTime > 0
739
- ? mediaCurrentTime
740
- : this.startPosition;
736
+ const currentTime = this.getLoadPosition();
741
737
  const start = levelDetails.fragmentStart;
742
738
  const end = levelDetails.edge;
743
739
  const withinSlidingWindow =