hls.js 1.5.14-0.canary.10559 → 1.5.15
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 +38 -41
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2911 -4558
- package/dist/hls.js.d.ts +112 -186
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +2291 -3311
- 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 +1813 -2835
- 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 +4707 -6356
- 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 +42 -42
- package/src/config.ts +2 -5
- package/src/controller/abr-controller.ts +25 -39
- package/src/controller/audio-stream-controller.ts +136 -156
- 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 +107 -263
- package/src/controller/buffer-controller.ts +97 -250
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -3
- package/src/controller/cmcd-controller.ts +14 -51
- package/src/controller/content-steering-controller.ts +15 -29
- package/src/controller/eme-controller.ts +23 -10
- package/src/controller/error-controller.ts +22 -28
- package/src/controller/fps-controller.ts +3 -8
- package/src/controller/fragment-finders.ts +16 -44
- package/src/controller/fragment-tracker.ts +25 -58
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/id3-track-controller.ts +35 -45
- package/src/controller/latency-controller.ts +13 -18
- package/src/controller/level-controller.ts +19 -37
- package/src/controller/stream-controller.ts +83 -100
- package/src/controller/subtitle-stream-controller.ts +47 -35
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +22 -20
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +16 -32
- package/src/crypt/fast-aes-key.ts +5 -28
- package/src/demux/audio/aacdemuxer.ts +5 -5
- package/src/demux/audio/ac3-demuxer.ts +4 -5
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/audio/base-audio-demuxer.ts +15 -21
- 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/inject-worker.ts +4 -38
- package/src/demux/mp4demuxer.ts +8 -17
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +83 -106
- package/src/demux/transmuxer-worker.ts +77 -111
- package/src/demux/transmuxer.ts +22 -46
- package/src/demux/tsdemuxer.ts +65 -131
- package/src/demux/video/avc-video-parser.ts +156 -219
- package/src/demux/video/base-video-parser.ts +9 -133
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/errors.ts +0 -2
- package/src/events.ts +1 -8
- package/src/exports-named.ts +1 -1
- package/src/hls.ts +48 -97
- package/src/loader/date-range.ts +5 -71
- package/src/loader/fragment-loader.ts +21 -23
- package/src/loader/fragment.ts +4 -8
- package/src/loader/key-loader.ts +1 -3
- package/src/loader/level-details.ts +6 -6
- package/src/loader/level-key.ts +9 -10
- package/src/loader/m3u8-parser.ts +144 -138
- package/src/loader/playlist-loader.ts +7 -5
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +84 -55
- package/src/remux/passthrough-remuxer.ts +8 -23
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +1 -3
- package/src/types/demuxer.ts +1 -3
- package/src/types/events.ts +6 -19
- package/src/types/fragment-tracker.ts +2 -2
- package/src/types/general.ts +6 -0
- package/src/types/media-playlist.ts +1 -9
- package/src/types/remuxer.ts +1 -1
- package/src/utils/attr-list.ts +9 -96
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/cea-608-parser.ts +3 -1
- package/src/utils/codecs.ts +5 -34
- package/src/utils/discontinuities.ts +47 -21
- package/src/utils/fetch-loader.ts +1 -1
- package/src/utils/hdr.ts +7 -4
- package/src/utils/imsc1-ttml-parser.ts +1 -1
- package/src/utils/keysystem-util.ts +6 -1
- package/src/utils/level-helper.ts +44 -71
- package/src/utils/logger.ts +23 -58
- package/src/utils/mp4-tools.ts +3 -5
- package/src/utils/rendition-helper.ts +74 -100
- package/src/utils/variable-substitution.ts +19 -0
- package/src/utils/webvtt-parser.ts +12 -2
- package/dist/hls.d.mts +0 -3179
- package/dist/hls.d.ts +0 -3179
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -718
- package/src/utils/encryption-methods-util.ts +0 -21
- package/src/utils/hash.ts +0 -10
- package/src/utils/utf8-utils.ts +0 -18
- package/src/version.ts +0 -1
@@ -1,22 +1,26 @@
|
|
1
1
|
import BaseVideoParser from './base-video-parser';
|
2
|
-
import
|
2
|
+
import {
|
3
3
|
DemuxedVideoTrack,
|
4
4
|
DemuxedUserdataTrack,
|
5
|
+
VideoSampleUnit,
|
5
6
|
} from '../../types/demuxer';
|
6
|
-
import {
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
import {
|
8
|
+
appendUint8Array,
|
9
|
+
parseSEIMessageFromNALu,
|
10
|
+
} from '../../utils/mp4-tools';
|
10
11
|
import ExpGolomb from './exp-golomb';
|
12
|
+
import type { PES } from '../tsdemuxer';
|
11
13
|
|
12
14
|
class AvcVideoParser extends BaseVideoParser {
|
13
|
-
public
|
15
|
+
public parseAVCPES(
|
14
16
|
track: DemuxedVideoTrack,
|
15
17
|
textTrack: DemuxedUserdataTrack,
|
16
18
|
pes: PES,
|
17
|
-
|
19
|
+
last: boolean,
|
20
|
+
duration: number,
|
18
21
|
) {
|
19
|
-
const units = this.
|
22
|
+
const units = this.parseAVCNALu(track, pes.data);
|
23
|
+
const debug = false;
|
20
24
|
let VideoSample = this.VideoSample;
|
21
25
|
let push: boolean;
|
22
26
|
let spsfound = false;
|
@@ -31,6 +35,7 @@ class AvcVideoParser extends BaseVideoParser {
|
|
31
35
|
false,
|
32
36
|
pes.pts,
|
33
37
|
pes.dts,
|
38
|
+
'',
|
34
39
|
);
|
35
40
|
}
|
36
41
|
|
@@ -44,7 +49,7 @@ class AvcVideoParser extends BaseVideoParser {
|
|
44
49
|
// only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
|
45
50
|
if (spsfound && data.length > 4) {
|
46
51
|
// retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
|
47
|
-
const sliceType =
|
52
|
+
const sliceType = new ExpGolomb(data).readSliceType();
|
48
53
|
// 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
|
49
54
|
// SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
|
50
55
|
// An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
|
@@ -73,8 +78,14 @@ class AvcVideoParser extends BaseVideoParser {
|
|
73
78
|
true,
|
74
79
|
pes.pts,
|
75
80
|
pes.dts,
|
81
|
+
'',
|
76
82
|
);
|
77
83
|
}
|
84
|
+
|
85
|
+
if (debug) {
|
86
|
+
VideoSample.debug += 'NDR ';
|
87
|
+
}
|
88
|
+
|
78
89
|
VideoSample.frame = true;
|
79
90
|
VideoSample.key = iskey;
|
80
91
|
|
@@ -94,15 +105,23 @@ class AvcVideoParser extends BaseVideoParser {
|
|
94
105
|
true,
|
95
106
|
pes.pts,
|
96
107
|
pes.dts,
|
108
|
+
'',
|
97
109
|
);
|
98
110
|
}
|
99
111
|
|
112
|
+
if (debug) {
|
113
|
+
VideoSample.debug += 'IDR ';
|
114
|
+
}
|
115
|
+
|
100
116
|
VideoSample.key = true;
|
101
117
|
VideoSample.frame = true;
|
102
118
|
break;
|
103
119
|
// SEI
|
104
120
|
case 6: {
|
105
121
|
push = true;
|
122
|
+
if (debug && VideoSample) {
|
123
|
+
VideoSample.debug += 'SEI ';
|
124
|
+
}
|
106
125
|
parseSEIMessageFromNALu(
|
107
126
|
unit.data,
|
108
127
|
1,
|
@@ -115,8 +134,13 @@ class AvcVideoParser extends BaseVideoParser {
|
|
115
134
|
case 7: {
|
116
135
|
push = true;
|
117
136
|
spsfound = true;
|
137
|
+
if (debug && VideoSample) {
|
138
|
+
VideoSample.debug += 'SPS ';
|
139
|
+
}
|
118
140
|
const sps = unit.data;
|
119
|
-
const
|
141
|
+
const expGolombDecoder = new ExpGolomb(sps);
|
142
|
+
const config = expGolombDecoder.readSPS();
|
143
|
+
|
120
144
|
if (
|
121
145
|
!track.sps ||
|
122
146
|
track.width !== config.width ||
|
@@ -128,6 +152,7 @@ class AvcVideoParser extends BaseVideoParser {
|
|
128
152
|
track.height = config.height;
|
129
153
|
track.pixelRatio = config.pixelRatio;
|
130
154
|
track.sps = [sps];
|
155
|
+
track.duration = duration;
|
131
156
|
const codecarray = sps.subarray(1, 4);
|
132
157
|
let codecstring = 'avc1.';
|
133
158
|
for (let i = 0; i < 3; i++) {
|
@@ -140,11 +165,15 @@ class AvcVideoParser extends BaseVideoParser {
|
|
140
165
|
}
|
141
166
|
track.codec = codecstring;
|
142
167
|
}
|
168
|
+
|
143
169
|
break;
|
144
170
|
}
|
145
171
|
// PPS
|
146
172
|
case 8:
|
147
173
|
push = true;
|
174
|
+
if (debug && VideoSample) {
|
175
|
+
VideoSample.debug += 'PPS ';
|
176
|
+
}
|
148
177
|
|
149
178
|
track.pps = [unit.data];
|
150
179
|
|
@@ -153,17 +182,16 @@ class AvcVideoParser extends BaseVideoParser {
|
|
153
182
|
case 9:
|
154
183
|
push = true;
|
155
184
|
track.audFound = true;
|
156
|
-
if (VideoSample
|
185
|
+
if (VideoSample) {
|
157
186
|
this.pushAccessUnit(VideoSample, track);
|
158
|
-
VideoSample = null;
|
159
|
-
}
|
160
|
-
if (!VideoSample) {
|
161
|
-
VideoSample = this.VideoSample = this.createVideoSample(
|
162
|
-
false,
|
163
|
-
pes.pts,
|
164
|
-
pes.dts,
|
165
|
-
);
|
166
187
|
}
|
188
|
+
|
189
|
+
VideoSample = this.VideoSample = this.createVideoSample(
|
190
|
+
false,
|
191
|
+
pes.pts,
|
192
|
+
pes.dts,
|
193
|
+
debug ? 'AUD ' : '',
|
194
|
+
);
|
167
195
|
break;
|
168
196
|
// Filler Data
|
169
197
|
case 12:
|
@@ -171,6 +199,9 @@ class AvcVideoParser extends BaseVideoParser {
|
|
171
199
|
break;
|
172
200
|
default:
|
173
201
|
push = false;
|
202
|
+
if (VideoSample) {
|
203
|
+
VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
|
204
|
+
}
|
174
205
|
|
175
206
|
break;
|
176
207
|
}
|
@@ -180,223 +211,129 @@ class AvcVideoParser extends BaseVideoParser {
|
|
180
211
|
}
|
181
212
|
});
|
182
213
|
// if last PES packet, push samples
|
183
|
-
if (
|
214
|
+
if (last && VideoSample) {
|
184
215
|
this.pushAccessUnit(VideoSample, track);
|
185
216
|
this.VideoSample = null;
|
186
217
|
}
|
187
218
|
}
|
188
219
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
220
|
+
private parseAVCNALu(
|
221
|
+
track: DemuxedVideoTrack,
|
222
|
+
array: Uint8Array,
|
223
|
+
): Array<{
|
224
|
+
data: Uint8Array;
|
225
|
+
type: number;
|
226
|
+
state?: number;
|
227
|
+
}> {
|
228
|
+
const len = array.byteLength;
|
229
|
+
let state = track.naluState || 0;
|
230
|
+
const lastState = state;
|
231
|
+
const units: VideoSampleUnit[] = [];
|
232
|
+
let i = 0;
|
233
|
+
let value: number;
|
234
|
+
let overflow: number;
|
235
|
+
let unitType: number;
|
236
|
+
let lastUnitStart = -1;
|
237
|
+
let lastUnitType: number = 0;
|
238
|
+
// logger.log('PES:' + Hex.hexDump(array));
|
202
239
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
let lastScale = 8;
|
211
|
-
let nextScale = 8;
|
212
|
-
let deltaScale;
|
213
|
-
for (let j = 0; j < count; j++) {
|
214
|
-
if (nextScale !== 0) {
|
215
|
-
deltaScale = reader.readEG();
|
216
|
-
nextScale = (lastScale + deltaScale + 256) % 256;
|
217
|
-
}
|
218
|
-
lastScale = nextScale === 0 ? lastScale : nextScale;
|
240
|
+
if (state === -1) {
|
241
|
+
// special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
|
242
|
+
lastUnitStart = 0;
|
243
|
+
// NALu type is value read from offset 0
|
244
|
+
lastUnitType = array[0] & 0x1f;
|
245
|
+
state = 0;
|
246
|
+
i = 1;
|
219
247
|
}
|
220
|
-
}
|
221
|
-
|
222
|
-
/**
|
223
|
-
* Read a sequence parameter set and return some interesting video
|
224
|
-
* properties. A sequence parameter set is the H264 metadata that
|
225
|
-
* describes the properties of upcoming video frames.
|
226
|
-
* @returns an object with configuration parsed from the
|
227
|
-
* sequence parameter set, including the dimensions of the
|
228
|
-
* associated video frames.
|
229
|
-
*/
|
230
|
-
readSPS(sps: Uint8Array): {
|
231
|
-
width: number;
|
232
|
-
height: number;
|
233
|
-
pixelRatio: [number, number];
|
234
|
-
} {
|
235
|
-
const eg = new ExpGolomb(sps);
|
236
|
-
let frameCropLeftOffset = 0;
|
237
|
-
let frameCropRightOffset = 0;
|
238
|
-
let frameCropTopOffset = 0;
|
239
|
-
let frameCropBottomOffset = 0;
|
240
|
-
let numRefFramesInPicOrderCntCycle;
|
241
|
-
let scalingListCount;
|
242
|
-
let i;
|
243
|
-
const readUByte = eg.readUByte.bind(eg);
|
244
|
-
const readBits = eg.readBits.bind(eg);
|
245
|
-
const readUEG = eg.readUEG.bind(eg);
|
246
|
-
const readBoolean = eg.readBoolean.bind(eg);
|
247
|
-
const skipBits = eg.skipBits.bind(eg);
|
248
|
-
const skipEG = eg.skipEG.bind(eg);
|
249
|
-
const skipUEG = eg.skipUEG.bind(eg);
|
250
|
-
const skipScalingList = this.skipScalingList.bind(this);
|
251
248
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
249
|
+
while (i < len) {
|
250
|
+
value = array[i++];
|
251
|
+
// optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
|
252
|
+
if (!state) {
|
253
|
+
state = value ? 0 : 1;
|
254
|
+
continue;
|
255
|
+
}
|
256
|
+
if (state === 1) {
|
257
|
+
state = value ? 0 : 2;
|
258
|
+
continue;
|
259
|
+
}
|
260
|
+
// here we have state either equal to 2 or 3
|
261
|
+
if (!value) {
|
262
|
+
state = 3;
|
263
|
+
} else if (value === 1) {
|
264
|
+
overflow = i - state - 1;
|
265
|
+
if (lastUnitStart >= 0) {
|
266
|
+
const unit: VideoSampleUnit = {
|
267
|
+
data: array.subarray(lastUnitStart, overflow),
|
268
|
+
type: lastUnitType,
|
269
|
+
};
|
270
|
+
// logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
|
271
|
+
units.push(unit);
|
272
|
+
} else {
|
273
|
+
// lastUnitStart is undefined => this is the first start code found in this PES packet
|
274
|
+
// first check if start code delimiter is overlapping between 2 PES packets,
|
275
|
+
// ie it started in last packet (lastState not zero)
|
276
|
+
// and ended at the beginning of this PES packet (i <= 4 - lastState)
|
277
|
+
const lastUnit = this.getLastNalUnit(track.samples);
|
278
|
+
if (lastUnit) {
|
279
|
+
if (lastState && i <= 4 - lastState) {
|
280
|
+
// start delimiter overlapping between PES packets
|
281
|
+
// strip start delimiter bytes from the end of last NAL unit
|
282
|
+
// check if lastUnit had a state different from zero
|
283
|
+
if (lastUnit.state) {
|
284
|
+
// strip last bytes
|
285
|
+
lastUnit.data = lastUnit.data.subarray(
|
286
|
+
0,
|
287
|
+
lastUnit.data.byteLength - lastState,
|
288
|
+
);
|
289
|
+
}
|
290
|
+
}
|
291
|
+
// If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
|
274
292
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
if (readBoolean()) {
|
283
|
-
// seq_scaling_list_present_flag[ i ]
|
284
|
-
if (i < 6) {
|
285
|
-
skipScalingList(16, eg);
|
286
|
-
} else {
|
287
|
-
skipScalingList(64, eg);
|
293
|
+
if (overflow > 0) {
|
294
|
+
// logger.log('first NALU found with overflow:' + overflow);
|
295
|
+
lastUnit.data = appendUint8Array(
|
296
|
+
lastUnit.data,
|
297
|
+
array.subarray(0, overflow),
|
298
|
+
);
|
299
|
+
lastUnit.state = 0;
|
288
300
|
}
|
289
301
|
}
|
290
302
|
}
|
303
|
+
// check if we can read unit type
|
304
|
+
if (i < len) {
|
305
|
+
unitType = array[i] & 0x1f;
|
306
|
+
// logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
307
|
+
lastUnitStart = i;
|
308
|
+
lastUnitType = unitType;
|
309
|
+
state = 0;
|
310
|
+
} else {
|
311
|
+
// not enough byte to read unit type. let's read it on next PES parsing
|
312
|
+
state = -1;
|
313
|
+
}
|
314
|
+
} else {
|
315
|
+
state = 0;
|
291
316
|
}
|
292
317
|
}
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
numRefFramesInPicOrderCntCycle = readUEG();
|
302
|
-
for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
|
303
|
-
skipEG();
|
304
|
-
} // offset_for_ref_frame[ i ]
|
318
|
+
if (lastUnitStart >= 0 && state >= 0) {
|
319
|
+
const unit: VideoSampleUnit = {
|
320
|
+
data: array.subarray(lastUnitStart, len),
|
321
|
+
type: lastUnitType,
|
322
|
+
state: state,
|
323
|
+
};
|
324
|
+
units.push(unit);
|
325
|
+
// logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
|
305
326
|
}
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
skipBits(1);
|
313
|
-
} // mb_adaptive_frame_field_flag
|
314
|
-
|
315
|
-
skipBits(1); // direct_8x8_inference_flag
|
316
|
-
if (readBoolean()) {
|
317
|
-
// frame_cropping_flag
|
318
|
-
frameCropLeftOffset = readUEG();
|
319
|
-
frameCropRightOffset = readUEG();
|
320
|
-
frameCropTopOffset = readUEG();
|
321
|
-
frameCropBottomOffset = readUEG();
|
322
|
-
}
|
323
|
-
let pixelRatio: [number, number] = [1, 1];
|
324
|
-
if (readBoolean()) {
|
325
|
-
// vui_parameters_present_flag
|
326
|
-
if (readBoolean()) {
|
327
|
-
// aspect_ratio_info_present_flag
|
328
|
-
const aspectRatioIdc = readUByte();
|
329
|
-
switch (aspectRatioIdc) {
|
330
|
-
case 1:
|
331
|
-
pixelRatio = [1, 1];
|
332
|
-
break;
|
333
|
-
case 2:
|
334
|
-
pixelRatio = [12, 11];
|
335
|
-
break;
|
336
|
-
case 3:
|
337
|
-
pixelRatio = [10, 11];
|
338
|
-
break;
|
339
|
-
case 4:
|
340
|
-
pixelRatio = [16, 11];
|
341
|
-
break;
|
342
|
-
case 5:
|
343
|
-
pixelRatio = [40, 33];
|
344
|
-
break;
|
345
|
-
case 6:
|
346
|
-
pixelRatio = [24, 11];
|
347
|
-
break;
|
348
|
-
case 7:
|
349
|
-
pixelRatio = [20, 11];
|
350
|
-
break;
|
351
|
-
case 8:
|
352
|
-
pixelRatio = [32, 11];
|
353
|
-
break;
|
354
|
-
case 9:
|
355
|
-
pixelRatio = [80, 33];
|
356
|
-
break;
|
357
|
-
case 10:
|
358
|
-
pixelRatio = [18, 11];
|
359
|
-
break;
|
360
|
-
case 11:
|
361
|
-
pixelRatio = [15, 11];
|
362
|
-
break;
|
363
|
-
case 12:
|
364
|
-
pixelRatio = [64, 33];
|
365
|
-
break;
|
366
|
-
case 13:
|
367
|
-
pixelRatio = [160, 99];
|
368
|
-
break;
|
369
|
-
case 14:
|
370
|
-
pixelRatio = [4, 3];
|
371
|
-
break;
|
372
|
-
case 15:
|
373
|
-
pixelRatio = [3, 2];
|
374
|
-
break;
|
375
|
-
case 16:
|
376
|
-
pixelRatio = [2, 1];
|
377
|
-
break;
|
378
|
-
case 255: {
|
379
|
-
pixelRatio = [
|
380
|
-
(readUByte() << 8) | readUByte(),
|
381
|
-
(readUByte() << 8) | readUByte(),
|
382
|
-
];
|
383
|
-
break;
|
384
|
-
}
|
385
|
-
}
|
327
|
+
// no NALu found
|
328
|
+
if (units.length === 0) {
|
329
|
+
// append pes.data to previous NAL unit
|
330
|
+
const lastUnit = this.getLastNalUnit(track.samples);
|
331
|
+
if (lastUnit) {
|
332
|
+
lastUnit.data = appendUint8Array(lastUnit.data, array);
|
386
333
|
}
|
387
334
|
}
|
388
|
-
|
389
|
-
|
390
|
-
(picWidthInMbsMinus1 + 1) * 16 -
|
391
|
-
frameCropLeftOffset * 2 -
|
392
|
-
frameCropRightOffset * 2,
|
393
|
-
),
|
394
|
-
height:
|
395
|
-
(2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 -
|
396
|
-
(frameMbsOnlyFlag ? 2 : 4) *
|
397
|
-
(frameCropTopOffset + frameCropBottomOffset),
|
398
|
-
pixelRatio: pixelRatio,
|
399
|
-
};
|
335
|
+
track.naluState = state;
|
336
|
+
return units;
|
400
337
|
}
|
401
338
|
}
|
402
339
|
|
@@ -1,20 +1,19 @@
|
|
1
1
|
import type { ParsedVideoSample } from '../tsdemuxer';
|
2
|
-
import
|
2
|
+
import {
|
3
3
|
DemuxedVideoTrack,
|
4
|
-
DemuxedUserdataTrack,
|
5
4
|
VideoSample,
|
6
5
|
VideoSampleUnit,
|
7
6
|
} from '../../types/demuxer';
|
8
|
-
import
|
9
|
-
import { appendUint8Array } from '../../utils/mp4-tools';
|
7
|
+
import { logger } from '../../utils/logger';
|
10
8
|
|
11
|
-
|
9
|
+
class BaseVideoParser {
|
12
10
|
protected VideoSample: ParsedVideoSample | null = null;
|
13
11
|
|
14
12
|
protected createVideoSample(
|
15
13
|
key: boolean,
|
16
14
|
pts: number | undefined,
|
17
15
|
dts: number | undefined,
|
16
|
+
debug: string,
|
18
17
|
): ParsedVideoSample {
|
19
18
|
return {
|
20
19
|
key,
|
@@ -22,6 +21,7 @@ abstract class BaseVideoParser {
|
|
22
21
|
pts,
|
23
22
|
dts,
|
24
23
|
units: [],
|
24
|
+
debug,
|
25
25
|
length: 0,
|
26
26
|
};
|
27
27
|
}
|
@@ -63,135 +63,11 @@ abstract class BaseVideoParser {
|
|
63
63
|
}
|
64
64
|
videoTrack.samples.push(VideoSample as VideoSample);
|
65
65
|
}
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
textTrack: DemuxedUserdataTrack,
|
71
|
-
pes: PES,
|
72
|
-
last: boolean,
|
73
|
-
);
|
74
|
-
|
75
|
-
protected abstract getNALuType(data: Uint8Array, offset: number): number;
|
76
|
-
|
77
|
-
protected parseNALu(
|
78
|
-
track: DemuxedVideoTrack,
|
79
|
-
array: Uint8Array,
|
80
|
-
endOfSegment: boolean,
|
81
|
-
): Array<{
|
82
|
-
data: Uint8Array;
|
83
|
-
type: number;
|
84
|
-
state?: number;
|
85
|
-
}> {
|
86
|
-
const len = array.byteLength;
|
87
|
-
let state = track.naluState || 0;
|
88
|
-
const lastState = state;
|
89
|
-
const units: VideoSampleUnit[] = [];
|
90
|
-
let i = 0;
|
91
|
-
let value: number;
|
92
|
-
let overflow: number;
|
93
|
-
let unitType: number;
|
94
|
-
let lastUnitStart = -1;
|
95
|
-
let lastUnitType: number = 0;
|
96
|
-
// logger.log('PES:' + Hex.hexDump(array));
|
97
|
-
|
98
|
-
if (state === -1) {
|
99
|
-
// special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
|
100
|
-
lastUnitStart = 0;
|
101
|
-
// NALu type is value read from offset 0
|
102
|
-
lastUnitType = this.getNALuType(array, 0);
|
103
|
-
state = 0;
|
104
|
-
i = 1;
|
105
|
-
}
|
106
|
-
|
107
|
-
while (i < len) {
|
108
|
-
value = array[i++];
|
109
|
-
// optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
|
110
|
-
if (!state) {
|
111
|
-
state = value ? 0 : 1;
|
112
|
-
continue;
|
113
|
-
}
|
114
|
-
if (state === 1) {
|
115
|
-
state = value ? 0 : 2;
|
116
|
-
continue;
|
117
|
-
}
|
118
|
-
// here we have state either equal to 2 or 3
|
119
|
-
if (!value) {
|
120
|
-
state = 3;
|
121
|
-
} else if (value === 1) {
|
122
|
-
overflow = i - state - 1;
|
123
|
-
if (lastUnitStart >= 0) {
|
124
|
-
const unit: VideoSampleUnit = {
|
125
|
-
data: array.subarray(lastUnitStart, overflow),
|
126
|
-
type: lastUnitType,
|
127
|
-
};
|
128
|
-
// logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
|
129
|
-
units.push(unit);
|
130
|
-
} else {
|
131
|
-
// lastUnitStart is undefined => this is the first start code found in this PES packet
|
132
|
-
// first check if start code delimiter is overlapping between 2 PES packets,
|
133
|
-
// ie it started in last packet (lastState not zero)
|
134
|
-
// and ended at the beginning of this PES packet (i <= 4 - lastState)
|
135
|
-
const lastUnit = this.getLastNalUnit(track.samples);
|
136
|
-
if (lastUnit) {
|
137
|
-
if (lastState && i <= 4 - lastState) {
|
138
|
-
// start delimiter overlapping between PES packets
|
139
|
-
// strip start delimiter bytes from the end of last NAL unit
|
140
|
-
// check if lastUnit had a state different from zero
|
141
|
-
if (lastUnit.state) {
|
142
|
-
// strip last bytes
|
143
|
-
lastUnit.data = lastUnit.data.subarray(
|
144
|
-
0,
|
145
|
-
lastUnit.data.byteLength - lastState,
|
146
|
-
);
|
147
|
-
}
|
148
|
-
}
|
149
|
-
// If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
|
150
|
-
|
151
|
-
if (overflow > 0) {
|
152
|
-
// logger.log('first NALU found with overflow:' + overflow);
|
153
|
-
lastUnit.data = appendUint8Array(
|
154
|
-
lastUnit.data,
|
155
|
-
array.subarray(0, overflow),
|
156
|
-
);
|
157
|
-
lastUnit.state = 0;
|
158
|
-
}
|
159
|
-
}
|
160
|
-
}
|
161
|
-
// check if we can read unit type
|
162
|
-
if (i < len) {
|
163
|
-
unitType = this.getNALuType(array, i);
|
164
|
-
// logger.log('find NALU @ offset:' + i + ',type:' + unitType);
|
165
|
-
lastUnitStart = i;
|
166
|
-
lastUnitType = unitType;
|
167
|
-
state = 0;
|
168
|
-
} else {
|
169
|
-
// not enough byte to read unit type. let's read it on next PES parsing
|
170
|
-
state = -1;
|
171
|
-
}
|
172
|
-
} else {
|
173
|
-
state = 0;
|
174
|
-
}
|
175
|
-
}
|
176
|
-
if (lastUnitStart >= 0 && state >= 0) {
|
177
|
-
const unit: VideoSampleUnit = {
|
178
|
-
data: array.subarray(lastUnitStart, len),
|
179
|
-
type: lastUnitType,
|
180
|
-
state: state,
|
181
|
-
};
|
182
|
-
units.push(unit);
|
183
|
-
// logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
|
184
|
-
}
|
185
|
-
// no NALu found
|
186
|
-
if (units.length === 0) {
|
187
|
-
// append pes.data to previous NAL unit
|
188
|
-
const lastUnit = this.getLastNalUnit(track.samples);
|
189
|
-
if (lastUnit) {
|
190
|
-
lastUnit.data = appendUint8Array(lastUnit.data, array);
|
191
|
-
}
|
66
|
+
if (VideoSample.debug.length) {
|
67
|
+
logger.log(
|
68
|
+
VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug,
|
69
|
+
);
|
192
70
|
}
|
193
|
-
track.naluState = state;
|
194
|
-
return units;
|
195
71
|
}
|
196
72
|
}
|
197
73
|
|