@svta/cml-iso-bmff 1.0.0-alpha.1 → 1.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,21 +1,28 @@
1
1
  import { UTF_8, decodeText, encodeText } from "@svta/cml-utils";
2
2
 
3
+ //#region src/utils/createWriterConfig.ts
4
+ function createWriterConfig(config) {
5
+ return { writers: config?.writers ?? {} };
6
+ }
7
+
8
+ //#endregion
3
9
  //#region src/utils/CONTAINERS.ts
10
+ /**
11
+ * List of container box types
12
+ *
13
+ * @public
14
+ */
4
15
  const CONTAINERS = [
5
16
  "dinf",
6
17
  "edts",
7
- "enca",
8
- "encv",
9
18
  "grpl",
10
19
  "mdia",
11
20
  "meco",
12
- "meta",
13
21
  "mfra",
14
22
  "minf",
15
23
  "moof",
16
24
  "moov",
17
25
  "mvex",
18
- "prsl",
19
26
  "schi",
20
27
  "sinf",
21
28
  "stbl",
@@ -42,6 +49,15 @@ function isContainer(box) {
42
49
  return "boxes" in box || CONTAINERS.includes(box.type);
43
50
  }
44
51
 
52
+ //#endregion
53
+ //#region src/fields/TEMPLATE.ts
54
+ /**
55
+ * The template field type
56
+ *
57
+ * @public
58
+ */
59
+ const TEMPLATE = "template";
60
+
45
61
  //#endregion
46
62
  //#region src/fields/UINT.ts
47
63
  /**
@@ -130,12 +146,15 @@ var IsoBoxWriteView = class {
130
146
  this.writeUint(0, 1);
131
147
  };
132
148
  this.writeBytes = (data) => {
133
- new Uint8Array(this.dataView.buffer).set(data, this.cursor);
134
- this.cursor += data.length;
149
+ if (!Array.isArray(data)) data = [data];
150
+ for (const bytes of data) {
151
+ new Uint8Array(this.dataView.buffer).set(bytes, this.cursor);
152
+ this.cursor += bytes.length;
153
+ }
135
154
  };
136
- this.writeArray = (data, type$1, size$1) => {
137
- const write = type$1 === UINT ? this.writeUint : this.writeInt;
138
- for (const value of data) write(value, size$1);
155
+ this.writeArray = (data, type$1, size$1, length) => {
156
+ const write = type$1 === UINT ? this.writeUint : type$1 === TEMPLATE ? this.writeTemplate : this.writeInt;
157
+ for (let i = 0; i < length; i++) write(data[i] ?? 0, size$1);
139
158
  };
140
159
  this.writeTemplate = (value, size$1) => {
141
160
  const shift = size$1 === 4 ? 16 : 8;
@@ -193,7 +212,43 @@ var IsoBoxWriteView = class {
193
212
  };
194
213
 
195
214
  //#endregion
196
- //#region src/writers/writeContainerBox.ts
215
+ //#region src/utils/writeBoxes.ts
216
+ /**
217
+ * Write boxes to an array of Uint8Arrays.
218
+ *
219
+ * @param boxes - The boxes to write
220
+ * @param config - The configuration for the writer
221
+ *
222
+ * @returns The written boxes
223
+ *
224
+ * @internal
225
+ */
226
+ function writeBoxes(boxes, config) {
227
+ return Array.from(boxes, (box) => writeBox(box, config));
228
+ }
229
+
230
+ //#endregion
231
+ //#region src/utils/writeChildBoxes.ts
232
+ /**
233
+ * Write child boxes
234
+ *
235
+ * @param boxes - The boxes to write
236
+ * @param config - The configuration for the writer
237
+ *
238
+ * @returns The byte arrays and total size of the written boxes
239
+ *
240
+ * @internal
241
+ */
242
+ function writeChildBoxes(boxes, config) {
243
+ const bytes = writeBoxes(boxes, config);
244
+ return {
245
+ bytes,
246
+ size: bytes.reduce((size, byte) => size + byte.byteLength, 0)
247
+ };
248
+ }
249
+
250
+ //#endregion
251
+ //#region src/utils/writeContainerBox.ts
197
252
  /**
198
253
  * Write a ContainerBox to an IsoBmffWriter.
199
254
  *
@@ -203,43 +258,37 @@ var IsoBoxWriteView = class {
203
258
  * @param box - The ContainerBox to write
204
259
  *
205
260
  * @returns An IsoBmffWriter containing the encoded box
261
+ *
262
+ * @internal
206
263
  */
207
- function writeContainerBox(box, writers) {
208
- const children = [];
264
+ function writeContainerBox(box, config) {
209
265
  const headerSize = 8;
210
- let childrenSize = 0;
211
- for (const childBox of box.boxes) {
212
- const view = writeIsoBox(childBox, writers);
213
- childrenSize += view.byteLength;
214
- children.push(view);
215
- }
216
- const totalSize = headerSize + childrenSize;
266
+ const { bytes, size } = writeChildBoxes(box.boxes, config);
267
+ const totalSize = headerSize + size;
217
268
  const writer = new IsoBoxWriteView(box.type, totalSize);
218
- for (const child of children) writer.writeBytes(child);
269
+ writer.writeBytes(bytes);
219
270
  return writer;
220
271
  }
221
272
 
222
273
  //#endregion
223
- //#region src/writeIsoBox.ts
274
+ //#region src/utils/writeBox.ts
224
275
  /**
225
- * Write an ISO box to a Uint8Array.
276
+ * Write a box to a Uint8Array.
226
277
  *
227
278
  * @param box - The box to write
228
279
  * @param writers - The writers to use
229
280
  * @returns The written box
230
281
  *
231
- * @public
282
+ * @internal
232
283
  */
233
- function writeIsoBox(box, writers) {
284
+ function writeBox(box, config) {
234
285
  let view = null;
235
286
  if ("type" in box) {
236
287
  const { type } = box;
237
- if (type !== "" && isContainer(box)) view = writeContainerBox(box, writers);
238
- else {
239
- const writer = writers[type];
240
- if (writer) view = writer(box);
241
- else if ("view" in box) view = box.view;
242
- }
288
+ const writer = config.writers?.[type];
289
+ if (writer) view = writer(box, config);
290
+ else if (isContainer(box)) view = writeContainerBox(box, config);
291
+ else if ("view" in box) view = box.view;
243
292
  if (!view) throw new Error(`No writer found for box type: ${type}`);
244
293
  }
245
294
  if ("buffer" in box) view = box;
@@ -247,6 +296,22 @@ function writeIsoBox(box, writers) {
247
296
  return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
248
297
  }
249
298
 
299
+ //#endregion
300
+ //#region src/writeIsoBox.ts
301
+ /**
302
+ * Write an ISO box to a Uint8Array.
303
+ *
304
+ * @param box - The box to write
305
+ * @param config - The configuration for the writer
306
+ *
307
+ * @returns The written box
308
+ *
309
+ * @public
310
+ */
311
+ function writeIsoBox(box, config) {
312
+ return writeBox(box, createWriterConfig(config));
313
+ }
314
+
250
315
  //#endregion
251
316
  //#region src/IsoBoxReadableStream.ts
252
317
  /**
@@ -263,13 +328,13 @@ var IsoBoxReadableStream = class extends ReadableStream {
263
328
  */
264
329
  constructor(boxes, config = {}) {
265
330
  const iterator = boxes[Symbol.iterator]();
266
- const { writers = {} } = config;
331
+ const cfg = createWriterConfig(config);
267
332
  function pull(controller) {
268
333
  const desiredSize = controller.desiredSize ?? 0;
269
334
  for (let i = 0; i < desiredSize; i++) {
270
335
  const { value, done } = iterator.next();
271
336
  if (done) controller.close();
272
- else controller.enqueue(writeIsoBox(value, writers));
337
+ else controller.enqueue(writeIsoBox(value, cfg));
273
338
  }
274
339
  }
275
340
  super({
@@ -299,260 +364,480 @@ function createIsoBoxReadableStream(boxes, config = {}) {
299
364
  }
300
365
 
301
366
  //#endregion
302
- //#region src/readers/readArdi.ts
367
+ //#region src/fields/DATA.ts
303
368
  /**
304
- * Parse a AudioRenderingIndicationBox from an IsoView
305
- *
306
- * @param view - The IsoView to read data from
307
- *
308
- * @returns A parsed AudioRenderingIndicationBox
369
+ * The data field type
309
370
  *
310
371
  * @public
311
372
  */
312
- function readArdi(view) {
313
- return {
314
- type: "ardi",
315
- ...view.readFullBox(),
316
- audioRenderingIndication: view.readUint(1)
317
- };
318
- }
373
+ const DATA = "data";
319
374
 
320
375
  //#endregion
321
- //#region src/readers/readAvc1.ts
376
+ //#region src/fields/INT.ts
322
377
  /**
323
- * Parse a VisualSampleEntryBox from an IsoView
324
- *
325
- * @param view - The IsoView to read data from
326
- *
327
- * @returns A parsed VisualSampleEntryBox
378
+ * The integer field type
328
379
  *
329
380
  * @public
330
381
  */
331
- function readAvc1(view) {
332
- const { readArray, readUint: readUint$1, readInt: readInt$1, readTemplate: readTemplate$1, readData: readData$1 } = view;
333
- return {
334
- type: "avc1",
335
- reserved1: readArray(UINT, 1, 6),
336
- dataReferenceIndex: readUint$1(2),
337
- preDefined1: readUint$1(2),
338
- reserved2: readUint$1(2),
339
- preDefined2: readArray(UINT, 4, 3),
340
- width: readUint$1(2),
341
- height: readUint$1(2),
342
- horizresolution: readTemplate$1(4),
343
- vertresolution: readTemplate$1(4),
344
- reserved3: readUint$1(4),
345
- frameCount: readUint$1(2),
346
- compressorName: readArray(UINT, 1, 32),
347
- depth: readUint$1(2),
348
- preDefined3: readInt$1(2),
349
- config: readData$1(-1)
350
- };
351
- }
382
+ const INT = "int";
352
383
 
353
384
  //#endregion
354
- //#region src/readers/readAvc2.ts
385
+ //#region src/fields/STRING.ts
355
386
  /**
356
- * Parse a VisualSampleEntryBox from an IsoView
357
- *
358
- * @param view - The IsoView to read data from
359
- *
360
- * @returns A parsed VisualSampleEntryBox
387
+ * The string field type
361
388
  *
362
389
  * @public
363
390
  */
364
- function readAvc2(view) {
365
- return {
366
- ...readAvc1(view),
367
- type: "avc2"
368
- };
369
- }
391
+ const STRING = "string";
370
392
 
371
393
  //#endregion
372
- //#region src/readers/readAvc3.ts
394
+ //#region src/fields/UTF8.ts
373
395
  /**
374
- * Parse a VisualSampleEntryBox from an IsoView
375
- *
376
- * @param view - The IsoView to read data from
377
- *
378
- * @returns A parsed VisualSampleEntryBox
396
+ * The UTF8 field type
379
397
  *
380
398
  * @public
381
399
  */
382
- function readAvc3(view) {
383
- return {
384
- ...readAvc1(view),
385
- type: "avc3"
386
- };
400
+ const UTF8 = "utf8";
401
+
402
+ //#endregion
403
+ //#region src/utils/readData.ts
404
+ function readData(dataView, offset, size) {
405
+ const length = size > 0 ? size : dataView.byteLength - (offset - dataView.byteOffset);
406
+ return new Uint8Array(dataView.buffer, offset, Math.max(length, 0));
387
407
  }
388
408
 
389
409
  //#endregion
390
- //#region src/readers/readAvc4.ts
410
+ //#region src/utils/readInt.ts
411
+ function readInt(dataView, offset, size) {
412
+ let result = NaN;
413
+ const cursor = offset - dataView.byteOffset;
414
+ switch (size) {
415
+ case 1:
416
+ result = dataView.getInt8(cursor);
417
+ break;
418
+ case 2:
419
+ result = dataView.getInt16(cursor);
420
+ break;
421
+ case 4:
422
+ result = dataView.getInt32(cursor);
423
+ break;
424
+ case 8:
425
+ const s1 = dataView.getInt32(cursor);
426
+ const s2 = dataView.getInt32(cursor + 4);
427
+ result = s1 * Math.pow(2, 32) + s2;
428
+ break;
429
+ }
430
+ return result;
431
+ }
432
+
433
+ //#endregion
434
+ //#region src/utils/readUint.ts
435
+ function readUint(dataView, offset, size) {
436
+ const cursor = offset - dataView.byteOffset;
437
+ let value = NaN;
438
+ let s1;
439
+ let s2;
440
+ switch (size) {
441
+ case 1:
442
+ value = dataView.getUint8(cursor);
443
+ break;
444
+ case 2:
445
+ value = dataView.getUint16(cursor);
446
+ break;
447
+ case 3:
448
+ s1 = dataView.getUint16(cursor);
449
+ s2 = dataView.getUint8(cursor + 2);
450
+ value = (s1 << 8) + s2;
451
+ break;
452
+ case 4:
453
+ value = dataView.getUint32(cursor);
454
+ break;
455
+ case 8:
456
+ s1 = dataView.getUint32(cursor);
457
+ s2 = dataView.getUint32(cursor + 4);
458
+ value = s1 * Math.pow(2, 32) + s2;
459
+ break;
460
+ }
461
+ return value;
462
+ }
463
+
464
+ //#endregion
465
+ //#region src/utils/readString.ts
466
+ function readString(dataView, offset, length) {
467
+ let str = "";
468
+ for (let c = 0; c < length; c++) {
469
+ const char = readUint(dataView, offset + c, 1);
470
+ str += String.fromCharCode(char);
471
+ }
472
+ return str;
473
+ }
474
+
475
+ //#endregion
476
+ //#region src/utils/readTemplate.ts
477
+ function readTemplate(dataView, offset, size) {
478
+ const half = size / 2;
479
+ return readUint(dataView, offset, half) + readUint(dataView, offset + half, half) / Math.pow(2, half);
480
+ }
481
+
482
+ //#endregion
483
+ //#region src/utils/readTerminatedString.ts
484
+ function readTerminatedString(dataView, offset) {
485
+ let str = "";
486
+ let cursor = offset;
487
+ while (cursor - dataView.byteOffset < dataView.byteLength) {
488
+ const char = readUint(dataView, cursor, 1);
489
+ if (char === 0) break;
490
+ str += String.fromCharCode(char);
491
+ cursor++;
492
+ }
493
+ return str;
494
+ }
495
+
496
+ //#endregion
497
+ //#region src/utils/readUtf8String.ts
391
498
  /**
392
- * Parse a VisualSampleEntryBox from an IsoView
393
- *
394
- * @param view - The IsoView to read data from
499
+ * Reads a UTF-8 string from a data view.
395
500
  *
396
- * @returns A parsed VisualSampleEntryBox
501
+ * @param dataView - The data view to read from.
502
+ * @param offset - The offset to start reading from.
503
+ * @returns The UTF-8 string.
397
504
  *
398
- * @public
505
+ * @internal
399
506
  */
400
- function readAvc4(view) {
401
- return {
402
- ...readAvc1(view),
403
- type: "avc4"
404
- };
507
+ function readUtf8String(dataView, offset) {
508
+ const length = dataView.byteLength - (offset - dataView.byteOffset);
509
+ return length > 0 ? decodeText(new DataView(dataView.buffer, offset, length), { encoding: UTF_8 }) : "";
405
510
  }
406
511
 
407
512
  //#endregion
408
- //#region src/readers/readCtts.ts
513
+ //#region src/utils/readUtf8TerminatedString.ts
409
514
  /**
410
- * Parse a CompositionTimeToSampleBox from an IsoView
411
- *
412
- * @param view - The IsoView to read data from
515
+ * Reads a UTF-8 terminated string from a data view.
413
516
  *
414
- * @returns A parsed CompositionTimeToSampleBox
517
+ * @param dataView - The data view to read from.
518
+ * @param offset - The offset to start reading from.
519
+ * @returns The UTF-8 terminated string.
415
520
  *
416
- * @public
521
+ * @internal
417
522
  */
418
- function readCtts(view) {
419
- const { version, flags } = view.readFullBox();
420
- const read = version === 1 ? view.readInt : view.readUint;
421
- const entryCount = view.readUint(4);
422
- return {
423
- type: "ctts",
424
- version,
425
- flags,
426
- entryCount,
427
- entries: view.readEntries(entryCount, () => ({
428
- sampleCount: view.readUint(4),
429
- sampleOffset: read(4)
430
- }))
431
- };
523
+ function readUtf8TerminatedString(dataView, offset) {
524
+ const length = dataView.byteLength - (offset - dataView.byteOffset);
525
+ let data = "";
526
+ if (length > 0) {
527
+ const view = new DataView(dataView.buffer, offset, length);
528
+ let l = 0;
529
+ for (; l < length; l++) if (view.getUint8(l) === 0) break;
530
+ data = decodeText(new DataView(dataView.buffer, offset, l), { encoding: UTF_8 });
531
+ }
532
+ return data;
432
533
  }
433
534
 
434
535
  //#endregion
435
- //#region src/readers/readDref.ts
536
+ //#region src/IsoBoxReadView.ts
436
537
  /**
437
- * Parse a DataReferenceBox from an IsoView
438
- *
439
- * @param view - The IsoView to read data from
440
- *
441
- * @returns A parsed DataReferenceBox
538
+ * ISO BMFF data view. Similar to DataView, but with additional methods for reading ISO BMFF data.
539
+ * It implements the iterator protocol, so it can be used in a for...of loop.
442
540
  *
443
541
  * @public
444
542
  */
445
- function readDref(view) {
446
- const { version, flags } = view.readFullBox();
447
- const entryCount = view.readUint(4);
448
- return {
449
- type: "dref",
450
- version,
451
- flags,
452
- entryCount,
453
- entries: view.readBoxes(entryCount)
454
- };
455
- }
543
+ var IsoBoxReadView = class IsoBoxReadView {
544
+ /**
545
+ * Creates a new IsoView instance. Similar to DataView, but with additional
546
+ * methods for reading ISO BMFF data. It implements the iterator protocol,
547
+ * so it can be used in a for...of loop.
548
+ *
549
+ * @param raw - The raw data to view.
550
+ * @param config - The configuration for the IsoView.
551
+ */
552
+ constructor(raw, config) {
553
+ this.truncated = false;
554
+ this.slice = (offset, size) => {
555
+ const isoView = new IsoBoxReadView(new DataView(this.dataView.buffer, offset, size), this.config);
556
+ const headerSize = this.offset - offset;
557
+ const bodySize = size - headerSize;
558
+ this.offset += bodySize;
559
+ isoView.jump(headerSize);
560
+ return isoView;
561
+ };
562
+ this.read = (type, size = 0) => {
563
+ const { dataView, offset } = this;
564
+ let result;
565
+ let cursor = size;
566
+ switch (type) {
567
+ case UINT:
568
+ result = readUint(dataView, offset, size);
569
+ break;
570
+ case INT:
571
+ result = readInt(dataView, offset, size);
572
+ break;
573
+ case TEMPLATE:
574
+ result = readTemplate(dataView, offset, size);
575
+ break;
576
+ case STRING:
577
+ if (size === -1) {
578
+ result = readTerminatedString(dataView, offset);
579
+ cursor = result.length + 1;
580
+ } else result = readString(dataView, offset, size);
581
+ break;
582
+ case DATA:
583
+ result = readData(dataView, offset, size);
584
+ cursor = result.length;
585
+ break;
586
+ case UTF8:
587
+ if (size === -1) {
588
+ result = readUtf8TerminatedString(dataView, offset);
589
+ cursor = result.length + 1;
590
+ } else result = readUtf8String(dataView, offset);
591
+ break;
592
+ default: result = -1;
593
+ }
594
+ this.offset += cursor;
595
+ return result;
596
+ };
597
+ this.readUint = (size) => {
598
+ return this.read(UINT, size);
599
+ };
600
+ this.readInt = (size) => {
601
+ return this.read(INT, size);
602
+ };
603
+ this.readString = (size) => {
604
+ return this.read(STRING, size);
605
+ };
606
+ this.readTemplate = (size) => {
607
+ return this.read(TEMPLATE, size);
608
+ };
609
+ this.readData = (size) => {
610
+ return this.read(DATA, size);
611
+ };
612
+ this.readUtf8 = (size) => {
613
+ return this.read(UTF8, size);
614
+ };
615
+ this.readFullBox = () => {
616
+ return {
617
+ version: this.readUint(1),
618
+ flags: this.readUint(3)
619
+ };
620
+ };
621
+ this.readArray = (type, size, length) => {
622
+ const value = [];
623
+ for (let i = 0; i < length; i++) value.push(this.read(type, size));
624
+ return value;
625
+ };
626
+ this.jump = (size) => {
627
+ this.offset += size;
628
+ };
629
+ this.readBox = () => {
630
+ const { dataView, offset } = this;
631
+ let cursor = 0;
632
+ const size = readUint(dataView, offset, 4);
633
+ const type = readString(dataView, offset + 4, 4);
634
+ const box = {
635
+ size,
636
+ type
637
+ };
638
+ cursor += 8;
639
+ if (box.size === 1) {
640
+ box.largesize = readUint(dataView, offset + cursor, 8);
641
+ cursor += 8;
642
+ }
643
+ const actualSize = box.size === 0 ? this.bytesRemaining : box.largesize ?? box.size;
644
+ if (this.cursor + actualSize > dataView.byteLength) {
645
+ this.truncated = true;
646
+ throw new Error("Truncated box");
647
+ }
648
+ this.jump(cursor);
649
+ if (type === "uuid") box.usertype = this.readArray("uint", 1, 16);
650
+ box.view = this.slice(offset, actualSize);
651
+ return box;
652
+ };
653
+ this.readBoxes = (length = -1) => {
654
+ const result = [];
655
+ for (const box of this) {
656
+ result.push(box);
657
+ if (length > 0 && result.length >= length) break;
658
+ }
659
+ return result;
660
+ };
661
+ this.readEntries = (length, map) => {
662
+ const result = [];
663
+ for (let i = 0; i < length; i++) result.push(map());
664
+ return result;
665
+ };
666
+ this.dataView = raw instanceof ArrayBuffer ? new DataView(raw) : raw instanceof DataView ? raw : new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
667
+ this.offset = this.dataView.byteOffset;
668
+ this.config = config || { readers: {} };
669
+ }
670
+ /**
671
+ * The buffer of the data view.
672
+ */
673
+ get buffer() {
674
+ return this.dataView.buffer;
675
+ }
676
+ /**
677
+ * The byte offset of the data view.
678
+ */
679
+ get byteOffset() {
680
+ return this.dataView.byteOffset;
681
+ }
682
+ /**
683
+ * The byte length of the data view.
684
+ */
685
+ get byteLength() {
686
+ return this.dataView.byteLength;
687
+ }
688
+ /**
689
+ * The current byteoffset in the data view.
690
+ */
691
+ get cursor() {
692
+ return this.offset - this.dataView.byteOffset;
693
+ }
694
+ /**
695
+ * Whether the end of the data view has been reached.
696
+ */
697
+ get done() {
698
+ return this.cursor >= this.dataView.byteLength || this.truncated;
699
+ }
700
+ /**
701
+ * The number of bytes remaining in the data view.
702
+ */
703
+ get bytesRemaining() {
704
+ return this.dataView.byteLength - this.cursor;
705
+ }
706
+ /**
707
+ * Iterates over the boxes in the data view.
708
+ *
709
+ * @returns A generator of boxes.
710
+ */
711
+ *[Symbol.iterator]() {
712
+ const { readers = {} } = this.config;
713
+ while (!this.done) try {
714
+ const box = this.readBox();
715
+ const { type, view } = box;
716
+ const parser = readers[type] || readers[type.trim()];
717
+ if (parser) Object.assign(box, parser(view));
718
+ if (isContainer(box) && !box.boxes) {
719
+ const boxes = [];
720
+ for (const child of view) boxes.push(child);
721
+ box.boxes = boxes;
722
+ }
723
+ yield box;
724
+ } catch (error) {
725
+ if (error instanceof Error && error.message === "Truncated box") break;
726
+ throw error;
727
+ }
728
+ }
729
+ };
456
730
 
457
731
  //#endregion
458
- //#region src/readers/readElng.ts
732
+ //#region src/readIsoBoxes.ts
459
733
  /**
460
- * Parse a ExtendedLanguageBox from an IsoView
734
+ * Reads ISO boxes from a data source.
461
735
  *
462
- * @param view - The IsoView to read data from
736
+ * @param raw - The raw ISO data
737
+ * @param config - The configuration for the IsoView
463
738
  *
464
- * @returns A parsed ExtendedLanguageBox
739
+ * @returns The parsed boxes
740
+ *
741
+ * @example
742
+ * {@includeCode ../test/readIsoBoxes.test.ts#example}
465
743
  *
466
744
  * @public
467
745
  */
468
- function readElng(view) {
469
- return {
470
- type: "elng",
471
- ...view.readFullBox(),
472
- extendedLanguage: view.readUtf8(-1)
473
- };
746
+ function readIsoBoxes(raw, config) {
747
+ const boxes = [];
748
+ for (const box of new IsoBoxReadView(raw, config)) boxes.push(box);
749
+ return boxes;
474
750
  }
475
751
 
476
752
  //#endregion
477
- //#region src/readers/readElst.ts
753
+ //#region src/traverseIsoBoxes.ts
478
754
  /**
479
- * Parse a Box from an IsoView
755
+ * Traverse ISO boxes
480
756
  *
481
- * @param view - The IsoView to read data from
757
+ * @param boxes - The boxes to traverse
758
+ * @param depthFirst - Whether to traverse the boxes depth-first or breadth-first
759
+ * @param maxDepth - The maximum depth to traverse. A value of 0 will only traverse the root boxes.
482
760
  *
483
- * @returns A parsed Box
761
+ * @returns A generator of boxes
762
+ *
763
+ * @example
764
+ * {@includeCode ../test/traverseIsoBoxes.test.ts#example}
484
765
  *
485
766
  * @public
486
767
  */
487
- function readElst(view) {
488
- const { version, flags } = view.readFullBox();
489
- const size = version === 1 ? 8 : 4;
490
- const entryCount = view.readUint(4);
491
- return {
492
- type: "elst",
493
- version,
494
- flags,
495
- entryCount,
496
- entries: view.readEntries(entryCount, () => ({
497
- segmentDuration: view.readUint(size),
498
- mediaTime: view.readInt(size),
499
- mediaRateInteger: view.readInt(2),
500
- mediaRateFraction: view.readInt(2)
501
- }))
502
- };
768
+ function* traverseIsoBoxes(boxes, depthFirst = true, maxDepth = Infinity) {
769
+ if (maxDepth < 0 || typeof maxDepth !== "number" || Number.isNaN(maxDepth)) return;
770
+ const queue = [[boxes, 0]];
771
+ while (queue.length > 0) {
772
+ const item = queue.shift();
773
+ if (!item) continue;
774
+ const [children, depth] = item;
775
+ for (const child of children) {
776
+ yield child;
777
+ if (depth >= maxDepth) continue;
778
+ if (isContainer(child) && child.boxes) {
779
+ const next = child.boxes;
780
+ if (depthFirst) yield* traverseIsoBoxes(next, depthFirst, maxDepth - 1);
781
+ else queue.push([next, depth + 1]);
782
+ }
783
+ }
784
+ }
503
785
  }
504
786
 
505
787
  //#endregion
506
- //#region src/readers/readEmsg.ts
788
+ //#region src/writeIsoBoxes.ts
507
789
  /**
508
- * Parse an EventMessageBox from an IsoView
790
+ * Writes ISO boxes to a readable stream.
791
+ *
792
+ * @param boxes - The boxes to write
793
+ * @param config - The configuration for the readable stream
794
+ *
795
+ * @returns A readable stream of the written boxes
796
+ *
797
+ * @example
798
+ * {@includeCode ../test/writeIsoBoxes.test.ts#example}
799
+ *
800
+ * @public
801
+ */
802
+ function writeIsoBoxes(boxes, config) {
803
+ return writeBoxes(boxes, createWriterConfig(config));
804
+ }
805
+
806
+ //#endregion
807
+ //#region src/readers/readArdi.ts
808
+ /**
809
+ * Parse a AudioRenderingIndicationBox from an IsoView
509
810
  *
510
811
  * @param view - The IsoView to read data from
511
812
  *
512
- * @returns A parsed EventMessageBox
813
+ * @returns A parsed AudioRenderingIndicationBox
513
814
  *
514
815
  * @public
515
816
  */
516
- function readEmsg(view) {
517
- const { readUint: readUint$1, readString: readString$1, readData: readData$1 } = view;
518
- const result = {
519
- type: "emsg",
520
- ...view.readFullBox()
817
+ function readArdi(view) {
818
+ return {
819
+ type: "ardi",
820
+ ...view.readFullBox(),
821
+ audioRenderingIndication: view.readUint(1)
521
822
  };
522
- if (result.version == 1) {
523
- result.timescale = readUint$1(4);
524
- result.presentationTime = readUint$1(8);
525
- result.eventDuration = readUint$1(4);
526
- result.id = readUint$1(4);
527
- result.schemeIdUri = readString$1(-1);
528
- result.value = readString$1(-1);
529
- } else {
530
- result.schemeIdUri = readString$1(-1);
531
- result.value = readString$1(-1);
532
- result.timescale = readUint$1(4);
533
- result.presentationTimeDelta = readUint$1(4);
534
- result.eventDuration = readUint$1(4);
535
- result.id = readUint$1(4);
536
- }
537
- result.messageData = readData$1(-1);
538
- return result;
539
823
  }
540
824
 
541
825
  //#endregion
542
- //#region src/readers/readMp4a.ts
826
+ //#region src/readers/readAudioSampleEntryBox.ts
543
827
  /**
544
- * Parse an AudioSampleEntry from an IsoView
828
+ * Parse a AudioSampleEntryBox from an IsoView
545
829
  *
830
+ * @param type - The type of AudioSampleEntryBox to read
546
831
  * @param view - The IsoView to read data from
547
832
  *
548
- * @returns A parsed AudioSampleEntry
833
+ * @returns A parsed AudioSampleEntryBox
549
834
  *
550
835
  * @public
551
836
  */
552
- function readMp4a(view) {
553
- const { readArray, readUint: readUint$1, readTemplate: readTemplate$1, readData: readData$1 } = view;
837
+ function readAudioSampleEntryBox(type, view) {
838
+ const { readArray, readUint: readUint$1, readTemplate: readTemplate$1, readBoxes } = view;
554
839
  return {
555
- type: "mp4a",
840
+ type,
556
841
  reserved1: readArray(UINT, 1, 6),
557
842
  dataReferenceIndex: readUint$1(2),
558
843
  reserved2: readArray(UINT, 4, 2),
@@ -561,30 +846,46 @@ function readMp4a(view) {
561
846
  preDefined: readUint$1(2),
562
847
  reserved3: readUint$1(2),
563
848
  samplerate: readTemplate$1(4),
564
- esds: readData$1(-1)
849
+ boxes: readBoxes()
565
850
  };
566
851
  }
567
852
 
568
853
  //#endregion
569
- //#region src/readers/readEnca.ts
854
+ //#region src/readers/readVisualSampleEntryBox.ts
570
855
  /**
571
- * Parse an AudioSampleEntry from an IsoView
856
+ * Parse a VisualSampleEntryBox from an IsoView
572
857
  *
858
+ * @param type - The type of VisualSampleEntryBox to read
573
859
  * @param view - The IsoView to read data from
574
860
  *
575
- * @returns A parsed AudioSampleEntry
861
+ * @returns A parsed VisualSampleEntryBox
576
862
  *
577
863
  * @public
578
864
  */
579
- function readEnca(view) {
865
+ function readVisualSampleEntryBox(type, view) {
866
+ const { readArray, readUint: readUint$1, readInt: readInt$1, readTemplate: readTemplate$1, readBoxes } = view;
580
867
  return {
581
- ...readMp4a(view),
582
- type: "enca"
868
+ type,
869
+ reserved1: readArray(UINT, 1, 6),
870
+ dataReferenceIndex: readUint$1(2),
871
+ preDefined1: readUint$1(2),
872
+ reserved2: readUint$1(2),
873
+ preDefined2: readArray(UINT, 4, 3),
874
+ width: readUint$1(2),
875
+ height: readUint$1(2),
876
+ horizresolution: readTemplate$1(4),
877
+ vertresolution: readTemplate$1(4),
878
+ reserved3: readUint$1(4),
879
+ frameCount: readUint$1(2),
880
+ compressorName: readArray(UINT, 1, 32),
881
+ depth: readUint$1(2),
882
+ preDefined3: readInt$1(2),
883
+ boxes: readBoxes()
583
884
  };
584
885
  }
585
886
 
586
887
  //#endregion
587
- //#region src/readers/readEncv.ts
888
+ //#region src/readers/readAvc1.ts
588
889
  /**
589
890
  * Parse a VisualSampleEntryBox from an IsoView
590
891
  *
@@ -594,175 +895,366 @@ function readEnca(view) {
594
895
  *
595
896
  * @public
596
897
  */
597
- function readEncv(view) {
598
- return {
599
- ...readAvc1(view),
600
- type: "encv"
601
- };
898
+ function readAvc1(view) {
899
+ return readVisualSampleEntryBox("avc1", view);
602
900
  }
603
901
 
604
902
  //#endregion
605
- //#region src/readers/readFree.ts
903
+ //#region src/readers/readAvc2.ts
606
904
  /**
607
- * Parse a Box from an IsoView
905
+ * Parse a VisualSampleEntryBox from an IsoView
608
906
  *
609
907
  * @param view - The IsoView to read data from
610
908
  *
611
- * @returns A parsed Box
909
+ * @returns A parsed VisualSampleEntryBox
612
910
  *
613
911
  * @public
614
912
  */
615
- function readFree(view) {
616
- return {
617
- type: "free",
618
- data: view.readData(-1)
619
- };
913
+ function readAvc2(view) {
914
+ return readVisualSampleEntryBox("avc2", view);
620
915
  }
621
916
 
622
917
  //#endregion
623
- //#region src/readers/readFrma.ts
918
+ //#region src/readers/readAvc3.ts
624
919
  /**
625
- * Parse an OriginalFormatBox from an IsoView
920
+ * Parse a VisualSampleEntryBox from an IsoView
626
921
  *
627
922
  * @param view - The IsoView to read data from
628
923
  *
629
- * @returns A parsed OriginalFormatBox
924
+ * @returns A parsed VisualSampleEntryBox
630
925
  *
631
926
  * @public
632
927
  */
633
- function readFrma(view) {
634
- return {
635
- type: "frma",
636
- dataFormat: view.readUint(4)
637
- };
928
+ function readAvc3(view) {
929
+ return readVisualSampleEntryBox("avc3", view);
638
930
  }
639
931
 
640
932
  //#endregion
641
- //#region src/fields/STRING.ts
642
- /**
643
- * The string field type
644
- *
645
- * @public
646
- */
647
- const STRING = "string";
648
-
649
- //#endregion
650
- //#region src/readers/readFtyp.ts
933
+ //#region src/readers/readAvc4.ts
651
934
  /**
652
- * Parse a FileTypeBox from an IsoView
935
+ * Parse a VisualSampleEntryBox from an IsoView
653
936
  *
654
937
  * @param view - The IsoView to read data from
655
938
  *
656
- * @returns A parsed FileTypeBox
939
+ * @returns A parsed VisualSampleEntryBox
657
940
  *
658
941
  * @public
659
942
  */
660
- function readFtyp(view) {
661
- const size = 4;
662
- const majorBrand = view.readString(4);
663
- const minorVersion = view.readUint(4);
664
- const length = view.bytesRemaining / size;
665
- return {
666
- type: "ftyp",
667
- majorBrand,
668
- minorVersion,
669
- compatibleBrands: view.readArray(STRING, size, length)
670
- };
943
+ function readAvc4(view) {
944
+ return readVisualSampleEntryBox("avc4", view);
671
945
  }
672
946
 
673
947
  //#endregion
674
- //#region src/readers/readHdlr.ts
948
+ //#region src/readers/readCtts.ts
675
949
  /**
676
- * Parse a HandlerReferenceBox from an IsoView
950
+ * Parse a CompositionTimeToSampleBox from an IsoView
677
951
  *
678
952
  * @param view - The IsoView to read data from
679
953
  *
680
- * @returns A parsed HandlerReferenceBox
954
+ * @returns A parsed CompositionTimeToSampleBox
681
955
  *
682
956
  * @public
683
957
  */
684
- function readHdlr(view) {
958
+ function readCtts(view) {
959
+ const { version, flags } = view.readFullBox();
960
+ const read = version === 1 ? view.readInt : view.readUint;
961
+ const entryCount = view.readUint(4);
685
962
  return {
686
- type: "hdlr",
687
- ...view.readFullBox(),
688
- preDefined: view.readUint(4),
689
- handlerType: view.readString(4),
690
- reserved: view.readArray(UINT, 4, 3),
691
- name: view.readString(-1)
963
+ type: "ctts",
964
+ version,
965
+ flags,
966
+ entryCount,
967
+ entries: view.readEntries(entryCount, () => ({
968
+ sampleCount: view.readUint(4),
969
+ sampleOffset: read(4)
970
+ }))
692
971
  };
693
972
  }
694
973
 
695
974
  //#endregion
696
- //#region src/readers/readHev1.ts
975
+ //#region src/readers/readDref.ts
697
976
  /**
698
- * Parse a VisualSampleEntryBox from an IsoView
977
+ * Parse a DataReferenceBox from an IsoView
699
978
  *
700
979
  * @param view - The IsoView to read data from
701
980
  *
702
- * @returns A parsed VisualSampleEntryBox
981
+ * @returns A parsed DataReferenceBox
703
982
  *
704
983
  * @public
705
984
  */
706
- function readHev1(view) {
985
+ function readDref(view) {
986
+ const { version, flags } = view.readFullBox();
987
+ const entryCount = view.readUint(4);
707
988
  return {
708
- ...readAvc1(view),
709
- type: "hev1"
989
+ type: "dref",
990
+ version,
991
+ flags,
992
+ entryCount,
993
+ entries: view.readBoxes(entryCount)
710
994
  };
711
995
  }
712
996
 
713
997
  //#endregion
714
- //#region src/readers/readHvc1.ts
998
+ //#region src/readers/readElng.ts
715
999
  /**
716
- * Parse a VisualSampleEntryBox from an IsoView
1000
+ * Parse a ExtendedLanguageBox from an IsoView
717
1001
  *
718
1002
  * @param view - The IsoView to read data from
719
1003
  *
720
- * @returns A parsed VisualSampleEntryBox
1004
+ * @returns A parsed ExtendedLanguageBox
721
1005
  *
722
1006
  * @public
723
1007
  */
724
- function readHvc1(view) {
1008
+ function readElng(view) {
725
1009
  return {
726
- ...readAvc1(view),
727
- type: "hvc1"
1010
+ type: "elng",
1011
+ ...view.readFullBox(),
1012
+ extendedLanguage: view.readUtf8(-1)
728
1013
  };
729
1014
  }
730
1015
 
731
1016
  //#endregion
732
- //#region src/readers/readIden.ts
1017
+ //#region src/readers/readElst.ts
733
1018
  /**
734
- * Parse a WebVTTCueIdBox from an IsoView
1019
+ * Parse a Box from an IsoView
735
1020
  *
736
1021
  * @param view - The IsoView to read data from
737
1022
  *
738
- * @returns A parsed WebVTTCueIdBox
1023
+ * @returns A parsed Box
739
1024
  *
740
1025
  * @public
741
1026
  */
742
- function readIden(view) {
1027
+ function readElst(view) {
1028
+ const { version, flags } = view.readFullBox();
1029
+ const size = version === 1 ? 8 : 4;
1030
+ const entryCount = view.readUint(4);
743
1031
  return {
744
- type: "iden",
745
- cueId: view.readUtf8(-1)
1032
+ type: "elst",
1033
+ version,
1034
+ flags,
1035
+ entryCount,
1036
+ entries: view.readEntries(entryCount, () => ({
1037
+ segmentDuration: view.readUint(size),
1038
+ mediaTime: view.readInt(size),
1039
+ mediaRateInteger: view.readInt(2),
1040
+ mediaRateFraction: view.readInt(2)
1041
+ }))
746
1042
  };
747
1043
  }
748
1044
 
749
1045
  //#endregion
750
- //#region src/readers/readImda.ts
1046
+ //#region src/readers/readEmsg.ts
751
1047
  /**
752
- * Parse a IdentifiedMediaDataBox from an IsoView
1048
+ * Parse an EventMessageBox from an IsoView
753
1049
  *
754
1050
  * @param view - The IsoView to read data from
755
1051
  *
756
- * @returns A parsed IdentifiedMediaDataBox
1052
+ * @returns A parsed EventMessageBox
757
1053
  *
758
1054
  * @public
759
1055
  */
760
- function readImda(view) {
761
- return {
762
- type: "imda",
763
- imdaIdentifier: view.readUint(4),
764
- data: view.readData(-1)
765
- };
1056
+ function readEmsg(view) {
1057
+ const { readUint: readUint$1, readString: readString$1, readData: readData$1 } = view;
1058
+ const result = {
1059
+ type: "emsg",
1060
+ ...view.readFullBox()
1061
+ };
1062
+ if (result.version == 1) {
1063
+ result.timescale = readUint$1(4);
1064
+ result.presentationTime = readUint$1(8);
1065
+ result.eventDuration = readUint$1(4);
1066
+ result.id = readUint$1(4);
1067
+ result.schemeIdUri = readString$1(-1);
1068
+ result.value = readString$1(-1);
1069
+ } else {
1070
+ result.schemeIdUri = readString$1(-1);
1071
+ result.value = readString$1(-1);
1072
+ result.timescale = readUint$1(4);
1073
+ result.presentationTimeDelta = readUint$1(4);
1074
+ result.eventDuration = readUint$1(4);
1075
+ result.id = readUint$1(4);
1076
+ }
1077
+ result.messageData = readData$1(-1);
1078
+ return result;
1079
+ }
1080
+
1081
+ //#endregion
1082
+ //#region src/readers/readEnca.ts
1083
+ /**
1084
+ * Parse an AudioSampleEntry from an IsoView
1085
+ *
1086
+ * @param view - The IsoView to read data from
1087
+ *
1088
+ * @returns A parsed AudioSampleEntry
1089
+ *
1090
+ * @public
1091
+ */
1092
+ function readEnca(view) {
1093
+ return readAudioSampleEntryBox("enca", view);
1094
+ }
1095
+
1096
+ //#endregion
1097
+ //#region src/readers/readEncv.ts
1098
+ /**
1099
+ * Parse a VisualSampleEntryBox from an IsoView
1100
+ *
1101
+ * @param view - The IsoView to read data from
1102
+ *
1103
+ * @returns A parsed VisualSampleEntryBox
1104
+ *
1105
+ * @public
1106
+ */
1107
+ function readEncv(view) {
1108
+ return readVisualSampleEntryBox("encv", view);
1109
+ }
1110
+
1111
+ //#endregion
1112
+ //#region src/readers/readFree.ts
1113
+ /**
1114
+ * Parse a Box from an IsoView
1115
+ *
1116
+ * @param view - The IsoView to read data from
1117
+ *
1118
+ * @returns A parsed Box
1119
+ *
1120
+ * @public
1121
+ */
1122
+ function readFree(view) {
1123
+ return {
1124
+ type: "free",
1125
+ data: view.readData(-1)
1126
+ };
1127
+ }
1128
+
1129
+ //#endregion
1130
+ //#region src/readers/readFrma.ts
1131
+ /**
1132
+ * Parse an OriginalFormatBox from an IsoView
1133
+ *
1134
+ * @param view - The IsoView to read data from
1135
+ *
1136
+ * @returns A parsed OriginalFormatBox
1137
+ *
1138
+ * @public
1139
+ */
1140
+ function readFrma(view) {
1141
+ return {
1142
+ type: "frma",
1143
+ dataFormat: view.readUint(4)
1144
+ };
1145
+ }
1146
+
1147
+ //#endregion
1148
+ //#region src/readers/readFtyp.ts
1149
+ /**
1150
+ * Parse a FileTypeBox from an IsoView
1151
+ *
1152
+ * @param view - The IsoView to read data from
1153
+ *
1154
+ * @returns A parsed FileTypeBox
1155
+ *
1156
+ * @public
1157
+ */
1158
+ function readFtyp(view) {
1159
+ const size = 4;
1160
+ const majorBrand = view.readString(4);
1161
+ const minorVersion = view.readUint(4);
1162
+ const length = view.bytesRemaining / size;
1163
+ return {
1164
+ type: "ftyp",
1165
+ majorBrand,
1166
+ minorVersion,
1167
+ compatibleBrands: view.readArray(STRING, size, length)
1168
+ };
1169
+ }
1170
+
1171
+ //#endregion
1172
+ //#region src/readers/readHdlr.ts
1173
+ /**
1174
+ * Parse a HandlerReferenceBox from an IsoView
1175
+ *
1176
+ * @param view - The IsoView to read data from
1177
+ *
1178
+ * @returns A parsed HandlerReferenceBox
1179
+ *
1180
+ * @public
1181
+ */
1182
+ function readHdlr(view) {
1183
+ return {
1184
+ type: "hdlr",
1185
+ ...view.readFullBox(),
1186
+ preDefined: view.readUint(4),
1187
+ handlerType: view.readString(4),
1188
+ reserved: view.readArray(UINT, 4, 3),
1189
+ name: view.readString(-1)
1190
+ };
1191
+ }
1192
+
1193
+ //#endregion
1194
+ //#region src/readers/readHev1.ts
1195
+ /**
1196
+ * Parse a VisualSampleEntryBox from an IsoView
1197
+ *
1198
+ * @param view - The IsoView to read data from
1199
+ *
1200
+ * @returns A parsed VisualSampleEntryBox
1201
+ *
1202
+ * @public
1203
+ */
1204
+ function readHev1(view) {
1205
+ return readVisualSampleEntryBox("hev1", view);
1206
+ }
1207
+
1208
+ //#endregion
1209
+ //#region src/readers/readHvc1.ts
1210
+ /**
1211
+ * Parse a VisualSampleEntryBox from an IsoView
1212
+ *
1213
+ * @param view - The IsoView to read data from
1214
+ *
1215
+ * @returns A parsed VisualSampleEntryBox
1216
+ *
1217
+ * @public
1218
+ */
1219
+ function readHvc1(view) {
1220
+ return readVisualSampleEntryBox("hvc1", view);
1221
+ }
1222
+
1223
+ //#endregion
1224
+ //#region src/readers/readIden.ts
1225
+ /**
1226
+ * Parse a WebVTTCueIdBox from an IsoView
1227
+ *
1228
+ * @param view - The IsoView to read data from
1229
+ *
1230
+ * @returns A parsed WebVTTCueIdBox
1231
+ *
1232
+ * @public
1233
+ */
1234
+ function readIden(view) {
1235
+ return {
1236
+ type: "iden",
1237
+ cueId: view.readUtf8(-1)
1238
+ };
1239
+ }
1240
+
1241
+ //#endregion
1242
+ //#region src/readers/readImda.ts
1243
+ /**
1244
+ * Parse a IdentifiedMediaDataBox from an IsoView
1245
+ *
1246
+ * @param view - The IsoView to read data from
1247
+ *
1248
+ * @returns A parsed IdentifiedMediaDataBox
1249
+ *
1250
+ * @public
1251
+ */
1252
+ function readImda(view) {
1253
+ return {
1254
+ type: "imda",
1255
+ imdaIdentifier: view.readUint(4),
1256
+ data: view.readData(-1)
1257
+ };
766
1258
  }
767
1259
 
768
1260
  //#endregion
@@ -936,6 +1428,21 @@ function readMfro(view) {
936
1428
  };
937
1429
  }
938
1430
 
1431
+ //#endregion
1432
+ //#region src/readers/readMp4a.ts
1433
+ /**
1434
+ * Parse an AudioSampleEntry from an IsoView
1435
+ *
1436
+ * @param view - The IsoView to read data from
1437
+ *
1438
+ * @returns A parsed AudioSampleEntry
1439
+ *
1440
+ * @public
1441
+ */
1442
+ function readMp4a(view) {
1443
+ return readAudioSampleEntryBox("mp4a", view);
1444
+ }
1445
+
939
1446
  //#endregion
940
1447
  //#region src/readers/readMvhd.ts
941
1448
  /**
@@ -963,7 +1470,7 @@ function readMvhd(view) {
963
1470
  volume: readTemplate$1(2),
964
1471
  reserved1: readUint$1(2),
965
1472
  reserved2: readArray(UINT, 4, 2),
966
- matrix: readArray(UINT, 4, 9),
1473
+ matrix: readArray(TEMPLATE, 4, 9),
967
1474
  preDefined: readArray(UINT, 4, 6),
968
1475
  nextTrackId: readUint$1(4)
969
1476
  };
@@ -1035,7 +1542,8 @@ function readPrsl(view) {
1035
1542
  entities: view.readEntries(numEntitiesInGroup, () => ({ entityId: view.readUint(4) })),
1036
1543
  preselectionTag: flags & 4096 ? view.readUtf8(-1) : void 0,
1037
1544
  selectionPriority: flags & 8192 ? view.readUint(1) : void 0,
1038
- interleavingTag: flags & 16384 ? view.readUtf8(-1) : void 0
1545
+ interleavingTag: flags & 16384 ? view.readUtf8(-1) : void 0,
1546
+ boxes: view.readBoxes()
1039
1547
  };
1040
1548
  }
1041
1549
 
@@ -1500,15 +2008,6 @@ function readTfra(view) {
1500
2008
  };
1501
2009
  }
1502
2010
 
1503
- //#endregion
1504
- //#region src/fields/TEMPLATE.ts
1505
- /**
1506
- * The template field type
1507
- *
1508
- * @public
1509
- */
1510
- const TEMPLATE = "template";
1511
-
1512
2011
  //#endregion
1513
2012
  //#region src/readers/readTkhd.ts
1514
2013
  /**
@@ -1576,586 +2075,154 @@ function readTrex(view) {
1576
2075
  * @returns A parsed TrackRunBox
1577
2076
  *
1578
2077
  * @public
1579
- */
1580
- function readTrun(view) {
1581
- const { version, flags } = view.readFullBox();
1582
- const sampleCount = view.readUint(4);
1583
- let dataOffset;
1584
- let firstSampleFlags;
1585
- if (flags & 1) dataOffset = view.readInt(4);
1586
- if (flags & 4) firstSampleFlags = view.readUint(4);
1587
- const samples = view.readEntries(sampleCount, () => {
1588
- const sample = {};
1589
- if (flags & 256) sample.sampleDuration = view.readUint(4);
1590
- if (flags & 512) sample.sampleSize = view.readUint(4);
1591
- if (flags & 1024) sample.sampleFlags = view.readUint(4);
1592
- if (flags & 2048) sample.sampleCompositionTimeOffset = version === 1 ? view.readInt(4) : view.readUint(4);
1593
- return sample;
1594
- });
1595
- return {
1596
- type: "trun",
1597
- version,
1598
- flags,
1599
- sampleCount,
1600
- dataOffset,
1601
- firstSampleFlags,
1602
- samples
1603
- };
1604
- }
1605
-
1606
- //#endregion
1607
- //#region src/readers/readUrl.ts
1608
- /**
1609
- * Parse a UrlBox from an IsoView
1610
- *
1611
- * @param view - The IsoView to read data from
1612
- *
1613
- * @returns A parsed UrlBox
1614
- *
1615
- * @public
1616
- */
1617
- function readUrl(view) {
1618
- return {
1619
- type: "url ",
1620
- ...view.readFullBox(),
1621
- location: view.readString(-1)
1622
- };
1623
- }
1624
-
1625
- //#endregion
1626
- //#region src/readers/readUrn.ts
1627
- /**
1628
- * Parse a UrnBox from an IsoView
1629
- *
1630
- * @param view - The IsoView to read data from
1631
- *
1632
- * @returns A parsed UrnBox
1633
- *
1634
- * @public
1635
- */
1636
- function readUrn(view) {
1637
- return {
1638
- type: "urn ",
1639
- ...view.readFullBox(),
1640
- name: view.readString(-1),
1641
- location: view.readString(-1)
1642
- };
1643
- }
1644
-
1645
- //#endregion
1646
- //#region src/readers/readVlab.ts
1647
- /**
1648
- * Parse a WebVTTSourceLabelBox from an IsoView
1649
- *
1650
- * @param view - The IsoView to read data from
1651
- *
1652
- * @returns A parsed WebVTTSourceLabelBox
1653
- *
1654
- * @public
1655
- */
1656
- function readVlab(view) {
1657
- return {
1658
- type: "vlab",
1659
- sourceLabel: view.readUtf8(-1)
1660
- };
1661
- }
1662
-
1663
- //#endregion
1664
- //#region src/readers/readVmhd.ts
1665
- /**
1666
- * Parse a VideoMediaHeaderBox from an IsoView
1667
- *
1668
- * @param view - The IsoView to read data from
1669
- *
1670
- * @returns A parsed VideoMediaHeaderBox
1671
- *
1672
- * @public
1673
- */
1674
- function readVmhd(view) {
1675
- return {
1676
- type: "vmhd",
1677
- ...view.readFullBox(),
1678
- graphicsmode: view.readUint(2),
1679
- opcolor: view.readArray(UINT, 2, 3)
1680
- };
1681
- }
1682
-
1683
- //#endregion
1684
- //#region src/readers/readVttC.ts
1685
- /**
1686
- * Parse a WebVTTConfigurationBox from an IsoView
1687
- *
1688
- * @param view - The IsoView to read data from
1689
- *
1690
- * @returns A parsed WebVttConfigurationBox
1691
- *
1692
- * @public
1693
- */
1694
- function readVttC(view) {
1695
- return {
1696
- type: "vttC",
1697
- config: view.readUtf8()
1698
- };
1699
- }
1700
-
1701
- //#endregion
1702
- //#region src/readers/readVtte.ts
1703
- /**
1704
- * Parse a WebVTT Empty Sample Box from an IsoView
1705
- *
1706
- * @returns A parsed WebVTT Empty Sample Box
1707
- *
1708
- * @public
1709
- */
1710
- function readVtte(_) {
1711
- return { type: "vtte" };
1712
- }
1713
-
1714
- //#endregion
1715
- //#region src/fields/DATA.ts
1716
- /**
1717
- * The data field type
1718
- *
1719
- * @public
1720
- */
1721
- const DATA = "data";
1722
-
1723
- //#endregion
1724
- //#region src/fields/INT.ts
1725
- /**
1726
- * The integer field type
1727
- *
1728
- * @public
1729
- */
1730
- const INT = "int";
1731
-
1732
- //#endregion
1733
- //#region src/fields/UTF8.ts
1734
- /**
1735
- * The UTF8 field type
1736
- *
1737
- * @public
1738
- */
1739
- const UTF8 = "utf8";
1740
-
1741
- //#endregion
1742
- //#region src/utils/readData.ts
1743
- function readData(dataView, offset, size) {
1744
- const length = size > 0 ? size : dataView.byteLength - (offset - dataView.byteOffset);
1745
- return new Uint8Array(dataView.buffer, offset, Math.max(length, 0));
1746
- }
1747
-
1748
- //#endregion
1749
- //#region src/utils/readInt.ts
1750
- function readInt(dataView, offset, size) {
1751
- let result = NaN;
1752
- const cursor = offset - dataView.byteOffset;
1753
- switch (size) {
1754
- case 1:
1755
- result = dataView.getInt8(cursor);
1756
- break;
1757
- case 2:
1758
- result = dataView.getInt16(cursor);
1759
- break;
1760
- case 4:
1761
- result = dataView.getInt32(cursor);
1762
- break;
1763
- case 8:
1764
- const s1 = dataView.getInt32(cursor);
1765
- const s2 = dataView.getInt32(cursor + 4);
1766
- result = s1 * Math.pow(2, 32) + s2;
1767
- break;
1768
- }
1769
- return result;
1770
- }
1771
-
1772
- //#endregion
1773
- //#region src/utils/readUint.ts
1774
- function readUint(dataView, offset, size) {
1775
- const cursor = offset - dataView.byteOffset;
1776
- let value = NaN;
1777
- let s1;
1778
- let s2;
1779
- switch (size) {
1780
- case 1:
1781
- value = dataView.getUint8(cursor);
1782
- break;
1783
- case 2:
1784
- value = dataView.getUint16(cursor);
1785
- break;
1786
- case 3:
1787
- s1 = dataView.getUint16(cursor);
1788
- s2 = dataView.getUint8(cursor + 2);
1789
- value = (s1 << 8) + s2;
1790
- break;
1791
- case 4:
1792
- value = dataView.getUint32(cursor);
1793
- break;
1794
- case 8:
1795
- s1 = dataView.getUint32(cursor);
1796
- s2 = dataView.getUint32(cursor + 4);
1797
- value = s1 * Math.pow(2, 32) + s2;
1798
- break;
1799
- }
1800
- return value;
1801
- }
1802
-
1803
- //#endregion
1804
- //#region src/utils/readString.ts
1805
- function readString(dataView, offset, length) {
1806
- let str = "";
1807
- for (let c = 0; c < length; c++) {
1808
- const char = readUint(dataView, offset + c, 1);
1809
- str += String.fromCharCode(char);
1810
- }
1811
- return str;
1812
- }
1813
-
1814
- //#endregion
1815
- //#region src/utils/readTemplate.ts
1816
- function readTemplate(dataView, offset, size) {
1817
- const half = size / 2;
1818
- return readUint(dataView, offset, half) + readUint(dataView, offset + half, half) / Math.pow(2, half);
1819
- }
1820
-
1821
- //#endregion
1822
- //#region src/utils/readTerminatedString.ts
1823
- function readTerminatedString(dataView, offset) {
1824
- let str = "";
1825
- let cursor = offset;
1826
- while (cursor - dataView.byteOffset < dataView.byteLength) {
1827
- const char = readUint(dataView, cursor, 1);
1828
- if (char === 0) break;
1829
- str += String.fromCharCode(char);
1830
- cursor++;
1831
- }
1832
- return str;
1833
- }
1834
-
1835
- //#endregion
1836
- //#region src/utils/readUtf8String.ts
1837
- /**
1838
- * Reads a UTF-8 string from a data view.
1839
- *
1840
- * @param dataView - The data view to read from.
1841
- * @param offset - The offset to start reading from.
1842
- * @returns The UTF-8 string.
1843
- *
1844
- * @internal
1845
- */
1846
- function readUtf8String(dataView, offset) {
1847
- const length = dataView.byteLength - (offset - dataView.byteOffset);
1848
- return length > 0 ? decodeText(new DataView(dataView.buffer, offset, length), { encoding: UTF_8 }) : "";
1849
- }
1850
-
1851
- //#endregion
1852
- //#region src/utils/readUtf8TerminatedString.ts
1853
- /**
1854
- * Reads a UTF-8 terminated string from a data view.
1855
- *
1856
- * @param dataView - The data view to read from.
1857
- * @param offset - The offset to start reading from.
1858
- * @returns The UTF-8 terminated string.
1859
- *
1860
- * @internal
1861
- */
1862
- function readUtf8TerminatedString(dataView, offset) {
1863
- const length = dataView.byteLength - (offset - dataView.byteOffset);
1864
- let data = "";
1865
- if (length > 0) {
1866
- const view = new DataView(dataView.buffer, offset, length);
1867
- let l = 0;
1868
- for (; l < length; l++) if (view.getUint8(l) === 0) break;
1869
- data = decodeText(new DataView(dataView.buffer, offset, l), { encoding: UTF_8 });
1870
- }
1871
- return data;
1872
- }
1873
-
1874
- //#endregion
1875
- //#region src/IsoBoxReadView.ts
1876
- /**
1877
- * ISO BMFF data view. Similar to DataView, but with additional methods for reading ISO BMFF data.
1878
- * It implements the iterator protocol, so it can be used in a for...of loop.
1879
- *
1880
- * @public
1881
- */
1882
- var IsoBoxReadView = class IsoBoxReadView {
1883
- /**
1884
- * Creates a new IsoView instance. Similar to DataView, but with additional
1885
- * methods for reading ISO BMFF data. It implements the iterator protocol,
1886
- * so it can be used in a for...of loop.
1887
- *
1888
- * @param raw - The raw data to view.
1889
- * @param config - The configuration for the IsoView.
1890
- */
1891
- constructor(raw, config) {
1892
- this.truncated = false;
1893
- this.slice = (offset, size) => {
1894
- const isoView = new IsoBoxReadView(new DataView(this.dataView.buffer, offset, size), this.config);
1895
- const headerSize = this.offset - offset;
1896
- const bodySize = size - headerSize;
1897
- this.offset += bodySize;
1898
- isoView.jump(headerSize);
1899
- return isoView;
1900
- };
1901
- this.read = (type, size = 0) => {
1902
- const { dataView, offset } = this;
1903
- let result;
1904
- let cursor = size;
1905
- switch (type) {
1906
- case UINT:
1907
- result = readUint(dataView, offset, size);
1908
- break;
1909
- case INT:
1910
- result = readInt(dataView, offset, size);
1911
- break;
1912
- case TEMPLATE:
1913
- result = readTemplate(dataView, offset, size);
1914
- break;
1915
- case STRING:
1916
- if (size === -1) {
1917
- result = readTerminatedString(dataView, offset);
1918
- cursor = result.length + 1;
1919
- } else result = readString(dataView, offset, size);
1920
- break;
1921
- case DATA:
1922
- result = readData(dataView, offset, size);
1923
- cursor = result.length;
1924
- break;
1925
- case UTF8:
1926
- if (size === -1) {
1927
- result = readUtf8TerminatedString(dataView, offset);
1928
- cursor = result.length + 1;
1929
- } else result = readUtf8String(dataView, offset);
1930
- break;
1931
- default: result = -1;
1932
- }
1933
- this.offset += cursor;
1934
- return result;
1935
- };
1936
- this.readUint = (size) => {
1937
- return this.read(UINT, size);
1938
- };
1939
- this.readInt = (size) => {
1940
- return this.read(INT, size);
1941
- };
1942
- this.readString = (size) => {
1943
- return this.read(STRING, size);
1944
- };
1945
- this.readTemplate = (size) => {
1946
- return this.read(TEMPLATE, size);
1947
- };
1948
- this.readData = (size) => {
1949
- return this.read(DATA, size);
1950
- };
1951
- this.readUtf8 = (size) => {
1952
- return this.read(UTF8, size);
1953
- };
1954
- this.readFullBox = () => {
1955
- return {
1956
- version: this.readUint(1),
1957
- flags: this.readUint(3)
1958
- };
1959
- };
1960
- this.readArray = (type, size, length) => {
1961
- const value = [];
1962
- for (let i = 0; i < length; i++) value.push(this.read(type, size));
1963
- return value;
1964
- };
1965
- this.jump = (size) => {
1966
- this.offset += size;
1967
- };
1968
- this.readBox = () => {
1969
- const { dataView, offset } = this;
1970
- let cursor = 0;
1971
- const size = readUint(dataView, offset, 4);
1972
- const type = readString(dataView, offset + 4, 4);
1973
- const box = {
1974
- size,
1975
- type
1976
- };
1977
- cursor += 8;
1978
- if (box.size === 1) {
1979
- box.largesize = readUint(dataView, offset + cursor, 8);
1980
- cursor += 8;
1981
- }
1982
- const actualSize = box.size === 0 ? this.bytesRemaining : box.largesize ?? box.size;
1983
- if (this.cursor + actualSize > dataView.byteLength) {
1984
- this.truncated = true;
1985
- throw new Error("Truncated box");
1986
- }
1987
- this.jump(cursor);
1988
- if (type === "uuid") box.usertype = this.readArray("uint", 1, 16);
1989
- box.view = this.slice(offset, actualSize);
1990
- return box;
1991
- };
1992
- this.readBoxes = (length = -1) => {
1993
- const result = [];
1994
- for (const box of this) {
1995
- result.push(box);
1996
- if (length > 0 && result.length >= length) break;
1997
- }
1998
- return result;
1999
- };
2000
- this.readEntries = (length, map) => {
2001
- const result = [];
2002
- for (let i = 0; i < length; i++) result.push(map());
2003
- return result;
2004
- };
2005
- this.dataView = raw instanceof ArrayBuffer ? new DataView(raw) : raw instanceof DataView ? raw : new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
2006
- this.offset = this.dataView.byteOffset;
2007
- this.config = config || { readers: {} };
2008
- }
2009
- /**
2010
- * The buffer of the data view.
2011
- */
2012
- get buffer() {
2013
- return this.dataView.buffer;
2014
- }
2015
- /**
2016
- * The byte offset of the data view.
2017
- */
2018
- get byteOffset() {
2019
- return this.dataView.byteOffset;
2020
- }
2021
- /**
2022
- * The byte length of the data view.
2023
- */
2024
- get byteLength() {
2025
- return this.dataView.byteLength;
2026
- }
2027
- /**
2028
- * The current byteoffset in the data view.
2029
- */
2030
- get cursor() {
2031
- return this.offset - this.dataView.byteOffset;
2032
- }
2033
- /**
2034
- * Whether the end of the data view has been reached.
2035
- */
2036
- get done() {
2037
- return this.cursor >= this.dataView.byteLength || this.truncated;
2038
- }
2039
- /**
2040
- * The number of bytes remaining in the data view.
2041
- */
2042
- get bytesRemaining() {
2043
- return this.dataView.byteLength - this.cursor;
2044
- }
2045
- /**
2046
- * Iterates over the boxes in the data view.
2047
- *
2048
- * @returns A generator of boxes.
2049
- */
2050
- *[Symbol.iterator]() {
2051
- const { readers = {} } = this.config;
2052
- while (!this.done) try {
2053
- const box = this.readBox();
2054
- const { type, view } = box;
2055
- const parser = readers[type] || readers[type.trim()];
2056
- if (parser) Object.assign(box, parser(view));
2057
- if (isContainer(box) && !box.boxes) {
2058
- const boxes = [];
2059
- for (const child of view) boxes.push(child);
2060
- box.boxes = boxes;
2061
- }
2062
- yield box;
2063
- } catch (error) {
2064
- if (error instanceof Error && error.message === "Truncated box") break;
2065
- throw error;
2066
- }
2067
- }
2068
- };
2078
+ */
2079
+ function readTrun(view) {
2080
+ const { version, flags } = view.readFullBox();
2081
+ const sampleCount = view.readUint(4);
2082
+ let dataOffset;
2083
+ let firstSampleFlags;
2084
+ if (flags & 1) dataOffset = view.readInt(4);
2085
+ if (flags & 4) firstSampleFlags = view.readUint(4);
2086
+ const samples = view.readEntries(sampleCount, () => {
2087
+ const sample = {};
2088
+ if (flags & 256) sample.sampleDuration = view.readUint(4);
2089
+ if (flags & 512) sample.sampleSize = view.readUint(4);
2090
+ if (flags & 1024) sample.sampleFlags = view.readUint(4);
2091
+ if (flags & 2048) sample.sampleCompositionTimeOffset = version === 1 ? view.readInt(4) : view.readUint(4);
2092
+ return sample;
2093
+ });
2094
+ return {
2095
+ type: "trun",
2096
+ version,
2097
+ flags,
2098
+ sampleCount,
2099
+ dataOffset,
2100
+ firstSampleFlags,
2101
+ samples
2102
+ };
2103
+ }
2069
2104
 
2070
2105
  //#endregion
2071
- //#region src/readIsoBoxes.ts
2106
+ //#region src/readers/readUrl.ts
2072
2107
  /**
2073
- * Reads ISO boxes from a data source.
2108
+ * Parse a UrlBox from an IsoView
2074
2109
  *
2075
- * @param raw - The raw ISO data
2076
- * @param config - The configuration for the IsoView
2110
+ * @param view - The IsoView to read data from
2077
2111
  *
2078
- * @returns The parsed boxes
2112
+ * @returns A parsed UrlBox
2079
2113
  *
2080
- * @example
2081
- * {@includeCode ../test/readIsoBoxes.test.ts#example}
2114
+ * @public
2115
+ */
2116
+ function readUrl(view) {
2117
+ return {
2118
+ type: "url ",
2119
+ ...view.readFullBox(),
2120
+ location: view.readString(-1)
2121
+ };
2122
+ }
2123
+
2124
+ //#endregion
2125
+ //#region src/readers/readUrn.ts
2126
+ /**
2127
+ * Parse a UrnBox from an IsoView
2128
+ *
2129
+ * @param view - The IsoView to read data from
2130
+ *
2131
+ * @returns A parsed UrnBox
2082
2132
  *
2083
2133
  * @public
2084
2134
  */
2085
- function readIsoBoxes(raw, config) {
2086
- const boxes = [];
2087
- for (const box of new IsoBoxReadView(raw, config)) boxes.push(box);
2088
- return boxes;
2135
+ function readUrn(view) {
2136
+ return {
2137
+ type: "urn ",
2138
+ ...view.readFullBox(),
2139
+ name: view.readString(-1),
2140
+ location: view.readString(-1)
2141
+ };
2089
2142
  }
2090
2143
 
2091
2144
  //#endregion
2092
- //#region src/traverseIsoBoxes.ts
2145
+ //#region src/readers/readVlab.ts
2093
2146
  /**
2094
- * Traverse ISO boxes
2147
+ * Parse a WebVTTSourceLabelBox from an IsoView
2095
2148
  *
2096
- * @param boxes - The boxes to traverse
2097
- * @param depthFirst - Whether to traverse the boxes depth-first or breadth-first
2098
- * @param maxDepth - The maximum depth to traverse. A value of 0 will only traverse the root boxes.
2149
+ * @param view - The IsoView to read data from
2099
2150
  *
2100
- * @returns A generator of boxes
2151
+ * @returns A parsed WebVTTSourceLabelBox
2101
2152
  *
2102
- * @example
2103
- * {@includeCode ../test/traverseIsoBoxes.test.ts#example}
2153
+ * @public
2154
+ */
2155
+ function readVlab(view) {
2156
+ return {
2157
+ type: "vlab",
2158
+ sourceLabel: view.readUtf8(-1)
2159
+ };
2160
+ }
2161
+
2162
+ //#endregion
2163
+ //#region src/readers/readVmhd.ts
2164
+ /**
2165
+ * Parse a VideoMediaHeaderBox from an IsoView
2166
+ *
2167
+ * @param view - The IsoView to read data from
2168
+ *
2169
+ * @returns A parsed VideoMediaHeaderBox
2104
2170
  *
2105
2171
  * @public
2106
2172
  */
2107
- function* traverseIsoBoxes(boxes, depthFirst = true, maxDepth = Infinity) {
2108
- if (maxDepth < 0 || typeof maxDepth !== "number" || Number.isNaN(maxDepth)) return;
2109
- const queue = [[boxes, 0]];
2110
- while (queue.length > 0) {
2111
- const item = queue.shift();
2112
- if (!item) continue;
2113
- const [children, depth] = item;
2114
- for (const child of children) {
2115
- yield child;
2116
- if (depth >= maxDepth) continue;
2117
- if (isContainer(child) && child.boxes) {
2118
- const next = child.boxes;
2119
- if (depthFirst) yield* traverseIsoBoxes(next, depthFirst, maxDepth - 1);
2120
- else queue.push([next, depth + 1]);
2121
- }
2122
- }
2123
- }
2173
+ function readVmhd(view) {
2174
+ return {
2175
+ type: "vmhd",
2176
+ ...view.readFullBox(),
2177
+ graphicsmode: view.readUint(2),
2178
+ opcolor: view.readArray(UINT, 2, 3)
2179
+ };
2124
2180
  }
2125
2181
 
2126
2182
  //#endregion
2127
- //#region src/utils/isFullBox.ts
2183
+ //#region src/readers/readVttC.ts
2128
2184
  /**
2129
- * Check if a box is a full box
2185
+ * Parse a WebVTTConfigurationBox from an IsoView
2130
2186
  *
2131
- * @param box - The box to check
2187
+ * @param view - The IsoView to read data from
2132
2188
  *
2133
- * @returns `true` if the box is a full box, `false` otherwise
2189
+ * @returns A parsed WebVttConfigurationBox
2134
2190
  *
2135
2191
  * @public
2136
2192
  */
2137
- function isFullBox(box) {
2138
- return "version" in box && "flags" in box;
2193
+ function readVttC(view) {
2194
+ return {
2195
+ type: "vttC",
2196
+ config: view.readUtf8()
2197
+ };
2139
2198
  }
2140
2199
 
2141
2200
  //#endregion
2142
- //#region src/writeIsoBoxes.ts
2201
+ //#region src/readers/readVtte.ts
2143
2202
  /**
2144
- * Writes ISO boxes to a readable stream.
2203
+ * Parse a WebVTT Empty Sample Box from an IsoView
2145
2204
  *
2146
- * @param boxes - The boxes to write
2147
- * @param config - The configuration for the readable stream
2205
+ * @returns A parsed WebVTT Empty Sample Box
2148
2206
  *
2149
- * @returns A readable stream of the written boxes
2207
+ * @public
2208
+ */
2209
+ function readVtte(_) {
2210
+ return { type: "vtte" };
2211
+ }
2212
+
2213
+ //#endregion
2214
+ //#region src/utils/isFullBox.ts
2215
+ /**
2216
+ * Check if a box is a full box
2150
2217
  *
2151
- * @example
2152
- * {@includeCode ../test/writeIsoBoxes.test.ts#example}
2218
+ * @param box - The box to check
2219
+ *
2220
+ * @returns `true` if the box is a full box, `false` otherwise
2153
2221
  *
2154
2222
  * @public
2155
2223
  */
2156
- function writeIsoBoxes(boxes, config) {
2157
- const { writers = {} } = config ?? {};
2158
- return Array.from(boxes, (box) => writeIsoBox(box, writers));
2224
+ function isFullBox(box) {
2225
+ return "version" in box && "flags" in box;
2159
2226
  }
2160
2227
 
2161
2228
  //#endregion
@@ -2176,6 +2243,45 @@ function writeArdi(box) {
2176
2243
  return writer;
2177
2244
  }
2178
2245
 
2246
+ //#endregion
2247
+ //#region src/writers/writeAudioSampleEntryBox.ts
2248
+ /**
2249
+ * Write an AudioSampleEntryBox to an IsoDataWriter.
2250
+ *
2251
+ * ISO/IEC 14496-12:2012 - 12.2.3 Audio Sample Entry
2252
+ *
2253
+ * @param box - The AudioSampleEntryBox fields to write
2254
+ * @param config - The IsoBoxWriteViewConfig to use
2255
+ *
2256
+ * @returns An IsoDataWriter containing the encoded box
2257
+ *
2258
+ * @public
2259
+ */
2260
+ function writeAudioSampleEntryBox(box, config) {
2261
+ const headerSize = 8;
2262
+ const reserved1Size = 6;
2263
+ const dataReferenceIndexSize = 2;
2264
+ const reserved2Size = 8;
2265
+ const channelcountSize = 2;
2266
+ const samplesizeSize = 2;
2267
+ const preDefinedSize = 2;
2268
+ const reserved3Size = 2;
2269
+ const samplerateSize = 4;
2270
+ const { bytes, size } = writeChildBoxes(box.boxes, config);
2271
+ const totalSize = headerSize + reserved1Size + dataReferenceIndexSize + reserved2Size + channelcountSize + samplesizeSize + preDefinedSize + reserved3Size + samplerateSize + size;
2272
+ const writer = new IsoBoxWriteView(box.type, totalSize);
2273
+ writer.writeArray(box.reserved1, UINT, 1, 6);
2274
+ writer.writeUint(box.dataReferenceIndex, 2);
2275
+ writer.writeArray(box.reserved2, UINT, 4, 2);
2276
+ writer.writeUint(box.channelcount, 2);
2277
+ writer.writeUint(box.samplesize, 2);
2278
+ writer.writeUint(box.preDefined, 2);
2279
+ writer.writeUint(box.reserved3, 2);
2280
+ writer.writeTemplate(box.samplerate, 4);
2281
+ writer.writeBytes(bytes);
2282
+ return writer;
2283
+ }
2284
+
2179
2285
  //#endregion
2180
2286
  //#region src/writers/writeVisualSampleEntryBox.ts
2181
2287
  /**
@@ -2184,13 +2290,13 @@ function writeArdi(box) {
2184
2290
  * ISO/IEC 14496-12:2012 - 12.1.3 Visual Sample Entry
2185
2291
  *
2186
2292
  * @param box - The VisualSampleEntryBox fields to write
2187
- * @param type - The box type
2293
+ * @param config - The configuration for the writer
2188
2294
  *
2189
2295
  * @returns An IsoDataWriter containing the encoded box
2190
2296
  *
2191
2297
  * @public
2192
2298
  */
2193
- function writeVisualSampleEntryBox(box, type) {
2299
+ function writeVisualSampleEntryBox(box, config) {
2194
2300
  const headerSize = 8;
2195
2301
  const reserved1Size = 6;
2196
2302
  const dataReferenceIndexSize = 2;
@@ -2206,23 +2312,24 @@ function writeVisualSampleEntryBox(box, type) {
2206
2312
  const compressorNameSize = 32;
2207
2313
  const depthSize = 2;
2208
2314
  const preDefined3Size = 2;
2209
- const configSize = box.config.length;
2210
- const writer = new IsoBoxWriteView(type, headerSize + reserved1Size + dataReferenceIndexSize + preDefined1Size + reserved2Size + preDefined2Size + widthSize + heightSize + horizresolutionSize + vertresolutionSize + reserved3Size + frameCountSize + compressorNameSize + depthSize + preDefined3Size + configSize);
2211
- for (let i = 0; i < 6; i++) writer.writeUint(box.reserved1[i] ?? 0, 1);
2315
+ const { bytes, size } = writeChildBoxes(box.boxes, config);
2316
+ const totalSize = headerSize + reserved1Size + dataReferenceIndexSize + preDefined1Size + reserved2Size + preDefined2Size + widthSize + heightSize + horizresolutionSize + vertresolutionSize + reserved3Size + frameCountSize + compressorNameSize + depthSize + preDefined3Size + size;
2317
+ const writer = new IsoBoxWriteView(box.type, totalSize);
2318
+ writer.writeArray(box.reserved1, UINT, 1, 6);
2212
2319
  writer.writeUint(box.dataReferenceIndex, 2);
2213
2320
  writer.writeUint(box.preDefined1, 2);
2214
2321
  writer.writeUint(box.reserved2, 2);
2215
- for (let i = 0; i < 3; i++) writer.writeUint(box.preDefined2[i] ?? 0, 4);
2322
+ writer.writeArray(box.preDefined2, UINT, 4, 3);
2216
2323
  writer.writeUint(box.width, 2);
2217
2324
  writer.writeUint(box.height, 2);
2218
2325
  writer.writeTemplate(box.horizresolution, 4);
2219
2326
  writer.writeTemplate(box.vertresolution, 4);
2220
2327
  writer.writeUint(box.reserved3, 4);
2221
2328
  writer.writeUint(box.frameCount, 2);
2222
- for (let i = 0; i < 32; i++) writer.writeUint(box.compressorName[i] ?? 0, 1);
2329
+ writer.writeArray(box.compressorName, UINT, 1, 32);
2223
2330
  writer.writeUint(box.depth, 2);
2224
2331
  writer.writeUint(box.preDefined3 & 65535, 2);
2225
- writer.writeBytes(box.config);
2332
+ writer.writeBytes(bytes);
2226
2333
  return writer;
2227
2334
  }
2228
2335
 
@@ -2234,13 +2341,14 @@ function writeVisualSampleEntryBox(box, type) {
2234
2341
  * ISO/IEC 14496-12:2012 - 12.1.3 Visual Sample Entry
2235
2342
  *
2236
2343
  * @param box - The VisualSampleEntryBox fields to write
2344
+ * @param config - The IsoBoxWriteViewConfig to use
2237
2345
  *
2238
2346
  * @returns An IsoDataWriter containing the encoded box
2239
2347
  *
2240
2348
  * @public
2241
2349
  */
2242
- function writeAvc1(box) {
2243
- return writeVisualSampleEntryBox(box, "avc1");
2350
+ function writeAvc1(box, config) {
2351
+ return writeVisualSampleEntryBox(box, config);
2244
2352
  }
2245
2353
 
2246
2354
  //#endregion
@@ -2249,13 +2357,14 @@ function writeAvc1(box) {
2249
2357
  * Write a VisualSampleEntryBox (avc2) to an IsoDataWriter.
2250
2358
  *
2251
2359
  * @param box - The VisualSampleEntryBox fields to write
2360
+ * @param config - The IsoBoxWriteViewConfig to use
2252
2361
  *
2253
2362
  * @returns An IsoDataWriter containing the encoded box
2254
2363
  *
2255
2364
  * @public
2256
2365
  */
2257
- function writeAvc2(box) {
2258
- return writeVisualSampleEntryBox(box, "avc2");
2366
+ function writeAvc2(box, config) {
2367
+ return writeVisualSampleEntryBox(box, config);
2259
2368
  }
2260
2369
 
2261
2370
  //#endregion
@@ -2264,13 +2373,14 @@ function writeAvc2(box) {
2264
2373
  * Write a VisualSampleEntryBox (avc3) to an IsoDataWriter.
2265
2374
  *
2266
2375
  * @param box - The VisualSampleEntryBox fields to write
2376
+ * @param config - The IsoBoxWriteViewConfig to use
2267
2377
  *
2268
2378
  * @returns An IsoDataWriter containing the encoded box
2269
2379
  *
2270
2380
  * @public
2271
2381
  */
2272
- function writeAvc3(box) {
2273
- return writeVisualSampleEntryBox(box, "avc3");
2382
+ function writeAvc3(box, config) {
2383
+ return writeVisualSampleEntryBox(box, config);
2274
2384
  }
2275
2385
 
2276
2386
  //#endregion
@@ -2279,13 +2389,14 @@ function writeAvc3(box) {
2279
2389
  * Write a VisualSampleEntryBox (avc4) to an IsoDataWriter.
2280
2390
  *
2281
2391
  * @param box - The VisualSampleEntryBox fields to write
2392
+ * @param config - The IsoBoxWriteViewConfig to use
2282
2393
  *
2283
2394
  * @returns An IsoDataWriter containing the encoded box
2284
2395
  *
2285
2396
  * @public
2286
2397
  */
2287
- function writeAvc4(box) {
2288
- return writeVisualSampleEntryBox(box, "avc4");
2398
+ function writeAvc4(box, config) {
2399
+ return writeVisualSampleEntryBox(box, config);
2289
2400
  }
2290
2401
 
2291
2402
  //#endregion
@@ -2316,6 +2427,31 @@ function writeCtts(box) {
2316
2427
  return writer;
2317
2428
  }
2318
2429
 
2430
+ //#endregion
2431
+ //#region src/writers/writeDref.ts
2432
+ /**
2433
+ * Write a DataReferenceBox to an IsoDataWriter.
2434
+ *
2435
+ * @param box - The DataReferenceBox fields to write
2436
+ * @param config - The IsoBoxWriteViewConfig to use
2437
+ *
2438
+ * @returns An IsoDataWriter containing the encoded box
2439
+ *
2440
+ * @public
2441
+ */
2442
+ function writeDref(box, config) {
2443
+ const headerSize = 8;
2444
+ const fullBoxSize = 4;
2445
+ const entryCountSize = 4;
2446
+ const entryCount = box.entries.length;
2447
+ const { bytes, size } = writeChildBoxes(box.entries, config);
2448
+ const writer = new IsoBoxWriteView("dref", headerSize + fullBoxSize + entryCountSize + size);
2449
+ writer.writeFullBox(box.version, box.flags);
2450
+ writer.writeUint(entryCount, 4);
2451
+ writer.writeBytes(bytes);
2452
+ return writer;
2453
+ }
2454
+
2319
2455
  //#endregion
2320
2456
  //#region src/writers/writeElng.ts
2321
2457
  /**
@@ -2418,33 +2554,14 @@ function writeEmsg(box) {
2418
2554
  * Write an AudioSampleEntryBox (enca) to an IsoDataWriter.
2419
2555
  *
2420
2556
  * @param box - The AudioSampleEntryBox fields to write
2557
+ * @param config - The IsoBoxWriteViewConfig to use
2421
2558
  *
2422
2559
  * @returns An IsoDataWriter containing the encoded box
2423
2560
  *
2424
2561
  * @public
2425
2562
  */
2426
- function writeEnca(box) {
2427
- const headerSize = 8;
2428
- const reserved1Size = 6;
2429
- const dataReferenceIndexSize = 2;
2430
- const reserved2Size = 8;
2431
- const channelcountSize = 2;
2432
- const samplesizeSize = 2;
2433
- const preDefinedSize = 2;
2434
- const reserved3Size = 2;
2435
- const samplerateSize = 4;
2436
- const esdsSize = box.esds.length;
2437
- const writer = new IsoBoxWriteView("enca", headerSize + reserved1Size + dataReferenceIndexSize + reserved2Size + channelcountSize + samplesizeSize + preDefinedSize + reserved3Size + samplerateSize + esdsSize);
2438
- for (let i = 0; i < 6; i++) writer.writeUint(box.reserved1[i] ?? 0, 1);
2439
- writer.writeUint(box.dataReferenceIndex, 2);
2440
- for (let i = 0; i < 2; i++) writer.writeUint(box.reserved2[i] ?? 0, 4);
2441
- writer.writeUint(box.channelcount, 2);
2442
- writer.writeUint(box.samplesize, 2);
2443
- writer.writeUint(box.preDefined, 2);
2444
- writer.writeUint(box.reserved3, 2);
2445
- writer.writeTemplate(box.samplerate, 4);
2446
- writer.writeBytes(box.esds);
2447
- return writer;
2563
+ function writeEnca(box, config) {
2564
+ return writeAudioSampleEntryBox(box, config);
2448
2565
  }
2449
2566
 
2450
2567
  //#endregion
@@ -2453,13 +2570,14 @@ function writeEnca(box) {
2453
2570
  * Write a VisualSampleEntryBox (encv) to an IsoDataWriter.
2454
2571
  *
2455
2572
  * @param box - The VisualSampleEntryBox fields to write
2573
+ * @param config - The IsoBoxWriteViewConfig to use
2456
2574
  *
2457
2575
  * @returns An IsoDataWriter containing the encoded box
2458
2576
  *
2459
2577
  * @public
2460
2578
  */
2461
- function writeEncv(box) {
2462
- return writeVisualSampleEntryBox(box, "encv");
2579
+ function writeEncv(box, config) {
2580
+ return writeVisualSampleEntryBox(box, config);
2463
2581
  }
2464
2582
 
2465
2583
  //#endregion
@@ -2549,7 +2667,7 @@ function writeHdlr(box) {
2549
2667
  writer.writeFullBox(box.version, box.flags);
2550
2668
  writer.writeUint(box.preDefined, 4);
2551
2669
  writer.writeString(box.handlerType);
2552
- for (let i = 0; i < 3; i++) writer.writeUint(box.reserved[i] ?? 0, 4);
2670
+ writer.writeArray(box.reserved, UINT, 4, 3);
2553
2671
  writer.writeTerminatedString(box.name);
2554
2672
  return writer;
2555
2673
  }
@@ -2560,13 +2678,14 @@ function writeHdlr(box) {
2560
2678
  * Write a VisualSampleEntryBox (hev1) to an IsoDataWriter.
2561
2679
  *
2562
2680
  * @param box - The VisualSampleEntryBox fields to write
2681
+ * @param config - The IsoBoxWriteViewConfig to use
2563
2682
  *
2564
2683
  * @returns An IsoDataWriter containing the encoded box
2565
2684
  *
2566
2685
  * @public
2567
2686
  */
2568
- function writeHev1(box) {
2569
- return writeVisualSampleEntryBox(box, "hev1");
2687
+ function writeHev1(box, config) {
2688
+ return writeVisualSampleEntryBox(box, config);
2570
2689
  }
2571
2690
 
2572
2691
  //#endregion
@@ -2575,13 +2694,14 @@ function writeHev1(box) {
2575
2694
  * Write a VisualSampleEntryBox (hvc1) to an IsoDataWriter.
2576
2695
  *
2577
2696
  * @param box - The VisualSampleEntryBox fields to write
2697
+ * @param config - The IsoBoxWriteViewConfig to use
2578
2698
  *
2579
2699
  * @returns An IsoDataWriter containing the encoded box
2580
2700
  *
2581
2701
  * @public
2582
2702
  */
2583
- function writeHvc1(box) {
2584
- return writeVisualSampleEntryBox(box, "hvc1");
2703
+ function writeHvc1(box, config) {
2704
+ return writeVisualSampleEntryBox(box, config);
2585
2705
  }
2586
2706
 
2587
2707
  //#endregion
@@ -2755,14 +2875,19 @@ function writeMehd(box) {
2755
2875
  * ISO/IEC 14496-12:2012 - 8.11.1 Meta Box
2756
2876
  *
2757
2877
  * @param box - The MetaBox fields to write
2878
+ * @param config - The IsoBoxWriteViewConfig to use
2758
2879
  *
2759
2880
  * @returns An IsoDataWriter containing the encoded box
2760
2881
  *
2761
2882
  * @public
2762
2883
  */
2763
- function writeMeta(box) {
2764
- const writer = new IsoBoxWriteView("meta", 12);
2884
+ function writeMeta(box, config) {
2885
+ const headerSize = 8;
2886
+ const fullBoxSize = 4;
2887
+ const { bytes, size } = writeChildBoxes(box.boxes, config);
2888
+ const writer = new IsoBoxWriteView("meta", headerSize + fullBoxSize + size);
2765
2889
  writer.writeFullBox(box.version, box.flags);
2890
+ writer.writeBytes(bytes);
2766
2891
  return writer;
2767
2892
  }
2768
2893
 
@@ -2819,28 +2944,8 @@ function writeMfro(box) {
2819
2944
  *
2820
2945
  * @public
2821
2946
  */
2822
- function writeMp4a(box) {
2823
- const headerSize = 8;
2824
- const reserved1Size = 6;
2825
- const dataReferenceIndexSize = 2;
2826
- const reserved2Size = 8;
2827
- const channelcountSize = 2;
2828
- const samplesizeSize = 2;
2829
- const preDefinedSize = 2;
2830
- const reserved3Size = 2;
2831
- const samplerateSize = 4;
2832
- const esdsSize = box.esds.length;
2833
- const writer = new IsoBoxWriteView("mp4a", headerSize + reserved1Size + dataReferenceIndexSize + reserved2Size + channelcountSize + samplesizeSize + preDefinedSize + reserved3Size + samplerateSize + esdsSize);
2834
- for (let i = 0; i < 6; i++) writer.writeUint(box.reserved1[i] ?? 0, 1);
2835
- writer.writeUint(box.dataReferenceIndex, 2);
2836
- for (let i = 0; i < 2; i++) writer.writeUint(box.reserved2[i] ?? 0, 4);
2837
- writer.writeUint(box.channelcount, 2);
2838
- writer.writeUint(box.samplesize, 2);
2839
- writer.writeUint(box.preDefined, 2);
2840
- writer.writeUint(box.reserved3, 2);
2841
- writer.writeTemplate(box.samplerate, 4);
2842
- writer.writeBytes(box.esds);
2843
- return writer;
2947
+ function writeMp4a(box, config) {
2948
+ return writeAudioSampleEntryBox(box, config);
2844
2949
  }
2845
2950
 
2846
2951
  //#endregion
@@ -2870,9 +2975,9 @@ function writeMvhd(box) {
2870
2975
  writer.writeTemplate(box.rate, 4);
2871
2976
  writer.writeTemplate(box.volume, 2);
2872
2977
  writer.writeUint(box.reserved1, 2);
2873
- for (let i = 0; i < 2; i++) writer.writeUint(box.reserved2[i] ?? 0, 4);
2874
- for (let i = 0; i < 9; i++) writer.writeTemplate(box.matrix[i] ?? 0, 4);
2875
- for (let i = 0; i < 6; i++) writer.writeUint(box.preDefined[i] ?? 0, 4);
2978
+ writer.writeArray(box.reserved2, UINT, 4, 2);
2979
+ writer.writeArray(box.matrix, TEMPLATE, 4, 9);
2980
+ writer.writeArray(box.preDefined, UINT, 4, 6);
2876
2981
  writer.writeUint(box.nextTrackId, 4);
2877
2982
  return writer;
2878
2983
  }
@@ -2924,12 +3029,13 @@ function writePrft(box) {
2924
3029
  * Write a PreselectionGroupBox to an IsoDataWriter.
2925
3030
  *
2926
3031
  * @param box - The PreselectionGroupBox fields to write
3032
+ * @param config - The IsoBoxWriteViewConfig to use
2927
3033
  *
2928
3034
  * @returns An IsoDataWriter containing the encoded box
2929
3035
  *
2930
3036
  * @public
2931
3037
  */
2932
- function writePrsl(box) {
3038
+ function writePrsl(box, config) {
2933
3039
  const preselectionTagBytes = box.flags & 4096 && box.preselectionTag ? encodeText(box.preselectionTag) : null;
2934
3040
  const interleavingTagBytes = box.flags & 16384 && box.interleavingTag ? encodeText(box.interleavingTag) : null;
2935
3041
  const headerSize = 8;
@@ -2940,7 +3046,8 @@ function writePrsl(box) {
2940
3046
  const preselectionTagSize = preselectionTagBytes ? preselectionTagBytes.length + 1 : 0;
2941
3047
  const selectionPrioritySize = box.flags & 8192 ? 1 : 0;
2942
3048
  const interleavingTagSize = interleavingTagBytes ? interleavingTagBytes.length + 1 : 0;
2943
- const writer = new IsoBoxWriteView("prsl", headerSize + fullBoxSize + groupIdSize + numEntitiesInGroupSize + entitiesSize + preselectionTagSize + selectionPrioritySize + interleavingTagSize);
3049
+ const { bytes, size } = writeChildBoxes(box.boxes, config);
3050
+ const writer = new IsoBoxWriteView("prsl", headerSize + fullBoxSize + groupIdSize + numEntitiesInGroupSize + entitiesSize + preselectionTagSize + selectionPrioritySize + interleavingTagSize + size);
2944
3051
  writer.writeFullBox(box.version, box.flags);
2945
3052
  writer.writeUint(box.groupId, 4);
2946
3053
  writer.writeUint(box.numEntitiesInGroup, 4);
@@ -2948,6 +3055,7 @@ function writePrsl(box) {
2948
3055
  if (preselectionTagBytes && box.preselectionTag) writer.writeUtf8TerminatedString(box.preselectionTag);
2949
3056
  if (box.flags & 8192) writer.writeUint(box.selectionPriority ?? 0, 1);
2950
3057
  if (interleavingTagBytes && box.interleavingTag) writer.writeUtf8TerminatedString(box.interleavingTag);
3058
+ writer.writeBytes(bytes);
2951
3059
  return writer;
2952
3060
  }
2953
3061
 
@@ -2974,13 +3082,13 @@ function writePssh(box) {
2974
3082
  const dataSize = box.dataSize;
2975
3083
  const writer = new IsoBoxWriteView("pssh", headerSize + fullBoxSize + systemIdSize + kidCountSize + kidSize + dataSizeField + dataSize);
2976
3084
  writer.writeFullBox(box.version, box.flags);
2977
- for (let i = 0; i < 16; i++) writer.writeUint(box.systemId[i] ?? 0, 1);
3085
+ writer.writeArray(box.systemId, UINT, 1, 16);
2978
3086
  if (box.version > 0) {
2979
3087
  writer.writeUint(box.kidCount, 4);
2980
- for (let i = 0; i < box.kidCount; i++) writer.writeUint(box.kid[i] ?? 0, 1);
3088
+ writer.writeArray(box.kid, UINT, 1, box.kidCount);
2981
3089
  }
2982
3090
  writer.writeUint(box.dataSize, 4);
2983
- for (let i = 0; i < box.dataSize; i++) writer.writeUint(box.data[i] ?? 0, 1);
3091
+ writer.writeArray(box.data, UINT, 1, box.dataSize);
2984
3092
  return writer;
2985
3093
  }
2986
3094
 
@@ -3170,6 +3278,31 @@ function writeSthd(box) {
3170
3278
  return writer;
3171
3279
  }
3172
3280
 
3281
+ //#endregion
3282
+ //#region src/writers/writeStsd.ts
3283
+ /**
3284
+ * Write a SampleDescriptionBox to an IsoDataWriter.
3285
+ *
3286
+ * @param box - The SampleDescriptionBox fields to write
3287
+ * @param config - The IsoBoxWriteViewConfig to use
3288
+ *
3289
+ * @returns An IsoDataWriter containing the encoded box
3290
+ *
3291
+ * @public
3292
+ */
3293
+ function writeStsd(box, config) {
3294
+ const headerSize = 8;
3295
+ const fullBoxSize = 4;
3296
+ const entryCountSize = 4;
3297
+ const entryCount = box.entries.length;
3298
+ const { bytes, size } = writeChildBoxes(box.entries, config);
3299
+ const writer = new IsoBoxWriteView("stsd", headerSize + fullBoxSize + entryCountSize + size);
3300
+ writer.writeFullBox(box.version, box.flags);
3301
+ writer.writeUint(entryCount, 4);
3302
+ writer.writeBytes(bytes);
3303
+ return writer;
3304
+ }
3305
+
3173
3306
  //#endregion
3174
3307
  //#region src/writers/writeStss.ts
3175
3308
  /**
@@ -3320,7 +3453,7 @@ function writeTenc(box) {
3320
3453
  writer.writeFullBox(box.version, box.flags);
3321
3454
  writer.writeUint(box.defaultIsEncrypted, 3);
3322
3455
  writer.writeUint(box.defaultIvSize, 1);
3323
- for (let i = 0; i < 16; i++) writer.writeUint(box.defaultKid[i] ?? 0, 1);
3456
+ writer.writeArray(box.defaultKid, UINT, 1, 16);
3324
3457
  return writer;
3325
3458
  }
3326
3459
 
@@ -3444,12 +3577,12 @@ function writeTkhd(box) {
3444
3577
  writer.writeUint(box.trackId, 4);
3445
3578
  writer.writeUint(box.reserved1, 4);
3446
3579
  writer.writeUint(box.duration, size);
3447
- for (let i = 0; i < 2; i++) writer.writeUint(box.reserved2[i] ?? 0, 4);
3580
+ writer.writeArray(box.reserved2, UINT, 4, 2);
3448
3581
  writer.writeUint(box.layer, 2);
3449
3582
  writer.writeUint(box.alternateGroup, 2);
3450
3583
  writer.writeTemplate(box.volume, 2);
3451
3584
  writer.writeUint(box.reserved3, 2);
3452
- for (let i = 0; i < 9; i++) writer.writeTemplate(box.matrix[i] ?? 0, 4);
3585
+ writer.writeArray(box.matrix, TEMPLATE, 4, 9);
3453
3586
  writer.writeTemplate(box.width, 4);
3454
3587
  writer.writeTemplate(box.height, 4);
3455
3588
  return writer;
@@ -3600,7 +3733,7 @@ function writeVmhd(box) {
3600
3733
  const writer = new IsoBoxWriteView("vmhd", 20);
3601
3734
  writer.writeFullBox(box.version, box.flags);
3602
3735
  writer.writeUint(box.graphicsmode, 2);
3603
- for (let i = 0; i < 3; i++) writer.writeUint(box.opcolor[i] ?? 0, 2);
3736
+ writer.writeArray(box.opcolor, UINT, 2, 3);
3604
3737
  return writer;
3605
3738
  }
3606
3739
 
@@ -3636,5 +3769,5 @@ function writeVtte(_) {
3636
3769
  }
3637
3770
 
3638
3771
  //#endregion
3639
- export { IsoBoxReadableStream, createIsoBoxReadableStream, isContainer, isFullBox, readArdi, readAvc1, readAvc2, readAvc3, readAvc4, readCtts, readDref, readElng, readElst, readEmsg, readEnca, readEncv, readFree, readFrma, readFtyp, readHdlr, readHev1, readHvc1, readIden, readImda, readIsoBoxes, readKind, readLabl, readMdat, readMdhd, readMehd, readMeta, readMfhd, readMfro, readMp4a, readMvhd, readPayl, readPrft, readPrsl, readPssh, readSchm, readSdtp, readSidx, readSkip, readSmhd, readSsix, readSthd, readStsd, readStss, readSttg, readStts, readStyp, readSubs, readTenc, readTfdt, readTfhd, readTfra, readTkhd, readTrex, readTrun, readUrl, readUrn, readVlab, readVmhd, readVttC, readVtte, traverseIsoBoxes, writeArdi, writeAvc1, writeAvc2, writeAvc3, writeAvc4, writeCtts, writeElng, writeElst, writeEmsg, writeEnca, writeEncv, writeFree, writeFrma, writeFtyp, writeHdlr, writeHev1, writeHvc1, writeIden, writeImda, writeIsoBox, writeIsoBoxes, writeKind, writeLabl, writeMdat, writeMdhd, writeMehd, writeMeta, writeMfhd, writeMfro, writeMp4a, writeMvhd, writePayl, writePrft, writePrsl, writePssh, writeSchm, writeSdtp, writeSidx, writeSkip, writeSmhd, writeSsix, writeSthd, writeStss, writeSttg, writeStts, writeStyp, writeSubs, writeTenc, writeTfdt, writeTfhd, writeTfra, writeTkhd, writeTrex, writeTrun, writeUrl, writeUrn, writeVisualSampleEntryBox, writeVlab, writeVmhd, writeVttC, writeVtte };
3772
+ export { CONTAINERS, IsoBoxReadableStream, createIsoBoxReadableStream, isContainer, isFullBox, readArdi, readAudioSampleEntryBox, readAvc1, readAvc2, readAvc3, readAvc4, readCtts, readDref, readElng, readElst, readEmsg, readEnca, readEncv, readFree, readFrma, readFtyp, readHdlr, readHev1, readHvc1, readIden, readImda, readIsoBoxes, readKind, readLabl, readMdat, readMdhd, readMehd, readMeta, readMfhd, readMfro, readMp4a, readMvhd, readPayl, readPrft, readPrsl, readPssh, readSchm, readSdtp, readSidx, readSkip, readSmhd, readSsix, readSthd, readStsd, readStss, readSttg, readStts, readStyp, readSubs, readTenc, readTfdt, readTfhd, readTfra, readTkhd, readTrex, readTrun, readUrl, readUrn, readVisualSampleEntryBox, readVlab, readVmhd, readVttC, readVtte, traverseIsoBoxes, writeArdi, writeAudioSampleEntryBox, writeAvc1, writeAvc2, writeAvc3, writeAvc4, writeCtts, writeDref, writeElng, writeElst, writeEmsg, writeEnca, writeEncv, writeFree, writeFrma, writeFtyp, writeHdlr, writeHev1, writeHvc1, writeIden, writeImda, writeIsoBox, writeIsoBoxes, writeKind, writeLabl, writeMdat, writeMdhd, writeMehd, writeMeta, writeMfhd, writeMfro, writeMp4a, writeMvhd, writePayl, writePrft, writePrsl, writePssh, writeSchm, writeSdtp, writeSidx, writeSkip, writeSmhd, writeSsix, writeSthd, writeStsd, writeStss, writeSttg, writeStts, writeStyp, writeSubs, writeTenc, writeTfdt, writeTfhd, writeTfra, writeTkhd, writeTrex, writeTrun, writeUrl, writeUrn, writeVisualSampleEntryBox, writeVlab, writeVmhd, writeVttC, writeVtte };
3640
3773
  //# sourceMappingURL=index.js.map