@zenvor/hls.js 1.0.0
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/LICENSE +28 -0
- package/README.md +472 -0
- package/dist/hls-demo.js +26995 -0
- package/dist/hls-demo.js.map +1 -0
- package/dist/hls.d.mts +4204 -0
- package/dist/hls.d.ts +4204 -0
- package/dist/hls.js +40050 -0
- package/dist/hls.js.d.ts +4204 -0
- package/dist/hls.js.map +1 -0
- package/dist/hls.light.js +27145 -0
- package/dist/hls.light.js.map +1 -0
- package/dist/hls.light.min.js +2 -0
- package/dist/hls.light.min.js.map +1 -0
- package/dist/hls.light.mjs +26392 -0
- package/dist/hls.light.mjs.map +1 -0
- package/dist/hls.min.js +2 -0
- package/dist/hls.min.js.map +1 -0
- package/dist/hls.mjs +38956 -0
- package/dist/hls.mjs.map +1 -0
- package/dist/hls.worker.js +2 -0
- package/dist/hls.worker.js.map +1 -0
- package/package.json +143 -0
- package/src/config.ts +794 -0
- package/src/controller/abr-controller.ts +1019 -0
- package/src/controller/algo-data-controller.ts +794 -0
- package/src/controller/audio-stream-controller.ts +1099 -0
- package/src/controller/audio-track-controller.ts +454 -0
- package/src/controller/base-playlist-controller.ts +438 -0
- package/src/controller/base-stream-controller.ts +2526 -0
- package/src/controller/buffer-controller.ts +2015 -0
- package/src/controller/buffer-operation-queue.ts +159 -0
- package/src/controller/cap-level-controller.ts +367 -0
- package/src/controller/cmcd-controller.ts +422 -0
- package/src/controller/content-steering-controller.ts +622 -0
- package/src/controller/eme-controller.ts +1617 -0
- package/src/controller/error-controller.ts +627 -0
- package/src/controller/fps-controller.ts +146 -0
- package/src/controller/fragment-finders.ts +256 -0
- package/src/controller/fragment-tracker.ts +567 -0
- package/src/controller/gap-controller.ts +719 -0
- package/src/controller/id3-track-controller.ts +488 -0
- package/src/controller/interstitial-player.ts +302 -0
- package/src/controller/interstitials-controller.ts +2895 -0
- package/src/controller/interstitials-schedule.ts +698 -0
- package/src/controller/latency-controller.ts +294 -0
- package/src/controller/level-controller.ts +776 -0
- package/src/controller/stream-controller.ts +1597 -0
- package/src/controller/subtitle-stream-controller.ts +508 -0
- package/src/controller/subtitle-track-controller.ts +617 -0
- package/src/controller/timeline-controller.ts +677 -0
- package/src/crypt/aes-crypto.ts +36 -0
- package/src/crypt/aes-decryptor.ts +339 -0
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +225 -0
- package/src/crypt/fast-aes-key.ts +39 -0
- package/src/define-plugin.d.ts +17 -0
- package/src/demux/audio/aacdemuxer.ts +126 -0
- package/src/demux/audio/ac3-demuxer.ts +170 -0
- package/src/demux/audio/adts.ts +249 -0
- package/src/demux/audio/base-audio-demuxer.ts +205 -0
- package/src/demux/audio/dolby.ts +21 -0
- package/src/demux/audio/mp3demuxer.ts +85 -0
- package/src/demux/audio/mpegaudio.ts +177 -0
- package/src/demux/chunk-cache.ts +42 -0
- package/src/demux/dummy-demuxed-track.ts +13 -0
- package/src/demux/inject-worker.ts +75 -0
- package/src/demux/mp4demuxer.ts +234 -0
- package/src/demux/sample-aes.ts +198 -0
- package/src/demux/transmuxer-interface.ts +449 -0
- package/src/demux/transmuxer-worker.ts +221 -0
- package/src/demux/transmuxer.ts +560 -0
- package/src/demux/tsdemuxer.ts +1256 -0
- package/src/demux/video/avc-video-parser.ts +401 -0
- package/src/demux/video/base-video-parser.ts +198 -0
- package/src/demux/video/exp-golomb.ts +153 -0
- package/src/demux/video/hevc-video-parser.ts +736 -0
- package/src/empty-es.js +5 -0
- package/src/empty.js +3 -0
- package/src/errors.ts +107 -0
- package/src/events.ts +548 -0
- package/src/exports-default.ts +3 -0
- package/src/exports-named.ts +81 -0
- package/src/hls.ts +1613 -0
- package/src/is-supported.ts +54 -0
- package/src/loader/date-range.ts +207 -0
- package/src/loader/fragment-loader.ts +403 -0
- package/src/loader/fragment.ts +487 -0
- package/src/loader/interstitial-asset-list.ts +162 -0
- package/src/loader/interstitial-event.ts +337 -0
- package/src/loader/key-loader.ts +439 -0
- package/src/loader/level-details.ts +203 -0
- package/src/loader/level-key.ts +259 -0
- package/src/loader/load-stats.ts +17 -0
- package/src/loader/m3u8-parser.ts +1072 -0
- package/src/loader/playlist-loader.ts +839 -0
- package/src/polyfills/number.ts +15 -0
- package/src/remux/aac-helper.ts +81 -0
- package/src/remux/mp4-generator.ts +1380 -0
- package/src/remux/mp4-remuxer.ts +1261 -0
- package/src/remux/passthrough-remuxer.ts +434 -0
- package/src/task-loop.ts +130 -0
- package/src/types/algo.ts +44 -0
- package/src/types/buffer.ts +105 -0
- package/src/types/component-api.ts +20 -0
- package/src/types/demuxer.ts +208 -0
- package/src/types/events.ts +574 -0
- package/src/types/fragment-tracker.ts +23 -0
- package/src/types/level.ts +268 -0
- package/src/types/loader.ts +198 -0
- package/src/types/media-playlist.ts +92 -0
- package/src/types/network-details.ts +3 -0
- package/src/types/remuxer.ts +104 -0
- package/src/types/track.ts +12 -0
- package/src/types/transmuxer.ts +46 -0
- package/src/types/tuples.ts +6 -0
- package/src/types/vtt.ts +11 -0
- package/src/utils/arrays.ts +22 -0
- package/src/utils/attr-list.ts +192 -0
- package/src/utils/binary-search.ts +46 -0
- package/src/utils/buffer-helper.ts +173 -0
- package/src/utils/cea-608-parser.ts +1413 -0
- package/src/utils/chunker.ts +41 -0
- package/src/utils/codecs.ts +314 -0
- package/src/utils/cues.ts +96 -0
- package/src/utils/discontinuities.ts +174 -0
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/error-helper.ts +95 -0
- package/src/utils/event-listener-helper.ts +16 -0
- package/src/utils/ewma-bandwidth-estimator.ts +97 -0
- package/src/utils/ewma.ts +43 -0
- package/src/utils/fetch-loader.ts +331 -0
- package/src/utils/global.ts +2 -0
- package/src/utils/hash.ts +10 -0
- package/src/utils/hdr.ts +67 -0
- package/src/utils/hex.ts +32 -0
- package/src/utils/imsc1-ttml-parser.ts +261 -0
- package/src/utils/keysystem-util.ts +45 -0
- package/src/utils/level-helper.ts +629 -0
- package/src/utils/logger.ts +120 -0
- package/src/utils/media-option-attributes.ts +49 -0
- package/src/utils/mediacapabilities-helper.ts +301 -0
- package/src/utils/mediakeys-helper.ts +210 -0
- package/src/utils/mediasource-helper.ts +37 -0
- package/src/utils/mp4-tools.ts +1473 -0
- package/src/utils/number.ts +3 -0
- package/src/utils/numeric-encoding-utils.ts +26 -0
- package/src/utils/output-filter.ts +46 -0
- package/src/utils/rendition-helper.ts +505 -0
- package/src/utils/safe-json-stringify.ts +22 -0
- package/src/utils/texttrack-utils.ts +164 -0
- package/src/utils/time-ranges.ts +17 -0
- package/src/utils/timescale-conversion.ts +46 -0
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/variable-substitution.ts +105 -0
- package/src/utils/vttcue.ts +384 -0
- package/src/utils/vttparser.ts +497 -0
- package/src/utils/webvtt-parser.ts +166 -0
- package/src/utils/xhr-loader.ts +337 -0
- package/src/version.ts +1 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import BasePlaylistController from './base-playlist-controller';
|
|
2
|
+
import { ErrorDetails, ErrorTypes } from '../errors';
|
|
3
|
+
import { Events } from '../events';
|
|
4
|
+
import { PlaylistContextType } from '../types/loader';
|
|
5
|
+
import { mediaAttributesIdentical } from '../utils/media-option-attributes';
|
|
6
|
+
import {
|
|
7
|
+
audioMatchPredicate,
|
|
8
|
+
findClosestLevelWithAudioGroup,
|
|
9
|
+
findMatchingOption,
|
|
10
|
+
matchesOption,
|
|
11
|
+
useAlternateAudio,
|
|
12
|
+
} from '../utils/rendition-helper';
|
|
13
|
+
import type Hls from '../hls';
|
|
14
|
+
import type {
|
|
15
|
+
AudioTrackLoadedData,
|
|
16
|
+
AudioTracksUpdatedData,
|
|
17
|
+
ErrorData,
|
|
18
|
+
LevelLoadingData,
|
|
19
|
+
LevelSwitchingData,
|
|
20
|
+
ManifestParsedData,
|
|
21
|
+
} from '../types/events';
|
|
22
|
+
import type { HlsUrlParameters } from '../types/level';
|
|
23
|
+
import type {
|
|
24
|
+
AudioSelectionOption,
|
|
25
|
+
MediaPlaylist,
|
|
26
|
+
} from '../types/media-playlist';
|
|
27
|
+
|
|
28
|
+
class AudioTrackController extends BasePlaylistController {
|
|
29
|
+
private tracks: MediaPlaylist[] = [];
|
|
30
|
+
private groupIds: (string | undefined)[] | null = null;
|
|
31
|
+
private tracksInGroup: MediaPlaylist[] = [];
|
|
32
|
+
private trackId: number = -1;
|
|
33
|
+
private currentTrack: MediaPlaylist | null = null;
|
|
34
|
+
private selectDefaultTrack: boolean = true;
|
|
35
|
+
|
|
36
|
+
constructor(hls: Hls) {
|
|
37
|
+
super(hls, 'audio-track-controller');
|
|
38
|
+
this.registerListeners();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private registerListeners() {
|
|
42
|
+
const { hls } = this;
|
|
43
|
+
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
|
44
|
+
hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
|
45
|
+
hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
|
46
|
+
hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);
|
|
47
|
+
hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);
|
|
48
|
+
hls.on(Events.ERROR, this.onError, this);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private unregisterListeners() {
|
|
52
|
+
const { hls } = this;
|
|
53
|
+
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
|
54
|
+
hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
|
|
55
|
+
hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this);
|
|
56
|
+
hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this);
|
|
57
|
+
hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);
|
|
58
|
+
hls.off(Events.ERROR, this.onError, this);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public destroy() {
|
|
62
|
+
this.unregisterListeners();
|
|
63
|
+
this.tracks.length = 0;
|
|
64
|
+
this.tracksInGroup.length = 0;
|
|
65
|
+
this.currentTrack = null;
|
|
66
|
+
super.destroy();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected onManifestLoading(): void {
|
|
70
|
+
this.tracks = [];
|
|
71
|
+
this.tracksInGroup = [];
|
|
72
|
+
this.groupIds = null;
|
|
73
|
+
this.currentTrack = null;
|
|
74
|
+
this.trackId = -1;
|
|
75
|
+
this.selectDefaultTrack = true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected onManifestParsed(
|
|
79
|
+
event: Events.MANIFEST_PARSED,
|
|
80
|
+
data: ManifestParsedData,
|
|
81
|
+
): void {
|
|
82
|
+
this.tracks = data.audioTracks || [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
protected onAudioTrackLoaded(
|
|
86
|
+
event: Events.AUDIO_TRACK_LOADED,
|
|
87
|
+
data: AudioTrackLoadedData,
|
|
88
|
+
): void {
|
|
89
|
+
const { id, groupId, details } = data;
|
|
90
|
+
const trackInActiveGroup = this.tracksInGroup[id];
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
!trackInActiveGroup ||
|
|
94
|
+
trackInActiveGroup.groupId !== (groupId as string | undefined)
|
|
95
|
+
) {
|
|
96
|
+
this.warn(
|
|
97
|
+
`Audio track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup?.groupId}`,
|
|
98
|
+
);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const curDetails = trackInActiveGroup.details;
|
|
103
|
+
trackInActiveGroup.details = data.details;
|
|
104
|
+
this.log(
|
|
105
|
+
`Audio track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (id === this.trackId) {
|
|
109
|
+
this.playlistLoaded(id, data, curDetails);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected onLevelLoading(
|
|
114
|
+
event: Events.LEVEL_LOADING,
|
|
115
|
+
data: LevelLoadingData,
|
|
116
|
+
): void {
|
|
117
|
+
this.switchLevel(data.level);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
protected onLevelSwitching(
|
|
121
|
+
event: Events.LEVEL_SWITCHING,
|
|
122
|
+
data: LevelSwitchingData,
|
|
123
|
+
): void {
|
|
124
|
+
this.switchLevel(data.level);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private switchLevel(levelIndex: number) {
|
|
128
|
+
const levelInfo = this.hls.levels[levelIndex];
|
|
129
|
+
if (!levelInfo) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const audioGroups = levelInfo.audioGroups || null;
|
|
133
|
+
const currentGroups = this.groupIds;
|
|
134
|
+
let currentTrack = this.currentTrack;
|
|
135
|
+
if (
|
|
136
|
+
!audioGroups ||
|
|
137
|
+
currentGroups?.length !== audioGroups?.length ||
|
|
138
|
+
audioGroups?.some((groupId) => currentGroups?.indexOf(groupId) === -1)
|
|
139
|
+
) {
|
|
140
|
+
this.groupIds = audioGroups;
|
|
141
|
+
this.trackId = -1;
|
|
142
|
+
this.currentTrack = null;
|
|
143
|
+
|
|
144
|
+
const audioTracks = this.tracks.filter(
|
|
145
|
+
(track): boolean =>
|
|
146
|
+
!audioGroups || audioGroups.indexOf(track.groupId) !== -1,
|
|
147
|
+
);
|
|
148
|
+
if (audioTracks.length) {
|
|
149
|
+
// Disable selectDefaultTrack if there are no default tracks
|
|
150
|
+
if (
|
|
151
|
+
this.selectDefaultTrack &&
|
|
152
|
+
!audioTracks.some((track) => track.default)
|
|
153
|
+
) {
|
|
154
|
+
this.selectDefaultTrack = false;
|
|
155
|
+
}
|
|
156
|
+
// track.id should match hls.audioTracks index
|
|
157
|
+
audioTracks.forEach((track, i) => {
|
|
158
|
+
track.id = i;
|
|
159
|
+
});
|
|
160
|
+
} else if (!currentTrack && !this.tracksInGroup.length) {
|
|
161
|
+
// Do not dispatch AUDIO_TRACKS_UPDATED when there were and are no tracks
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.tracksInGroup = audioTracks;
|
|
165
|
+
|
|
166
|
+
// Find preferred track
|
|
167
|
+
const audioPreference = this.hls.config.audioPreference;
|
|
168
|
+
if (!currentTrack && audioPreference) {
|
|
169
|
+
const groupIndex = findMatchingOption(
|
|
170
|
+
audioPreference,
|
|
171
|
+
audioTracks,
|
|
172
|
+
audioMatchPredicate,
|
|
173
|
+
);
|
|
174
|
+
if (groupIndex > -1) {
|
|
175
|
+
currentTrack = audioTracks[groupIndex];
|
|
176
|
+
} else {
|
|
177
|
+
const allIndex = findMatchingOption(audioPreference, this.tracks);
|
|
178
|
+
currentTrack = this.tracks[allIndex];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Select initial track
|
|
183
|
+
let trackId = this.findTrackId(currentTrack);
|
|
184
|
+
if (trackId === -1 && currentTrack) {
|
|
185
|
+
trackId = this.findTrackId(null);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Dispatch events and load track if needed
|
|
189
|
+
const audioTracksUpdated: AudioTracksUpdatedData = { audioTracks };
|
|
190
|
+
this.log(
|
|
191
|
+
`Updating audio tracks, ${
|
|
192
|
+
audioTracks.length
|
|
193
|
+
} track(s) found in group(s): ${audioGroups?.join(',')}`,
|
|
194
|
+
);
|
|
195
|
+
this.hls.trigger(Events.AUDIO_TRACKS_UPDATED, audioTracksUpdated);
|
|
196
|
+
|
|
197
|
+
const selectedTrackId = this.trackId;
|
|
198
|
+
if (trackId !== -1 && selectedTrackId === -1) {
|
|
199
|
+
this.setAudioTrack(trackId);
|
|
200
|
+
} else if (audioTracks.length && selectedTrackId === -1) {
|
|
201
|
+
const error = new Error(
|
|
202
|
+
`No audio track selected for current audio group-ID(s): ${this.groupIds?.join(
|
|
203
|
+
',',
|
|
204
|
+
)} track count: ${audioTracks.length}`,
|
|
205
|
+
);
|
|
206
|
+
this.warn(error.message);
|
|
207
|
+
|
|
208
|
+
this.hls.trigger(Events.ERROR, {
|
|
209
|
+
type: ErrorTypes.MEDIA_ERROR,
|
|
210
|
+
details: ErrorDetails.AUDIO_TRACK_LOAD_ERROR,
|
|
211
|
+
fatal: true,
|
|
212
|
+
error,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
protected onError(event: Events.ERROR, data: ErrorData): void {
|
|
219
|
+
if (data.fatal || !data.context) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (
|
|
224
|
+
data.context.type === PlaylistContextType.AUDIO_TRACK &&
|
|
225
|
+
data.context.id === this.trackId &&
|
|
226
|
+
(!this.groupIds || this.groupIds.indexOf(data.context.groupId) !== -1)
|
|
227
|
+
) {
|
|
228
|
+
this.checkRetry(data);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
get allAudioTracks(): MediaPlaylist[] {
|
|
233
|
+
return this.tracks;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
get audioTracks(): MediaPlaylist[] {
|
|
237
|
+
return this.tracksInGroup;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
get audioTrack(): number {
|
|
241
|
+
return this.trackId;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
set audioTrack(newId: number) {
|
|
245
|
+
// If audio track is selected from API then don't choose from the manifest default track
|
|
246
|
+
this.selectDefaultTrack = false;
|
|
247
|
+
this.setAudioTrack(newId);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
get nextAudioTrack(): number {
|
|
251
|
+
return this.trackId;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
set nextAudioTrack(newId: number) {
|
|
255
|
+
// If audio track is selected from API then don't choose from the manifest default track
|
|
256
|
+
this.selectDefaultTrack = false;
|
|
257
|
+
this.setAudioTrack(newId, false);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public setAudioOption(
|
|
261
|
+
audioOption: MediaPlaylist | AudioSelectionOption | undefined,
|
|
262
|
+
): MediaPlaylist | null {
|
|
263
|
+
const hls = this.hls;
|
|
264
|
+
hls.config.audioPreference = audioOption;
|
|
265
|
+
if (audioOption) {
|
|
266
|
+
const allAudioTracks = this.allAudioTracks;
|
|
267
|
+
this.selectDefaultTrack = false;
|
|
268
|
+
if (allAudioTracks.length) {
|
|
269
|
+
// First see if current option matches (no switch op)
|
|
270
|
+
const currentTrack = this.currentTrack;
|
|
271
|
+
if (
|
|
272
|
+
currentTrack &&
|
|
273
|
+
matchesOption(audioOption, currentTrack, audioMatchPredicate)
|
|
274
|
+
) {
|
|
275
|
+
return currentTrack;
|
|
276
|
+
}
|
|
277
|
+
// Find option in available tracks (tracksInGroup)
|
|
278
|
+
const groupIndex = findMatchingOption(
|
|
279
|
+
audioOption,
|
|
280
|
+
this.tracksInGroup,
|
|
281
|
+
audioMatchPredicate,
|
|
282
|
+
);
|
|
283
|
+
if (groupIndex > -1) {
|
|
284
|
+
const track = this.tracksInGroup[groupIndex];
|
|
285
|
+
this.setAudioTrack(groupIndex);
|
|
286
|
+
return track;
|
|
287
|
+
} else if (currentTrack) {
|
|
288
|
+
// Find option in nearest level audio group
|
|
289
|
+
let searchIndex = hls.loadLevel;
|
|
290
|
+
if (searchIndex === -1) {
|
|
291
|
+
searchIndex = hls.firstAutoLevel;
|
|
292
|
+
}
|
|
293
|
+
const switchIndex = findClosestLevelWithAudioGroup(
|
|
294
|
+
audioOption,
|
|
295
|
+
hls.levels,
|
|
296
|
+
allAudioTracks,
|
|
297
|
+
searchIndex,
|
|
298
|
+
audioMatchPredicate,
|
|
299
|
+
);
|
|
300
|
+
if (switchIndex === -1) {
|
|
301
|
+
// could not find matching variant
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
// and switch level to acheive the audio group switch
|
|
305
|
+
hls.nextLoadLevel = switchIndex;
|
|
306
|
+
}
|
|
307
|
+
if (audioOption.channels || audioOption.audioCodec) {
|
|
308
|
+
// Could not find a match with codec / channels predicate
|
|
309
|
+
// Find a match without channels or codec
|
|
310
|
+
const withoutCodecAndChannelsMatch = findMatchingOption(
|
|
311
|
+
audioOption,
|
|
312
|
+
allAudioTracks,
|
|
313
|
+
);
|
|
314
|
+
if (withoutCodecAndChannelsMatch > -1) {
|
|
315
|
+
return allAudioTracks[withoutCodecAndChannelsMatch];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private setAudioTrack(newId: number, flushImmediate: boolean = true): void {
|
|
324
|
+
const tracks = this.tracksInGroup;
|
|
325
|
+
|
|
326
|
+
// check if level idx is valid
|
|
327
|
+
if (newId < 0 || newId >= tracks.length) {
|
|
328
|
+
this.warn(`Invalid audio track id: ${newId}`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
this.selectDefaultTrack = false;
|
|
333
|
+
const lastTrack = this.currentTrack;
|
|
334
|
+
const track = tracks[newId];
|
|
335
|
+
const trackLoaded = track.details && !track.details.live;
|
|
336
|
+
if (newId === this.trackId && track === lastTrack && trackLoaded) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
this.log(
|
|
340
|
+
`Switching to audio-track ${newId} "${track.name}" lang:${track.lang} group:${track.groupId} channels:${track.channels}`,
|
|
341
|
+
);
|
|
342
|
+
this.trackId = newId;
|
|
343
|
+
this.currentTrack = track;
|
|
344
|
+
this.hls.trigger(Events.AUDIO_TRACK_SWITCHING, {
|
|
345
|
+
...track,
|
|
346
|
+
flushImmediate,
|
|
347
|
+
});
|
|
348
|
+
// Do not reload track unless live
|
|
349
|
+
if (trackLoaded) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const hlsUrlParameters = this.switchParams(
|
|
353
|
+
track.url,
|
|
354
|
+
lastTrack?.details,
|
|
355
|
+
track.details,
|
|
356
|
+
);
|
|
357
|
+
this.loadPlaylist(hlsUrlParameters);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private findTrackId(currentTrack: MediaPlaylist | null): number {
|
|
361
|
+
const audioTracks = this.tracksInGroup;
|
|
362
|
+
for (let i = 0; i < audioTracks.length; i++) {
|
|
363
|
+
const track = audioTracks[i];
|
|
364
|
+
if (this.selectDefaultTrack && !track.default) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (
|
|
368
|
+
!currentTrack ||
|
|
369
|
+
matchesOption(currentTrack, track, audioMatchPredicate)
|
|
370
|
+
) {
|
|
371
|
+
return i;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (currentTrack) {
|
|
375
|
+
const { name, lang, assocLang, characteristics, audioCodec, channels } =
|
|
376
|
+
currentTrack;
|
|
377
|
+
for (let i = 0; i < audioTracks.length; i++) {
|
|
378
|
+
const track = audioTracks[i];
|
|
379
|
+
if (
|
|
380
|
+
matchesOption(
|
|
381
|
+
{ name, lang, assocLang, characteristics, audioCodec, channels },
|
|
382
|
+
track,
|
|
383
|
+
audioMatchPredicate,
|
|
384
|
+
)
|
|
385
|
+
) {
|
|
386
|
+
return i;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
for (let i = 0; i < audioTracks.length; i++) {
|
|
390
|
+
const track = audioTracks[i];
|
|
391
|
+
if (
|
|
392
|
+
mediaAttributesIdentical(currentTrack.attrs, track.attrs, [
|
|
393
|
+
'LANGUAGE',
|
|
394
|
+
'ASSOC-LANGUAGE',
|
|
395
|
+
'CHARACTERISTICS',
|
|
396
|
+
])
|
|
397
|
+
) {
|
|
398
|
+
return i;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
for (let i = 0; i < audioTracks.length; i++) {
|
|
402
|
+
const track = audioTracks[i];
|
|
403
|
+
if (
|
|
404
|
+
mediaAttributesIdentical(currentTrack.attrs, track.attrs, [
|
|
405
|
+
'LANGUAGE',
|
|
406
|
+
])
|
|
407
|
+
) {
|
|
408
|
+
return i;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return -1;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
protected loadPlaylist(hlsUrlParameters?: HlsUrlParameters): void {
|
|
416
|
+
super.loadPlaylist();
|
|
417
|
+
const audioTrack = this.currentTrack;
|
|
418
|
+
if (!this.shouldLoadPlaylist(audioTrack)) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// Do not load audio rendition with URI matching main variant URI
|
|
422
|
+
if (useAlternateAudio(audioTrack.url, this.hls)) {
|
|
423
|
+
this.scheduleLoading(audioTrack, hlsUrlParameters);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
protected loadingPlaylist(
|
|
428
|
+
audioTrack: MediaPlaylist,
|
|
429
|
+
hlsUrlParameters: HlsUrlParameters | undefined,
|
|
430
|
+
) {
|
|
431
|
+
super.loadingPlaylist(audioTrack, hlsUrlParameters);
|
|
432
|
+
const id = audioTrack.id;
|
|
433
|
+
const groupId = audioTrack.groupId as string;
|
|
434
|
+
const url = this.getUrlWithDirectives(audioTrack.url, hlsUrlParameters);
|
|
435
|
+
const details = audioTrack.details;
|
|
436
|
+
const age = details?.age;
|
|
437
|
+
this.log(
|
|
438
|
+
`Loading audio-track ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}${
|
|
439
|
+
hlsUrlParameters?.msn !== undefined
|
|
440
|
+
? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part
|
|
441
|
+
: ''
|
|
442
|
+
}${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
|
|
443
|
+
);
|
|
444
|
+
this.hls.trigger(Events.AUDIO_TRACK_LOADING, {
|
|
445
|
+
url,
|
|
446
|
+
id,
|
|
447
|
+
groupId,
|
|
448
|
+
deliveryDirectives: hlsUrlParameters || null,
|
|
449
|
+
track: audioTrack,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export default AudioTrackController;
|