hls.js 1.6.0-beta.2.0.canary.10882 → 1.6.0-beta.2.0.canary.10883
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 -0
- package/dist/hls.d.ts +21 -0
- package/dist/hls.js +109 -64
- package/dist/hls.js.d.ts +21 -0
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +109 -64
- 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 +106 -63
- 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 +106 -63
- 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/types/events.ts +3 -0
- package/src/utils/buffer-helper.ts +5 -8
package/dist/hls.d.mts
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;
|
@@ -3340,6 +3360,7 @@ export declare class StreamController extends BaseStreamController implements Ne
|
|
3340
3360
|
protected flushMainBuffer(startOffset: number, endOffset: number): void;
|
3341
3361
|
protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
|
3342
3362
|
protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
|
3363
|
+
private onMediaWaiting;
|
3343
3364
|
private onMediaPlaying;
|
3344
3365
|
private onMediaSeeked;
|
3345
3366
|
protected triggerEnded(): void;
|
package/dist/hls.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;
|
@@ -3340,6 +3360,7 @@ export declare class StreamController extends BaseStreamController implements Ne
|
|
3340
3360
|
protected flushMainBuffer(startOffset: number, endOffset: number): void;
|
3341
3361
|
protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
|
3342
3362
|
protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
|
3363
|
+
private onMediaWaiting;
|
3343
3364
|
private onMediaPlaying;
|
3344
3365
|
private onMediaSeeked;
|
3345
3366
|
protected triggerEnded(): void;
|
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.10883");
|
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
|
|
@@ -16320,7 +16321,7 @@
|
|
16320
16321
|
return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment;
|
16321
16322
|
}
|
16322
16323
|
|
16323
|
-
var version = "1.6.0-beta.2.0.canary.
|
16324
|
+
var version = "1.6.0-beta.2.0.canary.10883";
|
16324
16325
|
|
16325
16326
|
// ensure the worker ends up in the bundle
|
16326
16327
|
// If the worker should not be included this gets aliased to empty.js
|
@@ -30362,6 +30363,7 @@
|
|
30362
30363
|
progressive: false,
|
30363
30364
|
lowLatencyMode: true,
|
30364
30365
|
cmcd: undefined,
|
30366
|
+
detectStallWithCurrentTimeMs: 1250,
|
30365
30367
|
enableDateRangeMetadataCues: true,
|
30366
30368
|
enableEmsgMetadataCues: true,
|
30367
30369
|
enableEmsgKLVMetadata: false,
|
@@ -31787,25 +31789,23 @@
|
|
31787
31789
|
});
|
31788
31790
|
}
|
31789
31791
|
|
31790
|
-
var STALL_MINIMUM_DURATION_MS = 250;
|
31791
31792
|
var MAX_START_GAP_JUMP = 2.0;
|
31792
31793
|
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
|
31793
31794
|
var SKIP_BUFFER_RANGE_START = 0.05;
|
31794
31795
|
var GapController = /*#__PURE__*/function (_Logger) {
|
31795
|
-
function GapController(
|
31796
|
+
function GapController(media, fragmentTracker, hls) {
|
31796
31797
|
var _this;
|
31797
31798
|
_this = _Logger.call(this, 'gap-controller', hls.logger) || this;
|
31798
|
-
_this.config = undefined;
|
31799
31799
|
_this.media = null;
|
31800
|
-
_this.fragmentTracker =
|
31801
|
-
_this.hls =
|
31800
|
+
_this.fragmentTracker = null;
|
31801
|
+
_this.hls = null;
|
31802
31802
|
_this.nudgeRetry = 0;
|
31803
31803
|
_this.stallReported = false;
|
31804
31804
|
_this.stalled = null;
|
31805
31805
|
_this.moved = false;
|
31806
31806
|
_this.seeking = false;
|
31807
31807
|
_this.ended = 0;
|
31808
|
-
_this.
|
31808
|
+
_this.waiting = 0;
|
31809
31809
|
_this.media = media;
|
31810
31810
|
_this.fragmentTracker = fragmentTracker;
|
31811
31811
|
_this.hls = hls;
|
@@ -31814,9 +31814,7 @@
|
|
31814
31814
|
_inheritsLoose(GapController, _Logger);
|
31815
31815
|
var _proto = GapController.prototype;
|
31816
31816
|
_proto.destroy = function destroy() {
|
31817
|
-
this.media = null;
|
31818
|
-
// @ts-ignore
|
31819
|
-
this.hls = this.fragmentTracker = null;
|
31817
|
+
this.media = this.hls = this.fragmentTracker = null;
|
31820
31818
|
}
|
31821
31819
|
|
31822
31820
|
/**
|
@@ -31826,10 +31824,10 @@
|
|
31826
31824
|
* @param lastCurrentTime - Previously read playhead position
|
31827
31825
|
*/;
|
31828
31826
|
_proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
|
31829
|
-
var
|
31830
|
-
|
31827
|
+
var _this$hls;
|
31828
|
+
var media = this.media,
|
31831
31829
|
stalled = this.stalled;
|
31832
|
-
if (media
|
31830
|
+
if (!media) {
|
31833
31831
|
return;
|
31834
31832
|
}
|
31835
31833
|
var currentTime = media.currentTime,
|
@@ -31847,43 +31845,45 @@
|
|
31847
31845
|
if (!seeking) {
|
31848
31846
|
this.nudgeRetry = 0;
|
31849
31847
|
}
|
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;
|
31848
|
+
if (this.waiting === 0) {
|
31849
|
+
this.stallResolved(currentTime);
|
31858
31850
|
}
|
31859
31851
|
return;
|
31860
31852
|
}
|
31861
31853
|
|
31862
31854
|
// Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
|
31863
31855
|
if (beginSeek || seeked) {
|
31864
|
-
|
31856
|
+
if (seeked) {
|
31857
|
+
this.stallResolved(currentTime);
|
31858
|
+
}
|
31865
31859
|
return;
|
31866
31860
|
}
|
31867
31861
|
|
31868
31862
|
// The playhead should not be moving
|
31869
|
-
if (media.paused && !seeking || media.ended || media.playbackRate === 0
|
31863
|
+
if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
|
31864
|
+
this.nudgeRetry = 0;
|
31865
|
+
this.stallResolved(currentTime);
|
31870
31866
|
// Fire MEDIA_ENDED to workaround event not being dispatched by browser
|
31871
|
-
if (!this.ended && media.ended) {
|
31867
|
+
if (!this.ended && media.ended && this.hls) {
|
31872
31868
|
this.ended = currentTime || 1;
|
31873
31869
|
this.hls.trigger(Events.MEDIA_ENDED, {
|
31874
31870
|
stalled: false
|
31875
31871
|
});
|
31876
31872
|
}
|
31873
|
+
return;
|
31874
|
+
}
|
31875
|
+
if (!BufferHelper.getBuffered(media).length) {
|
31877
31876
|
this.nudgeRetry = 0;
|
31878
31877
|
return;
|
31879
31878
|
}
|
31880
31879
|
var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
|
31881
31880
|
var nextStart = bufferInfo.nextStart || 0;
|
31882
|
-
|
31881
|
+
var fragmentTracker = this.fragmentTracker;
|
31882
|
+
if (seeking && fragmentTracker) {
|
31883
31883
|
// Waiting for seeking in a buffered range to complete
|
31884
31884
|
var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
|
31885
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 && !
|
31886
|
+
var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
|
31887
31887
|
if (hasEnoughBuffer || noBufferGap) {
|
31888
31888
|
return;
|
31889
31889
|
}
|
@@ -31893,7 +31893,7 @@
|
|
31893
31893
|
|
31894
31894
|
// Skip start gaps if we haven't played, but the last poll detected the start of a stall
|
31895
31895
|
// The addition poll gives the browser a chance to jump the gap for us
|
31896
|
-
if (!this.moved && this.stalled !== null) {
|
31896
|
+
if (!this.moved && this.stalled !== null && fragmentTracker) {
|
31897
31897
|
// There is no playable buffer (seeked, waiting for buffer)
|
31898
31898
|
var isBuffered = bufferInfo.len > 0;
|
31899
31899
|
if (!isBuffered && !nextStart) {
|
@@ -31907,7 +31907,7 @@
|
|
31907
31907
|
// that begins over 1 target duration after the video start position.
|
31908
31908
|
var isLive = !!(levelDetails != null && levelDetails.live);
|
31909
31909
|
var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
|
31910
|
-
var partialOrGap =
|
31910
|
+
var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
|
31911
31911
|
if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
|
31912
31912
|
if (!media.paused) {
|
31913
31913
|
this._trySkipBufferHole(partialOrGap);
|
@@ -31917,16 +31917,27 @@
|
|
31917
31917
|
}
|
31918
31918
|
|
31919
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;
|
31920
31925
|
var tnow = self.performance.now();
|
31926
|
+
var tWaiting = this.waiting;
|
31921
31927
|
if (stalled === null) {
|
31922
|
-
|
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
|
+
}
|
31923
31934
|
return;
|
31924
31935
|
}
|
31925
31936
|
var stalledDuration = tnow - stalled;
|
31926
|
-
if (!seeking && stalledDuration >=
|
31937
|
+
if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
|
31927
31938
|
// Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
|
31928
31939
|
if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
|
31929
|
-
if (
|
31940
|
+
if (this.ended) {
|
31930
31941
|
return;
|
31931
31942
|
}
|
31932
31943
|
this.ended = currentTime || 1;
|
@@ -31937,12 +31948,26 @@
|
|
31937
31948
|
}
|
31938
31949
|
// Report stalling after trying to fix
|
31939
31950
|
this._reportStall(bufferInfo);
|
31940
|
-
if (!this.media) {
|
31951
|
+
if (!this.media || !this.hls) {
|
31941
31952
|
return;
|
31942
31953
|
}
|
31943
31954
|
}
|
31944
31955
|
var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
|
31945
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
|
+
}
|
31946
31971
|
}
|
31947
31972
|
|
31948
31973
|
/**
|
@@ -31952,10 +31977,11 @@
|
|
31952
31977
|
* @private
|
31953
31978
|
*/;
|
31954
31979
|
_proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
|
31955
|
-
var
|
31956
|
-
|
31980
|
+
var _this$hls2;
|
31981
|
+
var fragmentTracker = this.fragmentTracker,
|
31957
31982
|
media = this.media;
|
31958
|
-
|
31983
|
+
var config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
|
31984
|
+
if (!media || !fragmentTracker || !config) {
|
31959
31985
|
return;
|
31960
31986
|
}
|
31961
31987
|
var currentTime = media.currentTime;
|
@@ -31975,13 +32001,12 @@
|
|
31975
32001
|
// we may just have to "nudge" the playlist as the browser decoding/rendering engine
|
31976
32002
|
// needs to cross some sort of threshold covering all source-buffers content
|
31977
32003
|
// to start playing properly.
|
31978
|
-
|
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) {
|
31979
32006
|
this.warn('Trying to nudge playhead over buffer-hole');
|
31980
32007
|
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
|
31981
32008
|
// We only try to jump the hole if it's under the configured size
|
31982
|
-
|
31983
|
-
this.stalled = null;
|
31984
|
-
this._tryNudgeBuffer();
|
32009
|
+
this._tryNudgeBuffer(bufferInfo);
|
31985
32010
|
}
|
31986
32011
|
}
|
31987
32012
|
|
@@ -31993,8 +32018,9 @@
|
|
31993
32018
|
_proto._reportStall = function _reportStall(bufferInfo) {
|
31994
32019
|
var hls = this.hls,
|
31995
32020
|
media = this.media,
|
31996
|
-
stallReported = this.stallReported
|
31997
|
-
|
32021
|
+
stallReported = this.stallReported,
|
32022
|
+
stalled = this.stalled;
|
32023
|
+
if (!stallReported && stalled !== null && media && hls) {
|
31998
32024
|
// Report stalled error once
|
31999
32025
|
this.stallReported = true;
|
32000
32026
|
var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
|
@@ -32004,7 +32030,11 @@
|
|
32004
32030
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
32005
32031
|
fatal: false,
|
32006
32032
|
error: error,
|
32007
|
-
buffer: bufferInfo.len
|
32033
|
+
buffer: bufferInfo.len,
|
32034
|
+
bufferInfo: bufferInfo,
|
32035
|
+
stalled: {
|
32036
|
+
start: stalled
|
32037
|
+
}
|
32008
32038
|
});
|
32009
32039
|
}
|
32010
32040
|
}
|
@@ -32015,10 +32045,11 @@
|
|
32015
32045
|
* @private
|
32016
32046
|
*/;
|
32017
32047
|
_proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
|
32018
|
-
var
|
32019
|
-
|
32048
|
+
var _this$hls3;
|
32049
|
+
var fragmentTracker = this.fragmentTracker,
|
32020
32050
|
media = this.media;
|
32021
|
-
|
32051
|
+
var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
|
32052
|
+
if (!media || !fragmentTracker || !config) {
|
32022
32053
|
return 0;
|
32023
32054
|
}
|
32024
32055
|
|
@@ -32033,7 +32064,6 @@
|
|
32033
32064
|
if (gapLength > 0 && (bufferStarved || waiting)) {
|
32034
32065
|
// Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
|
32035
32066
|
if (gapLength > config.maxBufferHole) {
|
32036
|
-
var fragmentTracker = this.fragmentTracker;
|
32037
32067
|
var startGap = false;
|
32038
32068
|
if (currentTime === 0) {
|
32039
32069
|
var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
|
@@ -32064,17 +32094,18 @@
|
|
32064
32094
|
var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
|
32065
32095
|
this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
|
32066
32096
|
this.moved = true;
|
32067
|
-
this.stalled = null;
|
32068
32097
|
media.currentTime = targetTime;
|
32069
|
-
if (partial && !partial.gap) {
|
32098
|
+
if (partial && !partial.gap && this.hls) {
|
32070
32099
|
var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
|
32071
|
-
hls.trigger(Events.ERROR, {
|
32100
|
+
this.hls.trigger(Events.ERROR, {
|
32072
32101
|
type: ErrorTypes.MEDIA_ERROR,
|
32073
32102
|
details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
|
32074
32103
|
fatal: false,
|
32075
32104
|
error: error,
|
32076
32105
|
reason: error.message,
|
32077
|
-
frag: partial
|
32106
|
+
frag: partial,
|
32107
|
+
buffer: bufferInfo.len,
|
32108
|
+
bufferInfo: bufferInfo
|
32078
32109
|
});
|
32079
32110
|
}
|
32080
32111
|
return targetTime;
|
@@ -32087,13 +32118,13 @@
|
|
32087
32118
|
* Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
|
32088
32119
|
* @private
|
32089
32120
|
*/;
|
32090
|
-
_proto._tryNudgeBuffer = function _tryNudgeBuffer() {
|
32091
|
-
var
|
32092
|
-
hls = this.hls,
|
32121
|
+
_proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
|
32122
|
+
var hls = this.hls,
|
32093
32123
|
media = this.media,
|
32094
32124
|
nudgeRetry = this.nudgeRetry;
|
32095
|
-
|
32096
|
-
|
32125
|
+
var config = hls == null ? undefined : hls.config;
|
32126
|
+
if (!media || !config) {
|
32127
|
+
return 0;
|
32097
32128
|
}
|
32098
32129
|
var currentTime = media.currentTime;
|
32099
32130
|
this.nudgeRetry++;
|
@@ -32107,7 +32138,9 @@
|
|
32107
32138
|
type: ErrorTypes.MEDIA_ERROR,
|
32108
32139
|
details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
|
32109
32140
|
error: error,
|
32110
|
-
fatal: false
|
32141
|
+
fatal: false,
|
32142
|
+
buffer: bufferInfo.len,
|
32143
|
+
bufferInfo: bufferInfo
|
32111
32144
|
});
|
32112
32145
|
} else {
|
32113
32146
|
var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
|
@@ -32116,7 +32149,9 @@
|
|
32116
32149
|
type: ErrorTypes.MEDIA_ERROR,
|
32117
32150
|
details: ErrorDetails.BUFFER_STALLED_ERROR,
|
32118
32151
|
error: _error,
|
32119
|
-
fatal: true
|
32152
|
+
fatal: true,
|
32153
|
+
buffer: bufferInfo.len,
|
32154
|
+
bufferInfo: bufferInfo
|
32120
32155
|
});
|
32121
32156
|
}
|
32122
32157
|
};
|
@@ -32172,11 +32207,18 @@
|
|
32172
32207
|
_this.backtrackFragment = null;
|
32173
32208
|
_this.audioCodecSwitch = false;
|
32174
32209
|
_this.videoBuffer = null;
|
32210
|
+
_this.onMediaWaiting = function () {
|
32211
|
+
var gapController = _this.gapController;
|
32212
|
+
if (gapController) {
|
32213
|
+
gapController.waiting = self.performance.now();
|
32214
|
+
}
|
32215
|
+
};
|
32175
32216
|
_this.onMediaPlaying = function () {
|
32176
32217
|
// tick to speed up FRAG_CHANGED triggering
|
32177
32218
|
var gapController = _this.gapController;
|
32178
32219
|
if (gapController) {
|
32179
32220
|
gapController.ended = 0;
|
32221
|
+
gapController.waiting = 0;
|
32180
32222
|
}
|
32181
32223
|
_this.tick();
|
32182
32224
|
};
|
@@ -32231,7 +32273,7 @@
|
|
32231
32273
|
};
|
32232
32274
|
_proto.onHandlerDestroying = function onHandlerDestroying() {
|
32233
32275
|
// @ts-ignore
|
32234
|
-
this.onMediaPlaying = this.onMediaSeeked = null;
|
32276
|
+
this.onMediaPlaying = this.onMediaSeeked = this.onMediaWaiting = null;
|
32235
32277
|
this.unregisterListeners();
|
32236
32278
|
_BaseStreamController.prototype.onHandlerDestroying.call(this);
|
32237
32279
|
};
|
@@ -32552,15 +32594,18 @@
|
|
32552
32594
|
var media = data.media;
|
32553
32595
|
media.removeEventListener('playing', this.onMediaPlaying);
|
32554
32596
|
media.removeEventListener('seeked', this.onMediaSeeked);
|
32597
|
+
media.removeEventListener('waiting', this.onMediaWaiting);
|
32555
32598
|
media.addEventListener('playing', this.onMediaPlaying);
|
32556
32599
|
media.addEventListener('seeked', this.onMediaSeeked);
|
32557
|
-
|
32600
|
+
media.addEventListener('waiting', this.onMediaWaiting);
|
32601
|
+
this.gapController = new GapController(media, this.fragmentTracker, this.hls);
|
32558
32602
|
};
|
32559
32603
|
_proto.onMediaDetaching = function onMediaDetaching(event, data) {
|
32560
32604
|
var media = this.media;
|
32561
32605
|
if (media) {
|
32562
32606
|
media.removeEventListener('playing', this.onMediaPlaying);
|
32563
32607
|
media.removeEventListener('seeked', this.onMediaSeeked);
|
32608
|
+
media.removeEventListener('waiting', this.onMediaWaiting);
|
32564
32609
|
}
|
32565
32610
|
this.videoBuffer = null;
|
32566
32611
|
this.fragPlaying = null;
|
@@ -32970,7 +33015,7 @@
|
|
32970
33015
|
var startPosition = this.startPosition;
|
32971
33016
|
// only adjust currentTime if different from startPosition or if startPosition not buffered
|
32972
33017
|
// 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) {
|
33018
|
+
if (startPosition >= 0 && currentTime < startPosition) {
|
32974
33019
|
if (media.seeking) {
|
32975
33020
|
this.log("could not seek to " + startPosition + ", already seeking at " + currentTime);
|
32976
33021
|
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;
|
@@ -3340,6 +3360,7 @@ export declare class StreamController extends BaseStreamController implements Ne
|
|
3340
3360
|
protected flushMainBuffer(startOffset: number, endOffset: number): void;
|
3341
3361
|
protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
|
3342
3362
|
protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
|
3363
|
+
private onMediaWaiting;
|
3343
3364
|
private onMediaPlaying;
|
3344
3365
|
private onMediaSeeked;
|
3345
3366
|
protected triggerEnded(): void;
|