agdi 3.3.6 → 3.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +13 -0
  2. package/dist/APEv2Parser-EU45AV6X.js +14 -0
  3. package/dist/AiffParser-FOX7GQ42.js +189 -0
  4. package/dist/AsfParser-HD5CSGIO.js +610 -0
  5. package/dist/DsdiffParser-OJREDMBI.js +188 -0
  6. package/dist/DsfParser-2YL4ARJ7.js +110 -0
  7. package/dist/FlacParser-IVV4RYF6.js +15 -0
  8. package/dist/MP4Parser-NLX4A2YN.js +1140 -0
  9. package/dist/MatroskaParser-4OEK43GF.js +654 -0
  10. package/dist/MpegParser-PXKEUF2B.js +642 -0
  11. package/dist/MusepackParser-54QGYRLY.js +322 -0
  12. package/dist/OggParser-KRV5QCGZ.js +435 -0
  13. package/dist/WavPackParser-XPZSQFVS.js +203 -0
  14. package/dist/WaveParser-27IS2RAI.js +294 -0
  15. package/dist/chunk-2B4QMSZW.js +311 -0
  16. package/dist/chunk-3JKZUGPJ.js +70 -0
  17. package/dist/chunk-4VNS5WPM.js +42 -0
  18. package/dist/chunk-65JVFJ3X.js +729 -0
  19. package/dist/chunk-6OKLAJRQ.js +0 -0
  20. package/dist/chunk-AGSFUVRG.js +439 -0
  21. package/dist/chunk-GD35BJSH.js +177 -0
  22. package/dist/chunk-HNLU36CC.js +702 -0
  23. package/dist/{chunk-M2FF7ETI.js → chunk-J6OLLWVT.js} +1 -1
  24. package/dist/chunk-LREP5CZP.js +146 -0
  25. package/dist/chunk-M54HVABG.js +34 -0
  26. package/dist/{chunk-S45VXJEO.js → chunk-OPFFFAQC.js} +19 -1
  27. package/dist/chunk-VGOIHW7D.js +1529 -0
  28. package/dist/chunk-YIHDW7JC.js +314 -0
  29. package/dist/config-D3QBUN2Y.js +13 -0
  30. package/dist/{config-ZFU7TSU2.js → config-K2XM6D4Z.js} +3 -2
  31. package/dist/{event-bus-Q3WCETQQ.js → event-bus-MO5SFUME.js} +1 -0
  32. package/dist/index.js +3966 -1203
  33. package/dist/lib-2XISBYT3.js +144950 -0
  34. package/dist/lib-HCGLI2GJ.js +4161 -0
  35. package/dist/{telemetry-service-OHU5NKON.js → telemetry-service-76YPOPDM.js} +8 -4
  36. package/package.json +5 -2
@@ -0,0 +1,1140 @@
1
+ import "./chunk-6OKLAJRQ.js";
2
+ import {
3
+ TrackType
4
+ } from "./chunk-M54HVABG.js";
5
+ import {
6
+ Genres
7
+ } from "./chunk-2B4QMSZW.js";
8
+ import {
9
+ FourCcToken
10
+ } from "./chunk-YIHDW7JC.js";
11
+ import "./chunk-HNLU36CC.js";
12
+ import "./chunk-LREP5CZP.js";
13
+ import {
14
+ getBit,
15
+ uint8ArrayToHex
16
+ } from "./chunk-GD35BJSH.js";
17
+ import {
18
+ BasicParser,
19
+ INT16_BE,
20
+ INT24_BE,
21
+ INT32_BE,
22
+ INT8,
23
+ StringType,
24
+ UINT16_BE,
25
+ UINT24_BE,
26
+ UINT32_BE,
27
+ UINT64_BE,
28
+ UINT8,
29
+ Uint8ArrayType,
30
+ lib_exports,
31
+ makeUnexpectedFileContentError,
32
+ require_src,
33
+ textDecode
34
+ } from "./chunk-VGOIHW7D.js";
35
+ import {
36
+ __toESM
37
+ } from "./chunk-4VNS5WPM.js";
38
+
39
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mp4/MP4Parser.js
40
+ var import_debug3 = __toESM(require_src(), 1);
41
+
42
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mp4/Atom.js
43
+ var import_debug2 = __toESM(require_src(), 1);
44
+
45
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mp4/AtomToken.js
46
+ var import_debug = __toESM(require_src(), 1);
47
+ var debug = (0, import_debug.default)("music-metadata:parser:MP4:atom");
48
+ var Mp4ContentError = class extends makeUnexpectedFileContentError("MP4") {
49
+ };
50
+ var Header = {
51
+ len: 8,
52
+ get: (buf, off) => {
53
+ const length = UINT32_BE.get(buf, off);
54
+ if (length < 0)
55
+ throw new Mp4ContentError("Invalid atom header length");
56
+ return {
57
+ length: BigInt(length),
58
+ name: new StringType(4, "latin1").get(buf, off + 4)
59
+ };
60
+ },
61
+ put: (buf, off, hdr) => {
62
+ UINT32_BE.put(buf, off, Number(hdr.length));
63
+ return FourCcToken.put(buf, off + 4, hdr.name);
64
+ }
65
+ };
66
+ var ExtendedSize = UINT64_BE;
67
+ var ftyp = {
68
+ len: 4,
69
+ get: (buf, off) => {
70
+ return {
71
+ type: new StringType(4, "ascii").get(buf, off)
72
+ };
73
+ }
74
+ };
75
+ var FixedLengthAtom = class {
76
+ /**
77
+ *
78
+ * @param {number} len Length as specified in the size field
79
+ * @param {number} expLen Total length of sum of specified fields in the standard
80
+ * @param atomId Atom ID
81
+ */
82
+ constructor(len, expLen, atomId) {
83
+ if (len < expLen) {
84
+ throw new Mp4ContentError(`Atom ${atomId} expected to be ${expLen}, but specifies ${len} bytes long.`);
85
+ }
86
+ if (len > expLen) {
87
+ debug(`Warning: atom ${atomId} expected to be ${expLen}, but was actually ${len} bytes long.`);
88
+ }
89
+ this.len = len;
90
+ }
91
+ };
92
+ var SecondsSinceMacEpoch = {
93
+ len: 4,
94
+ get: (buf, off) => {
95
+ const secondsSinceUnixEpoch = UINT32_BE.get(buf, off) - 2082844800;
96
+ return new Date(secondsSinceUnixEpoch * 1e3);
97
+ }
98
+ };
99
+ var MdhdAtom = class extends FixedLengthAtom {
100
+ constructor(len) {
101
+ super(len, 24, "mdhd");
102
+ }
103
+ get(buf, off) {
104
+ return {
105
+ version: UINT8.get(buf, off + 0),
106
+ flags: UINT24_BE.get(buf, off + 1),
107
+ creationTime: SecondsSinceMacEpoch.get(buf, off + 4),
108
+ modificationTime: SecondsSinceMacEpoch.get(buf, off + 8),
109
+ timeScale: UINT32_BE.get(buf, off + 12),
110
+ duration: UINT32_BE.get(buf, off + 16),
111
+ language: UINT16_BE.get(buf, off + 20),
112
+ quality: UINT16_BE.get(buf, off + 22)
113
+ };
114
+ }
115
+ };
116
+ var MvhdAtom = class extends FixedLengthAtom {
117
+ constructor(len) {
118
+ super(len, 100, "mvhd");
119
+ }
120
+ get(buf, off) {
121
+ return {
122
+ version: UINT8.get(buf, off),
123
+ flags: UINT24_BE.get(buf, off + 1),
124
+ creationTime: SecondsSinceMacEpoch.get(buf, off + 4),
125
+ modificationTime: SecondsSinceMacEpoch.get(buf, off + 8),
126
+ timeScale: UINT32_BE.get(buf, off + 12),
127
+ duration: UINT32_BE.get(buf, off + 16),
128
+ preferredRate: UINT32_BE.get(buf, off + 20),
129
+ preferredVolume: UINT16_BE.get(buf, off + 24),
130
+ // ignore reserver: 10 bytes
131
+ // ignore matrix structure: 36 bytes
132
+ previewTime: UINT32_BE.get(buf, off + 72),
133
+ previewDuration: UINT32_BE.get(buf, off + 76),
134
+ posterTime: UINT32_BE.get(buf, off + 80),
135
+ selectionTime: UINT32_BE.get(buf, off + 84),
136
+ selectionDuration: UINT32_BE.get(buf, off + 88),
137
+ currentTime: UINT32_BE.get(buf, off + 92),
138
+ nextTrackID: UINT32_BE.get(buf, off + 96)
139
+ };
140
+ }
141
+ };
142
+ var DataAtom = class {
143
+ constructor(len) {
144
+ this.len = len;
145
+ }
146
+ get(buf, off) {
147
+ return {
148
+ type: {
149
+ set: UINT8.get(buf, off + 0),
150
+ type: UINT24_BE.get(buf, off + 1)
151
+ },
152
+ locale: UINT24_BE.get(buf, off + 4),
153
+ value: new Uint8ArrayType(this.len - 8).get(buf, off + 8)
154
+ };
155
+ }
156
+ };
157
+ var NameAtom = class {
158
+ constructor(len) {
159
+ this.len = len;
160
+ }
161
+ get(buf, off) {
162
+ return {
163
+ version: UINT8.get(buf, off),
164
+ flags: UINT24_BE.get(buf, off + 1),
165
+ name: new StringType(this.len - 4, "utf-8").get(buf, off + 4)
166
+ };
167
+ }
168
+ };
169
+ var TrackHeaderAtom = class {
170
+ constructor(len) {
171
+ this.len = len;
172
+ }
173
+ get(buf, off) {
174
+ return {
175
+ version: UINT8.get(buf, off),
176
+ flags: UINT24_BE.get(buf, off + 1),
177
+ creationTime: SecondsSinceMacEpoch.get(buf, off + 4),
178
+ modificationTime: SecondsSinceMacEpoch.get(buf, off + 8),
179
+ trackId: UINT32_BE.get(buf, off + 12),
180
+ // reserved 4 bytes
181
+ duration: UINT32_BE.get(buf, off + 20),
182
+ layer: UINT16_BE.get(buf, off + 24),
183
+ alternateGroup: UINT16_BE.get(buf, off + 26),
184
+ volume: UINT16_BE.get(buf, off + 28)
185
+ // ToDo: fixed point
186
+ // ToDo: add remaining fields
187
+ };
188
+ }
189
+ };
190
+ var stsdHeader = {
191
+ len: 8,
192
+ get: (buf, off) => {
193
+ return {
194
+ version: UINT8.get(buf, off),
195
+ flags: UINT24_BE.get(buf, off + 1),
196
+ numberOfEntries: UINT32_BE.get(buf, off + 4)
197
+ };
198
+ }
199
+ };
200
+ var SampleDescriptionTable = class {
201
+ constructor(len) {
202
+ this.len = len;
203
+ }
204
+ get(buf, off) {
205
+ const descrLen = this.len - 12;
206
+ return {
207
+ dataFormat: FourCcToken.get(buf, off),
208
+ dataReferenceIndex: UINT16_BE.get(buf, off + 10),
209
+ description: descrLen > 0 ? new Uint8ArrayType(descrLen).get(buf, off + 12) : void 0
210
+ };
211
+ }
212
+ };
213
+ var StsdAtom = class {
214
+ constructor(len) {
215
+ this.len = len;
216
+ }
217
+ get(buf, off) {
218
+ const header = stsdHeader.get(buf, off);
219
+ off += stsdHeader.len;
220
+ const table = [];
221
+ for (let n = 0; n < header.numberOfEntries; ++n) {
222
+ const size = UINT32_BE.get(buf, off);
223
+ off += UINT32_BE.len;
224
+ table.push(new SampleDescriptionTable(size - UINT32_BE.len).get(buf, off));
225
+ off += size;
226
+ }
227
+ return {
228
+ header,
229
+ table
230
+ };
231
+ }
232
+ };
233
+ var SoundSampleDescriptionVersion = {
234
+ len: 8,
235
+ get(buf, off) {
236
+ return {
237
+ version: INT16_BE.get(buf, off),
238
+ revision: INT16_BE.get(buf, off + 2),
239
+ vendor: INT32_BE.get(buf, off + 4)
240
+ };
241
+ }
242
+ };
243
+ var SoundSampleDescriptionV0 = {
244
+ len: 12,
245
+ get(buf, off) {
246
+ return {
247
+ numAudioChannels: INT16_BE.get(buf, off + 0),
248
+ sampleSize: INT16_BE.get(buf, off + 2),
249
+ compressionId: INT16_BE.get(buf, off + 4),
250
+ packetSize: INT16_BE.get(buf, off + 6),
251
+ sampleRate: UINT16_BE.get(buf, off + 8) + UINT16_BE.get(buf, off + 10) / 1e4
252
+ };
253
+ }
254
+ };
255
+ var SimpleTableAtom = class {
256
+ constructor(len, token) {
257
+ this.len = len;
258
+ this.token = token;
259
+ }
260
+ get(buf, off) {
261
+ const nrOfEntries = INT32_BE.get(buf, off + 4);
262
+ return {
263
+ version: INT8.get(buf, off + 0),
264
+ flags: INT24_BE.get(buf, off + 1),
265
+ numberOfEntries: nrOfEntries,
266
+ entries: readTokenTable(buf, this.token, off + 8, this.len - 8, nrOfEntries)
267
+ };
268
+ }
269
+ };
270
+ var TimeToSampleToken = {
271
+ len: 8,
272
+ get(buf, off) {
273
+ return {
274
+ count: INT32_BE.get(buf, off + 0),
275
+ duration: INT32_BE.get(buf, off + 4)
276
+ };
277
+ }
278
+ };
279
+ var SttsAtom = class extends SimpleTableAtom {
280
+ constructor(len) {
281
+ super(len, TimeToSampleToken);
282
+ }
283
+ };
284
+ var SampleToChunkToken = {
285
+ len: 12,
286
+ get(buf, off) {
287
+ return {
288
+ firstChunk: INT32_BE.get(buf, off),
289
+ samplesPerChunk: INT32_BE.get(buf, off + 4),
290
+ sampleDescriptionId: INT32_BE.get(buf, off + 8)
291
+ };
292
+ }
293
+ };
294
+ var StscAtom = class extends SimpleTableAtom {
295
+ constructor(len) {
296
+ super(len, SampleToChunkToken);
297
+ }
298
+ };
299
+ var StszAtom = class {
300
+ constructor(len) {
301
+ this.len = len;
302
+ }
303
+ get(buf, off) {
304
+ const nrOfEntries = INT32_BE.get(buf, off + 8);
305
+ return {
306
+ version: INT8.get(buf, off),
307
+ flags: INT24_BE.get(buf, off + 1),
308
+ sampleSize: INT32_BE.get(buf, off + 4),
309
+ numberOfEntries: nrOfEntries,
310
+ entries: readTokenTable(buf, INT32_BE, off + 12, this.len - 12, nrOfEntries)
311
+ };
312
+ }
313
+ };
314
+ var StcoAtom = class extends SimpleTableAtom {
315
+ constructor(len) {
316
+ super(len, INT32_BE);
317
+ this.len = len;
318
+ }
319
+ };
320
+ var ChapterText = class {
321
+ constructor(len) {
322
+ this.len = len;
323
+ }
324
+ get(buf, off) {
325
+ const titleLen = INT16_BE.get(buf, off + 0);
326
+ const str = new StringType(titleLen, "utf-8");
327
+ return str.get(buf, off + 2);
328
+ }
329
+ };
330
+ function readTokenTable(buf, token, off, remainingLen, numberOfEntries) {
331
+ debug(`remainingLen=${remainingLen}, numberOfEntries=${numberOfEntries} * token-len=${token.len}`);
332
+ if (remainingLen === 0)
333
+ return [];
334
+ if (remainingLen !== numberOfEntries * token.len)
335
+ throw new Mp4ContentError("mismatch number-of-entries with remaining atom-length");
336
+ const entries = [];
337
+ for (let n = 0; n < numberOfEntries; ++n) {
338
+ entries.push(token.get(buf, off));
339
+ off += token.len;
340
+ }
341
+ return entries;
342
+ }
343
+ var TrackFragmentHeaderBox = class {
344
+ constructor(len) {
345
+ this.len = len;
346
+ }
347
+ get(buf, off) {
348
+ const flagOffset = off + 1;
349
+ const header = {
350
+ version: INT8.get(buf, off),
351
+ flags: {
352
+ baseDataOffsetPresent: getBit(buf, flagOffset + 2, 0),
353
+ sampleDescriptionIndexPresent: getBit(buf, flagOffset + 2, 1),
354
+ defaultSampleDurationPresent: getBit(buf, flagOffset + 2, 3),
355
+ defaultSampleSizePresent: getBit(buf, flagOffset + 2, 4),
356
+ defaultSampleFlagsPresent: getBit(buf, flagOffset + 2, 5),
357
+ defaultDurationIsEmpty: getBit(buf, flagOffset, 0),
358
+ defaultBaseIsMoof: getBit(buf, flagOffset, 1)
359
+ },
360
+ trackId: UINT32_BE.get(buf, 4)
361
+ };
362
+ let dynOffset = 8;
363
+ if (header.flags.baseDataOffsetPresent) {
364
+ header.baseDataOffset = UINT64_BE.get(buf, dynOffset);
365
+ dynOffset += 8;
366
+ }
367
+ if (header.flags.sampleDescriptionIndexPresent) {
368
+ header.sampleDescriptionIndex = UINT32_BE.get(buf, dynOffset);
369
+ dynOffset += 4;
370
+ }
371
+ if (header.flags.defaultSampleDurationPresent) {
372
+ header.defaultSampleDuration = UINT32_BE.get(buf, dynOffset);
373
+ dynOffset += 4;
374
+ }
375
+ if (header.flags.defaultSampleSizePresent) {
376
+ header.defaultSampleSize = UINT32_BE.get(buf, dynOffset);
377
+ dynOffset += 4;
378
+ }
379
+ if (header.flags.defaultSampleFlagsPresent) {
380
+ header.defaultSampleFlags = UINT32_BE.get(buf, dynOffset);
381
+ }
382
+ return header;
383
+ }
384
+ };
385
+ var TrackRunBox = class {
386
+ constructor(len) {
387
+ this.len = len;
388
+ }
389
+ get(buf, off) {
390
+ const flagOffset = off + 1;
391
+ const trun = {
392
+ version: INT8.get(buf, off),
393
+ flags: {
394
+ dataOffsetPresent: getBit(buf, flagOffset + 2, 0),
395
+ firstSampleFlagsPresent: getBit(buf, flagOffset + 2, 2),
396
+ sampleDurationPresent: getBit(buf, flagOffset + 1, 0),
397
+ sampleSizePresent: getBit(buf, flagOffset + 1, 1),
398
+ sampleFlagsPresent: getBit(buf, flagOffset + 1, 2),
399
+ sampleCompositionTimeOffsetsPresent: getBit(buf, flagOffset + 1, 3)
400
+ },
401
+ sampleCount: UINT32_BE.get(buf, off + 4),
402
+ samples: []
403
+ };
404
+ let dynOffset = off + 8;
405
+ if (trun.flags.dataOffsetPresent) {
406
+ trun.dataOffset = UINT32_BE.get(buf, dynOffset);
407
+ dynOffset += 4;
408
+ }
409
+ if (trun.flags.firstSampleFlagsPresent) {
410
+ trun.firstSampleFlags = UINT32_BE.get(buf, dynOffset);
411
+ dynOffset += 4;
412
+ }
413
+ for (let n = 0; n < trun.sampleCount; ++n) {
414
+ if (dynOffset >= this.len) {
415
+ debug("TrackRunBox size mismatch");
416
+ break;
417
+ }
418
+ const sample = {};
419
+ if (trun.flags.sampleDurationPresent) {
420
+ sample.sampleDuration = UINT32_BE.get(buf, dynOffset);
421
+ dynOffset += 4;
422
+ }
423
+ if (trun.flags.sampleSizePresent) {
424
+ sample.sampleSize = UINT32_BE.get(buf, dynOffset);
425
+ dynOffset += 4;
426
+ }
427
+ if (trun.flags.sampleFlagsPresent) {
428
+ sample.sampleFlags = UINT32_BE.get(buf, dynOffset);
429
+ dynOffset += 4;
430
+ }
431
+ if (trun.flags.sampleCompositionTimeOffsetsPresent) {
432
+ sample.sampleCompositionTimeOffset = UINT32_BE.get(buf, dynOffset);
433
+ dynOffset += 4;
434
+ }
435
+ trun.samples.push(sample);
436
+ }
437
+ return trun;
438
+ }
439
+ };
440
+ var HandlerBox = class {
441
+ constructor(len) {
442
+ this.len = len;
443
+ }
444
+ get(buf, off) {
445
+ const _flagOffset = off + 1;
446
+ const charTypeToken = new StringType(4, "utf-8");
447
+ return {
448
+ version: INT8.get(buf, off),
449
+ flags: UINT24_BE.get(buf, off + 1),
450
+ componentType: charTypeToken.get(buf, off + 4),
451
+ handlerType: charTypeToken.get(buf, off + 8),
452
+ componentName: new StringType(this.len - 28, "utf-8").get(buf, off + 28)
453
+ };
454
+ }
455
+ };
456
+ var ChapterTrackReferenceBox = class {
457
+ constructor(len) {
458
+ this.len = len;
459
+ }
460
+ get(buf, off) {
461
+ let dynOffset = 0;
462
+ const trackIds = [];
463
+ while (dynOffset < this.len) {
464
+ trackIds.push(UINT32_BE.get(buf, off + dynOffset));
465
+ dynOffset += 4;
466
+ }
467
+ return trackIds;
468
+ }
469
+ };
470
+
471
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mp4/Atom.js
472
+ var debug2 = (0, import_debug2.default)("music-metadata:parser:MP4:Atom");
473
+ var Atom = class _Atom {
474
+ static async readAtom(tokenizer, dataHandler, parent, remaining) {
475
+ const offset = tokenizer.position;
476
+ debug2(`Reading next token on offset=${offset}...`);
477
+ const header = await tokenizer.readToken(Header);
478
+ const extended = header.length === 1n;
479
+ if (extended) {
480
+ header.length = await tokenizer.readToken(ExtendedSize);
481
+ }
482
+ const atomBean = new _Atom(header, extended, parent);
483
+ const payloadLength = atomBean.getPayloadLength(remaining);
484
+ debug2(`parse atom name=${atomBean.atomPath}, extended=${atomBean.extended}, offset=${offset}, len=${atomBean.header.length}`);
485
+ await atomBean.readData(tokenizer, dataHandler, payloadLength);
486
+ return atomBean;
487
+ }
488
+ constructor(header, extended, parent) {
489
+ this.header = header;
490
+ this.extended = extended;
491
+ this.parent = parent;
492
+ this.children = [];
493
+ this.atomPath = (this.parent ? `${this.parent.atomPath}.` : "") + this.header.name;
494
+ }
495
+ getHeaderLength() {
496
+ return this.extended ? 16 : 8;
497
+ }
498
+ getPayloadLength(remaining) {
499
+ return (this.header.length === 0n ? remaining : Number(this.header.length)) - this.getHeaderLength();
500
+ }
501
+ async readAtoms(tokenizer, dataHandler, size) {
502
+ while (size > 0) {
503
+ const atomBean = await _Atom.readAtom(tokenizer, dataHandler, this, size);
504
+ this.children.push(atomBean);
505
+ size -= atomBean.header.length === 0n ? size : Number(atomBean.header.length);
506
+ }
507
+ }
508
+ async readData(tokenizer, dataHandler, remaining) {
509
+ switch (this.header.name) {
510
+ // "Container" atoms, contains nested atoms
511
+ case "moov":
512
+ // The Movie Atom: contains other atoms
513
+ case "udta":
514
+ // User defined atom
515
+ case "mdia":
516
+ // Media atom
517
+ case "minf":
518
+ // Media Information Atom
519
+ case "stbl":
520
+ // The Sample Table Atom
521
+ case "<id>":
522
+ case "ilst":
523
+ case "tref":
524
+ case "moof":
525
+ return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining));
526
+ case "meta": {
527
+ const peekHeader = await tokenizer.peekToken(Header);
528
+ const paddingLength = peekHeader.name === "hdlr" ? 0 : 4;
529
+ await tokenizer.ignore(paddingLength);
530
+ return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining) - paddingLength);
531
+ }
532
+ default:
533
+ return dataHandler(this, remaining);
534
+ }
535
+ }
536
+ };
537
+
538
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mp4/MP4Parser.js
539
+ var debug3 = (0, import_debug3.default)("music-metadata:parser:MP4");
540
+ var tagFormat = "iTunes";
541
+ var encoderDict = {
542
+ raw: {
543
+ lossy: false,
544
+ format: "raw"
545
+ },
546
+ MAC3: {
547
+ lossy: true,
548
+ format: "MACE 3:1"
549
+ },
550
+ MAC6: {
551
+ lossy: true,
552
+ format: "MACE 6:1"
553
+ },
554
+ ima4: {
555
+ lossy: true,
556
+ format: "IMA 4:1"
557
+ },
558
+ ulaw: {
559
+ lossy: true,
560
+ format: "uLaw 2:1"
561
+ },
562
+ alaw: {
563
+ lossy: true,
564
+ format: "uLaw 2:1"
565
+ },
566
+ Qclp: {
567
+ lossy: true,
568
+ format: "QUALCOMM PureVoice"
569
+ },
570
+ ".mp3": {
571
+ lossy: true,
572
+ format: "MPEG-1 layer 3"
573
+ },
574
+ alac: {
575
+ lossy: false,
576
+ format: "ALAC"
577
+ },
578
+ "ac-3": {
579
+ lossy: true,
580
+ format: "AC-3"
581
+ },
582
+ mp4a: {
583
+ lossy: true,
584
+ format: "MPEG-4/AAC"
585
+ },
586
+ mp4s: {
587
+ lossy: true,
588
+ format: "MP4S"
589
+ },
590
+ // Closed Captioning Media, https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW87
591
+ c608: {
592
+ lossy: true,
593
+ format: "CEA-608"
594
+ },
595
+ c708: {
596
+ lossy: true,
597
+ format: "CEA-708"
598
+ }
599
+ };
600
+ function distinct(value, index, self) {
601
+ return self.indexOf(value) === index;
602
+ }
603
+ var MP4Parser = class _MP4Parser extends BasicParser {
604
+ constructor() {
605
+ super(...arguments);
606
+ this.tracks = /* @__PURE__ */ new Map();
607
+ this.hasVideoTrack = false;
608
+ this.hasAudioTrack = true;
609
+ this.atomParsers = {
610
+ /**
611
+ * Parse movie header (mvhd) atom
612
+ * Ref: https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-56313
613
+ */
614
+ mvhd: async (len) => {
615
+ const mvhd = await this.tokenizer.readToken(new MvhdAtom(len));
616
+ this.metadata.setFormat("creationTime", mvhd.creationTime);
617
+ this.metadata.setFormat("modificationTime", mvhd.modificationTime);
618
+ },
619
+ chap: async (len) => {
620
+ const td = this.getTrackDescription();
621
+ const trackIds = [];
622
+ while (len >= UINT32_BE.len) {
623
+ trackIds.push(await this.tokenizer.readNumber(UINT32_BE));
624
+ len -= UINT32_BE.len;
625
+ }
626
+ td.chapterList = trackIds;
627
+ },
628
+ /**
629
+ * Parse mdat atom.
630
+ * Will scan for chapters
631
+ */
632
+ mdat: async (len) => {
633
+ if (this.options.includeChapters) {
634
+ const trackWithChapters = [...this.tracks.values()].filter((track) => track.chapterList);
635
+ if (trackWithChapters.length === 1) {
636
+ const chapterTrackIds = trackWithChapters[0].chapterList;
637
+ const chapterTracks = [...this.tracks.values()].filter((track) => chapterTrackIds.indexOf(track.header.trackId) !== -1);
638
+ if (chapterTracks.length === 1) {
639
+ return this.parseChapterTrack(chapterTracks[0], trackWithChapters[0], len);
640
+ }
641
+ }
642
+ }
643
+ await this.tokenizer.ignore(len);
644
+ },
645
+ ftyp: async (len) => {
646
+ const types = [];
647
+ while (len > 0) {
648
+ const ftype = await this.tokenizer.readToken(ftyp);
649
+ len -= ftyp.len;
650
+ const value = ftype.type.replace(/\W/g, "");
651
+ if (value.length > 0) {
652
+ types.push(value);
653
+ }
654
+ }
655
+ debug3(`ftyp: ${types.join("/")}`);
656
+ const x = types.filter(distinct).join("/");
657
+ this.metadata.setFormat("container", x);
658
+ },
659
+ /**
660
+ * Parse sample description atom
661
+ */
662
+ stsd: async (len) => {
663
+ const stsd = await this.tokenizer.readToken(new StsdAtom(len));
664
+ const trackDescription = this.getTrackDescription();
665
+ trackDescription.soundSampleDescription = stsd.table.map((dfEntry) => this.parseSoundSampleDescription(dfEntry));
666
+ },
667
+ /**
668
+ * Parse sample-sizes atom ('stsz')
669
+ */
670
+ stsz: async (len) => {
671
+ const stsz = await this.tokenizer.readToken(new StszAtom(len));
672
+ const td = this.getTrackDescription();
673
+ td.sampleSize = stsz.sampleSize;
674
+ td.sampleSizeTable = stsz.entries;
675
+ },
676
+ date: async (len) => {
677
+ const date = await this.tokenizer.readToken(new StringType(len, "utf-8"));
678
+ await this.addTag("date", date);
679
+ }
680
+ };
681
+ }
682
+ static read_BE_Integer(array, signed) {
683
+ const integerType = (signed ? "INT" : "UINT") + array.length * 8 + (array.length > 1 ? "_BE" : "");
684
+ const token = lib_exports[integerType];
685
+ if (!token) {
686
+ throw new Mp4ContentError(`Token for integer type not found: "${integerType}"`);
687
+ }
688
+ return Number(token.get(array, 0));
689
+ }
690
+ async parse() {
691
+ this.hasVideoTrack = false;
692
+ this.hasAudioTrack = true;
693
+ this.tracks.clear();
694
+ let remainingFileSize = this.tokenizer.fileInfo.size || 0;
695
+ while (!this.tokenizer.fileInfo.size || remainingFileSize > 0) {
696
+ try {
697
+ const token = await this.tokenizer.peekToken(Header);
698
+ if (token.name === "\0\0\0\0") {
699
+ const errMsg = `Error at offset=${this.tokenizer.position}: box.id=0`;
700
+ debug3(errMsg);
701
+ this.addWarning(errMsg);
702
+ break;
703
+ }
704
+ } catch (error) {
705
+ if (error instanceof Error) {
706
+ const errMsg = `Error at offset=${this.tokenizer.position}: ${error.message}`;
707
+ debug3(errMsg);
708
+ this.addWarning(errMsg);
709
+ } else
710
+ throw error;
711
+ break;
712
+ }
713
+ const rootAtom = await Atom.readAtom(this.tokenizer, (atom, remaining) => this.handleAtom(atom, remaining), null, remainingFileSize);
714
+ remainingFileSize -= rootAtom.header.length === BigInt(0) ? remainingFileSize : Number(rootAtom.header.length);
715
+ }
716
+ const formatList = [];
717
+ this.tracks.forEach((track) => {
718
+ const trackFormats = [];
719
+ track.soundSampleDescription.forEach((ssd) => {
720
+ const streamInfo = {};
721
+ const encoderInfo = encoderDict[ssd.dataFormat];
722
+ if (encoderInfo) {
723
+ trackFormats.push(encoderInfo.format);
724
+ streamInfo.codecName = encoderInfo.format;
725
+ } else {
726
+ streamInfo.codecName = `<${ssd.dataFormat}>`;
727
+ }
728
+ if (ssd.description) {
729
+ const { description } = ssd;
730
+ if (description.sampleRate > 0) {
731
+ streamInfo.type = TrackType.audio;
732
+ streamInfo.audio = {
733
+ samplingFrequency: description.sampleRate,
734
+ bitDepth: description.sampleSize,
735
+ channels: description.numAudioChannels
736
+ };
737
+ }
738
+ }
739
+ this.metadata.addStreamInfo(streamInfo);
740
+ });
741
+ if (trackFormats.length >= 1) {
742
+ formatList.push(trackFormats.join("/"));
743
+ }
744
+ });
745
+ if (formatList.length > 0) {
746
+ this.metadata.setFormat("codec", formatList.filter(distinct).join("+"));
747
+ }
748
+ const audioTracks = [...this.tracks.values()].filter((track) => {
749
+ return track.soundSampleDescription.length >= 1 && track.soundSampleDescription[0].description && track.soundSampleDescription[0].description.numAudioChannels > 0;
750
+ });
751
+ for (const audioTrack of audioTracks) {
752
+ if (audioTrack.media.header && audioTrack.media.header.timeScale > 0) {
753
+ audioTrack.sampleRate = audioTrack.media.header.timeScale;
754
+ if (audioTrack.media.header.duration > 0) {
755
+ debug3("Using duration defined on audio track");
756
+ audioTrack.samples = audioTrack.media.header.duration;
757
+ audioTrack.duration = audioTrack.samples / audioTrack.sampleRate;
758
+ }
759
+ if (audioTrack.fragments.length > 0) {
760
+ debug3("Calculate duration defined in track fragments");
761
+ let totalTimeUnits = 0;
762
+ audioTrack.sizeInBytes = 0;
763
+ for (const fragment of audioTrack.fragments) {
764
+ for (const sample of fragment.trackRun.samples) {
765
+ const dur = sample.sampleDuration ?? fragment.header.defaultSampleDuration ?? 0;
766
+ const size = sample.sampleSize ?? fragment.header.defaultSampleSize ?? 0;
767
+ if (dur === 0) {
768
+ throw new Error("Missing sampleDuration and no defaultSampleDuration in track fragment header");
769
+ }
770
+ if (size === 0) {
771
+ throw new Error("Missing sampleSize and no defaultSampleSize in track fragment header");
772
+ }
773
+ totalTimeUnits += dur;
774
+ audioTrack.sizeInBytes += size;
775
+ }
776
+ }
777
+ if (!audioTrack.samples) {
778
+ audioTrack.samples = totalTimeUnits;
779
+ }
780
+ if (!audioTrack.duration) {
781
+ audioTrack.duration = totalTimeUnits / audioTrack.sampleRate;
782
+ }
783
+ } else if (audioTrack.sampleSizeTable.length > 0) {
784
+ audioTrack.sizeInBytes = audioTrack.sampleSizeTable.reduce((sum, n) => sum + n, 0);
785
+ }
786
+ }
787
+ const ssd = audioTrack.soundSampleDescription[0];
788
+ if (ssd.description && audioTrack.media.header) {
789
+ this.metadata.setFormat("sampleRate", ssd.description.sampleRate);
790
+ this.metadata.setFormat("bitsPerSample", ssd.description.sampleSize);
791
+ this.metadata.setFormat("numberOfChannels", ssd.description.numAudioChannels);
792
+ if (audioTrack.media.header.timeScale === 0 && audioTrack.timeToSampleTable.length > 0) {
793
+ const totalSampleSize = audioTrack.timeToSampleTable.map((ttstEntry) => ttstEntry.count * ttstEntry.duration).reduce((total, sampleSize) => total + sampleSize);
794
+ audioTrack.duration = totalSampleSize / ssd.description.sampleRate;
795
+ }
796
+ }
797
+ const encoderInfo = encoderDict[ssd.dataFormat];
798
+ if (encoderInfo) {
799
+ this.metadata.setFormat("lossless", !encoderInfo.lossy);
800
+ }
801
+ }
802
+ if (audioTracks.length >= 1) {
803
+ const firstAudioTrack = audioTracks[0];
804
+ if (firstAudioTrack.duration) {
805
+ this.metadata.setFormat("duration", firstAudioTrack.duration);
806
+ if (firstAudioTrack.sizeInBytes) {
807
+ this.metadata.setFormat("bitrate", 8 * firstAudioTrack.sizeInBytes / firstAudioTrack.duration);
808
+ }
809
+ }
810
+ }
811
+ this.metadata.setFormat("hasAudio", this.hasAudioTrack);
812
+ this.metadata.setFormat("hasVideo", this.hasVideoTrack);
813
+ }
814
+ async handleAtom(atom, remaining) {
815
+ if (atom.parent) {
816
+ switch (atom.parent.header.name) {
817
+ case "ilst":
818
+ case "<id>":
819
+ return this.parseMetadataItemData(atom);
820
+ case "moov":
821
+ switch (atom.header.name) {
822
+ case "trak":
823
+ return this.parseTrackBox(atom);
824
+ case "udta":
825
+ return this.parseTrackBox(atom);
826
+ }
827
+ break;
828
+ case "moof":
829
+ switch (atom.header.name) {
830
+ case "traf":
831
+ return this.parseTrackFragmentBox(atom);
832
+ }
833
+ }
834
+ }
835
+ if (this.atomParsers[atom.header.name]) {
836
+ return this.atomParsers[atom.header.name](remaining);
837
+ }
838
+ debug3(`No parser for atom path=${atom.atomPath}, payload-len=${remaining}, ignoring atom`);
839
+ await this.tokenizer.ignore(remaining);
840
+ }
841
+ getTrackDescription() {
842
+ const tracks = [...this.tracks.values()];
843
+ return tracks[tracks.length - 1];
844
+ }
845
+ async addTag(id, value) {
846
+ await this.metadata.addTag(tagFormat, id, value);
847
+ }
848
+ addWarning(message) {
849
+ debug3(`Warning: ${message}`);
850
+ this.metadata.addWarning(message);
851
+ }
852
+ /**
853
+ * Parse data of Meta-item-list-atom (item of 'ilst' atom)
854
+ * @param metaAtom
855
+ * Ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW8
856
+ */
857
+ parseMetadataItemData(metaAtom) {
858
+ let tagKey = metaAtom.header.name;
859
+ return metaAtom.readAtoms(this.tokenizer, async (child, remaining) => {
860
+ const payLoadLength = child.getPayloadLength(remaining);
861
+ switch (child.header.name) {
862
+ case "data":
863
+ return this.parseValueAtom(tagKey, child);
864
+ case "name":
865
+ // name atom (optional)
866
+ case "mean":
867
+ case "rate": {
868
+ const name = await this.tokenizer.readToken(new NameAtom(payLoadLength));
869
+ tagKey += `:${name.name}`;
870
+ break;
871
+ }
872
+ default: {
873
+ const uint8Array = await this.tokenizer.readToken(new Uint8ArrayType(payLoadLength));
874
+ this.addWarning(`Unsupported meta-item: ${tagKey}[${child.header.name}] => value=${uint8ArrayToHex(uint8Array)} ascii=${textDecode(uint8Array, "ascii")}`);
875
+ }
876
+ }
877
+ }, metaAtom.getPayloadLength(0));
878
+ }
879
+ async parseValueAtom(tagKey, metaAtom) {
880
+ const dataAtom = await this.tokenizer.readToken(new DataAtom(Number(metaAtom.header.length) - Header.len));
881
+ if (dataAtom.type.set !== 0) {
882
+ throw new Mp4ContentError(`Unsupported type-set != 0: ${dataAtom.type.set}`);
883
+ }
884
+ switch (dataAtom.type.type) {
885
+ case 0:
886
+ switch (tagKey) {
887
+ case "trkn":
888
+ case "disk": {
889
+ const num = UINT8.get(dataAtom.value, 3);
890
+ const of = UINT8.get(dataAtom.value, 5);
891
+ await this.addTag(tagKey, `${num}/${of}`);
892
+ break;
893
+ }
894
+ case "gnre": {
895
+ const genreInt = UINT8.get(dataAtom.value, 1);
896
+ const genreStr = Genres[genreInt - 1];
897
+ await this.addTag(tagKey, genreStr);
898
+ break;
899
+ }
900
+ case "rate": {
901
+ const rate = textDecode(dataAtom.value, "ascii");
902
+ await this.addTag(tagKey, rate);
903
+ break;
904
+ }
905
+ default:
906
+ debug3(`unknown proprietary value type for: ${metaAtom.atomPath}`);
907
+ }
908
+ break;
909
+ case 1:
910
+ // UTF-8: Without any count or NULL terminator
911
+ case 18:
912
+ await this.addTag(tagKey, textDecode(dataAtom.value));
913
+ break;
914
+ case 13:
915
+ if (this.options.skipCovers)
916
+ break;
917
+ await this.addTag(tagKey, {
918
+ format: "image/jpeg",
919
+ data: Uint8Array.from(dataAtom.value)
920
+ });
921
+ break;
922
+ case 14:
923
+ if (this.options.skipCovers)
924
+ break;
925
+ await this.addTag(tagKey, {
926
+ format: "image/png",
927
+ data: Uint8Array.from(dataAtom.value)
928
+ });
929
+ break;
930
+ case 21:
931
+ await this.addTag(tagKey, _MP4Parser.read_BE_Integer(dataAtom.value, true));
932
+ break;
933
+ case 22:
934
+ await this.addTag(tagKey, _MP4Parser.read_BE_Integer(dataAtom.value, false));
935
+ break;
936
+ case 65:
937
+ await this.addTag(tagKey, UINT8.get(dataAtom.value, 0));
938
+ break;
939
+ case 66:
940
+ await this.addTag(tagKey, UINT16_BE.get(dataAtom.value, 0));
941
+ break;
942
+ case 67:
943
+ await this.addTag(tagKey, UINT32_BE.get(dataAtom.value, 0));
944
+ break;
945
+ default:
946
+ this.addWarning(`atom key=${tagKey}, has unknown well-known-type (data-type): ${dataAtom.type.type}`);
947
+ }
948
+ }
949
+ async parseTrackBox(trakBox) {
950
+ const track = {
951
+ media: {},
952
+ fragments: []
953
+ };
954
+ await trakBox.readAtoms(this.tokenizer, async (child, remaining) => {
955
+ const payLoadLength = child.getPayloadLength(remaining);
956
+ switch (child.header.name) {
957
+ case "chap": {
958
+ const chap = await this.tokenizer.readToken(new ChapterTrackReferenceBox(remaining));
959
+ track.chapterList = chap;
960
+ break;
961
+ }
962
+ case "tkhd":
963
+ track.header = await this.tokenizer.readToken(new TrackHeaderAtom(payLoadLength));
964
+ break;
965
+ case "hdlr":
966
+ track.handler = await this.tokenizer.readToken(new HandlerBox(payLoadLength));
967
+ track.isAudio = () => track.handler.handlerType === "audi" || track.handler.handlerType === "soun";
968
+ track.isVideo = () => track.handler.handlerType === "vide";
969
+ if (track.isAudio()) {
970
+ this.hasAudioTrack = true;
971
+ } else if (track.isVideo()) {
972
+ this.hasVideoTrack = true;
973
+ }
974
+ break;
975
+ case "mdhd": {
976
+ const mdhd_data = await this.tokenizer.readToken(new MdhdAtom(payLoadLength));
977
+ track.media.header = mdhd_data;
978
+ break;
979
+ }
980
+ case "stco": {
981
+ const stco = await this.tokenizer.readToken(new StcoAtom(payLoadLength));
982
+ track.chunkOffsetTable = stco.entries;
983
+ break;
984
+ }
985
+ case "stsc": {
986
+ const stsc = await this.tokenizer.readToken(new StscAtom(payLoadLength));
987
+ track.sampleToChunkTable = stsc.entries;
988
+ break;
989
+ }
990
+ case "stsd": {
991
+ const stsd = await this.tokenizer.readToken(new StsdAtom(payLoadLength));
992
+ track.soundSampleDescription = stsd.table.map((dfEntry) => this.parseSoundSampleDescription(dfEntry));
993
+ break;
994
+ }
995
+ case "stts": {
996
+ const stts = await this.tokenizer.readToken(new SttsAtom(payLoadLength));
997
+ track.timeToSampleTable = stts.entries;
998
+ break;
999
+ }
1000
+ case "stsz": {
1001
+ const stsz = await this.tokenizer.readToken(new StszAtom(payLoadLength));
1002
+ track.sampleSize = stsz.sampleSize;
1003
+ track.sampleSizeTable = stsz.entries;
1004
+ break;
1005
+ }
1006
+ case "dinf":
1007
+ case "vmhd":
1008
+ case "smhd":
1009
+ debug3(`Ignoring: ${child.header.name}`);
1010
+ await this.tokenizer.ignore(payLoadLength);
1011
+ break;
1012
+ default: {
1013
+ debug3(`Unexpected track box: ${child.header.name}`);
1014
+ await this.tokenizer.ignore(payLoadLength);
1015
+ }
1016
+ }
1017
+ }, trakBox.getPayloadLength(0));
1018
+ this.tracks.set(track.header.trackId, track);
1019
+ }
1020
+ parseTrackFragmentBox(trafBox) {
1021
+ let tfhd;
1022
+ return trafBox.readAtoms(this.tokenizer, async (child, remaining) => {
1023
+ const payLoadLength = child.getPayloadLength(remaining);
1024
+ switch (child.header.name) {
1025
+ case "tfhd": {
1026
+ const fragmentHeaderBox = new TrackFragmentHeaderBox(child.getPayloadLength(remaining));
1027
+ tfhd = await this.tokenizer.readToken(fragmentHeaderBox);
1028
+ break;
1029
+ }
1030
+ case "tfdt":
1031
+ await this.tokenizer.ignore(payLoadLength);
1032
+ break;
1033
+ case "trun": {
1034
+ const trackRunBox = new TrackRunBox(payLoadLength);
1035
+ const trun = await this.tokenizer.readToken(trackRunBox);
1036
+ if (tfhd) {
1037
+ const track = this.tracks.get(tfhd.trackId);
1038
+ track?.fragments.push({ header: tfhd, trackRun: trun });
1039
+ }
1040
+ break;
1041
+ }
1042
+ default: {
1043
+ debug3(`Unexpected box: ${child.header.name}`);
1044
+ await this.tokenizer.ignore(payLoadLength);
1045
+ }
1046
+ }
1047
+ }, trafBox.getPayloadLength(0));
1048
+ }
1049
+ /**
1050
+ * @param sampleDescription
1051
+ * Ref: https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-128916
1052
+ */
1053
+ parseSoundSampleDescription(sampleDescription) {
1054
+ const ssd = {
1055
+ dataFormat: sampleDescription.dataFormat,
1056
+ dataReferenceIndex: sampleDescription.dataReferenceIndex
1057
+ };
1058
+ let offset = 0;
1059
+ if (sampleDescription.description) {
1060
+ const version = SoundSampleDescriptionVersion.get(sampleDescription.description, offset);
1061
+ offset += SoundSampleDescriptionVersion.len;
1062
+ if (version.version === 0 || version.version === 1) {
1063
+ ssd.description = SoundSampleDescriptionV0.get(sampleDescription.description, offset);
1064
+ } else {
1065
+ debug3(`Warning: sound-sample-description ${version} not implemented`);
1066
+ }
1067
+ }
1068
+ return ssd;
1069
+ }
1070
+ async parseChapterTrack(chapterTrack, track, len) {
1071
+ if (!chapterTrack.sampleSize) {
1072
+ if (chapterTrack.chunkOffsetTable.length !== chapterTrack.sampleSizeTable.length)
1073
+ throw new Error("Expected equal chunk-offset-table & sample-size-table length.");
1074
+ }
1075
+ const chapters = [];
1076
+ for (let i = 0; i < chapterTrack.chunkOffsetTable.length && len > 0; ++i) {
1077
+ const start = chapterTrack.timeToSampleTable.slice(0, i).reduce((acc, cur) => acc + cur.duration, 0);
1078
+ const chunkOffset = chapterTrack.chunkOffsetTable[i];
1079
+ const nextChunkLen = chunkOffset - this.tokenizer.position;
1080
+ const sampleSize = chapterTrack.sampleSize > 0 ? chapterTrack.sampleSize : chapterTrack.sampleSizeTable[i];
1081
+ len -= nextChunkLen + sampleSize;
1082
+ if (len < 0)
1083
+ throw new Mp4ContentError("Chapter chunk exceeding token length");
1084
+ await this.tokenizer.ignore(nextChunkLen);
1085
+ const title = await this.tokenizer.readToken(new ChapterText(sampleSize));
1086
+ debug3(`Chapter ${i + 1}: ${title}`);
1087
+ const chapter = {
1088
+ title,
1089
+ timeScale: chapterTrack.media.header ? chapterTrack.media.header.timeScale : 0,
1090
+ start,
1091
+ sampleOffset: this.findSampleOffset(track, this.tokenizer.position)
1092
+ };
1093
+ debug3(`Chapter title=${chapter.title}, offset=${chapter.sampleOffset}/${track.header.duration}`);
1094
+ chapters.push(chapter);
1095
+ }
1096
+ this.metadata.setFormat("chapters", chapters);
1097
+ await this.tokenizer.ignore(len);
1098
+ }
1099
+ findSampleOffset(track, chapterOffset) {
1100
+ let chunkIndex = 0;
1101
+ while (chunkIndex < track.chunkOffsetTable.length && track.chunkOffsetTable[chunkIndex] < chapterOffset) {
1102
+ ++chunkIndex;
1103
+ }
1104
+ return this.getChunkDuration(chunkIndex + 1, track);
1105
+ }
1106
+ getChunkDuration(chunkId, track) {
1107
+ let ttsi = 0;
1108
+ let ttsc = track.timeToSampleTable[ttsi].count;
1109
+ let ttsd = track.timeToSampleTable[ttsi].duration;
1110
+ let curChunkId = 1;
1111
+ let samplesPerChunk = this.getSamplesPerChunk(curChunkId, track.sampleToChunkTable);
1112
+ let totalDuration = 0;
1113
+ while (curChunkId < chunkId) {
1114
+ const nrOfSamples = Math.min(ttsc, samplesPerChunk);
1115
+ totalDuration += nrOfSamples * ttsd;
1116
+ ttsc -= nrOfSamples;
1117
+ samplesPerChunk -= nrOfSamples;
1118
+ if (samplesPerChunk === 0) {
1119
+ ++curChunkId;
1120
+ samplesPerChunk = this.getSamplesPerChunk(curChunkId, track.sampleToChunkTable);
1121
+ } else {
1122
+ ++ttsi;
1123
+ ttsc = track.timeToSampleTable[ttsi].count;
1124
+ ttsd = track.timeToSampleTable[ttsi].duration;
1125
+ }
1126
+ }
1127
+ return totalDuration;
1128
+ }
1129
+ getSamplesPerChunk(chunkId, stcTable) {
1130
+ for (let i = 0; i < stcTable.length - 1; ++i) {
1131
+ if (chunkId >= stcTable[i].firstChunk && chunkId < stcTable[i + 1].firstChunk) {
1132
+ return stcTable[i].samplesPerChunk;
1133
+ }
1134
+ }
1135
+ return stcTable[stcTable.length - 1].samplesPerChunk;
1136
+ }
1137
+ };
1138
+ export {
1139
+ MP4Parser
1140
+ };