hls.js 1.5.3 → 1.5.5-0.canary.9977
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 +1 -0
- package/dist/hls-demo.js +10 -0
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +1954 -1103
- package/dist/hls.js.d.ts +63 -50
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1631 -784
- 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 +1428 -590
- 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 +1703 -866
- 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 +18 -18
- package/src/config.ts +3 -2
- package/src/controller/abr-controller.ts +24 -22
- package/src/controller/audio-stream-controller.ts +16 -17
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +7 -7
- package/src/controller/base-stream-controller.ts +56 -29
- package/src/controller/buffer-controller.ts +11 -11
- package/src/controller/cap-level-controller.ts +1 -2
- package/src/controller/cmcd-controller.ts +25 -3
- package/src/controller/content-steering-controller.ts +8 -6
- package/src/controller/eme-controller.ts +9 -22
- package/src/controller/error-controller.ts +6 -8
- package/src/controller/fps-controller.ts +2 -3
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/latency-controller.ts +9 -11
- package/src/controller/level-controller.ts +5 -17
- package/src/controller/stream-controller.ts +27 -33
- package/src/controller/subtitle-stream-controller.ts +14 -15
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +23 -30
- package/src/crypt/aes-crypto.ts +21 -2
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +32 -18
- package/src/crypt/fast-aes-key.ts +24 -5
- package/src/demux/audio/adts.ts +9 -4
- package/src/demux/sample-aes.ts +2 -0
- package/src/demux/transmuxer-interface.ts +4 -12
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +63 -37
- package/src/demux/video/avc-video-parser.ts +208 -119
- package/src/demux/video/base-video-parser.ts +134 -2
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +746 -0
- package/src/events.ts +7 -0
- package/src/hls.ts +42 -34
- package/src/loader/fragment-loader.ts +9 -2
- package/src/loader/key-loader.ts +2 -0
- package/src/loader/level-key.ts +10 -9
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +23 -7
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +2 -0
- package/src/types/demuxer.ts +3 -0
- package/src/types/events.ts +4 -0
- package/src/utils/codecs.ts +33 -4
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/logger.ts +53 -24
- package/src/utils/mp4-tools.ts +28 -9
package/src/types/demuxer.ts
CHANGED
@@ -64,6 +64,7 @@ export interface DemuxedAudioTrack extends DemuxedTrack {
|
|
64
64
|
segmentCodec?: string;
|
65
65
|
channelCount?: number;
|
66
66
|
manifestCodec?: string;
|
67
|
+
parsedCodec?: string;
|
67
68
|
samples: AudioSample[];
|
68
69
|
}
|
69
70
|
|
@@ -72,12 +73,14 @@ export interface DemuxedVideoTrackBase extends DemuxedTrack {
|
|
72
73
|
height?: number;
|
73
74
|
pixelRatio?: [number, number];
|
74
75
|
audFound?: boolean;
|
76
|
+
vps?: Uint8Array[];
|
75
77
|
pps?: Uint8Array[];
|
76
78
|
sps?: Uint8Array[];
|
77
79
|
naluState?: number;
|
78
80
|
segmentCodec?: string;
|
79
81
|
manifestCodec?: string;
|
80
82
|
samples: VideoSample[] | Uint8Array;
|
83
|
+
params?: object;
|
81
84
|
}
|
82
85
|
|
83
86
|
export interface DemuxedVideoTrack extends DemuxedVideoTrackBase {
|
package/src/types/events.ts
CHANGED
package/src/utils/codecs.ts
CHANGED
@@ -147,12 +147,15 @@ function getCodecCompatibleNameLower(
|
|
147
147
|
return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]!;
|
148
148
|
}
|
149
149
|
|
150
|
-
// Idealy fLaC and Opus would be first (spec-compliant) but
|
151
|
-
// some browsers will report that fLaC is supported then fail.
|
152
|
-
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
153
150
|
const codecsToCheck = {
|
151
|
+
// Idealy fLaC and Opus would be first (spec-compliant) but
|
152
|
+
// some browsers will report that fLaC is supported then fail.
|
153
|
+
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
|
154
154
|
flac: ['flac', 'fLaC', 'FLAC'],
|
155
155
|
opus: ['opus', 'Opus'],
|
156
|
+
// Replace audio codec info if browser does not support mp4a.40.34,
|
157
|
+
// and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
|
158
|
+
'mp4a.40.34': ['mp3'],
|
156
159
|
}[lowerCaseCodec];
|
157
160
|
|
158
161
|
for (let i = 0; i < codecsToCheck.length; i++) {
|
@@ -165,13 +168,18 @@ function getCodecCompatibleNameLower(
|
|
165
168
|
) {
|
166
169
|
CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
|
167
170
|
return codecsToCheck[i];
|
171
|
+
} else if (
|
172
|
+
codecsToCheck[i] === 'mp3' &&
|
173
|
+
getMediaSource(preferManagedMediaSource)?.isTypeSupported('audio/mpeg')
|
174
|
+
) {
|
175
|
+
return '';
|
168
176
|
}
|
169
177
|
}
|
170
178
|
|
171
179
|
return lowerCaseCodec;
|
172
180
|
}
|
173
181
|
|
174
|
-
const AUDIO_CODEC_REGEXP = /flac|opus/i;
|
182
|
+
const AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
|
175
183
|
export function getCodecCompatibleName(
|
176
184
|
codec: string,
|
177
185
|
preferManagedMediaSource = true,
|
@@ -209,3 +217,24 @@ export function convertAVC1ToAVCOTI(codec: string) {
|
|
209
217
|
}
|
210
218
|
return codec;
|
211
219
|
}
|
220
|
+
|
221
|
+
export interface TypeSupported {
|
222
|
+
mpeg: boolean;
|
223
|
+
mp3: boolean;
|
224
|
+
ac3: boolean;
|
225
|
+
}
|
226
|
+
|
227
|
+
export function getM2TSSupportedAudioTypes(
|
228
|
+
preferManagedMediaSource: boolean,
|
229
|
+
): TypeSupported {
|
230
|
+
const MediaSource = getMediaSource(preferManagedMediaSource) || {
|
231
|
+
isTypeSupported: () => false,
|
232
|
+
};
|
233
|
+
return {
|
234
|
+
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
|
235
|
+
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
|
236
|
+
ac3: __USE_M2TS_ADVANCED_CODECS__
|
237
|
+
? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
|
238
|
+
: false,
|
239
|
+
};
|
240
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { DecrypterAesMode } from '../crypt/decrypter-aes-mode';
|
2
|
+
|
3
|
+
export function isFullSegmentEncryption(method: string): boolean {
|
4
|
+
return (
|
5
|
+
method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR'
|
6
|
+
);
|
7
|
+
}
|
8
|
+
|
9
|
+
export function getAesModeFromFullSegmentMethod(
|
10
|
+
method: string,
|
11
|
+
): DecrypterAesMode {
|
12
|
+
switch (method) {
|
13
|
+
case 'AES-128':
|
14
|
+
case 'AES-256':
|
15
|
+
return DecrypterAesMode.cbc;
|
16
|
+
case 'AES-256-CTR':
|
17
|
+
return DecrypterAesMode.ctr;
|
18
|
+
default:
|
19
|
+
throw new Error(`invalid full segment method ${method}`);
|
20
|
+
}
|
21
|
+
}
|
package/src/utils/logger.ts
CHANGED
@@ -11,6 +11,25 @@ export interface ILogger {
|
|
11
11
|
error: ILogFunction;
|
12
12
|
}
|
13
13
|
|
14
|
+
export class Logger implements ILogger {
|
15
|
+
trace: ILogFunction;
|
16
|
+
debug: ILogFunction;
|
17
|
+
log: ILogFunction;
|
18
|
+
warn: ILogFunction;
|
19
|
+
info: ILogFunction;
|
20
|
+
error: ILogFunction;
|
21
|
+
|
22
|
+
constructor(label: string, logger: ILogger) {
|
23
|
+
const lb = `[${label}]:`;
|
24
|
+
this.trace = noop;
|
25
|
+
this.debug = logger.debug.bind(null, lb);
|
26
|
+
this.log = logger.log.bind(null, lb);
|
27
|
+
this.warn = logger.warn.bind(null, lb);
|
28
|
+
this.info = logger.info.bind(null, lb);
|
29
|
+
this.error = logger.error.bind(null, lb);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
14
33
|
const noop: ILogFunction = function () {};
|
15
34
|
|
16
35
|
const fakeLogger: ILogger = {
|
@@ -22,7 +41,9 @@ const fakeLogger: ILogger = {
|
|
22
41
|
error: noop,
|
23
42
|
};
|
24
43
|
|
25
|
-
|
44
|
+
function createLogger() {
|
45
|
+
return Object.assign({}, fakeLogger);
|
46
|
+
}
|
26
47
|
|
27
48
|
// let lastCallTime;
|
28
49
|
// function formatMsgWithTimeInfo(type, msg) {
|
@@ -33,33 +54,37 @@ let exportedLogger: ILogger = fakeLogger;
|
|
33
54
|
// return msg;
|
34
55
|
// }
|
35
56
|
|
36
|
-
function consolePrintFn(type: string): ILogFunction {
|
57
|
+
function consolePrintFn(type: string, id: string | undefined): ILogFunction {
|
37
58
|
const func: ILogFunction = self.console[type];
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
return noop;
|
59
|
+
return func
|
60
|
+
? func.bind(self.console, `${id ? '[' + id + '] ' : ''}[${type}] >`)
|
61
|
+
: noop;
|
42
62
|
}
|
43
63
|
|
44
|
-
function
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
});
|
64
|
+
function getLoggerFn(
|
65
|
+
key: string,
|
66
|
+
debugConfig: boolean | Partial<ILogger>,
|
67
|
+
id: string | undefined,
|
68
|
+
): ILogFunction {
|
69
|
+
return debugConfig[key]
|
70
|
+
? debugConfig[key].bind(debugConfig)
|
71
|
+
: consolePrintFn(key, id);
|
53
72
|
}
|
54
73
|
|
55
|
-
|
74
|
+
let exportedLogger: ILogger = createLogger();
|
75
|
+
|
76
|
+
export function enableLogs(
|
77
|
+
debugConfig: boolean | ILogger,
|
78
|
+
context: string,
|
79
|
+
id?: string | undefined,
|
80
|
+
): ILogger {
|
56
81
|
// check that console is available
|
82
|
+
const newLogger = createLogger();
|
57
83
|
if (
|
58
84
|
(typeof console === 'object' && debugConfig === true) ||
|
59
85
|
typeof debugConfig === 'object'
|
60
86
|
) {
|
61
|
-
|
62
|
-
debugConfig,
|
87
|
+
const keys: (keyof ILogger)[] = [
|
63
88
|
// Remove out from list here to hard-disable a log-level
|
64
89
|
// 'trace',
|
65
90
|
'debug',
|
@@ -67,19 +92,23 @@ export function enableLogs(debugConfig: boolean | ILogger, id: string): void {
|
|
67
92
|
'info',
|
68
93
|
'warn',
|
69
94
|
'error',
|
70
|
-
|
95
|
+
];
|
96
|
+
keys.forEach((key) => {
|
97
|
+
newLogger[key] = getLoggerFn(key, debugConfig, id);
|
98
|
+
});
|
71
99
|
// Some browsers don't allow to use bind on console object anyway
|
72
100
|
// fallback to default if needed
|
73
101
|
try {
|
74
|
-
|
75
|
-
`Debug logs enabled for "${
|
102
|
+
newLogger.log(
|
103
|
+
`Debug logs enabled for "${context}" in hls.js version ${__VERSION__}`,
|
76
104
|
);
|
77
105
|
} catch (e) {
|
78
|
-
|
106
|
+
/* log fn threw an exception. All logger methods are no-ops. */
|
107
|
+
return createLogger();
|
79
108
|
}
|
80
|
-
} else {
|
81
|
-
exportedLogger = fakeLogger;
|
82
109
|
}
|
110
|
+
exportedLogger = newLogger;
|
111
|
+
return newLogger;
|
83
112
|
}
|
84
113
|
|
85
114
|
export const logger: ILogger = exportedLogger;
|
package/src/utils/mp4-tools.ts
CHANGED
@@ -38,6 +38,13 @@ export function readUint32(buffer: Uint8Array, offset: number): number {
|
|
38
38
|
return val < 0 ? 4294967296 + val : val;
|
39
39
|
}
|
40
40
|
|
41
|
+
export function readUint64(buffer: Uint8Array, offset: number) {
|
42
|
+
let result = readUint32(buffer, offset);
|
43
|
+
result *= Math.pow(2, 32);
|
44
|
+
result += readUint32(buffer, offset + 4);
|
45
|
+
return result;
|
46
|
+
}
|
47
|
+
|
41
48
|
export function readSint32(buffer: Uint8Array, offset: number): number {
|
42
49
|
return (
|
43
50
|
(buffer[offset] << 24) |
|
@@ -125,15 +132,15 @@ export function parseSegmentIndex(sidx: Uint8Array): SidxInfo | null {
|
|
125
132
|
const timescale = readUint32(sidx, index);
|
126
133
|
index += 4;
|
127
134
|
|
128
|
-
|
129
|
-
|
130
|
-
const earliestPresentationTime = 0;
|
131
|
-
const firstOffset = 0;
|
135
|
+
let earliestPresentationTime = 0;
|
136
|
+
let firstOffset = 0;
|
132
137
|
|
133
138
|
if (version === 0) {
|
134
|
-
index +=
|
139
|
+
earliestPresentationTime = readUint32(sidx, (index += 4));
|
140
|
+
firstOffset = readUint32(sidx, (index += 4));
|
135
141
|
} else {
|
136
|
-
index +=
|
142
|
+
earliestPresentationTime = readUint64(sidx, (index += 8));
|
143
|
+
firstOffset = readUint64(sidx, (index += 8));
|
137
144
|
}
|
138
145
|
|
139
146
|
// skip reserved
|
@@ -677,19 +684,31 @@ export function getDuration(data: Uint8Array, initData: InitData) {
|
|
677
684
|
}
|
678
685
|
if (videoDuration === 0 && audioDuration === 0) {
|
679
686
|
// If duration samples are not available in the traf use sidx subsegment_duration
|
687
|
+
let sidxMinStart = Infinity;
|
688
|
+
let sidxMaxEnd = 0;
|
680
689
|
let sidxDuration = 0;
|
681
690
|
const sidxs = findBox(data, ['sidx']);
|
682
691
|
for (let i = 0; i < sidxs.length; i++) {
|
683
692
|
const sidx = parseSegmentIndex(sidxs[i]);
|
684
693
|
if (sidx?.references) {
|
685
|
-
|
694
|
+
sidxMinStart = Math.min(
|
695
|
+
sidxMinStart,
|
696
|
+
sidx.earliestPresentationTime / sidx.timescale,
|
697
|
+
);
|
698
|
+
const subSegmentDuration = sidx.references.reduce(
|
686
699
|
(dur, ref) => dur + ref.info.duration || 0,
|
687
700
|
0,
|
688
701
|
);
|
702
|
+
sidxMaxEnd = Math.max(
|
703
|
+
sidxMaxEnd,
|
704
|
+
subSegmentDuration + sidx.earliestPresentationTime / sidx.timescale,
|
705
|
+
);
|
706
|
+
sidxDuration = sidxMaxEnd - sidxMinStart;
|
689
707
|
}
|
690
708
|
}
|
691
|
-
|
692
|
-
|
709
|
+
if (sidxDuration && Number.isFinite(sidxDuration)) {
|
710
|
+
return sidxDuration;
|
711
|
+
}
|
693
712
|
}
|
694
713
|
if (videoDuration) {
|
695
714
|
return videoDuration;
|