hls.js 1.6.0-beta.2.0.canary.10882 → 1.6.0-beta.2.0.canary.10884
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.d.mts +21 -1
- package/dist/hls.d.ts +21 -1
- package/dist/hls.js +130 -98
- package/dist/hls.js.d.ts +21 -1
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +130 -98
- 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 +123 -93
- 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 +123 -93
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +2 -0
- package/src/controller/gap-controller.ts +92 -58
- package/src/controller/stream-controller.ts +13 -3
- package/src/events.ts +3 -0
- package/src/hls.ts +2 -1
- package/src/loader/level-details.ts +0 -2
- package/src/loader/m3u8-parser.ts +1 -1
- package/src/types/events.ts +3 -0
- package/src/utils/buffer-helper.ts +5 -8
- package/src/utils/level-helper.ts +29 -35
package/dist/hls.light.mjs
CHANGED
@@ -77,6 +77,7 @@ let Events = /*#__PURE__*/function (Events) {
|
|
77
77
|
Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
|
78
78
|
Events["MEDIA_DETACHED"] = "hlsMediaDetached";
|
79
79
|
Events["MEDIA_ENDED"] = "hlsMediaEnded";
|
80
|
+
Events["STALL_RESOLVED"] = "hlsStallResolved";
|
80
81
|
Events["BUFFER_RESET"] = "hlsBufferReset";
|
81
82
|
Events["BUFFER_CODECS"] = "hlsBufferCodecs";
|
82
83
|
Events["BUFFER_CREATED"] = "hlsBufferCreated";
|
@@ -401,7 +402,7 @@ function enableLogs(debugConfig, context, id) {
|
|
401
402
|
// Some browsers don't allow to use bind on console object anyway
|
402
403
|
// fallback to default if needed
|
403
404
|
try {
|
404
|
-
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.2.0.canary.
|
405
|
+
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-beta.2.0.canary.10884"}`);
|
405
406
|
} catch (e) {
|
406
407
|
/* log fn threw an exception. All logger methods are no-ops. */
|
407
408
|
return createLogger();
|
@@ -3150,8 +3151,6 @@ class LevelDetails {
|
|
3150
3151
|
this.advancedDateTime = undefined;
|
3151
3152
|
this.updated = true;
|
3152
3153
|
this.advanced = true;
|
3153
|
-
this.availabilityDelay = undefined;
|
3154
|
-
// Manifest reload synchronization
|
3155
3154
|
this.misses = 0;
|
3156
3155
|
this.startCC = 0;
|
3157
3156
|
this.startSN = 0;
|
@@ -3203,7 +3202,6 @@ class LevelDetails {
|
|
3203
3202
|
} else {
|
3204
3203
|
this.misses = previous.misses + 1;
|
3205
3204
|
}
|
3206
|
-
this.availabilityDelay = previous.availabilityDelay;
|
3207
3205
|
}
|
3208
3206
|
get hasProgramDateTime() {
|
3209
3207
|
if (this.fragments.length) {
|
@@ -5041,7 +5039,7 @@ class M3U8Parser {
|
|
5041
5039
|
if (!level.live) {
|
5042
5040
|
lastFragment.endList = true;
|
5043
5041
|
}
|
5044
|
-
if (firstFragment &&
|
5042
|
+
if (firstFragment && level.startCC === undefined) {
|
5045
5043
|
level.startCC = firstFragment.cc;
|
5046
5044
|
}
|
5047
5045
|
/**
|
@@ -5324,15 +5322,16 @@ function mergeDetails(oldDetails, newDetails) {
|
|
5324
5322
|
delete oldDetails.fragmentHint.endPTS;
|
5325
5323
|
}
|
5326
5324
|
// check if old/new playlists have fragments in common
|
5327
|
-
// loop through overlapping SN and update startPTS
|
5328
|
-
let ccOffset = 0;
|
5325
|
+
// loop through overlapping SN and update startPTS, cc, and duration if any found
|
5329
5326
|
let PTSFrag;
|
5330
|
-
mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag) => {
|
5331
|
-
if (
|
5332
|
-
|
5333
|
-
|
5334
|
-
|
5335
|
-
|
5327
|
+
mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag, newFragIndex, newFragments) => {
|
5328
|
+
if (newDetails.skippedSegments) {
|
5329
|
+
if (newFrag.cc !== oldFrag.cc) {
|
5330
|
+
const ccOffset = oldFrag.cc - newFrag.cc;
|
5331
|
+
for (let i = newFragIndex; i < newFragments.length; i++) {
|
5332
|
+
newFragments[i].cc += ccOffset;
|
5333
|
+
}
|
5334
|
+
}
|
5336
5335
|
}
|
5337
5336
|
if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) {
|
5338
5337
|
newFrag.setStart(newFrag.startPTS = oldFrag.startPTS);
|
@@ -5361,7 +5360,8 @@ function mergeDetails(oldDetails, newDetails) {
|
|
5361
5360
|
currentInitSegment = oldFrag.initSegment;
|
5362
5361
|
}
|
5363
5362
|
});
|
5364
|
-
const
|
5363
|
+
const newFragments = newDetails.fragments;
|
5364
|
+
const fragmentsToCheck = newDetails.fragmentHint ? newFragments.concat(newDetails.fragmentHint) : newFragments;
|
5365
5365
|
if (currentInitSegment) {
|
5366
5366
|
fragmentsToCheck.forEach(frag => {
|
5367
5367
|
var _currentInitSegment;
|
@@ -5371,17 +5371,15 @@ function mergeDetails(oldDetails, newDetails) {
|
|
5371
5371
|
});
|
5372
5372
|
}
|
5373
5373
|
if (newDetails.skippedSegments) {
|
5374
|
-
newDetails.deltaUpdateFailed =
|
5374
|
+
newDetails.deltaUpdateFailed = newFragments.some(frag => !frag);
|
5375
5375
|
if (newDetails.deltaUpdateFailed) {
|
5376
5376
|
logger.warn('[level-helper] Previous playlist missing segments skipped in delta playlist');
|
5377
5377
|
for (let i = newDetails.skippedSegments; i--;) {
|
5378
|
-
|
5379
|
-
}
|
5380
|
-
newDetails.startSN = newDetails.fragments[0].sn;
|
5381
|
-
if (!newDetails.startCC) {
|
5382
|
-
newDetails.startCC = newDetails.fragments[0].cc;
|
5378
|
+
newFragments.shift();
|
5383
5379
|
}
|
5380
|
+
newDetails.startSN = newFragments[0].sn;
|
5384
5381
|
} else {
|
5382
|
+
newDetails.endCC = newFragments[newFragments.length - 1].cc;
|
5385
5383
|
if (newDetails.canSkipDateRanges) {
|
5386
5384
|
newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails);
|
5387
5385
|
}
|
@@ -5396,16 +5394,6 @@ function mergeDetails(oldDetails, newDetails) {
|
|
5396
5394
|
mapDateRanges(programDateTimes, newDetails);
|
5397
5395
|
}
|
5398
5396
|
}
|
5399
|
-
const newFragments = newDetails.fragments;
|
5400
|
-
if (ccOffset) {
|
5401
|
-
logger.warn('discontinuity sliding from playlist, take drift into account');
|
5402
|
-
for (let i = 0; i < newFragments.length; i++) {
|
5403
|
-
newFragments[i].cc += ccOffset;
|
5404
|
-
}
|
5405
|
-
}
|
5406
|
-
if (newDetails.skippedSegments) {
|
5407
|
-
newDetails.startCC = newDetails.fragments[0].cc;
|
5408
|
-
}
|
5409
5397
|
|
5410
5398
|
// Merge parts
|
5411
5399
|
mapPartIntersection(oldDetails.partList, newDetails.partList, (oldPart, newPart) => {
|
@@ -5503,7 +5491,7 @@ function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) {
|
|
5503
5491
|
newFrag = newDetails.fragments[i] = oldFrag;
|
5504
5492
|
}
|
5505
5493
|
if (oldFrag && newFrag) {
|
5506
|
-
intersectionFn(oldFrag, newFrag);
|
5494
|
+
intersectionFn(oldFrag, newFrag, i, newFrags);
|
5507
5495
|
}
|
5508
5496
|
}
|
5509
5497
|
}
|
@@ -7283,8 +7271,7 @@ class BufferHelper {
|
|
7283
7271
|
return {
|
7284
7272
|
len: 0,
|
7285
7273
|
start: pos,
|
7286
|
-
end: pos
|
7287
|
-
nextStart: undefined
|
7274
|
+
end: pos
|
7288
7275
|
};
|
7289
7276
|
}
|
7290
7277
|
static bufferedInfo(buffered, pos, maxHoleDuration) {
|
@@ -7347,7 +7334,8 @@ class BufferHelper {
|
|
7347
7334
|
len: bufferLen,
|
7348
7335
|
start: bufferStart || 0,
|
7349
7336
|
end: bufferEnd || 0,
|
7350
|
-
nextStart: bufferStartNext
|
7337
|
+
nextStart: bufferStartNext,
|
7338
|
+
buffered
|
7351
7339
|
};
|
7352
7340
|
}
|
7353
7341
|
|
@@ -17283,6 +17271,7 @@ const hlsDefaultConfig = _objectSpread2(_objectSpread2({
|
|
17283
17271
|
progressive: false,
|
17284
17272
|
lowLatencyMode: true,
|
17285
17273
|
cmcd: undefined,
|
17274
|
+
detectStallWithCurrentTimeMs: 1250,
|
17286
17275
|
enableDateRangeMetadataCues: true,
|
17287
17276
|
enableEmsgMetadataCues: true,
|
17288
17277
|
enableEmsgKLVMetadata: false,
|
@@ -18768,32 +18757,28 @@ function assignTrackIdsByGroup(tracks) {
|
|
18768
18757
|
});
|
18769
18758
|
}
|
18770
18759
|
|
18771
|
-
const STALL_MINIMUM_DURATION_MS = 250;
|
18772
18760
|
const MAX_START_GAP_JUMP = 2.0;
|
18773
18761
|
const SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
18774
18762
|
const SKIP_BUFFER_RANGE_START = 0.05;
|
18775
18763
|
class GapController extends Logger {
|
18776
|
-
constructor(
|
18764
|
+
constructor(media, fragmentTracker, hls) {
|
18777
18765
|
super('gap-controller', hls.logger);
|
18778
|
-
this.config = undefined;
|
18779
18766
|
this.media = null;
|
18780
|
-
this.fragmentTracker =
|
18781
|
-
this.hls =
|
18767
|
+
this.fragmentTracker = null;
|
18768
|
+
this.hls = null;
|
18782
18769
|
this.nudgeRetry = 0;
|
18783
18770
|
this.stallReported = false;
|
18784
18771
|
this.stalled = null;
|
18785
18772
|
this.moved = false;
|
18786
18773
|
this.seeking = false;
|
18787
18774
|
this.ended = 0;
|
18788
|
-
this.
|
18775
|
+
this.waiting = 0;
|
18789
18776
|
this.media = media;
|
18790
18777
|
this.fragmentTracker = fragmentTracker;
|
18791
18778
|
this.hls = hls;
|
18792
18779
|
}
|
18793
18780
|
destroy() {
|
18794
|
-
this.media = null;
|
18795
|
-
// @ts-ignore
|
18796
|
-
this.hls = this.fragmentTracker = null;
|
18781
|
+
this.media = this.hls = this.fragmentTracker = null;
|
18797
18782
|
}
|
18798
18783
|
|
18799
18784
|
/**
|
@@ -18803,12 +18788,12 @@ class GapController extends Logger {
|
|
18803
18788
|
* @param lastCurrentTime - Previously read playhead position
|
18804
18789
|
*/
|
18805
18790
|
poll(lastCurrentTime, activeFrag, levelDetails, state) {
|
18791
|
+
var _this$hls;
|
18806
18792
|
const {
|
18807
|
-
config,
|
18808
18793
|
media,
|
18809
18794
|
stalled
|
18810
18795
|
} = this;
|
18811
|
-
if (media
|
18796
|
+
if (!media) {
|
18812
18797
|
return;
|
18813
18798
|
}
|
18814
18799
|
const {
|
@@ -18828,43 +18813,45 @@ class GapController extends Logger {
|
|
18828
18813
|
if (!seeking) {
|
18829
18814
|
this.nudgeRetry = 0;
|
18830
18815
|
}
|
18831
|
-
if (
|
18832
|
-
|
18833
|
-
if (this.stallReported) {
|
18834
|
-
const _stalledDuration = self.performance.now() - stalled;
|
18835
|
-
this.warn(`playback not stuck anymore @${currentTime}, after ${Math.round(_stalledDuration)}ms`);
|
18836
|
-
this.stallReported = false;
|
18837
|
-
}
|
18838
|
-
this.stalled = null;
|
18816
|
+
if (this.waiting === 0) {
|
18817
|
+
this.stallResolved(currentTime);
|
18839
18818
|
}
|
18840
18819
|
return;
|
18841
18820
|
}
|
18842
18821
|
|
18843
18822
|
// Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
|
18844
18823
|
if (beginSeek || seeked) {
|
18845
|
-
|
18824
|
+
if (seeked) {
|
18825
|
+
this.stallResolved(currentTime);
|
18826
|
+
}
|
18846
18827
|
return;
|
18847
18828
|
}
|
18848
18829
|
|
18849
18830
|
// The playhead should not be moving
|
18850
|
-
if (media.paused && !seeking || media.ended || media.playbackRate === 0
|
18831
|
+
if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
|
18832
|
+
this.nudgeRetry = 0;
|
18833
|
+
this.stallResolved(currentTime);
|
18851
18834
|
// Fire MEDIA_ENDED to workaround event not being dispatched by browser
|
18852
|
-
if (!this.ended && media.ended) {
|
18835
|
+
if (!this.ended && media.ended && this.hls) {
|
18853
18836
|
this.ended = currentTime || 1;
|
18854
18837
|
this.hls.trigger(Events.MEDIA_ENDED, {
|
18855
18838
|
stalled: false
|
18856
18839
|
});
|
18857
18840
|
}
|
18841
|
+
return;
|
18842
|
+
}
|
18843
|
+
if (!BufferHelper.getBuffered(media).length) {
|
18858
18844
|
this.nudgeRetry = 0;
|
18859
18845
|
return;
|
18860
18846
|
}
|
18861
18847
|
const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
18862
18848
|
const nextStart = bufferInfo.nextStart || 0;
|
18863
|
-
|
18849
|
+
const fragmentTracker = this.fragmentTracker;
|
18850
|
+
if (seeking && fragmentTracker) {
|
18864
18851
|
// Waiting for seeking in a buffered range to complete
|
18865
18852
|
const hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
|
18866
18853
|
// Next buffered range is too far ahead to jump to while still seeking
|
18867
|
-
const noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !
|
18854
|
+
const noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
|
18868
18855
|
if (hasEnoughBuffer || noBufferGap) {
|
18869
18856
|
return;
|
18870
18857
|
}
|
@@ -18874,7 +18861,7 @@ class GapController extends Logger {
|
|
18874
18861
|
|
18875
18862
|
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
18876
18863
|
// The addition poll gives the browser a chance to jump the gap for us
|
18877
|
-
if (!this.moved && this.stalled !== null) {
|
18864
|
+
if (!this.moved && this.stalled !== null && fragmentTracker) {
|
18878
18865
|
// There is no playable buffer (seeked, waiting for buffer)
|
18879
18866
|
const isBuffered = bufferInfo.len > 0;
|
18880
18867
|
if (!isBuffered && !nextStart) {
|
@@ -18888,7 +18875,7 @@ class GapController extends Logger {
|
|
18888
18875
|
// that begins over 1 target duration after the video start position.
|
18889
18876
|
const isLive = !!(levelDetails != null && levelDetails.live);
|
18890
18877
|
const maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
|
18891
|
-
const partialOrGap =
|
18878
|
+
const partialOrGap = fragmentTracker.getPartialFragment(currentTime);
|
18892
18879
|
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
18893
18880
|
if (!media.paused) {
|
18894
18881
|
this._trySkipBufferHole(partialOrGap);
|
@@ -18898,16 +18885,27 @@ class GapController extends Logger {
|
|
18898
18885
|
}
|
18899
18886
|
|
18900
18887
|
// Start tracking stall time
|
18888
|
+
const config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
|
18889
|
+
if (!config) {
|
18890
|
+
return;
|
18891
|
+
}
|
18892
|
+
const detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
|
18901
18893
|
const tnow = self.performance.now();
|
18894
|
+
const tWaiting = this.waiting;
|
18902
18895
|
if (stalled === null) {
|
18903
|
-
|
18896
|
+
// Use time of recent "waiting" event
|
18897
|
+
if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
|
18898
|
+
this.stalled = tWaiting;
|
18899
|
+
} else {
|
18900
|
+
this.stalled = tnow;
|
18901
|
+
}
|
18904
18902
|
return;
|
18905
18903
|
}
|
18906
18904
|
const stalledDuration = tnow - stalled;
|
18907
|
-
if (!seeking && stalledDuration >=
|
18905
|
+
if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
|
18908
18906
|
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
18909
18907
|
if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
|
18910
|
-
if (
|
18908
|
+
if (this.ended) {
|
18911
18909
|
return;
|
18912
18910
|
}
|
18913
18911
|
this.ended = currentTime || 1;
|
@@ -18918,13 +18916,27 @@ class GapController extends Logger {
|
|
18918
18916
|
}
|
18919
18917
|
// Report stalling after trying to fix
|
18920
18918
|
this._reportStall(bufferInfo);
|
18921
|
-
if (!this.media) {
|
18919
|
+
if (!this.media || !this.hls) {
|
18922
18920
|
return;
|
18923
18921
|
}
|
18924
18922
|
}
|
18925
18923
|
const bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
|
18926
18924
|
this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
|
18927
18925
|
}
|
18926
|
+
stallResolved(currentTime) {
|
18927
|
+
const stalled = this.stalled;
|
18928
|
+
if (stalled && this.hls) {
|
18929
|
+
this.stalled = null;
|
18930
|
+
// The playhead is now moving, but was previously stalled
|
18931
|
+
if (this.stallReported) {
|
18932
|
+
const stalledDuration = self.performance.now() - stalled;
|
18933
|
+
this.warn(`playback not stuck anymore @${currentTime}, after ${Math.round(stalledDuration)}ms`);
|
18934
|
+
this.stallReported = false;
|
18935
|
+
this.waiting = 0;
|
18936
|
+
this.hls.trigger(Events.STALL_RESOLVED, {});
|
18937
|
+
}
|
18938
|
+
}
|
18939
|
+
}
|
18928
18940
|
|
18929
18941
|
/**
|
18930
18942
|
* Detects and attempts to fix known buffer stalling issues.
|
@@ -18933,12 +18945,13 @@ class GapController extends Logger {
|
|
18933
18945
|
* @private
|
18934
18946
|
*/
|
18935
18947
|
_tryFixBufferStall(bufferInfo, stalledDurationMs) {
|
18948
|
+
var _this$hls2;
|
18936
18949
|
const {
|
18937
|
-
config,
|
18938
18950
|
fragmentTracker,
|
18939
18951
|
media
|
18940
18952
|
} = this;
|
18941
|
-
|
18953
|
+
const config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
|
18954
|
+
if (!media || !fragmentTracker || !config) {
|
18942
18955
|
return;
|
18943
18956
|
}
|
18944
18957
|
const currentTime = media.currentTime;
|
@@ -18958,13 +18971,12 @@ class GapController extends Logger {
|
|
18958
18971
|
// we may just have to "nudge" the playlist as the browser decoding/rendering engine
|
18959
18972
|
// needs to cross some sort of threshold covering all source-buffers content
|
18960
18973
|
// to start playing properly.
|
18961
|
-
|
18974
|
+
const bufferedRanges = bufferInfo.buffered;
|
18975
|
+
if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
|
18962
18976
|
this.warn('Trying to nudge playhead over buffer-hole');
|
18963
18977
|
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
18964
18978
|
// We only try to jump the hole if it's under the configured size
|
18965
|
-
|
18966
|
-
this.stalled = null;
|
18967
|
-
this._tryNudgeBuffer();
|
18979
|
+
this._tryNudgeBuffer(bufferInfo);
|
18968
18980
|
}
|
18969
18981
|
}
|
18970
18982
|
|
@@ -18977,9 +18989,10 @@ class GapController extends Logger {
|
|
18977
18989
|
const {
|
18978
18990
|
hls,
|
18979
18991
|
media,
|
18980
|
-
stallReported
|
18992
|
+
stallReported,
|
18993
|
+
stalled
|
18981
18994
|
} = this;
|
18982
|
-
if (!stallReported && media) {
|
18995
|
+
if (!stallReported && stalled !== null && media && hls) {
|
18983
18996
|
// Report stalled error once
|
18984
18997
|
this.stallReported = true;
|
18985
18998
|
const error = new Error(`Playback stalling at @${media.currentTime} due to low buffer (${JSON.stringify(bufferInfo)})`);
|
@@ -18989,7 +19002,11 @@ class GapController extends Logger {
|
|
18989
19002
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
18990
19003
|
fatal: false,
|
18991
19004
|
error,
|
18992
|
-
buffer: bufferInfo.len
|
19005
|
+
buffer: bufferInfo.len,
|
19006
|
+
bufferInfo,
|
19007
|
+
stalled: {
|
19008
|
+
start: stalled
|
19009
|
+
}
|
18993
19010
|
});
|
18994
19011
|
}
|
18995
19012
|
}
|
@@ -19000,12 +19017,13 @@ class GapController extends Logger {
|
|
19000
19017
|
* @private
|
19001
19018
|
*/
|
19002
19019
|
_trySkipBufferHole(partial) {
|
19020
|
+
var _this$hls3;
|
19003
19021
|
const {
|
19004
|
-
|
19005
|
-
hls,
|
19022
|
+
fragmentTracker,
|
19006
19023
|
media
|
19007
19024
|
} = this;
|
19008
|
-
|
19025
|
+
const config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
|
19026
|
+
if (!media || !fragmentTracker || !config) {
|
19009
19027
|
return 0;
|
19010
19028
|
}
|
19011
19029
|
|
@@ -19020,9 +19038,6 @@ class GapController extends Logger {
|
|
19020
19038
|
if (gapLength > 0 && (bufferStarved || waiting)) {
|
19021
19039
|
// Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
|
19022
19040
|
if (gapLength > config.maxBufferHole) {
|
19023
|
-
const {
|
19024
|
-
fragmentTracker
|
19025
|
-
} = this;
|
19026
19041
|
let startGap = false;
|
19027
19042
|
if (currentTime === 0) {
|
19028
19043
|
const startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
|
@@ -19053,17 +19068,18 @@ class GapController extends Logger {
|
|
19053
19068
|
const targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
19054
19069
|
this.warn(`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`);
|
19055
19070
|
this.moved = true;
|
19056
|
-
this.stalled = null;
|
19057
19071
|
media.currentTime = targetTime;
|
19058
|
-
if (partial && !partial.gap) {
|
19072
|
+
if (partial && !partial.gap && this.hls) {
|
19059
19073
|
const error = new Error(`fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`);
|
19060
|
-
hls.trigger(Events.ERROR, {
|
19074
|
+
this.hls.trigger(Events.ERROR, {
|
19061
19075
|
type: ErrorTypes.MEDIA_ERROR,
|
19062
19076
|
details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
|
19063
19077
|
fatal: false,
|
19064
19078
|
error,
|
19065
19079
|
reason: error.message,
|
19066
|
-
frag: partial
|
19080
|
+
frag: partial,
|
19081
|
+
buffer: bufferInfo.len,
|
19082
|
+
bufferInfo
|
19067
19083
|
});
|
19068
19084
|
}
|
19069
19085
|
return targetTime;
|
@@ -19076,15 +19092,15 @@ class GapController extends Logger {
|
|
19076
19092
|
* Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
|
19077
19093
|
* @private
|
19078
19094
|
*/
|
19079
|
-
_tryNudgeBuffer() {
|
19095
|
+
_tryNudgeBuffer(bufferInfo) {
|
19080
19096
|
const {
|
19081
|
-
config,
|
19082
19097
|
hls,
|
19083
19098
|
media,
|
19084
19099
|
nudgeRetry
|
19085
19100
|
} = this;
|
19086
|
-
|
19087
|
-
|
19101
|
+
const config = hls == null ? undefined : hls.config;
|
19102
|
+
if (!media || !config) {
|
19103
|
+
return 0;
|
19088
19104
|
}
|
19089
19105
|
const currentTime = media.currentTime;
|
19090
19106
|
this.nudgeRetry++;
|
@@ -19098,7 +19114,9 @@ class GapController extends Logger {
|
|
19098
19114
|
type: ErrorTypes.MEDIA_ERROR,
|
19099
19115
|
details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
|
19100
19116
|
error,
|
19101
|
-
fatal: false
|
19117
|
+
fatal: false,
|
19118
|
+
buffer: bufferInfo.len,
|
19119
|
+
bufferInfo
|
19102
19120
|
});
|
19103
19121
|
} else {
|
19104
19122
|
const error = new Error(`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`);
|
@@ -19107,13 +19125,15 @@ class GapController extends Logger {
|
|
19107
19125
|
type: ErrorTypes.MEDIA_ERROR,
|
19108
19126
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
19109
19127
|
error,
|
19110
|
-
fatal: true
|
19128
|
+
fatal: true,
|
19129
|
+
buffer: bufferInfo.len,
|
19130
|
+
bufferInfo
|
19111
19131
|
});
|
19112
19132
|
}
|
19113
19133
|
}
|
19114
19134
|
}
|
19115
19135
|
|
19116
|
-
const version = "1.6.0-beta.2.0.canary.
|
19136
|
+
const version = "1.6.0-beta.2.0.canary.10884";
|
19117
19137
|
|
19118
19138
|
// ensure the worker ends up in the bundle
|
19119
19139
|
// If the worker should not be included this gets aliased to empty.js
|
@@ -19540,11 +19560,18 @@ class StreamController extends BaseStreamController {
|
|
19540
19560
|
this.backtrackFragment = null;
|
19541
19561
|
this.audioCodecSwitch = false;
|
19542
19562
|
this.videoBuffer = null;
|
19563
|
+
this.onMediaWaiting = () => {
|
19564
|
+
const gapController = this.gapController;
|
19565
|
+
if (gapController) {
|
19566
|
+
gapController.waiting = self.performance.now();
|
19567
|
+
}
|
19568
|
+
};
|
19543
19569
|
this.onMediaPlaying = () => {
|
19544
19570
|
// tick to speed up FRAG_CHANGED triggering
|
19545
19571
|
const gapController = this.gapController;
|
19546
19572
|
if (gapController) {
|
19547
19573
|
gapController.ended = 0;
|
19574
|
+
gapController.waiting = 0;
|
19548
19575
|
}
|
19549
19576
|
this.tick();
|
19550
19577
|
};
|
@@ -19600,7 +19627,7 @@ class StreamController extends BaseStreamController {
|
|
19600
19627
|
}
|
19601
19628
|
onHandlerDestroying() {
|
19602
19629
|
// @ts-ignore
|
19603
|
-
this.onMediaPlaying = this.onMediaSeeked = null;
|
19630
|
+
this.onMediaPlaying = this.onMediaSeeked = this.onMediaWaiting = null;
|
19604
19631
|
this.unregisterListeners();
|
19605
19632
|
super.onHandlerDestroying();
|
19606
19633
|
}
|
@@ -19931,9 +19958,11 @@ class StreamController extends BaseStreamController {
|
|
19931
19958
|
const media = data.media;
|
19932
19959
|
media.removeEventListener('playing', this.onMediaPlaying);
|
19933
19960
|
media.removeEventListener('seeked', this.onMediaSeeked);
|
19961
|
+
media.removeEventListener('waiting', this.onMediaWaiting);
|
19934
19962
|
media.addEventListener('playing', this.onMediaPlaying);
|
19935
19963
|
media.addEventListener('seeked', this.onMediaSeeked);
|
19936
|
-
|
19964
|
+
media.addEventListener('waiting', this.onMediaWaiting);
|
19965
|
+
this.gapController = new GapController(media, this.fragmentTracker, this.hls);
|
19937
19966
|
}
|
19938
19967
|
onMediaDetaching(event, data) {
|
19939
19968
|
const {
|
@@ -19942,6 +19971,7 @@ class StreamController extends BaseStreamController {
|
|
19942
19971
|
if (media) {
|
19943
19972
|
media.removeEventListener('playing', this.onMediaPlaying);
|
19944
19973
|
media.removeEventListener('seeked', this.onMediaSeeked);
|
19974
|
+
media.removeEventListener('waiting', this.onMediaWaiting);
|
19945
19975
|
}
|
19946
19976
|
this.videoBuffer = null;
|
19947
19977
|
this.fragPlaying = null;
|
@@ -20369,7 +20399,7 @@ class StreamController extends BaseStreamController {
|
|
20369
20399
|
let startPosition = this.startPosition;
|
20370
20400
|
// only adjust currentTime if different from startPosition or if startPosition not buffered
|
20371
20401
|
// at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
|
20372
|
-
if (startPosition >= 0) {
|
20402
|
+
if (startPosition >= 0 && currentTime < startPosition) {
|
20373
20403
|
if (media.seeking) {
|
20374
20404
|
this.log(`could not seek to ${startPosition}, already seeking at ${currentTime}`);
|
20375
20405
|
return;
|