hls.js 1.5.8-0.canary.10170 → 1.5.8
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 +3 -4
- package/dist/hls-demo.js +3 -12
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2366 -3626
- package/dist/hls.js.d.ts +84 -98
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1643 -2278
- 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 +1258 -1903
- 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 +1531 -2794
- 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 +30 -30
- package/src/config.ts +2 -3
- package/src/controller/abr-controller.ts +20 -24
- package/src/controller/audio-stream-controller.ts +74 -68
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +10 -27
- package/src/controller/base-stream-controller.ts +38 -160
- package/src/controller/buffer-controller.ts +92 -230
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -3
- package/src/controller/cmcd-controller.ts +9 -30
- package/src/controller/content-steering-controller.ts +6 -8
- package/src/controller/eme-controller.ts +23 -10
- package/src/controller/error-controller.ts +8 -6
- package/src/controller/fps-controller.ts +3 -8
- package/src/controller/fragment-tracker.ts +11 -15
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/id3-track-controller.ts +7 -7
- package/src/controller/latency-controller.ts +11 -9
- package/src/controller/level-controller.ts +19 -13
- package/src/controller/stream-controller.ts +32 -37
- package/src/controller/subtitle-stream-controller.ts +40 -28
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +31 -25
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +18 -32
- package/src/crypt/fast-aes-key.ts +5 -24
- package/src/demux/audio/aacdemuxer.ts +2 -2
- package/src/demux/audio/ac3-demuxer.ts +3 -4
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/audio/base-audio-demuxer.ts +14 -16
- package/src/demux/audio/mp3demuxer.ts +3 -4
- package/src/demux/audio/mpegaudio.ts +1 -1
- package/src/demux/id3.ts +411 -0
- package/src/demux/mp4demuxer.ts +7 -7
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +12 -4
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +37 -71
- package/src/demux/video/avc-video-parser.ts +119 -208
- package/src/demux/video/base-video-parser.ts +2 -134
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/events.ts +1 -8
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +37 -49
- package/src/loader/fragment-loader.ts +3 -10
- package/src/loader/key-loader.ts +1 -3
- package/src/loader/level-key.ts +9 -10
- package/src/loader/playlist-loader.ts +5 -4
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +8 -24
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +1 -3
- package/src/types/demuxer.ts +0 -3
- package/src/types/events.ts +0 -4
- package/src/types/remuxer.ts +1 -1
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/codecs.ts +5 -34
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +6 -1
- package/src/utils/logger.ts +23 -58
- package/src/utils/mp4-tools.ts +3 -5
- package/src/utils/webvtt-parser.ts +1 -1
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -749
- package/src/utils/encryption-methods-util.ts +0 -21
- package/src/utils/utf8-utils.ts +0 -18
package/src/crypt/decrypter.ts
CHANGED
@@ -4,7 +4,6 @@ import AESDecryptor, { removePadding } from './aes-decryptor';
|
|
4
4
|
import { logger } from '../utils/logger';
|
5
5
|
import { appendUint8Array } from '../utils/mp4-tools';
|
6
6
|
import { sliceUint8 } from '../utils/typed-array';
|
7
|
-
import { DecrypterAesMode } from './decrypter-aes-mode';
|
8
7
|
import type { HlsConfig } from '../config';
|
9
8
|
|
10
9
|
const CHUNK_SIZE = 16; // 16 bytes, 128 bits
|
@@ -20,10 +19,9 @@ export default class Decrypter {
|
|
20
19
|
private currentIV: ArrayBuffer | null = null;
|
21
20
|
private currentResult: ArrayBuffer | null = null;
|
22
21
|
private useSoftware: boolean;
|
23
|
-
private enableSoftwareAES: boolean;
|
24
22
|
|
25
23
|
constructor(config: HlsConfig, { removePKCS7Padding = true } = {}) {
|
26
|
-
this.
|
24
|
+
this.useSoftware = config.enableSoftwareAES;
|
27
25
|
this.removePKCS7Padding = removePKCS7Padding;
|
28
26
|
// built in decryptor expects PKCS7 padding
|
29
27
|
if (removePKCS7Padding) {
|
@@ -38,7 +36,9 @@ export default class Decrypter {
|
|
38
36
|
/* no-op */
|
39
37
|
}
|
40
38
|
}
|
41
|
-
|
39
|
+
if (this.subtle === null) {
|
40
|
+
this.useSoftware = true;
|
41
|
+
}
|
42
42
|
}
|
43
43
|
|
44
44
|
destroy() {
|
@@ -82,11 +82,10 @@ export default class Decrypter {
|
|
82
82
|
data: Uint8Array | ArrayBuffer,
|
83
83
|
key: ArrayBuffer,
|
84
84
|
iv: ArrayBuffer,
|
85
|
-
aesMode: DecrypterAesMode,
|
86
85
|
): Promise<ArrayBuffer> {
|
87
86
|
if (this.useSoftware) {
|
88
87
|
return new Promise((resolve, reject) => {
|
89
|
-
this.softwareDecrypt(new Uint8Array(data), key, iv
|
88
|
+
this.softwareDecrypt(new Uint8Array(data), key, iv);
|
90
89
|
const decryptResult = this.flush();
|
91
90
|
if (decryptResult) {
|
92
91
|
resolve(decryptResult.buffer);
|
@@ -95,7 +94,7 @@ export default class Decrypter {
|
|
95
94
|
}
|
96
95
|
});
|
97
96
|
}
|
98
|
-
return this.webCryptoDecrypt(new Uint8Array(data), key, iv
|
97
|
+
return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
|
99
98
|
}
|
100
99
|
|
101
100
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
@@ -104,13 +103,8 @@ export default class Decrypter {
|
|
104
103
|
data: Uint8Array,
|
105
104
|
key: ArrayBuffer,
|
106
105
|
iv: ArrayBuffer,
|
107
|
-
aesMode: DecrypterAesMode,
|
108
106
|
): ArrayBuffer | null {
|
109
107
|
const { currentIV, currentResult, remainderData } = this;
|
110
|
-
if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) {
|
111
|
-
logger.warn('SoftwareDecrypt: can only handle AES-128-CBC');
|
112
|
-
return null;
|
113
|
-
}
|
114
108
|
this.logOnce('JS AES decrypt');
|
115
109
|
// The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
|
116
110
|
// This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
|
@@ -153,22 +147,21 @@ export default class Decrypter {
|
|
153
147
|
data: Uint8Array,
|
154
148
|
key: ArrayBuffer,
|
155
149
|
iv: ArrayBuffer,
|
156
|
-
aesMode: DecrypterAesMode,
|
157
150
|
): Promise<ArrayBuffer> {
|
158
151
|
const subtle = this.subtle;
|
159
152
|
if (this.key !== key || !this.fastAesKey) {
|
160
153
|
this.key = key;
|
161
|
-
this.fastAesKey = new FastAESKey(subtle, key
|
154
|
+
this.fastAesKey = new FastAESKey(subtle, key);
|
162
155
|
}
|
163
156
|
return this.fastAesKey
|
164
157
|
.expandKey()
|
165
|
-
.then((aesKey
|
158
|
+
.then((aesKey) => {
|
166
159
|
// decrypt using web crypto
|
167
160
|
if (!subtle) {
|
168
161
|
return Promise.reject(new Error('web crypto not initialized'));
|
169
162
|
}
|
170
163
|
this.logOnce('WebCrypto AES decrypt');
|
171
|
-
const crypto = new AESCrypto(subtle, new Uint8Array(iv)
|
164
|
+
const crypto = new AESCrypto(subtle, new Uint8Array(iv));
|
172
165
|
return crypto.decrypt(data.buffer, aesKey);
|
173
166
|
})
|
174
167
|
.catch((err) => {
|
@@ -176,26 +169,19 @@ export default class Decrypter {
|
|
176
169
|
`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`,
|
177
170
|
);
|
178
171
|
|
179
|
-
return this.onWebCryptoError(data, key, iv
|
172
|
+
return this.onWebCryptoError(data, key, iv);
|
180
173
|
});
|
181
174
|
}
|
182
175
|
|
183
|
-
private onWebCryptoError(data, key, iv
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
if (decryptResult) {
|
191
|
-
return decryptResult.buffer;
|
192
|
-
}
|
176
|
+
private onWebCryptoError(data, key, iv): ArrayBuffer | never {
|
177
|
+
this.useSoftware = true;
|
178
|
+
this.logEnabled = true;
|
179
|
+
this.softwareDecrypt(data, key, iv);
|
180
|
+
const decryptResult = this.flush();
|
181
|
+
if (decryptResult) {
|
182
|
+
return decryptResult.buffer;
|
193
183
|
}
|
194
|
-
throw new Error(
|
195
|
-
'WebCrypto' +
|
196
|
-
(enableSoftwareAES ? ' and softwareDecrypt' : '') +
|
197
|
-
': failed to decrypt data',
|
198
|
-
);
|
184
|
+
throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data');
|
199
185
|
}
|
200
186
|
|
201
187
|
private getValidChunk(data: Uint8Array): Uint8Array {
|
@@ -1,35 +1,16 @@
|
|
1
|
-
import { DecrypterAesMode } from './decrypter-aes-mode';
|
2
|
-
|
3
1
|
export default class FastAESKey {
|
4
2
|
private subtle: any;
|
5
3
|
private key: ArrayBuffer;
|
6
|
-
private aesMode: DecrypterAesMode;
|
7
4
|
|
8
|
-
constructor(subtle, key
|
5
|
+
constructor(subtle, key) {
|
9
6
|
this.subtle = subtle;
|
10
7
|
this.key = key;
|
11
|
-
this.aesMode = aesMode;
|
12
8
|
}
|
13
9
|
|
14
10
|
expandKey() {
|
15
|
-
|
16
|
-
|
17
|
-
'
|
18
|
-
|
19
|
-
{ name: subtleAlgoName },
|
20
|
-
false,
|
21
|
-
['encrypt', 'decrypt'],
|
22
|
-
);
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
26
|
-
function getSubtleAlgoName(aesMode: DecrypterAesMode) {
|
27
|
-
switch (aesMode) {
|
28
|
-
case DecrypterAesMode.cbc:
|
29
|
-
return 'AES-CBC';
|
30
|
-
case DecrypterAesMode.ctr:
|
31
|
-
return 'AES-CTR';
|
32
|
-
default:
|
33
|
-
throw new Error(`[FastAESKey] invalid aes mode ${aesMode}`);
|
11
|
+
return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, [
|
12
|
+
'encrypt',
|
13
|
+
'decrypt',
|
14
|
+
]);
|
34
15
|
}
|
35
16
|
}
|
@@ -5,7 +5,7 @@ import BaseAudioDemuxer from './base-audio-demuxer';
|
|
5
5
|
import * as ADTS from './adts';
|
6
6
|
import * as MpegAudio from './mpegaudio';
|
7
7
|
import { logger } from '../../utils/logger';
|
8
|
-
import
|
8
|
+
import * as ID3 from '../id3';
|
9
9
|
import type { HlsEventEmitter } from '../../events';
|
10
10
|
import type { HlsConfig } from '../../config';
|
11
11
|
|
@@ -51,7 +51,7 @@ class AACDemuxer extends BaseAudioDemuxer {
|
|
51
51
|
// Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
|
52
52
|
// Layer bits (position 14 and 15) in header should be always 0 for ADTS
|
53
53
|
// More info https://wiki.multimedia.cx/index.php?title=ADTS
|
54
|
-
const id3Data =
|
54
|
+
const id3Data = ID3.getID3Data(data, 0);
|
55
55
|
let offset = id3Data?.length || 0;
|
56
56
|
|
57
57
|
if (MpegAudio.probe(data, offset)) {
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import BaseAudioDemuxer from './base-audio-demuxer';
|
2
|
-
import {
|
3
|
-
import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
|
2
|
+
import { getID3Data, getTimeStamp } from '../id3';
|
4
3
|
import { getAudioBSID } from './dolby';
|
5
4
|
import type { HlsEventEmitter } from '../../events';
|
6
5
|
import type { AudioFrame, DemuxedAudioTrack } from '../../types/demuxer';
|
@@ -62,7 +61,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
|
|
62
61
|
return false;
|
63
62
|
}
|
64
63
|
|
65
|
-
const id3Data =
|
64
|
+
const id3Data = getID3Data(data, 0);
|
66
65
|
if (!id3Data) {
|
67
66
|
return false;
|
68
67
|
}
|
@@ -72,7 +71,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
|
|
72
71
|
if (
|
73
72
|
data[offset] === 0x0b &&
|
74
73
|
data[offset + 1] === 0x77 &&
|
75
|
-
|
74
|
+
getTimeStamp(id3Data) !== undefined &&
|
76
75
|
// check the bsid to confirm ac-3
|
77
76
|
getAudioBSID(data, offset) < 16
|
78
77
|
) {
|
package/src/demux/audio/adts.ts
CHANGED
@@ -17,7 +17,6 @@ type AudioConfig = {
|
|
17
17
|
samplerate: number;
|
18
18
|
channelCount: number;
|
19
19
|
codec: string;
|
20
|
-
parsedCodec: string;
|
21
20
|
manifestCodec: string;
|
22
21
|
};
|
23
22
|
|
@@ -33,7 +32,6 @@ export function getAudioConfig(
|
|
33
32
|
audioCodec: string,
|
34
33
|
): AudioConfig | void {
|
35
34
|
let adtsObjectType: number;
|
36
|
-
let originalAdtsObjectType: number;
|
37
35
|
let adtsExtensionSamplingIndex: number;
|
38
36
|
let adtsChannelConfig: number;
|
39
37
|
let config: number[];
|
@@ -44,8 +42,7 @@ export function getAudioConfig(
|
|
44
42
|
8000, 7350,
|
45
43
|
];
|
46
44
|
// byte 2
|
47
|
-
adtsObjectType =
|
48
|
-
((data[offset + 2] & 0xc0) >>> 6) + 1;
|
45
|
+
adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1;
|
49
46
|
const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
|
50
47
|
if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
|
51
48
|
const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`);
|
@@ -64,8 +61,8 @@ export function getAudioConfig(
|
|
64
61
|
logger.log(
|
65
62
|
`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`,
|
66
63
|
);
|
67
|
-
//
|
68
|
-
if (/firefox
|
64
|
+
// firefox: freq less than 24kHz = AAC SBR (HE-AAC)
|
65
|
+
if (/firefox/i.test(userAgent)) {
|
69
66
|
if (adtsSamplingIndex >= 6) {
|
70
67
|
adtsObjectType = 5;
|
71
68
|
config = new Array(4);
|
@@ -170,7 +167,6 @@ export function getAudioConfig(
|
|
170
167
|
samplerate: adtsSamplingRates[adtsSamplingIndex],
|
171
168
|
channelCount: adtsChannelConfig,
|
172
169
|
codec: 'mp4a.40.' + adtsObjectType,
|
173
|
-
parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
|
174
170
|
manifestCodec,
|
175
171
|
};
|
176
172
|
}
|
@@ -248,9 +244,8 @@ export function initTrackConfig(
|
|
248
244
|
track.channelCount = config.channelCount;
|
249
245
|
track.codec = config.codec;
|
250
246
|
track.manifestCodec = config.manifestCodec;
|
251
|
-
track.parsedCodec = config.parsedCodec;
|
252
247
|
logger.log(
|
253
|
-
`parsed codec:${track.
|
248
|
+
`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
|
254
249
|
);
|
255
250
|
}
|
256
251
|
}
|
@@ -1,21 +1,19 @@
|
|
1
|
+
import * as ID3 from '../id3';
|
1
2
|
import {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
DemuxerResult,
|
4
|
+
Demuxer,
|
5
|
+
DemuxedAudioTrack,
|
6
|
+
AudioFrame,
|
7
|
+
DemuxedMetadataTrack,
|
8
|
+
DemuxedVideoTrackBase,
|
9
|
+
DemuxedUserdataTrack,
|
10
|
+
KeyData,
|
10
11
|
MetadataSchema,
|
11
12
|
} from '../../types/demuxer';
|
12
13
|
import { dummyTrack } from '../dummy-demuxed-track';
|
13
14
|
import { appendUint8Array } from '../../utils/mp4-tools';
|
14
15
|
import { sliceUint8 } from '../../utils/typed-array';
|
15
16
|
import { RationalTimestamp } from '../../utils/timescale-conversion';
|
16
|
-
import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
|
17
|
-
import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
|
18
|
-
import { canParseId3 } from '@svta/common-media-library/id3/canParseId3';
|
19
17
|
|
20
18
|
class BaseAudioDemuxer implements Demuxer {
|
21
19
|
protected _audioTrack!: DemuxedAudioTrack;
|
@@ -71,12 +69,12 @@ class BaseAudioDemuxer implements Demuxer {
|
|
71
69
|
this.cachedData = null;
|
72
70
|
}
|
73
71
|
|
74
|
-
let id3Data: Uint8Array | undefined =
|
72
|
+
let id3Data: Uint8Array | undefined = ID3.getID3Data(data, 0);
|
75
73
|
let offset = id3Data ? id3Data.length : 0;
|
76
74
|
let lastDataIndex;
|
77
75
|
const track = this._audioTrack;
|
78
76
|
const id3Track = this._id3Track;
|
79
|
-
const timestamp = id3Data ?
|
77
|
+
const timestamp = id3Data ? ID3.getTimeStamp(id3Data) : undefined;
|
80
78
|
const length = data.length;
|
81
79
|
|
82
80
|
if (
|
@@ -113,9 +111,9 @@ class BaseAudioDemuxer implements Demuxer {
|
|
113
111
|
} else {
|
114
112
|
offset = length;
|
115
113
|
}
|
116
|
-
} else if (
|
117
|
-
// after a canParse, a call to
|
118
|
-
id3Data =
|
114
|
+
} else if (ID3.canParse(data, offset)) {
|
115
|
+
// after a ID3.canParse, a call to ID3.getID3Data *should* always returns some data
|
116
|
+
id3Data = ID3.getID3Data(data, offset)!;
|
119
117
|
id3Track.samples.push({
|
120
118
|
pts: this.lastPTS,
|
121
119
|
dts: this.lastPTS,
|
@@ -2,11 +2,10 @@
|
|
2
2
|
* MP3 demuxer
|
3
3
|
*/
|
4
4
|
import BaseAudioDemuxer from './base-audio-demuxer';
|
5
|
+
import { getID3Data, getTimeStamp } from '../id3';
|
5
6
|
import { getAudioBSID } from './dolby';
|
6
7
|
import { logger } from '../../utils/logger';
|
7
8
|
import * as MpegAudio from './mpegaudio';
|
8
|
-
import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
|
9
|
-
import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
|
10
9
|
|
11
10
|
class MP3Demuxer extends BaseAudioDemuxer {
|
12
11
|
resetInitSegment(
|
@@ -40,7 +39,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
|
40
39
|
// Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
|
41
40
|
// Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
|
42
41
|
// More info http://www.mp3-tech.org/programmer/frame_header.html
|
43
|
-
const id3Data =
|
42
|
+
const id3Data = getID3Data(data, 0);
|
44
43
|
let offset = id3Data?.length || 0;
|
45
44
|
|
46
45
|
// Check for ac-3|ec-3 sync bytes and return false if present
|
@@ -48,7 +47,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
|
48
47
|
id3Data &&
|
49
48
|
data[offset] === 0x0b &&
|
50
49
|
data[offset + 1] === 0x77 &&
|
51
|
-
|
50
|
+
getTimeStamp(id3Data) !== undefined &&
|
52
51
|
// check the bsid to confirm ac-3 or ec-3 (not mp3)
|
53
52
|
getAudioBSID(data, offset) <= 16
|
54
53
|
) {
|