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.
- package/dist/hls.js +46 -29
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +42 -26
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +39 -23
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +43 -26
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/package.json +1 -1
- package/src/controller/abr-controller.ts +41 -17
- package/src/controller/base-stream-controller.ts +4 -1
- package/src/controller/fragment-tracker.ts +0 -1
- package/src/controller/interstitials-controller.ts +13 -10
- package/src/controller/stream-controller.ts +1 -5
@@ -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
|
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.
|
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
|
-
|
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
|
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
|
-
!
|
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
|
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[
|
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 =
|
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 =
|
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 ${
|
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
|
-
|
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
|
-
}
|
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
|
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(
|
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
|
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 =
|