hls.js 1.6.0-beta.1.0.canary.10764 → 1.6.0-beta.1.0.canary.10766
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hls.d.mts +6 -0
- package/dist/hls.d.ts +6 -0
- package/dist/hls.js +89 -39
- package/dist/hls.js.d.ts +6 -0
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +72 -29
- 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 +70 -29
- 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 +87 -39
- 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 +6 -2
- package/src/controller/audio-stream-controller.ts +7 -7
- package/src/controller/audio-track-controller.ts +1 -0
- package/src/controller/base-stream-controller.ts +35 -20
- package/src/controller/level-controller.ts +3 -2
- package/src/controller/stream-controller.ts +7 -7
- package/src/controller/subtitle-stream-controller.ts +9 -5
- package/src/controller/subtitle-track-controller.ts +1 -0
- package/src/loader/fragment.ts +5 -1
- package/src/loader/playlist-loader.ts +38 -8
- package/src/types/events.ts +4 -0
- package/src/types/loader.ts +4 -1
package/package.json
CHANGED
@@ -795,14 +795,15 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
795
795
|
| undefined;
|
796
796
|
if (
|
797
797
|
typeof mediaCapabilities?.decodingInfo === 'function' &&
|
798
|
-
requiresMediaCapabilitiesDecodingInfo(
|
798
|
+
(requiresMediaCapabilitiesDecodingInfo(
|
799
799
|
levelInfo,
|
800
800
|
audioTracksByGroup,
|
801
801
|
currentVideoRange,
|
802
802
|
currentFrameRate,
|
803
803
|
currentBw,
|
804
804
|
audioPreference,
|
805
|
-
)
|
805
|
+
) ||
|
806
|
+
levelInfo.videoCodec?.substring(0, 4) === 'hvc1') // Force media capabilities check for HEVC to avoid failure on Windows
|
806
807
|
) {
|
807
808
|
levelInfo.supportedPromise = getMediaDecodingInfoPromise(
|
808
809
|
levelInfo,
|
@@ -831,6 +832,9 @@ class AbrController extends Logger implements AbrComponentAPI {
|
|
831
832
|
if (index > -1 && levels.length > 1) {
|
832
833
|
this.log(`Removing unsupported level ${index}`);
|
833
834
|
this.hls.removeLevel(index);
|
835
|
+
if (this.hls.loadLevel === -1) {
|
836
|
+
this.hls.nextLoadLevel = 0;
|
837
|
+
}
|
834
838
|
}
|
835
839
|
}
|
836
840
|
});
|
@@ -5,7 +5,7 @@ import ChunkCache from '../demux/chunk-cache';
|
|
5
5
|
import TransmuxerInterface from '../demux/transmuxer-interface';
|
6
6
|
import { ErrorDetails } from '../errors';
|
7
7
|
import { Events } from '../events';
|
8
|
-
import { ElementaryStreamTypes } from '../loader/fragment';
|
8
|
+
import { ElementaryStreamTypes, isMediaFragment } from '../loader/fragment';
|
9
9
|
import { Level } from '../types/level';
|
10
10
|
import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
|
11
11
|
import { ChunkMetadata } from '../types/transmuxer';
|
@@ -409,8 +409,8 @@ class AudioStreamController
|
|
409
409
|
if (
|
410
410
|
this.startFragRequested &&
|
411
411
|
mainFragLoading &&
|
412
|
-
mainFragLoading
|
413
|
-
frag
|
412
|
+
isMediaFragment(mainFragLoading) &&
|
413
|
+
isMediaFragment(frag) &&
|
414
414
|
!frag.endList &&
|
415
415
|
(!trackDetails.live ||
|
416
416
|
(!this.loadingParts && targetBufferTime < this.hls.liveSyncPosition!))
|
@@ -694,7 +694,7 @@ class AudioStreamController
|
|
694
694
|
private onFragLoading(event: Events.FRAG_LOADING, data: FragLoadingData) {
|
695
695
|
if (
|
696
696
|
data.frag.type === PlaylistLevelType.MAIN &&
|
697
|
-
data.frag
|
697
|
+
isMediaFragment(data.frag)
|
698
698
|
) {
|
699
699
|
this.mainFragLoading = data;
|
700
700
|
if (this.state === State.IDLE) {
|
@@ -722,8 +722,8 @@ class AudioStreamController
|
|
722
722
|
);
|
723
723
|
return;
|
724
724
|
}
|
725
|
-
if (frag
|
726
|
-
this.fragPrevious = frag
|
725
|
+
if (isMediaFragment(frag)) {
|
726
|
+
this.fragPrevious = frag;
|
727
727
|
const track = this.switchingTrack;
|
728
728
|
if (track) {
|
729
729
|
this.bufferedTrack = track;
|
@@ -962,7 +962,7 @@ class AudioStreamController
|
|
962
962
|
fragState === FragmentState.NOT_LOADED ||
|
963
963
|
fragState === FragmentState.PARTIAL
|
964
964
|
) {
|
965
|
-
if (frag
|
965
|
+
if (!isMediaFragment(frag)) {
|
966
966
|
this._loadInitSegment(frag, track);
|
967
967
|
} else if (track.details?.live && !this.initPTS[frag.cc]) {
|
968
968
|
this.log(
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { NetworkErrorAction } from './error-controller';
|
1
|
+
import { ErrorActionFlags, NetworkErrorAction } from './error-controller';
|
2
2
|
import {
|
3
3
|
findFragmentByPDT,
|
4
4
|
findFragmentByPTS,
|
@@ -8,6 +8,12 @@ import { FragmentState } from './fragment-tracker';
|
|
8
8
|
import Decrypter from '../crypt/decrypter';
|
9
9
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
10
10
|
import { Events } from '../events';
|
11
|
+
import {
|
12
|
+
type Fragment,
|
13
|
+
isMediaFragment,
|
14
|
+
type MediaFragment,
|
15
|
+
type Part,
|
16
|
+
} from '../loader/fragment';
|
11
17
|
import FragmentLoader from '../loader/fragment-loader';
|
12
18
|
import TaskLoop from '../task-loop';
|
13
19
|
import { PlaylistLevelType } from '../types/loader';
|
@@ -31,7 +37,6 @@ import type { FragmentTracker } from './fragment-tracker';
|
|
31
37
|
import type { HlsConfig } from '../config';
|
32
38
|
import type TransmuxerInterface from '../demux/transmuxer-interface';
|
33
39
|
import type Hls from '../hls';
|
34
|
-
import type { Fragment, MediaFragment, Part } from '../loader/fragment';
|
35
40
|
import type {
|
36
41
|
FragmentLoadProgressCallback,
|
37
42
|
LoadError,
|
@@ -714,7 +719,7 @@ export default class BaseStreamController
|
|
714
719
|
: '(detached)'
|
715
720
|
})`,
|
716
721
|
);
|
717
|
-
if (frag
|
722
|
+
if (isMediaFragment(frag)) {
|
718
723
|
if (frag.type !== PlaylistLevelType.SUBTITLE) {
|
719
724
|
const el = frag.elementaryStreams;
|
720
725
|
if (!Object.keys(el).some((type) => !!el[type])) {
|
@@ -803,7 +808,7 @@ export default class BaseStreamController
|
|
803
808
|
|
804
809
|
const fragPrevious = this.fragPrevious;
|
805
810
|
if (
|
806
|
-
frag
|
811
|
+
isMediaFragment(frag) &&
|
807
812
|
(!fragPrevious || frag.sn !== fragPrevious.sn)
|
808
813
|
) {
|
809
814
|
const shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
|
@@ -817,7 +822,7 @@ export default class BaseStreamController
|
|
817
822
|
}
|
818
823
|
}
|
819
824
|
targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
|
820
|
-
if (this.loadingParts && frag
|
825
|
+
if (this.loadingParts && isMediaFragment(frag)) {
|
821
826
|
const partList = details.partList;
|
822
827
|
if (partList && progressCallback) {
|
823
828
|
if (targetBufferTime > frag.end && details.fragmentHint) {
|
@@ -885,7 +890,7 @@ export default class BaseStreamController
|
|
885
890
|
}
|
886
891
|
}
|
887
892
|
|
888
|
-
if (frag
|
893
|
+
if (isMediaFragment(frag) && this.loadingParts) {
|
889
894
|
this.log(
|
890
895
|
`LL-Part loading OFF after next part miss @${targetBufferTime.toFixed(
|
891
896
|
2,
|
@@ -1661,7 +1666,7 @@ export default class BaseStreamController
|
|
1661
1666
|
}
|
1662
1667
|
|
1663
1668
|
private handleFragLoadAborted(frag: Fragment, part: Part | undefined) {
|
1664
|
-
if (this.transmuxer && frag
|
1669
|
+
if (this.transmuxer && isMediaFragment(frag) && frag.stats.aborted) {
|
1665
1670
|
this.warn(
|
1666
1671
|
`Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${
|
1667
1672
|
frag.level
|
@@ -1708,12 +1713,18 @@ export default class BaseStreamController
|
|
1708
1713
|
}
|
1709
1714
|
// keep retrying until the limit will be reached
|
1710
1715
|
const errorAction = data.errorAction;
|
1711
|
-
const { action, retryCount = 0, retryConfig } = errorAction || {};
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1716
|
+
const { action, flags, retryCount = 0, retryConfig } = errorAction || {};
|
1717
|
+
const couldRetry = !!errorAction && !!retryConfig;
|
1718
|
+
const retry = couldRetry && action === NetworkErrorAction.RetryRequest;
|
1719
|
+
const noAlternate =
|
1720
|
+
couldRetry &&
|
1721
|
+
!errorAction.resolved &&
|
1722
|
+
flags === ErrorActionFlags.MoveAllAlternatesMatchingHost;
|
1723
|
+
if (!retry && noAlternate && isMediaFragment(frag) && !frag.endList) {
|
1724
|
+
this.resetFragmentErrors(filterType);
|
1725
|
+
this.treatAsGap(frag);
|
1726
|
+
errorAction.resolved = true;
|
1727
|
+
} else if ((retry || noAlternate) && retryCount < retryConfig.maxNumRetry) {
|
1717
1728
|
this.resetStartWhenNotLoaded(this.levelLastLoaded);
|
1718
1729
|
const delay = getRetryDelay(retryConfig, retryCount);
|
1719
1730
|
this.warn(
|
@@ -1742,9 +1753,7 @@ export default class BaseStreamController
|
|
1742
1753
|
);
|
1743
1754
|
return;
|
1744
1755
|
}
|
1745
|
-
} else if (
|
1746
|
-
errorAction?.action === NetworkErrorAction.SendAlternateToPenaltyBox
|
1747
|
-
) {
|
1756
|
+
} else if (action === NetworkErrorAction.SendAlternateToPenaltyBox) {
|
1748
1757
|
this.state = State.WAITING_LEVEL;
|
1749
1758
|
} else {
|
1750
1759
|
this.state = State.ERROR;
|
@@ -1925,10 +1934,7 @@ export default class BaseStreamController
|
|
1925
1934
|
);
|
1926
1935
|
if (level.fragmentError === 0) {
|
1927
1936
|
// Mark and track the odd empty segment as a gap to avoid reloading
|
1928
|
-
level
|
1929
|
-
frag.gap = true;
|
1930
|
-
this.fragmentTracker.removeFragment(frag);
|
1931
|
-
this.fragmentTracker.fragBuffered(frag, true);
|
1937
|
+
this.treatAsGap(frag, level);
|
1932
1938
|
}
|
1933
1939
|
this.warn(error.message);
|
1934
1940
|
this.hls.trigger(Events.ERROR, {
|
@@ -1970,6 +1976,15 @@ export default class BaseStreamController
|
|
1970
1976
|
)}]${part && frag.type === 'main' ? 'INDEPENDENT=' + (part.independent ? 'YES' : 'NO') : ''}`;
|
1971
1977
|
}
|
1972
1978
|
|
1979
|
+
private treatAsGap(frag: MediaFragment, level?: Level) {
|
1980
|
+
if (level) {
|
1981
|
+
level.fragmentError++;
|
1982
|
+
}
|
1983
|
+
frag.gap = true;
|
1984
|
+
this.fragmentTracker.removeFragment(frag);
|
1985
|
+
this.fragmentTracker.fragBuffered(frag, true);
|
1986
|
+
}
|
1987
|
+
|
1973
1988
|
protected resetTransmuxer() {
|
1974
1989
|
this.transmuxer?.reset();
|
1975
1990
|
}
|
@@ -601,7 +601,7 @@ export default class LevelController extends BasePlaylistController {
|
|
601
601
|
|
602
602
|
protected onLevelLoaded(event: Events.LEVEL_LOADED, data: LevelLoadedData) {
|
603
603
|
const { level, details } = data;
|
604
|
-
const curLevel =
|
604
|
+
const curLevel = data.levelInfo;
|
605
605
|
|
606
606
|
if (!curLevel) {
|
607
607
|
this.warn(`Invalid level index ${level}`);
|
@@ -612,7 +612,7 @@ export default class LevelController extends BasePlaylistController {
|
|
612
612
|
}
|
613
613
|
|
614
614
|
// only process level loaded events matching with expected level
|
615
|
-
if (
|
615
|
+
if (curLevel === this.currentLevel) {
|
616
616
|
// reset level load error counter on successful level loaded only if there is no issues with fragments
|
617
617
|
if (curLevel.fragmentError === 0) {
|
618
618
|
curLevel.loadError = 0;
|
@@ -665,6 +665,7 @@ export default class LevelController extends BasePlaylistController {
|
|
665
665
|
this.hls.trigger(Events.LEVEL_LOADING, {
|
666
666
|
url,
|
667
667
|
level: currentLevelIndex,
|
668
|
+
levelInfo: currentLevel,
|
668
669
|
pathwayId: currentLevel.attrs['PATHWAY-ID'],
|
669
670
|
id: 0, // Deprecated Level urlId
|
670
671
|
deliveryDirectives: hlsUrlParameters || null,
|
@@ -6,7 +6,7 @@ import TransmuxerInterface from '../demux/transmuxer-interface';
|
|
6
6
|
import { ErrorDetails } from '../errors';
|
7
7
|
import { Events } from '../events';
|
8
8
|
import { changeTypeSupported } from '../is-supported';
|
9
|
-
import { ElementaryStreamTypes } from '../loader/fragment';
|
9
|
+
import { ElementaryStreamTypes, isMediaFragment } from '../loader/fragment';
|
10
10
|
import { PlaylistContextType, PlaylistLevelType } from '../types/loader';
|
11
11
|
import { ChunkMetadata } from '../types/transmuxer';
|
12
12
|
import { BufferHelper } from '../utils/buffer-helper';
|
@@ -319,7 +319,7 @@ export default class StreamController
|
|
319
319
|
this.couldBacktrack &&
|
320
320
|
!this.fragPrevious &&
|
321
321
|
frag &&
|
322
|
-
frag
|
322
|
+
isMediaFragment(frag) &&
|
323
323
|
this.fragmentTracker.getState(frag) !== FragmentState.OK
|
324
324
|
) {
|
325
325
|
const backtrackSn = (this.backtrackFragment ?? frag).sn as number;
|
@@ -378,7 +378,7 @@ export default class StreamController
|
|
378
378
|
fragState === FragmentState.NOT_LOADED ||
|
379
379
|
fragState === FragmentState.PARTIAL
|
380
380
|
) {
|
381
|
-
if (frag
|
381
|
+
if (!isMediaFragment(frag)) {
|
382
382
|
this._loadInitSegment(frag, level);
|
383
383
|
} else if (this.bitrateTest) {
|
384
384
|
this.log(
|
@@ -646,7 +646,7 @@ export default class StreamController
|
|
646
646
|
if (!levels || this.state !== State.IDLE) {
|
647
647
|
return;
|
648
648
|
}
|
649
|
-
const level =
|
649
|
+
const level = data.levelInfo;
|
650
650
|
if (
|
651
651
|
!level.details ||
|
652
652
|
(level.details.live && this.levelLastLoaded !== level) ||
|
@@ -674,7 +674,7 @@ export default class StreamController
|
|
674
674
|
}, cc [${newDetails.startCC}, ${newDetails.endCC}] duration:${duration}`,
|
675
675
|
);
|
676
676
|
|
677
|
-
const curLevel =
|
677
|
+
const curLevel = data.levelInfo;
|
678
678
|
const fragCurrent = this.fragCurrent;
|
679
679
|
if (
|
680
680
|
fragCurrent &&
|
@@ -963,8 +963,8 @@ export default class StreamController
|
|
963
963
|
this.fragLastKbps = Math.round(
|
964
964
|
(8 * stats.total) / (stats.buffering.end - stats.loading.first),
|
965
965
|
);
|
966
|
-
if (frag
|
967
|
-
this.fragPrevious = frag
|
966
|
+
if (isMediaFragment(frag)) {
|
967
|
+
this.fragPrevious = frag;
|
968
968
|
}
|
969
969
|
this.fragBufferedComplete(frag, part);
|
970
970
|
}
|
@@ -3,6 +3,11 @@ import { findFragmentByPTS } from './fragment-finders';
|
|
3
3
|
import { FragmentState } from './fragment-tracker';
|
4
4
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
5
5
|
import { Events } from '../events';
|
6
|
+
import {
|
7
|
+
type Fragment,
|
8
|
+
isMediaFragment,
|
9
|
+
type MediaFragment,
|
10
|
+
} from '../loader/fragment';
|
6
11
|
import { Level } from '../types/level';
|
7
12
|
import { PlaylistLevelType } from '../types/loader';
|
8
13
|
import { BufferHelper } from '../utils/buffer-helper';
|
@@ -15,7 +20,6 @@ import { addSliding } from '../utils/level-helper';
|
|
15
20
|
import { subtitleOptionsIdentical } from '../utils/media-option-attributes';
|
16
21
|
import type Hls from '../hls';
|
17
22
|
import type { FragmentTracker } from './fragment-tracker';
|
18
|
-
import type { Fragment, MediaFragment } from '../loader/fragment';
|
19
23
|
import type KeyLoader from '../loader/key-loader';
|
20
24
|
import type { LevelDetails } from '../loader/level-details';
|
21
25
|
import type { NetworkComponentAPI } from '../types/component-api';
|
@@ -126,8 +130,8 @@ export class SubtitleStreamController
|
|
126
130
|
data: SubtitleFragProcessed,
|
127
131
|
) {
|
128
132
|
const { frag, success } = data;
|
129
|
-
if (frag
|
130
|
-
this.fragPrevious = frag
|
133
|
+
if (isMediaFragment(frag)) {
|
134
|
+
this.fragPrevious = frag;
|
131
135
|
}
|
132
136
|
this.state = State.IDLE;
|
133
137
|
if (!success) {
|
@@ -466,7 +470,7 @@ export class SubtitleStreamController
|
|
466
470
|
return;
|
467
471
|
}
|
468
472
|
foundFrag = this.mapToInitFragWhenRequired(foundFrag) as Fragment;
|
469
|
-
if (foundFrag
|
473
|
+
if (isMediaFragment(foundFrag)) {
|
470
474
|
// Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment
|
471
475
|
const curSNIdx = foundFrag.sn - trackDetails.startSN;
|
472
476
|
const prevFrag = fragments[curSNIdx - 1];
|
@@ -492,7 +496,7 @@ export class SubtitleStreamController
|
|
492
496
|
level: Level,
|
493
497
|
targetBufferTime: number,
|
494
498
|
) {
|
495
|
-
if (frag
|
499
|
+
if (!isMediaFragment(frag)) {
|
496
500
|
this._loadInitSegment(frag, level);
|
497
501
|
} else {
|
498
502
|
super.loadFragment(frag, level, targetBufferTime);
|
package/src/loader/fragment.ts
CHANGED
@@ -152,6 +152,10 @@ export type MediaFragmentRef = {
|
|
152
152
|
programDateTime: number | null;
|
153
153
|
};
|
154
154
|
|
155
|
+
export function isMediaFragment(frag: Fragment): frag is MediaFragment {
|
156
|
+
return frag.sn !== 'initSegment';
|
157
|
+
}
|
158
|
+
|
155
159
|
/**
|
156
160
|
* Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.
|
157
161
|
*/
|
@@ -323,7 +327,7 @@ export class Fragment extends BaseSegment {
|
|
323
327
|
}
|
324
328
|
|
325
329
|
get ref(): MediaFragmentRef | null {
|
326
|
-
if (this
|
330
|
+
if (!isMediaFragment(this)) {
|
327
331
|
return null;
|
328
332
|
}
|
329
333
|
if (!this._ref) {
|
@@ -18,10 +18,11 @@ import type { NetworkComponentAPI } from '../types/component-api';
|
|
18
18
|
import type {
|
19
19
|
ErrorData,
|
20
20
|
LevelLoadingData,
|
21
|
+
LevelsUpdatedData,
|
21
22
|
ManifestLoadingData,
|
22
23
|
TrackLoadingData,
|
23
24
|
} from '../types/events';
|
24
|
-
import type { LevelParsed, VariableMap } from '../types/level';
|
25
|
+
import type { Level, LevelParsed, VariableMap } from '../types/level';
|
25
26
|
import type {
|
26
27
|
Loader,
|
27
28
|
LoaderCallbacks,
|
@@ -31,7 +32,7 @@ import type {
|
|
31
32
|
LoaderStats,
|
32
33
|
PlaylistLoaderContext,
|
33
34
|
} from '../types/loader';
|
34
|
-
import type { MediaAttributes } from '../types/media-playlist';
|
35
|
+
import type { MediaAttributes, MediaPlaylist } from '../types/media-playlist';
|
35
36
|
|
36
37
|
function mapContextToLevelType(
|
37
38
|
context: PlaylistLoaderContext,
|
@@ -86,6 +87,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
86
87
|
hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
87
88
|
hls.on(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this);
|
88
89
|
hls.on(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this);
|
90
|
+
hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
|
89
91
|
}
|
90
92
|
|
91
93
|
private unregisterListeners() {
|
@@ -94,6 +96,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
94
96
|
hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
95
97
|
hls.off(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this);
|
96
98
|
hls.off(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this);
|
99
|
+
hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
|
97
100
|
}
|
98
101
|
|
99
102
|
/**
|
@@ -118,7 +121,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
118
121
|
return this.loaders[context.type];
|
119
122
|
}
|
120
123
|
|
121
|
-
private resetInternalLoader(contextType): void {
|
124
|
+
private resetInternalLoader(contextType: PlaylistContextType): void {
|
122
125
|
if (this.loaders[contextType]) {
|
123
126
|
delete this.loaders[contextType];
|
124
127
|
}
|
@@ -134,7 +137,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
134
137
|
loader.destroy();
|
135
138
|
}
|
136
139
|
|
137
|
-
this.resetInternalLoader(contextType);
|
140
|
+
this.resetInternalLoader(contextType as PlaylistContextType);
|
138
141
|
}
|
139
142
|
}
|
140
143
|
|
@@ -157,11 +160,12 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
157
160
|
type: PlaylistContextType.MANIFEST,
|
158
161
|
url,
|
159
162
|
deliveryDirectives: null,
|
163
|
+
levelOrTrack: null,
|
160
164
|
});
|
161
165
|
}
|
162
166
|
|
163
167
|
private onLevelLoading(event: Events.LEVEL_LOADING, data: LevelLoadingData) {
|
164
|
-
const { id, level, pathwayId, url, deliveryDirectives } = data;
|
168
|
+
const { id, level, pathwayId, url, deliveryDirectives, levelInfo } = data;
|
165
169
|
this.load({
|
166
170
|
id,
|
167
171
|
level,
|
@@ -170,6 +174,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
170
174
|
type: PlaylistContextType.LEVEL,
|
171
175
|
url,
|
172
176
|
deliveryDirectives,
|
177
|
+
levelOrTrack: levelInfo,
|
173
178
|
});
|
174
179
|
}
|
175
180
|
|
@@ -177,7 +182,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
177
182
|
event: Events.AUDIO_TRACK_LOADING,
|
178
183
|
data: TrackLoadingData,
|
179
184
|
) {
|
180
|
-
const { id, groupId, url, deliveryDirectives } = data;
|
185
|
+
const { id, groupId, url, deliveryDirectives, track } = data;
|
181
186
|
this.load({
|
182
187
|
id,
|
183
188
|
groupId,
|
@@ -186,6 +191,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
186
191
|
type: PlaylistContextType.AUDIO_TRACK,
|
187
192
|
url,
|
188
193
|
deliveryDirectives,
|
194
|
+
levelOrTrack: track,
|
189
195
|
});
|
190
196
|
}
|
191
197
|
|
@@ -193,7 +199,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
193
199
|
event: Events.SUBTITLE_TRACK_LOADING,
|
194
200
|
data: TrackLoadingData,
|
195
201
|
) {
|
196
|
-
const { id, groupId, url, deliveryDirectives } = data;
|
202
|
+
const { id, groupId, url, deliveryDirectives, track } = data;
|
197
203
|
this.load({
|
198
204
|
id,
|
199
205
|
groupId,
|
@@ -202,9 +208,30 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
202
208
|
type: PlaylistContextType.SUBTITLE_TRACK,
|
203
209
|
url,
|
204
210
|
deliveryDirectives,
|
211
|
+
levelOrTrack: track,
|
205
212
|
});
|
206
213
|
}
|
207
214
|
|
215
|
+
private onLevelsUpdated(
|
216
|
+
event: Events.LEVELS_UPDATED,
|
217
|
+
data: LevelsUpdatedData,
|
218
|
+
) {
|
219
|
+
// abort and delete loader of removed levels
|
220
|
+
const loader = this.loaders[PlaylistContextType.LEVEL];
|
221
|
+
if (loader) {
|
222
|
+
const context = loader.context;
|
223
|
+
if (
|
224
|
+
context &&
|
225
|
+
!data.levels.some(
|
226
|
+
(lvl) => lvl === (context as PlaylistLoaderContext).levelOrTrack,
|
227
|
+
)
|
228
|
+
) {
|
229
|
+
loader.abort();
|
230
|
+
delete this.loaders[PlaylistContextType.LEVEL];
|
231
|
+
}
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
208
235
|
private load(context: PlaylistLoaderContext): void {
|
209
236
|
const config = this.hls.config;
|
210
237
|
|
@@ -217,7 +244,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
217
244
|
if (
|
218
245
|
loaderContext &&
|
219
246
|
loaderContext.url === context.url &&
|
220
|
-
loaderContext.
|
247
|
+
loaderContext.levelOrTrack === context.levelOrTrack
|
221
248
|
) {
|
222
249
|
// same URL can't overlap
|
223
250
|
this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
|
@@ -680,6 +707,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
680
707
|
case PlaylistContextType.LEVEL:
|
681
708
|
hls.trigger(Events.LEVEL_LOADED, {
|
682
709
|
details: levelDetails,
|
710
|
+
levelInfo: (context.levelOrTrack as Level) || hls.levels[0],
|
683
711
|
level: levelIndex || 0,
|
684
712
|
id: id || 0,
|
685
713
|
stats,
|
@@ -690,6 +718,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
690
718
|
case PlaylistContextType.AUDIO_TRACK:
|
691
719
|
hls.trigger(Events.AUDIO_TRACK_LOADED, {
|
692
720
|
details: levelDetails,
|
721
|
+
track: context.levelOrTrack as MediaPlaylist,
|
693
722
|
id: id || 0,
|
694
723
|
groupId: groupId || '',
|
695
724
|
stats,
|
@@ -700,6 +729,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
|
700
729
|
case PlaylistContextType.SUBTITLE_TRACK:
|
701
730
|
hls.trigger(Events.SUBTITLE_TRACK_LOADED, {
|
702
731
|
details: levelDetails,
|
732
|
+
track: context.levelOrTrack as MediaPlaylist,
|
703
733
|
id: id || 0,
|
704
734
|
groupId: groupId || '',
|
705
735
|
stats,
|
package/src/types/events.ts
CHANGED
@@ -188,6 +188,7 @@ export interface LevelSwitchedData {
|
|
188
188
|
export interface TrackLoadingData {
|
189
189
|
id: number;
|
190
190
|
groupId: string;
|
191
|
+
track: MediaPlaylist;
|
191
192
|
url: string;
|
192
193
|
deliveryDirectives: HlsUrlParameters | null;
|
193
194
|
}
|
@@ -195,6 +196,7 @@ export interface TrackLoadingData {
|
|
195
196
|
export interface LevelLoadingData {
|
196
197
|
id: number;
|
197
198
|
level: number;
|
199
|
+
levelInfo: Level;
|
198
200
|
pathwayId: string | undefined;
|
199
201
|
url: string;
|
200
202
|
deliveryDirectives: HlsUrlParameters | null;
|
@@ -207,12 +209,14 @@ export interface TrackLoadedData {
|
|
207
209
|
networkDetails: any;
|
208
210
|
stats: LoaderStats;
|
209
211
|
deliveryDirectives: HlsUrlParameters | null;
|
212
|
+
track: MediaPlaylist;
|
210
213
|
}
|
211
214
|
|
212
215
|
export interface LevelLoadedData {
|
213
216
|
details: LevelDetails;
|
214
217
|
id: number;
|
215
218
|
level: number;
|
219
|
+
levelInfo: Level;
|
216
220
|
networkDetails: any;
|
217
221
|
stats: LoaderStats;
|
218
222
|
deliveryDirectives: HlsUrlParameters | null;
|
package/src/types/loader.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import type { LoaderConfig } from '../config';
|
2
|
-
import type { HlsUrlParameters } from './level';
|
2
|
+
import type { HlsUrlParameters, Level } from './level';
|
3
|
+
import type { MediaPlaylist } from './media-playlist';
|
3
4
|
import type { Fragment } from '../loader/fragment';
|
4
5
|
import type { Part } from '../loader/fragment';
|
5
6
|
import type { KeyLoaderInfo } from '../loader/key-loader';
|
@@ -191,4 +192,6 @@ export interface PlaylistLoaderContext extends LoaderContext {
|
|
191
192
|
levelDetails?: LevelDetails;
|
192
193
|
// Blocking playlist request delivery directives (or null id none were added to playlist url
|
193
194
|
deliveryDirectives: HlsUrlParameters | null;
|
195
|
+
// Reference to level or track object in hls.levels, hls.allAudioTracks, or hls.allSubtitleTracks (null when loading MVP)
|
196
|
+
levelOrTrack: Level | MediaPlaylist | null;
|
194
197
|
}
|