hls.js 1.5.7-0.canary.10014 → 1.5.7-0.canary.10016
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hls.js +279 -162
- package/dist/hls.js.d.ts +13 -6
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +262 -129
- 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 +203 -72
- 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 +220 -105
- 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/controller/abr-controller.ts +3 -0
- package/src/controller/audio-stream-controller.ts +26 -38
- package/src/controller/base-stream-controller.ts +5 -2
- package/src/controller/buffer-controller.ts +192 -56
- package/src/controller/buffer-operation-queue.ts +16 -19
- package/src/controller/fragment-tracker.ts +15 -11
- package/src/controller/stream-controller.ts +9 -0
- package/src/controller/subtitle-stream-controller.ts +1 -15
- package/src/hls.ts +7 -3
- package/src/utils/codecs.ts +1 -1
- package/src/utils/mp4-tools.ts +3 -1
package/dist/hls.mjs
CHANGED
@@ -512,7 +512,7 @@ function enableLogs(debugConfig, context, id) {
|
|
512
512
|
// Some browsers don't allow to use bind on console object anyway
|
513
513
|
// fallback to default if needed
|
514
514
|
try {
|
515
|
-
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.7-0.canary.
|
515
|
+
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.7-0.canary.10016"}`);
|
516
516
|
} catch (e) {
|
517
517
|
/* log fn threw an exception. All logger methods are no-ops. */
|
518
518
|
return createLogger();
|
@@ -1941,7 +1941,9 @@ function parseStsd(stsd) {
|
|
1941
1941
|
}
|
1942
1942
|
function skipBERInteger(bytes, i) {
|
1943
1943
|
const limit = i + 5;
|
1944
|
-
while (bytes[i++] & 0x80 && i < limit) {
|
1944
|
+
while (bytes[i++] & 0x80 && i < limit) {
|
1945
|
+
/* do nothing */
|
1946
|
+
}
|
1945
1947
|
return i;
|
1946
1948
|
}
|
1947
1949
|
function toHex(x) {
|
@@ -7290,6 +7292,9 @@ class AbrController extends Logger {
|
|
7290
7292
|
partCurrent,
|
7291
7293
|
hls
|
7292
7294
|
} = this;
|
7295
|
+
if (hls.levels.length <= 1) {
|
7296
|
+
return hls.loadLevel;
|
7297
|
+
}
|
7293
7298
|
const {
|
7294
7299
|
maxAutoLevel,
|
7295
7300
|
config,
|
@@ -7700,13 +7705,16 @@ class FragmentTracker {
|
|
7700
7705
|
* If not found any Fragment, return null
|
7701
7706
|
*/
|
7702
7707
|
getBufferedFrag(position, levelType) {
|
7708
|
+
return this.getFragAtPos(position, levelType, true);
|
7709
|
+
}
|
7710
|
+
getFragAtPos(position, levelType, buffered) {
|
7703
7711
|
const {
|
7704
7712
|
fragments
|
7705
7713
|
} = this;
|
7706
7714
|
const keys = Object.keys(fragments);
|
7707
7715
|
for (let i = keys.length; i--;) {
|
7708
7716
|
const fragmentEntity = fragments[keys[i]];
|
7709
|
-
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) {
|
7717
|
+
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) {
|
7710
7718
|
const frag = fragmentEntity.body;
|
7711
7719
|
if (frag.start <= position && position <= frag.end) {
|
7712
7720
|
return frag;
|
@@ -7961,7 +7969,8 @@ class FragmentTracker {
|
|
7961
7969
|
const {
|
7962
7970
|
frag,
|
7963
7971
|
part,
|
7964
|
-
timeRanges
|
7972
|
+
timeRanges,
|
7973
|
+
type
|
7965
7974
|
} = data;
|
7966
7975
|
if (frag.sn === 'initSegment') {
|
7967
7976
|
return;
|
@@ -7976,10 +7985,8 @@ class FragmentTracker {
|
|
7976
7985
|
}
|
7977
7986
|
// Store the latest timeRanges loaded in the buffer
|
7978
7987
|
this.timeRanges = timeRanges;
|
7979
|
-
|
7980
|
-
|
7981
|
-
this.detectEvictedFragments(elementaryStream, timeRange, playlistType, part);
|
7982
|
-
});
|
7988
|
+
const timeRange = timeRanges[type];
|
7989
|
+
this.detectEvictedFragments(type, timeRange, playlistType, part);
|
7983
7990
|
}
|
7984
7991
|
onFragBuffered(event, data) {
|
7985
7992
|
this.detectPartialFragments(data);
|
@@ -9934,7 +9941,7 @@ class BaseStreamController extends TaskLoop {
|
|
9934
9941
|
// Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
|
9935
9942
|
if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
|
9936
9943
|
const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
|
9937
|
-
if (bufferedFragAtPos && bufferInfo.nextStart
|
9944
|
+
if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) {
|
9938
9945
|
return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole));
|
9939
9946
|
}
|
9940
9947
|
}
|
@@ -16790,9 +16797,8 @@ class AudioStreamController extends BaseStreamController {
|
|
16790
16797
|
this.state = State.ENDED;
|
16791
16798
|
return;
|
16792
16799
|
}
|
16793
|
-
const mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);
|
16794
16800
|
const bufferLen = bufferInfo.len;
|
16795
|
-
const maxBufLen =
|
16801
|
+
const maxBufLen = hls.maxBufferLength;
|
16796
16802
|
const fragments = trackDetails.fragments;
|
16797
16803
|
const start = fragments[0].start;
|
16798
16804
|
let targetBufferTime = this.flushing ? this.getLoadPosition() : bufferInfo.end;
|
@@ -16827,32 +16833,25 @@ class AudioStreamController extends BaseStreamController {
|
|
16827
16833
|
this.bufferFlushed = true;
|
16828
16834
|
return;
|
16829
16835
|
}
|
16830
|
-
|
16831
|
-
|
16832
|
-
|
16833
|
-
|
16834
|
-
|
16835
|
-
|
16836
|
-
|
16837
|
-
|
16838
|
-
|
16839
|
-
|
16840
|
-
|
16841
|
-
|
16842
|
-
|
16843
|
-
|
16844
|
-
|
16836
|
+
if (!trackDetails.live || targetBufferTime < this.hls.liveSyncPosition) {
|
16837
|
+
// Request audio segments up to one fragment ahead of main buffer
|
16838
|
+
const mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN);
|
16839
|
+
const atBufferSyncLimit = !!mainBufferInfo && frag.start > mainBufferInfo.end + frag.duration;
|
16840
|
+
if (atBufferSyncLimit) {
|
16841
|
+
// Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo
|
16842
|
+
const mainFrag = this.fragmentTracker.getFragAtPos(frag.start, PlaylistLevelType.MAIN);
|
16843
|
+
if (mainFrag === null) {
|
16844
|
+
return;
|
16845
|
+
}
|
16846
|
+
// Bridge gaps in main buffer (also prevents loop loading at gaps)
|
16847
|
+
atGap || (atGap = !!mainFrag.gap || mainBufferInfo.len === 0);
|
16848
|
+
if (!atGap || bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {
|
16849
|
+
return;
|
16850
|
+
}
|
16845
16851
|
}
|
16846
16852
|
}
|
16847
16853
|
this.loadFragment(frag, levelInfo, targetBufferTime);
|
16848
16854
|
}
|
16849
|
-
getMaxBufferLength(mainBufferLength) {
|
16850
|
-
const maxConfigBuffer = super.getMaxBufferLength();
|
16851
|
-
if (!mainBufferLength) {
|
16852
|
-
return maxConfigBuffer;
|
16853
|
-
}
|
16854
|
-
return Math.min(Math.max(maxConfigBuffer, mainBufferLength), this.config.maxMaxBufferLength);
|
16855
|
-
}
|
16856
16855
|
onMediaDetaching() {
|
16857
16856
|
this.videoBuffer = null;
|
16858
16857
|
this.bufferFlushed = this.flushing = false;
|
@@ -17946,9 +17945,8 @@ class SubtitleStreamController extends BaseStreamController {
|
|
17946
17945
|
end: targetBufferTime,
|
17947
17946
|
len: bufferLen
|
17948
17947
|
} = bufferedInfo;
|
17949
|
-
const mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN);
|
17950
17948
|
const trackDetails = track.details;
|
17951
|
-
const maxBufLen = this.
|
17949
|
+
const maxBufLen = this.hls.maxBufferLength + trackDetails.levelTargetDuration;
|
17952
17950
|
if (bufferLen > maxBufLen) {
|
17953
17951
|
return;
|
17954
17952
|
}
|
@@ -17985,13 +17983,6 @@ class SubtitleStreamController extends BaseStreamController {
|
|
17985
17983
|
}
|
17986
17984
|
}
|
17987
17985
|
}
|
17988
|
-
getMaxBufferLength(mainBufferLength) {
|
17989
|
-
const maxConfigBuffer = super.getMaxBufferLength();
|
17990
|
-
if (!mainBufferLength) {
|
17991
|
-
return maxConfigBuffer;
|
17992
|
-
}
|
17993
|
-
return Math.max(maxConfigBuffer, mainBufferLength);
|
17994
|
-
}
|
17995
17986
|
loadFragment(frag, level, targetBufferTime) {
|
17996
17987
|
this.fragCurrent = frag;
|
17997
17988
|
if (frag.sn === 'initSegment') {
|
@@ -18489,24 +18480,22 @@ class BufferOperationQueue {
|
|
18489
18480
|
this.executeNext(type);
|
18490
18481
|
}
|
18491
18482
|
}
|
18492
|
-
insertAbort(operation, type) {
|
18493
|
-
const queue = this.queues[type];
|
18494
|
-
queue.unshift(operation);
|
18495
|
-
this.executeNext(type);
|
18496
|
-
}
|
18497
18483
|
appendBlocker(type) {
|
18498
|
-
|
18499
|
-
|
18500
|
-
|
18484
|
+
return new Promise(resolve => {
|
18485
|
+
const operation = {
|
18486
|
+
execute: resolve,
|
18487
|
+
onStart: () => {},
|
18488
|
+
onComplete: () => {},
|
18489
|
+
onError: () => {}
|
18490
|
+
};
|
18491
|
+
this.append(operation, type);
|
18501
18492
|
});
|
18502
|
-
|
18503
|
-
|
18504
|
-
|
18505
|
-
|
18506
|
-
|
18507
|
-
}
|
18508
|
-
this.append(operation, type);
|
18509
|
-
return promise;
|
18493
|
+
}
|
18494
|
+
unblockAudio(op) {
|
18495
|
+
const queue = this.queues.audio;
|
18496
|
+
if (queue[0] === op) {
|
18497
|
+
this.shiftAndExecuteNext('audio');
|
18498
|
+
}
|
18510
18499
|
}
|
18511
18500
|
executeNext(type) {
|
18512
18501
|
const queue = this.queues[type];
|
@@ -18539,7 +18528,7 @@ class BufferOperationQueue {
|
|
18539
18528
|
|
18540
18529
|
const VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;
|
18541
18530
|
class BufferController extends Logger {
|
18542
|
-
constructor(hls) {
|
18531
|
+
constructor(hls, fragmentTracker) {
|
18543
18532
|
super('buffer-controller', hls.logger);
|
18544
18533
|
// The level details used to determine duration, target-duration and live
|
18545
18534
|
this.details = null;
|
@@ -18550,6 +18539,7 @@ class BufferController extends Logger {
|
|
18550
18539
|
// References to event listeners for each SourceBuffer, so that they can be referenced for event removal
|
18551
18540
|
this.listeners = void 0;
|
18552
18541
|
this.hls = void 0;
|
18542
|
+
this.fragmentTracker = void 0;
|
18553
18543
|
// The number of BUFFER_CODEC events received before any sourceBuffers are created
|
18554
18544
|
this.bufferCodecEventsExpected = 0;
|
18555
18545
|
// The total number of BUFFER_CODEC events received
|
@@ -18560,6 +18550,10 @@ class BufferController extends Logger {
|
|
18560
18550
|
this.mediaSource = null;
|
18561
18551
|
// Last MP3 audio chunk appended
|
18562
18552
|
this.lastMpegAudioChunk = null;
|
18553
|
+
// Audio fragment blocked from appending until corresponding video appends or context changes
|
18554
|
+
this.blockedAudioAppend = null;
|
18555
|
+
// Keep track of video append position for unblocking audio
|
18556
|
+
this.lastVideoAppendEnd = 0;
|
18563
18557
|
this.appendSource = void 0;
|
18564
18558
|
// counters
|
18565
18559
|
this.appendErrors = {
|
@@ -18591,7 +18585,10 @@ class BufferController extends Logger {
|
|
18591
18585
|
this.log('Media source opened');
|
18592
18586
|
if (media) {
|
18593
18587
|
media.removeEventListener('emptied', this._onMediaEmptied);
|
18594
|
-
this.
|
18588
|
+
const durationAndRange = this.getDurationAndRange();
|
18589
|
+
if (durationAndRange) {
|
18590
|
+
this.updateMediaSource(durationAndRange);
|
18591
|
+
}
|
18595
18592
|
this.hls.trigger(Events.MEDIA_ATTACHED, {
|
18596
18593
|
media,
|
18597
18594
|
mediaSource: mediaSource
|
@@ -18619,6 +18616,7 @@ class BufferController extends Logger {
|
|
18619
18616
|
}
|
18620
18617
|
};
|
18621
18618
|
this.hls = hls;
|
18619
|
+
this.fragmentTracker = fragmentTracker;
|
18622
18620
|
this.appendSource = hls.config.preferManagedMediaSource;
|
18623
18621
|
this._initSourceBuffer();
|
18624
18622
|
this.registerListeners();
|
@@ -18631,7 +18629,7 @@ class BufferController extends Logger {
|
|
18631
18629
|
this.details = null;
|
18632
18630
|
this.lastMpegAudioChunk = null;
|
18633
18631
|
// @ts-ignore
|
18634
|
-
this.hls = null;
|
18632
|
+
this.hls = this.fragmentTracker = null;
|
18635
18633
|
// @ts-ignore
|
18636
18634
|
this._onMediaSourceOpen = this._onMediaSourceClose = null;
|
18637
18635
|
// @ts-ignore
|
@@ -18687,6 +18685,8 @@ class BufferController extends Logger {
|
|
18687
18685
|
audiovideo: 0
|
18688
18686
|
};
|
18689
18687
|
this.lastMpegAudioChunk = null;
|
18688
|
+
this.blockedAudioAppend = null;
|
18689
|
+
this.lastVideoAppendEnd = 0;
|
18690
18690
|
}
|
18691
18691
|
onManifestLoading() {
|
18692
18692
|
this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0;
|
@@ -18824,9 +18824,10 @@ class BufferController extends Logger {
|
|
18824
18824
|
const trackNames = Object.keys(data);
|
18825
18825
|
trackNames.forEach(trackName => {
|
18826
18826
|
if (sourceBufferCount) {
|
18827
|
+
var _track$buffer;
|
18827
18828
|
// check if SourceBuffer codec needs to change
|
18828
18829
|
const track = this.tracks[trackName];
|
18829
|
-
if (track && typeof track.buffer.changeType === 'function') {
|
18830
|
+
if (track && typeof ((_track$buffer = track.buffer) == null ? void 0 : _track$buffer.changeType) === 'function') {
|
18830
18831
|
var _trackCodec;
|
18831
18832
|
const {
|
18832
18833
|
id,
|
@@ -18896,20 +18897,54 @@ class BufferController extends Logger {
|
|
18896
18897
|
};
|
18897
18898
|
operationQueue.append(operation, type, !!this.pendingTracks[type]);
|
18898
18899
|
}
|
18900
|
+
blockAudio(partOrFrag) {
|
18901
|
+
var _this$fragmentTracker;
|
18902
|
+
const pStart = partOrFrag.start;
|
18903
|
+
const pTime = pStart + partOrFrag.duration * 0.05;
|
18904
|
+
const atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true;
|
18905
|
+
if (atGap) {
|
18906
|
+
return;
|
18907
|
+
}
|
18908
|
+
const op = {
|
18909
|
+
execute: () => {
|
18910
|
+
var _this$fragmentTracker2;
|
18911
|
+
if (this.lastVideoAppendEnd > pTime || this.sourceBuffer.video && BufferHelper.isBuffered(this.sourceBuffer.video, pTime) || ((_this$fragmentTracker2 = this.fragmentTracker.getAppendedFrag(pTime, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker2.gap) === true) {
|
18912
|
+
this.blockedAudioAppend = null;
|
18913
|
+
this.operationQueue.shiftAndExecuteNext('audio');
|
18914
|
+
}
|
18915
|
+
},
|
18916
|
+
onStart: () => {},
|
18917
|
+
onComplete: () => {},
|
18918
|
+
onError: () => {}
|
18919
|
+
};
|
18920
|
+
this.blockedAudioAppend = {
|
18921
|
+
op,
|
18922
|
+
frag: partOrFrag
|
18923
|
+
};
|
18924
|
+
this.operationQueue.append(op, 'audio', true);
|
18925
|
+
}
|
18926
|
+
unblockAudio() {
|
18927
|
+
const blockedAudioAppend = this.blockedAudioAppend;
|
18928
|
+
if (blockedAudioAppend) {
|
18929
|
+
this.blockedAudioAppend = null;
|
18930
|
+
this.operationQueue.unblockAudio(blockedAudioAppend.op);
|
18931
|
+
}
|
18932
|
+
}
|
18899
18933
|
onBufferAppending(event, eventData) {
|
18900
18934
|
const {
|
18901
|
-
hls,
|
18902
18935
|
operationQueue,
|
18903
18936
|
tracks
|
18904
18937
|
} = this;
|
18905
18938
|
const {
|
18906
18939
|
data,
|
18907
18940
|
type,
|
18941
|
+
parent,
|
18908
18942
|
frag,
|
18909
18943
|
part,
|
18910
18944
|
chunkMeta
|
18911
18945
|
} = eventData;
|
18912
18946
|
const chunkStats = chunkMeta.buffering[type];
|
18947
|
+
const sn = frag.sn;
|
18913
18948
|
const bufferAppendingStart = self.performance.now();
|
18914
18949
|
chunkStats.start = bufferAppendingStart;
|
18915
18950
|
const fragBuffering = frag.stats.buffering;
|
@@ -18932,7 +18967,36 @@ class BufferController extends Logger {
|
|
18932
18967
|
checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn;
|
18933
18968
|
this.lastMpegAudioChunk = chunkMeta;
|
18934
18969
|
}
|
18935
|
-
|
18970
|
+
|
18971
|
+
// Block audio append until overlapping video append
|
18972
|
+
const videoSb = this.sourceBuffer.video;
|
18973
|
+
if (videoSb && sn !== 'initSegment') {
|
18974
|
+
const partOrFrag = part || frag;
|
18975
|
+
const blockedAudioAppend = this.blockedAudioAppend;
|
18976
|
+
if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) {
|
18977
|
+
const pStart = partOrFrag.start;
|
18978
|
+
const pTime = pStart + partOrFrag.duration * 0.05;
|
18979
|
+
const vbuffered = videoSb.buffered;
|
18980
|
+
const vappending = this.operationQueue.current('video');
|
18981
|
+
if (!vbuffered.length && !vappending) {
|
18982
|
+
// wait for video before appending audio
|
18983
|
+
this.blockAudio(partOrFrag);
|
18984
|
+
} else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) {
|
18985
|
+
// audio is ahead of video
|
18986
|
+
this.blockAudio(partOrFrag);
|
18987
|
+
}
|
18988
|
+
} else if (type === 'video') {
|
18989
|
+
const videoAppendEnd = partOrFrag.end;
|
18990
|
+
if (blockedAudioAppend) {
|
18991
|
+
const audioStart = blockedAudioAppend.frag.start;
|
18992
|
+
if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) {
|
18993
|
+
this.unblockAudio();
|
18994
|
+
}
|
18995
|
+
}
|
18996
|
+
this.lastVideoAppendEnd = videoAppendEnd;
|
18997
|
+
}
|
18998
|
+
}
|
18999
|
+
const fragStart = (part || frag).start;
|
18936
19000
|
const operation = {
|
18937
19001
|
execute: () => {
|
18938
19002
|
chunkStats.executeStart = self.performance.now();
|
@@ -18941,7 +19005,7 @@ class BufferController extends Logger {
|
|
18941
19005
|
if (sb) {
|
18942
19006
|
const delta = fragStart - sb.timestampOffset;
|
18943
19007
|
if (Math.abs(delta) >= 0.1) {
|
18944
|
-
this.log(`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${
|
19008
|
+
this.log(`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${sn})`);
|
18945
19009
|
sb.timestampOffset = fragStart;
|
18946
19010
|
}
|
18947
19011
|
}
|
@@ -19008,22 +19072,21 @@ class BufferController extends Logger {
|
|
19008
19072
|
/* with UHD content, we could get loop of quota exceeded error until
|
19009
19073
|
browser is able to evict some data from sourcebuffer. Retrying can help recover.
|
19010
19074
|
*/
|
19011
|
-
this.warn(`Failed ${appendErrorCount}/${hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`);
|
19012
|
-
if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
|
19075
|
+
this.warn(`Failed ${appendErrorCount}/${this.hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`);
|
19076
|
+
if (appendErrorCount >= this.hls.config.appendErrorMaxRetry) {
|
19013
19077
|
event.fatal = true;
|
19014
19078
|
}
|
19015
19079
|
}
|
19016
|
-
hls.trigger(Events.ERROR, event);
|
19080
|
+
this.hls.trigger(Events.ERROR, event);
|
19017
19081
|
}
|
19018
19082
|
};
|
19019
19083
|
operationQueue.append(operation, type, !!this.pendingTracks[type]);
|
19020
19084
|
}
|
19021
|
-
|
19022
|
-
|
19023
|
-
|
19024
|
-
|
19025
|
-
|
19026
|
-
execute: this.removeExecutor.bind(this, type, data.startOffset, data.endOffset),
|
19085
|
+
getFlushOp(type, start, end) {
|
19086
|
+
return {
|
19087
|
+
execute: () => {
|
19088
|
+
this.removeExecutor(type, start, end);
|
19089
|
+
},
|
19027
19090
|
onStart: () => {
|
19028
19091
|
// logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
19029
19092
|
},
|
@@ -19036,12 +19099,22 @@ class BufferController extends Logger {
|
|
19036
19099
|
onError: error => {
|
19037
19100
|
this.warn(`Failed to remove from ${type} SourceBuffer`, error);
|
19038
19101
|
}
|
19039
|
-
}
|
19040
|
-
|
19041
|
-
|
19102
|
+
};
|
19103
|
+
}
|
19104
|
+
onBufferFlushing(event, data) {
|
19105
|
+
const {
|
19106
|
+
operationQueue
|
19107
|
+
} = this;
|
19108
|
+
const {
|
19109
|
+
type,
|
19110
|
+
startOffset,
|
19111
|
+
endOffset
|
19112
|
+
} = data;
|
19113
|
+
if (type) {
|
19114
|
+
operationQueue.append(this.getFlushOp(type, startOffset, endOffset), type);
|
19042
19115
|
} else {
|
19043
|
-
this.getSourceBufferTypes().forEach(
|
19044
|
-
operationQueue.append(
|
19116
|
+
this.getSourceBufferTypes().forEach(sbType => {
|
19117
|
+
operationQueue.append(this.getFlushOp(sbType, startOffset, endOffset), sbType);
|
19045
19118
|
});
|
19046
19119
|
}
|
19047
19120
|
}
|
@@ -19088,6 +19161,9 @@ class BufferController extends Logger {
|
|
19088
19161
|
// on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos()
|
19089
19162
|
// an undefined data.type will mark all buffers as EOS.
|
19090
19163
|
onBufferEos(event, data) {
|
19164
|
+
if (data.type === 'video') {
|
19165
|
+
this.unblockAudio();
|
19166
|
+
}
|
19091
19167
|
const ended = this.getSourceBufferTypes().reduce((acc, type) => {
|
19092
19168
|
const sb = this.sourceBuffer[type];
|
19093
19169
|
if (sb && (!data.type || data.type === type)) {
|
@@ -19130,10 +19206,14 @@ class BufferController extends Logger {
|
|
19130
19206
|
return;
|
19131
19207
|
}
|
19132
19208
|
this.details = details;
|
19209
|
+
const durationAndRange = this.getDurationAndRange();
|
19210
|
+
if (!durationAndRange) {
|
19211
|
+
return;
|
19212
|
+
}
|
19133
19213
|
if (this.getSourceBufferTypes().length) {
|
19134
|
-
this.blockBuffers(this.
|
19214
|
+
this.blockBuffers(() => this.updateMediaSource(durationAndRange));
|
19135
19215
|
} else {
|
19136
|
-
this.
|
19216
|
+
this.updateMediaSource(durationAndRange);
|
19137
19217
|
}
|
19138
19218
|
}
|
19139
19219
|
trimBuffers() {
|
@@ -19238,9 +19318,9 @@ class BufferController extends Logger {
|
|
19238
19318
|
* 'liveDurationInfinity` is set to `true`
|
19239
19319
|
* More details: https://github.com/video-dev/hls.js/issues/355
|
19240
19320
|
*/
|
19241
|
-
|
19321
|
+
getDurationAndRange() {
|
19242
19322
|
if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
|
19243
|
-
return;
|
19323
|
+
return null;
|
19244
19324
|
}
|
19245
19325
|
const {
|
19246
19326
|
details,
|
@@ -19254,25 +19334,41 @@ class BufferController extends Logger {
|
|
19254
19334
|
if (details.live && hls.config.liveDurationInfinity) {
|
19255
19335
|
// Override duration to Infinity
|
19256
19336
|
mediaSource.duration = Infinity;
|
19257
|
-
|
19337
|
+
const len = details.fragments.length;
|
19338
|
+
if (len && details.live && !!mediaSource.setLiveSeekableRange) {
|
19339
|
+
const start = Math.max(0, details.fragments[0].start);
|
19340
|
+
const end = Math.max(start, start + details.totalduration);
|
19341
|
+
return {
|
19342
|
+
duration: Infinity,
|
19343
|
+
start,
|
19344
|
+
end
|
19345
|
+
};
|
19346
|
+
}
|
19347
|
+
return {
|
19348
|
+
duration: Infinity
|
19349
|
+
};
|
19258
19350
|
} else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) {
|
19259
|
-
|
19260
|
-
|
19261
|
-
|
19262
|
-
// flushing already buffered portion when switching between quality level
|
19263
|
-
this.log(`Updating Media Source duration to ${levelDuration.toFixed(3)}`);
|
19264
|
-
mediaSource.duration = levelDuration;
|
19351
|
+
return {
|
19352
|
+
duration: levelDuration
|
19353
|
+
};
|
19265
19354
|
}
|
19355
|
+
return null;
|
19266
19356
|
}
|
19267
|
-
|
19268
|
-
|
19269
|
-
|
19270
|
-
|
19271
|
-
|
19272
|
-
|
19273
|
-
|
19274
|
-
|
19275
|
-
|
19357
|
+
updateMediaSource({
|
19358
|
+
duration,
|
19359
|
+
start,
|
19360
|
+
end
|
19361
|
+
}) {
|
19362
|
+
if (!this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') {
|
19363
|
+
return;
|
19364
|
+
}
|
19365
|
+
if (isFiniteNumber(duration)) {
|
19366
|
+
this.log(`Updating Media Source duration to ${duration.toFixed(3)}`);
|
19367
|
+
}
|
19368
|
+
this.mediaSource.duration = duration;
|
19369
|
+
if (start !== undefined && end !== undefined) {
|
19370
|
+
this.log(`Media Source duration is set to ${this.mediaSource.duration}. Setting seekable range to ${start}-${end}.`);
|
19371
|
+
this.mediaSource.setLiveSeekableRange(start, end);
|
19276
19372
|
}
|
19277
19373
|
}
|
19278
19374
|
checkPendingTracks() {
|
@@ -19455,6 +19551,7 @@ class BufferController extends Logger {
|
|
19455
19551
|
}
|
19456
19552
|
return;
|
19457
19553
|
}
|
19554
|
+
sb.ending = false;
|
19458
19555
|
sb.ended = false;
|
19459
19556
|
sb.appendBuffer(data);
|
19460
19557
|
}
|
@@ -19474,10 +19571,14 @@ class BufferController extends Logger {
|
|
19474
19571
|
|
19475
19572
|
// logger.debug(`[buffer-controller]: Blocking ${buffers} SourceBuffer`);
|
19476
19573
|
const blockingOperations = buffers.map(type => operationQueue.appendBlocker(type));
|
19477
|
-
|
19574
|
+
const audioBlocked = buffers.length > 1 && !!this.blockedAudioAppend;
|
19575
|
+
if (audioBlocked) {
|
19576
|
+
this.unblockAudio();
|
19577
|
+
}
|
19578
|
+
Promise.all(blockingOperations).then(result => {
|
19478
19579
|
// logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
|
19479
19580
|
onUnblocked();
|
19480
|
-
buffers.forEach(type => {
|
19581
|
+
buffers.forEach((type, i) => {
|
19481
19582
|
const sb = this.sourceBuffer[type];
|
19482
19583
|
// Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to
|
19483
19584
|
// true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration)
|
@@ -28496,6 +28597,17 @@ class StreamController extends BaseStreamController {
|
|
28496
28597
|
getMainFwdBufferInfo() {
|
28497
28598
|
return this.getFwdBufferInfo(this.mediaBuffer ? this.mediaBuffer : this.media, PlaylistLevelType.MAIN);
|
28498
28599
|
}
|
28600
|
+
get maxBufferLength() {
|
28601
|
+
const {
|
28602
|
+
levels,
|
28603
|
+
level
|
28604
|
+
} = this;
|
28605
|
+
const levelInfo = levels == null ? void 0 : levels[level];
|
28606
|
+
if (!levelInfo) {
|
28607
|
+
return this.config.maxBufferLength;
|
28608
|
+
}
|
28609
|
+
return this.getMaxBufferLength(levelInfo.maxBitrate);
|
28610
|
+
}
|
28499
28611
|
backtrack(frag) {
|
28500
28612
|
this.couldBacktrack = true;
|
28501
28613
|
// Causes findFragments to backtrack through fragments to find the keyframe
|
@@ -28601,7 +28713,7 @@ class Hls {
|
|
28601
28713
|
* Get the video-dev/hls.js package version.
|
28602
28714
|
*/
|
28603
28715
|
static get version() {
|
28604
|
-
return "1.5.7-0.canary.
|
28716
|
+
return "1.5.7-0.canary.10016";
|
28605
28717
|
}
|
28606
28718
|
|
28607
28719
|
/**
|
@@ -28703,7 +28815,9 @@ class Hls {
|
|
28703
28815
|
} = config;
|
28704
28816
|
const errorController = new ConfigErrorController(this);
|
28705
28817
|
const abrController = this.abrController = new ConfigAbrController(this);
|
28706
|
-
|
28818
|
+
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
28819
|
+
const fragmentTracker = new FragmentTracker(this);
|
28820
|
+
const bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker);
|
28707
28821
|
const capLevelController = this.capLevelController = new ConfigCapLevelController(this);
|
28708
28822
|
const fpsController = new ConfigFpsController(this);
|
28709
28823
|
const playListLoader = new PlaylistLoader(this);
|
@@ -28712,8 +28826,6 @@ class Hls {
|
|
28712
28826
|
// ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first
|
28713
28827
|
const contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null;
|
28714
28828
|
const levelController = this.levelController = new LevelController(this, contentSteering);
|
28715
|
-
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
28716
|
-
const fragmentTracker = new FragmentTracker(this);
|
28717
28829
|
const keyLoader = new KeyLoader(this.config);
|
28718
28830
|
const streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
|
28719
28831
|
|
@@ -29241,6 +29353,9 @@ class Hls {
|
|
29241
29353
|
get mainForwardBufferInfo() {
|
29242
29354
|
return this.streamController.getMainFwdBufferInfo();
|
29243
29355
|
}
|
29356
|
+
get maxBufferLength() {
|
29357
|
+
return this.streamController.maxBufferLength;
|
29358
|
+
}
|
29244
29359
|
|
29245
29360
|
/**
|
29246
29361
|
* Find and select the best matching audio track, making a level switch when a Group change is necessary.
|