hls.js 1.5.9 → 1.5.10-0.canary.10321
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 +3479 -2197
- package/dist/hls.js.d.ts +108 -85
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2407 -1761
- 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 +1994 -1321
- 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 +2868 -1563
- 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 +35 -35
- package/src/config.ts +3 -2
- package/src/controller/abr-controller.ts +24 -20
- package/src/controller/audio-stream-controller.ts +68 -74
- 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 +160 -38
- package/src/controller/buffer-controller.ts +230 -92
- 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 +6 -8
- package/src/controller/fps-controller.ts +8 -3
- package/src/controller/fragment-tracker.ts +15 -11
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/id3-track-controller.ts +7 -7
- package/src/controller/latency-controller.ts +9 -11
- package/src/controller/level-controller.ts +37 -19
- package/src/controller/stream-controller.ts +37 -32
- package/src/controller/subtitle-stream-controller.ts +28 -40
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +19 -21
- 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 +4 -12
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +71 -37
- package/src/demux/video/avc-video-parser.ts +208 -119
- package/src/demux/video/base-video-parser.ts +147 -18
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +749 -0
- package/src/empty-es.js +5 -0
- package/src/events.ts +8 -1
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +61 -38
- package/src/loader/fragment-loader.ts +10 -3
- package/src/loader/key-loader.ts +3 -1
- package/src/loader/level-key.ts +10 -9
- package/src/loader/playlist-loader.ts +4 -5
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +24 -8
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +3 -1
- package/src/types/demuxer.ts +4 -0
- package/src/types/events.ts +4 -0
- package/src/types/remuxer.ts +1 -1
- 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/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +1 -6
- package/src/utils/logger.ts +58 -23
- package/src/utils/mp4-tools.ts +5 -3
- package/src/utils/utf8-utils.ts +18 -0
- package/src/utils/webvtt-parser.ts +1 -1
- package/src/utils/xhr-loader.ts +5 -5
- package/src/demux/id3.ts +0 -411
package/src/crypt/aes-crypto.ts
CHANGED
@@ -1,13 +1,32 @@
|
|
1
|
+
import { DecrypterAesMode } from './decrypter-aes-mode';
|
2
|
+
|
1
3
|
export default class AESCrypto {
|
2
4
|
private subtle: SubtleCrypto;
|
3
5
|
private aesIV: Uint8Array;
|
6
|
+
private aesMode: DecrypterAesMode;
|
4
7
|
|
5
|
-
constructor(subtle: SubtleCrypto, iv: Uint8Array) {
|
8
|
+
constructor(subtle: SubtleCrypto, iv: Uint8Array, aesMode: DecrypterAesMode) {
|
6
9
|
this.subtle = subtle;
|
7
10
|
this.aesIV = iv;
|
11
|
+
this.aesMode = aesMode;
|
8
12
|
}
|
9
13
|
|
10
14
|
decrypt(data: ArrayBuffer, key: CryptoKey) {
|
11
|
-
|
15
|
+
switch (this.aesMode) {
|
16
|
+
case DecrypterAesMode.cbc:
|
17
|
+
return this.subtle.decrypt(
|
18
|
+
{ name: 'AES-CBC', iv: this.aesIV },
|
19
|
+
key,
|
20
|
+
data,
|
21
|
+
);
|
22
|
+
case DecrypterAesMode.ctr:
|
23
|
+
return this.subtle.decrypt(
|
24
|
+
{ name: 'AES-CTR', counter: this.aesIV, length: 64 }, //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block
|
25
|
+
key,
|
26
|
+
data,
|
27
|
+
);
|
28
|
+
default:
|
29
|
+
throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`);
|
30
|
+
}
|
12
31
|
}
|
13
32
|
}
|
package/src/crypt/decrypter.ts
CHANGED
@@ -4,6 +4,7 @@ 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';
|
7
8
|
import type { HlsConfig } from '../config';
|
8
9
|
|
9
10
|
const CHUNK_SIZE = 16; // 16 bytes, 128 bits
|
@@ -19,9 +20,10 @@ export default class Decrypter {
|
|
19
20
|
private currentIV: ArrayBuffer | null = null;
|
20
21
|
private currentResult: ArrayBuffer | null = null;
|
21
22
|
private useSoftware: boolean;
|
23
|
+
private enableSoftwareAES: boolean;
|
22
24
|
|
23
25
|
constructor(config: HlsConfig, { removePKCS7Padding = true } = {}) {
|
24
|
-
this.
|
26
|
+
this.enableSoftwareAES = config.enableSoftwareAES;
|
25
27
|
this.removePKCS7Padding = removePKCS7Padding;
|
26
28
|
// built in decryptor expects PKCS7 padding
|
27
29
|
if (removePKCS7Padding) {
|
@@ -36,7 +38,6 @@ export default class Decrypter {
|
|
36
38
|
/* no-op */
|
37
39
|
}
|
38
40
|
}
|
39
|
-
|
40
41
|
this.useSoftware = !this.subtle;
|
41
42
|
}
|
42
43
|
|
@@ -81,10 +82,11 @@ export default class Decrypter {
|
|
81
82
|
data: Uint8Array | ArrayBuffer,
|
82
83
|
key: ArrayBuffer,
|
83
84
|
iv: ArrayBuffer,
|
85
|
+
aesMode: DecrypterAesMode,
|
84
86
|
): Promise<ArrayBuffer> {
|
85
87
|
if (this.useSoftware) {
|
86
88
|
return new Promise((resolve, reject) => {
|
87
|
-
this.softwareDecrypt(new Uint8Array(data), key, iv);
|
89
|
+
this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode);
|
88
90
|
const decryptResult = this.flush();
|
89
91
|
if (decryptResult) {
|
90
92
|
resolve(decryptResult.buffer);
|
@@ -93,7 +95,7 @@ export default class Decrypter {
|
|
93
95
|
}
|
94
96
|
});
|
95
97
|
}
|
96
|
-
return this.webCryptoDecrypt(new Uint8Array(data), key, iv);
|
98
|
+
return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode);
|
97
99
|
}
|
98
100
|
|
99
101
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
@@ -102,8 +104,13 @@ export default class Decrypter {
|
|
102
104
|
data: Uint8Array,
|
103
105
|
key: ArrayBuffer,
|
104
106
|
iv: ArrayBuffer,
|
107
|
+
aesMode: DecrypterAesMode,
|
105
108
|
): ArrayBuffer | null {
|
106
109
|
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
|
+
}
|
107
114
|
this.logOnce('JS AES decrypt');
|
108
115
|
// The output is staggered during progressive parsing - the current result is cached, and emitted on the next call
|
109
116
|
// This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached
|
@@ -146,23 +153,24 @@ export default class Decrypter {
|
|
146
153
|
data: Uint8Array,
|
147
154
|
key: ArrayBuffer,
|
148
155
|
iv: ArrayBuffer,
|
156
|
+
aesMode: DecrypterAesMode,
|
149
157
|
): Promise<ArrayBuffer> {
|
150
158
|
if (this.key !== key || !this.fastAesKey) {
|
151
159
|
if (!this.subtle) {
|
152
|
-
return Promise.resolve(this.onWebCryptoError(data, key, iv));
|
160
|
+
return Promise.resolve(this.onWebCryptoError(data, key, iv, aesMode));
|
153
161
|
}
|
154
162
|
this.key = key;
|
155
|
-
this.fastAesKey = new FastAESKey(this.subtle, key);
|
163
|
+
this.fastAesKey = new FastAESKey(this.subtle, key, aesMode);
|
156
164
|
}
|
157
165
|
return this.fastAesKey
|
158
166
|
.expandKey()
|
159
|
-
.then((aesKey) => {
|
167
|
+
.then((aesKey: CryptoKey) => {
|
160
168
|
// decrypt using web crypto
|
161
169
|
if (!this.subtle) {
|
162
170
|
return Promise.reject(new Error('web crypto not initialized'));
|
163
171
|
}
|
164
172
|
this.logOnce('WebCrypto AES decrypt');
|
165
|
-
const crypto = new AESCrypto(this.subtle, new Uint8Array(iv));
|
173
|
+
const crypto = new AESCrypto(this.subtle, new Uint8Array(iv), aesMode);
|
166
174
|
return crypto.decrypt(data.buffer, aesKey);
|
167
175
|
})
|
168
176
|
.catch((err) => {
|
@@ -170,7 +178,7 @@ export default class Decrypter {
|
|
170
178
|
`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`,
|
171
179
|
);
|
172
180
|
|
173
|
-
return this.onWebCryptoError(data, key, iv);
|
181
|
+
return this.onWebCryptoError(data, key, iv, aesMode);
|
174
182
|
});
|
175
183
|
}
|
176
184
|
|
@@ -178,15 +186,23 @@ export default class Decrypter {
|
|
178
186
|
data: Uint8Array,
|
179
187
|
key: ArrayBuffer,
|
180
188
|
iv: ArrayBuffer,
|
189
|
+
aesMode: DecrypterAesMode,
|
181
190
|
): ArrayBuffer | never {
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
191
|
+
const enableSoftwareAES = this.enableSoftwareAES;
|
192
|
+
if (enableSoftwareAES) {
|
193
|
+
this.useSoftware = true;
|
194
|
+
this.logEnabled = true;
|
195
|
+
this.softwareDecrypt(data, key, iv, aesMode);
|
196
|
+
const decryptResult = this.flush();
|
197
|
+
if (decryptResult) {
|
198
|
+
return decryptResult.buffer;
|
199
|
+
}
|
188
200
|
}
|
189
|
-
throw new Error(
|
201
|
+
throw new Error(
|
202
|
+
'WebCrypto' +
|
203
|
+
(enableSoftwareAES ? ' and softwareDecrypt' : '') +
|
204
|
+
': failed to decrypt data',
|
205
|
+
);
|
190
206
|
}
|
191
207
|
|
192
208
|
private getValidChunk(data: Uint8Array): Uint8Array {
|
@@ -1,16 +1,39 @@
|
|
1
|
+
import { DecrypterAesMode } from './decrypter-aes-mode';
|
2
|
+
|
1
3
|
export default class FastAESKey {
|
2
4
|
private subtle: SubtleCrypto;
|
3
5
|
private key: ArrayBuffer;
|
6
|
+
private aesMode: DecrypterAesMode;
|
4
7
|
|
5
|
-
constructor(
|
8
|
+
constructor(
|
9
|
+
subtle: SubtleCrypto,
|
10
|
+
key: ArrayBuffer,
|
11
|
+
aesMode: DecrypterAesMode,
|
12
|
+
) {
|
6
13
|
this.subtle = subtle;
|
7
14
|
this.key = key;
|
15
|
+
this.aesMode = aesMode;
|
8
16
|
}
|
9
17
|
|
10
18
|
expandKey() {
|
11
|
-
|
12
|
-
|
13
|
-
'
|
14
|
-
|
19
|
+
const subtleAlgoName = getSubtleAlgoName(this.aesMode);
|
20
|
+
return this.subtle.importKey(
|
21
|
+
'raw',
|
22
|
+
this.key,
|
23
|
+
{ name: subtleAlgoName },
|
24
|
+
false,
|
25
|
+
['encrypt', 'decrypt'],
|
26
|
+
);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
function getSubtleAlgoName(aesMode: DecrypterAesMode) {
|
31
|
+
switch (aesMode) {
|
32
|
+
case DecrypterAesMode.cbc:
|
33
|
+
return 'AES-CBC';
|
34
|
+
case DecrypterAesMode.ctr:
|
35
|
+
return 'AES-CTR';
|
36
|
+
default:
|
37
|
+
throw new Error(`[FastAESKey] invalid aes mode ${aesMode}`);
|
15
38
|
}
|
16
39
|
}
|
@@ -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 { getId3Data } from '@svta/common-media-library/id3/getId3Data';
|
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 = getId3Data(data, 0);
|
55
55
|
let offset = id3Data?.length || 0;
|
56
56
|
|
57
57
|
if (MpegAudio.probe(data, offset)) {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import BaseAudioDemuxer from './base-audio-demuxer';
|
2
|
-
import {
|
2
|
+
import { getId3Data } from '@svta/common-media-library/id3/getId3Data';
|
3
|
+
import { getId3Timestamp } from '@svta/common-media-library/id3/getId3Timestamp';
|
3
4
|
import { getAudioBSID } from './dolby';
|
4
5
|
import type { HlsEventEmitter } from '../../events';
|
5
6
|
import type { AudioFrame, DemuxedAudioTrack } from '../../types/demuxer';
|
@@ -61,7 +62,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
|
|
61
62
|
return false;
|
62
63
|
}
|
63
64
|
|
64
|
-
const id3Data =
|
65
|
+
const id3Data = getId3Data(data, 0);
|
65
66
|
if (!id3Data) {
|
66
67
|
return false;
|
67
68
|
}
|
@@ -71,7 +72,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
|
|
71
72
|
if (
|
72
73
|
data[offset] === 0x0b &&
|
73
74
|
data[offset + 1] === 0x77 &&
|
74
|
-
|
75
|
+
getId3Timestamp(id3Data) !== undefined &&
|
75
76
|
// check the bsid to confirm ac-3
|
76
77
|
getAudioBSID(data, offset) < 16
|
77
78
|
) {
|
package/src/demux/audio/adts.ts
CHANGED
@@ -17,6 +17,7 @@ type AudioConfig = {
|
|
17
17
|
samplerate: number;
|
18
18
|
channelCount: number;
|
19
19
|
codec: string;
|
20
|
+
parsedCodec: string;
|
20
21
|
manifestCodec: string;
|
21
22
|
};
|
22
23
|
|
@@ -32,6 +33,7 @@ export function getAudioConfig(
|
|
32
33
|
audioCodec: string,
|
33
34
|
): AudioConfig | void {
|
34
35
|
let adtsObjectType: number;
|
36
|
+
let originalAdtsObjectType: number;
|
35
37
|
let adtsExtensionSamplingIndex: number;
|
36
38
|
let adtsChannelConfig: number;
|
37
39
|
let config: number[];
|
@@ -42,7 +44,8 @@ export function getAudioConfig(
|
|
42
44
|
8000, 7350,
|
43
45
|
];
|
44
46
|
// byte 2
|
45
|
-
adtsObjectType =
|
47
|
+
adtsObjectType = originalAdtsObjectType =
|
48
|
+
((data[offset + 2] & 0xc0) >>> 6) + 1;
|
46
49
|
const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2;
|
47
50
|
if (adtsSamplingIndex > adtsSamplingRates.length - 1) {
|
48
51
|
const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`);
|
@@ -61,8 +64,8 @@ export function getAudioConfig(
|
|
61
64
|
logger.log(
|
62
65
|
`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`,
|
63
66
|
);
|
64
|
-
//
|
65
|
-
if (/firefox/i.test(userAgent)) {
|
67
|
+
// Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC)
|
68
|
+
if (/firefox|palemoon/i.test(userAgent)) {
|
66
69
|
if (adtsSamplingIndex >= 6) {
|
67
70
|
adtsObjectType = 5;
|
68
71
|
config = new Array(4);
|
@@ -167,6 +170,7 @@ export function getAudioConfig(
|
|
167
170
|
samplerate: adtsSamplingRates[adtsSamplingIndex],
|
168
171
|
channelCount: adtsChannelConfig,
|
169
172
|
codec: 'mp4a.40.' + adtsObjectType,
|
173
|
+
parsedCodec: 'mp4a.40.' + originalAdtsObjectType,
|
170
174
|
manifestCodec,
|
171
175
|
};
|
172
176
|
}
|
@@ -244,8 +248,9 @@ export function initTrackConfig(
|
|
244
248
|
track.channelCount = config.channelCount;
|
245
249
|
track.codec = config.codec;
|
246
250
|
track.manifestCodec = config.manifestCodec;
|
251
|
+
track.parsedCodec = config.parsedCodec;
|
247
252
|
logger.log(
|
248
|
-
`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
|
253
|
+
`parsed codec:${track.parsedCodec}, codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
|
249
254
|
);
|
250
255
|
}
|
251
256
|
}
|
@@ -1,19 +1,21 @@
|
|
1
|
-
import * as ID3 from '../id3';
|
2
1
|
import {
|
3
|
-
DemuxerResult,
|
4
|
-
Demuxer,
|
5
|
-
DemuxedAudioTrack,
|
6
|
-
AudioFrame,
|
7
|
-
DemuxedMetadataTrack,
|
8
|
-
DemuxedVideoTrackBase,
|
9
|
-
DemuxedUserdataTrack,
|
10
|
-
KeyData,
|
2
|
+
type DemuxerResult,
|
3
|
+
type Demuxer,
|
4
|
+
type DemuxedAudioTrack,
|
5
|
+
type AudioFrame,
|
6
|
+
type DemuxedMetadataTrack,
|
7
|
+
type DemuxedVideoTrackBase,
|
8
|
+
type DemuxedUserdataTrack,
|
9
|
+
type KeyData,
|
11
10
|
MetadataSchema,
|
12
11
|
} from '../../types/demuxer';
|
13
12
|
import { dummyTrack } from '../dummy-demuxed-track';
|
14
13
|
import { appendUint8Array } from '../../utils/mp4-tools';
|
15
14
|
import { sliceUint8 } from '../../utils/typed-array';
|
16
15
|
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';
|
17
19
|
|
18
20
|
class BaseAudioDemuxer implements Demuxer {
|
19
21
|
protected _audioTrack!: DemuxedAudioTrack;
|
@@ -69,12 +71,12 @@ class BaseAudioDemuxer implements Demuxer {
|
|
69
71
|
this.cachedData = null;
|
70
72
|
}
|
71
73
|
|
72
|
-
let id3Data: Uint8Array | undefined =
|
74
|
+
let id3Data: Uint8Array | undefined = getId3Data(data, 0);
|
73
75
|
let offset = id3Data ? id3Data.length : 0;
|
74
76
|
let lastDataIndex;
|
75
77
|
const track = this._audioTrack;
|
76
78
|
const id3Track = this._id3Track;
|
77
|
-
const timestamp = id3Data ?
|
79
|
+
const timestamp = id3Data ? getId3Timestamp(id3Data) : undefined;
|
78
80
|
const length = data.length;
|
79
81
|
|
80
82
|
if (
|
@@ -111,9 +113,9 @@ class BaseAudioDemuxer implements Demuxer {
|
|
111
113
|
} else {
|
112
114
|
offset = length;
|
113
115
|
}
|
114
|
-
} else if (
|
115
|
-
// after a
|
116
|
-
id3Data =
|
116
|
+
} else if (canParseId3(data, offset)) {
|
117
|
+
// after a canParse, a call to getId3Data *should* always returns some data
|
118
|
+
id3Data = getId3Data(data, offset)!;
|
117
119
|
id3Track.samples.push({
|
118
120
|
pts: this.lastPTS,
|
119
121
|
dts: this.lastPTS,
|
@@ -2,10 +2,11 @@
|
|
2
2
|
* MP3 demuxer
|
3
3
|
*/
|
4
4
|
import BaseAudioDemuxer from './base-audio-demuxer';
|
5
|
-
import { getID3Data, getTimeStamp } from '../id3';
|
6
5
|
import { getAudioBSID } from './dolby';
|
7
6
|
import { logger } from '../../utils/logger';
|
8
7
|
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';
|
9
10
|
|
10
11
|
class MP3Demuxer extends BaseAudioDemuxer {
|
11
12
|
resetInitSegment(
|
@@ -39,7 +40,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
|
39
40
|
// Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
|
40
41
|
// Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
|
41
42
|
// More info http://www.mp3-tech.org/programmer/frame_header.html
|
42
|
-
const id3Data =
|
43
|
+
const id3Data = getId3Data(data, 0);
|
43
44
|
let offset = id3Data?.length || 0;
|
44
45
|
|
45
46
|
// Check for ac-3|ec-3 sync bytes and return false if present
|
@@ -47,7 +48,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
|
47
48
|
id3Data &&
|
48
49
|
data[offset] === 0x0b &&
|
49
50
|
data[offset + 1] === 0x77 &&
|
50
|
-
|
51
|
+
getId3Timestamp(id3Data) !== undefined &&
|
51
52
|
// check the bsid to confirm ac-3 or ec-3 (not mp3)
|
52
53
|
getAudioBSID(data, offset) <= 16
|
53
54
|
) {
|
package/src/demux/mp4demuxer.ts
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
* MP4 demuxer
|
3
3
|
*/
|
4
4
|
import {
|
5
|
-
Demuxer,
|
6
|
-
DemuxerResult,
|
7
|
-
PassthroughTrack,
|
8
|
-
DemuxedAudioTrack,
|
9
|
-
DemuxedUserdataTrack,
|
10
|
-
DemuxedMetadataTrack,
|
11
|
-
KeyData,
|
5
|
+
type Demuxer,
|
6
|
+
type DemuxerResult,
|
7
|
+
type PassthroughTrack,
|
8
|
+
type DemuxedAudioTrack,
|
9
|
+
type DemuxedUserdataTrack,
|
10
|
+
type DemuxedMetadataTrack,
|
11
|
+
type KeyData,
|
12
12
|
MetadataSchema,
|
13
13
|
} from '../types/demuxer';
|
14
14
|
import {
|
package/src/demux/sample-aes.ts
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
import { HlsConfig } from '../config';
|
6
6
|
import Decrypter from '../crypt/decrypter';
|
7
|
+
import { DecrypterAesMode } from '../crypt/decrypter-aes-mode';
|
7
8
|
import { HlsEventEmitter } from '../events';
|
8
9
|
import type {
|
9
10
|
AudioSample,
|
@@ -30,6 +31,7 @@ class SampleAesDecrypter {
|
|
30
31
|
encryptedData,
|
31
32
|
this.keyData.key.buffer,
|
32
33
|
this.keyData.iv.buffer,
|
34
|
+
DecrypterAesMode.cbc,
|
33
35
|
);
|
34
36
|
}
|
35
37
|
|
@@ -12,14 +12,13 @@ import Transmuxer, {
|
|
12
12
|
} from '../demux/transmuxer';
|
13
13
|
import { logger } from '../utils/logger';
|
14
14
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
15
|
-
import { getMediaSource } from '../utils/mediasource-helper';
|
16
15
|
import { EventEmitter } from 'eventemitter3';
|
17
16
|
import { Fragment, Part } from '../loader/fragment';
|
17
|
+
import { getM2TSSupportedAudioTypes } from '../utils/codecs';
|
18
18
|
import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer';
|
19
19
|
import type Hls from '../hls';
|
20
20
|
import type { HlsEventEmitter } from '../events';
|
21
21
|
import type { PlaylistLevelType } from '../types/loader';
|
22
|
-
import type { TypeSupported } from './tsdemuxer';
|
23
22
|
import type { RationalTimestamp } from '../utils/timescale-conversion';
|
24
23
|
|
25
24
|
export default class TransmuxerInterface {
|
@@ -64,16 +63,9 @@ export default class TransmuxerInterface {
|
|
64
63
|
this.observer.on(Events.FRAG_DECRYPTED, forwardMessage);
|
65
64
|
this.observer.on(Events.ERROR, forwardMessage);
|
66
65
|
|
67
|
-
const
|
68
|
-
|
69
|
-
|
70
|
-
const m2tsTypeSupported: TypeSupported = {
|
71
|
-
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
|
72
|
-
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
|
73
|
-
ac3: __USE_M2TS_ADVANCED_CODECS__
|
74
|
-
? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
|
75
|
-
: false,
|
76
|
-
};
|
66
|
+
const m2tsTypeSupported = getM2TSSupportedAudioTypes(
|
67
|
+
config.preferManagedMediaSource,
|
68
|
+
);
|
77
69
|
|
78
70
|
// navigator.vendor is not always available in Web Worker
|
79
71
|
// refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Transmuxer, { isPromise } from '../demux/transmuxer';
|
2
2
|
import { Events } from '../events';
|
3
|
-
import {
|
3
|
+
import { enableLogs, type ILogFunction, type ILogger } from '../utils/logger';
|
4
4
|
import { EventEmitter } from 'eventemitter3';
|
5
5
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
6
6
|
import type { RemuxedTrack, RemuxerResult } from '../types/remuxer';
|
@@ -21,7 +21,7 @@ function startWorker(self) {
|
|
21
21
|
observer.on(Events.ERROR, forwardMessage);
|
22
22
|
|
23
23
|
// forward logger events to main thread
|
24
|
-
const forwardWorkerLogs = () => {
|
24
|
+
const forwardWorkerLogs = (logger: ILogger) => {
|
25
25
|
for (const logFn in logger) {
|
26
26
|
const func: ILogFunction = (message?) => {
|
27
27
|
forwardMessage('workerLog', {
|
@@ -46,8 +46,8 @@ function startWorker(self) {
|
|
46
46
|
data.vendor,
|
47
47
|
data.id,
|
48
48
|
);
|
49
|
-
enableLogs(config.debug, data.id);
|
50
|
-
forwardWorkerLogs();
|
49
|
+
const logger = enableLogs(config.debug, data.id);
|
50
|
+
forwardWorkerLogs(logger);
|
51
51
|
forwardMessage('init', null);
|
52
52
|
break;
|
53
53
|
}
|
package/src/demux/transmuxer.ts
CHANGED
@@ -4,18 +4,23 @@ import { ErrorTypes, ErrorDetails } from '../errors';
|
|
4
4
|
import Decrypter from '../crypt/decrypter';
|
5
5
|
import AACDemuxer from './audio/aacdemuxer';
|
6
6
|
import MP4Demuxer from '../demux/mp4demuxer';
|
7
|
-
import TSDemuxer
|
7
|
+
import TSDemuxer from '../demux/tsdemuxer';
|
8
8
|
import MP3Demuxer from './audio/mp3demuxer';
|
9
9
|
import { AC3Demuxer } from './audio/ac3-demuxer';
|
10
10
|
import MP4Remuxer from '../remux/mp4-remuxer';
|
11
11
|
import PassThroughRemuxer from '../remux/passthrough-remuxer';
|
12
12
|
import { logger } from '../utils/logger';
|
13
|
+
import {
|
14
|
+
isFullSegmentEncryption,
|
15
|
+
getAesModeFromFullSegmentMethod,
|
16
|
+
} from '../utils/encryption-methods-util';
|
13
17
|
import type { Demuxer, DemuxerResult, KeyData } from '../types/demuxer';
|
14
18
|
import type { Remuxer } from '../types/remuxer';
|
15
19
|
import type { TransmuxerResult, ChunkMetadata } from '../types/transmuxer';
|
16
20
|
import type { HlsConfig } from '../config';
|
17
21
|
import type { DecryptData } from '../loader/level-key';
|
18
22
|
import type { PlaylistLevelType } from '../types/loader';
|
23
|
+
import type { TypeSupported } from '../utils/codecs';
|
19
24
|
import type { RationalTimestamp } from '../utils/timescale-conversion';
|
20
25
|
import { optionalSelf } from '../utils/global';
|
21
26
|
|
@@ -114,8 +119,10 @@ export default class Transmuxer {
|
|
114
119
|
} = transmuxConfig;
|
115
120
|
|
116
121
|
const keyData = getEncryptionType(uintData, decryptdata);
|
117
|
-
if (keyData && keyData.method
|
122
|
+
if (keyData && isFullSegmentEncryption(keyData.method)) {
|
118
123
|
const decrypter = this.getDecrypter();
|
124
|
+
const aesMode = getAesModeFromFullSegmentMethod(keyData.method);
|
125
|
+
|
119
126
|
// Software decryption is synchronous; webCrypto is not
|
120
127
|
if (decrypter.isSync()) {
|
121
128
|
// Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
|
@@ -124,6 +131,7 @@ export default class Transmuxer {
|
|
124
131
|
uintData,
|
125
132
|
keyData.key.buffer,
|
126
133
|
keyData.iv.buffer,
|
134
|
+
aesMode,
|
127
135
|
);
|
128
136
|
// For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
|
129
137
|
const loadingParts = chunkMeta.part > -1;
|
@@ -137,7 +145,12 @@ export default class Transmuxer {
|
|
137
145
|
uintData = new Uint8Array(decryptedData);
|
138
146
|
} else {
|
139
147
|
this.decryptionPromise = decrypter
|
140
|
-
.webCryptoDecrypt(
|
148
|
+
.webCryptoDecrypt(
|
149
|
+
uintData,
|
150
|
+
keyData.key.buffer,
|
151
|
+
keyData.iv.buffer,
|
152
|
+
aesMode,
|
153
|
+
)
|
141
154
|
.then((decryptedData): TransmuxerResult => {
|
142
155
|
// Calling push here is important; if flush() is called while this is still resolving, this ensures that
|
143
156
|
// the decrypted data has been transmuxed
|