hls.js 1.6.0-beta.2.0.canary.10924 → 1.6.0-beta.2.0.canary.10925
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 +60 -29
- package/dist/hls.d.ts +60 -29
- package/dist/hls.js +646 -488
- package/dist/hls.js.d.ts +60 -29
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +3855 -3696
- 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 +1650 -1490
- 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 +650 -491
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/package.json +1 -1
- package/src/config.ts +15 -9
- package/src/controller/base-stream-controller.ts +12 -8
- package/src/controller/buffer-controller.ts +19 -22
- package/src/controller/gap-controller.ts +257 -35
- package/src/controller/interstitials-controller.ts +13 -10
- package/src/controller/stream-controller.ts +26 -73
- package/src/hls.ts +47 -3
- package/src/utils/buffer-helper.ts +35 -13
- package/src/utils/event-listener-helper.ts +16 -0
package/dist/hls.js
CHANGED
@@ -1059,7 +1059,7 @@
|
|
1059
1059
|
// Some browsers don't allow to use bind on console object anyway
|
1060
1060
|
// fallback to default if needed
|
1061
1061
|
try {
|
1062
|
-
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.10925");
|
1063
1063
|
} catch (e) {
|
1064
1064
|
/* log fn threw an exception. All logger methods are no-ops. */
|
1065
1065
|
return createLogger();
|
@@ -6504,38 +6504,55 @@
|
|
6504
6504
|
}
|
6505
6505
|
return false;
|
6506
6506
|
};
|
6507
|
+
BufferHelper.bufferedRanges = function bufferedRanges(media) {
|
6508
|
+
if (media) {
|
6509
|
+
var timeRanges = BufferHelper.getBuffered(media);
|
6510
|
+
return BufferHelper.timeRangesToArray(timeRanges);
|
6511
|
+
}
|
6512
|
+
return [];
|
6513
|
+
};
|
6514
|
+
BufferHelper.timeRangesToArray = function timeRangesToArray(timeRanges) {
|
6515
|
+
var buffered = [];
|
6516
|
+
for (var i = 0; i < timeRanges.length; i++) {
|
6517
|
+
buffered.push({
|
6518
|
+
start: timeRanges.start(i),
|
6519
|
+
end: timeRanges.end(i)
|
6520
|
+
});
|
6521
|
+
}
|
6522
|
+
return buffered;
|
6523
|
+
};
|
6507
6524
|
BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) {
|
6508
6525
|
if (media) {
|
6509
|
-
var
|
6510
|
-
if (
|
6511
|
-
var buffered = [];
|
6512
|
-
for (var i = 0; i < vbuffered.length; i++) {
|
6513
|
-
buffered.push({
|
6514
|
-
start: vbuffered.start(i),
|
6515
|
-
end: vbuffered.end(i)
|
6516
|
-
});
|
6517
|
-
}
|
6526
|
+
var buffered = BufferHelper.bufferedRanges(media);
|
6527
|
+
if (buffered.length) {
|
6518
6528
|
return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration);
|
6519
6529
|
}
|
6520
6530
|
}
|
6521
6531
|
return {
|
6522
6532
|
len: 0,
|
6523
6533
|
start: pos,
|
6524
|
-
end: pos
|
6534
|
+
end: pos,
|
6535
|
+
bufferedIndex: -1
|
6525
6536
|
};
|
6526
6537
|
};
|
6527
6538
|
BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) {
|
6528
6539
|
pos = Math.max(0, pos);
|
6529
6540
|
// sort on buffer.start/smaller end (IE does not always return sorted buffered range)
|
6530
|
-
buffered.
|
6531
|
-
|
6532
|
-
|
6541
|
+
if (buffered.length > 1) {
|
6542
|
+
buffered.sort(function (a, b) {
|
6543
|
+
return a.start - b.start || b.end - a.end;
|
6544
|
+
});
|
6545
|
+
}
|
6546
|
+
var bufferedIndex = -1;
|
6533
6547
|
var buffered2 = [];
|
6534
6548
|
if (maxHoleDuration) {
|
6535
6549
|
// there might be some small holes between buffer time range
|
6536
6550
|
// consider that holes smaller than maxHoleDuration are irrelevant and build another
|
6537
6551
|
// buffer time range representations that discards those holes
|
6538
6552
|
for (var i = 0; i < buffered.length; i++) {
|
6553
|
+
if (pos >= buffered[i].start && pos <= buffered[i].end) {
|
6554
|
+
bufferedIndex = i;
|
6555
|
+
}
|
6539
6556
|
var buf2len = buffered2.length;
|
6540
6557
|
if (buf2len) {
|
6541
6558
|
var buf2end = buffered2[buf2len - 1].end;
|
@@ -6561,24 +6578,25 @@
|
|
6561
6578
|
buffered2 = buffered;
|
6562
6579
|
}
|
6563
6580
|
var bufferLen = 0;
|
6581
|
+
var nextStart;
|
6564
6582
|
|
6565
|
-
//
|
6566
|
-
var bufferStartNext;
|
6567
|
-
|
6568
|
-
// bufferStart and bufferEnd are buffer boundaries around current video position
|
6583
|
+
// bufferStart and bufferEnd are buffer boundaries around current playback position (pos)
|
6569
6584
|
var bufferStart = pos;
|
6570
6585
|
var bufferEnd = pos;
|
6571
6586
|
for (var _i = 0; _i < buffered2.length; _i++) {
|
6572
6587
|
var start = buffered2[_i].start;
|
6573
6588
|
var end = buffered2[_i].end;
|
6574
6589
|
// logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));
|
6590
|
+
if (bufferedIndex === -1 && pos >= start && pos <= end) {
|
6591
|
+
bufferedIndex = _i;
|
6592
|
+
}
|
6575
6593
|
if (pos + maxHoleDuration >= start && pos < end) {
|
6576
6594
|
// play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length
|
6577
6595
|
bufferStart = start;
|
6578
6596
|
bufferEnd = end;
|
6579
6597
|
bufferLen = bufferEnd - pos;
|
6580
6598
|
} else if (pos + maxHoleDuration < start) {
|
6581
|
-
|
6599
|
+
nextStart = start;
|
6582
6600
|
break;
|
6583
6601
|
}
|
6584
6602
|
}
|
@@ -6586,8 +6604,9 @@
|
|
6586
6604
|
len: bufferLen,
|
6587
6605
|
start: bufferStart || 0,
|
6588
6606
|
end: bufferEnd || 0,
|
6589
|
-
nextStart:
|
6590
|
-
buffered: buffered
|
6607
|
+
nextStart: nextStart,
|
6608
|
+
buffered: buffered,
|
6609
|
+
bufferedIndex: bufferedIndex
|
6591
6610
|
};
|
6592
6611
|
}
|
6593
6612
|
|
@@ -8866,7 +8885,6 @@
|
|
8866
8885
|
// reset startPosition and lastCurrentTime to restart playback @ stream beginning
|
8867
8886
|
_this.log("setting startPosition to 0 because media ended");
|
8868
8887
|
_this.startPosition = _this.lastCurrentTime = 0;
|
8869
|
-
_this.triggerEnded();
|
8870
8888
|
};
|
8871
8889
|
_this.playlistType = playlistType;
|
8872
8890
|
_this.hls = hls;
|
@@ -9015,9 +9033,6 @@
|
|
9015
9033
|
this.startFragRequested = false;
|
9016
9034
|
};
|
9017
9035
|
_proto.onError = function onError(event, data) {};
|
9018
|
-
_proto.triggerEnded = function triggerEnded() {
|
9019
|
-
/* overridden in stream-controller */
|
9020
|
-
};
|
9021
9036
|
_proto.onManifestLoaded = function onManifestLoaded(event, data) {
|
9022
9037
|
this.startTimeOffset = data.startTimeOffset;
|
9023
9038
|
};
|
@@ -10194,6 +10209,14 @@
|
|
10194
10209
|
get: function get() {
|
10195
10210
|
return this.buffering;
|
10196
10211
|
}
|
10212
|
+
}, {
|
10213
|
+
key: "inFlightFrag",
|
10214
|
+
get: function get() {
|
10215
|
+
return {
|
10216
|
+
frag: this.fragCurrent,
|
10217
|
+
state: this.state
|
10218
|
+
};
|
10219
|
+
}
|
10197
10220
|
}, {
|
10198
10221
|
key: "state",
|
10199
10222
|
get: function get() {
|
@@ -16311,7 +16334,7 @@
|
|
16311
16334
|
return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment;
|
16312
16335
|
}
|
16313
16336
|
|
16314
|
-
var version = "1.6.0-beta.2.0.canary.
|
16337
|
+
var version = "1.6.0-beta.2.0.canary.10925";
|
16315
16338
|
|
16316
16339
|
// ensure the worker ends up in the bundle
|
16317
16340
|
// If the worker should not be included this gets aliased to empty.js
|
@@ -16682,7 +16705,7 @@
|
|
16682
16705
|
return TransmuxerInterface;
|
16683
16706
|
}();
|
16684
16707
|
|
16685
|
-
var TICK_INTERVAL$
|
16708
|
+
var TICK_INTERVAL$3 = 100; // how often to tick in ms
|
16686
16709
|
var AudioStreamController = /*#__PURE__*/function (_BaseStreamController) {
|
16687
16710
|
function AudioStreamController(hls, fragmentTracker, keyLoader) {
|
16688
16711
|
var _this;
|
@@ -16793,7 +16816,7 @@
|
|
16793
16816
|
}
|
16794
16817
|
var lastCurrentTime = this.lastCurrentTime;
|
16795
16818
|
this.stopLoad();
|
16796
|
-
this.setInterval(TICK_INTERVAL$
|
16819
|
+
this.setInterval(TICK_INTERVAL$3);
|
16797
16820
|
if (lastCurrentTime > 0 && startPosition === -1) {
|
16798
16821
|
this.log("Override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3));
|
16799
16822
|
startPosition = lastCurrentTime;
|
@@ -17025,7 +17048,7 @@
|
|
17025
17048
|
this.flushAudioIfNeeded(data);
|
17026
17049
|
if (this.state !== State.STOPPED) {
|
17027
17050
|
// switching to audio track, start timer if not already started
|
17028
|
-
this.setInterval(TICK_INTERVAL$
|
17051
|
+
this.setInterval(TICK_INTERVAL$3);
|
17029
17052
|
this.state = State.IDLE;
|
17030
17053
|
this.tick();
|
17031
17054
|
}
|
@@ -18732,7 +18755,6 @@
|
|
18732
18755
|
var sbTrack = transferredTrack != null && transferredTrack.buffer ? transferredTrack : track;
|
18733
18756
|
var sbCodec = (sbTrack == null ? undefined : sbTrack.pendingCodec) || (sbTrack == null ? undefined : sbTrack.codec);
|
18734
18757
|
var trackLevelCodec = sbTrack == null ? undefined : sbTrack.levelCodec;
|
18735
|
-
var forceChangeType = !sbTrack || !!_this9.hls.config.assetPlayerId;
|
18736
18758
|
if (!track) {
|
18737
18759
|
track = tracks[trackName] = {
|
18738
18760
|
buffer: undefined,
|
@@ -18749,7 +18771,7 @@
|
|
18749
18771
|
var currentCodec = currentCodecFull == null ? undefined : currentCodecFull.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1');
|
18750
18772
|
var trackCodec = pickMostCompleteCodecName(codec, levelCodec);
|
18751
18773
|
var nextCodec = (_trackCodec = trackCodec) == null ? undefined : _trackCodec.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1');
|
18752
|
-
if (trackCodec &&
|
18774
|
+
if (trackCodec && currentCodecFull && currentCodec !== nextCodec) {
|
18753
18775
|
if (trackName.slice(0, 5) === 'audio') {
|
18754
18776
|
trackCodec = getCodecCompatibleName(trackCodec, _this9.appendSource);
|
18755
18777
|
}
|
@@ -23839,6 +23861,14 @@
|
|
23839
23861
|
return AssetListLoader;
|
23840
23862
|
}();
|
23841
23863
|
|
23864
|
+
function addEventListener(el, type, listener) {
|
23865
|
+
removeEventListener(el, type, listener);
|
23866
|
+
el.addEventListener(type, listener);
|
23867
|
+
}
|
23868
|
+
function removeEventListener(el, type, listener) {
|
23869
|
+
el.removeEventListener(type, listener);
|
23870
|
+
}
|
23871
|
+
|
23842
23872
|
function playWithCatch(media) {
|
23843
23873
|
media == null ? undefined : media.play().catch(function () {
|
23844
23874
|
/* no-op */
|
@@ -24154,24 +24184,23 @@
|
|
24154
24184
|
this.onScheduleUpdate = null;
|
24155
24185
|
};
|
24156
24186
|
_proto.onDestroying = function onDestroying() {
|
24157
|
-
var media = this.primaryMedia;
|
24187
|
+
var media = this.primaryMedia || this.media;
|
24158
24188
|
if (media) {
|
24159
24189
|
this.removeMediaListeners(media);
|
24160
24190
|
}
|
24161
24191
|
};
|
24162
24192
|
_proto.removeMediaListeners = function removeMediaListeners(media) {
|
24163
|
-
|
24164
|
-
|
24165
|
-
|
24166
|
-
|
24193
|
+
removeEventListener(media, 'play', this.onPlay);
|
24194
|
+
removeEventListener(media, 'pause', this.onPause);
|
24195
|
+
removeEventListener(media, 'seeking', this.onSeeking);
|
24196
|
+
removeEventListener(media, 'timeupdate', this.onTimeupdate);
|
24167
24197
|
};
|
24168
24198
|
_proto.onMediaAttaching = function onMediaAttaching(event, data) {
|
24169
24199
|
var media = this.media = data.media;
|
24170
|
-
this.
|
24171
|
-
|
24172
|
-
|
24173
|
-
|
24174
|
-
media.addEventListener('pause', this.onPause);
|
24200
|
+
addEventListener(media, 'seeking', this.onSeeking);
|
24201
|
+
addEventListener(media, 'timeupdate', this.onTimeupdate);
|
24202
|
+
addEventListener(media, 'play', this.onPlay);
|
24203
|
+
addEventListener(media, 'pause', this.onPause);
|
24175
24204
|
};
|
24176
24205
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
24177
24206
|
var playingItem = this.playingItem;
|
@@ -25676,7 +25705,7 @@
|
|
25676
25705
|
}]);
|
25677
25706
|
}(Logger);
|
25678
25707
|
|
25679
|
-
var TICK_INTERVAL$
|
25708
|
+
var TICK_INTERVAL$2 = 500; // how often to tick in ms
|
25680
25709
|
|
25681
25710
|
var SubtitleStreamController = /*#__PURE__*/function (_BaseStreamController) {
|
25682
25711
|
function SubtitleStreamController(hls, fragmentTracker, keyLoader) {
|
@@ -25718,7 +25747,7 @@
|
|
25718
25747
|
_proto.startLoad = function startLoad(startPosition) {
|
25719
25748
|
this.stopLoad();
|
25720
25749
|
this.state = State.IDLE;
|
25721
|
-
this.setInterval(TICK_INTERVAL$
|
25750
|
+
this.setInterval(TICK_INTERVAL$2);
|
25722
25751
|
this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;
|
25723
25752
|
this.tick();
|
25724
25753
|
};
|
@@ -25854,7 +25883,7 @@
|
|
25854
25883
|
this.mediaBuffer = null;
|
25855
25884
|
}
|
25856
25885
|
if (currentTrack && this.state !== State.STOPPED) {
|
25857
|
-
this.setInterval(TICK_INTERVAL$
|
25886
|
+
this.setInterval(TICK_INTERVAL$2);
|
25858
25887
|
}
|
25859
25888
|
}
|
25860
25889
|
|
@@ -30255,16 +30284,20 @@
|
|
30255
30284
|
frontBufferFlushThreshold: Infinity,
|
30256
30285
|
maxBufferSize: 60 * 1000 * 1000,
|
30257
30286
|
// used by stream-controller
|
30258
|
-
|
30287
|
+
maxFragLookUpTolerance: 0.25,
|
30259
30288
|
// used by stream-controller
|
30289
|
+
maxBufferHole: 0.1,
|
30290
|
+
// used by stream-controller and gap-controller
|
30291
|
+
detectStallWithCurrentTimeMs: 1250,
|
30292
|
+
// used by gap-controller
|
30260
30293
|
highBufferWatchdogPeriod: 2,
|
30261
|
-
// used by
|
30294
|
+
// used by gap-controller
|
30262
30295
|
nudgeOffset: 0.1,
|
30263
|
-
// used by
|
30296
|
+
// used by gap-controller
|
30264
30297
|
nudgeMaxRetry: 3,
|
30265
|
-
// used by
|
30266
|
-
|
30267
|
-
// used by
|
30298
|
+
// used by gap-controller
|
30299
|
+
nudgeOnVideoHole: true,
|
30300
|
+
// used by gap-controller
|
30268
30301
|
liveSyncDurationCount: 3,
|
30269
30302
|
// used by latency-controller
|
30270
30303
|
liveSyncOnStallIncrease: 1,
|
@@ -30363,7 +30396,6 @@
|
|
30363
30396
|
progressive: false,
|
30364
30397
|
lowLatencyMode: true,
|
30365
30398
|
cmcd: undefined,
|
30366
|
-
detectStallWithCurrentTimeMs: 1250,
|
30367
30399
|
enableDateRangeMetadataCues: true,
|
30368
30400
|
enableEmsgMetadataCues: true,
|
30369
30401
|
enableEmsgKLVMetadata: false,
|
@@ -30618,6 +30650,534 @@
|
|
30618
30650
|
}
|
30619
30651
|
}
|
30620
30652
|
|
30653
|
+
var MAX_START_GAP_JUMP = 2.0;
|
30654
|
+
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
30655
|
+
var SKIP_BUFFER_RANGE_START = 0.05;
|
30656
|
+
var TICK_INTERVAL$1 = 100;
|
30657
|
+
var GapController = /*#__PURE__*/function (_TaskLoop) {
|
30658
|
+
function GapController(hls, fragmentTracker) {
|
30659
|
+
var _this;
|
30660
|
+
_this = _TaskLoop.call(this, 'gap-controller', hls.logger) || this;
|
30661
|
+
_this.hls = null;
|
30662
|
+
_this.fragmentTracker = null;
|
30663
|
+
_this.media = null;
|
30664
|
+
_this.mediaSource = undefined;
|
30665
|
+
_this.nudgeRetry = 0;
|
30666
|
+
_this.stallReported = false;
|
30667
|
+
_this.stalled = null;
|
30668
|
+
_this.moved = false;
|
30669
|
+
_this.seeking = false;
|
30670
|
+
_this.buffered = {};
|
30671
|
+
_this.lastCurrentTime = 0;
|
30672
|
+
_this.ended = 0;
|
30673
|
+
_this.waiting = 0;
|
30674
|
+
_this.onMediaPlaying = function () {
|
30675
|
+
_this.ended = 0;
|
30676
|
+
_this.waiting = 0;
|
30677
|
+
};
|
30678
|
+
_this.onMediaWaiting = function () {
|
30679
|
+
var _this$media;
|
30680
|
+
if ((_this$media = _this.media) != null && _this$media.seeking) {
|
30681
|
+
return;
|
30682
|
+
}
|
30683
|
+
_this.waiting = self.performance.now();
|
30684
|
+
_this.tick();
|
30685
|
+
};
|
30686
|
+
_this.onMediaEnded = function () {
|
30687
|
+
if (_this.hls) {
|
30688
|
+
var _this$media2;
|
30689
|
+
// ended is set when triggering MEDIA_ENDED so that we do not trigger it again on stall or on tick with media.ended
|
30690
|
+
_this.ended = ((_this$media2 = _this.media) == null ? undefined : _this$media2.currentTime) || 1;
|
30691
|
+
_this.hls.trigger(Events.MEDIA_ENDED, {
|
30692
|
+
stalled: false
|
30693
|
+
});
|
30694
|
+
}
|
30695
|
+
};
|
30696
|
+
_this.hls = hls;
|
30697
|
+
_this.fragmentTracker = fragmentTracker;
|
30698
|
+
_this.registerListeners();
|
30699
|
+
return _this;
|
30700
|
+
}
|
30701
|
+
_inheritsLoose(GapController, _TaskLoop);
|
30702
|
+
var _proto = GapController.prototype;
|
30703
|
+
_proto.registerListeners = function registerListeners() {
|
30704
|
+
var hls = this.hls;
|
30705
|
+
if (hls) {
|
30706
|
+
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
30707
|
+
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
30708
|
+
hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
|
30709
|
+
}
|
30710
|
+
};
|
30711
|
+
_proto.unregisterListeners = function unregisterListeners() {
|
30712
|
+
var hls = this.hls;
|
30713
|
+
if (hls) {
|
30714
|
+
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
30715
|
+
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
30716
|
+
hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
|
30717
|
+
}
|
30718
|
+
};
|
30719
|
+
_proto.destroy = function destroy() {
|
30720
|
+
_TaskLoop.prototype.destroy.call(this);
|
30721
|
+
this.unregisterListeners();
|
30722
|
+
this.media = this.hls = this.fragmentTracker = null;
|
30723
|
+
this.mediaSource = undefined;
|
30724
|
+
};
|
30725
|
+
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
30726
|
+
this.setInterval(TICK_INTERVAL$1);
|
30727
|
+
this.mediaSource = data.mediaSource;
|
30728
|
+
var media = this.media = data.media;
|
30729
|
+
addEventListener(media, 'playing', this.onMediaPlaying);
|
30730
|
+
addEventListener(media, 'waiting', this.onMediaWaiting);
|
30731
|
+
addEventListener(media, 'ended', this.onMediaEnded);
|
30732
|
+
};
|
30733
|
+
_proto.onMediaDetaching = function onMediaDetaching(event, data) {
|
30734
|
+
this.clearInterval();
|
30735
|
+
var media = this.media;
|
30736
|
+
if (media) {
|
30737
|
+
removeEventListener(media, 'playing', this.onMediaPlaying);
|
30738
|
+
removeEventListener(media, 'waiting', this.onMediaWaiting);
|
30739
|
+
removeEventListener(media, 'ended', this.onMediaEnded);
|
30740
|
+
this.media = null;
|
30741
|
+
}
|
30742
|
+
this.mediaSource = undefined;
|
30743
|
+
};
|
30744
|
+
_proto.onBufferAppended = function onBufferAppended(event, data) {
|
30745
|
+
this.buffered = data.timeRanges;
|
30746
|
+
};
|
30747
|
+
_proto.tick = function tick() {
|
30748
|
+
var _this$media3;
|
30749
|
+
if (!((_this$media3 = this.media) != null && _this$media3.readyState) || !this.hasBuffered) {
|
30750
|
+
return;
|
30751
|
+
}
|
30752
|
+
var currentTime = this.media.currentTime;
|
30753
|
+
this.poll(currentTime, this.lastCurrentTime);
|
30754
|
+
this.lastCurrentTime = currentTime;
|
30755
|
+
}
|
30756
|
+
|
30757
|
+
/**
|
30758
|
+
* Checks if the playhead is stuck within a gap, and if so, attempts to free it.
|
30759
|
+
* A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range).
|
30760
|
+
*
|
30761
|
+
* @param lastCurrentTime - Previously read playhead position
|
30762
|
+
*/;
|
30763
|
+
_proto.poll = function poll(currentTime, lastCurrentTime) {
|
30764
|
+
var _this$hls, _this$hls2;
|
30765
|
+
var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
|
30766
|
+
if (!config) {
|
30767
|
+
return;
|
30768
|
+
}
|
30769
|
+
var media = this.media,
|
30770
|
+
stalled = this.stalled;
|
30771
|
+
if (!media) {
|
30772
|
+
return;
|
30773
|
+
}
|
30774
|
+
var seeking = media.seeking;
|
30775
|
+
var seeked = this.seeking && !seeking;
|
30776
|
+
var beginSeek = !this.seeking && seeking;
|
30777
|
+
var pausedEndedOrHalted = media.paused && !seeking || media.ended || media.playbackRate === 0;
|
30778
|
+
this.seeking = seeking;
|
30779
|
+
|
30780
|
+
// The playhead is moving, no-op
|
30781
|
+
if (currentTime !== lastCurrentTime) {
|
30782
|
+
if (lastCurrentTime) {
|
30783
|
+
this.ended = 0;
|
30784
|
+
}
|
30785
|
+
this.moved = true;
|
30786
|
+
if (!seeking) {
|
30787
|
+
this.nudgeRetry = 0;
|
30788
|
+
// When crossing between buffered video time ranges, but not audio, flush pipeline with seek (Chrome)
|
30789
|
+
if (config.nudgeOnVideoHole && !pausedEndedOrHalted && currentTime > lastCurrentTime) {
|
30790
|
+
this.nudgeOnVideoHole(currentTime, lastCurrentTime);
|
30791
|
+
}
|
30792
|
+
}
|
30793
|
+
if (this.waiting === 0) {
|
30794
|
+
this.stallResolved(currentTime);
|
30795
|
+
}
|
30796
|
+
return;
|
30797
|
+
}
|
30798
|
+
|
30799
|
+
// Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
|
30800
|
+
if (beginSeek || seeked) {
|
30801
|
+
if (seeked) {
|
30802
|
+
this.stallResolved(currentTime);
|
30803
|
+
}
|
30804
|
+
return;
|
30805
|
+
}
|
30806
|
+
|
30807
|
+
// The playhead should not be moving
|
30808
|
+
if (pausedEndedOrHalted) {
|
30809
|
+
this.nudgeRetry = 0;
|
30810
|
+
this.stallResolved(currentTime);
|
30811
|
+
// Fire MEDIA_ENDED to workaround event not being dispatched by browser
|
30812
|
+
if (!this.ended && media.ended && this.hls) {
|
30813
|
+
this.ended = currentTime || 1;
|
30814
|
+
this.hls.trigger(Events.MEDIA_ENDED, {
|
30815
|
+
stalled: false
|
30816
|
+
});
|
30817
|
+
}
|
30818
|
+
return;
|
30819
|
+
}
|
30820
|
+
if (!BufferHelper.getBuffered(media).length) {
|
30821
|
+
this.nudgeRetry = 0;
|
30822
|
+
return;
|
30823
|
+
}
|
30824
|
+
|
30825
|
+
// Resolve stalls at buffer holes using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
30826
|
+
var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
30827
|
+
var nextStart = bufferInfo.nextStart || 0;
|
30828
|
+
var fragmentTracker = this.fragmentTracker;
|
30829
|
+
if (seeking && fragmentTracker && this.hls) {
|
30830
|
+
// Is there a fragment loading/parsing/appending before currentTime?
|
30831
|
+
var inFlightDependency = getInFlightDependency(this.hls.inFlightFragments, currentTime);
|
30832
|
+
|
30833
|
+
// Waiting for seeking in a buffered range to complete
|
30834
|
+
var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
|
30835
|
+
// Next buffered range is too far ahead to jump to while still seeking
|
30836
|
+
var noBufferHole = !nextStart || inFlightDependency || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
|
30837
|
+
if (hasEnoughBuffer || noBufferHole) {
|
30838
|
+
return;
|
30839
|
+
}
|
30840
|
+
// Reset moved state when seeking to a point in or before a gap/hole
|
30841
|
+
this.moved = false;
|
30842
|
+
}
|
30843
|
+
|
30844
|
+
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
30845
|
+
// The addition poll gives the browser a chance to jump the gap for us
|
30846
|
+
var levelDetails = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.latestLevelDetails;
|
30847
|
+
if (!this.moved && this.stalled !== null && fragmentTracker) {
|
30848
|
+
// There is no playable buffer (seeked, waiting for buffer)
|
30849
|
+
var isBuffered = bufferInfo.len > 0;
|
30850
|
+
if (!isBuffered && !nextStart) {
|
30851
|
+
return;
|
30852
|
+
}
|
30853
|
+
// Jump start gaps within jump threshold
|
30854
|
+
var startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime;
|
30855
|
+
|
30856
|
+
// When joining a live stream with audio tracks, account for live playlist window sliding by allowing
|
30857
|
+
// a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
|
30858
|
+
// that begins over 1 target duration after the video start position.
|
30859
|
+
var isLive = !!(levelDetails != null && levelDetails.live);
|
30860
|
+
var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
|
30861
|
+
var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
|
30862
|
+
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
30863
|
+
if (!media.paused) {
|
30864
|
+
this._trySkipBufferHole(partialOrGap);
|
30865
|
+
}
|
30866
|
+
return;
|
30867
|
+
}
|
30868
|
+
}
|
30869
|
+
|
30870
|
+
// Start tracking stall time
|
30871
|
+
var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
|
30872
|
+
var tnow = self.performance.now();
|
30873
|
+
var tWaiting = this.waiting;
|
30874
|
+
if (stalled === null) {
|
30875
|
+
// Use time of recent "waiting" event
|
30876
|
+
if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
|
30877
|
+
this.stalled = tWaiting;
|
30878
|
+
} else {
|
30879
|
+
this.stalled = tnow;
|
30880
|
+
}
|
30881
|
+
return;
|
30882
|
+
}
|
30883
|
+
var stalledDuration = tnow - stalled;
|
30884
|
+
if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
|
30885
|
+
var _this$mediaSource;
|
30886
|
+
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
30887
|
+
if (((_this$mediaSource = this.mediaSource) == null ? undefined : _this$mediaSource.readyState) === 'ended' && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
|
30888
|
+
if (this.ended) {
|
30889
|
+
return;
|
30890
|
+
}
|
30891
|
+
this.ended = currentTime || 1;
|
30892
|
+
this.hls.trigger(Events.MEDIA_ENDED, {
|
30893
|
+
stalled: true
|
30894
|
+
});
|
30895
|
+
return;
|
30896
|
+
}
|
30897
|
+
// Report stalling after trying to fix
|
30898
|
+
this._reportStall(bufferInfo);
|
30899
|
+
if (!this.media || !this.hls) {
|
30900
|
+
return;
|
30901
|
+
}
|
30902
|
+
}
|
30903
|
+
var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
|
30904
|
+
this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
|
30905
|
+
};
|
30906
|
+
_proto.stallResolved = function stallResolved(currentTime) {
|
30907
|
+
var stalled = this.stalled;
|
30908
|
+
if (stalled && this.hls) {
|
30909
|
+
this.stalled = null;
|
30910
|
+
// The playhead is now moving, but was previously stalled
|
30911
|
+
if (this.stallReported) {
|
30912
|
+
var stalledDuration = self.performance.now() - stalled;
|
30913
|
+
this.log("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
|
30914
|
+
this.stallReported = false;
|
30915
|
+
this.waiting = 0;
|
30916
|
+
this.hls.trigger(Events.STALL_RESOLVED, {});
|
30917
|
+
}
|
30918
|
+
}
|
30919
|
+
};
|
30920
|
+
_proto.nudgeOnVideoHole = function nudgeOnVideoHole(currentTime, lastCurrentTime) {
|
30921
|
+
var _this$buffered$audio;
|
30922
|
+
// Chrome will play one second past a hole in video buffered time ranges without rendering any video from the subsequent range and then stall as long as audio is buffered:
|
30923
|
+
// https://github.com/video-dev/hls.js/issues/5631
|
30924
|
+
// https://issues.chromium.org/issues/40280613#comment10
|
30925
|
+
// Detect the potential for this situation and proactively seek to flush the video pipeline once the playhead passes the start of the video hole.
|
30926
|
+
// When there are audio and video buffers and currentTime is past the end of the first video buffered range...
|
30927
|
+
var videoSourceBuffered = this.buffered.video;
|
30928
|
+
if (this.hls && this.media && this.fragmentTracker && (_this$buffered$audio = this.buffered.audio) != null && _this$buffered$audio.length && videoSourceBuffered && videoSourceBuffered.length > 1 && currentTime > videoSourceBuffered.end(0)) {
|
30929
|
+
// and audio is buffered at the playhead
|
30930
|
+
var audioBufferInfo = BufferHelper.bufferedInfo(BufferHelper.timeRangesToArray(this.buffered.audio), currentTime, 0);
|
30931
|
+
if (audioBufferInfo.len > 1 && lastCurrentTime >= audioBufferInfo.start) {
|
30932
|
+
var videoTimes = BufferHelper.timeRangesToArray(videoSourceBuffered);
|
30933
|
+
var lastBufferedIndex = BufferHelper.bufferedInfo(videoTimes, lastCurrentTime, 0).bufferedIndex;
|
30934
|
+
// nudge when crossing into another video buffered range (hole).
|
30935
|
+
if (lastBufferedIndex > -1 && lastBufferedIndex < videoTimes.length - 1) {
|
30936
|
+
var bufferedIndex = BufferHelper.bufferedInfo(videoTimes, currentTime, 0).bufferedIndex;
|
30937
|
+
var holeStart = videoTimes[lastBufferedIndex].end;
|
30938
|
+
var holeEnd = videoTimes[lastBufferedIndex + 1].start;
|
30939
|
+
if ((bufferedIndex === -1 || bufferedIndex > lastBufferedIndex) && holeEnd - holeStart < 1 &&
|
30940
|
+
// `maxBufferHole` may be too small and setting it to 0 should not disable this feature
|
30941
|
+
currentTime - holeStart < 2) {
|
30942
|
+
var error = new Error("nudging playhead to flush pipeline after video hole. currentTime: " + currentTime + " hole: " + holeStart + " -> " + holeEnd + " buffered index: " + bufferedIndex);
|
30943
|
+
this.warn(error.message);
|
30944
|
+
// Magic number to flush the pipeline without interuption to audio playback:
|
30945
|
+
this.media.currentTime += 0.000001;
|
30946
|
+
var frag = this.fragmentTracker.getPartialFragment(currentTime) || undefined;
|
30947
|
+
var bufferInfo = BufferHelper.bufferInfo(this.media, currentTime, 0);
|
30948
|
+
this.hls.trigger(Events.ERROR, {
|
30949
|
+
type: ErrorTypes.MEDIA_ERROR,
|
30950
|
+
details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
|
30951
|
+
fatal: false,
|
30952
|
+
error: error,
|
30953
|
+
reason: error.message,
|
30954
|
+
frag: frag,
|
30955
|
+
buffer: bufferInfo.len,
|
30956
|
+
bufferInfo: bufferInfo
|
30957
|
+
});
|
30958
|
+
}
|
30959
|
+
}
|
30960
|
+
}
|
30961
|
+
}
|
30962
|
+
}
|
30963
|
+
|
30964
|
+
/**
|
30965
|
+
* Detects and attempts to fix known buffer stalling issues.
|
30966
|
+
* @param bufferInfo - The properties of the current buffer.
|
30967
|
+
* @param stalledDurationMs - The amount of time Hls.js has been stalling for.
|
30968
|
+
* @private
|
30969
|
+
*/;
|
30970
|
+
_proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
|
30971
|
+
var _this$hls3;
|
30972
|
+
var fragmentTracker = this.fragmentTracker,
|
30973
|
+
media = this.media;
|
30974
|
+
var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
|
30975
|
+
if (!media || !fragmentTracker || !config) {
|
30976
|
+
return;
|
30977
|
+
}
|
30978
|
+
var currentTime = media.currentTime;
|
30979
|
+
var partial = fragmentTracker.getPartialFragment(currentTime);
|
30980
|
+
if (partial) {
|
30981
|
+
// Try to skip over the buffer hole caused by a partial fragment
|
30982
|
+
// This method isn't limited by the size of the gap between buffered ranges
|
30983
|
+
var targetTime = this._trySkipBufferHole(partial);
|
30984
|
+
// we return here in this case, meaning
|
30985
|
+
// the branch below only executes when we haven't seeked to a new position
|
30986
|
+
if (targetTime || !this.media) {
|
30987
|
+
return;
|
30988
|
+
}
|
30989
|
+
}
|
30990
|
+
|
30991
|
+
// if we haven't had to skip over a buffer hole of a partial fragment
|
30992
|
+
// we may just have to "nudge" the playlist as the browser decoding/rendering engine
|
30993
|
+
// needs to cross some sort of threshold covering all source-buffers content
|
30994
|
+
// to start playing properly.
|
30995
|
+
var bufferedRanges = bufferInfo.buffered;
|
30996
|
+
if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && (stalledDurationMs > config.highBufferWatchdogPeriod * 1000 || this.waiting)) {
|
30997
|
+
this.warn('Trying to nudge playhead over buffer-hole');
|
30998
|
+
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
30999
|
+
// We only try to jump the hole if it's under the configured size
|
31000
|
+
this._tryNudgeBuffer(bufferInfo);
|
31001
|
+
}
|
31002
|
+
}
|
31003
|
+
|
31004
|
+
/**
|
31005
|
+
* Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.
|
31006
|
+
* @param bufferLen - The playhead distance from the end of the current buffer segment.
|
31007
|
+
* @private
|
31008
|
+
*/;
|
31009
|
+
_proto._reportStall = function _reportStall(bufferInfo) {
|
31010
|
+
var hls = this.hls,
|
31011
|
+
media = this.media,
|
31012
|
+
stallReported = this.stallReported,
|
31013
|
+
stalled = this.stalled;
|
31014
|
+
if (!stallReported && stalled !== null && media && hls) {
|
31015
|
+
// Report stalled error once
|
31016
|
+
this.stallReported = true;
|
31017
|
+
var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
|
31018
|
+
this.warn(error.message);
|
31019
|
+
hls.trigger(Events.ERROR, {
|
31020
|
+
type: ErrorTypes.MEDIA_ERROR,
|
31021
|
+
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
31022
|
+
fatal: false,
|
31023
|
+
error: error,
|
31024
|
+
buffer: bufferInfo.len,
|
31025
|
+
bufferInfo: bufferInfo,
|
31026
|
+
stalled: {
|
31027
|
+
start: stalled
|
31028
|
+
}
|
31029
|
+
});
|
31030
|
+
}
|
31031
|
+
}
|
31032
|
+
|
31033
|
+
/**
|
31034
|
+
* Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments
|
31035
|
+
* @param partial - The partial fragment found at the current time (where playback is stalling).
|
31036
|
+
* @private
|
31037
|
+
*/;
|
31038
|
+
_proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
|
31039
|
+
var _this$hls4;
|
31040
|
+
var fragmentTracker = this.fragmentTracker,
|
31041
|
+
media = this.media;
|
31042
|
+
var config = (_this$hls4 = this.hls) == null ? undefined : _this$hls4.config;
|
31043
|
+
if (!media || !fragmentTracker || !config) {
|
31044
|
+
return 0;
|
31045
|
+
}
|
31046
|
+
|
31047
|
+
// Check if currentTime is between unbuffered regions of partial fragments
|
31048
|
+
var currentTime = media.currentTime;
|
31049
|
+
var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
31050
|
+
var startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart;
|
31051
|
+
if (startTime) {
|
31052
|
+
var bufferStarved = bufferInfo.len <= config.maxBufferHole;
|
31053
|
+
var waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3;
|
31054
|
+
var gapLength = startTime - currentTime;
|
31055
|
+
if (gapLength > 0 && (bufferStarved || waiting)) {
|
31056
|
+
// Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
|
31057
|
+
if (gapLength > config.maxBufferHole) {
|
31058
|
+
var startGap = false;
|
31059
|
+
if (currentTime === 0) {
|
31060
|
+
var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
|
31061
|
+
if (startFrag && startTime < startFrag.end) {
|
31062
|
+
startGap = true;
|
31063
|
+
}
|
31064
|
+
}
|
31065
|
+
if (!startGap) {
|
31066
|
+
var startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN);
|
31067
|
+
if (startProvisioned) {
|
31068
|
+
var moreToLoad = false;
|
31069
|
+
var pos = startProvisioned.end;
|
31070
|
+
while (pos < startTime) {
|
31071
|
+
var provisioned = fragmentTracker.getPartialFragment(pos);
|
31072
|
+
if (provisioned) {
|
31073
|
+
pos += provisioned.duration;
|
31074
|
+
} else {
|
31075
|
+
moreToLoad = true;
|
31076
|
+
break;
|
31077
|
+
}
|
31078
|
+
}
|
31079
|
+
if (moreToLoad) {
|
31080
|
+
return 0;
|
31081
|
+
}
|
31082
|
+
}
|
31083
|
+
}
|
31084
|
+
}
|
31085
|
+
var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
31086
|
+
this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
|
31087
|
+
this.moved = true;
|
31088
|
+
media.currentTime = targetTime;
|
31089
|
+
if (!(partial != null && partial.gap) && this.hls) {
|
31090
|
+
var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
|
31091
|
+
this.hls.trigger(Events.ERROR, {
|
31092
|
+
type: ErrorTypes.MEDIA_ERROR,
|
31093
|
+
details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
|
31094
|
+
fatal: false,
|
31095
|
+
error: error,
|
31096
|
+
reason: error.message,
|
31097
|
+
frag: partial || undefined,
|
31098
|
+
buffer: bufferInfo.len,
|
31099
|
+
bufferInfo: bufferInfo
|
31100
|
+
});
|
31101
|
+
}
|
31102
|
+
return targetTime;
|
31103
|
+
}
|
31104
|
+
}
|
31105
|
+
return 0;
|
31106
|
+
}
|
31107
|
+
|
31108
|
+
/**
|
31109
|
+
* Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
|
31110
|
+
* @private
|
31111
|
+
*/;
|
31112
|
+
_proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
|
31113
|
+
var hls = this.hls,
|
31114
|
+
media = this.media,
|
31115
|
+
nudgeRetry = this.nudgeRetry;
|
31116
|
+
var config = hls == null ? undefined : hls.config;
|
31117
|
+
if (!media || !config) {
|
31118
|
+
return 0;
|
31119
|
+
}
|
31120
|
+
var currentTime = media.currentTime;
|
31121
|
+
this.nudgeRetry++;
|
31122
|
+
if (nudgeRetry < config.nudgeMaxRetry) {
|
31123
|
+
var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
|
31124
|
+
// playback stalled in buffered area ... let's nudge currentTime to try to overcome this
|
31125
|
+
var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
|
31126
|
+
this.warn(error.message);
|
31127
|
+
media.currentTime = targetTime;
|
31128
|
+
hls.trigger(Events.ERROR, {
|
31129
|
+
type: ErrorTypes.MEDIA_ERROR,
|
31130
|
+
details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
|
31131
|
+
error: error,
|
31132
|
+
fatal: false,
|
31133
|
+
buffer: bufferInfo.len,
|
31134
|
+
bufferInfo: bufferInfo
|
31135
|
+
});
|
31136
|
+
} else {
|
31137
|
+
var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
|
31138
|
+
this.error(_error.message);
|
31139
|
+
hls.trigger(Events.ERROR, {
|
31140
|
+
type: ErrorTypes.MEDIA_ERROR,
|
31141
|
+
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
31142
|
+
error: _error,
|
31143
|
+
fatal: true,
|
31144
|
+
buffer: bufferInfo.len,
|
31145
|
+
bufferInfo: bufferInfo
|
31146
|
+
});
|
31147
|
+
}
|
31148
|
+
};
|
31149
|
+
return _createClass(GapController, [{
|
31150
|
+
key: "hasBuffered",
|
31151
|
+
get: function get() {
|
31152
|
+
return Object.keys(this.buffered).length > 0;
|
31153
|
+
}
|
31154
|
+
}]);
|
31155
|
+
}(TaskLoop);
|
31156
|
+
function getInFlightDependency(inFlightFragments, currentTime) {
|
31157
|
+
var main = inFlight(inFlightFragments.main);
|
31158
|
+
if (main && main.start <= currentTime) {
|
31159
|
+
return main;
|
31160
|
+
}
|
31161
|
+
var audio = inFlight(inFlightFragments.audio);
|
31162
|
+
if (audio && audio.start <= currentTime) {
|
31163
|
+
return audio;
|
31164
|
+
}
|
31165
|
+
return null;
|
31166
|
+
}
|
31167
|
+
function inFlight(inFlightData) {
|
31168
|
+
if (!inFlightData) {
|
31169
|
+
return null;
|
31170
|
+
}
|
31171
|
+
switch (inFlightData.state) {
|
31172
|
+
case State.IDLE:
|
31173
|
+
case State.STOPPED:
|
31174
|
+
case State.ENDED:
|
31175
|
+
case State.ERROR:
|
31176
|
+
return null;
|
31177
|
+
}
|
31178
|
+
return inFlightData.frag;
|
31179
|
+
}
|
31180
|
+
|
30621
31181
|
var MIN_CUE_DURATION = 0.25;
|
30622
31182
|
function getCueClass() {
|
30623
31183
|
if (typeof self === 'undefined') return undefined;
|
@@ -31789,375 +32349,6 @@
|
|
31789
32349
|
});
|
31790
32350
|
}
|
31791
32351
|
|
31792
|
-
var MAX_START_GAP_JUMP = 2.0;
|
31793
|
-
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
31794
|
-
var SKIP_BUFFER_RANGE_START = 0.05;
|
31795
|
-
var GapController = /*#__PURE__*/function (_Logger) {
|
31796
|
-
function GapController(media, fragmentTracker, hls) {
|
31797
|
-
var _this;
|
31798
|
-
_this = _Logger.call(this, 'gap-controller', hls.logger) || this;
|
31799
|
-
_this.media = null;
|
31800
|
-
_this.fragmentTracker = null;
|
31801
|
-
_this.hls = null;
|
31802
|
-
_this.nudgeRetry = 0;
|
31803
|
-
_this.stallReported = false;
|
31804
|
-
_this.stalled = null;
|
31805
|
-
_this.moved = false;
|
31806
|
-
_this.seeking = false;
|
31807
|
-
_this.ended = 0;
|
31808
|
-
_this.waiting = 0;
|
31809
|
-
_this.media = media;
|
31810
|
-
_this.fragmentTracker = fragmentTracker;
|
31811
|
-
_this.hls = hls;
|
31812
|
-
return _this;
|
31813
|
-
}
|
31814
|
-
_inheritsLoose(GapController, _Logger);
|
31815
|
-
var _proto = GapController.prototype;
|
31816
|
-
_proto.destroy = function destroy() {
|
31817
|
-
this.media = this.hls = this.fragmentTracker = null;
|
31818
|
-
}
|
31819
|
-
|
31820
|
-
/**
|
31821
|
-
* Checks if the playhead is stuck within a gap, and if so, attempts to free it.
|
31822
|
-
* A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range).
|
31823
|
-
*
|
31824
|
-
* @param lastCurrentTime - Previously read playhead position
|
31825
|
-
*/;
|
31826
|
-
_proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
|
31827
|
-
var _this$hls;
|
31828
|
-
var media = this.media,
|
31829
|
-
stalled = this.stalled;
|
31830
|
-
if (!media) {
|
31831
|
-
return;
|
31832
|
-
}
|
31833
|
-
var currentTime = media.currentTime,
|
31834
|
-
seeking = media.seeking;
|
31835
|
-
var seeked = this.seeking && !seeking;
|
31836
|
-
var beginSeek = !this.seeking && seeking;
|
31837
|
-
this.seeking = seeking;
|
31838
|
-
|
31839
|
-
// The playhead is moving, no-op
|
31840
|
-
if (currentTime !== lastCurrentTime) {
|
31841
|
-
if (lastCurrentTime) {
|
31842
|
-
this.ended = 0;
|
31843
|
-
}
|
31844
|
-
this.moved = true;
|
31845
|
-
if (!seeking) {
|
31846
|
-
this.nudgeRetry = 0;
|
31847
|
-
}
|
31848
|
-
if (this.waiting === 0) {
|
31849
|
-
this.stallResolved(currentTime);
|
31850
|
-
}
|
31851
|
-
return;
|
31852
|
-
}
|
31853
|
-
|
31854
|
-
// Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
|
31855
|
-
if (beginSeek || seeked) {
|
31856
|
-
if (seeked) {
|
31857
|
-
this.stallResolved(currentTime);
|
31858
|
-
}
|
31859
|
-
return;
|
31860
|
-
}
|
31861
|
-
|
31862
|
-
// The playhead should not be moving
|
31863
|
-
if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
|
31864
|
-
this.nudgeRetry = 0;
|
31865
|
-
this.stallResolved(currentTime);
|
31866
|
-
// Fire MEDIA_ENDED to workaround event not being dispatched by browser
|
31867
|
-
if (!this.ended && media.ended && this.hls) {
|
31868
|
-
this.ended = currentTime || 1;
|
31869
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
31870
|
-
stalled: false
|
31871
|
-
});
|
31872
|
-
}
|
31873
|
-
return;
|
31874
|
-
}
|
31875
|
-
if (!BufferHelper.getBuffered(media).length) {
|
31876
|
-
this.nudgeRetry = 0;
|
31877
|
-
return;
|
31878
|
-
}
|
31879
|
-
var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
31880
|
-
var nextStart = bufferInfo.nextStart || 0;
|
31881
|
-
var fragmentTracker = this.fragmentTracker;
|
31882
|
-
if (seeking && fragmentTracker) {
|
31883
|
-
// Waiting for seeking in a buffered range to complete
|
31884
|
-
var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
|
31885
|
-
// 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 && !fragmentTracker.getPartialFragment(currentTime);
|
31887
|
-
if (hasEnoughBuffer || noBufferGap) {
|
31888
|
-
return;
|
31889
|
-
}
|
31890
|
-
// Reset moved state when seeking to a point in or before a gap
|
31891
|
-
this.moved = false;
|
31892
|
-
}
|
31893
|
-
|
31894
|
-
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
31895
|
-
// The addition poll gives the browser a chance to jump the gap for us
|
31896
|
-
if (!this.moved && this.stalled !== null && fragmentTracker) {
|
31897
|
-
// There is no playable buffer (seeked, waiting for buffer)
|
31898
|
-
var isBuffered = bufferInfo.len > 0;
|
31899
|
-
if (!isBuffered && !nextStart) {
|
31900
|
-
return;
|
31901
|
-
}
|
31902
|
-
// Jump start gaps within jump threshold
|
31903
|
-
var startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime;
|
31904
|
-
|
31905
|
-
// When joining a live stream with audio tracks, account for live playlist window sliding by allowing
|
31906
|
-
// a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment
|
31907
|
-
// that begins over 1 target duration after the video start position.
|
31908
|
-
var isLive = !!(levelDetails != null && levelDetails.live);
|
31909
|
-
var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
|
31910
|
-
var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
|
31911
|
-
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
31912
|
-
if (!media.paused) {
|
31913
|
-
this._trySkipBufferHole(partialOrGap);
|
31914
|
-
}
|
31915
|
-
return;
|
31916
|
-
}
|
31917
|
-
}
|
31918
|
-
|
31919
|
-
// Start tracking stall time
|
31920
|
-
var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
|
31921
|
-
if (!config) {
|
31922
|
-
return;
|
31923
|
-
}
|
31924
|
-
var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
|
31925
|
-
var tnow = self.performance.now();
|
31926
|
-
var tWaiting = this.waiting;
|
31927
|
-
if (stalled === null) {
|
31928
|
-
// Use time of recent "waiting" event
|
31929
|
-
if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
|
31930
|
-
this.stalled = tWaiting;
|
31931
|
-
} else {
|
31932
|
-
this.stalled = tnow;
|
31933
|
-
}
|
31934
|
-
return;
|
31935
|
-
}
|
31936
|
-
var stalledDuration = tnow - stalled;
|
31937
|
-
if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
|
31938
|
-
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
31939
|
-
if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
|
31940
|
-
if (this.ended) {
|
31941
|
-
return;
|
31942
|
-
}
|
31943
|
-
this.ended = currentTime || 1;
|
31944
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
31945
|
-
stalled: true
|
31946
|
-
});
|
31947
|
-
return;
|
31948
|
-
}
|
31949
|
-
// Report stalling after trying to fix
|
31950
|
-
this._reportStall(bufferInfo);
|
31951
|
-
if (!this.media || !this.hls) {
|
31952
|
-
return;
|
31953
|
-
}
|
31954
|
-
}
|
31955
|
-
var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
|
31956
|
-
this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
|
31957
|
-
};
|
31958
|
-
_proto.stallResolved = function stallResolved(currentTime) {
|
31959
|
-
var stalled = this.stalled;
|
31960
|
-
if (stalled && this.hls) {
|
31961
|
-
this.stalled = null;
|
31962
|
-
// The playhead is now moving, but was previously stalled
|
31963
|
-
if (this.stallReported) {
|
31964
|
-
var stalledDuration = self.performance.now() - stalled;
|
31965
|
-
this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
|
31966
|
-
this.stallReported = false;
|
31967
|
-
this.waiting = 0;
|
31968
|
-
this.hls.trigger(Events.STALL_RESOLVED, {});
|
31969
|
-
}
|
31970
|
-
}
|
31971
|
-
}
|
31972
|
-
|
31973
|
-
/**
|
31974
|
-
* Detects and attempts to fix known buffer stalling issues.
|
31975
|
-
* @param bufferInfo - The properties of the current buffer.
|
31976
|
-
* @param stalledDurationMs - The amount of time Hls.js has been stalling for.
|
31977
|
-
* @private
|
31978
|
-
*/;
|
31979
|
-
_proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
|
31980
|
-
var _this$hls2;
|
31981
|
-
var fragmentTracker = this.fragmentTracker,
|
31982
|
-
media = this.media;
|
31983
|
-
var config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
|
31984
|
-
if (!media || !fragmentTracker || !config) {
|
31985
|
-
return;
|
31986
|
-
}
|
31987
|
-
var currentTime = media.currentTime;
|
31988
|
-
var partial = fragmentTracker.getPartialFragment(currentTime);
|
31989
|
-
if (partial) {
|
31990
|
-
// Try to skip over the buffer hole caused by a partial fragment
|
31991
|
-
// This method isn't limited by the size of the gap between buffered ranges
|
31992
|
-
var targetTime = this._trySkipBufferHole(partial);
|
31993
|
-
// we return here in this case, meaning
|
31994
|
-
// the branch below only executes when we haven't seeked to a new position
|
31995
|
-
if (targetTime || !this.media) {
|
31996
|
-
return;
|
31997
|
-
}
|
31998
|
-
}
|
31999
|
-
|
32000
|
-
// if we haven't had to skip over a buffer hole of a partial fragment
|
32001
|
-
// we may just have to "nudge" the playlist as the browser decoding/rendering engine
|
32002
|
-
// needs to cross some sort of threshold covering all source-buffers content
|
32003
|
-
// to start playing properly.
|
32004
|
-
var bufferedRanges = bufferInfo.buffered;
|
32005
|
-
if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
|
32006
|
-
this.warn('Trying to nudge playhead over buffer-hole');
|
32007
|
-
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
32008
|
-
// We only try to jump the hole if it's under the configured size
|
32009
|
-
this._tryNudgeBuffer(bufferInfo);
|
32010
|
-
}
|
32011
|
-
}
|
32012
|
-
|
32013
|
-
/**
|
32014
|
-
* Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.
|
32015
|
-
* @param bufferLen - The playhead distance from the end of the current buffer segment.
|
32016
|
-
* @private
|
32017
|
-
*/;
|
32018
|
-
_proto._reportStall = function _reportStall(bufferInfo) {
|
32019
|
-
var hls = this.hls,
|
32020
|
-
media = this.media,
|
32021
|
-
stallReported = this.stallReported,
|
32022
|
-
stalled = this.stalled;
|
32023
|
-
if (!stallReported && stalled !== null && media && hls) {
|
32024
|
-
// Report stalled error once
|
32025
|
-
this.stallReported = true;
|
32026
|
-
var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
|
32027
|
-
this.warn(error.message);
|
32028
|
-
hls.trigger(Events.ERROR, {
|
32029
|
-
type: ErrorTypes.MEDIA_ERROR,
|
32030
|
-
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
32031
|
-
fatal: false,
|
32032
|
-
error: error,
|
32033
|
-
buffer: bufferInfo.len,
|
32034
|
-
bufferInfo: bufferInfo,
|
32035
|
-
stalled: {
|
32036
|
-
start: stalled
|
32037
|
-
}
|
32038
|
-
});
|
32039
|
-
}
|
32040
|
-
}
|
32041
|
-
|
32042
|
-
/**
|
32043
|
-
* Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments
|
32044
|
-
* @param partial - The partial fragment found at the current time (where playback is stalling).
|
32045
|
-
* @private
|
32046
|
-
*/;
|
32047
|
-
_proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
|
32048
|
-
var _this$hls3;
|
32049
|
-
var fragmentTracker = this.fragmentTracker,
|
32050
|
-
media = this.media;
|
32051
|
-
var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
|
32052
|
-
if (!media || !fragmentTracker || !config) {
|
32053
|
-
return 0;
|
32054
|
-
}
|
32055
|
-
|
32056
|
-
// Check if currentTime is between unbuffered regions of partial fragments
|
32057
|
-
var currentTime = media.currentTime;
|
32058
|
-
var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
32059
|
-
var startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart;
|
32060
|
-
if (startTime) {
|
32061
|
-
var bufferStarved = bufferInfo.len <= config.maxBufferHole;
|
32062
|
-
var waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3;
|
32063
|
-
var gapLength = startTime - currentTime;
|
32064
|
-
if (gapLength > 0 && (bufferStarved || waiting)) {
|
32065
|
-
// Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
|
32066
|
-
if (gapLength > config.maxBufferHole) {
|
32067
|
-
var startGap = false;
|
32068
|
-
if (currentTime === 0) {
|
32069
|
-
var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
|
32070
|
-
if (startFrag && startTime < startFrag.end) {
|
32071
|
-
startGap = true;
|
32072
|
-
}
|
32073
|
-
}
|
32074
|
-
if (!startGap) {
|
32075
|
-
var startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN);
|
32076
|
-
if (startProvisioned) {
|
32077
|
-
var moreToLoad = false;
|
32078
|
-
var pos = startProvisioned.end;
|
32079
|
-
while (pos < startTime) {
|
32080
|
-
var provisioned = fragmentTracker.getPartialFragment(pos);
|
32081
|
-
if (provisioned) {
|
32082
|
-
pos += provisioned.duration;
|
32083
|
-
} else {
|
32084
|
-
moreToLoad = true;
|
32085
|
-
break;
|
32086
|
-
}
|
32087
|
-
}
|
32088
|
-
if (moreToLoad) {
|
32089
|
-
return 0;
|
32090
|
-
}
|
32091
|
-
}
|
32092
|
-
}
|
32093
|
-
}
|
32094
|
-
var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
32095
|
-
this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
|
32096
|
-
this.moved = true;
|
32097
|
-
media.currentTime = targetTime;
|
32098
|
-
if (partial && !partial.gap && this.hls) {
|
32099
|
-
var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
|
32100
|
-
this.hls.trigger(Events.ERROR, {
|
32101
|
-
type: ErrorTypes.MEDIA_ERROR,
|
32102
|
-
details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
|
32103
|
-
fatal: false,
|
32104
|
-
error: error,
|
32105
|
-
reason: error.message,
|
32106
|
-
frag: partial,
|
32107
|
-
buffer: bufferInfo.len,
|
32108
|
-
bufferInfo: bufferInfo
|
32109
|
-
});
|
32110
|
-
}
|
32111
|
-
return targetTime;
|
32112
|
-
}
|
32113
|
-
}
|
32114
|
-
return 0;
|
32115
|
-
}
|
32116
|
-
|
32117
|
-
/**
|
32118
|
-
* Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
|
32119
|
-
* @private
|
32120
|
-
*/;
|
32121
|
-
_proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
|
32122
|
-
var hls = this.hls,
|
32123
|
-
media = this.media,
|
32124
|
-
nudgeRetry = this.nudgeRetry;
|
32125
|
-
var config = hls == null ? undefined : hls.config;
|
32126
|
-
if (!media || !config) {
|
32127
|
-
return 0;
|
32128
|
-
}
|
32129
|
-
var currentTime = media.currentTime;
|
32130
|
-
this.nudgeRetry++;
|
32131
|
-
if (nudgeRetry < config.nudgeMaxRetry) {
|
32132
|
-
var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
|
32133
|
-
// playback stalled in buffered area ... let's nudge currentTime to try to overcome this
|
32134
|
-
var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime);
|
32135
|
-
this.warn(error.message);
|
32136
|
-
media.currentTime = targetTime;
|
32137
|
-
hls.trigger(Events.ERROR, {
|
32138
|
-
type: ErrorTypes.MEDIA_ERROR,
|
32139
|
-
details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
|
32140
|
-
error: error,
|
32141
|
-
fatal: false,
|
32142
|
-
buffer: bufferInfo.len,
|
32143
|
-
bufferInfo: bufferInfo
|
32144
|
-
});
|
32145
|
-
} else {
|
32146
|
-
var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
|
32147
|
-
this.error(_error.message);
|
32148
|
-
hls.trigger(Events.ERROR, {
|
32149
|
-
type: ErrorTypes.MEDIA_ERROR,
|
32150
|
-
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
32151
|
-
error: _error,
|
32152
|
-
fatal: true,
|
32153
|
-
buffer: bufferInfo.len,
|
32154
|
-
bufferInfo: bufferInfo
|
32155
|
-
});
|
32156
|
-
}
|
32157
|
-
};
|
32158
|
-
return GapController;
|
32159
|
-
}(Logger);
|
32160
|
-
|
32161
32352
|
function getSourceBuffer() {
|
32162
32353
|
return self.SourceBuffer || self.WebKitSourceBuffer;
|
32163
32354
|
}
|
@@ -32195,7 +32386,6 @@
|
|
32195
32386
|
var _this;
|
32196
32387
|
_this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this;
|
32197
32388
|
_this.audioCodecSwap = false;
|
32198
|
-
_this.gapController = null;
|
32199
32389
|
_this.level = -1;
|
32200
32390
|
_this._forceStartLoad = false;
|
32201
32391
|
_this._hasEnoughToStart = false;
|
@@ -32207,19 +32397,8 @@
|
|
32207
32397
|
_this.backtrackFragment = null;
|
32208
32398
|
_this.audioCodecSwitch = false;
|
32209
32399
|
_this.videoBuffer = null;
|
32210
|
-
_this.onMediaWaiting = function () {
|
32211
|
-
var gapController = _this.gapController;
|
32212
|
-
if (gapController) {
|
32213
|
-
gapController.waiting = self.performance.now();
|
32214
|
-
}
|
32215
|
-
};
|
32216
32400
|
_this.onMediaPlaying = function () {
|
32217
32401
|
// tick to speed up FRAG_CHANGED triggering
|
32218
|
-
var gapController = _this.gapController;
|
32219
|
-
if (gapController) {
|
32220
|
-
gapController.ended = 0;
|
32221
|
-
gapController.waiting = 0;
|
32222
|
-
}
|
32223
32402
|
_this.tick();
|
32224
32403
|
};
|
32225
32404
|
_this.onMediaSeeked = function () {
|
@@ -32273,7 +32452,7 @@
|
|
32273
32452
|
};
|
32274
32453
|
_proto.onHandlerDestroying = function onHandlerDestroying() {
|
32275
32454
|
// @ts-ignore
|
32276
|
-
this.onMediaPlaying = this.onMediaSeeked =
|
32455
|
+
this.onMediaPlaying = this.onMediaSeeked = null;
|
32277
32456
|
this.unregisterListeners();
|
32278
32457
|
_BaseStreamController.prototype.onHandlerDestroying.call(this);
|
32279
32458
|
};
|
@@ -32362,8 +32541,11 @@
|
|
32362
32541
|
this.onTickEnd();
|
32363
32542
|
};
|
32364
32543
|
_proto.onTickEnd = function onTickEnd() {
|
32544
|
+
var _this$media2;
|
32365
32545
|
_BaseStreamController.prototype.onTickEnd.call(this);
|
32366
|
-
this.
|
32546
|
+
if ((_this$media2 = this.media) != null && _this$media2.readyState) {
|
32547
|
+
this.lastCurrentTime = this.media.currentTime;
|
32548
|
+
}
|
32367
32549
|
this.checkFragmentChanged();
|
32368
32550
|
};
|
32369
32551
|
_proto.doTickIdle = function doTickIdle() {
|
@@ -32592,27 +32774,17 @@
|
|
32592
32774
|
_proto.onMediaAttached = function onMediaAttached(event, data) {
|
32593
32775
|
_BaseStreamController.prototype.onMediaAttached.call(this, event, data);
|
32594
32776
|
var media = data.media;
|
32595
|
-
media
|
32596
|
-
media
|
32597
|
-
media.removeEventListener('waiting', this.onMediaWaiting);
|
32598
|
-
media.addEventListener('playing', this.onMediaPlaying);
|
32599
|
-
media.addEventListener('seeked', this.onMediaSeeked);
|
32600
|
-
media.addEventListener('waiting', this.onMediaWaiting);
|
32601
|
-
this.gapController = new GapController(media, this.fragmentTracker, this.hls);
|
32777
|
+
addEventListener(media, 'playing', this.onMediaPlaying);
|
32778
|
+
addEventListener(media, 'seeked', this.onMediaSeeked);
|
32602
32779
|
};
|
32603
32780
|
_proto.onMediaDetaching = function onMediaDetaching(event, data) {
|
32604
32781
|
var media = this.media;
|
32605
32782
|
if (media) {
|
32606
|
-
|
32607
|
-
|
32608
|
-
media.removeEventListener('waiting', this.onMediaWaiting);
|
32783
|
+
removeEventListener(media, 'playing', this.onMediaPlaying);
|
32784
|
+
removeEventListener(media, 'seeked', this.onMediaSeeked);
|
32609
32785
|
}
|
32610
32786
|
this.videoBuffer = null;
|
32611
32787
|
this.fragPlaying = null;
|
32612
|
-
if (this.gapController) {
|
32613
|
-
this.gapController.destroy();
|
32614
|
-
this.gapController = null;
|
32615
|
-
}
|
32616
32788
|
_BaseStreamController.prototype.onMediaDetaching.call(this, event, data);
|
32617
32789
|
var transferringMedia = !!data.transferMedia;
|
32618
32790
|
if (transferringMedia) {
|
@@ -32620,19 +32792,6 @@
|
|
32620
32792
|
}
|
32621
32793
|
this._hasEnoughToStart = false;
|
32622
32794
|
};
|
32623
|
-
_proto.triggerEnded = function triggerEnded() {
|
32624
|
-
var gapController = this.gapController;
|
32625
|
-
if (gapController) {
|
32626
|
-
var _this$media2;
|
32627
|
-
if (gapController.ended) {
|
32628
|
-
return;
|
32629
|
-
}
|
32630
|
-
gapController.ended = ((_this$media2 = this.media) == null ? undefined : _this$media2.currentTime) || 1;
|
32631
|
-
}
|
32632
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
32633
|
-
stalled: false
|
32634
|
-
});
|
32635
|
-
};
|
32636
32795
|
_proto.onManifestLoading = function onManifestLoading() {
|
32637
32796
|
_BaseStreamController.prototype.onManifestLoading.call(this);
|
32638
32797
|
// reset buffer on manifest loading
|
@@ -32952,25 +33111,6 @@
|
|
32952
33111
|
this.recoverWorkerError(data);
|
32953
33112
|
break;
|
32954
33113
|
}
|
32955
|
-
}
|
32956
|
-
|
32957
|
-
// Checks the health of the buffer and attempts to resolve playback stalls.
|
32958
|
-
;
|
32959
|
-
_proto.checkBuffer = function checkBuffer() {
|
32960
|
-
var media = this.media,
|
32961
|
-
gapController = this.gapController;
|
32962
|
-
if (!media || !gapController || !media.readyState) {
|
32963
|
-
// Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0)
|
32964
|
-
return;
|
32965
|
-
}
|
32966
|
-
if (this._hasEnoughToStart || !BufferHelper.getBuffered(media).length) {
|
32967
|
-
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
|
32968
|
-
var state = this.state;
|
32969
|
-
var activeFrag = state !== State.IDLE ? this.fragCurrent : null;
|
32970
|
-
var levelDetails = this.getLevelDetails();
|
32971
|
-
gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state);
|
32972
|
-
}
|
32973
|
-
this.lastCurrentTime = media.currentTime;
|
32974
33114
|
};
|
32975
33115
|
_proto.onFragLoadEmergencyAborted = function onFragLoadEmergencyAborted() {
|
32976
33116
|
this.state = State.IDLE;
|
@@ -32986,8 +33126,10 @@
|
|
32986
33126
|
var type = _ref.type;
|
32987
33127
|
if (type !== ElementaryStreamTypes.AUDIO || !this.altAudio) {
|
32988
33128
|
var mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media;
|
32989
|
-
|
32990
|
-
|
33129
|
+
if (mediaBuffer) {
|
33130
|
+
this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);
|
33131
|
+
this.tick();
|
33132
|
+
}
|
32991
33133
|
}
|
32992
33134
|
};
|
32993
33135
|
_proto.onLevelsUpdated = function onLevelsUpdated(event, data) {
|
@@ -34306,9 +34448,12 @@
|
|
34306
34448
|
this.latencyController = undefined;
|
34307
34449
|
this.levelController = undefined;
|
34308
34450
|
this.streamController = undefined;
|
34451
|
+
this.audioStreamController = undefined;
|
34452
|
+
this.subtititleStreamController = undefined;
|
34309
34453
|
this.audioTrackController = undefined;
|
34310
34454
|
this.subtitleTrackController = undefined;
|
34311
34455
|
this.interstitialsController = undefined;
|
34456
|
+
this.gapController = undefined;
|
34312
34457
|
this.emeController = undefined;
|
34313
34458
|
this.cmcdController = undefined;
|
34314
34459
|
this._media = null;
|
@@ -34346,6 +34491,7 @@
|
|
34346
34491
|
var id3TrackController = new ID3TrackController(this);
|
34347
34492
|
var keyLoader = new KeyLoader(this.config);
|
34348
34493
|
var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
|
34494
|
+
var gapController = this.gapController = new GapController(this, fragmentTracker);
|
34349
34495
|
|
34350
34496
|
// Cap level controller uses streamController to flush the buffer
|
34351
34497
|
capLevelController.setStreamController(streamController);
|
@@ -34359,17 +34505,17 @@
|
|
34359
34505
|
networkControllers.splice(1, 0, contentSteering);
|
34360
34506
|
}
|
34361
34507
|
this.networkControllers = networkControllers;
|
34362
|
-
var coreComponents = [abrController, bufferController, capLevelController, fpsController, id3TrackController, fragmentTracker];
|
34508
|
+
var coreComponents = [abrController, bufferController, gapController, capLevelController, fpsController, id3TrackController, fragmentTracker];
|
34363
34509
|
this.audioTrackController = this.createController(config.audioTrackController, networkControllers);
|
34364
34510
|
var AudioStreamControllerClass = config.audioStreamController;
|
34365
34511
|
if (AudioStreamControllerClass) {
|
34366
|
-
networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
|
34512
|
+
networkControllers.push(this.audioStreamController = new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
|
34367
34513
|
}
|
34368
34514
|
// Instantiate subtitleTrackController before SubtitleStreamController to receive level events first
|
34369
34515
|
this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers);
|
34370
34516
|
var SubtitleStreamControllerClass = config.subtitleStreamController;
|
34371
34517
|
if (SubtitleStreamControllerClass) {
|
34372
|
-
networkControllers.push(new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader));
|
34518
|
+
networkControllers.push(this.subtititleStreamController = new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader));
|
34373
34519
|
}
|
34374
34520
|
this.createController(config.timelineController, coreComponents);
|
34375
34521
|
keyLoader.emeController = this.emeController = this.createController(config.emeController, coreComponents);
|
@@ -34644,11 +34790,10 @@
|
|
34644
34790
|
}
|
34645
34791
|
});
|
34646
34792
|
}
|
34647
|
-
}
|
34648
|
-
|
34793
|
+
};
|
34649
34794
|
/**
|
34650
34795
|
* Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
|
34651
|
-
|
34796
|
+
*/
|
34652
34797
|
_proto.swapAudioCodec = function swapAudioCodec() {
|
34653
34798
|
this.logger.log('swapAudioCodec');
|
34654
34799
|
this.streamController.swapAudioCodec();
|
@@ -34746,6 +34891,19 @@
|
|
34746
34891
|
get: function get() {
|
34747
34892
|
return this.streamController.bufferingEnabled;
|
34748
34893
|
}
|
34894
|
+
}, {
|
34895
|
+
key: "inFlightFragments",
|
34896
|
+
get: function get() {
|
34897
|
+
var _inFlightData;
|
34898
|
+
var inFlightData = (_inFlightData = {}, _inFlightData[PlaylistLevelType.MAIN] = this.streamController.inFlightFrag, _inFlightData);
|
34899
|
+
if (this.audioStreamController) {
|
34900
|
+
inFlightData[PlaylistLevelType.AUDIO] = this.audioStreamController.inFlightFrag;
|
34901
|
+
}
|
34902
|
+
if (this.subtititleStreamController) {
|
34903
|
+
inFlightData[PlaylistLevelType.SUBTITLE] = this.subtititleStreamController.inFlightFrag;
|
34904
|
+
}
|
34905
|
+
return inFlightData;
|
34906
|
+
}
|
34749
34907
|
}, {
|
34750
34908
|
key: "sessionId",
|
34751
34909
|
get: function get() {
|