mediabunny 1.1.1 → 1.3.0

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 (72) hide show
  1. package/dist/bundles/mediabunny.cjs +406 -141
  2. package/dist/bundles/mediabunny.min.cjs +4 -4
  3. package/dist/bundles/mediabunny.min.mjs +4 -4
  4. package/dist/bundles/mediabunny.mjs +406 -141
  5. package/dist/mediabunny.d.ts +31 -2
  6. package/dist/modules/codec-data.d.ts +9 -0
  7. package/dist/modules/codec-data.d.ts.map +1 -1
  8. package/dist/modules/codec-data.js +278 -145
  9. package/dist/modules/codec.js +2 -2
  10. package/dist/modules/conversion.js +8 -8
  11. package/dist/modules/index.js +14 -14
  12. package/dist/modules/input-format.js +12 -12
  13. package/dist/modules/input-track.d.ts +16 -3
  14. package/dist/modules/input-track.d.ts.map +1 -1
  15. package/dist/modules/input-track.js +34 -5
  16. package/dist/modules/input.js +4 -4
  17. package/dist/modules/isobmff/isobmff-boxes.js +5 -5
  18. package/dist/modules/isobmff/isobmff-demuxer.js +9 -9
  19. package/dist/modules/isobmff/isobmff-muxer.js +10 -10
  20. package/dist/modules/matroska/matroska-demuxer.js +9 -9
  21. package/dist/modules/matroska/matroska-muxer.d.ts.map +1 -1
  22. package/dist/modules/matroska/matroska-muxer.js +9 -11
  23. package/dist/modules/media-sink.d.ts +11 -0
  24. package/dist/modules/media-sink.d.ts.map +1 -1
  25. package/dist/modules/media-sink.js +70 -17
  26. package/dist/modules/media-source.js +7 -7
  27. package/dist/modules/misc.d.ts +1 -1
  28. package/dist/modules/misc.d.ts.map +1 -1
  29. package/dist/modules/misc.js +4 -1
  30. package/dist/modules/mp3/mp3-demuxer.js +6 -6
  31. package/dist/modules/mp3/mp3-muxer.js +4 -4
  32. package/dist/modules/mp3/mp3-reader.js +2 -2
  33. package/dist/modules/mp3/mp3-writer.js +1 -1
  34. package/dist/modules/muxer.js +1 -1
  35. package/dist/modules/ogg/ogg-demuxer.js +9 -9
  36. package/dist/modules/ogg/ogg-misc.js +2 -2
  37. package/dist/modules/ogg/ogg-muxer.js +6 -6
  38. package/dist/modules/ogg/ogg-reader.js +1 -1
  39. package/dist/modules/output-format.d.ts +5 -0
  40. package/dist/modules/output-format.d.ts.map +1 -1
  41. package/dist/modules/output-format.js +9 -6
  42. package/dist/modules/output.js +4 -4
  43. package/dist/modules/packet.js +1 -1
  44. package/dist/modules/reader.js +1 -1
  45. package/dist/modules/sample.js +1 -1
  46. package/dist/modules/source.js +1 -1
  47. package/dist/modules/target.js +1 -1
  48. package/dist/modules/wave/riff-reader.d.ts +1 -0
  49. package/dist/modules/wave/riff-reader.d.ts.map +1 -1
  50. package/dist/modules/wave/riff-reader.js +13 -0
  51. package/dist/modules/wave/riff-writer.d.ts +2 -1
  52. package/dist/modules/wave/riff-writer.d.ts.map +1 -1
  53. package/dist/modules/wave/riff-writer.js +8 -3
  54. package/dist/modules/wave/wave-demuxer.d.ts.map +1 -1
  55. package/dist/modules/wave/wave-demuxer.js +24 -9
  56. package/dist/modules/wave/wave-muxer.d.ts +3 -0
  57. package/dist/modules/wave/wave-muxer.d.ts.map +1 -1
  58. package/dist/modules/wave/wave-muxer.js +55 -14
  59. package/dist/modules/writer.js +1 -1
  60. package/package.json +2 -2
  61. package/src/codec-data.ts +317 -155
  62. package/src/conversion.ts +1 -1
  63. package/src/input-format.ts +1 -1
  64. package/src/input-track.ts +42 -3
  65. package/src/matroska/matroska-muxer.ts +1 -2
  66. package/src/media-sink.ts +88 -10
  67. package/src/misc.ts +6 -2
  68. package/src/output-format.ts +9 -0
  69. package/src/wave/riff-reader.ts +15 -0
  70. package/src/wave/riff-writer.ts +9 -3
  71. package/src/wave/wave-demuxer.ts +24 -3
  72. package/src/wave/wave-muxer.ts +57 -9
@@ -159,6 +159,9 @@ var Mediabunny = (() => {
159
159
  return bit;
160
160
  }
161
161
  readBits(n) {
162
+ if (n === 1) {
163
+ return this.readBit();
164
+ }
162
165
  let result = 0;
163
166
  for (let i = 0; i < n; i++) {
164
167
  result <<= 1;
@@ -189,7 +192,7 @@ var Mediabunny = (() => {
189
192
  };
190
193
  var readExpGolomb = (bitstream) => {
191
194
  let leadingZeroBits = 0;
192
- while (bitstream.readBit() === 0 && leadingZeroBits < 32) {
195
+ while (bitstream.readBits(1) === 0 && leadingZeroBits < 32) {
193
196
  leadingZeroBits++;
194
197
  }
195
198
  if (leadingZeroBits >= 32) {
@@ -1773,6 +1776,31 @@ var Mediabunny = (() => {
1773
1776
  }
1774
1777
  return nalUnits;
1775
1778
  };
1779
+ var findNalUnitsInLengthPrefixed = (packetData, lengthSize) => {
1780
+ const nalUnits = [];
1781
+ let offset = 0;
1782
+ const dataView = new DataView(packetData.buffer, packetData.byteOffset, packetData.byteLength);
1783
+ while (offset + lengthSize <= packetData.length) {
1784
+ let nalUnitLength;
1785
+ if (lengthSize === 1) {
1786
+ nalUnitLength = dataView.getUint8(offset);
1787
+ } else if (lengthSize === 2) {
1788
+ nalUnitLength = dataView.getUint16(offset, false);
1789
+ } else if (lengthSize === 3) {
1790
+ nalUnitLength = (dataView.getUint16(offset, false) << 8) + dataView.getUint8(offset + 2);
1791
+ } else if (lengthSize === 4) {
1792
+ nalUnitLength = dataView.getUint32(offset, false);
1793
+ } else {
1794
+ assertNever(lengthSize);
1795
+ assert(false);
1796
+ }
1797
+ offset += lengthSize;
1798
+ const nalUnit = packetData.subarray(offset, offset + nalUnitLength);
1799
+ nalUnits.push(nalUnit);
1800
+ offset += nalUnitLength;
1801
+ }
1802
+ return nalUnits;
1803
+ };
1776
1804
  var removeEmulationPreventionBytes = (data) => {
1777
1805
  const result = [];
1778
1806
  const len = data.length;
@@ -2348,22 +2376,6 @@ var Mediabunny = (() => {
2348
2376
  return new Uint8Array(bytes2);
2349
2377
  };
2350
2378
  var extractVp9CodecInfoFromPacket = (packet) => {
2351
- const lastByte = packet[packet.length - 1];
2352
- if (lastByte && (lastByte & 224) === 192) {
2353
- const bytesPerFrameSize = ((lastByte & 24) >> 3) + 1;
2354
- const numFrames = (lastByte & 7) + 1;
2355
- const indexSize = 2 + numFrames * bytesPerFrameSize;
2356
- if (packet[packet.length - indexSize] !== lastByte) {
2357
- return null;
2358
- }
2359
- let frameSize = 0;
2360
- const offset = packet.length - indexSize + 1;
2361
- for (let i = 0; i < bytesPerFrameSize; i++) {
2362
- if (!packet[offset + i]) return null;
2363
- frameSize |= packet[offset + i] << 8 * i;
2364
- }
2365
- packet = packet.subarray(0, frameSize);
2366
- }
2367
2379
  const bitstream = new Bitstream(packet);
2368
2380
  const frameMarker = bitstream.readBits(2);
2369
2381
  if (frameMarker !== 2) {
@@ -2437,13 +2449,12 @@ var Mediabunny = (() => {
2437
2449
  matrixCoefficients
2438
2450
  };
2439
2451
  };
2440
- var extractAv1CodecInfoFromPacket = (packet) => {
2452
+ function* iterateAv1PacketObus(packet) {
2441
2453
  const bitstream = new Bitstream(packet);
2442
2454
  const readLeb128 = () => {
2443
2455
  let value = 0;
2444
2456
  for (let i = 0; i < 8; i++) {
2445
2457
  const byte = bitstream.readAlignedByte();
2446
- if (byte === void 0) return 0;
2447
2458
  value |= (byte & 127) << i * 7;
2448
2459
  if (!(byte & 128)) {
2449
2460
  break;
@@ -2458,121 +2469,133 @@ var Mediabunny = (() => {
2458
2469
  return value;
2459
2470
  };
2460
2471
  while (bitstream.getBitsLeft() >= 8) {
2461
- const obuHeader = bitstream.readBits(8);
2462
- const obuType = obuHeader >> 3 & 15;
2463
- const obuExtension = obuHeader >> 2 & 1;
2464
- const obuHasSizeField = obuHeader >> 1 & 1;
2472
+ bitstream.skipBits(1);
2473
+ const obuType = bitstream.readBits(4);
2474
+ const obuExtension = bitstream.readBits(1);
2475
+ const obuHasSizeField = bitstream.readBits(1);
2476
+ bitstream.skipBits(1);
2465
2477
  if (obuExtension) {
2466
2478
  bitstream.skipBits(8);
2467
2479
  }
2468
2480
  let obuSize;
2469
2481
  if (obuHasSizeField) {
2470
2482
  const obuSizeValue = readLeb128();
2471
- if (obuSizeValue === null) return null;
2483
+ if (obuSizeValue === null) return;
2472
2484
  obuSize = obuSizeValue;
2473
2485
  } else {
2474
2486
  obuSize = Math.floor(bitstream.getBitsLeft() / 8);
2475
2487
  }
2476
- if (obuType === 1) {
2477
- const seqProfile = bitstream.readBits(3);
2478
- const stillPicture = bitstream.readBits(1);
2479
- const reducedStillPictureHeader = bitstream.readBits(1);
2480
- let seqLevel = 0;
2481
- let seqTier = 0;
2482
- let bufferDelayLengthMinus1 = 0;
2483
- if (reducedStillPictureHeader) {
2484
- seqLevel = bitstream.readBits(5);
2485
- } else {
2486
- const timingInfoPresentFlag = bitstream.readBits(1);
2487
- if (timingInfoPresentFlag) {
2488
- bitstream.skipBits(32);
2489
- bitstream.skipBits(32);
2490
- const equalPictureInterval = bitstream.readBits(1);
2491
- if (equalPictureInterval) {
2492
- return null;
2493
- }
2488
+ assert(bitstream.pos % 8 === 0);
2489
+ yield {
2490
+ type: obuType,
2491
+ data: packet.subarray(bitstream.pos / 8, bitstream.pos / 8 + obuSize)
2492
+ };
2493
+ bitstream.skipBits(obuSize * 8);
2494
+ }
2495
+ }
2496
+ var extractAv1CodecInfoFromPacket = (packet) => {
2497
+ for (const { type, data } of iterateAv1PacketObus(packet)) {
2498
+ if (type !== 1) {
2499
+ continue;
2500
+ }
2501
+ const bitstream = new Bitstream(data);
2502
+ const seqProfile = bitstream.readBits(3);
2503
+ const stillPicture = bitstream.readBits(1);
2504
+ const reducedStillPictureHeader = bitstream.readBits(1);
2505
+ let seqLevel = 0;
2506
+ let seqTier = 0;
2507
+ let bufferDelayLengthMinus1 = 0;
2508
+ if (reducedStillPictureHeader) {
2509
+ seqLevel = bitstream.readBits(5);
2510
+ } else {
2511
+ const timingInfoPresentFlag = bitstream.readBits(1);
2512
+ if (timingInfoPresentFlag) {
2513
+ bitstream.skipBits(32);
2514
+ bitstream.skipBits(32);
2515
+ const equalPictureInterval = bitstream.readBits(1);
2516
+ if (equalPictureInterval) {
2517
+ return null;
2494
2518
  }
2495
- const decoderModelInfoPresentFlag = bitstream.readBits(1);
2496
- if (decoderModelInfoPresentFlag) {
2497
- bufferDelayLengthMinus1 = bitstream.readBits(5);
2498
- bitstream.skipBits(32);
2499
- bitstream.skipBits(5);
2500
- bitstream.skipBits(5);
2519
+ }
2520
+ const decoderModelInfoPresentFlag = bitstream.readBits(1);
2521
+ if (decoderModelInfoPresentFlag) {
2522
+ bufferDelayLengthMinus1 = bitstream.readBits(5);
2523
+ bitstream.skipBits(32);
2524
+ bitstream.skipBits(5);
2525
+ bitstream.skipBits(5);
2526
+ }
2527
+ const operatingPointsCntMinus1 = bitstream.readBits(5);
2528
+ for (let i = 0; i <= operatingPointsCntMinus1; i++) {
2529
+ bitstream.skipBits(12);
2530
+ const seqLevelIdx = bitstream.readBits(5);
2531
+ if (i === 0) {
2532
+ seqLevel = seqLevelIdx;
2501
2533
  }
2502
- const operatingPointsCntMinus1 = bitstream.readBits(5);
2503
- for (let i = 0; i <= operatingPointsCntMinus1; i++) {
2504
- bitstream.skipBits(12);
2505
- const seqLevelIdx = bitstream.readBits(5);
2534
+ if (seqLevelIdx > 7) {
2535
+ const seqTierTemp = bitstream.readBits(1);
2506
2536
  if (i === 0) {
2507
- seqLevel = seqLevelIdx;
2508
- }
2509
- if (seqLevelIdx > 7) {
2510
- const seqTierTemp = bitstream.readBits(1);
2511
- if (i === 0) {
2512
- seqTier = seqTierTemp;
2513
- }
2537
+ seqTier = seqTierTemp;
2514
2538
  }
2515
- if (decoderModelInfoPresentFlag) {
2516
- const decoderModelPresentForThisOp = bitstream.readBits(1);
2517
- if (decoderModelPresentForThisOp) {
2518
- const n = bufferDelayLengthMinus1 + 1;
2519
- bitstream.skipBits(n);
2520
- bitstream.skipBits(n);
2521
- bitstream.skipBits(1);
2522
- }
2523
- }
2524
- const initialDisplayDelayPresentFlag = bitstream.readBits(1);
2525
- if (initialDisplayDelayPresentFlag) {
2526
- bitstream.skipBits(4);
2539
+ }
2540
+ if (decoderModelInfoPresentFlag) {
2541
+ const decoderModelPresentForThisOp = bitstream.readBits(1);
2542
+ if (decoderModelPresentForThisOp) {
2543
+ const n = bufferDelayLengthMinus1 + 1;
2544
+ bitstream.skipBits(n);
2545
+ bitstream.skipBits(n);
2546
+ bitstream.skipBits(1);
2527
2547
  }
2528
2548
  }
2549
+ const initialDisplayDelayPresentFlag = bitstream.readBits(1);
2550
+ if (initialDisplayDelayPresentFlag) {
2551
+ bitstream.skipBits(4);
2552
+ }
2529
2553
  }
2530
- const highBitdepth = bitstream.readBits(1);
2531
- let bitDepth = 8;
2532
- if (seqProfile === 2 && highBitdepth) {
2533
- const twelveBit = bitstream.readBits(1);
2534
- bitDepth = twelveBit ? 12 : 10;
2535
- } else if (seqProfile <= 2) {
2536
- bitDepth = highBitdepth ? 10 : 8;
2537
- }
2538
- let monochrome = 0;
2539
- if (seqProfile !== 1) {
2540
- monochrome = bitstream.readBits(1);
2541
- }
2542
- let chromaSubsamplingX = 1;
2543
- let chromaSubsamplingY = 1;
2544
- let chromaSamplePosition = 0;
2545
- if (!monochrome) {
2546
- if (seqProfile === 0) {
2547
- chromaSubsamplingX = 1;
2548
- chromaSubsamplingY = 1;
2549
- } else if (seqProfile === 1) {
2550
- chromaSubsamplingX = 0;
2551
- chromaSubsamplingY = 0;
2552
- } else {
2553
- if (bitDepth === 12) {
2554
- chromaSubsamplingX = bitstream.readBits(1);
2555
- if (chromaSubsamplingX) {
2556
- chromaSubsamplingY = bitstream.readBits(1);
2557
- }
2554
+ }
2555
+ const highBitdepth = bitstream.readBits(1);
2556
+ let bitDepth = 8;
2557
+ if (seqProfile === 2 && highBitdepth) {
2558
+ const twelveBit = bitstream.readBits(1);
2559
+ bitDepth = twelveBit ? 12 : 10;
2560
+ } else if (seqProfile <= 2) {
2561
+ bitDepth = highBitdepth ? 10 : 8;
2562
+ }
2563
+ let monochrome = 0;
2564
+ if (seqProfile !== 1) {
2565
+ monochrome = bitstream.readBits(1);
2566
+ }
2567
+ let chromaSubsamplingX = 1;
2568
+ let chromaSubsamplingY = 1;
2569
+ let chromaSamplePosition = 0;
2570
+ if (!monochrome) {
2571
+ if (seqProfile === 0) {
2572
+ chromaSubsamplingX = 1;
2573
+ chromaSubsamplingY = 1;
2574
+ } else if (seqProfile === 1) {
2575
+ chromaSubsamplingX = 0;
2576
+ chromaSubsamplingY = 0;
2577
+ } else {
2578
+ if (bitDepth === 12) {
2579
+ chromaSubsamplingX = bitstream.readBits(1);
2580
+ if (chromaSubsamplingX) {
2581
+ chromaSubsamplingY = bitstream.readBits(1);
2558
2582
  }
2559
2583
  }
2560
- if (chromaSubsamplingX && chromaSubsamplingY) {
2561
- chromaSamplePosition = bitstream.readBits(2);
2562
- }
2563
2584
  }
2564
- return {
2565
- profile: seqProfile,
2566
- level: seqLevel,
2567
- tier: seqTier,
2568
- bitDepth,
2569
- monochrome,
2570
- chromaSubsamplingX,
2571
- chromaSubsamplingY,
2572
- chromaSamplePosition
2573
- };
2585
+ if (chromaSubsamplingX && chromaSubsamplingY) {
2586
+ chromaSamplePosition = bitstream.readBits(2);
2587
+ }
2574
2588
  }
2575
- bitstream.skipBits(obuSize * 8);
2589
+ return {
2590
+ profile: seqProfile,
2591
+ level: seqLevel,
2592
+ tier: seqTier,
2593
+ bitDepth,
2594
+ monochrome,
2595
+ chromaSubsamplingX,
2596
+ chromaSubsamplingY,
2597
+ chromaSamplePosition
2598
+ };
2576
2599
  }
2577
2600
  return null;
2578
2601
  };
@@ -2703,6 +2726,105 @@ var Mediabunny = (() => {
2703
2726
  }
2704
2727
  return { modeBlockflags };
2705
2728
  };
2729
+ var determineVideoPacketType = async (videoTrack, packet) => {
2730
+ assert(videoTrack.codec);
2731
+ switch (videoTrack.codec) {
2732
+ case "avc":
2733
+ {
2734
+ const decoderConfig = await videoTrack.getDecoderConfig();
2735
+ assert(decoderConfig);
2736
+ let nalUnits;
2737
+ if (decoderConfig.description) {
2738
+ const bytes2 = toUint8Array(decoderConfig.description);
2739
+ const lengthSizeMinusOne = bytes2[4] & 3;
2740
+ const lengthSize = lengthSizeMinusOne + 1;
2741
+ nalUnits = findNalUnitsInLengthPrefixed(packet.data, lengthSize);
2742
+ } else {
2743
+ nalUnits = findNalUnitsInAnnexB(packet.data);
2744
+ }
2745
+ const isKeyframe = nalUnits.some((x) => extractNalUnitTypeForAvc(x) === 5);
2746
+ return isKeyframe ? "key" : "delta";
2747
+ }
2748
+ ;
2749
+ case "hevc":
2750
+ {
2751
+ const decoderConfig = await videoTrack.getDecoderConfig();
2752
+ assert(decoderConfig);
2753
+ let nalUnits;
2754
+ if (decoderConfig.description) {
2755
+ const bytes2 = toUint8Array(decoderConfig.description);
2756
+ const lengthSizeMinusOne = bytes2[21] & 3;
2757
+ const lengthSize = lengthSizeMinusOne + 1;
2758
+ nalUnits = findNalUnitsInLengthPrefixed(packet.data, lengthSize);
2759
+ } else {
2760
+ nalUnits = findNalUnitsInAnnexB(packet.data);
2761
+ }
2762
+ const isKeyframe = nalUnits.some((x) => {
2763
+ const type = extractNalUnitTypeForHevc(x);
2764
+ return 16 <= type && type <= 23;
2765
+ });
2766
+ return isKeyframe ? "key" : "delta";
2767
+ }
2768
+ ;
2769
+ case "vp8":
2770
+ {
2771
+ const frameType = packet.data[0] & 1;
2772
+ return frameType === 0 ? "key" : "delta";
2773
+ }
2774
+ ;
2775
+ case "vp9":
2776
+ {
2777
+ const bitstream = new Bitstream(packet.data);
2778
+ if (bitstream.readBits(2) !== 2) {
2779
+ return null;
2780
+ }
2781
+ ;
2782
+ const profileLowBit = bitstream.readBits(1);
2783
+ const profileHighBit = bitstream.readBits(1);
2784
+ const profile = (profileHighBit << 1) + profileLowBit;
2785
+ if (profile === 3) {
2786
+ bitstream.skipBits(1);
2787
+ }
2788
+ const showExistingFrame = bitstream.readBits(1);
2789
+ if (showExistingFrame) {
2790
+ return null;
2791
+ }
2792
+ const frameType = bitstream.readBits(1);
2793
+ return frameType === 0 ? "key" : "delta";
2794
+ }
2795
+ ;
2796
+ case "av1":
2797
+ {
2798
+ let reducedStillPictureHeader = false;
2799
+ for (const { type, data } of iterateAv1PacketObus(packet.data)) {
2800
+ if (type === 1) {
2801
+ const bitstream = new Bitstream(data);
2802
+ bitstream.skipBits(4);
2803
+ reducedStillPictureHeader = !!bitstream.readBits(1);
2804
+ } else if (type === 3 || type === 6 || type === 7) {
2805
+ if (reducedStillPictureHeader) {
2806
+ return "key";
2807
+ }
2808
+ const bitstream = new Bitstream(data);
2809
+ const showExistingFrame = bitstream.readBits(1);
2810
+ if (showExistingFrame) {
2811
+ return null;
2812
+ }
2813
+ const frameType = bitstream.readBits(2);
2814
+ return frameType === 0 ? "key" : "delta";
2815
+ }
2816
+ }
2817
+ return null;
2818
+ }
2819
+ ;
2820
+ default:
2821
+ {
2822
+ assertNever(videoTrack.codec);
2823
+ assert(false);
2824
+ }
2825
+ ;
2826
+ }
2827
+ };
2706
2828
 
2707
2829
  // src/isobmff/isobmff-boxes.ts
2708
2830
  var IsobmffBoxWriter = class {
@@ -5970,7 +6092,7 @@ ${cue.notes ?? ""}`;
5970
6092
  if (chunk.type !== "key") return;
5971
6093
  if (!trackData.info.decoderConfig.colorSpace || !trackData.info.decoderConfig.colorSpace.matrix) return;
5972
6094
  const bitstream = new Bitstream(chunk.data);
5973
- if (bitstream.readBits(2) !== 2) return;
6095
+ bitstream.skipBits(2);
5974
6096
  const profileLowBit = bitstream.readBits(1);
5975
6097
  const profileHighBit = bitstream.readBits(1);
5976
6098
  const profile = (profileHighBit << 1) + profileLowBit;
@@ -7933,12 +8055,34 @@ ${cue.notes ?? ""}`;
7933
8055
  if (options.metadataOnly !== void 0 && typeof options.metadataOnly !== "boolean") {
7934
8056
  throw new TypeError("options.metadataOnly, when defined, must be a boolean.");
7935
8057
  }
8058
+ if (options.verifyKeyPackets !== void 0 && typeof options.verifyKeyPackets !== "boolean") {
8059
+ throw new TypeError("options.verifyKeyPackets, when defined, must be a boolean.");
8060
+ }
8061
+ if (options.verifyKeyPackets && options.metadataOnly) {
8062
+ throw new TypeError("options.verifyKeyPackets and options.metadataOnly cannot be enabled together.");
8063
+ }
7936
8064
  };
7937
8065
  var validateTimestamp = (timestamp) => {
7938
8066
  if (typeof timestamp !== "number" || Number.isNaN(timestamp)) {
7939
8067
  throw new TypeError("timestamp must be a number.");
7940
8068
  }
7941
8069
  };
8070
+ var maybeFixPacketType = (track, promise, options) => {
8071
+ if (options.verifyKeyPackets) {
8072
+ return promise.then(async (packet) => {
8073
+ if (!packet || packet.type === "delta") {
8074
+ return packet;
8075
+ }
8076
+ const determinedType = await track.determinePacketType(packet);
8077
+ if (determinedType) {
8078
+ packet.type = determinedType;
8079
+ }
8080
+ return packet;
8081
+ });
8082
+ } else {
8083
+ return promise;
8084
+ }
8085
+ };
7942
8086
  var EncodedPacketSink = class {
7943
8087
  constructor(track) {
7944
8088
  if (!(track instanceof InputTrack)) {
@@ -7952,7 +8096,7 @@ ${cue.notes ?? ""}`;
7952
8096
  */
7953
8097
  getFirstPacket(options = {}) {
7954
8098
  validatePacketRetrievalOptions(options);
7955
- return this._track._backing.getFirstPacket(options);
8099
+ return maybeFixPacketType(this._track, this._track._backing.getFirstPacket(options), options);
7956
8100
  }
7957
8101
  /**
7958
8102
  * Retrieves the packet corresponding to the given timestamp, in seconds. More specifically, returns the last packet
@@ -7965,7 +8109,7 @@ ${cue.notes ?? ""}`;
7965
8109
  getPacket(timestamp, options = {}) {
7966
8110
  validateTimestamp(timestamp);
7967
8111
  validatePacketRetrievalOptions(options);
7968
- return this._track._backing.getPacket(timestamp, options);
8112
+ return maybeFixPacketType(this._track, this._track._backing.getPacket(timestamp, options), options);
7969
8113
  }
7970
8114
  /**
7971
8115
  * Retrieves the packet following the given packet (in decode order), or null if the given packet is the
@@ -7976,7 +8120,7 @@ ${cue.notes ?? ""}`;
7976
8120
  throw new TypeError("packet must be an EncodedPacket.");
7977
8121
  }
7978
8122
  validatePacketRetrievalOptions(options);
7979
- return this._track._backing.getNextPacket(packet, options);
8123
+ return maybeFixPacketType(this._track, this._track._backing.getNextPacket(packet, options), options);
7980
8124
  }
7981
8125
  /**
7982
8126
  * Retrieves the key packet corresponding to the given timestamp, in seconds. More specifically, returns the last
@@ -7985,23 +8129,49 @@ ${cue.notes ?? ""}`;
7985
8129
  * last key packet using `getKeyPacket(Infinity)`. The method returns null if the timestamp is before the first
7986
8130
  * key packet in the track.
7987
8131
  *
8132
+ * To ensure that the returned packet is guaranteed to be a real key frame, enable `options.verifyKeyPackets`.
8133
+ *
7988
8134
  * @param timestamp - The timestamp used for retrieval, in seconds.
7989
8135
  */
7990
- getKeyPacket(timestamp, options = {}) {
8136
+ async getKeyPacket(timestamp, options = {}) {
7991
8137
  validateTimestamp(timestamp);
7992
8138
  validatePacketRetrievalOptions(options);
7993
- return this._track._backing.getKeyPacket(timestamp, options);
8139
+ if (!options.verifyKeyPackets) {
8140
+ return this._track._backing.getKeyPacket(timestamp, options);
8141
+ }
8142
+ const packet = await this._track._backing.getKeyPacket(timestamp, options);
8143
+ if (!packet || packet.type === "delta") {
8144
+ return packet;
8145
+ }
8146
+ const determinedType = await this._track.determinePacketType(packet);
8147
+ if (determinedType === "delta") {
8148
+ return this.getKeyPacket(packet.timestamp - 1 / this._track.timeResolution, options);
8149
+ }
8150
+ return packet;
7994
8151
  }
7995
8152
  /**
7996
8153
  * Retrieves the key packet following the given packet (in decode order), or null if the given packet is the last
7997
8154
  * key packet.
8155
+ *
8156
+ * To ensure that the returned packet is guaranteed to be a real key frame, enable `options.verifyKeyPackets`.
7998
8157
  */
7999
- getNextKeyPacket(packet, options = {}) {
8158
+ async getNextKeyPacket(packet, options = {}) {
8000
8159
  if (!(packet instanceof EncodedPacket)) {
8001
8160
  throw new TypeError("packet must be an EncodedPacket.");
8002
8161
  }
8003
8162
  validatePacketRetrievalOptions(options);
8004
- return this._track._backing.getNextKeyPacket(packet, options);
8163
+ if (!options.verifyKeyPackets) {
8164
+ return this._track._backing.getNextKeyPacket(packet, options);
8165
+ }
8166
+ const nextPacket = await this._track._backing.getNextKeyPacket(packet, options);
8167
+ if (!nextPacket || nextPacket.type === "delta") {
8168
+ return nextPacket;
8169
+ }
8170
+ const determinedType = await this._track.determinePacketType(nextPacket);
8171
+ if (determinedType === "delta") {
8172
+ return this.getNextKeyPacket(nextPacket, options);
8173
+ }
8174
+ return nextPacket;
8005
8175
  }
8006
8176
  /**
8007
8177
  * Creates an async iterator that yields the packets in this track in decode order. To enable fast iteration, this
@@ -8147,7 +8317,7 @@ ${cue.notes ?? ""}`;
8147
8317
  }
8148
8318
  });
8149
8319
  const packetSink = this._createPacketSink();
8150
- const keyPacket = await packetSink.getKeyPacket(startTimestamp) ?? await packetSink.getFirstPacket();
8320
+ const keyPacket = await packetSink.getKeyPacket(startTimestamp, { verifyKeyPackets: true }) ?? await packetSink.getFirstPacket();
8151
8321
  if (!keyPacket) {
8152
8322
  return;
8153
8323
  }
@@ -8155,7 +8325,7 @@ ${cue.notes ?? ""}`;
8155
8325
  let endPacket = void 0;
8156
8326
  if (endTimestamp < Infinity) {
8157
8327
  const packet = await packetSink.getPacket(endTimestamp);
8158
- const keyPacket2 = !packet ? null : packet.type === "key" && packet.timestamp === endTimestamp ? packet : await packetSink.getNextKeyPacket(packet);
8328
+ const keyPacket2 = !packet ? null : packet.type === "key" && packet.timestamp === endTimestamp ? packet : await packetSink.getNextKeyPacket(packet, { verifyKeyPackets: true });
8159
8329
  if (keyPacket2) {
8160
8330
  endPacket = keyPacket2;
8161
8331
  }
@@ -8307,7 +8477,7 @@ ${cue.notes ?? ""}`;
8307
8477
  break;
8308
8478
  }
8309
8479
  const targetPacket = await packetSink.getPacket(timestamp);
8310
- const keyPacket = targetPacket && await packetSink.getKeyPacket(timestamp);
8480
+ const keyPacket = targetPacket && await packetSink.getKeyPacket(timestamp, { verifyKeyPackets: true });
8311
8481
  if (!keyPacket) {
8312
8482
  if (maxSequenceNumber !== -1) {
8313
8483
  await decodePackets();
@@ -9158,7 +9328,10 @@ ${cue.notes ?? ""}`;
9158
9328
  const colorSpace = await this._backing.getColorSpace();
9159
9329
  return colorSpace.primaries === "bt2020" || colorSpace.primaries === "smpte432" || colorSpace.transfer === "pg" || colorSpace.transfer === "hlg" || colorSpace.matrix === "bt2020-ncl";
9160
9330
  }
9161
- /** Returns the decoder configuration for decoding the track's packets using a VideoDecoder. */
9331
+ /**
9332
+ * Returns the decoder configuration for decoding the track's packets using a VideoDecoder. Returns null if the
9333
+ * track's codec is unknown.
9334
+ */
9162
9335
  getDecoderConfig() {
9163
9336
  return this._backing.getDecoderConfig();
9164
9337
  }
@@ -9187,6 +9360,18 @@ ${cue.notes ?? ""}`;
9187
9360
  return false;
9188
9361
  }
9189
9362
  }
9363
+ async determinePacketType(packet) {
9364
+ if (!(packet instanceof EncodedPacket)) {
9365
+ throw new TypeError("packet must be an EncodedPacket.");
9366
+ }
9367
+ if (packet.isMetadataOnly) {
9368
+ throw new TypeError("packet must not be metadata-only to determine its type.");
9369
+ }
9370
+ if (this.codec === null) {
9371
+ return null;
9372
+ }
9373
+ return determineVideoPacketType(this, packet);
9374
+ }
9190
9375
  };
9191
9376
  var InputAudioTrack = class extends InputTrack {
9192
9377
  /** @internal */
@@ -9208,7 +9393,10 @@ ${cue.notes ?? ""}`;
9208
9393
  get sampleRate() {
9209
9394
  return this._backing.getSampleRate();
9210
9395
  }
9211
- /** Returns the decoder configuration for decoding the track's packets using an AudioDecoder. */
9396
+ /**
9397
+ * Returns the decoder configuration for decoding the track's packets using an AudioDecoder. Returns null if the
9398
+ * track's codec is unknown.
9399
+ */
9212
9400
  getDecoderConfig() {
9213
9401
  return this._backing.getDecoderConfig();
9214
9402
  }
@@ -9241,6 +9429,15 @@ ${cue.notes ?? ""}`;
9241
9429
  return false;
9242
9430
  }
9243
9431
  }
9432
+ async determinePacketType(packet) {
9433
+ if (!(packet instanceof EncodedPacket)) {
9434
+ throw new TypeError("packet must be an EncodedPacket.");
9435
+ }
9436
+ if (this.codec === null) {
9437
+ return null;
9438
+ }
9439
+ return "key";
9440
+ }
9244
9441
  };
9245
9442
 
9246
9443
  // src/reader.ts
@@ -9411,6 +9608,18 @@ ${cue.notes ?? ""}`;
9411
9608
  this.pos += 4;
9412
9609
  return view2.getUint32(offset, this.littleEndian);
9413
9610
  }
9611
+ readU64() {
9612
+ let low;
9613
+ let high;
9614
+ if (this.littleEndian) {
9615
+ low = this.readU32();
9616
+ high = this.readU32();
9617
+ } else {
9618
+ high = this.readU32();
9619
+ low = this.readU32();
9620
+ }
9621
+ return high * 4294967296 + low;
9622
+ }
9414
9623
  readAscii(length) {
9415
9624
  const { view: view2, offset } = this.reader.getViewAndOffset(this.pos, this.pos + length);
9416
9625
  this.pos += length;
@@ -9438,25 +9647,38 @@ ${cue.notes ?? ""}`;
9438
9647
  return this.metadataPromise ??= (async () => {
9439
9648
  const actualFileSize = await this.metadataReader.reader.source.getSize();
9440
9649
  const riffType = this.metadataReader.readAscii(4);
9441
- this.metadataReader.littleEndian = riffType === "RIFF";
9442
- const totalFileSize = Math.min(this.metadataReader.readU32() + 8, actualFileSize);
9650
+ this.metadataReader.littleEndian = riffType !== "RIFX";
9651
+ const isRf64 = riffType === "RF64";
9652
+ const outerChunkSize = this.metadataReader.readU32();
9653
+ let totalFileSize = isRf64 ? actualFileSize : Math.min(outerChunkSize + 8, actualFileSize);
9443
9654
  const format = this.metadataReader.readAscii(4);
9444
9655
  if (format !== "WAVE") {
9445
9656
  throw new Error("Invalid WAVE file - wrong format");
9446
9657
  }
9447
9658
  this.metadataReader.pos = 12;
9659
+ let chunksRead = 0;
9660
+ let dataChunkSize = null;
9448
9661
  while (this.metadataReader.pos < totalFileSize) {
9449
9662
  await this.metadataReader.reader.loadRange(this.metadataReader.pos, this.metadataReader.pos + 8);
9450
9663
  const chunkId = this.metadataReader.readAscii(4);
9451
9664
  const chunkSize = this.metadataReader.readU32();
9452
9665
  const startPos = this.metadataReader.pos;
9666
+ if (isRf64 && chunksRead === 0 && chunkId !== "ds64") {
9667
+ throw new Error('Invalid RF64 file: First chunk must be "ds64".');
9668
+ }
9453
9669
  if (chunkId === "fmt ") {
9454
9670
  await this.parseFmtChunk(chunkSize);
9455
9671
  } else if (chunkId === "data") {
9672
+ dataChunkSize ??= chunkSize;
9456
9673
  this.dataStart = this.metadataReader.pos;
9457
- this.dataSize = Math.min(chunkSize, totalFileSize - this.dataStart);
9674
+ this.dataSize = Math.min(dataChunkSize, totalFileSize - this.dataStart);
9675
+ } else if (chunkId === "ds64") {
9676
+ const riffChunkSize = this.metadataReader.readU64();
9677
+ dataChunkSize = this.metadataReader.readU64();
9678
+ totalFileSize = Math.min(riffChunkSize + 8, actualFileSize);
9458
9679
  }
9459
9680
  this.metadataReader.pos = startPos + chunkSize + (chunkSize & 1);
9681
+ chunksRead++;
9460
9682
  }
9461
9683
  if (!this.audioInfo) {
9462
9684
  throw new Error('Invalid WAVE file - missing "fmt " chunk');
@@ -9651,13 +9873,18 @@ ${cue.notes ?? ""}`;
9651
9873
  this.helper = new Uint8Array(8);
9652
9874
  this.helperView = new DataView(this.helper.buffer);
9653
9875
  }
9876
+ writeU16(value) {
9877
+ this.helperView.setUint16(0, value, true);
9878
+ this.writer.write(this.helper.subarray(0, 2));
9879
+ }
9654
9880
  writeU32(value) {
9655
9881
  this.helperView.setUint32(0, value, true);
9656
9882
  this.writer.write(this.helper.subarray(0, 4));
9657
9883
  }
9658
- writeU16(value) {
9659
- this.helperView.setUint16(0, value, true);
9660
- this.writer.write(this.helper.subarray(0, 2));
9884
+ writeU64(value) {
9885
+ this.helperView.setUint32(0, value, true);
9886
+ this.helperView.setUint32(4, Math.floor(value / 2 ** 32), true);
9887
+ this.writer.write(this.helper);
9661
9888
  }
9662
9889
  writeAscii(text) {
9663
9890
  this.writer.write(new TextEncoder().encode(text));
@@ -9670,9 +9897,12 @@ ${cue.notes ?? ""}`;
9670
9897
  super(output);
9671
9898
  this.headerWritten = false;
9672
9899
  this.dataSize = 0;
9900
+ this.sampleRate = null;
9901
+ this.sampleCount = 0;
9673
9902
  this.format = format;
9674
9903
  this.writer = output._writer;
9675
9904
  this.riffWriter = new RiffWriter(output._writer);
9905
+ this.isRf64 = !!format._options.large;
9676
9906
  }
9677
9907
  async start() {
9678
9908
  }
@@ -9690,11 +9920,18 @@ ${cue.notes ?? ""}`;
9690
9920
  assert(meta);
9691
9921
  assert(meta.decoderConfig);
9692
9922
  this.writeHeader(track, meta.decoderConfig);
9923
+ this.sampleRate = meta.decoderConfig.sampleRate;
9693
9924
  this.headerWritten = true;
9694
9925
  }
9695
9926
  this.validateAndNormalizeTimestamp(track, packet.timestamp, packet.type === "key");
9927
+ if (!this.isRf64 && this.writer.getPos() + packet.data.byteLength >= 2 ** 32) {
9928
+ throw new Error(
9929
+ "Adding more audio data would exceed the maximum RIFF size of 4 GiB. To write larger files, use RF64 by setting `large: true` in the WavOutputFormatOptions."
9930
+ );
9931
+ }
9696
9932
  this.writer.write(packet.data);
9697
9933
  this.dataSize += packet.data.byteLength;
9934
+ this.sampleCount += Math.round(packet.duration * this.sampleRate);
9698
9935
  await this.writer.flush();
9699
9936
  } finally {
9700
9937
  release();
@@ -9722,9 +9959,21 @@ ${cue.notes ?? ""}`;
9722
9959
  const channels = config.numberOfChannels;
9723
9960
  const sampleRate = config.sampleRate;
9724
9961
  const blockSize = pcmInfo.sampleSize * channels;
9725
- this.riffWriter.writeAscii("RIFF");
9726
- this.riffWriter.writeU32(0);
9962
+ this.riffWriter.writeAscii(this.isRf64 ? "RF64" : "RIFF");
9963
+ if (this.isRf64) {
9964
+ this.riffWriter.writeU32(4294967295);
9965
+ } else {
9966
+ this.riffWriter.writeU32(0);
9967
+ }
9727
9968
  this.riffWriter.writeAscii("WAVE");
9969
+ if (this.isRf64) {
9970
+ this.riffWriter.writeAscii("ds64");
9971
+ this.riffWriter.writeU32(28);
9972
+ this.riffWriter.writeU64(0);
9973
+ this.riffWriter.writeU64(0);
9974
+ this.riffWriter.writeU64(0);
9975
+ this.riffWriter.writeU32(0);
9976
+ }
9728
9977
  this.riffWriter.writeAscii("fmt ");
9729
9978
  this.riffWriter.writeU32(16);
9730
9979
  this.riffWriter.writeU16(format);
@@ -9734,7 +9983,11 @@ ${cue.notes ?? ""}`;
9734
9983
  this.riffWriter.writeU16(blockSize);
9735
9984
  this.riffWriter.writeU16(8 * pcmInfo.sampleSize);
9736
9985
  this.riffWriter.writeAscii("data");
9737
- this.riffWriter.writeU32(0);
9986
+ if (this.isRf64) {
9987
+ this.riffWriter.writeU32(4294967295);
9988
+ } else {
9989
+ this.riffWriter.writeU32(0);
9990
+ }
9738
9991
  if (this.format._options.onHeader) {
9739
9992
  const { data, start } = this.writer.stopTrackingWrites();
9740
9993
  this.format._options.onHeader(data, start);
@@ -9743,10 +9996,19 @@ ${cue.notes ?? ""}`;
9743
9996
  async finalize() {
9744
9997
  const release = await this.mutex.acquire();
9745
9998
  const endPos = this.writer.getPos();
9746
- this.writer.seek(4);
9747
- this.riffWriter.writeU32(this.dataSize + 36);
9748
- this.writer.seek(40);
9749
- this.riffWriter.writeU32(this.dataSize);
9999
+ if (this.isRf64) {
10000
+ this.writer.seek(20);
10001
+ this.riffWriter.writeU64(endPos - 8);
10002
+ this.writer.seek(28);
10003
+ this.riffWriter.writeU64(this.dataSize);
10004
+ this.writer.seek(36);
10005
+ this.riffWriter.writeU64(this.sampleCount);
10006
+ } else {
10007
+ this.writer.seek(4);
10008
+ this.riffWriter.writeU32(endPos - 8);
10009
+ this.writer.seek(40);
10010
+ this.riffWriter.writeU32(this.dataSize);
10011
+ }
9750
10012
  this.writer.seek(endPos);
9751
10013
  release();
9752
10014
  }
@@ -10005,6 +10267,9 @@ ${cue.notes ?? ""}`;
10005
10267
  if (!options || typeof options !== "object") {
10006
10268
  throw new TypeError("options must be an object.");
10007
10269
  }
10270
+ if (options.large !== void 0 && typeof options.large !== "boolean") {
10271
+ throw new TypeError("options.large, when provided, must be a boolean.");
10272
+ }
10008
10273
  if (options.onHeader !== void 0 && typeof options.onHeader !== "function") {
10009
10274
  throw new TypeError("options.onHeader, when provided, must be a function.");
10010
10275
  }
@@ -15973,7 +16238,7 @@ ${cue.notes ?? ""}`;
15973
16238
  }
15974
16239
  const riffReader = new RiffReader(input._mainReader);
15975
16240
  const riffType = riffReader.readAscii(4);
15976
- if (riffType !== "RIFF" && riffType !== "RIFX") {
16241
+ if (riffType !== "RIFF" && riffType !== "RIFX" && riffType !== "RF64") {
15977
16242
  return false;
15978
16243
  }
15979
16244
  riffReader.pos = 8;
@@ -16380,7 +16645,7 @@ ${cue.notes ?? ""}`;
16380
16645
  const decoderConfig = await track.getDecoderConfig();
16381
16646
  const meta = { decoderConfig: decoderConfig ?? void 0 };
16382
16647
  const endPacket = Number.isFinite(this._endTimestamp) ? await sink.getPacket(this._endTimestamp, { metadataOnly: true }) ?? void 0 : void 0;
16383
- for await (const packet of sink.packets(void 0, endPacket)) {
16648
+ for await (const packet of sink.packets(void 0, endPacket, { verifyKeyPackets: true })) {
16384
16649
  if (this._synchronizer.shouldWait(track.id, packet.timestamp)) {
16385
16650
  await this._synchronizer.wait(packet.timestamp);
16386
16651
  }