hls.js 1.5.13 → 1.5.14-0.canary.10417
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/README.md +4 -3
- package/dist/hls-demo.js +41 -38
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +4211 -2666
- package/dist/hls.js.d.ts +179 -110
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2841 -1921
- 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 +2569 -1639
- 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 +3572 -2017
- 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 +38 -38
- package/src/config.ts +5 -2
- package/src/controller/abr-controller.ts +39 -25
- package/src/controller/audio-stream-controller.ts +156 -136
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +27 -10
- package/src/controller/base-stream-controller.ts +234 -89
- package/src/controller/buffer-controller.ts +250 -97
- package/src/controller/buffer-operation-queue.ts +16 -19
- package/src/controller/cap-level-controller.ts +3 -2
- package/src/controller/cmcd-controller.ts +51 -14
- package/src/controller/content-steering-controller.ts +29 -15
- package/src/controller/eme-controller.ts +10 -23
- package/src/controller/error-controller.ts +28 -22
- package/src/controller/fps-controller.ts +8 -3
- package/src/controller/fragment-finders.ts +44 -16
- package/src/controller/fragment-tracker.ts +58 -25
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/id3-track-controller.ts +45 -35
- package/src/controller/latency-controller.ts +18 -13
- package/src/controller/level-controller.ts +37 -19
- package/src/controller/stream-controller.ts +100 -83
- package/src/controller/subtitle-stream-controller.ts +35 -47
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +20 -22
- package/src/crypt/aes-crypto.ts +21 -2
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +32 -16
- package/src/crypt/fast-aes-key.ts +28 -5
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +4 -3
- package/src/demux/audio/adts.ts +9 -4
- package/src/demux/audio/base-audio-demuxer.ts +16 -14
- package/src/demux/audio/mp3demuxer.ts +4 -3
- package/src/demux/audio/mpegaudio.ts +1 -1
- package/src/demux/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +2 -0
- package/src/demux/transmuxer-interface.ts +8 -16
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +75 -38
- package/src/demux/video/avc-video-parser.ts +210 -121
- package/src/demux/video/base-video-parser.ts +135 -2
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +749 -0
- package/src/events.ts +8 -1
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +84 -47
- package/src/loader/date-range.ts +71 -5
- package/src/loader/fragment-loader.ts +23 -21
- package/src/loader/fragment.ts +8 -4
- package/src/loader/key-loader.ts +3 -1
- package/src/loader/level-details.ts +6 -6
- package/src/loader/level-key.ts +10 -9
- package/src/loader/m3u8-parser.ts +138 -144
- package/src/loader/playlist-loader.ts +5 -7
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +32 -62
- package/src/remux/passthrough-remuxer.ts +1 -1
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +3 -1
- package/src/types/demuxer.ts +3 -0
- package/src/types/events.ts +19 -6
- package/src/types/fragment-tracker.ts +2 -2
- package/src/types/media-playlist.ts +9 -1
- package/src/types/remuxer.ts +1 -1
- package/src/utils/attr-list.ts +96 -9
- package/src/utils/buffer-helper.ts +12 -31
- package/src/utils/cea-608-parser.ts +1 -3
- package/src/utils/codecs.ts +34 -5
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/hash.ts +10 -0
- package/src/utils/hdr.ts +4 -7
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +1 -6
- package/src/utils/level-helper.ts +71 -44
- package/src/utils/logger.ts +58 -23
- package/src/utils/mp4-tools.ts +5 -3
- package/src/utils/rendition-helper.ts +100 -74
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/variable-substitution.ts +0 -19
- package/src/utils/webvtt-parser.ts +2 -12
- package/src/demux/id3.ts +0 -411
- package/src/types/general.ts +0 -6
@@ -13,6 +13,7 @@ export type CodecSetTier = {
|
|
13
13
|
minBitrate: number;
|
14
14
|
minHeight: number;
|
15
15
|
minFramerate: number;
|
16
|
+
minIndex: number;
|
16
17
|
maxScore: number;
|
17
18
|
videoRanges: Record<string, number>;
|
18
19
|
channels: Record<string, number>;
|
@@ -32,6 +33,7 @@ type StartParameters = {
|
|
32
33
|
preferHDR: boolean;
|
33
34
|
minFramerate: number;
|
34
35
|
minBitrate: number;
|
36
|
+
minIndex: number;
|
35
37
|
};
|
36
38
|
|
37
39
|
export function getStartCodecTier(
|
@@ -44,13 +46,15 @@ export function getStartCodecTier(
|
|
44
46
|
const codecSets = Object.keys(codecTiers);
|
45
47
|
const channelsPreference = audioPreference?.channels;
|
46
48
|
const audioCodecPreference = audioPreference?.audioCodec;
|
49
|
+
const videoCodecPreference = videoPreference?.videoCodec;
|
47
50
|
const preferStereo = channelsPreference && parseInt(channelsPreference) === 2;
|
48
51
|
// Use first level set to determine stereo, and minimum resolution and framerate
|
49
|
-
let hasStereo =
|
52
|
+
let hasStereo = false;
|
50
53
|
let hasCurrentVideoRange = false;
|
51
54
|
let minHeight = Infinity;
|
52
55
|
let minFramerate = Infinity;
|
53
56
|
let minBitrate = Infinity;
|
57
|
+
let minIndex = Infinity;
|
54
58
|
let selectedScore = 0;
|
55
59
|
let videoRanges: Array<VideoRange> = [];
|
56
60
|
|
@@ -61,7 +65,7 @@ export function getStartCodecTier(
|
|
61
65
|
|
62
66
|
for (let i = codecSets.length; i--; ) {
|
63
67
|
const tier = codecTiers[codecSets[i]];
|
64
|
-
hasStereo
|
68
|
+
hasStereo ||= tier.channels[2] > 0;
|
65
69
|
minHeight = Math.min(minHeight, tier.minHeight);
|
66
70
|
minFramerate = Math.min(minFramerate, tier.minFramerate);
|
67
71
|
minBitrate = Math.min(minBitrate, tier.minBitrate);
|
@@ -70,7 +74,6 @@ export function getStartCodecTier(
|
|
70
74
|
);
|
71
75
|
if (matchingVideoRanges.length > 0) {
|
72
76
|
hasCurrentVideoRange = true;
|
73
|
-
videoRanges = matchingVideoRanges;
|
74
77
|
}
|
75
78
|
}
|
76
79
|
minHeight = Number.isFinite(minHeight) ? minHeight : 0;
|
@@ -82,8 +85,8 @@ export function getStartCodecTier(
|
|
82
85
|
// If there are no variants with matching preference, set currentVideoRange to undefined
|
83
86
|
if (!hasCurrentVideoRange) {
|
84
87
|
currentVideoRange = undefined;
|
85
|
-
videoRanges = [];
|
86
88
|
}
|
89
|
+
const hasMultipleSets = codecSets.length > 1;
|
87
90
|
const codecSet = codecSets.reduce(
|
88
91
|
(selected: string | undefined, candidate: string) => {
|
89
92
|
// 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
|
@@ -91,80 +94,99 @@ export function getStartCodecTier(
|
|
91
94
|
if (candidate === selected) {
|
92
95
|
return selected;
|
93
96
|
}
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
if (!candidateTier.hasDefaultAudio) {
|
102
|
-
logStartCodecCandidateIgnored(
|
103
|
-
candidate,
|
104
|
-
`no renditions with default or auto-select sound found`,
|
105
|
-
);
|
106
|
-
return selected;
|
107
|
-
}
|
108
|
-
if (
|
109
|
-
audioCodecPreference &&
|
110
|
-
candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0
|
111
|
-
) {
|
112
|
-
logStartCodecCandidateIgnored(
|
113
|
-
candidate,
|
114
|
-
`audio codec preference "${audioCodecPreference}" not found`,
|
115
|
-
);
|
116
|
-
return selected;
|
117
|
-
}
|
118
|
-
if (channelsPreference && !preferStereo) {
|
119
|
-
if (!candidateTier.channels[channelsPreference]) {
|
97
|
+
videoRanges = hasCurrentVideoRange
|
98
|
+
? allowedVideoRanges.filter(
|
99
|
+
(range) => candidateTier.videoRanges[range] > 0,
|
100
|
+
)
|
101
|
+
: [];
|
102
|
+
if (hasMultipleSets) {
|
103
|
+
if (candidateTier.minBitrate > currentBw) {
|
120
104
|
logStartCodecCandidateIgnored(
|
121
105
|
candidate,
|
122
|
-
`
|
123
|
-
|
124
|
-
|
106
|
+
`min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`,
|
107
|
+
);
|
108
|
+
return selected;
|
109
|
+
}
|
110
|
+
if (!candidateTier.hasDefaultAudio) {
|
111
|
+
logStartCodecCandidateIgnored(
|
112
|
+
candidate,
|
113
|
+
`no renditions with default or auto-select sound found`,
|
114
|
+
);
|
115
|
+
return selected;
|
116
|
+
}
|
117
|
+
if (
|
118
|
+
audioCodecPreference &&
|
119
|
+
candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0
|
120
|
+
) {
|
121
|
+
logStartCodecCandidateIgnored(
|
122
|
+
candidate,
|
123
|
+
`audio codec preference "${audioCodecPreference}" not found`,
|
124
|
+
);
|
125
|
+
return selected;
|
126
|
+
}
|
127
|
+
if (channelsPreference && !preferStereo) {
|
128
|
+
if (!candidateTier.channels[channelsPreference]) {
|
129
|
+
logStartCodecCandidateIgnored(
|
130
|
+
candidate,
|
131
|
+
`no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(
|
132
|
+
candidateTier.channels,
|
133
|
+
)})`,
|
134
|
+
);
|
135
|
+
return selected;
|
136
|
+
}
|
137
|
+
} else if (
|
138
|
+
(!audioCodecPreference || preferStereo) &&
|
139
|
+
hasStereo &&
|
140
|
+
candidateTier.channels['2'] === 0
|
141
|
+
) {
|
142
|
+
logStartCodecCandidateIgnored(
|
143
|
+
candidate,
|
144
|
+
`no renditions with stereo sound found`,
|
145
|
+
);
|
146
|
+
return selected;
|
147
|
+
}
|
148
|
+
if (candidateTier.minHeight > maxHeight) {
|
149
|
+
logStartCodecCandidateIgnored(
|
150
|
+
candidate,
|
151
|
+
`min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`,
|
152
|
+
);
|
153
|
+
return selected;
|
154
|
+
}
|
155
|
+
if (candidateTier.minFramerate > maxFramerate) {
|
156
|
+
logStartCodecCandidateIgnored(
|
157
|
+
candidate,
|
158
|
+
`min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`,
|
159
|
+
);
|
160
|
+
return selected;
|
161
|
+
}
|
162
|
+
if (
|
163
|
+
!videoRanges.some((range) => candidateTier.videoRanges[range] > 0)
|
164
|
+
) {
|
165
|
+
logStartCodecCandidateIgnored(
|
166
|
+
candidate,
|
167
|
+
`no variants with VIDEO-RANGE of ${JSON.stringify(
|
168
|
+
videoRanges,
|
169
|
+
)} found`,
|
170
|
+
);
|
171
|
+
return selected;
|
172
|
+
}
|
173
|
+
if (
|
174
|
+
videoCodecPreference &&
|
175
|
+
candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0
|
176
|
+
) {
|
177
|
+
logStartCodecCandidateIgnored(
|
178
|
+
candidate,
|
179
|
+
`video codec preference "${videoCodecPreference}" not found`,
|
180
|
+
);
|
181
|
+
return selected;
|
182
|
+
}
|
183
|
+
if (candidateTier.maxScore < selectedScore) {
|
184
|
+
logStartCodecCandidateIgnored(
|
185
|
+
candidate,
|
186
|
+
`max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`,
|
125
187
|
);
|
126
188
|
return selected;
|
127
189
|
}
|
128
|
-
} else if (
|
129
|
-
(!audioCodecPreference || preferStereo) &&
|
130
|
-
hasStereo &&
|
131
|
-
candidateTier.channels['2'] === 0
|
132
|
-
) {
|
133
|
-
logStartCodecCandidateIgnored(
|
134
|
-
candidate,
|
135
|
-
`no renditions with stereo sound found`,
|
136
|
-
);
|
137
|
-
return selected;
|
138
|
-
}
|
139
|
-
if (candidateTier.minHeight > maxHeight) {
|
140
|
-
logStartCodecCandidateIgnored(
|
141
|
-
candidate,
|
142
|
-
`min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`,
|
143
|
-
);
|
144
|
-
return selected;
|
145
|
-
}
|
146
|
-
if (candidateTier.minFramerate > maxFramerate) {
|
147
|
-
logStartCodecCandidateIgnored(
|
148
|
-
candidate,
|
149
|
-
`min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`,
|
150
|
-
);
|
151
|
-
return selected;
|
152
|
-
}
|
153
|
-
if (!videoRanges.some((range) => candidateTier.videoRanges[range] > 0)) {
|
154
|
-
logStartCodecCandidateIgnored(
|
155
|
-
candidate,
|
156
|
-
`no variants with VIDEO-RANGE of ${JSON.stringify(
|
157
|
-
videoRanges,
|
158
|
-
)} found`,
|
159
|
-
);
|
160
|
-
return selected;
|
161
|
-
}
|
162
|
-
if (candidateTier.maxScore < selectedScore) {
|
163
|
-
logStartCodecCandidateIgnored(
|
164
|
-
candidate,
|
165
|
-
`max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`,
|
166
|
-
);
|
167
|
-
return selected;
|
168
190
|
}
|
169
191
|
// Remove candiates with less preferred codecs or more errors
|
170
192
|
if (
|
@@ -175,6 +197,7 @@ export function getStartCodecTier(
|
|
175
197
|
) {
|
176
198
|
return selected;
|
177
199
|
}
|
200
|
+
minIndex = candidateTier.minIndex;
|
178
201
|
selectedScore = candidateTier.maxScore;
|
179
202
|
return candidate;
|
180
203
|
},
|
@@ -186,6 +209,7 @@ export function getStartCodecTier(
|
|
186
209
|
preferHDR,
|
187
210
|
minFramerate,
|
188
211
|
minBitrate,
|
212
|
+
minIndex,
|
189
213
|
};
|
190
214
|
}
|
191
215
|
|
@@ -243,7 +267,7 @@ export function getCodecTiers(
|
|
243
267
|
): Record<string, CodecSetTier> {
|
244
268
|
return levels
|
245
269
|
.slice(minAutoLevel, maxAutoLevel + 1)
|
246
|
-
.reduce((tiers: Record<string, CodecSetTier>, level) => {
|
270
|
+
.reduce((tiers: Record<string, CodecSetTier>, level, index) => {
|
247
271
|
if (!level.codecSet) {
|
248
272
|
return tiers;
|
249
273
|
}
|
@@ -254,6 +278,7 @@ export function getCodecTiers(
|
|
254
278
|
minBitrate: Infinity,
|
255
279
|
minHeight: Infinity,
|
256
280
|
minFramerate: Infinity,
|
281
|
+
minIndex: index,
|
257
282
|
maxScore: 0,
|
258
283
|
videoRanges: { SDR: 0 },
|
259
284
|
channels: { '2': 0 },
|
@@ -265,6 +290,7 @@ export function getCodecTiers(
|
|
265
290
|
const lesserWidthOrHeight = Math.min(level.height, level.width);
|
266
291
|
tier.minHeight = Math.min(tier.minHeight, lesserWidthOrHeight);
|
267
292
|
tier.minFramerate = Math.min(tier.minFramerate, level.frameRate);
|
293
|
+
tier.minIndex = Math.min(tier.minIndex, index);
|
268
294
|
tier.maxScore = Math.max(tier.maxScore, level.score);
|
269
295
|
tier.fragmentError += level.fragmentError;
|
270
296
|
tier.videoRanges[level.videoRange] =
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// breaking up those two types in order to clarify what is happening in the decoding path.
|
2
|
+
type DecodedFrame<T> = { key: string; data: T; info?: any };
|
3
|
+
export type Frame = DecodedFrame<ArrayBuffer | string>;
|
4
|
+
// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
|
5
|
+
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
|
6
|
+
/* utf.js - UTF-8 <=> UTF-16 convertion
|
7
|
+
*
|
8
|
+
* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
9
|
+
* Version: 1.0
|
10
|
+
* LastModified: Dec 25 1999
|
11
|
+
* This library is free. You can redistribute it and/or modify it.
|
12
|
+
*/
|
13
|
+
|
14
|
+
export function strToUtf8array(str: string): Uint8Array {
|
15
|
+
return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
|
16
|
+
c.charCodeAt(0),
|
17
|
+
);
|
18
|
+
}
|
@@ -9,25 +9,6 @@ export function hasVariableReferences(str: string): boolean {
|
|
9
9
|
return VARIABLE_REPLACEMENT_REGEX.test(str);
|
10
10
|
}
|
11
11
|
|
12
|
-
export function substituteVariablesInAttributes(
|
13
|
-
parsed: Pick<
|
14
|
-
ParsedMultivariantPlaylist | LevelDetails,
|
15
|
-
'variableList' | 'hasVariableRefs' | 'playlistParsingError'
|
16
|
-
>,
|
17
|
-
attr: AttrList,
|
18
|
-
attributeNames: string[],
|
19
|
-
) {
|
20
|
-
if (parsed.variableList !== null || parsed.hasVariableRefs) {
|
21
|
-
for (let i = attributeNames.length; i--; ) {
|
22
|
-
const name = attributeNames[i];
|
23
|
-
const value = attr[name];
|
24
|
-
if (value) {
|
25
|
-
attr[name] = substituteVariables(parsed, value);
|
26
|
-
}
|
27
|
-
}
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
12
|
export function substituteVariables(
|
32
13
|
parsed: Pick<
|
33
14
|
ParsedMultivariantPlaylist | LevelDetails,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { VTTParser } from './vttparser';
|
2
|
-
import { utf8ArrayToStr } from '
|
2
|
+
import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
|
3
|
+
import { hash } from './hash';
|
3
4
|
import {
|
4
5
|
RationalTimestamp,
|
5
6
|
toMpegTsClockFromTimescale,
|
@@ -45,17 +46,6 @@ const cueString2millis = function (timeString: string) {
|
|
45
46
|
return ts;
|
46
47
|
};
|
47
48
|
|
48
|
-
// From https://github.com/darkskyapp/string-hash
|
49
|
-
const hash = function (text: string) {
|
50
|
-
let hash = 5381;
|
51
|
-
let i = text.length;
|
52
|
-
while (i) {
|
53
|
-
hash = (hash * 33) ^ text.charCodeAt(--i);
|
54
|
-
}
|
55
|
-
|
56
|
-
return (hash >>> 0).toString();
|
57
|
-
};
|
58
|
-
|
59
49
|
// Create a unique hash id for a cue based on start/end times and text.
|
60
50
|
// This helps timeline-controller to avoid showing repeated captions.
|
61
51
|
export function generateCueId(
|