hls.js 1.5.14-0.canary.10431 → 1.5.14-0.canary.10432
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/dist/hls.js +316 -232
- package/dist/hls.js.d.ts +4 -2
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +316 -235
- 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 +244 -168
- 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 +244 -165
- 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 +1 -1
- package/src/controller/base-stream-controller.ts +5 -4
- package/src/demux/audio/aacdemuxer.ts +3 -3
- package/src/demux/audio/ac3-demuxer.ts +1 -1
- package/src/demux/inject-worker.ts +38 -4
- package/src/demux/transmuxer-interface.ts +98 -67
- package/src/demux/transmuxer-worker.ts +110 -76
- package/src/demux/transmuxer.ts +29 -16
- package/src/demux/tsdemuxer.ts +47 -24
- package/src/hls.ts +2 -1
- package/src/remux/mp4-remuxer.ts +24 -23
- package/src/remux/passthrough-remuxer.ts +22 -7
- package/src/version.ts +1 -0
@@ -6,114 +6,121 @@ import { ErrorDetails, ErrorTypes } from '../errors';
|
|
6
6
|
import type { RemuxedTrack, RemuxerResult } from '../types/remuxer';
|
7
7
|
import type { TransmuxerResult, ChunkMetadata } from '../types/transmuxer';
|
8
8
|
|
9
|
+
const transmuxers: (Transmuxer | undefined)[] = [];
|
10
|
+
|
9
11
|
if (typeof __IN_WORKER__ !== 'undefined' && __IN_WORKER__) {
|
10
|
-
startWorker(
|
12
|
+
startWorker();
|
11
13
|
}
|
12
14
|
|
13
|
-
function startWorker(
|
14
|
-
const observer = new EventEmitter();
|
15
|
-
const forwardMessage = (ev, data) => {
|
16
|
-
self.postMessage({ event: ev, data: data });
|
17
|
-
};
|
18
|
-
|
19
|
-
// forward events to main thread
|
20
|
-
observer.on(Events.FRAG_DECRYPTED, forwardMessage);
|
21
|
-
observer.on(Events.ERROR, forwardMessage);
|
22
|
-
|
23
|
-
// forward logger events to main thread
|
24
|
-
const forwardWorkerLogs = (logger: ILogger) => {
|
25
|
-
for (const logFn in logger) {
|
26
|
-
const func: ILogFunction = (message?) => {
|
27
|
-
forwardMessage('workerLog', {
|
28
|
-
logType: logFn,
|
29
|
-
message,
|
30
|
-
});
|
31
|
-
};
|
32
|
-
|
33
|
-
logger[logFn] = func;
|
34
|
-
}
|
35
|
-
};
|
36
|
-
|
15
|
+
function startWorker() {
|
37
16
|
self.addEventListener('message', (ev) => {
|
38
17
|
const data = ev.data;
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
);
|
49
|
-
const logger = enableLogs(config.debug, data.id);
|
50
|
-
forwardWorkerLogs(logger);
|
51
|
-
forwardMessage('init', null);
|
52
|
-
break;
|
18
|
+
const instanceNo = data.instanceNo;
|
19
|
+
if (instanceNo === undefined) {
|
20
|
+
return;
|
21
|
+
}
|
22
|
+
const transmuxer = transmuxers[instanceNo];
|
23
|
+
if (data.cmd === 'reset') {
|
24
|
+
delete transmuxers[data.resetNo];
|
25
|
+
if (transmuxer) {
|
26
|
+
transmuxer.destroy();
|
53
27
|
}
|
28
|
+
data.cmd = 'init';
|
29
|
+
}
|
30
|
+
if (data.cmd === 'init') {
|
31
|
+
const config = JSON.parse(data.config);
|
32
|
+
const observer = new EventEmitter();
|
33
|
+
observer.on(Events.FRAG_DECRYPTED, forwardMessage);
|
34
|
+
observer.on(Events.ERROR, forwardMessage);
|
35
|
+
const logger = enableLogs(config.debug, data.id);
|
36
|
+
forwardWorkerLogs(logger, instanceNo);
|
37
|
+
transmuxers[instanceNo] = new Transmuxer(
|
38
|
+
observer,
|
39
|
+
data.typeSupported,
|
40
|
+
config,
|
41
|
+
'',
|
42
|
+
data.id,
|
43
|
+
logger,
|
44
|
+
);
|
45
|
+
forwardMessage('init', null, instanceNo);
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
if (!transmuxer) {
|
49
|
+
return;
|
50
|
+
}
|
51
|
+
switch (data.cmd) {
|
54
52
|
case 'configure': {
|
55
|
-
|
53
|
+
transmuxer.configure(data.config);
|
56
54
|
break;
|
57
55
|
}
|
58
56
|
case 'demux': {
|
59
57
|
const transmuxResult: TransmuxerResult | Promise<TransmuxerResult> =
|
60
|
-
|
58
|
+
transmuxer.push(
|
61
59
|
data.data,
|
62
60
|
data.decryptdata,
|
63
61
|
data.chunkMeta,
|
64
62
|
data.state,
|
65
63
|
);
|
66
64
|
if (isPromise(transmuxResult)) {
|
67
|
-
self.transmuxer.async = true;
|
68
65
|
transmuxResult
|
69
66
|
.then((data) => {
|
70
|
-
emitTransmuxComplete(self, data);
|
67
|
+
emitTransmuxComplete(self, data, instanceNo);
|
71
68
|
})
|
72
69
|
.catch((error) => {
|
73
|
-
forwardMessage(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
70
|
+
forwardMessage(
|
71
|
+
Events.ERROR,
|
72
|
+
{
|
73
|
+
instanceNo,
|
74
|
+
type: ErrorTypes.MEDIA_ERROR,
|
75
|
+
details: ErrorDetails.FRAG_PARSING_ERROR,
|
76
|
+
chunkMeta: data.chunkMeta,
|
77
|
+
fatal: false,
|
78
|
+
error,
|
79
|
+
err: error,
|
80
|
+
reason: `transmuxer-worker push error`,
|
81
|
+
},
|
82
|
+
instanceNo,
|
83
|
+
);
|
82
84
|
});
|
83
85
|
} else {
|
84
|
-
self
|
85
|
-
emitTransmuxComplete(self, transmuxResult);
|
86
|
+
emitTransmuxComplete(self, transmuxResult, instanceNo);
|
86
87
|
}
|
87
88
|
break;
|
88
89
|
}
|
89
90
|
case 'flush': {
|
90
|
-
const
|
91
|
-
|
92
|
-
|
93
|
-
if (asyncFlush || self.transmuxer.async) {
|
94
|
-
if (!isPromise(transmuxResult)) {
|
95
|
-
transmuxResult = Promise.resolve(transmuxResult);
|
96
|
-
}
|
91
|
+
const chunkMeta = data.chunkMeta as ChunkMetadata;
|
92
|
+
const transmuxResult = transmuxer.flush(chunkMeta);
|
93
|
+
if (isPromise(transmuxResult)) {
|
97
94
|
transmuxResult
|
98
95
|
.then((results: Array<TransmuxerResult>) => {
|
99
|
-
handleFlushResult(
|
96
|
+
handleFlushResult(
|
97
|
+
self,
|
98
|
+
results as Array<TransmuxerResult>,
|
99
|
+
chunkMeta,
|
100
|
+
instanceNo,
|
101
|
+
);
|
100
102
|
})
|
101
103
|
.catch((error) => {
|
102
|
-
forwardMessage(
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
104
|
+
forwardMessage(
|
105
|
+
Events.ERROR,
|
106
|
+
{
|
107
|
+
type: ErrorTypes.MEDIA_ERROR,
|
108
|
+
details: ErrorDetails.FRAG_PARSING_ERROR,
|
109
|
+
chunkMeta: data.chunkMeta,
|
110
|
+
fatal: false,
|
111
|
+
error,
|
112
|
+
err: error,
|
113
|
+
reason: `transmuxer-worker flush error`,
|
114
|
+
},
|
115
|
+
instanceNo,
|
116
|
+
);
|
111
117
|
});
|
112
118
|
} else {
|
113
119
|
handleFlushResult(
|
114
120
|
self,
|
115
121
|
transmuxResult as Array<TransmuxerResult>,
|
116
|
-
|
122
|
+
chunkMeta,
|
123
|
+
instanceNo,
|
117
124
|
);
|
118
125
|
}
|
119
126
|
break;
|
@@ -127,6 +134,7 @@ function startWorker(self) {
|
|
127
134
|
function emitTransmuxComplete(
|
128
135
|
self: any,
|
129
136
|
transmuxResult: TransmuxerResult,
|
137
|
+
instanceNo: number,
|
130
138
|
): boolean {
|
131
139
|
if (isEmptyResult(transmuxResult.remuxResult)) {
|
132
140
|
return false;
|
@@ -140,7 +148,7 @@ function emitTransmuxComplete(
|
|
140
148
|
addToTransferable(transferable, video);
|
141
149
|
}
|
142
150
|
self.postMessage(
|
143
|
-
{ event: 'transmuxComplete', data: transmuxResult },
|
151
|
+
{ event: 'transmuxComplete', data: transmuxResult, instanceNo },
|
144
152
|
transferable,
|
145
153
|
);
|
146
154
|
return true;
|
@@ -164,16 +172,42 @@ function handleFlushResult(
|
|
164
172
|
self: any,
|
165
173
|
results: Array<TransmuxerResult>,
|
166
174
|
chunkMeta: ChunkMetadata,
|
175
|
+
instanceNo: number,
|
167
176
|
) {
|
168
177
|
const parsed = results.reduce(
|
169
|
-
(parsed, result) =>
|
178
|
+
(parsed, result) =>
|
179
|
+
emitTransmuxComplete(self, result, instanceNo) || parsed,
|
170
180
|
false,
|
171
181
|
);
|
172
182
|
if (!parsed) {
|
173
183
|
// Emit at least one "transmuxComplete" message even if media is not found to update stream-controller state to PARSING
|
174
|
-
self.postMessage({
|
184
|
+
self.postMessage({
|
185
|
+
event: 'transmuxComplete',
|
186
|
+
data: results[0],
|
187
|
+
instanceNo,
|
188
|
+
});
|
189
|
+
}
|
190
|
+
self.postMessage({ event: 'flush', data: chunkMeta, instanceNo });
|
191
|
+
}
|
192
|
+
|
193
|
+
function forwardMessage(event, data, instanceNo) {
|
194
|
+
self.postMessage({ event, data, instanceNo });
|
195
|
+
}
|
196
|
+
|
197
|
+
function forwardWorkerLogs(logger: ILogger, instanceNo: number) {
|
198
|
+
for (const logFn in logger) {
|
199
|
+
const func: ILogFunction = (message?) => {
|
200
|
+
forwardMessage(
|
201
|
+
'workerLog',
|
202
|
+
{
|
203
|
+
logType: logFn,
|
204
|
+
message,
|
205
|
+
},
|
206
|
+
instanceNo,
|
207
|
+
);
|
208
|
+
};
|
209
|
+
logger[logFn] = func;
|
175
210
|
}
|
176
|
-
self.postMessage({ event: 'flush', data: chunkMeta });
|
177
211
|
}
|
178
212
|
|
179
213
|
function isEmptyResult(remuxResult: RemuxerResult) {
|
package/src/demux/transmuxer.ts
CHANGED
@@ -9,7 +9,7 @@ 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
|
-
import {
|
12
|
+
import { PlaylistLevelType } from '../types/loader';
|
13
13
|
import {
|
14
14
|
isFullSegmentEncryption,
|
15
15
|
getAesModeFromFullSegmentMethod,
|
@@ -19,18 +19,16 @@ import type { Remuxer } from '../types/remuxer';
|
|
19
19
|
import type { TransmuxerResult, ChunkMetadata } from '../types/transmuxer';
|
20
20
|
import type { HlsConfig } from '../config';
|
21
21
|
import type { DecryptData } from '../loader/level-key';
|
22
|
-
import type { PlaylistLevelType } from '../types/loader';
|
23
22
|
import type { TypeSupported } from '../utils/codecs';
|
23
|
+
import type { ILogger } from '../utils/logger';
|
24
24
|
import type { RationalTimestamp } from '../utils/timescale-conversion';
|
25
|
-
import { optionalSelf } from '../utils/global';
|
26
25
|
|
27
|
-
let now;
|
26
|
+
let now: () => number;
|
28
27
|
// performance.now() not available on WebWorker, at least on Safari Desktop
|
29
28
|
try {
|
30
29
|
now = self.performance.now.bind(self.performance);
|
31
30
|
} catch (err) {
|
32
|
-
|
33
|
-
now = optionalSelf?.Date.now;
|
31
|
+
now = Date.now;
|
34
32
|
}
|
35
33
|
|
36
34
|
type MuxConfig =
|
@@ -52,7 +50,8 @@ if (__USE_M2TS_ADVANCED_CODECS__) {
|
|
52
50
|
}
|
53
51
|
|
54
52
|
export default class Transmuxer {
|
55
|
-
|
53
|
+
private asyncResult: boolean = false;
|
54
|
+
private logger: ILogger;
|
56
55
|
private observer: HlsEventEmitter;
|
57
56
|
private typeSupported: TypeSupported;
|
58
57
|
private config: HlsConfig;
|
@@ -72,12 +71,14 @@ export default class Transmuxer {
|
|
72
71
|
config: HlsConfig,
|
73
72
|
vendor: string,
|
74
73
|
id: PlaylistLevelType,
|
74
|
+
logger: ILogger,
|
75
75
|
) {
|
76
76
|
this.observer = observer;
|
77
77
|
this.typeSupported = typeSupported;
|
78
78
|
this.config = config;
|
79
79
|
this.vendor = vendor;
|
80
80
|
this.id = id;
|
81
|
+
this.logger = logger;
|
81
82
|
}
|
82
83
|
|
83
84
|
configure(transmuxConfig: TransmuxConfig) {
|
@@ -144,6 +145,7 @@ export default class Transmuxer {
|
|
144
145
|
}
|
145
146
|
uintData = new Uint8Array(decryptedData);
|
146
147
|
} else {
|
148
|
+
this.asyncResult = true;
|
147
149
|
this.decryptionPromise = decrypter
|
148
150
|
.webCryptoDecrypt(
|
149
151
|
uintData,
|
@@ -162,7 +164,7 @@ export default class Transmuxer {
|
|
162
164
|
this.decryptionPromise = null;
|
163
165
|
return result;
|
164
166
|
});
|
165
|
-
return this.decryptionPromise
|
167
|
+
return this.decryptionPromise;
|
166
168
|
}
|
167
169
|
}
|
168
170
|
|
@@ -170,7 +172,7 @@ export default class Transmuxer {
|
|
170
172
|
if (resetMuxers) {
|
171
173
|
const error = this.configureTransmuxer(uintData);
|
172
174
|
if (error) {
|
173
|
-
logger.warn(`[transmuxer] ${error.message}`);
|
175
|
+
this.logger.warn(`[transmuxer] ${error.message}`);
|
174
176
|
this.observer.emit(Events.ERROR, Events.ERROR, {
|
175
177
|
type: ErrorTypes.MEDIA_ERROR,
|
176
178
|
details: ErrorDetails.FRAG_PARSING_ERROR,
|
@@ -208,6 +210,8 @@ export default class Transmuxer {
|
|
208
210
|
accurateTimeOffset,
|
209
211
|
chunkMeta,
|
210
212
|
);
|
213
|
+
this.asyncResult = isPromise(result);
|
214
|
+
|
211
215
|
const currentState = this.currentTransmuxState;
|
212
216
|
|
213
217
|
currentState.contiguous = true;
|
@@ -228,6 +232,7 @@ export default class Transmuxer {
|
|
228
232
|
const { decrypter, currentTransmuxState, decryptionPromise } = this;
|
229
233
|
|
230
234
|
if (decryptionPromise) {
|
235
|
+
this.asyncResult = true;
|
231
236
|
// Upon resolution, the decryption promise calls push() and returns its TransmuxerResult up the stack. Therefore
|
232
237
|
// only flushing is required for async decryption
|
233
238
|
return decryptionPromise.then(() => {
|
@@ -254,11 +259,16 @@ export default class Transmuxer {
|
|
254
259
|
if (!demuxer || !remuxer) {
|
255
260
|
// If probing failed, then Hls.js has been given content its not able to handle
|
256
261
|
stats.executeEnd = now();
|
257
|
-
|
262
|
+
const emptyResults = [emptyResult(chunkMeta)];
|
263
|
+
if (this.asyncResult) {
|
264
|
+
return Promise.resolve(emptyResults);
|
265
|
+
}
|
266
|
+
return emptyResults;
|
258
267
|
}
|
259
268
|
|
260
269
|
const demuxResultOrPromise = demuxer.flush(timeOffset);
|
261
270
|
if (isPromise(demuxResultOrPromise)) {
|
271
|
+
this.asyncResult = true;
|
262
272
|
// Decrypt final SAMPLE-AES samples
|
263
273
|
return demuxResultOrPromise.then((demuxResult) => {
|
264
274
|
this.flushRemux(transmuxResults, demuxResult, chunkMeta);
|
@@ -267,6 +277,9 @@ export default class Transmuxer {
|
|
267
277
|
}
|
268
278
|
|
269
279
|
this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta);
|
280
|
+
if (this.asyncResult) {
|
281
|
+
return Promise.resolve(transmuxResults);
|
282
|
+
}
|
270
283
|
return transmuxResults;
|
271
284
|
}
|
272
285
|
|
@@ -277,10 +290,10 @@ export default class Transmuxer {
|
|
277
290
|
) {
|
278
291
|
const { audioTrack, videoTrack, id3Track, textTrack } = demuxResult;
|
279
292
|
const { accurateTimeOffset, timeOffset } = this.currentTransmuxState;
|
280
|
-
logger.log(
|
281
|
-
`[transmuxer.ts]: Flushed
|
293
|
+
this.logger.log(
|
294
|
+
`[transmuxer.ts]: Flushed ${this.id} sn: ${chunkMeta.sn}${
|
282
295
|
chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''
|
283
|
-
} of level ${chunkMeta.level}`,
|
296
|
+
} of ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'} ${chunkMeta.level}`,
|
284
297
|
);
|
285
298
|
const remuxResult = this.remuxer!.remux(
|
286
299
|
audioTrack,
|
@@ -438,7 +451,7 @@ export default class Transmuxer {
|
|
438
451
|
// probe for content type
|
439
452
|
let mux;
|
440
453
|
for (let i = 0, len = muxConfig.length; i < len; i++) {
|
441
|
-
if (muxConfig[i].demux?.probe(data)) {
|
454
|
+
if (muxConfig[i].demux?.probe(data, this.logger)) {
|
442
455
|
mux = muxConfig[i];
|
443
456
|
break;
|
444
457
|
}
|
@@ -452,10 +465,10 @@ export default class Transmuxer {
|
|
452
465
|
const Remuxer: MuxConfig['remux'] = mux.remux;
|
453
466
|
const Demuxer: MuxConfig['demux'] = mux.demux;
|
454
467
|
if (!remuxer || !(remuxer instanceof Remuxer)) {
|
455
|
-
this.remuxer = new Remuxer(observer, config, typeSupported,
|
468
|
+
this.remuxer = new Remuxer(observer, config, typeSupported, this.logger);
|
456
469
|
}
|
457
470
|
if (!demuxer || !(demuxer instanceof Demuxer)) {
|
458
|
-
this.demuxer = new Demuxer(observer, config, typeSupported);
|
471
|
+
this.demuxer = new Demuxer(observer, config, typeSupported, this.logger);
|
459
472
|
this.probe = Demuxer.probe;
|
460
473
|
}
|
461
474
|
}
|
package/src/demux/tsdemuxer.ts
CHANGED
@@ -18,11 +18,11 @@ import HevcVideoParser from './video/hevc-video-parser';
|
|
18
18
|
import SampleAesDecrypter from './sample-aes';
|
19
19
|
import { Events } from '../events';
|
20
20
|
import { appendUint8Array, RemuxerTrackIdConfig } from '../utils/mp4-tools';
|
21
|
-
import { logger } from '../utils/logger';
|
22
21
|
import { ErrorTypes, ErrorDetails } from '../errors';
|
23
22
|
import type { HlsConfig } from '../config';
|
24
23
|
import type { HlsEventEmitter } from '../events';
|
25
24
|
import type { TypeSupported } from '../utils/codecs';
|
25
|
+
import type { ILogger } from '../utils/logger';
|
26
26
|
import {
|
27
27
|
MetadataSchema,
|
28
28
|
type DemuxedVideoTrack,
|
@@ -54,9 +54,10 @@ export type ParsedVideoSample = ParsedTimestamp &
|
|
54
54
|
const PACKET_LENGTH = 188;
|
55
55
|
|
56
56
|
class TSDemuxer implements Demuxer {
|
57
|
+
private readonly logger: ILogger;
|
57
58
|
private readonly observer: HlsEventEmitter;
|
58
59
|
private readonly config: HlsConfig;
|
59
|
-
private typeSupported: TypeSupported;
|
60
|
+
private readonly typeSupported: TypeSupported;
|
60
61
|
|
61
62
|
private sampleAes: SampleAesDecrypter | null = null;
|
62
63
|
private pmtParsed: boolean = false;
|
@@ -77,14 +78,16 @@ class TSDemuxer implements Demuxer {
|
|
77
78
|
observer: HlsEventEmitter,
|
78
79
|
config: HlsConfig,
|
79
80
|
typeSupported: TypeSupported,
|
81
|
+
logger: ILogger,
|
80
82
|
) {
|
81
83
|
this.observer = observer;
|
82
84
|
this.config = config;
|
83
85
|
this.typeSupported = typeSupported;
|
86
|
+
this.logger = logger;
|
84
87
|
this.videoParser = null;
|
85
88
|
}
|
86
89
|
|
87
|
-
static probe(data: Uint8Array) {
|
90
|
+
static probe(data: Uint8Array, logger: ILogger) {
|
88
91
|
const syncOffset = TSDemuxer.syncOffset(data);
|
89
92
|
if (syncOffset > 0) {
|
90
93
|
logger.warn(
|
@@ -288,7 +291,7 @@ class TSDemuxer implements Demuxer {
|
|
288
291
|
switch (pid) {
|
289
292
|
case videoPid:
|
290
293
|
if (stt) {
|
291
|
-
if (videoData && (pes = parsePES(videoData))) {
|
294
|
+
if (videoData && (pes = parsePES(videoData, this.logger))) {
|
292
295
|
if (this.videoParser === null) {
|
293
296
|
switch (videoTrack.segmentCodec) {
|
294
297
|
case 'avc':
|
@@ -321,7 +324,7 @@ class TSDemuxer implements Demuxer {
|
|
321
324
|
break;
|
322
325
|
case audioPid:
|
323
326
|
if (stt) {
|
324
|
-
if (audioData && (pes = parsePES(audioData))) {
|
327
|
+
if (audioData && (pes = parsePES(audioData, this.logger))) {
|
325
328
|
switch (audioTrack.segmentCodec) {
|
326
329
|
case 'aac':
|
327
330
|
this.parseAACPES(audioTrack, pes);
|
@@ -345,7 +348,7 @@ class TSDemuxer implements Demuxer {
|
|
345
348
|
break;
|
346
349
|
case id3Pid:
|
347
350
|
if (stt) {
|
348
|
-
if (id3Data && (pes = parsePES(id3Data))) {
|
351
|
+
if (id3Data && (pes = parsePES(id3Data, this.logger))) {
|
349
352
|
this.parseID3PES(id3Track, pes);
|
350
353
|
}
|
351
354
|
|
@@ -362,7 +365,7 @@ class TSDemuxer implements Demuxer {
|
|
362
365
|
}
|
363
366
|
|
364
367
|
pmtId = this._pmtId = parsePAT(data, offset);
|
365
|
-
// logger.log('PMT PID:' + this._pmtId);
|
368
|
+
// this.logger.log('PMT PID:' + this._pmtId);
|
366
369
|
break;
|
367
370
|
case pmtId: {
|
368
371
|
if (stt) {
|
@@ -375,6 +378,7 @@ class TSDemuxer implements Demuxer {
|
|
375
378
|
this.typeSupported,
|
376
379
|
isSampleAes,
|
377
380
|
this.observer,
|
381
|
+
this.logger,
|
378
382
|
);
|
379
383
|
|
380
384
|
// only update track id if track PID found while parsing PMT
|
@@ -400,7 +404,7 @@ class TSDemuxer implements Demuxer {
|
|
400
404
|
}
|
401
405
|
|
402
406
|
if (unknownPID !== null && !pmtParsed) {
|
403
|
-
logger.warn(
|
407
|
+
this.logger.warn(
|
404
408
|
`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`,
|
405
409
|
);
|
406
410
|
unknownPID = null;
|
@@ -428,6 +432,8 @@ class TSDemuxer implements Demuxer {
|
|
428
432
|
new Error(
|
429
433
|
`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`,
|
430
434
|
),
|
435
|
+
undefined,
|
436
|
+
this.logger,
|
431
437
|
);
|
432
438
|
}
|
433
439
|
|
@@ -477,7 +483,7 @@ class TSDemuxer implements Demuxer {
|
|
477
483
|
const id3Data = id3Track.pesData;
|
478
484
|
// try to parse last PES packets
|
479
485
|
let pes: PES | null;
|
480
|
-
if (videoData && (pes = parsePES(videoData))) {
|
486
|
+
if (videoData && (pes = parsePES(videoData, this.logger))) {
|
481
487
|
if (this.videoParser === null) {
|
482
488
|
switch (videoTrack.segmentCodec) {
|
483
489
|
case 'avc':
|
@@ -505,7 +511,7 @@ class TSDemuxer implements Demuxer {
|
|
505
511
|
videoTrack.pesData = videoData;
|
506
512
|
}
|
507
513
|
|
508
|
-
if (audioData && (pes = parsePES(audioData))) {
|
514
|
+
if (audioData && (pes = parsePES(audioData, this.logger))) {
|
509
515
|
switch (audioTrack.segmentCodec) {
|
510
516
|
case 'aac':
|
511
517
|
this.parseAACPES(audioTrack, pes);
|
@@ -522,7 +528,7 @@ class TSDemuxer implements Demuxer {
|
|
522
528
|
audioTrack.pesData = null;
|
523
529
|
} else {
|
524
530
|
if (audioData?.size) {
|
525
|
-
logger.log(
|
531
|
+
this.logger.log(
|
526
532
|
'last AAC PES packet truncated,might overlap between fragments',
|
527
533
|
);
|
528
534
|
}
|
@@ -531,7 +537,7 @@ class TSDemuxer implements Demuxer {
|
|
531
537
|
audioTrack.pesData = audioData;
|
532
538
|
}
|
533
539
|
|
534
|
-
if (id3Data && (pes = parsePES(id3Data))) {
|
540
|
+
if (id3Data && (pes = parsePES(id3Data, this.logger))) {
|
535
541
|
this.parseID3PES(id3Track, pes);
|
536
542
|
id3Track.pesData = null;
|
537
543
|
} else {
|
@@ -625,7 +631,12 @@ class TSDemuxer implements Demuxer {
|
|
625
631
|
} else {
|
626
632
|
reason = 'No ADTS header found in AAC PES';
|
627
633
|
}
|
628
|
-
emitParsingError(
|
634
|
+
emitParsingError(
|
635
|
+
this.observer,
|
636
|
+
new Error(reason),
|
637
|
+
recoverable,
|
638
|
+
this.logger,
|
639
|
+
);
|
629
640
|
if (!recoverable) {
|
630
641
|
return;
|
631
642
|
}
|
@@ -648,7 +659,7 @@ class TSDemuxer implements Demuxer {
|
|
648
659
|
const frameDuration = ADTS.getFrameDuration(track.samplerate as number);
|
649
660
|
pts = aacOverFlow.sample.pts + frameDuration;
|
650
661
|
} else {
|
651
|
-
logger.warn('[tsdemuxer]: AAC PES unknown PTS');
|
662
|
+
this.logger.warn('[tsdemuxer]: AAC PES unknown PTS');
|
652
663
|
return;
|
653
664
|
}
|
654
665
|
|
@@ -679,7 +690,7 @@ class TSDemuxer implements Demuxer {
|
|
679
690
|
let offset = 0;
|
680
691
|
const pts = pes.pts;
|
681
692
|
if (pts === undefined) {
|
682
|
-
logger.warn('[tsdemuxer]: MPEG PES unknown PTS');
|
693
|
+
this.logger.warn('[tsdemuxer]: MPEG PES unknown PTS');
|
683
694
|
return;
|
684
695
|
}
|
685
696
|
|
@@ -711,7 +722,7 @@ class TSDemuxer implements Demuxer {
|
|
711
722
|
const data = pes.data;
|
712
723
|
const pts = pes.pts;
|
713
724
|
if (pts === undefined) {
|
714
|
-
logger.warn('[tsdemuxer]: AC3 PES unknown PTS');
|
725
|
+
this.logger.warn('[tsdemuxer]: AC3 PES unknown PTS');
|
715
726
|
return;
|
716
727
|
}
|
717
728
|
const length = data.length;
|
@@ -730,7 +741,7 @@ class TSDemuxer implements Demuxer {
|
|
730
741
|
|
731
742
|
private parseID3PES(id3Track: DemuxedMetadataTrack, pes: PES) {
|
732
743
|
if (pes.pts === undefined) {
|
733
|
-
logger.warn('[tsdemuxer]: ID3 PES unknown PTS');
|
744
|
+
this.logger.warn('[tsdemuxer]: ID3 PES unknown PTS');
|
734
745
|
return;
|
735
746
|
}
|
736
747
|
const id3Sample = Object.assign({}, pes as Required<PES>, {
|
@@ -757,6 +768,7 @@ function parsePMT(
|
|
757
768
|
typeSupported: TypeSupported,
|
758
769
|
isSampleAes: boolean,
|
759
770
|
observer: HlsEventEmitter,
|
771
|
+
logger: ILogger,
|
760
772
|
) {
|
761
773
|
const result = {
|
762
774
|
audioPid: -1,
|
@@ -779,7 +791,7 @@ function parsePMT(
|
|
779
791
|
switch (data[offset]) {
|
780
792
|
case 0xcf: // SAMPLE-AES AAC
|
781
793
|
if (!isSampleAes) {
|
782
|
-
logEncryptedSamplesFoundInUnencryptedStream('ADTS AAC');
|
794
|
+
logEncryptedSamplesFoundInUnencryptedStream('ADTS AAC', this.logger);
|
783
795
|
break;
|
784
796
|
}
|
785
797
|
/* falls through */
|
@@ -802,7 +814,7 @@ function parsePMT(
|
|
802
814
|
|
803
815
|
case 0xdb: // SAMPLE-AES AVC
|
804
816
|
if (!isSampleAes) {
|
805
|
-
logEncryptedSamplesFoundInUnencryptedStream('H.264');
|
817
|
+
logEncryptedSamplesFoundInUnencryptedStream('H.264', this.logger);
|
806
818
|
break;
|
807
819
|
}
|
808
820
|
/* falls through */
|
@@ -830,7 +842,7 @@ function parsePMT(
|
|
830
842
|
|
831
843
|
case 0xc1: // SAMPLE-AES AC3
|
832
844
|
if (!isSampleAes) {
|
833
|
-
logEncryptedSamplesFoundInUnencryptedStream('AC-3');
|
845
|
+
logEncryptedSamplesFoundInUnencryptedStream('AC-3', this.logger);
|
834
846
|
break;
|
835
847
|
}
|
836
848
|
/* falls through */
|
@@ -886,7 +898,12 @@ function parsePMT(
|
|
886
898
|
case 0xc2: // SAMPLE-AES EC3
|
887
899
|
/* falls through */
|
888
900
|
case 0x87:
|
889
|
-
emitParsingError(
|
901
|
+
emitParsingError(
|
902
|
+
observer,
|
903
|
+
new Error('Unsupported EC-3 in M2TS found'),
|
904
|
+
undefined,
|
905
|
+
this.logger,
|
906
|
+
);
|
890
907
|
return result;
|
891
908
|
|
892
909
|
case 0x24: // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
|
@@ -900,6 +917,8 @@ function parsePMT(
|
|
900
917
|
emitParsingError(
|
901
918
|
observer,
|
902
919
|
new Error('Unsupported HEVC in M2TS found'),
|
920
|
+
undefined,
|
921
|
+
this.logger,
|
903
922
|
);
|
904
923
|
return result;
|
905
924
|
}
|
@@ -919,7 +938,8 @@ function parsePMT(
|
|
919
938
|
function emitParsingError(
|
920
939
|
observer: HlsEventEmitter,
|
921
940
|
error: Error,
|
922
|
-
levelRetry
|
941
|
+
levelRetry: boolean | undefined,
|
942
|
+
logger: ILogger,
|
923
943
|
) {
|
924
944
|
logger.warn(`parsing error: ${error.message}`);
|
925
945
|
observer.emit(Events.ERROR, Events.ERROR, {
|
@@ -932,11 +952,14 @@ function emitParsingError(
|
|
932
952
|
});
|
933
953
|
}
|
934
954
|
|
935
|
-
function logEncryptedSamplesFoundInUnencryptedStream(
|
955
|
+
function logEncryptedSamplesFoundInUnencryptedStream(
|
956
|
+
type: string,
|
957
|
+
logger: ILogger,
|
958
|
+
) {
|
936
959
|
logger.log(`${type} with AES-128-CBC encryption found in unencrypted stream`);
|
937
960
|
}
|
938
961
|
|
939
|
-
function parsePES(stream: ElementaryStreamData): PES | null {
|
962
|
+
function parsePES(stream: ElementaryStreamData, logger: ILogger): PES | null {
|
940
963
|
let i = 0;
|
941
964
|
let frag: Uint8Array;
|
942
965
|
let pesLen: number;
|