hls.js 1.5.12-0.canary.10366 → 1.5.12-0.canary.10367
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 +293 -194
- package/dist/hls.js.d.ts +14 -8
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +211 -131
- 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 +208 -129
- 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 +290 -192
- 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 +1 -1
- package/src/controller/audio-stream-controller.ts +86 -84
- package/src/controller/base-stream-controller.ts +50 -41
- package/src/controller/buffer-controller.ts +20 -5
- package/src/controller/error-controller.ts +22 -14
- package/src/controller/fragment-finders.ts +33 -3
- package/src/controller/fragment-tracker.ts +30 -1
- package/src/controller/stream-controller.ts +54 -47
- package/src/controller/subtitle-stream-controller.ts +4 -4
- package/src/controller/timeline-controller.ts +1 -1
- package/src/hls.ts +1 -1
- package/src/loader/fragment-loader.ts +14 -19
- package/src/loader/fragment.ts +2 -2
- package/src/loader/level-details.ts +1 -2
- package/src/loader/playlist-loader.ts +1 -2
- package/src/remux/mp4-remuxer.ts +12 -8
- package/src/remux/passthrough-remuxer.ts +1 -1
- package/src/types/events.ts +13 -1
- package/src/utils/level-helper.ts +4 -5
- package/src/utils/rendition-helper.ts +84 -79
package/dist/hls.mjs
CHANGED
@@ -420,7 +420,7 @@ function enableLogs(debugConfig, context, id) {
|
|
420
420
|
// Some browsers don't allow to use bind on console object anyway
|
421
421
|
// fallback to default if needed
|
422
422
|
try {
|
423
|
-
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.12-0.canary.
|
423
|
+
newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.12-0.canary.10367"}`);
|
424
424
|
} catch (e) {
|
425
425
|
/* log fn threw an exception. All logger methods are no-ops. */
|
426
426
|
return createLogger();
|
@@ -3937,10 +3937,9 @@ class PlaylistLoader {
|
|
3937
3937
|
type
|
3938
3938
|
} = context;
|
3939
3939
|
const url = getResponseUrl(response, context);
|
3940
|
-
const levelUrlId = 0;
|
3941
3940
|
const levelId = isFiniteNumber(level) ? level : isFiniteNumber(id) ? id : 0;
|
3942
3941
|
const levelType = mapContextToLevelType(context);
|
3943
|
-
const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType,
|
3942
|
+
const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, 0, this.variableList);
|
3944
3943
|
|
3945
3944
|
// We have done our first request (Manifest-type) and receive
|
3946
3945
|
// not a master playlist but a chunk-list (track/level)
|
@@ -5830,8 +5829,7 @@ function computeReloadInterval(newDetails, distanceToLiveEdgeMs = Infinity) {
|
|
5830
5829
|
}
|
5831
5830
|
return Math.round(reloadInterval);
|
5832
5831
|
}
|
5833
|
-
function getFragmentWithSN(
|
5834
|
-
const details = level == null ? void 0 : level.details;
|
5832
|
+
function getFragmentWithSN(details, sn, fragCurrent) {
|
5835
5833
|
if (!details) {
|
5836
5834
|
return null;
|
5837
5835
|
}
|
@@ -5848,12 +5846,11 @@ function getFragmentWithSN(level, sn, fragCurrent) {
|
|
5848
5846
|
}
|
5849
5847
|
return null;
|
5850
5848
|
}
|
5851
|
-
function getPartWith(
|
5852
|
-
|
5853
|
-
if (!(level != null && level.details)) {
|
5849
|
+
function getPartWith(details, sn, partIndex) {
|
5850
|
+
if (!details) {
|
5854
5851
|
return null;
|
5855
5852
|
}
|
5856
|
-
return findPart(
|
5853
|
+
return findPart(details.partList, sn, partIndex);
|
5857
5854
|
}
|
5858
5855
|
function findPart(partList, sn, partIndex) {
|
5859
5856
|
if (partList) {
|
@@ -5997,12 +5994,15 @@ function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) {
|
|
5997
5994
|
function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0, nextFragLookupTolerance = 0.005) {
|
5998
5995
|
let fragNext = null;
|
5999
5996
|
if (fragPrevious) {
|
6000
|
-
fragNext = fragments[fragPrevious.sn - fragments[0].sn
|
5997
|
+
fragNext = fragments[1 + fragPrevious.sn - fragments[0].sn] || null;
|
6001
5998
|
// check for buffer-end rounding error
|
6002
5999
|
const bufferEdgeError = fragPrevious.endDTS - bufferEnd;
|
6003
6000
|
if (bufferEdgeError > 0 && bufferEdgeError < 0.0000015) {
|
6004
6001
|
bufferEnd += 0.0000015;
|
6005
6002
|
}
|
6003
|
+
if (fragNext && fragPrevious.level !== fragNext.level && fragNext.end <= fragPrevious.end) {
|
6004
|
+
fragNext = fragments[2 + fragPrevious.sn - fragments[0].sn] || null;
|
6005
|
+
}
|
6006
6006
|
} else if (bufferEnd === 0 && fragments[0].start === 0) {
|
6007
6007
|
fragNext = fragments[0];
|
6008
6008
|
}
|
@@ -6093,6 +6093,24 @@ function findFragWithCC(fragments, cc) {
|
|
6093
6093
|
}
|
6094
6094
|
});
|
6095
6095
|
}
|
6096
|
+
function findNearestWithCC(details, cc, fragment) {
|
6097
|
+
if (details) {
|
6098
|
+
if (details.startCC <= cc && details.endCC >= cc) {
|
6099
|
+
const start = fragment.start;
|
6100
|
+
const end = fragment.end;
|
6101
|
+
return BinarySearch.search(details.fragments, candidate => {
|
6102
|
+
if (candidate.cc < cc || candidate.end <= start) {
|
6103
|
+
return 1;
|
6104
|
+
} else if (candidate.cc > cc || candidate.start >= end) {
|
6105
|
+
return -1;
|
6106
|
+
} else {
|
6107
|
+
return 0;
|
6108
|
+
}
|
6109
|
+
});
|
6110
|
+
}
|
6111
|
+
}
|
6112
|
+
return null;
|
6113
|
+
}
|
6096
6114
|
|
6097
6115
|
var NetworkErrorAction = {
|
6098
6116
|
DoNothing: 0,
|
@@ -6154,7 +6172,7 @@ class ErrorController extends Logger {
|
|
6154
6172
|
this.playlistError = 0;
|
6155
6173
|
}
|
6156
6174
|
onError(event, data) {
|
6157
|
-
var _data$frag
|
6175
|
+
var _data$frag;
|
6158
6176
|
if (data.fatal) {
|
6159
6177
|
return;
|
6160
6178
|
}
|
@@ -6170,10 +6188,7 @@ class ErrorController extends Logger {
|
|
6170
6188
|
case ErrorDetails.FRAG_PARSING_ERROR:
|
6171
6189
|
// ignore empty segment errors marked as gap
|
6172
6190
|
if ((_data$frag = data.frag) != null && _data$frag.gap) {
|
6173
|
-
data.errorAction =
|
6174
|
-
action: NetworkErrorAction.DoNothing,
|
6175
|
-
flags: ErrorActionFlags.None
|
6176
|
-
};
|
6191
|
+
data.errorAction = createDoNothingErrorAction();
|
6177
6192
|
return;
|
6178
6193
|
}
|
6179
6194
|
// falls through
|
@@ -6240,7 +6255,11 @@ class ErrorController extends Logger {
|
|
6240
6255
|
case ErrorDetails.BUFFER_ADD_CODEC_ERROR:
|
6241
6256
|
case ErrorDetails.REMUX_ALLOC_ERROR:
|
6242
6257
|
case ErrorDetails.BUFFER_APPEND_ERROR:
|
6243
|
-
|
6258
|
+
// Buffer-controller can set errorAction when append errors can be ignored or resolved locally
|
6259
|
+
if (!data.errorAction) {
|
6260
|
+
var _data$level;
|
6261
|
+
data.errorAction = this.getLevelSwitchAction(data, (_data$level = data.level) != null ? _data$level : hls.loadLevel);
|
6262
|
+
}
|
6244
6263
|
return;
|
6245
6264
|
case ErrorDetails.INTERNAL_EXCEPTION:
|
6246
6265
|
case ErrorDetails.BUFFER_APPENDING_ERROR:
|
@@ -6249,10 +6268,7 @@ class ErrorController extends Logger {
|
|
6249
6268
|
case ErrorDetails.BUFFER_STALLED_ERROR:
|
6250
6269
|
case ErrorDetails.BUFFER_SEEK_OVER_HOLE:
|
6251
6270
|
case ErrorDetails.BUFFER_NUDGE_ON_STALL:
|
6252
|
-
data.errorAction =
|
6253
|
-
action: NetworkErrorAction.DoNothing,
|
6254
|
-
flags: ErrorActionFlags.None
|
6255
|
-
};
|
6271
|
+
data.errorAction = createDoNothingErrorAction();
|
6256
6272
|
return;
|
6257
6273
|
}
|
6258
6274
|
if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) {
|
@@ -6465,6 +6481,13 @@ class ErrorController extends Logger {
|
|
6465
6481
|
}
|
6466
6482
|
}
|
6467
6483
|
}
|
6484
|
+
function createDoNothingErrorAction(resolved) {
|
6485
|
+
const errorAction = {
|
6486
|
+
action: NetworkErrorAction.DoNothing,
|
6487
|
+
flags: ErrorActionFlags.None
|
6488
|
+
};
|
6489
|
+
return errorAction;
|
6490
|
+
}
|
6468
6491
|
|
6469
6492
|
class BasePlaylistController extends Logger {
|
6470
6493
|
constructor(hls, logPrefix) {
|
@@ -7042,6 +7065,7 @@ function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPrefer
|
|
7042
7065
|
if (!hasCurrentVideoRange) {
|
7043
7066
|
currentVideoRange = undefined;
|
7044
7067
|
}
|
7068
|
+
const hasMultipleSets = codecSets.length > 1;
|
7045
7069
|
const codecSet = codecSets.reduce((selected, candidate) => {
|
7046
7070
|
// Remove candiates which do not meet bitrate, default audio, stereo or channels preference, 1080p or lower, 30fps or lower, or SDR/HDR selection if present
|
7047
7071
|
const candidateTier = codecTiers[candidate];
|
@@ -7049,46 +7073,48 @@ function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPrefer
|
|
7049
7073
|
return selected;
|
7050
7074
|
}
|
7051
7075
|
videoRanges = hasCurrentVideoRange ? allowedVideoRanges.filter(range => candidateTier.videoRanges[range] > 0) : [];
|
7052
|
-
if (
|
7053
|
-
|
7054
|
-
|
7055
|
-
|
7056
|
-
|
7057
|
-
|
7058
|
-
|
7059
|
-
|
7060
|
-
|
7061
|
-
|
7062
|
-
|
7063
|
-
|
7064
|
-
|
7065
|
-
if (!
|
7066
|
-
|
7076
|
+
if (hasMultipleSets) {
|
7077
|
+
if (candidateTier.minBitrate > currentBw) {
|
7078
|
+
logStartCodecCandidateIgnored(candidate, `min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`);
|
7079
|
+
return selected;
|
7080
|
+
}
|
7081
|
+
if (!candidateTier.hasDefaultAudio) {
|
7082
|
+
logStartCodecCandidateIgnored(candidate, `no renditions with default or auto-select sound found`);
|
7083
|
+
return selected;
|
7084
|
+
}
|
7085
|
+
if (audioCodecPreference && candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0) {
|
7086
|
+
logStartCodecCandidateIgnored(candidate, `audio codec preference "${audioCodecPreference}" not found`);
|
7087
|
+
return selected;
|
7088
|
+
}
|
7089
|
+
if (channelsPreference && !preferStereo) {
|
7090
|
+
if (!candidateTier.channels[channelsPreference]) {
|
7091
|
+
logStartCodecCandidateIgnored(candidate, `no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(candidateTier.channels)})`);
|
7092
|
+
return selected;
|
7093
|
+
}
|
7094
|
+
} else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels['2'] === 0) {
|
7095
|
+
logStartCodecCandidateIgnored(candidate, `no renditions with stereo sound found`);
|
7096
|
+
return selected;
|
7097
|
+
}
|
7098
|
+
if (candidateTier.minHeight > maxHeight) {
|
7099
|
+
logStartCodecCandidateIgnored(candidate, `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`);
|
7100
|
+
return selected;
|
7101
|
+
}
|
7102
|
+
if (candidateTier.minFramerate > maxFramerate) {
|
7103
|
+
logStartCodecCandidateIgnored(candidate, `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`);
|
7104
|
+
return selected;
|
7105
|
+
}
|
7106
|
+
if (!videoRanges.some(range => candidateTier.videoRanges[range] > 0)) {
|
7107
|
+
logStartCodecCandidateIgnored(candidate, `no variants with VIDEO-RANGE of ${JSON.stringify(videoRanges)} found`);
|
7108
|
+
return selected;
|
7109
|
+
}
|
7110
|
+
if (videoCodecPreference && candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0) {
|
7111
|
+
logStartCodecCandidateIgnored(candidate, `video codec preference "${videoCodecPreference}" not found`);
|
7112
|
+
return selected;
|
7113
|
+
}
|
7114
|
+
if (candidateTier.maxScore < selectedScore) {
|
7115
|
+
logStartCodecCandidateIgnored(candidate, `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`);
|
7067
7116
|
return selected;
|
7068
7117
|
}
|
7069
|
-
} else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels['2'] === 0) {
|
7070
|
-
logStartCodecCandidateIgnored(candidate, `no renditions with stereo sound found`);
|
7071
|
-
return selected;
|
7072
|
-
}
|
7073
|
-
if (candidateTier.minHeight > maxHeight) {
|
7074
|
-
logStartCodecCandidateIgnored(candidate, `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`);
|
7075
|
-
return selected;
|
7076
|
-
}
|
7077
|
-
if (candidateTier.minFramerate > maxFramerate) {
|
7078
|
-
logStartCodecCandidateIgnored(candidate, `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`);
|
7079
|
-
return selected;
|
7080
|
-
}
|
7081
|
-
if (!videoRanges.some(range => candidateTier.videoRanges[range] > 0)) {
|
7082
|
-
logStartCodecCandidateIgnored(candidate, `no variants with VIDEO-RANGE of ${JSON.stringify(videoRanges)} found`);
|
7083
|
-
return selected;
|
7084
|
-
}
|
7085
|
-
if (videoCodecPreference && candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0) {
|
7086
|
-
logStartCodecCandidateIgnored(candidate, `video codec preference "${videoCodecPreference}" not found`);
|
7087
|
-
return selected;
|
7088
|
-
}
|
7089
|
-
if (candidateTier.maxScore < selectedScore) {
|
7090
|
-
logStartCodecCandidateIgnored(candidate, `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`);
|
7091
|
-
return selected;
|
7092
7118
|
}
|
7093
7119
|
// Remove candiates with less preferred codecs or more errors
|
7094
7120
|
if (selected && (codecsSetSelectionPreferenceValue(candidate) >= codecsSetSelectionPreferenceValue(selected) || candidateTier.fragmentError > codecTiers[selected].fragmentError)) {
|
@@ -7895,7 +7921,7 @@ class AbrController extends Logger {
|
|
7895
7921
|
if (levelsSkipped.length) {
|
7896
7922
|
this.trace(`Skipped level(s) ${levelsSkipped.join(',')} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${level.codecs}" ${currentVideoRange}`);
|
7897
7923
|
}
|
7898
|
-
this.info(`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${
|
7924
|
+
this.info(`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${levelInfo.codecSet} videoRange:${levelInfo.videoRange} hls.loadLevel:${loadLevel}`);
|
7899
7925
|
}
|
7900
7926
|
if (firstSelection) {
|
7901
7927
|
this.firstSelection = i;
|
@@ -8064,6 +8090,7 @@ class FragmentTracker {
|
|
8064
8090
|
const {
|
8065
8091
|
hls
|
8066
8092
|
} = this;
|
8093
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
8067
8094
|
hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
|
8068
8095
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
8069
8096
|
hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);
|
@@ -8072,6 +8099,7 @@ class FragmentTracker {
|
|
8072
8099
|
const {
|
8073
8100
|
hls
|
8074
8101
|
} = this;
|
8102
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
8075
8103
|
hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
|
8076
8104
|
hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
8077
8105
|
hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);
|
@@ -8137,7 +8165,7 @@ class FragmentTracker {
|
|
8137
8165
|
* The browser will unload parts of the buffer to free up memory for new buffer data
|
8138
8166
|
* Fragments will need to be reloaded when the buffer is freed up, removing partial fragments will allow them to reload(since there might be parts that are still playable)
|
8139
8167
|
*/
|
8140
|
-
detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart) {
|
8168
|
+
detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart, removeAppending) {
|
8141
8169
|
if (this.timeRanges) {
|
8142
8170
|
this.timeRanges[elementaryStream] = timeRange;
|
8143
8171
|
}
|
@@ -8152,7 +8180,7 @@ class FragmentTracker {
|
|
8152
8180
|
if (appendedPartSn >= fragmentEntity.body.sn) {
|
8153
8181
|
return;
|
8154
8182
|
}
|
8155
|
-
if (!fragmentEntity.buffered && !fragmentEntity.loaded) {
|
8183
|
+
if (!fragmentEntity.buffered && (!fragmentEntity.loaded || removeAppending)) {
|
8156
8184
|
if (fragmentEntity.body.type === playlistType) {
|
8157
8185
|
this.removeFragment(fragmentEntity.body);
|
8158
8186
|
}
|
@@ -8162,6 +8190,10 @@ class FragmentTracker {
|
|
8162
8190
|
if (!esData) {
|
8163
8191
|
return;
|
8164
8192
|
}
|
8193
|
+
if (esData.time.length === 0) {
|
8194
|
+
this.removeFragment(fragmentEntity.body);
|
8195
|
+
return;
|
8196
|
+
}
|
8165
8197
|
esData.time.some(time => {
|
8166
8198
|
const isNotBuffered = !this.isTimeBuffered(time.startPTS, time.endPTS, timeRange);
|
8167
8199
|
if (isNotBuffered) {
|
@@ -8348,6 +8380,9 @@ class FragmentTracker {
|
|
8348
8380
|
}
|
8349
8381
|
return false;
|
8350
8382
|
}
|
8383
|
+
onManifestLoading() {
|
8384
|
+
this.removeAllFragments();
|
8385
|
+
}
|
8351
8386
|
onFragLoaded(event, data) {
|
8352
8387
|
// don't track initsegment (for which sn is not a number)
|
8353
8388
|
// don't track frags used for bitrateTest, they're irrelevant.
|
@@ -8396,6 +8431,22 @@ class FragmentTracker {
|
|
8396
8431
|
const fragKey = getFragmentKey(fragment);
|
8397
8432
|
return !!this.fragments[fragKey];
|
8398
8433
|
}
|
8434
|
+
hasFragments(type) {
|
8435
|
+
const {
|
8436
|
+
fragments
|
8437
|
+
} = this;
|
8438
|
+
const keys = Object.keys(fragments);
|
8439
|
+
if (!type) {
|
8440
|
+
return keys.length > 0;
|
8441
|
+
}
|
8442
|
+
for (let i = keys.length; i--;) {
|
8443
|
+
const fragmentEntity = fragments[keys[i]];
|
8444
|
+
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === type) {
|
8445
|
+
return true;
|
8446
|
+
}
|
8447
|
+
}
|
8448
|
+
return false;
|
8449
|
+
}
|
8399
8450
|
hasParts(type) {
|
8400
8451
|
var _this$activePartLists;
|
8401
8452
|
return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);
|
@@ -8800,8 +8851,9 @@ class FragmentLoader {
|
|
8800
8851
|
frag.gap = false;
|
8801
8852
|
}
|
8802
8853
|
}
|
8803
|
-
const loader = this.loader =
|
8854
|
+
const loader = this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config);
|
8804
8855
|
const loaderContext = createLoaderContext(frag);
|
8856
|
+
frag.loader = loader;
|
8805
8857
|
const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default);
|
8806
8858
|
const loaderConfig = {
|
8807
8859
|
loadPolicy,
|
@@ -8894,8 +8946,9 @@ class FragmentLoader {
|
|
8894
8946
|
reject(createGapLoadError(frag, part));
|
8895
8947
|
return;
|
8896
8948
|
}
|
8897
|
-
const loader = this.loader =
|
8949
|
+
const loader = this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config);
|
8898
8950
|
const loaderContext = createLoaderContext(frag, part);
|
8951
|
+
frag.loader = loader;
|
8899
8952
|
// Should we define another load policy for parts?
|
8900
8953
|
const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default);
|
8901
8954
|
const loaderConfig = {
|
@@ -9590,6 +9643,7 @@ class BaseStreamController extends TaskLoop {
|
|
9590
9643
|
this.initPTS = [];
|
9591
9644
|
this.buffering = true;
|
9592
9645
|
this.loadingParts = false;
|
9646
|
+
this.loopSn = void 0;
|
9593
9647
|
this.onMediaSeeking = () => {
|
9594
9648
|
const {
|
9595
9649
|
config,
|
@@ -9689,6 +9743,9 @@ class BaseStreamController extends TaskLoop {
|
|
9689
9743
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
9690
9744
|
startLoad(startPosition) {}
|
9691
9745
|
stopLoad() {
|
9746
|
+
if (this.state === State.STOPPED) {
|
9747
|
+
return;
|
9748
|
+
}
|
9692
9749
|
this.fragmentLoader.abort();
|
9693
9750
|
this.keyLoader.abort(this.playlistType);
|
9694
9751
|
const frag = this.fragCurrent;
|
@@ -9762,15 +9819,20 @@ class BaseStreamController extends TaskLoop {
|
|
9762
9819
|
this.keyLoader.detach();
|
9763
9820
|
}
|
9764
9821
|
this.media = this.mediaBuffer = null;
|
9765
|
-
this.
|
9822
|
+
this.loopSn = undefined;
|
9823
|
+
this.startFragRequested = this.loadedmetadata = this.loadingParts = false;
|
9766
9824
|
this.fragmentTracker.removeAllFragments();
|
9767
9825
|
this.stopLoad();
|
9768
9826
|
}
|
9769
|
-
onManifestLoading() {
|
9827
|
+
onManifestLoading() {
|
9828
|
+
this.initPTS = [];
|
9829
|
+
this.levels = this.levelLastLoaded = this.fragCurrent = null;
|
9830
|
+
this.lastCurrentTime = this.startPosition = 0;
|
9831
|
+
this.startFragRequested = false;
|
9832
|
+
}
|
9770
9833
|
onError(event, data) {}
|
9771
9834
|
onManifestLoaded(event, data) {
|
9772
9835
|
this.startTimeOffset = data.startTimeOffset;
|
9773
|
-
this.initPTS = [];
|
9774
9836
|
}
|
9775
9837
|
onHandlerDestroying() {
|
9776
9838
|
this.stopLoad();
|
@@ -9793,6 +9855,7 @@ class BaseStreamController extends TaskLoop {
|
|
9793
9855
|
super.onHandlerDestroyed();
|
9794
9856
|
}
|
9795
9857
|
loadFragment(frag, level, targetBufferTime) {
|
9858
|
+
this.startFragRequested = true;
|
9796
9859
|
this._loadFragForPlayback(frag, level, targetBufferTime);
|
9797
9860
|
}
|
9798
9861
|
_loadFragForPlayback(frag, level, targetBufferTime) {
|
@@ -10021,6 +10084,7 @@ class BaseStreamController extends TaskLoop {
|
|
10021
10084
|
_handleFragmentLoadProgress(frag) {}
|
10022
10085
|
_doFragLoad(frag, level, targetBufferTime = null, progressCallback) {
|
10023
10086
|
var _frag$decryptdata;
|
10087
|
+
this.fragCurrent = frag;
|
10024
10088
|
const details = level == null ? void 0 : level.details;
|
10025
10089
|
if (!this.levels || !details) {
|
10026
10090
|
throw new Error(`frag load aborted, missing level${details ? '' : ' detail'}s`);
|
@@ -10148,7 +10212,7 @@ class BaseStreamController extends TaskLoop {
|
|
10148
10212
|
partsLoaded[part.index] = partLoadedData;
|
10149
10213
|
const loadedPart = partLoadedData.part;
|
10150
10214
|
this.hls.trigger(Events.FRAG_LOADED, partLoadedData);
|
10151
|
-
const nextPart = getPartWith(level, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1);
|
10215
|
+
const nextPart = getPartWith(level.details, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1);
|
10152
10216
|
if (nextPart) {
|
10153
10217
|
loadPart(nextPart);
|
10154
10218
|
} else {
|
@@ -10243,8 +10307,9 @@ class BaseStreamController extends TaskLoop {
|
|
10243
10307
|
return null;
|
10244
10308
|
}
|
10245
10309
|
const level = levels[levelIndex];
|
10246
|
-
const
|
10247
|
-
const
|
10310
|
+
const levelDetails = level.details;
|
10311
|
+
const part = partIndex > -1 ? getPartWith(levelDetails, sn, partIndex) : null;
|
10312
|
+
const frag = part ? part.fragment : getFragmentWithSN(levelDetails, sn, fragCurrent);
|
10248
10313
|
if (!frag) {
|
10249
10314
|
return null;
|
10250
10315
|
}
|
@@ -10359,7 +10424,8 @@ class BaseStreamController extends TaskLoop {
|
|
10359
10424
|
return false;
|
10360
10425
|
}
|
10361
10426
|
getAppendedFrag(position, playlistType = PlaylistLevelType.MAIN) {
|
10362
|
-
|
10427
|
+
var _this$fragmentTracker;
|
10428
|
+
const fragOrPart = (_this$fragmentTracker = this.fragmentTracker) == null ? void 0 : _this$fragmentTracker.getAppendedFrag(position, playlistType);
|
10363
10429
|
if (fragOrPart && 'fragment' in fragOrPart) {
|
10364
10430
|
return fragOrPart.fragment;
|
10365
10431
|
}
|
@@ -10414,22 +10480,25 @@ class BaseStreamController extends TaskLoop {
|
|
10414
10480
|
return (trackerState === FragmentState.OK || trackerState === FragmentState.PARTIAL && !!frag.gap) && this.nextLoadPosition > targetBufferTime;
|
10415
10481
|
}
|
10416
10482
|
getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, playlistType, maxBufLen) {
|
10417
|
-
|
10418
|
-
|
10419
|
-
|
10420
|
-
|
10421
|
-
|
10422
|
-
|
10423
|
-
|
10424
|
-
|
10425
|
-
|
10426
|
-
|
10427
|
-
|
10428
|
-
|
10429
|
-
|
10483
|
+
let nextFragment = null;
|
10484
|
+
if (frag.gap) {
|
10485
|
+
nextFragment = this.getNextFragment(this.nextLoadPosition, levelDetails);
|
10486
|
+
if (nextFragment && !nextFragment.gap && bufferInfo.nextStart) {
|
10487
|
+
// Media buffered after GAP tags should not make the next buffer timerange exceed forward buffer length
|
10488
|
+
const nextbufferInfo = this.getFwdBufferInfoAtPos(this.mediaBuffer ? this.mediaBuffer : this.media, bufferInfo.nextStart, playlistType);
|
10489
|
+
if (nextbufferInfo !== null && bufferInfo.len + nextbufferInfo.len >= maxBufLen) {
|
10490
|
+
// Returning here might result in not finding an audio and video candiate to skip to
|
10491
|
+
const sn = nextFragment.sn;
|
10492
|
+
if (this.loopSn !== sn) {
|
10493
|
+
this.log(`buffer full after gaps in "${playlistType}" playlist starting at sn: ${sn}`);
|
10494
|
+
this.loopSn = sn;
|
10495
|
+
}
|
10496
|
+
return null;
|
10497
|
+
}
|
10430
10498
|
}
|
10431
10499
|
}
|
10432
|
-
|
10500
|
+
this.loopSn = undefined;
|
10501
|
+
return nextFragment;
|
10433
10502
|
}
|
10434
10503
|
mapToInitFragWhenRequired(frag) {
|
10435
10504
|
// If an initSegment is present, it must be buffered first
|
@@ -10638,7 +10707,7 @@ class BaseStreamController extends TaskLoop {
|
|
10638
10707
|
if (startTimeOffset !== null && isFiniteNumber(startTimeOffset)) {
|
10639
10708
|
startPosition = sliding + startTimeOffset;
|
10640
10709
|
if (startTimeOffset < 0) {
|
10641
|
-
startPosition += details.
|
10710
|
+
startPosition += details.edge;
|
10642
10711
|
}
|
10643
10712
|
startPosition = Math.min(Math.max(sliding, startPosition), sliding + details.totalduration);
|
10644
10713
|
this.log(`Start time offset ${startTimeOffset} found in ${offsetInMultivariantPlaylist ? 'multivariant' : 'media'} playlist, adjust startPosition to ${startPosition}`);
|
@@ -14980,10 +15049,11 @@ class MP4Remuxer {
|
|
14980
15049
|
if (this.ISGenerated) {
|
14981
15050
|
var _videoTrack$pixelRati, _config$pixelRatio, _videoTrack$pixelRati2, _config$pixelRatio2;
|
14982
15051
|
const config = this.videoTrackConfig;
|
14983
|
-
if (config && (videoTrack.width !== config.width || videoTrack.height !== config.height || ((_videoTrack$pixelRati = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati[0]) !== ((_config$pixelRatio = config.pixelRatio) == null ? void 0 : _config$pixelRatio[0]) || ((_videoTrack$pixelRati2 = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati2[1]) !== ((_config$pixelRatio2 = config.pixelRatio) == null ? void 0 : _config$pixelRatio2[1]))) {
|
15052
|
+
if (config && (videoTrack.width !== config.width || videoTrack.height !== config.height || ((_videoTrack$pixelRati = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati[0]) !== ((_config$pixelRatio = config.pixelRatio) == null ? void 0 : _config$pixelRatio[0]) || ((_videoTrack$pixelRati2 = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati2[1]) !== ((_config$pixelRatio2 = config.pixelRatio) == null ? void 0 : _config$pixelRatio2[1])) || !config && enoughVideoSamples || this.nextAudioPts === null && enoughAudioSamples) {
|
14984
15053
|
this.resetInitSegment();
|
14985
15054
|
}
|
14986
|
-
}
|
15055
|
+
}
|
15056
|
+
if (!this.ISGenerated) {
|
14987
15057
|
initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);
|
14988
15058
|
}
|
14989
15059
|
const isVideoContiguous = this.isVideoContiguous;
|
@@ -15901,7 +15971,7 @@ class PassThroughRemuxer {
|
|
15901
15971
|
if (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale && accurateTimeOffset) {
|
15902
15972
|
initSegment.initPTS = decodeTime - timeOffset;
|
15903
15973
|
if (initPTS && initPTS.timescale === 1) {
|
15904
|
-
logger.warn(`Adjusting initPTS
|
15974
|
+
logger.warn(`Adjusting initPTS @${timeOffset} from ${initPTS.baseTime / initPTS.timescale} to ${initSegment.initPTS}`);
|
15905
15975
|
}
|
15906
15976
|
this.initPTS = initPTS = {
|
15907
15977
|
baseTime: initSegment.initPTS,
|
@@ -17035,9 +17105,8 @@ const TICK_INTERVAL$2 = 100; // how often to tick in ms
|
|
17035
17105
|
class AudioStreamController extends BaseStreamController {
|
17036
17106
|
constructor(hls, fragmentTracker, keyLoader) {
|
17037
17107
|
super(hls, fragmentTracker, keyLoader, 'audio-stream-controller', PlaylistLevelType.AUDIO);
|
17038
|
-
this.
|
17039
|
-
this.
|
17040
|
-
this.waitingVideoCC = -1;
|
17108
|
+
this.videoAnchor = null;
|
17109
|
+
this.mainFragLoading = null;
|
17041
17110
|
this.bufferedTrack = null;
|
17042
17111
|
this.switchingTrack = null;
|
17043
17112
|
this.trackId = -1;
|
@@ -17069,6 +17138,7 @@ class AudioStreamController extends BaseStreamController {
|
|
17069
17138
|
hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
|
17070
17139
|
hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
|
17071
17140
|
hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);
|
17141
|
+
hls.on(Events.FRAG_LOADING, this.onFragLoading, this);
|
17072
17142
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
17073
17143
|
}
|
17074
17144
|
unregisterListeners() {
|
@@ -17088,6 +17158,7 @@ class AudioStreamController extends BaseStreamController {
|
|
17088
17158
|
hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
|
17089
17159
|
hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
|
17090
17160
|
hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);
|
17161
|
+
hls.on(Events.FRAG_LOADING, this.onFragLoading, this);
|
17091
17162
|
hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
17092
17163
|
}
|
17093
17164
|
|
@@ -17100,20 +17171,35 @@ class AudioStreamController extends BaseStreamController {
|
|
17100
17171
|
}) {
|
17101
17172
|
// Always update the new INIT PTS
|
17102
17173
|
// Can change due level switch
|
17103
|
-
if (id ===
|
17174
|
+
if (id === PlaylistLevelType.MAIN) {
|
17104
17175
|
const cc = frag.cc;
|
17105
|
-
|
17176
|
+
const inFlightFrag = this.fragCurrent;
|
17177
|
+
this.initPTS[cc] = {
|
17106
17178
|
baseTime: initPTS,
|
17107
17179
|
timescale
|
17108
17180
|
};
|
17109
17181
|
this.log(`InitPTS for cc: ${cc} found from main: ${initPTS}/${timescale}`);
|
17110
|
-
this.
|
17182
|
+
this.videoAnchor = frag;
|
17111
17183
|
// If we are waiting, tick immediately to unblock audio fragment transmuxing
|
17112
17184
|
if (this.state === State.WAITING_INIT_PTS) {
|
17185
|
+
const waitingData = this.waitingData;
|
17186
|
+
if (!waitingData || waitingData.frag.cc !== cc) {
|
17187
|
+
this.nextLoadPosition = this.findSyncFrag(frag).start;
|
17188
|
+
}
|
17113
17189
|
this.tick();
|
17190
|
+
} else if (!this.loadedmetadata && inFlightFrag && inFlightFrag.cc !== cc) {
|
17191
|
+
this.startFragRequested = false;
|
17192
|
+
this.nextLoadPosition = this.findSyncFrag(frag).start;
|
17193
|
+
inFlightFrag.abortRequests();
|
17194
|
+
this.resetLoadingState();
|
17114
17195
|
}
|
17115
17196
|
}
|
17116
17197
|
}
|
17198
|
+
findSyncFrag(mainFrag) {
|
17199
|
+
const trackDetails = this.getLevelDetails();
|
17200
|
+
const cc = mainFrag.cc;
|
17201
|
+
return findNearestWithCC(trackDetails, cc, mainFrag) || trackDetails && findFragWithCC(trackDetails.fragments, cc) || mainFrag;
|
17202
|
+
}
|
17117
17203
|
startLoad(startPosition) {
|
17118
17204
|
if (!this.levels) {
|
17119
17205
|
this.startPosition = startPosition;
|
@@ -17183,9 +17269,9 @@ class AudioStreamController extends BaseStreamController {
|
|
17183
17269
|
cache,
|
17184
17270
|
complete
|
17185
17271
|
} = waitingData;
|
17272
|
+
const videoAnchor = this.videoAnchor;
|
17186
17273
|
if (this.initPTS[frag.cc] !== undefined) {
|
17187
17274
|
this.waitingData = null;
|
17188
|
-
this.waitingVideoCC = -1;
|
17189
17275
|
this.state = State.FRAG_LOADING;
|
17190
17276
|
const payload = cache.flush();
|
17191
17277
|
const data = {
|
@@ -17198,21 +17284,13 @@ class AudioStreamController extends BaseStreamController {
|
|
17198
17284
|
if (complete) {
|
17199
17285
|
super._handleFragmentLoadComplete(data);
|
17200
17286
|
}
|
17201
|
-
} else if (
|
17287
|
+
} else if (videoAnchor && videoAnchor.cc !== waitingData.frag.cc) {
|
17202
17288
|
// Drop waiting fragment if videoTrackCC has changed since waitingFragment was set and initPTS was not found
|
17203
|
-
this.log(`Waiting fragment cc (${frag.cc}) cancelled because video is at cc ${
|
17289
|
+
this.log(`Waiting fragment cc (${frag.cc}) cancelled because video is at cc ${videoAnchor.cc}`);
|
17290
|
+
this.nextLoadPosition = this.findSyncFrag(videoAnchor).start;
|
17204
17291
|
this.clearWaitingFragment();
|
17205
|
-
} else {
|
17206
|
-
// Drop waiting fragment if an earlier fragment is needed
|
17207
|
-
const pos = this.getLoadPosition();
|
17208
|
-
const bufferInfo = BufferHelper.bufferInfo(this.mediaBuffer, pos, this.config.maxBufferHole);
|
17209
|
-
const waitingFragmentAtPosition = fragmentWithinToleranceTest(bufferInfo.end, this.config.maxFragLookUpTolerance, frag);
|
17210
|
-
if (waitingFragmentAtPosition < 0) {
|
17211
|
-
this.log(`Waiting fragment cc (${frag.cc}) @ ${frag.start} cancelled because another fragment at ${bufferInfo.end} is needed`);
|
17212
|
-
this.clearWaitingFragment();
|
17213
|
-
}
|
17214
17292
|
}
|
17215
|
-
} else {
|
17293
|
+
} else if (this.state !== State.STOPPED) {
|
17216
17294
|
this.state = State.IDLE;
|
17217
17295
|
}
|
17218
17296
|
}
|
@@ -17222,9 +17300,12 @@ class AudioStreamController extends BaseStreamController {
|
|
17222
17300
|
clearWaitingFragment() {
|
17223
17301
|
const waitingData = this.waitingData;
|
17224
17302
|
if (waitingData) {
|
17303
|
+
if (!this.loadedmetadata) {
|
17304
|
+
// Load overlapping fragment on start when discontinuity start times are not aligned
|
17305
|
+
this.startFragRequested = false;
|
17306
|
+
}
|
17225
17307
|
this.fragmentTracker.removeFragment(waitingData.frag);
|
17226
17308
|
this.waitingData = null;
|
17227
|
-
this.waitingVideoCC = -1;
|
17228
17309
|
if (this.state !== State.STOPPED) {
|
17229
17310
|
this.state = State.IDLE;
|
17230
17311
|
}
|
@@ -17292,9 +17373,10 @@ class AudioStreamController extends BaseStreamController {
|
|
17292
17373
|
const maxBufLen = hls.maxBufferLength;
|
17293
17374
|
const fragments = trackDetails.fragments;
|
17294
17375
|
const start = fragments[0].start;
|
17295
|
-
|
17376
|
+
const loadPosition = this.getLoadPosition();
|
17377
|
+
let targetBufferTime = this.flushing ? loadPosition : bufferInfo.end;
|
17296
17378
|
if (switchingTrack && media) {
|
17297
|
-
const pos =
|
17379
|
+
const pos = loadPosition;
|
17298
17380
|
// STABLE
|
17299
17381
|
if (bufferedTrack && !mediaAttributesIdentical(switchingTrack.attrs, bufferedTrack.attrs)) {
|
17300
17382
|
targetBufferTime = pos;
|
@@ -17314,37 +17396,26 @@ class AudioStreamController extends BaseStreamController {
|
|
17314
17396
|
return;
|
17315
17397
|
}
|
17316
17398
|
let frag = this.getNextFragment(targetBufferTime, trackDetails);
|
17317
|
-
let atGap = false;
|
17318
17399
|
// Avoid loop loading by using nextLoadPosition set for backtracking and skipping consecutive GAP tags
|
17319
17400
|
if (frag && this.isLoopLoading(frag, targetBufferTime)) {
|
17320
|
-
atGap = !!frag.gap;
|
17321
17401
|
frag = this.getNextFragmentLoopLoading(frag, trackDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen);
|
17322
17402
|
}
|
17323
17403
|
if (!frag) {
|
17324
17404
|
this.bufferFlushed = true;
|
17325
17405
|
return;
|
17326
17406
|
}
|
17327
|
-
if (!trackDetails.live || targetBufferTime < this.hls.liveSyncPosition) {
|
17407
|
+
if (this.startFragRequested && (!trackDetails.live || targetBufferTime < this.hls.liveSyncPosition)) {
|
17328
17408
|
// Request audio segments up to one fragment ahead of main buffer
|
17329
|
-
const
|
17330
|
-
const
|
17331
|
-
|
17332
|
-
|
17333
|
-
|
17334
|
-
if (mainFrag === null) {
|
17335
|
-
return;
|
17336
|
-
}
|
17337
|
-
// Bridge gaps in main buffer (also prevents loop loading at gaps)
|
17338
|
-
atGap || (atGap = !!mainFrag.gap || mainBufferInfo.len === 0);
|
17339
|
-
if (!atGap || bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) {
|
17340
|
-
return;
|
17341
|
-
}
|
17409
|
+
const mainFragLoading = this.mainFragLoading;
|
17410
|
+
const mainTargetBufferEnd = mainFragLoading ? (mainFragLoading.part || mainFragLoading.frag).end : null;
|
17411
|
+
const atBufferSyncLimit = mainTargetBufferEnd !== null && frag.start > mainTargetBufferEnd;
|
17412
|
+
if (atBufferSyncLimit && !frag.endList) {
|
17413
|
+
return;
|
17342
17414
|
}
|
17343
17415
|
}
|
17344
17416
|
this.loadFragment(frag, levelInfo, targetBufferTime);
|
17345
17417
|
}
|
17346
17418
|
onMediaDetaching() {
|
17347
|
-
this.videoBuffer = null;
|
17348
17419
|
this.bufferFlushed = this.flushing = false;
|
17349
17420
|
super.onMediaDetaching();
|
17350
17421
|
}
|
@@ -17388,12 +17459,10 @@ class AudioStreamController extends BaseStreamController {
|
|
17388
17459
|
}
|
17389
17460
|
}
|
17390
17461
|
onManifestLoading() {
|
17391
|
-
|
17392
|
-
this.startPosition = this.lastCurrentTime = 0;
|
17462
|
+
super.onManifestLoading();
|
17393
17463
|
this.bufferFlushed = this.flushing = false;
|
17394
|
-
this.
|
17395
|
-
this.
|
17396
|
-
this.trackId = this.videoTrackCC = this.waitingVideoCC = -1;
|
17464
|
+
this.mainDetails = this.waitingData = this.videoAnchor = this.bufferedTrack = this.cachedTrackLoadedData = this.switchingTrack = null;
|
17465
|
+
this.trackId = -1;
|
17397
17466
|
}
|
17398
17467
|
onLevelLoaded(event, data) {
|
17399
17468
|
this.mainDetails = data.details;
|
@@ -17509,7 +17578,6 @@ class AudioStreamController extends BaseStreamController {
|
|
17509
17578
|
complete: false
|
17510
17579
|
};
|
17511
17580
|
cache.push(new Uint8Array(payload));
|
17512
|
-
this.waitingVideoCC = this.videoTrackCC;
|
17513
17581
|
this.state = State.WAITING_INIT_PTS;
|
17514
17582
|
}
|
17515
17583
|
}
|
@@ -17523,7 +17591,7 @@ class AudioStreamController extends BaseStreamController {
|
|
17523
17591
|
onBufferReset( /* event: Events.BUFFER_RESET */
|
17524
17592
|
) {
|
17525
17593
|
// reset reference to sourcebuffers
|
17526
|
-
this.mediaBuffer =
|
17594
|
+
this.mediaBuffer = null;
|
17527
17595
|
this.loadedmetadata = false;
|
17528
17596
|
}
|
17529
17597
|
onBufferCreated(event, data) {
|
@@ -17531,8 +17599,13 @@ class AudioStreamController extends BaseStreamController {
|
|
17531
17599
|
if (audioTrack) {
|
17532
17600
|
this.mediaBuffer = audioTrack.buffer || null;
|
17533
17601
|
}
|
17534
|
-
|
17535
|
-
|
17602
|
+
}
|
17603
|
+
onFragLoading(event, data) {
|
17604
|
+
if (data.frag.type === PlaylistLevelType.MAIN && data.frag.sn !== 'initSegment') {
|
17605
|
+
this.mainFragLoading = data;
|
17606
|
+
if (this.state === State.IDLE) {
|
17607
|
+
this.tick();
|
17608
|
+
}
|
17536
17609
|
}
|
17537
17610
|
}
|
17538
17611
|
onFragBuffered(event, data) {
|
@@ -17542,12 +17615,9 @@ class AudioStreamController extends BaseStreamController {
|
|
17542
17615
|
} = data;
|
17543
17616
|
if (frag.type !== PlaylistLevelType.AUDIO) {
|
17544
17617
|
if (!this.loadedmetadata && frag.type === PlaylistLevelType.MAIN) {
|
17545
|
-
const
|
17546
|
-
if (
|
17547
|
-
|
17548
|
-
if (bufferedTimeRanges.length) {
|
17549
|
-
this.loadedmetadata = true;
|
17550
|
-
}
|
17618
|
+
const bufferedState = this.fragmentTracker.getState(frag);
|
17619
|
+
if (bufferedState === FragmentState.OK || bufferedState === FragmentState.PARTIAL) {
|
17620
|
+
this.loadedmetadata = true;
|
17551
17621
|
}
|
17552
17622
|
}
|
17553
17623
|
return;
|
@@ -17728,12 +17798,15 @@ class AudioStreamController extends BaseStreamController {
|
|
17728
17798
|
if (tracks.video) {
|
17729
17799
|
delete tracks.video;
|
17730
17800
|
}
|
17801
|
+
if (tracks.audiovideo) {
|
17802
|
+
delete tracks.audiovideo;
|
17803
|
+
}
|
17731
17804
|
|
17732
17805
|
// include levelCodec in audio and video tracks
|
17733
|
-
|
17734
|
-
if (!track) {
|
17806
|
+
if (!tracks.audio) {
|
17735
17807
|
return;
|
17736
17808
|
}
|
17809
|
+
const track = tracks.audio;
|
17737
17810
|
track.id = 'audio';
|
17738
17811
|
const variantAudioCodecs = currentLevel.audioCodec;
|
17739
17812
|
this.log(`Init audio buffer, container:${track.container}, codecs[level/parsed]=[${variantAudioCodecs}/${track.codec}]`);
|
@@ -17760,7 +17833,6 @@ class AudioStreamController extends BaseStreamController {
|
|
17760
17833
|
loadFragment(frag, track, targetBufferTime) {
|
17761
17834
|
// only load if fragment is not loaded or if in audio switch
|
17762
17835
|
const fragState = this.fragmentTracker.getState(frag);
|
17763
|
-
this.fragCurrent = frag;
|
17764
17836
|
|
17765
17837
|
// we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch
|
17766
17838
|
if (this.switchingTrack || fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) {
|
@@ -17775,7 +17847,6 @@ class AudioStreamController extends BaseStreamController {
|
|
17775
17847
|
alignMediaPlaylistByPDT(track.details, mainDetails);
|
17776
17848
|
}
|
17777
17849
|
} else {
|
17778
|
-
this.startFragRequested = true;
|
17779
17850
|
super.loadFragment(frag, track, targetBufferTime);
|
17780
17851
|
}
|
17781
17852
|
} else {
|
@@ -18172,8 +18243,8 @@ class SubtitleStreamController extends BaseStreamController {
|
|
18172
18243
|
this.tick();
|
18173
18244
|
}
|
18174
18245
|
onManifestLoading() {
|
18246
|
+
super.onManifestLoading();
|
18175
18247
|
this.mainDetails = null;
|
18176
|
-
this.fragmentTracker.removeAllFragments();
|
18177
18248
|
}
|
18178
18249
|
onMediaDetaching() {
|
18179
18250
|
this.tracksBuffered = [];
|
@@ -18187,7 +18258,9 @@ class SubtitleStreamController extends BaseStreamController {
|
|
18187
18258
|
frag,
|
18188
18259
|
success
|
18189
18260
|
} = data;
|
18190
|
-
|
18261
|
+
if (frag.sn !== 'initSegment') {
|
18262
|
+
this.fragPrevious = frag;
|
18263
|
+
}
|
18191
18264
|
this.state = State.IDLE;
|
18192
18265
|
if (!success) {
|
18193
18266
|
return;
|
@@ -18480,11 +18553,9 @@ class SubtitleStreamController extends BaseStreamController {
|
|
18480
18553
|
}
|
18481
18554
|
}
|
18482
18555
|
loadFragment(frag, level, targetBufferTime) {
|
18483
|
-
this.fragCurrent = frag;
|
18484
18556
|
if (frag.sn === 'initSegment') {
|
18485
18557
|
this._loadInitSegment(frag, level);
|
18486
18558
|
} else {
|
18487
|
-
this.startFragRequested = true;
|
18488
18559
|
super.loadFragment(frag, level, targetBufferTime);
|
18489
18560
|
}
|
18490
18561
|
}
|
@@ -19149,6 +19220,7 @@ class BufferController extends Logger {
|
|
19149
19220
|
hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
|
19150
19221
|
hls.on(Events.FRAG_PARSED, this.onFragParsed, this);
|
19151
19222
|
hls.on(Events.FRAG_CHANGED, this.onFragChanged, this);
|
19223
|
+
hls.on(Events.ERROR, this.onError, this);
|
19152
19224
|
}
|
19153
19225
|
unregisterListeners() {
|
19154
19226
|
const {
|
@@ -19166,6 +19238,7 @@ class BufferController extends Logger {
|
|
19166
19238
|
hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
|
19167
19239
|
hls.off(Events.FRAG_PARSED, this.onFragParsed, this);
|
19168
19240
|
hls.off(Events.FRAG_CHANGED, this.onFragChanged, this);
|
19241
|
+
hls.off(Events.ERROR, this.onError, this);
|
19169
19242
|
}
|
19170
19243
|
_initSourceBuffer() {
|
19171
19244
|
this.sourceBuffer = {};
|
@@ -19175,11 +19248,7 @@ class BufferController extends Logger {
|
|
19175
19248
|
video: [],
|
19176
19249
|
audiovideo: []
|
19177
19250
|
};
|
19178
|
-
this.
|
19179
|
-
audio: 0,
|
19180
|
-
video: 0,
|
19181
|
-
audiovideo: 0
|
19182
|
-
};
|
19251
|
+
this.resetAppendErrors();
|
19183
19252
|
this.lastMpegAudioChunk = null;
|
19184
19253
|
this.blockedAudioAppend = null;
|
19185
19254
|
this.lastVideoAppendEnd = 0;
|
@@ -19716,6 +19785,22 @@ class BufferController extends Logger {
|
|
19716
19785
|
this.updateMediaSource(durationAndRange);
|
19717
19786
|
}
|
19718
19787
|
}
|
19788
|
+
onError(event, data) {
|
19789
|
+
if (data.details === ErrorDetails.BUFFER_APPEND_ERROR && data.frag) {
|
19790
|
+
var _data$errorAction;
|
19791
|
+
const nextAutoLevel = (_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.nextAutoLevel;
|
19792
|
+
if (isFiniteNumber(nextAutoLevel) && nextAutoLevel !== data.frag.level) {
|
19793
|
+
this.resetAppendErrors();
|
19794
|
+
}
|
19795
|
+
}
|
19796
|
+
}
|
19797
|
+
resetAppendErrors() {
|
19798
|
+
this.appendErrors = {
|
19799
|
+
audio: 0,
|
19800
|
+
video: 0,
|
19801
|
+
audiovideo: 0
|
19802
|
+
};
|
19803
|
+
}
|
19719
19804
|
trimBuffers() {
|
19720
19805
|
const {
|
19721
19806
|
hls,
|
@@ -22542,7 +22627,7 @@ class TimelineController {
|
|
22542
22627
|
const {
|
22543
22628
|
unparsedVttFrags
|
22544
22629
|
} = this;
|
22545
|
-
if (id ===
|
22630
|
+
if (id === PlaylistLevelType.MAIN) {
|
22546
22631
|
this.initPTS[frag.cc] = {
|
22547
22632
|
baseTime: initPTS,
|
22548
22633
|
timescale
|
@@ -28289,7 +28374,6 @@ class StreamController extends BaseStreamController {
|
|
28289
28374
|
loadFragment(frag, level, targetBufferTime) {
|
28290
28375
|
// Check if fragment is not loaded
|
28291
28376
|
const fragState = this.fragmentTracker.getState(frag);
|
28292
|
-
this.fragCurrent = frag;
|
28293
28377
|
if (fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) {
|
28294
28378
|
if (frag.sn === 'initSegment') {
|
28295
28379
|
this._loadInitSegment(frag, level);
|
@@ -28297,7 +28381,6 @@ class StreamController extends BaseStreamController {
|
|
28297
28381
|
this.log(`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`);
|
28298
28382
|
this._loadBitrateTestFrag(frag, level);
|
28299
28383
|
} else {
|
28300
|
-
this.startFragRequested = true;
|
28301
28384
|
super.loadFragment(frag, level, targetBufferTime);
|
28302
28385
|
}
|
28303
28386
|
} else {
|
@@ -28431,14 +28514,14 @@ class StreamController extends BaseStreamController {
|
|
28431
28514
|
super.onMediaDetaching();
|
28432
28515
|
}
|
28433
28516
|
onManifestLoading() {
|
28517
|
+
super.onManifestLoading();
|
28434
28518
|
// reset buffer on manifest loading
|
28435
28519
|
this.log('Trigger BUFFER_RESET');
|
28436
28520
|
this.hls.trigger(Events.BUFFER_RESET, undefined);
|
28437
|
-
this.fragmentTracker.removeAllFragments();
|
28438
28521
|
this.couldBacktrack = false;
|
28439
|
-
this.
|
28440
|
-
this.
|
28441
|
-
this.altAudio = this.audioOnly =
|
28522
|
+
this.fragLastKbps = 0;
|
28523
|
+
this.fragPlaying = this.backtrackFragment = null;
|
28524
|
+
this.altAudio = this.audioOnly = false;
|
28442
28525
|
}
|
28443
28526
|
onManifestParsed(event, data) {
|
28444
28527
|
// detect if we have different kind of audio codecs used amongst playlists
|
@@ -28734,7 +28817,7 @@ class StreamController extends BaseStreamController {
|
|
28734
28817
|
// in that case, reset startFragRequested flag
|
28735
28818
|
if (!this.loadedmetadata) {
|
28736
28819
|
this.startFragRequested = false;
|
28737
|
-
this.nextLoadPosition = this.
|
28820
|
+
this.nextLoadPosition = this.lastCurrentTime;
|
28738
28821
|
}
|
28739
28822
|
this.tickImmediate();
|
28740
28823
|
}
|
@@ -28822,7 +28905,7 @@ class StreamController extends BaseStreamController {
|
|
28822
28905
|
}
|
28823
28906
|
_handleTransmuxComplete(transmuxResult) {
|
28824
28907
|
var _id3$samples;
|
28825
|
-
const id =
|
28908
|
+
const id = this.playlistType;
|
28826
28909
|
const {
|
28827
28910
|
hls
|
28828
28911
|
} = this;
|
@@ -29027,31 +29110,38 @@ class StreamController extends BaseStreamController {
|
|
29027
29110
|
audio.levelCodec = audioCodec;
|
29028
29111
|
audio.id = 'main';
|
29029
29112
|
this.log(`Init audio buffer, container:${audio.container}, codecs[selected/level/parsed]=[${audioCodec || ''}/${currentLevel.audioCodec || ''}/${audio.codec}]`);
|
29113
|
+
delete tracks.audiovideo;
|
29030
29114
|
}
|
29031
29115
|
if (video) {
|
29032
29116
|
video.levelCodec = currentLevel.videoCodec;
|
29033
29117
|
video.id = 'main';
|
29034
29118
|
this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${video.codec}]`);
|
29119
|
+
delete tracks.audiovideo;
|
29035
29120
|
}
|
29036
29121
|
if (audiovideo) {
|
29037
29122
|
this.log(`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`);
|
29123
|
+
delete tracks.video;
|
29124
|
+
delete tracks.audio;
|
29125
|
+
}
|
29126
|
+
const trackTypes = Object.keys(tracks);
|
29127
|
+
if (trackTypes.length) {
|
29128
|
+
this.hls.trigger(Events.BUFFER_CODECS, tracks);
|
29129
|
+
// loop through tracks that are going to be provided to bufferController
|
29130
|
+
trackTypes.forEach(trackName => {
|
29131
|
+
const track = tracks[trackName];
|
29132
|
+
const initSegment = track.initSegment;
|
29133
|
+
if (initSegment != null && initSegment.byteLength) {
|
29134
|
+
this.hls.trigger(Events.BUFFER_APPENDING, {
|
29135
|
+
type: trackName,
|
29136
|
+
data: initSegment,
|
29137
|
+
frag,
|
29138
|
+
part: null,
|
29139
|
+
chunkMeta,
|
29140
|
+
parent: frag.type
|
29141
|
+
});
|
29142
|
+
}
|
29143
|
+
});
|
29038
29144
|
}
|
29039
|
-
this.hls.trigger(Events.BUFFER_CODECS, tracks);
|
29040
|
-
// loop through tracks that are going to be provided to bufferController
|
29041
|
-
Object.keys(tracks).forEach(trackName => {
|
29042
|
-
const track = tracks[trackName];
|
29043
|
-
const initSegment = track.initSegment;
|
29044
|
-
if (initSegment != null && initSegment.byteLength) {
|
29045
|
-
this.hls.trigger(Events.BUFFER_APPENDING, {
|
29046
|
-
type: trackName,
|
29047
|
-
data: initSegment,
|
29048
|
-
frag,
|
29049
|
-
part: null,
|
29050
|
-
chunkMeta,
|
29051
|
-
parent: frag.type
|
29052
|
-
});
|
29053
|
-
}
|
29054
|
-
});
|
29055
29145
|
// trigger handler right now
|
29056
29146
|
this.tickImmediate();
|
29057
29147
|
}
|
@@ -29128,20 +29218,28 @@ class StreamController extends BaseStreamController {
|
|
29128
29218
|
return -1;
|
29129
29219
|
}
|
29130
29220
|
get currentFrag() {
|
29131
|
-
|
29132
|
-
if (
|
29133
|
-
return this.fragPlaying
|
29221
|
+
var _this$media2;
|
29222
|
+
if (this.fragPlaying) {
|
29223
|
+
return this.fragPlaying;
|
29224
|
+
}
|
29225
|
+
const currentTime = ((_this$media2 = this.media) == null ? void 0 : _this$media2.currentTime) || this.lastCurrentTime;
|
29226
|
+
if (isFiniteNumber(currentTime)) {
|
29227
|
+
return this.getAppendedFrag(currentTime);
|
29134
29228
|
}
|
29135
29229
|
return null;
|
29136
29230
|
}
|
29137
29231
|
get currentProgramDateTime() {
|
29138
|
-
|
29139
|
-
|
29140
|
-
|
29141
|
-
const
|
29142
|
-
|
29143
|
-
|
29144
|
-
|
29232
|
+
var _this$media3;
|
29233
|
+
const currentTime = ((_this$media3 = this.media) == null ? void 0 : _this$media3.currentTime) || this.lastCurrentTime;
|
29234
|
+
if (isFiniteNumber(currentTime)) {
|
29235
|
+
const details = this.getLevelDetails();
|
29236
|
+
const frag = this.currentFrag || (details ? findFragmentByPTS(null, details.fragments, currentTime) : null);
|
29237
|
+
if (frag) {
|
29238
|
+
const programDateTime = frag.programDateTime;
|
29239
|
+
if (programDateTime !== null) {
|
29240
|
+
const epocMs = programDateTime + (currentTime - frag.start) * 1000;
|
29241
|
+
return new Date(epocMs);
|
29242
|
+
}
|
29145
29243
|
}
|
29146
29244
|
}
|
29147
29245
|
return null;
|
@@ -29174,7 +29272,7 @@ class Hls {
|
|
29174
29272
|
* Get the video-dev/hls.js package version.
|
29175
29273
|
*/
|
29176
29274
|
static get version() {
|
29177
|
-
return "1.5.12-0.canary.
|
29275
|
+
return "1.5.12-0.canary.10367";
|
29178
29276
|
}
|
29179
29277
|
|
29180
29278
|
/**
|
@@ -29305,7 +29403,7 @@ class Hls {
|
|
29305
29403
|
if (AudioStreamControllerClass) {
|
29306
29404
|
networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
|
29307
29405
|
}
|
29308
|
-
// subtitleTrackController
|
29406
|
+
// Instantiate subtitleTrackController before SubtitleStreamController to receive level events first
|
29309
29407
|
this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers);
|
29310
29408
|
const SubtitleStreamControllerClass = config.subtitleStreamController;
|
29311
29409
|
if (SubtitleStreamControllerClass) {
|