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.
Files changed (109) hide show
  1. package/README.md +3 -4
  2. package/dist/hls-demo.js +38 -41
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +2911 -4558
  5. package/dist/hls.js.d.ts +112 -186
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +2291 -3311
  8. package/dist/hls.light.js.map +1 -1
  9. package/dist/hls.light.min.js +1 -1
  10. package/dist/hls.light.min.js.map +1 -1
  11. package/dist/hls.light.mjs +1813 -2835
  12. package/dist/hls.light.mjs.map +1 -1
  13. package/dist/hls.min.js +1 -1
  14. package/dist/hls.min.js.map +1 -1
  15. package/dist/hls.mjs +4707 -6356
  16. package/dist/hls.mjs.map +1 -1
  17. package/dist/hls.worker.js +1 -1
  18. package/dist/hls.worker.js.map +1 -1
  19. package/package.json +42 -42
  20. package/src/config.ts +2 -5
  21. package/src/controller/abr-controller.ts +25 -39
  22. package/src/controller/audio-stream-controller.ts +136 -156
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +10 -27
  25. package/src/controller/base-stream-controller.ts +107 -263
  26. package/src/controller/buffer-controller.ts +97 -250
  27. package/src/controller/buffer-operation-queue.ts +19 -16
  28. package/src/controller/cap-level-controller.ts +2 -3
  29. package/src/controller/cmcd-controller.ts +14 -51
  30. package/src/controller/content-steering-controller.ts +15 -29
  31. package/src/controller/eme-controller.ts +23 -10
  32. package/src/controller/error-controller.ts +22 -28
  33. package/src/controller/fps-controller.ts +3 -8
  34. package/src/controller/fragment-finders.ts +16 -44
  35. package/src/controller/fragment-tracker.ts +25 -58
  36. package/src/controller/gap-controller.ts +16 -43
  37. package/src/controller/id3-track-controller.ts +35 -45
  38. package/src/controller/latency-controller.ts +13 -18
  39. package/src/controller/level-controller.ts +19 -37
  40. package/src/controller/stream-controller.ts +83 -100
  41. package/src/controller/subtitle-stream-controller.ts +47 -35
  42. package/src/controller/subtitle-track-controller.ts +3 -5
  43. package/src/controller/timeline-controller.ts +22 -20
  44. package/src/crypt/aes-crypto.ts +2 -21
  45. package/src/crypt/decrypter.ts +16 -32
  46. package/src/crypt/fast-aes-key.ts +5 -28
  47. package/src/demux/audio/aacdemuxer.ts +5 -5
  48. package/src/demux/audio/ac3-demuxer.ts +4 -5
  49. package/src/demux/audio/adts.ts +4 -9
  50. package/src/demux/audio/base-audio-demuxer.ts +15 -21
  51. package/src/demux/audio/mp3demuxer.ts +3 -4
  52. package/src/demux/audio/mpegaudio.ts +1 -1
  53. package/src/demux/id3.ts +411 -0
  54. package/src/demux/inject-worker.ts +4 -38
  55. package/src/demux/mp4demuxer.ts +8 -17
  56. package/src/demux/sample-aes.ts +0 -2
  57. package/src/demux/transmuxer-interface.ts +83 -106
  58. package/src/demux/transmuxer-worker.ts +77 -111
  59. package/src/demux/transmuxer.ts +22 -46
  60. package/src/demux/tsdemuxer.ts +65 -131
  61. package/src/demux/video/avc-video-parser.ts +156 -219
  62. package/src/demux/video/base-video-parser.ts +9 -133
  63. package/src/demux/video/exp-golomb.ts +208 -0
  64. package/src/errors.ts +0 -2
  65. package/src/events.ts +1 -8
  66. package/src/exports-named.ts +1 -1
  67. package/src/hls.ts +48 -97
  68. package/src/loader/date-range.ts +5 -71
  69. package/src/loader/fragment-loader.ts +21 -23
  70. package/src/loader/fragment.ts +4 -8
  71. package/src/loader/key-loader.ts +1 -3
  72. package/src/loader/level-details.ts +6 -6
  73. package/src/loader/level-key.ts +9 -10
  74. package/src/loader/m3u8-parser.ts +144 -138
  75. package/src/loader/playlist-loader.ts +7 -5
  76. package/src/remux/mp4-generator.ts +1 -196
  77. package/src/remux/mp4-remuxer.ts +84 -55
  78. package/src/remux/passthrough-remuxer.ts +8 -23
  79. package/src/task-loop.ts +2 -5
  80. package/src/types/component-api.ts +1 -3
  81. package/src/types/demuxer.ts +1 -3
  82. package/src/types/events.ts +6 -19
  83. package/src/types/fragment-tracker.ts +2 -2
  84. package/src/types/general.ts +6 -0
  85. package/src/types/media-playlist.ts +1 -9
  86. package/src/types/remuxer.ts +1 -1
  87. package/src/utils/attr-list.ts +9 -96
  88. package/src/utils/buffer-helper.ts +31 -12
  89. package/src/utils/cea-608-parser.ts +3 -1
  90. package/src/utils/codecs.ts +5 -34
  91. package/src/utils/discontinuities.ts +47 -21
  92. package/src/utils/fetch-loader.ts +1 -1
  93. package/src/utils/hdr.ts +7 -4
  94. package/src/utils/imsc1-ttml-parser.ts +1 -1
  95. package/src/utils/keysystem-util.ts +6 -1
  96. package/src/utils/level-helper.ts +44 -71
  97. package/src/utils/logger.ts +23 -58
  98. package/src/utils/mp4-tools.ts +3 -5
  99. package/src/utils/rendition-helper.ts +74 -100
  100. package/src/utils/variable-substitution.ts +19 -0
  101. package/src/utils/webvtt-parser.ts +12 -2
  102. package/dist/hls.d.mts +0 -3179
  103. package/dist/hls.d.ts +0 -3179
  104. package/src/crypt/decrypter-aes-mode.ts +0 -4
  105. package/src/demux/video/hevc-video-parser.ts +0 -718
  106. package/src/utils/encryption-methods-util.ts +0 -21
  107. package/src/utils/hash.ts +0 -10
  108. package/src/utils/utf8-utils.ts +0 -18
  109. package/src/version.ts +0 -1
@@ -1,22 +1,26 @@
1
1
  import BaseVideoParser from './base-video-parser';
2
- import type {
2
+ import {
3
3
  DemuxedVideoTrack,
4
4
  DemuxedUserdataTrack,
5
+ VideoSampleUnit,
5
6
  } from '../../types/demuxer';
6
- import { parseSEIMessageFromNALu } from '../../utils/mp4-tools';
7
-
8
- import type { PES } from '../tsdemuxer';
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 parsePES(
15
+ public parseAVCPES(
14
16
  track: DemuxedVideoTrack,
15
17
  textTrack: DemuxedUserdataTrack,
16
18
  pes: PES,
17
- endOfSegment: boolean,
19
+ last: boolean,
20
+ duration: number,
18
21
  ) {
19
- const units = this.parseNALu(track, pes.data, endOfSegment);
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 = this.readSliceType(data);
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 config = this.readSPS(sps);
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?.frame) {
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 (endOfSegment && VideoSample) {
214
+ if (last && VideoSample) {
184
215
  this.pushAccessUnit(VideoSample, track);
185
216
  this.VideoSample = null;
186
217
  }
187
218
  }
188
219
 
189
- protected getNALuType(data: Uint8Array, offset: number): number {
190
- return data[offset] & 0x1f;
191
- }
192
-
193
- readSliceType(data: Uint8Array) {
194
- const eg = new ExpGolomb(data);
195
- // skip NALu type
196
- eg.readUByte();
197
- // discard first_mb_in_slice
198
- eg.readUEG();
199
- // return slice_type
200
- return eg.readUEG();
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
- * The scaling list is optionally transmitted as part of a sequence parameter
205
- * set and is not relevant to transmuxing.
206
- * @param count the number of entries in this scaling list
207
- * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
208
- */
209
- skipScalingList(count: number, reader: ExpGolomb): void {
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
- readUByte();
253
- const profileIdc = readUByte(); // profile_idc
254
- readBits(5); // profileCompat constraint_set[0-4]_flag, u(5)
255
- skipBits(3); // reserved_zero_3bits u(3),
256
- readUByte(); // level_idc u(8)
257
- skipUEG(); // seq_parameter_set_id
258
- // some profiles have more optional data we don't need
259
- if (
260
- profileIdc === 100 ||
261
- profileIdc === 110 ||
262
- profileIdc === 122 ||
263
- profileIdc === 244 ||
264
- profileIdc === 44 ||
265
- profileIdc === 83 ||
266
- profileIdc === 86 ||
267
- profileIdc === 118 ||
268
- profileIdc === 128
269
- ) {
270
- const chromaFormatIdc = readUEG();
271
- if (chromaFormatIdc === 3) {
272
- skipBits(1);
273
- } // separate_colour_plane_flag
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
- skipUEG(); // bit_depth_luma_minus8
276
- skipUEG(); // bit_depth_chroma_minus8
277
- skipBits(1); // qpprime_y_zero_transform_bypass_flag
278
- if (readBoolean()) {
279
- // seq_scaling_matrix_present_flag
280
- scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
281
- for (i = 0; i < scalingListCount; i++) {
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
- skipUEG(); // log2_max_frame_num_minus4
294
- const picOrderCntType = readUEG();
295
- if (picOrderCntType === 0) {
296
- readUEG(); // log2_max_pic_order_cnt_lsb_minus4
297
- } else if (picOrderCntType === 1) {
298
- skipBits(1); // delta_pic_order_always_zero_flag
299
- skipEG(); // offset_for_non_ref_pic
300
- skipEG(); // offset_for_top_to_bottom_field
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
- skipUEG(); // max_num_ref_frames
307
- skipBits(1); // gaps_in_frame_num_value_allowed_flag
308
- const picWidthInMbsMinus1 = readUEG();
309
- const picHeightInMapUnitsMinus1 = readUEG();
310
- const frameMbsOnlyFlag = readBits(1);
311
- if (frameMbsOnlyFlag === 0) {
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
- return {
389
- width: Math.ceil(
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 type {
2
+ import {
3
3
  DemuxedVideoTrack,
4
- DemuxedUserdataTrack,
5
4
  VideoSample,
6
5
  VideoSampleUnit,
7
6
  } from '../../types/demuxer';
8
- import type { PES } from '../tsdemuxer';
9
- import { appendUint8Array } from '../../utils/mp4-tools';
7
+ import { logger } from '../../utils/logger';
10
8
 
11
- abstract class BaseVideoParser {
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
- abstract parsePES(
69
- track: DemuxedVideoTrack,
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