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.js
CHANGED
@@ -785,6 +785,7 @@
|
|
785
785
|
Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
|
786
786
|
Events["MEDIA_DETACHED"] = "hlsMediaDetached";
|
787
787
|
Events["MEDIA_ENDED"] = "hlsMediaEnded";
|
788
|
+
Events["STALL_RESOLVED"] = "hlsStallResolved";
|
788
789
|
Events["BUFFER_RESET"] = "hlsBufferReset";
|
789
790
|
Events["BUFFER_CODECS"] = "hlsBufferCodecs";
|
790
791
|
Events["BUFFER_CREATED"] = "hlsBufferCreated";
|
@@ -1058,7 +1059,7 @@
|
|
1058
1059
|
// Some browsers don't allow to use bind on console object anyway
|
1059
1060
|
// fallback to default if needed
|
1060
1061
|
try {
|
1061
|
-
newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.
|
1062
|
+
newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10884");
|
1062
1063
|
} catch (e) {
|
1063
1064
|
/* log fn threw an exception. All logger methods are no-ops. */
|
1064
1065
|
return createLogger();
|
@@ -6521,8 +6522,7 @@
|
|
6521
6522
|
return {
|
6522
6523
|
len: 0,
|
6523
6524
|
start: pos,
|
6524
|
-
end: pos
|
6525
|
-
nextStart: undefined
|
6525
|
+
end: pos
|
6526
6526
|
};
|
6527
6527
|
};
|
6528
6528
|
BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) {
|
@@ -6587,7 +6587,8 @@
|
|
6587
6587
|
len: bufferLen,
|
6588
6588
|
start: bufferStart || 0,
|
6589
6589
|
end: bufferEnd || 0,
|
6590
|
-
nextStart: bufferStartNext
|
6590
|
+
nextStart: bufferStartNext,
|
6591
|
+
buffered: buffered
|
6591
6592
|
};
|
6592
6593
|
}
|
6593
6594
|
|
@@ -6986,8 +6987,6 @@
|
|
6986
6987
|
this.advancedDateTime = undefined;
|
6987
6988
|
this.updated = true;
|
6988
6989
|
this.advanced = true;
|
6989
|
-
this.availabilityDelay = undefined;
|
6990
|
-
// Manifest reload synchronization
|
6991
6990
|
this.misses = 0;
|
6992
6991
|
this.startCC = 0;
|
6993
6992
|
this.startSN = 0;
|
@@ -7040,7 +7039,6 @@
|
|
7040
7039
|
} else {
|
7041
7040
|
this.misses = previous.misses + 1;
|
7042
7041
|
}
|
7043
|
-
this.availabilityDelay = previous.availabilityDelay;
|
7044
7042
|
};
|
7045
7043
|
return _createClass(LevelDetails, [{
|
7046
7044
|
key: "hasProgramDateTime",
|
@@ -8042,7 +8040,7 @@
|
|
8042
8040
|
if (!level.live) {
|
8043
8041
|
lastFragment.endList = true;
|
8044
8042
|
}
|
8045
|
-
if (firstFragment &&
|
8043
|
+
if (firstFragment && level.startCC === undefined) {
|
8046
8044
|
level.startCC = firstFragment.cc;
|
8047
8045
|
}
|
8048
8046
|
/**
|
@@ -8332,15 +8330,16 @@
|
|
8332
8330
|
delete oldDetails.fragmentHint.endPTS;
|
8333
8331
|
}
|
8334
8332
|
// check if old/new playlists have fragments in common
|
8335
|
-
// loop through overlapping SN and update startPTS
|
8336
|
-
var ccOffset = 0;
|
8333
|
+
// loop through overlapping SN and update startPTS, cc, and duration if any found
|
8337
8334
|
var PTSFrag;
|
8338
|
-
mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag) {
|
8339
|
-
if (
|
8340
|
-
|
8341
|
-
|
8342
|
-
|
8343
|
-
|
8335
|
+
mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag, newFragIndex, newFragments) {
|
8336
|
+
if (newDetails.skippedSegments) {
|
8337
|
+
if (newFrag.cc !== oldFrag.cc) {
|
8338
|
+
var ccOffset = oldFrag.cc - newFrag.cc;
|
8339
|
+
for (var _i = newFragIndex; _i < newFragments.length; _i++) {
|
8340
|
+
newFragments[_i].cc += ccOffset;
|
8341
|
+
}
|
8342
|
+
}
|
8344
8343
|
}
|
8345
8344
|
if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) {
|
8346
8345
|
newFrag.setStart(newFrag.startPTS = oldFrag.startPTS);
|
@@ -8369,7 +8368,8 @@
|
|
8369
8368
|
currentInitSegment = oldFrag.initSegment;
|
8370
8369
|
}
|
8371
8370
|
});
|
8372
|
-
var
|
8371
|
+
var newFragments = newDetails.fragments;
|
8372
|
+
var fragmentsToCheck = newDetails.fragmentHint ? newFragments.concat(newDetails.fragmentHint) : newFragments;
|
8373
8373
|
if (currentInitSegment) {
|
8374
8374
|
fragmentsToCheck.forEach(function (frag) {
|
8375
8375
|
var _currentInitSegment;
|
@@ -8379,19 +8379,17 @@
|
|
8379
8379
|
});
|
8380
8380
|
}
|
8381
8381
|
if (newDetails.skippedSegments) {
|
8382
|
-
newDetails.deltaUpdateFailed =
|
8382
|
+
newDetails.deltaUpdateFailed = newFragments.some(function (frag) {
|
8383
8383
|
return !frag;
|
8384
8384
|
});
|
8385
8385
|
if (newDetails.deltaUpdateFailed) {
|
8386
8386
|
logger.warn('[level-helper] Previous playlist missing segments skipped in delta playlist');
|
8387
|
-
for (var
|
8388
|
-
|
8389
|
-
}
|
8390
|
-
newDetails.startSN = newDetails.fragments[0].sn;
|
8391
|
-
if (!newDetails.startCC) {
|
8392
|
-
newDetails.startCC = newDetails.fragments[0].cc;
|
8387
|
+
for (var _i2 = newDetails.skippedSegments; _i2--;) {
|
8388
|
+
newFragments.shift();
|
8393
8389
|
}
|
8390
|
+
newDetails.startSN = newFragments[0].sn;
|
8394
8391
|
} else {
|
8392
|
+
newDetails.endCC = newFragments[newFragments.length - 1].cc;
|
8395
8393
|
if (newDetails.canSkipDateRanges) {
|
8396
8394
|
newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails);
|
8397
8395
|
}
|
@@ -8399,25 +8397,15 @@
|
|
8399
8397
|
return frag.rawProgramDateTime;
|
8400
8398
|
});
|
8401
8399
|
if (oldDetails.hasProgramDateTime && !newDetails.hasProgramDateTime) {
|
8402
|
-
for (var
|
8403
|
-
if (fragmentsToCheck[
|
8404
|
-
assignProgramDateTime(fragmentsToCheck[
|
8400
|
+
for (var _i3 = 1; _i3 < fragmentsToCheck.length; _i3++) {
|
8401
|
+
if (fragmentsToCheck[_i3].programDateTime === null) {
|
8402
|
+
assignProgramDateTime(fragmentsToCheck[_i3], fragmentsToCheck[_i3 - 1], programDateTimes);
|
8405
8403
|
}
|
8406
8404
|
}
|
8407
8405
|
}
|
8408
8406
|
mapDateRanges(programDateTimes, newDetails);
|
8409
8407
|
}
|
8410
8408
|
}
|
8411
|
-
var newFragments = newDetails.fragments;
|
8412
|
-
if (ccOffset) {
|
8413
|
-
logger.warn('discontinuity sliding from playlist, take drift into account');
|
8414
|
-
for (var _i3 = 0; _i3 < newFragments.length; _i3++) {
|
8415
|
-
newFragments[_i3].cc += ccOffset;
|
8416
|
-
}
|
8417
|
-
}
|
8418
|
-
if (newDetails.skippedSegments) {
|
8419
|
-
newDetails.startCC = newDetails.fragments[0].cc;
|
8420
|
-
}
|
8421
8409
|
|
8422
8410
|
// Merge parts
|
8423
8411
|
mapPartIntersection(oldDetails.partList, newDetails.partList, function (oldPart, newPart) {
|
@@ -8513,7 +8501,7 @@
|
|
8513
8501
|
_newFrag = newDetails.fragments[i] = _oldFrag;
|
8514
8502
|
}
|
8515
8503
|
if (_oldFrag && _newFrag) {
|
8516
|
-
intersectionFn(_oldFrag, _newFrag);
|
8504
|
+
intersectionFn(_oldFrag, _newFrag, i, newFrags);
|
8517
8505
|
}
|
8518
8506
|
}
|
8519
8507
|
}
|
@@ -16320,7 +16308,7 @@
|
|
16320
16308
|
return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment;
|
16321
16309
|
}
|
16322
16310
|
|
16323
|
-
var version = "1.6.0-beta.2.0.canary.
|
16311
|
+
var version = "1.6.0-beta.2.0.canary.10884";
|
16324
16312
|
|
16325
16313
|
// ensure the worker ends up in the bundle
|
16326
16314
|
// If the worker should not be included this gets aliased to empty.js
|
@@ -30362,6 +30350,7 @@
|
|
30362
30350
|
progressive: false,
|
30363
30351
|
lowLatencyMode: true,
|
30364
30352
|
cmcd: undefined,
|
30353
|
+
detectStallWithCurrentTimeMs: 1250,
|
30365
30354
|
enableDateRangeMetadataCues: true,
|
30366
30355
|
enableEmsgMetadataCues: true,
|
30367
30356
|
enableEmsgKLVMetadata: false,
|
@@ -31787,25 +31776,23 @@
|
|
31787
31776
|
});
|
31788
31777
|
}
|
31789
31778
|
|
31790
|
-
var STALL_MINIMUM_DURATION_MS = 250;
|
31791
31779
|
var MAX_START_GAP_JUMP = 2.0;
|
31792
31780
|
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
31793
31781
|
var SKIP_BUFFER_RANGE_START = 0.05;
|
31794
31782
|
var GapController = /*#__PURE__*/function (_Logger) {
|
31795
|
-
function GapController(
|
31783
|
+
function GapController(media, fragmentTracker, hls) {
|
31796
31784
|
var _this;
|
31797
31785
|
_this = _Logger.call(this, 'gap-controller', hls.logger) || this;
|
31798
|
-
_this.config = undefined;
|
31799
31786
|
_this.media = null;
|
31800
|
-
_this.fragmentTracker =
|
31801
|
-
_this.hls =
|
31787
|
+
_this.fragmentTracker = null;
|
31788
|
+
_this.hls = null;
|
31802
31789
|
_this.nudgeRetry = 0;
|
31803
31790
|
_this.stallReported = false;
|
31804
31791
|
_this.stalled = null;
|
31805
31792
|
_this.moved = false;
|
31806
31793
|
_this.seeking = false;
|
31807
31794
|
_this.ended = 0;
|
31808
|
-
_this.
|
31795
|
+
_this.waiting = 0;
|
31809
31796
|
_this.media = media;
|
31810
31797
|
_this.fragmentTracker = fragmentTracker;
|
31811
31798
|
_this.hls = hls;
|
@@ -31814,9 +31801,7 @@
|
|
31814
31801
|
_inheritsLoose(GapController, _Logger);
|
31815
31802
|
var _proto = GapController.prototype;
|
31816
31803
|
_proto.destroy = function destroy() {
|
31817
|
-
this.media = null;
|
31818
|
-
// @ts-ignore
|
31819
|
-
this.hls = this.fragmentTracker = null;
|
31804
|
+
this.media = this.hls = this.fragmentTracker = null;
|
31820
31805
|
}
|
31821
31806
|
|
31822
31807
|
/**
|
@@ -31826,10 +31811,10 @@
|
|
31826
31811
|
* @param lastCurrentTime - Previously read playhead position
|
31827
31812
|
*/;
|
31828
31813
|
_proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
|
31829
|
-
var
|
31830
|
-
|
31814
|
+
var _this$hls;
|
31815
|
+
var media = this.media,
|
31831
31816
|
stalled = this.stalled;
|
31832
|
-
if (media
|
31817
|
+
if (!media) {
|
31833
31818
|
return;
|
31834
31819
|
}
|
31835
31820
|
var currentTime = media.currentTime,
|
@@ -31847,43 +31832,45 @@
|
|
31847
31832
|
if (!seeking) {
|
31848
31833
|
this.nudgeRetry = 0;
|
31849
31834
|
}
|
31850
|
-
if (
|
31851
|
-
|
31852
|
-
if (this.stallReported) {
|
31853
|
-
var _stalledDuration = self.performance.now() - stalled;
|
31854
|
-
this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
|
31855
|
-
this.stallReported = false;
|
31856
|
-
}
|
31857
|
-
this.stalled = null;
|
31835
|
+
if (this.waiting === 0) {
|
31836
|
+
this.stallResolved(currentTime);
|
31858
31837
|
}
|
31859
31838
|
return;
|
31860
31839
|
}
|
31861
31840
|
|
31862
31841
|
// Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
|
31863
31842
|
if (beginSeek || seeked) {
|
31864
|
-
|
31843
|
+
if (seeked) {
|
31844
|
+
this.stallResolved(currentTime);
|
31845
|
+
}
|
31865
31846
|
return;
|
31866
31847
|
}
|
31867
31848
|
|
31868
31849
|
// The playhead should not be moving
|
31869
|
-
if (media.paused && !seeking || media.ended || media.playbackRate === 0
|
31850
|
+
if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
|
31851
|
+
this.nudgeRetry = 0;
|
31852
|
+
this.stallResolved(currentTime);
|
31870
31853
|
// Fire MEDIA_ENDED to workaround event not being dispatched by browser
|
31871
|
-
if (!this.ended && media.ended) {
|
31854
|
+
if (!this.ended && media.ended && this.hls) {
|
31872
31855
|
this.ended = currentTime || 1;
|
31873
31856
|
this.hls.trigger(Events.MEDIA_ENDED, {
|
31874
31857
|
stalled: false
|
31875
31858
|
});
|
31876
31859
|
}
|
31860
|
+
return;
|
31861
|
+
}
|
31862
|
+
if (!BufferHelper.getBuffered(media).length) {
|
31877
31863
|
this.nudgeRetry = 0;
|
31878
31864
|
return;
|
31879
31865
|
}
|
31880
31866
|
var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
31881
31867
|
var nextStart = bufferInfo.nextStart || 0;
|
31882
|
-
|
31868
|
+
var fragmentTracker = this.fragmentTracker;
|
31869
|
+
if (seeking && fragmentTracker) {
|
31883
31870
|
// Waiting for seeking in a buffered range to complete
|
31884
31871
|
var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
|
31885
31872
|
// Next buffered range is too far ahead to jump to while still seeking
|
31886
|
-
var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !
|
31873
|
+
var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
|
31887
31874
|
if (hasEnoughBuffer || noBufferGap) {
|
31888
31875
|
return;
|
31889
31876
|
}
|
@@ -31893,7 +31880,7 @@
|
|
31893
31880
|
|
31894
31881
|
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
31895
31882
|
// The addition poll gives the browser a chance to jump the gap for us
|
31896
|
-
if (!this.moved && this.stalled !== null) {
|
31883
|
+
if (!this.moved && this.stalled !== null && fragmentTracker) {
|
31897
31884
|
// There is no playable buffer (seeked, waiting for buffer)
|
31898
31885
|
var isBuffered = bufferInfo.len > 0;
|
31899
31886
|
if (!isBuffered && !nextStart) {
|
@@ -31907,7 +31894,7 @@
|
|
31907
31894
|
// that begins over 1 target duration after the video start position.
|
31908
31895
|
var isLive = !!(levelDetails != null && levelDetails.live);
|
31909
31896
|
var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
|
31910
|
-
var partialOrGap =
|
31897
|
+
var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
|
31911
31898
|
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
31912
31899
|
if (!media.paused) {
|
31913
31900
|
this._trySkipBufferHole(partialOrGap);
|
@@ -31917,16 +31904,27 @@
|
|
31917
31904
|
}
|
31918
31905
|
|
31919
31906
|
// Start tracking stall time
|
31907
|
+
var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
|
31908
|
+
if (!config) {
|
31909
|
+
return;
|
31910
|
+
}
|
31911
|
+
var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
|
31920
31912
|
var tnow = self.performance.now();
|
31913
|
+
var tWaiting = this.waiting;
|
31921
31914
|
if (stalled === null) {
|
31922
|
-
|
31915
|
+
// Use time of recent "waiting" event
|
31916
|
+
if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
|
31917
|
+
this.stalled = tWaiting;
|
31918
|
+
} else {
|
31919
|
+
this.stalled = tnow;
|
31920
|
+
}
|
31923
31921
|
return;
|
31924
31922
|
}
|
31925
31923
|
var stalledDuration = tnow - stalled;
|
31926
|
-
if (!seeking && stalledDuration >=
|
31924
|
+
if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
|
31927
31925
|
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
31928
31926
|
if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
|
31929
|
-
if (
|
31927
|
+
if (this.ended) {
|
31930
31928
|
return;
|
31931
31929
|
}
|
31932
31930
|
this.ended = currentTime || 1;
|
@@ -31937,12 +31935,26 @@
|
|
31937
31935
|
}
|
31938
31936
|
// Report stalling after trying to fix
|
31939
31937
|
this._reportStall(bufferInfo);
|
31940
|
-
if (!this.media) {
|
31938
|
+
if (!this.media || !this.hls) {
|
31941
31939
|
return;
|
31942
31940
|
}
|
31943
31941
|
}
|
31944
31942
|
var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
|
31945
31943
|
this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
|
31944
|
+
};
|
31945
|
+
_proto.stallResolved = function stallResolved(currentTime) {
|
31946
|
+
var stalled = this.stalled;
|
31947
|
+
if (stalled && this.hls) {
|
31948
|
+
this.stalled = null;
|
31949
|
+
// The playhead is now moving, but was previously stalled
|
31950
|
+
if (this.stallReported) {
|
31951
|
+
var stalledDuration = self.performance.now() - stalled;
|
31952
|
+
this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
|
31953
|
+
this.stallReported = false;
|
31954
|
+
this.waiting = 0;
|
31955
|
+
this.hls.trigger(Events.STALL_RESOLVED, {});
|
31956
|
+
}
|
31957
|
+
}
|
31946
31958
|
}
|
31947
31959
|
|
31948
31960
|
/**
|
@@ -31952,10 +31964,11 @@
|
|
31952
31964
|
* @private
|
31953
31965
|
*/;
|
31954
31966
|
_proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
|
31955
|
-
var
|
31956
|
-
|
31967
|
+
var _this$hls2;
|
31968
|
+
var fragmentTracker = this.fragmentTracker,
|
31957
31969
|
media = this.media;
|
31958
|
-
|
31970
|
+
var config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
|
31971
|
+
if (!media || !fragmentTracker || !config) {
|
31959
31972
|
return;
|
31960
31973
|
}
|
31961
31974
|
var currentTime = media.currentTime;
|
@@ -31975,13 +31988,12 @@
|
|
31975
31988
|
// we may just have to "nudge" the playlist as the browser decoding/rendering engine
|
31976
31989
|
// needs to cross some sort of threshold covering all source-buffers content
|
31977
31990
|
// to start playing properly.
|
31978
|
-
|
31991
|
+
var bufferedRanges = bufferInfo.buffered;
|
31992
|
+
if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
|
31979
31993
|
this.warn('Trying to nudge playhead over buffer-hole');
|
31980
31994
|
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
31981
31995
|
// We only try to jump the hole if it's under the configured size
|
31982
|
-
|
31983
|
-
this.stalled = null;
|
31984
|
-
this._tryNudgeBuffer();
|
31996
|
+
this._tryNudgeBuffer(bufferInfo);
|
31985
31997
|
}
|
31986
31998
|
}
|
31987
31999
|
|
@@ -31993,8 +32005,9 @@
|
|
31993
32005
|
_proto._reportStall = function _reportStall(bufferInfo) {
|
31994
32006
|
var hls = this.hls,
|
31995
32007
|
media = this.media,
|
31996
|
-
stallReported = this.stallReported
|
31997
|
-
|
32008
|
+
stallReported = this.stallReported,
|
32009
|
+
stalled = this.stalled;
|
32010
|
+
if (!stallReported && stalled !== null && media && hls) {
|
31998
32011
|
// Report stalled error once
|
31999
32012
|
this.stallReported = true;
|
32000
32013
|
var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
|
@@ -32004,7 +32017,11 @@
|
|
32004
32017
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
32005
32018
|
fatal: false,
|
32006
32019
|
error: error,
|
32007
|
-
buffer: bufferInfo.len
|
32020
|
+
buffer: bufferInfo.len,
|
32021
|
+
bufferInfo: bufferInfo,
|
32022
|
+
stalled: {
|
32023
|
+
start: stalled
|
32024
|
+
}
|
32008
32025
|
});
|
32009
32026
|
}
|
32010
32027
|
}
|
@@ -32015,10 +32032,11 @@
|
|
32015
32032
|
* @private
|
32016
32033
|
*/;
|
32017
32034
|
_proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
|
32018
|
-
var
|
32019
|
-
|
32035
|
+
var _this$hls3;
|
32036
|
+
var fragmentTracker = this.fragmentTracker,
|
32020
32037
|
media = this.media;
|
32021
|
-
|
32038
|
+
var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
|
32039
|
+
if (!media || !fragmentTracker || !config) {
|
32022
32040
|
return 0;
|
32023
32041
|
}
|
32024
32042
|
|
@@ -32033,7 +32051,6 @@
|
|
32033
32051
|
if (gapLength > 0 && (bufferStarved || waiting)) {
|
32034
32052
|
// Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
|
32035
32053
|
if (gapLength > config.maxBufferHole) {
|
32036
|
-
var fragmentTracker = this.fragmentTracker;
|
32037
32054
|
var startGap = false;
|
32038
32055
|
if (currentTime === 0) {
|
32039
32056
|
var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
|
@@ -32064,17 +32081,18 @@
|
|
32064
32081
|
var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
32065
32082
|
this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
|
32066
32083
|
this.moved = true;
|
32067
|
-
this.stalled = null;
|
32068
32084
|
media.currentTime = targetTime;
|
32069
|
-
if (partial && !partial.gap) {
|
32085
|
+
if (partial && !partial.gap && this.hls) {
|
32070
32086
|
var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
|
32071
|
-
hls.trigger(Events.ERROR, {
|
32087
|
+
this.hls.trigger(Events.ERROR, {
|
32072
32088
|
type: ErrorTypes.MEDIA_ERROR,
|
32073
32089
|
details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
|
32074
32090
|
fatal: false,
|
32075
32091
|
error: error,
|
32076
32092
|
reason: error.message,
|
32077
|
-
frag: partial
|
32093
|
+
frag: partial,
|
32094
|
+
buffer: bufferInfo.len,
|
32095
|
+
bufferInfo: bufferInfo
|
32078
32096
|
});
|
32079
32097
|
}
|
32080
32098
|
return targetTime;
|
@@ -32087,13 +32105,13 @@
|
|
32087
32105
|
* Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
|
32088
32106
|
* @private
|
32089
32107
|
*/;
|
32090
|
-
_proto._tryNudgeBuffer = function _tryNudgeBuffer() {
|
32091
|
-
var
|
32092
|
-
hls = this.hls,
|
32108
|
+
_proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
|
32109
|
+
var hls = this.hls,
|
32093
32110
|
media = this.media,
|
32094
32111
|
nudgeRetry = this.nudgeRetry;
|
32095
|
-
|
32096
|
-
|
32112
|
+
var config = hls == null ? undefined : hls.config;
|
32113
|
+
if (!media || !config) {
|
32114
|
+
return 0;
|
32097
32115
|
}
|
32098
32116
|
var currentTime = media.currentTime;
|
32099
32117
|
this.nudgeRetry++;
|
@@ -32107,7 +32125,9 @@
|
|
32107
32125
|
type: ErrorTypes.MEDIA_ERROR,
|
32108
32126
|
details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
|
32109
32127
|
error: error,
|
32110
|
-
fatal: false
|
32128
|
+
fatal: false,
|
32129
|
+
buffer: bufferInfo.len,
|
32130
|
+
bufferInfo: bufferInfo
|
32111
32131
|
});
|
32112
32132
|
} else {
|
32113
32133
|
var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
|
@@ -32116,7 +32136,9 @@
|
|
32116
32136
|
type: ErrorTypes.MEDIA_ERROR,
|
32117
32137
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
32118
32138
|
error: _error,
|
32119
|
-
fatal: true
|
32139
|
+
fatal: true,
|
32140
|
+
buffer: bufferInfo.len,
|
32141
|
+
bufferInfo: bufferInfo
|
32120
32142
|
});
|
32121
32143
|
}
|
32122
32144
|
};
|
@@ -32172,11 +32194,18 @@
|
|
32172
32194
|
_this.backtrackFragment = null;
|
32173
32195
|
_this.audioCodecSwitch = false;
|
32174
32196
|
_this.videoBuffer = null;
|
32197
|
+
_this.onMediaWaiting = function () {
|
32198
|
+
var gapController = _this.gapController;
|
32199
|
+
if (gapController) {
|
32200
|
+
gapController.waiting = self.performance.now();
|
32201
|
+
}
|
32202
|
+
};
|
32175
32203
|
_this.onMediaPlaying = function () {
|
32176
32204
|
// tick to speed up FRAG_CHANGED triggering
|
32177
32205
|
var gapController = _this.gapController;
|
32178
32206
|
if (gapController) {
|
32179
32207
|
gapController.ended = 0;
|
32208
|
+
gapController.waiting = 0;
|
32180
32209
|
}
|
32181
32210
|
_this.tick();
|
32182
32211
|
};
|
@@ -32231,7 +32260,7 @@
|
|
32231
32260
|
};
|
32232
32261
|
_proto.onHandlerDestroying = function onHandlerDestroying() {
|
32233
32262
|
// @ts-ignore
|
32234
|
-
this.onMediaPlaying = this.onMediaSeeked = null;
|
32263
|
+
this.onMediaPlaying = this.onMediaSeeked = this.onMediaWaiting = null;
|
32235
32264
|
this.unregisterListeners();
|
32236
32265
|
_BaseStreamController.prototype.onHandlerDestroying.call(this);
|
32237
32266
|
};
|
@@ -32552,15 +32581,18 @@
|
|
32552
32581
|
var media = data.media;
|
32553
32582
|
media.removeEventListener('playing', this.onMediaPlaying);
|
32554
32583
|
media.removeEventListener('seeked', this.onMediaSeeked);
|
32584
|
+
media.removeEventListener('waiting', this.onMediaWaiting);
|
32555
32585
|
media.addEventListener('playing', this.onMediaPlaying);
|
32556
32586
|
media.addEventListener('seeked', this.onMediaSeeked);
|
32557
|
-
|
32587
|
+
media.addEventListener('waiting', this.onMediaWaiting);
|
32588
|
+
this.gapController = new GapController(media, this.fragmentTracker, this.hls);
|
32558
32589
|
};
|
32559
32590
|
_proto.onMediaDetaching = function onMediaDetaching(event, data) {
|
32560
32591
|
var media = this.media;
|
32561
32592
|
if (media) {
|
32562
32593
|
media.removeEventListener('playing', this.onMediaPlaying);
|
32563
32594
|
media.removeEventListener('seeked', this.onMediaSeeked);
|
32595
|
+
media.removeEventListener('waiting', this.onMediaWaiting);
|
32564
32596
|
}
|
32565
32597
|
this.videoBuffer = null;
|
32566
32598
|
this.fragPlaying = null;
|
@@ -32970,7 +33002,7 @@
|
|
32970
33002
|
var startPosition = this.startPosition;
|
32971
33003
|
// only adjust currentTime if different from startPosition or if startPosition not buffered
|
32972
33004
|
// at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
|
32973
|
-
if (startPosition >= 0) {
|
33005
|
+
if (startPosition >= 0 && currentTime < startPosition) {
|
32974
33006
|
if (media.seeking) {
|
32975
33007
|
this.log("could not seek to " + startPosition + ", already seeking at " + currentTime);
|
32976
33008
|
return;
|
package/dist/hls.js.d.ts
CHANGED
@@ -571,6 +571,19 @@ export declare type BufferInfo = {
|
|
571
571
|
start: number;
|
572
572
|
end: number;
|
573
573
|
nextStart?: number;
|
574
|
+
buffered?: BufferTimeRange[];
|
575
|
+
};
|
576
|
+
|
577
|
+
/**
|
578
|
+
* Provides methods dealing with buffer length retrieval for example.
|
579
|
+
*
|
580
|
+
* In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property.
|
581
|
+
*
|
582
|
+
* Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered
|
583
|
+
*/
|
584
|
+
export declare type BufferTimeRange = {
|
585
|
+
start: number;
|
586
|
+
end: number;
|
574
587
|
};
|
575
588
|
|
576
589
|
export declare class CapLevelController implements ComponentAPI {
|
@@ -1017,6 +1030,7 @@ export declare interface ErrorData {
|
|
1017
1030
|
fatal: boolean;
|
1018
1031
|
errorAction?: IErrorAction;
|
1019
1032
|
buffer?: number;
|
1033
|
+
bufferInfo?: BufferInfo;
|
1020
1034
|
bytes?: number;
|
1021
1035
|
chunkMeta?: ChunkMetadata;
|
1022
1036
|
context?: PlaylistLoaderContext;
|
@@ -1027,6 +1041,9 @@ export declare interface ErrorData {
|
|
1027
1041
|
levelRetry?: boolean;
|
1028
1042
|
loader?: Loader<LoaderContext>;
|
1029
1043
|
networkDetails?: any;
|
1044
|
+
stalled?: {
|
1045
|
+
start: number;
|
1046
|
+
};
|
1030
1047
|
stats?: LoaderStats;
|
1031
1048
|
mimeType?: string;
|
1032
1049
|
reason?: string;
|
@@ -1110,6 +1127,7 @@ export declare enum Events {
|
|
1110
1127
|
MEDIA_DETACHING = "hlsMediaDetaching",
|
1111
1128
|
MEDIA_DETACHED = "hlsMediaDetached",
|
1112
1129
|
MEDIA_ENDED = "hlsMediaEnded",
|
1130
|
+
STALL_RESOLVED = "hlsStallResolved",
|
1113
1131
|
BUFFER_RESET = "hlsBufferReset",
|
1114
1132
|
BUFFER_CODECS = "hlsBufferCodecs",
|
1115
1133
|
BUFFER_CREATED = "hlsBufferCreated",
|
@@ -1948,6 +1966,7 @@ export declare type HlsConfig = {
|
|
1948
1966
|
progressive: boolean;
|
1949
1967
|
lowLatencyMode: boolean;
|
1950
1968
|
primarySessionId?: string;
|
1969
|
+
detectStallWithCurrentTimeMs: number;
|
1951
1970
|
} & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig;
|
1952
1971
|
|
1953
1972
|
export declare interface HlsEventEmitter {
|
@@ -1969,6 +1988,7 @@ export declare interface HlsListeners {
|
|
1969
1988
|
[Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING, data: MediaDetachingData) => void;
|
1970
1989
|
[Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED, data: MediaDetachedData) => void;
|
1971
1990
|
[Events.MEDIA_ENDED]: (event: Events.MEDIA_ENDED, data: MediaEndedData) => void;
|
1991
|
+
[Events.STALL_RESOLVED]: (event: Events.STALL_RESOLVED, data: {}) => void;
|
1972
1992
|
[Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;
|
1973
1993
|
[Events.BUFFER_CODECS]: (event: Events.BUFFER_CODECS, data: BufferCodecsData) => void;
|
1974
1994
|
[Events.BUFFER_CREATED]: (event: Events.BUFFER_CREATED, data: BufferCreatedData) => void;
|
@@ -2558,7 +2578,6 @@ export declare class LevelDetails {
|
|
2558
2578
|
advancedDateTime?: number;
|
2559
2579
|
updated: boolean;
|
2560
2580
|
advanced: boolean;
|
2561
|
-
availabilityDelay?: number;
|
2562
2581
|
misses: number;
|
2563
2582
|
startCC: number;
|
2564
2583
|
startSN: number;
|
@@ -3340,6 +3359,7 @@ export declare class StreamController extends BaseStreamController implements Ne
|
|
3340
3359
|
protected flushMainBuffer(startOffset: number, endOffset: number): void;
|
3341
3360
|
protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
|
3342
3361
|
protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
|
3362
|
+
private onMediaWaiting;
|
3343
3363
|
private onMediaPlaying;
|
3344
3364
|
private onMediaSeeked;
|
3345
3365
|
protected triggerEnded(): void;
|