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.light.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();
|
@@ -3535,10 +3535,9 @@ class PlaylistLoader {
|
|
3535
3535
|
type
|
3536
3536
|
} = context;
|
3537
3537
|
const url = getResponseUrl(response, context);
|
3538
|
-
const levelUrlId = 0;
|
3539
3538
|
const levelId = isFiniteNumber(level) ? level : isFiniteNumber(id) ? id : 0;
|
3540
3539
|
const levelType = mapContextToLevelType(context);
|
3541
|
-
const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType,
|
3540
|
+
const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, 0, this.variableList);
|
3542
3541
|
|
3543
3542
|
// We have done our first request (Manifest-type) and receive
|
3544
3543
|
// not a master playlist but a chunk-list (track/level)
|
@@ -5284,8 +5283,7 @@ function computeReloadInterval(newDetails, distanceToLiveEdgeMs = Infinity) {
|
|
5284
5283
|
}
|
5285
5284
|
return Math.round(reloadInterval);
|
5286
5285
|
}
|
5287
|
-
function getFragmentWithSN(
|
5288
|
-
const details = level == null ? void 0 : level.details;
|
5286
|
+
function getFragmentWithSN(details, sn, fragCurrent) {
|
5289
5287
|
if (!details) {
|
5290
5288
|
return null;
|
5291
5289
|
}
|
@@ -5302,12 +5300,11 @@ function getFragmentWithSN(level, sn, fragCurrent) {
|
|
5302
5300
|
}
|
5303
5301
|
return null;
|
5304
5302
|
}
|
5305
|
-
function getPartWith(
|
5306
|
-
|
5307
|
-
if (!(level != null && level.details)) {
|
5303
|
+
function getPartWith(details, sn, partIndex) {
|
5304
|
+
if (!details) {
|
5308
5305
|
return null;
|
5309
5306
|
}
|
5310
|
-
return findPart(
|
5307
|
+
return findPart(details.partList, sn, partIndex);
|
5311
5308
|
}
|
5312
5309
|
function findPart(partList, sn, partIndex) {
|
5313
5310
|
if (partList) {
|
@@ -5451,12 +5448,15 @@ function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) {
|
|
5451
5448
|
function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0, nextFragLookupTolerance = 0.005) {
|
5452
5449
|
let fragNext = null;
|
5453
5450
|
if (fragPrevious) {
|
5454
|
-
fragNext = fragments[fragPrevious.sn - fragments[0].sn
|
5451
|
+
fragNext = fragments[1 + fragPrevious.sn - fragments[0].sn] || null;
|
5455
5452
|
// check for buffer-end rounding error
|
5456
5453
|
const bufferEdgeError = fragPrevious.endDTS - bufferEnd;
|
5457
5454
|
if (bufferEdgeError > 0 && bufferEdgeError < 0.0000015) {
|
5458
5455
|
bufferEnd += 0.0000015;
|
5459
5456
|
}
|
5457
|
+
if (fragNext && fragPrevious.level !== fragNext.level && fragNext.end <= fragPrevious.end) {
|
5458
|
+
fragNext = fragments[2 + fragPrevious.sn - fragments[0].sn] || null;
|
5459
|
+
}
|
5460
5460
|
} else if (bufferEnd === 0 && fragments[0].start === 0) {
|
5461
5461
|
fragNext = fragments[0];
|
5462
5462
|
}
|
@@ -5608,7 +5608,7 @@ class ErrorController extends Logger {
|
|
5608
5608
|
this.playlistError = 0;
|
5609
5609
|
}
|
5610
5610
|
onError(event, data) {
|
5611
|
-
var _data$frag
|
5611
|
+
var _data$frag;
|
5612
5612
|
if (data.fatal) {
|
5613
5613
|
return;
|
5614
5614
|
}
|
@@ -5624,10 +5624,7 @@ class ErrorController extends Logger {
|
|
5624
5624
|
case ErrorDetails.FRAG_PARSING_ERROR:
|
5625
5625
|
// ignore empty segment errors marked as gap
|
5626
5626
|
if ((_data$frag = data.frag) != null && _data$frag.gap) {
|
5627
|
-
data.errorAction =
|
5628
|
-
action: NetworkErrorAction.DoNothing,
|
5629
|
-
flags: ErrorActionFlags.None
|
5630
|
-
};
|
5627
|
+
data.errorAction = createDoNothingErrorAction();
|
5631
5628
|
return;
|
5632
5629
|
}
|
5633
5630
|
// falls through
|
@@ -5694,7 +5691,11 @@ class ErrorController extends Logger {
|
|
5694
5691
|
case ErrorDetails.BUFFER_ADD_CODEC_ERROR:
|
5695
5692
|
case ErrorDetails.REMUX_ALLOC_ERROR:
|
5696
5693
|
case ErrorDetails.BUFFER_APPEND_ERROR:
|
5697
|
-
|
5694
|
+
// Buffer-controller can set errorAction when append errors can be ignored or resolved locally
|
5695
|
+
if (!data.errorAction) {
|
5696
|
+
var _data$level;
|
5697
|
+
data.errorAction = this.getLevelSwitchAction(data, (_data$level = data.level) != null ? _data$level : hls.loadLevel);
|
5698
|
+
}
|
5698
5699
|
return;
|
5699
5700
|
case ErrorDetails.INTERNAL_EXCEPTION:
|
5700
5701
|
case ErrorDetails.BUFFER_APPENDING_ERROR:
|
@@ -5703,10 +5704,7 @@ class ErrorController extends Logger {
|
|
5703
5704
|
case ErrorDetails.BUFFER_STALLED_ERROR:
|
5704
5705
|
case ErrorDetails.BUFFER_SEEK_OVER_HOLE:
|
5705
5706
|
case ErrorDetails.BUFFER_NUDGE_ON_STALL:
|
5706
|
-
data.errorAction =
|
5707
|
-
action: NetworkErrorAction.DoNothing,
|
5708
|
-
flags: ErrorActionFlags.None
|
5709
|
-
};
|
5707
|
+
data.errorAction = createDoNothingErrorAction();
|
5710
5708
|
return;
|
5711
5709
|
}
|
5712
5710
|
if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) {
|
@@ -5919,6 +5917,13 @@ class ErrorController extends Logger {
|
|
5919
5917
|
}
|
5920
5918
|
}
|
5921
5919
|
}
|
5920
|
+
function createDoNothingErrorAction(resolved) {
|
5921
|
+
const errorAction = {
|
5922
|
+
action: NetworkErrorAction.DoNothing,
|
5923
|
+
flags: ErrorActionFlags.None
|
5924
|
+
};
|
5925
|
+
return errorAction;
|
5926
|
+
}
|
5922
5927
|
|
5923
5928
|
class BasePlaylistController extends Logger {
|
5924
5929
|
constructor(hls, logPrefix) {
|
@@ -6371,6 +6376,7 @@ function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPrefer
|
|
6371
6376
|
if (!hasCurrentVideoRange) {
|
6372
6377
|
currentVideoRange = undefined;
|
6373
6378
|
}
|
6379
|
+
const hasMultipleSets = codecSets.length > 1;
|
6374
6380
|
const codecSet = codecSets.reduce((selected, candidate) => {
|
6375
6381
|
// 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
|
6376
6382
|
const candidateTier = codecTiers[candidate];
|
@@ -6378,46 +6384,48 @@ function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPrefer
|
|
6378
6384
|
return selected;
|
6379
6385
|
}
|
6380
6386
|
videoRanges = hasCurrentVideoRange ? allowedVideoRanges.filter(range => candidateTier.videoRanges[range] > 0) : [];
|
6381
|
-
if (
|
6382
|
-
|
6383
|
-
|
6384
|
-
|
6385
|
-
|
6386
|
-
|
6387
|
-
|
6388
|
-
|
6389
|
-
|
6390
|
-
|
6391
|
-
|
6392
|
-
|
6393
|
-
|
6394
|
-
if (!
|
6395
|
-
|
6387
|
+
if (hasMultipleSets) {
|
6388
|
+
if (candidateTier.minBitrate > currentBw) {
|
6389
|
+
logStartCodecCandidateIgnored(candidate, `min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`);
|
6390
|
+
return selected;
|
6391
|
+
}
|
6392
|
+
if (!candidateTier.hasDefaultAudio) {
|
6393
|
+
logStartCodecCandidateIgnored(candidate, `no renditions with default or auto-select sound found`);
|
6394
|
+
return selected;
|
6395
|
+
}
|
6396
|
+
if (audioCodecPreference && candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0) {
|
6397
|
+
logStartCodecCandidateIgnored(candidate, `audio codec preference "${audioCodecPreference}" not found`);
|
6398
|
+
return selected;
|
6399
|
+
}
|
6400
|
+
if (channelsPreference && !preferStereo) {
|
6401
|
+
if (!candidateTier.channels[channelsPreference]) {
|
6402
|
+
logStartCodecCandidateIgnored(candidate, `no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(candidateTier.channels)})`);
|
6403
|
+
return selected;
|
6404
|
+
}
|
6405
|
+
} else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels['2'] === 0) {
|
6406
|
+
logStartCodecCandidateIgnored(candidate, `no renditions with stereo sound found`);
|
6407
|
+
return selected;
|
6408
|
+
}
|
6409
|
+
if (candidateTier.minHeight > maxHeight) {
|
6410
|
+
logStartCodecCandidateIgnored(candidate, `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`);
|
6411
|
+
return selected;
|
6412
|
+
}
|
6413
|
+
if (candidateTier.minFramerate > maxFramerate) {
|
6414
|
+
logStartCodecCandidateIgnored(candidate, `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`);
|
6415
|
+
return selected;
|
6416
|
+
}
|
6417
|
+
if (!videoRanges.some(range => candidateTier.videoRanges[range] > 0)) {
|
6418
|
+
logStartCodecCandidateIgnored(candidate, `no variants with VIDEO-RANGE of ${JSON.stringify(videoRanges)} found`);
|
6419
|
+
return selected;
|
6420
|
+
}
|
6421
|
+
if (videoCodecPreference && candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0) {
|
6422
|
+
logStartCodecCandidateIgnored(candidate, `video codec preference "${videoCodecPreference}" not found`);
|
6423
|
+
return selected;
|
6424
|
+
}
|
6425
|
+
if (candidateTier.maxScore < selectedScore) {
|
6426
|
+
logStartCodecCandidateIgnored(candidate, `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`);
|
6396
6427
|
return selected;
|
6397
6428
|
}
|
6398
|
-
} else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels['2'] === 0) {
|
6399
|
-
logStartCodecCandidateIgnored(candidate, `no renditions with stereo sound found`);
|
6400
|
-
return selected;
|
6401
|
-
}
|
6402
|
-
if (candidateTier.minHeight > maxHeight) {
|
6403
|
-
logStartCodecCandidateIgnored(candidate, `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`);
|
6404
|
-
return selected;
|
6405
|
-
}
|
6406
|
-
if (candidateTier.minFramerate > maxFramerate) {
|
6407
|
-
logStartCodecCandidateIgnored(candidate, `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`);
|
6408
|
-
return selected;
|
6409
|
-
}
|
6410
|
-
if (!videoRanges.some(range => candidateTier.videoRanges[range] > 0)) {
|
6411
|
-
logStartCodecCandidateIgnored(candidate, `no variants with VIDEO-RANGE of ${JSON.stringify(videoRanges)} found`);
|
6412
|
-
return selected;
|
6413
|
-
}
|
6414
|
-
if (videoCodecPreference && candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0) {
|
6415
|
-
logStartCodecCandidateIgnored(candidate, `video codec preference "${videoCodecPreference}" not found`);
|
6416
|
-
return selected;
|
6417
|
-
}
|
6418
|
-
if (candidateTier.maxScore < selectedScore) {
|
6419
|
-
logStartCodecCandidateIgnored(candidate, `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`);
|
6420
|
-
return selected;
|
6421
6429
|
}
|
6422
6430
|
// Remove candiates with less preferred codecs or more errors
|
6423
6431
|
if (selected && (codecsSetSelectionPreferenceValue(candidate) >= codecsSetSelectionPreferenceValue(selected) || candidateTier.fragmentError > codecTiers[selected].fragmentError)) {
|
@@ -7095,7 +7103,7 @@ class AbrController extends Logger {
|
|
7095
7103
|
if (levelsSkipped.length) {
|
7096
7104
|
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}`);
|
7097
7105
|
}
|
7098
|
-
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:${
|
7106
|
+
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}`);
|
7099
7107
|
}
|
7100
7108
|
if (firstSelection) {
|
7101
7109
|
this.firstSelection = i;
|
@@ -7437,6 +7445,7 @@ class BufferController extends Logger {
|
|
7437
7445
|
hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
|
7438
7446
|
hls.on(Events.FRAG_PARSED, this.onFragParsed, this);
|
7439
7447
|
hls.on(Events.FRAG_CHANGED, this.onFragChanged, this);
|
7448
|
+
hls.on(Events.ERROR, this.onError, this);
|
7440
7449
|
}
|
7441
7450
|
unregisterListeners() {
|
7442
7451
|
const {
|
@@ -7454,6 +7463,7 @@ class BufferController extends Logger {
|
|
7454
7463
|
hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
|
7455
7464
|
hls.off(Events.FRAG_PARSED, this.onFragParsed, this);
|
7456
7465
|
hls.off(Events.FRAG_CHANGED, this.onFragChanged, this);
|
7466
|
+
hls.off(Events.ERROR, this.onError, this);
|
7457
7467
|
}
|
7458
7468
|
_initSourceBuffer() {
|
7459
7469
|
this.sourceBuffer = {};
|
@@ -7463,11 +7473,7 @@ class BufferController extends Logger {
|
|
7463
7473
|
video: [],
|
7464
7474
|
audiovideo: []
|
7465
7475
|
};
|
7466
|
-
this.
|
7467
|
-
audio: 0,
|
7468
|
-
video: 0,
|
7469
|
-
audiovideo: 0
|
7470
|
-
};
|
7476
|
+
this.resetAppendErrors();
|
7471
7477
|
this.lastMpegAudioChunk = null;
|
7472
7478
|
this.blockedAudioAppend = null;
|
7473
7479
|
this.lastVideoAppendEnd = 0;
|
@@ -8004,6 +8010,22 @@ class BufferController extends Logger {
|
|
8004
8010
|
this.updateMediaSource(durationAndRange);
|
8005
8011
|
}
|
8006
8012
|
}
|
8013
|
+
onError(event, data) {
|
8014
|
+
if (data.details === ErrorDetails.BUFFER_APPEND_ERROR && data.frag) {
|
8015
|
+
var _data$errorAction;
|
8016
|
+
const nextAutoLevel = (_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.nextAutoLevel;
|
8017
|
+
if (isFiniteNumber(nextAutoLevel) && nextAutoLevel !== data.frag.level) {
|
8018
|
+
this.resetAppendErrors();
|
8019
|
+
}
|
8020
|
+
}
|
8021
|
+
}
|
8022
|
+
resetAppendErrors() {
|
8023
|
+
this.appendErrors = {
|
8024
|
+
audio: 0,
|
8025
|
+
video: 0,
|
8026
|
+
audiovideo: 0
|
8027
|
+
};
|
8028
|
+
}
|
8007
8029
|
trimBuffers() {
|
8008
8030
|
const {
|
8009
8031
|
hls,
|
@@ -10672,6 +10694,7 @@ class FragmentTracker {
|
|
10672
10694
|
const {
|
10673
10695
|
hls
|
10674
10696
|
} = this;
|
10697
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
10675
10698
|
hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
|
10676
10699
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
10677
10700
|
hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);
|
@@ -10680,6 +10703,7 @@ class FragmentTracker {
|
|
10680
10703
|
const {
|
10681
10704
|
hls
|
10682
10705
|
} = this;
|
10706
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
10683
10707
|
hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
|
10684
10708
|
hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
10685
10709
|
hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);
|
@@ -10745,7 +10769,7 @@ class FragmentTracker {
|
|
10745
10769
|
* The browser will unload parts of the buffer to free up memory for new buffer data
|
10746
10770
|
* 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)
|
10747
10771
|
*/
|
10748
|
-
detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart) {
|
10772
|
+
detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart, removeAppending) {
|
10749
10773
|
if (this.timeRanges) {
|
10750
10774
|
this.timeRanges[elementaryStream] = timeRange;
|
10751
10775
|
}
|
@@ -10760,7 +10784,7 @@ class FragmentTracker {
|
|
10760
10784
|
if (appendedPartSn >= fragmentEntity.body.sn) {
|
10761
10785
|
return;
|
10762
10786
|
}
|
10763
|
-
if (!fragmentEntity.buffered && !fragmentEntity.loaded) {
|
10787
|
+
if (!fragmentEntity.buffered && (!fragmentEntity.loaded || removeAppending)) {
|
10764
10788
|
if (fragmentEntity.body.type === playlistType) {
|
10765
10789
|
this.removeFragment(fragmentEntity.body);
|
10766
10790
|
}
|
@@ -10770,6 +10794,10 @@ class FragmentTracker {
|
|
10770
10794
|
if (!esData) {
|
10771
10795
|
return;
|
10772
10796
|
}
|
10797
|
+
if (esData.time.length === 0) {
|
10798
|
+
this.removeFragment(fragmentEntity.body);
|
10799
|
+
return;
|
10800
|
+
}
|
10773
10801
|
esData.time.some(time => {
|
10774
10802
|
const isNotBuffered = !this.isTimeBuffered(time.startPTS, time.endPTS, timeRange);
|
10775
10803
|
if (isNotBuffered) {
|
@@ -10956,6 +10984,9 @@ class FragmentTracker {
|
|
10956
10984
|
}
|
10957
10985
|
return false;
|
10958
10986
|
}
|
10987
|
+
onManifestLoading() {
|
10988
|
+
this.removeAllFragments();
|
10989
|
+
}
|
10959
10990
|
onFragLoaded(event, data) {
|
10960
10991
|
// don't track initsegment (for which sn is not a number)
|
10961
10992
|
// don't track frags used for bitrateTest, they're irrelevant.
|
@@ -11004,6 +11035,22 @@ class FragmentTracker {
|
|
11004
11035
|
const fragKey = getFragmentKey(fragment);
|
11005
11036
|
return !!this.fragments[fragKey];
|
11006
11037
|
}
|
11038
|
+
hasFragments(type) {
|
11039
|
+
const {
|
11040
|
+
fragments
|
11041
|
+
} = this;
|
11042
|
+
const keys = Object.keys(fragments);
|
11043
|
+
if (!type) {
|
11044
|
+
return keys.length > 0;
|
11045
|
+
}
|
11046
|
+
for (let i = keys.length; i--;) {
|
11047
|
+
const fragmentEntity = fragments[keys[i]];
|
11048
|
+
if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === type) {
|
11049
|
+
return true;
|
11050
|
+
}
|
11051
|
+
}
|
11052
|
+
return false;
|
11053
|
+
}
|
11007
11054
|
hasParts(type) {
|
11008
11055
|
var _this$activePartLists;
|
11009
11056
|
return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length);
|
@@ -11105,8 +11152,9 @@ class FragmentLoader {
|
|
11105
11152
|
frag.gap = false;
|
11106
11153
|
}
|
11107
11154
|
}
|
11108
|
-
const loader = this.loader =
|
11155
|
+
const loader = this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config);
|
11109
11156
|
const loaderContext = createLoaderContext(frag);
|
11157
|
+
frag.loader = loader;
|
11110
11158
|
const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default);
|
11111
11159
|
const loaderConfig = {
|
11112
11160
|
loadPolicy,
|
@@ -11199,8 +11247,9 @@ class FragmentLoader {
|
|
11199
11247
|
reject(createGapLoadError(frag, part));
|
11200
11248
|
return;
|
11201
11249
|
}
|
11202
|
-
const loader = this.loader =
|
11250
|
+
const loader = this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config);
|
11203
11251
|
const loaderContext = createLoaderContext(frag, part);
|
11252
|
+
frag.loader = loader;
|
11204
11253
|
// Should we define another load policy for parts?
|
11205
11254
|
const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default);
|
11206
11255
|
const loaderConfig = {
|
@@ -12438,6 +12487,7 @@ class BaseStreamController extends TaskLoop {
|
|
12438
12487
|
this.initPTS = [];
|
12439
12488
|
this.buffering = true;
|
12440
12489
|
this.loadingParts = false;
|
12490
|
+
this.loopSn = void 0;
|
12441
12491
|
this.onMediaSeeking = () => {
|
12442
12492
|
const {
|
12443
12493
|
config,
|
@@ -12537,6 +12587,9 @@ class BaseStreamController extends TaskLoop {
|
|
12537
12587
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
12538
12588
|
startLoad(startPosition) {}
|
12539
12589
|
stopLoad() {
|
12590
|
+
if (this.state === State.STOPPED) {
|
12591
|
+
return;
|
12592
|
+
}
|
12540
12593
|
this.fragmentLoader.abort();
|
12541
12594
|
this.keyLoader.abort(this.playlistType);
|
12542
12595
|
const frag = this.fragCurrent;
|
@@ -12610,15 +12663,20 @@ class BaseStreamController extends TaskLoop {
|
|
12610
12663
|
this.keyLoader.detach();
|
12611
12664
|
}
|
12612
12665
|
this.media = this.mediaBuffer = null;
|
12613
|
-
this.
|
12666
|
+
this.loopSn = undefined;
|
12667
|
+
this.startFragRequested = this.loadedmetadata = this.loadingParts = false;
|
12614
12668
|
this.fragmentTracker.removeAllFragments();
|
12615
12669
|
this.stopLoad();
|
12616
12670
|
}
|
12617
|
-
onManifestLoading() {
|
12671
|
+
onManifestLoading() {
|
12672
|
+
this.initPTS = [];
|
12673
|
+
this.levels = this.levelLastLoaded = this.fragCurrent = null;
|
12674
|
+
this.lastCurrentTime = this.startPosition = 0;
|
12675
|
+
this.startFragRequested = false;
|
12676
|
+
}
|
12618
12677
|
onError(event, data) {}
|
12619
12678
|
onManifestLoaded(event, data) {
|
12620
12679
|
this.startTimeOffset = data.startTimeOffset;
|
12621
|
-
this.initPTS = [];
|
12622
12680
|
}
|
12623
12681
|
onHandlerDestroying() {
|
12624
12682
|
this.stopLoad();
|
@@ -12641,6 +12699,7 @@ class BaseStreamController extends TaskLoop {
|
|
12641
12699
|
super.onHandlerDestroyed();
|
12642
12700
|
}
|
12643
12701
|
loadFragment(frag, level, targetBufferTime) {
|
12702
|
+
this.startFragRequested = true;
|
12644
12703
|
this._loadFragForPlayback(frag, level, targetBufferTime);
|
12645
12704
|
}
|
12646
12705
|
_loadFragForPlayback(frag, level, targetBufferTime) {
|
@@ -12869,6 +12928,7 @@ class BaseStreamController extends TaskLoop {
|
|
12869
12928
|
_handleFragmentLoadProgress(frag) {}
|
12870
12929
|
_doFragLoad(frag, level, targetBufferTime = null, progressCallback) {
|
12871
12930
|
var _frag$decryptdata;
|
12931
|
+
this.fragCurrent = frag;
|
12872
12932
|
const details = level == null ? void 0 : level.details;
|
12873
12933
|
if (!this.levels || !details) {
|
12874
12934
|
throw new Error(`frag load aborted, missing level${details ? '' : ' detail'}s`);
|
@@ -12996,7 +13056,7 @@ class BaseStreamController extends TaskLoop {
|
|
12996
13056
|
partsLoaded[part.index] = partLoadedData;
|
12997
13057
|
const loadedPart = partLoadedData.part;
|
12998
13058
|
this.hls.trigger(Events.FRAG_LOADED, partLoadedData);
|
12999
|
-
const nextPart = getPartWith(level, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1);
|
13059
|
+
const nextPart = getPartWith(level.details, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1);
|
13000
13060
|
if (nextPart) {
|
13001
13061
|
loadPart(nextPart);
|
13002
13062
|
} else {
|
@@ -13091,8 +13151,9 @@ class BaseStreamController extends TaskLoop {
|
|
13091
13151
|
return null;
|
13092
13152
|
}
|
13093
13153
|
const level = levels[levelIndex];
|
13094
|
-
const
|
13095
|
-
const
|
13154
|
+
const levelDetails = level.details;
|
13155
|
+
const part = partIndex > -1 ? getPartWith(levelDetails, sn, partIndex) : null;
|
13156
|
+
const frag = part ? part.fragment : getFragmentWithSN(levelDetails, sn, fragCurrent);
|
13096
13157
|
if (!frag) {
|
13097
13158
|
return null;
|
13098
13159
|
}
|
@@ -13207,7 +13268,8 @@ class BaseStreamController extends TaskLoop {
|
|
13207
13268
|
return false;
|
13208
13269
|
}
|
13209
13270
|
getAppendedFrag(position, playlistType = PlaylistLevelType.MAIN) {
|
13210
|
-
|
13271
|
+
var _this$fragmentTracker;
|
13272
|
+
const fragOrPart = (_this$fragmentTracker = this.fragmentTracker) == null ? void 0 : _this$fragmentTracker.getAppendedFrag(position, playlistType);
|
13211
13273
|
if (fragOrPart && 'fragment' in fragOrPart) {
|
13212
13274
|
return fragOrPart.fragment;
|
13213
13275
|
}
|
@@ -13262,22 +13324,25 @@ class BaseStreamController extends TaskLoop {
|
|
13262
13324
|
return (trackerState === FragmentState.OK || trackerState === FragmentState.PARTIAL && !!frag.gap) && this.nextLoadPosition > targetBufferTime;
|
13263
13325
|
}
|
13264
13326
|
getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, playlistType, maxBufLen) {
|
13265
|
-
|
13266
|
-
|
13267
|
-
|
13268
|
-
|
13269
|
-
|
13270
|
-
|
13271
|
-
|
13272
|
-
|
13273
|
-
|
13274
|
-
|
13275
|
-
|
13276
|
-
|
13277
|
-
|
13327
|
+
let nextFragment = null;
|
13328
|
+
if (frag.gap) {
|
13329
|
+
nextFragment = this.getNextFragment(this.nextLoadPosition, levelDetails);
|
13330
|
+
if (nextFragment && !nextFragment.gap && bufferInfo.nextStart) {
|
13331
|
+
// Media buffered after GAP tags should not make the next buffer timerange exceed forward buffer length
|
13332
|
+
const nextbufferInfo = this.getFwdBufferInfoAtPos(this.mediaBuffer ? this.mediaBuffer : this.media, bufferInfo.nextStart, playlistType);
|
13333
|
+
if (nextbufferInfo !== null && bufferInfo.len + nextbufferInfo.len >= maxBufLen) {
|
13334
|
+
// Returning here might result in not finding an audio and video candiate to skip to
|
13335
|
+
const sn = nextFragment.sn;
|
13336
|
+
if (this.loopSn !== sn) {
|
13337
|
+
this.log(`buffer full after gaps in "${playlistType}" playlist starting at sn: ${sn}`);
|
13338
|
+
this.loopSn = sn;
|
13339
|
+
}
|
13340
|
+
return null;
|
13341
|
+
}
|
13278
13342
|
}
|
13279
13343
|
}
|
13280
|
-
|
13344
|
+
this.loopSn = undefined;
|
13345
|
+
return nextFragment;
|
13281
13346
|
}
|
13282
13347
|
mapToInitFragWhenRequired(frag) {
|
13283
13348
|
// If an initSegment is present, it must be buffered first
|
@@ -13486,7 +13551,7 @@ class BaseStreamController extends TaskLoop {
|
|
13486
13551
|
if (startTimeOffset !== null && isFiniteNumber(startTimeOffset)) {
|
13487
13552
|
startPosition = sliding + startTimeOffset;
|
13488
13553
|
if (startTimeOffset < 0) {
|
13489
|
-
startPosition += details.
|
13554
|
+
startPosition += details.edge;
|
13490
13555
|
}
|
13491
13556
|
startPosition = Math.min(Math.max(sliding, startPosition), sliding + details.totalduration);
|
13492
13557
|
this.log(`Start time offset ${startTimeOffset} found in ${offsetInMultivariantPlaylist ? 'multivariant' : 'media'} playlist, adjust startPosition to ${startPosition}`);
|
@@ -17053,10 +17118,11 @@ class MP4Remuxer {
|
|
17053
17118
|
if (this.ISGenerated) {
|
17054
17119
|
var _videoTrack$pixelRati, _config$pixelRatio, _videoTrack$pixelRati2, _config$pixelRatio2;
|
17055
17120
|
const config = this.videoTrackConfig;
|
17056
|
-
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]))) {
|
17121
|
+
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) {
|
17057
17122
|
this.resetInitSegment();
|
17058
17123
|
}
|
17059
|
-
}
|
17124
|
+
}
|
17125
|
+
if (!this.ISGenerated) {
|
17060
17126
|
initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);
|
17061
17127
|
}
|
17062
17128
|
const isVideoContiguous = this.isVideoContiguous;
|
@@ -17974,7 +18040,7 @@ class PassThroughRemuxer {
|
|
17974
18040
|
if (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale && accurateTimeOffset) {
|
17975
18041
|
initSegment.initPTS = decodeTime - timeOffset;
|
17976
18042
|
if (initPTS && initPTS.timescale === 1) {
|
17977
|
-
logger.warn(`Adjusting initPTS
|
18043
|
+
logger.warn(`Adjusting initPTS @${timeOffset} from ${initPTS.baseTime / initPTS.timescale} to ${initSegment.initPTS}`);
|
17978
18044
|
}
|
17979
18045
|
this.initPTS = initPTS = {
|
17980
18046
|
baseTime: initSegment.initPTS,
|
@@ -19693,7 +19759,6 @@ class StreamController extends BaseStreamController {
|
|
19693
19759
|
loadFragment(frag, level, targetBufferTime) {
|
19694
19760
|
// Check if fragment is not loaded
|
19695
19761
|
const fragState = this.fragmentTracker.getState(frag);
|
19696
|
-
this.fragCurrent = frag;
|
19697
19762
|
if (fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) {
|
19698
19763
|
if (frag.sn === 'initSegment') {
|
19699
19764
|
this._loadInitSegment(frag, level);
|
@@ -19701,7 +19766,6 @@ class StreamController extends BaseStreamController {
|
|
19701
19766
|
this.log(`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`);
|
19702
19767
|
this._loadBitrateTestFrag(frag, level);
|
19703
19768
|
} else {
|
19704
|
-
this.startFragRequested = true;
|
19705
19769
|
super.loadFragment(frag, level, targetBufferTime);
|
19706
19770
|
}
|
19707
19771
|
} else {
|
@@ -19835,14 +19899,14 @@ class StreamController extends BaseStreamController {
|
|
19835
19899
|
super.onMediaDetaching();
|
19836
19900
|
}
|
19837
19901
|
onManifestLoading() {
|
19902
|
+
super.onManifestLoading();
|
19838
19903
|
// reset buffer on manifest loading
|
19839
19904
|
this.log('Trigger BUFFER_RESET');
|
19840
19905
|
this.hls.trigger(Events.BUFFER_RESET, undefined);
|
19841
|
-
this.fragmentTracker.removeAllFragments();
|
19842
19906
|
this.couldBacktrack = false;
|
19843
|
-
this.
|
19844
|
-
this.
|
19845
|
-
this.altAudio = this.audioOnly =
|
19907
|
+
this.fragLastKbps = 0;
|
19908
|
+
this.fragPlaying = this.backtrackFragment = null;
|
19909
|
+
this.altAudio = this.audioOnly = false;
|
19846
19910
|
}
|
19847
19911
|
onManifestParsed(event, data) {
|
19848
19912
|
// detect if we have different kind of audio codecs used amongst playlists
|
@@ -20138,7 +20202,7 @@ class StreamController extends BaseStreamController {
|
|
20138
20202
|
// in that case, reset startFragRequested flag
|
20139
20203
|
if (!this.loadedmetadata) {
|
20140
20204
|
this.startFragRequested = false;
|
20141
|
-
this.nextLoadPosition = this.
|
20205
|
+
this.nextLoadPosition = this.lastCurrentTime;
|
20142
20206
|
}
|
20143
20207
|
this.tickImmediate();
|
20144
20208
|
}
|
@@ -20226,7 +20290,7 @@ class StreamController extends BaseStreamController {
|
|
20226
20290
|
}
|
20227
20291
|
_handleTransmuxComplete(transmuxResult) {
|
20228
20292
|
var _id3$samples;
|
20229
|
-
const id =
|
20293
|
+
const id = this.playlistType;
|
20230
20294
|
const {
|
20231
20295
|
hls
|
20232
20296
|
} = this;
|
@@ -20431,31 +20495,38 @@ class StreamController extends BaseStreamController {
|
|
20431
20495
|
audio.levelCodec = audioCodec;
|
20432
20496
|
audio.id = 'main';
|
20433
20497
|
this.log(`Init audio buffer, container:${audio.container}, codecs[selected/level/parsed]=[${audioCodec || ''}/${currentLevel.audioCodec || ''}/${audio.codec}]`);
|
20498
|
+
delete tracks.audiovideo;
|
20434
20499
|
}
|
20435
20500
|
if (video) {
|
20436
20501
|
video.levelCodec = currentLevel.videoCodec;
|
20437
20502
|
video.id = 'main';
|
20438
20503
|
this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${video.codec}]`);
|
20504
|
+
delete tracks.audiovideo;
|
20439
20505
|
}
|
20440
20506
|
if (audiovideo) {
|
20441
20507
|
this.log(`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`);
|
20508
|
+
delete tracks.video;
|
20509
|
+
delete tracks.audio;
|
20510
|
+
}
|
20511
|
+
const trackTypes = Object.keys(tracks);
|
20512
|
+
if (trackTypes.length) {
|
20513
|
+
this.hls.trigger(Events.BUFFER_CODECS, tracks);
|
20514
|
+
// loop through tracks that are going to be provided to bufferController
|
20515
|
+
trackTypes.forEach(trackName => {
|
20516
|
+
const track = tracks[trackName];
|
20517
|
+
const initSegment = track.initSegment;
|
20518
|
+
if (initSegment != null && initSegment.byteLength) {
|
20519
|
+
this.hls.trigger(Events.BUFFER_APPENDING, {
|
20520
|
+
type: trackName,
|
20521
|
+
data: initSegment,
|
20522
|
+
frag,
|
20523
|
+
part: null,
|
20524
|
+
chunkMeta,
|
20525
|
+
parent: frag.type
|
20526
|
+
});
|
20527
|
+
}
|
20528
|
+
});
|
20442
20529
|
}
|
20443
|
-
this.hls.trigger(Events.BUFFER_CODECS, tracks);
|
20444
|
-
// loop through tracks that are going to be provided to bufferController
|
20445
|
-
Object.keys(tracks).forEach(trackName => {
|
20446
|
-
const track = tracks[trackName];
|
20447
|
-
const initSegment = track.initSegment;
|
20448
|
-
if (initSegment != null && initSegment.byteLength) {
|
20449
|
-
this.hls.trigger(Events.BUFFER_APPENDING, {
|
20450
|
-
type: trackName,
|
20451
|
-
data: initSegment,
|
20452
|
-
frag,
|
20453
|
-
part: null,
|
20454
|
-
chunkMeta,
|
20455
|
-
parent: frag.type
|
20456
|
-
});
|
20457
|
-
}
|
20458
|
-
});
|
20459
20530
|
// trigger handler right now
|
20460
20531
|
this.tickImmediate();
|
20461
20532
|
}
|
@@ -20532,20 +20603,28 @@ class StreamController extends BaseStreamController {
|
|
20532
20603
|
return -1;
|
20533
20604
|
}
|
20534
20605
|
get currentFrag() {
|
20535
|
-
|
20536
|
-
if (
|
20537
|
-
return this.fragPlaying
|
20606
|
+
var _this$media2;
|
20607
|
+
if (this.fragPlaying) {
|
20608
|
+
return this.fragPlaying;
|
20609
|
+
}
|
20610
|
+
const currentTime = ((_this$media2 = this.media) == null ? void 0 : _this$media2.currentTime) || this.lastCurrentTime;
|
20611
|
+
if (isFiniteNumber(currentTime)) {
|
20612
|
+
return this.getAppendedFrag(currentTime);
|
20538
20613
|
}
|
20539
20614
|
return null;
|
20540
20615
|
}
|
20541
20616
|
get currentProgramDateTime() {
|
20542
|
-
|
20543
|
-
|
20544
|
-
|
20545
|
-
const
|
20546
|
-
|
20547
|
-
|
20548
|
-
|
20617
|
+
var _this$media3;
|
20618
|
+
const currentTime = ((_this$media3 = this.media) == null ? void 0 : _this$media3.currentTime) || this.lastCurrentTime;
|
20619
|
+
if (isFiniteNumber(currentTime)) {
|
20620
|
+
const details = this.getLevelDetails();
|
20621
|
+
const frag = this.currentFrag || (details ? findFragmentByPTS(null, details.fragments, currentTime) : null);
|
20622
|
+
if (frag) {
|
20623
|
+
const programDateTime = frag.programDateTime;
|
20624
|
+
if (programDateTime !== null) {
|
20625
|
+
const epocMs = programDateTime + (currentTime - frag.start) * 1000;
|
20626
|
+
return new Date(epocMs);
|
20627
|
+
}
|
20549
20628
|
}
|
20550
20629
|
}
|
20551
20630
|
return null;
|
@@ -20578,7 +20657,7 @@ class Hls {
|
|
20578
20657
|
* Get the video-dev/hls.js package version.
|
20579
20658
|
*/
|
20580
20659
|
static get version() {
|
20581
|
-
return "1.5.12-0.canary.
|
20660
|
+
return "1.5.12-0.canary.10367";
|
20582
20661
|
}
|
20583
20662
|
|
20584
20663
|
/**
|
@@ -20709,7 +20788,7 @@ class Hls {
|
|
20709
20788
|
if (AudioStreamControllerClass) {
|
20710
20789
|
networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
|
20711
20790
|
}
|
20712
|
-
// subtitleTrackController
|
20791
|
+
// Instantiate subtitleTrackController before SubtitleStreamController to receive level events first
|
20713
20792
|
this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers);
|
20714
20793
|
const SubtitleStreamControllerClass = config.subtitleStreamController;
|
20715
20794
|
if (SubtitleStreamControllerClass) {
|