@remotion/media-parser 4.0.210 → 4.0.211

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 (113) hide show
  1. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +12 -4
  2. package/dist/boxes/iso-base-media/mdat/mdat.js +13 -3
  3. package/dist/boxes/iso-base-media/process-box.js +46 -7
  4. package/dist/boxes/iso-base-media/traversal.d.ts +2 -7
  5. package/dist/boxes/iso-base-media/traversal.js +5 -15
  6. package/dist/boxes/webm/color.js +29 -22
  7. package/dist/boxes/webm/ebml.d.ts +1 -1
  8. package/dist/boxes/webm/get-track.js +1 -16
  9. package/dist/boxes/webm/segments/parse-children.js +6 -0
  10. package/dist/buffer-iterator.d.ts +4 -1
  11. package/dist/buffer-iterator.js +31 -5
  12. package/dist/create/cluster.d.ts +8 -2
  13. package/dist/create/cluster.js +3 -2
  14. package/dist/create/create-media.d.ts +1 -1
  15. package/dist/create/create-media.js +44 -27
  16. package/dist/create/matroska-cues.d.ts +1 -1
  17. package/dist/create/matroska-cues.js +4 -5
  18. package/dist/create/matroska-trackentry.js +0 -8
  19. package/dist/esm/from-fetch.mjs +41 -6
  20. package/dist/esm/from-node.mjs +2 -1
  21. package/dist/esm/from-web-file.mjs +2 -1
  22. package/dist/esm/index.mjs +239 -106
  23. package/dist/get-audio-codec.d.ts +1 -1
  24. package/dist/get-audio-codec.js +4 -1
  25. package/dist/index.d.ts +1 -0
  26. package/dist/parse-media.js +34 -25
  27. package/dist/parser-context.d.ts +1 -0
  28. package/dist/readers/from-fetch.d.ts +12 -0
  29. package/dist/readers/from-fetch.js +59 -13
  30. package/dist/readers/from-node.js +1 -0
  31. package/dist/readers/from-web-file.js +1 -0
  32. package/dist/readers/reader.d.ts +1 -0
  33. package/dist/traversal.d.ts +21 -0
  34. package/dist/traversal.js +158 -1
  35. package/package.json +2 -2
  36. package/dist/av1-codec-string.d.ts +0 -3
  37. package/dist/av1-codec-string.js +0 -91
  38. package/dist/boxes/iso-base-media/ftype.d.ts +0 -9
  39. package/dist/boxes/iso-base-media/ftype.js +0 -31
  40. package/dist/boxes/iso-base-media/stsd/avcc-hvcc.d.ts +0 -20
  41. package/dist/boxes/iso-base-media/stsd/avcc-hvcc.js +0 -73
  42. package/dist/boxes/iso-base-media/stts/stts.d.ts +0 -15
  43. package/dist/boxes/iso-base-media/stts/stts.js +0 -35
  44. package/dist/boxes/webm/bitstream/av1/bitstream-frame-header.d.ts +0 -14
  45. package/dist/boxes/webm/bitstream/av1/bitstream-frame-header.js +0 -67
  46. package/dist/boxes/webm/bitstream/av1/bitstream-frame.d.ts +0 -11
  47. package/dist/boxes/webm/bitstream/av1/bitstream-frame.js +0 -14
  48. package/dist/boxes/webm/bitstream/av1/chroma-sample-position.d.ts +0 -6
  49. package/dist/boxes/webm/bitstream/av1/chroma-sample-position.js +0 -9
  50. package/dist/boxes/webm/bitstream/av1/color-config.d.ts +0 -16
  51. package/dist/boxes/webm/bitstream/av1/color-config.js +0 -103
  52. package/dist/boxes/webm/bitstream/av1/color-primaries.d.ts +0 -14
  53. package/dist/boxes/webm/bitstream/av1/color-primaries.js +0 -17
  54. package/dist/boxes/webm/bitstream/av1/decoder-model-info.d.ts +0 -9
  55. package/dist/boxes/webm/bitstream/av1/decoder-model-info.js +0 -17
  56. package/dist/boxes/webm/bitstream/av1/frame.d.ts +0 -0
  57. package/dist/boxes/webm/bitstream/av1/frame.js +0 -1
  58. package/dist/boxes/webm/bitstream/av1/header-segment.d.ts +0 -51
  59. package/dist/boxes/webm/bitstream/av1/header-segment.js +0 -183
  60. package/dist/boxes/webm/bitstream/av1/matrix-coefficients.d.ts +0 -17
  61. package/dist/boxes/webm/bitstream/av1/matrix-coefficients.js +0 -20
  62. package/dist/boxes/webm/bitstream/av1/operating-parameters-info.d.ts +0 -10
  63. package/dist/boxes/webm/bitstream/av1/operating-parameters-info.js +0 -15
  64. package/dist/boxes/webm/bitstream/av1/temporal-point-info.d.ts +0 -5
  65. package/dist/boxes/webm/bitstream/av1/temporal-point-info.js +0 -8
  66. package/dist/boxes/webm/bitstream/av1/timing-info.d.ts +0 -8
  67. package/dist/boxes/webm/bitstream/av1/timing-info.js +0 -20
  68. package/dist/boxes/webm/bitstream/av1/transfer-characteristics.d.ts +0 -21
  69. package/dist/boxes/webm/bitstream/av1/transfer-characteristics.js +0 -24
  70. package/dist/boxes/webm/bitstream/av1/uvlc.d.ts +0 -2
  71. package/dist/boxes/webm/bitstream/av1/uvlc.js +0 -20
  72. package/dist/boxes/webm/bitstream/av1.d.ts +0 -20
  73. package/dist/boxes/webm/bitstream/av1.js +0 -118
  74. package/dist/boxes/webm/bitstream/h264/get-h264-descriptor.d.ts +0 -0
  75. package/dist/boxes/webm/bitstream/h264/get-h264-descriptor.js +0 -1
  76. package/dist/boxes/webm/segments/duration.d.ts +0 -6
  77. package/dist/boxes/webm/segments/duration.js +0 -19
  78. package/dist/boxes/webm/segments/info.d.ts +0 -9
  79. package/dist/boxes/webm/segments/info.js +0 -22
  80. package/dist/boxes/webm/segments/main.d.ts +0 -5
  81. package/dist/boxes/webm/segments/main.js +0 -2
  82. package/dist/boxes/webm/segments/muxing.d.ts +0 -6
  83. package/dist/boxes/webm/segments/muxing.js +0 -11
  84. package/dist/boxes/webm/segments/seek-head.d.ts +0 -9
  85. package/dist/boxes/webm/segments/seek-head.js +0 -22
  86. package/dist/boxes/webm/segments/seek-position.d.ts +0 -6
  87. package/dist/boxes/webm/segments/seek-position.js +0 -11
  88. package/dist/boxes/webm/segments/seek.d.ts +0 -13
  89. package/dist/boxes/webm/segments/seek.js +0 -35
  90. package/dist/boxes/webm/segments/timestamp-scale.d.ts +0 -6
  91. package/dist/boxes/webm/segments/timestamp-scale.js +0 -11
  92. package/dist/boxes/webm/segments/tracks.d.ts +0 -8
  93. package/dist/boxes/webm/segments/tracks.js +0 -21
  94. package/dist/boxes/webm/segments/unknown.d.ts +0 -6
  95. package/dist/boxes/webm/segments/unknown.js +0 -11
  96. package/dist/boxes/webm/segments/void.d.ts +0 -6
  97. package/dist/boxes/webm/segments/void.js +0 -11
  98. package/dist/boxes/webm/segments/writing.d.ts +0 -6
  99. package/dist/boxes/webm/segments/writing.js +0 -11
  100. package/dist/boxes/webm/tracks.d.ts +0 -8
  101. package/dist/boxes/webm/tracks.js +0 -21
  102. package/dist/combine-uint8array.d.ts +0 -1
  103. package/dist/combine-uint8array.js +0 -13
  104. package/dist/create/cues.d.ts +0 -0
  105. package/dist/create/cues.js +0 -1
  106. package/dist/from-web.d.ts +0 -2
  107. package/dist/from-web.js +0 -45
  108. package/dist/get-video-metadata.d.ts +0 -2
  109. package/dist/get-video-metadata.js +0 -44
  110. package/dist/read-and-increment-offset.d.ts +0 -28
  111. package/dist/read-and-increment-offset.js +0 -177
  112. package/dist/understand-vorbis.d.ts +0 -1
  113. package/dist/understand-vorbis.js +0 -12
@@ -1,4 +1,32 @@
1
1
  // src/readers/from-fetch.ts
2
+ function parseContentRange(input) {
3
+ const matches = input.match(/^(\w+) ((\d+)-(\d+)|\*)\/(\d+|\*)$/);
4
+ if (!matches)
5
+ return null;
6
+ const [, unit, , start, end, size] = matches;
7
+ const range = {
8
+ unit,
9
+ start: start != null ? Number(start) : null,
10
+ end: end != null ? Number(end) : null,
11
+ size: size === "*" ? null : Number(size)
12
+ };
13
+ if (range.start === null && range.end === null && range.size === null) {
14
+ return null;
15
+ }
16
+ return range;
17
+ }
18
+ var validateContentRangeAndDetectIfSupported = (actualRange, parsedContentRange) => {
19
+ if (typeof actualRange === "number" && parsedContentRange?.start !== actualRange) {
20
+ if (actualRange === 0) {
21
+ return { supportsContentRange: false };
22
+ }
23
+ throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
24
+ }
25
+ if (actualRange !== null && typeof actualRange !== "number" && (parsedContentRange?.start !== actualRange[0] || parsedContentRange?.end !== actualRange[1])) {
26
+ throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
27
+ }
28
+ return { supportsContentRange: true };
29
+ };
2
30
  var fetchReader = {
3
31
  read: async (src, range, signal) => {
4
32
  if (typeof src !== "string") {
@@ -9,20 +37,25 @@ var fetchReader = {
9
37
  return Promise.reject(new Error(resolvedUrl + " is not a URL - needs to start with http:// or https:// or blob:. If you want to read a local file, pass `reader: nodeReader` to parseMedia()."));
10
38
  }
11
39
  const controller = new AbortController;
40
+ const cache = typeof navigator !== "undefined" && navigator.userAgent.includes("Cloudflare-Workers") ? undefined : "no-store";
41
+ const actualRange = range === null ? 0 : range;
12
42
  const res = await fetch(resolvedUrl, {
13
- headers: range === null ? {} : typeof range === "number" ? {
14
- Range: `bytes=${range}`
43
+ headers: typeof actualRange === "number" ? {
44
+ Range: `bytes=${actualRange}-`
15
45
  } : {
16
- Range: `bytes=${`${range[0]}-${range[1]}`}`
46
+ Range: `bytes=${`${actualRange[0]}-${actualRange[1]}`}`
17
47
  },
18
48
  signal: controller.signal,
19
- cache: "no-store"
49
+ cache
20
50
  });
51
+ const contentRange = res.headers.get("content-range");
52
+ const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
53
+ const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange);
21
54
  signal?.addEventListener("abort", () => {
22
55
  controller.abort();
23
56
  }, { once: true });
24
57
  if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
25
- throw new Error(`Server returned status code ${res.status} for ${src}`);
58
+ throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
26
59
  }
27
60
  if (!res.body) {
28
61
  throw new Error("No body");
@@ -47,7 +80,8 @@ var fetchReader = {
47
80
  }
48
81
  },
49
82
  contentLength,
50
- name: name ?? fallbackName
83
+ name: name ?? fallbackName,
84
+ supportsContentRange
51
85
  };
52
86
  },
53
87
  getLength: async (src) => {
@@ -1060,11 +1094,16 @@ var makeCluster = async (w, timestamp) => {
1060
1094
  clusterSize += simpleBlock2.byteLength;
1061
1095
  await w.updateDataAt(clusterVIntPosition, getVariableInt(clusterSize, CLUSTER_MIN_VINT_WIDTH));
1062
1096
  await w.write(simpleBlock2);
1097
+ return { timecodeRelativeToCluster };
1063
1098
  };
1064
- const shouldMakeNewCluster = (newT, keyframe) => {
1099
+ const shouldMakeNewCluster = ({
1100
+ isVideo,
1101
+ keyframe,
1102
+ newT
1103
+ }) => {
1065
1104
  const newTimestamp = timestampToClusterTimestamp(newT);
1066
1105
  const oldTimestamp = timestampToClusterTimestamp(timestamp);
1067
- return newTimestamp - oldTimestamp >= 2000 && keyframe;
1106
+ return newTimestamp - oldTimestamp >= 2000 && keyframe && isVideo;
1068
1107
  };
1069
1108
  return { addSample, shouldMakeNewCluster };
1070
1109
  };
@@ -1094,18 +1133,18 @@ var createMatroskaCues = (cues) => {
1094
1133
  type: "CueTime",
1095
1134
  minVintWidth: null,
1096
1135
  value: {
1097
- value: timestampToClusterTimestamp(cue.time),
1136
+ value: cue.time,
1098
1137
  byteLength: null
1099
1138
  }
1100
1139
  },
1101
- ...cue.trackNumbers.map((trackNumber2) => ({
1140
+ {
1102
1141
  type: "CueTrackPositions",
1103
1142
  value: [
1104
1143
  {
1105
1144
  type: "CueTrack",
1106
1145
  minVintWidth: null,
1107
1146
  value: {
1108
- value: trackNumber2,
1147
+ value: cue.trackNumber,
1109
1148
  byteLength: null
1110
1149
  }
1111
1150
  },
@@ -1119,7 +1158,7 @@ var createMatroskaCues = (cues) => {
1119
1158
  }
1120
1159
  ],
1121
1160
  minVintWidth: null
1122
- }))
1161
+ }
1123
1162
  ],
1124
1163
  minVintWidth: null
1125
1164
  };
@@ -1258,6 +1297,11 @@ var createMatroskaSegment = (children) => {
1258
1297
  });
1259
1298
  };
1260
1299
 
1300
+ // src/truthy.ts
1301
+ function truthy(value) {
1302
+ return Boolean(value);
1303
+ }
1304
+
1261
1305
  // src/boxes/webm/traversal.ts
1262
1306
  var getMainSegment = (segments) => {
1263
1307
  return segments.find((s) => s.type === "Segment");
@@ -1484,7 +1528,7 @@ var makeMatroskaColorBytes = ({
1484
1528
  type: "Colour",
1485
1529
  minVintWidth: null,
1486
1530
  value: [
1487
- {
1531
+ transferChracteristicsValue === 2 ? null : {
1488
1532
  type: "TransferCharacteristics",
1489
1533
  value: {
1490
1534
  value: transferChracteristicsValue,
@@ -1492,7 +1536,7 @@ var makeMatroskaColorBytes = ({
1492
1536
  },
1493
1537
  minVintWidth: null
1494
1538
  },
1495
- {
1539
+ matrixCoefficientsValue === 2 ? null : {
1496
1540
  type: "MatrixCoefficients",
1497
1541
  value: {
1498
1542
  value: matrixCoefficientsValue,
@@ -1500,7 +1544,7 @@ var makeMatroskaColorBytes = ({
1500
1544
  },
1501
1545
  minVintWidth: null
1502
1546
  },
1503
- {
1547
+ primariesValue === 2 ? null : {
1504
1548
  type: "Primaries",
1505
1549
  value: {
1506
1550
  value: primariesValue,
@@ -1516,7 +1560,7 @@ var makeMatroskaColorBytes = ({
1516
1560
  },
1517
1561
  minVintWidth: null
1518
1562
  }
1519
- ]
1563
+ ].filter(truthy)
1520
1564
  });
1521
1565
  };
1522
1566
 
@@ -1644,14 +1688,6 @@ var makeMatroskaAudioTrackEntryBytes = ({
1644
1688
  },
1645
1689
  minVintWidth: null
1646
1690
  },
1647
- {
1648
- type: "TrackTimestampScale",
1649
- value: {
1650
- value: 1,
1651
- size: "64"
1652
- },
1653
- minVintWidth: null
1654
- },
1655
1691
  {
1656
1692
  type: "CodecID",
1657
1693
  value: makeAudioCodecId(codec),
@@ -1788,13 +1824,15 @@ var createMedia = async (writer) => {
1788
1824
  const cues = [];
1789
1825
  const trackNumbers = [];
1790
1826
  const matroskaSegment = createMatroskaSegment([
1791
- matroskaInfo,
1792
1827
  ...createMatroskaSeekHead(seeks),
1828
+ matroskaInfo,
1793
1829
  ...makeMatroskaTracks(currentTracks)
1794
1830
  ]);
1795
- const durationOffset = (matroskaSegment.offsets.children[0].children.find((c) => c.field === "Duration")?.offset ?? 0) + w.getWrittenByteCount();
1831
+ const infoSegment = matroskaSegment.offsets.children.find((o) => o.field === "Info");
1832
+ const durationOffset = (infoSegment?.children.find((c) => c.field === "Duration")?.offset ?? 0) + w.getWrittenByteCount();
1796
1833
  const tracksOffset = (matroskaSegment.offsets.children.find((o) => o.field === "Tracks")?.offset ?? 0) + w.getWrittenByteCount();
1797
1834
  const seekHeadOffset = (matroskaSegment.offsets.children.find((o) => o.field === "SeekHead")?.offset ?? 0) + w.getWrittenByteCount();
1835
+ const infoOffset = (infoSegment?.offset ?? 0) + w.getWrittenByteCount();
1798
1836
  if (!seekHeadOffset) {
1799
1837
  throw new Error("could not get seek offset");
1800
1838
  }
@@ -1804,6 +1842,13 @@ var createMedia = async (writer) => {
1804
1842
  if (!tracksOffset) {
1805
1843
  throw new Error("could not get tracks offset");
1806
1844
  }
1845
+ if (!infoOffset) {
1846
+ throw new Error("could not get tracks offset");
1847
+ }
1848
+ seeks.push({
1849
+ hexString: matroskaElements.Info,
1850
+ byte: infoOffset - seekHeadOffset
1851
+ });
1807
1852
  seeks.push({
1808
1853
  hexString: matroskaElements.Tracks,
1809
1854
  byte: tracksOffset - seekHeadOffset
@@ -1812,10 +1857,10 @@ var createMedia = async (writer) => {
1812
1857
  const updatedSeek = createMatroskaSeekHead(seeks);
1813
1858
  await w.updateDataAt(seekHeadOffset, combineUint8Arrays(updatedSeek.map((b) => b.bytes)));
1814
1859
  };
1815
- const segmentOffset = w.getWrittenByteCount() + matroskaToHex(matroskaElements.Segment).byteLength;
1860
+ const segmentOffset = w.getWrittenByteCount();
1816
1861
  const updateSegmentSize = async (size) => {
1817
1862
  const data = getVariableInt(size, MATROSKA_SEGMENT_MIN_VINT_WIDTH);
1818
- await w.updateDataAt(segmentOffset, data);
1863
+ await w.updateDataAt(segmentOffset + matroskaToHex(matroskaElements.Segment).byteLength, data);
1819
1864
  };
1820
1865
  await w.write(matroskaSegment.bytes);
1821
1866
  const clusterOffset = w.getWrittenByteCount();
@@ -1824,32 +1869,39 @@ var createMedia = async (writer) => {
1824
1869
  hexString: matroskaElements.Cluster,
1825
1870
  byte: clusterOffset - seekHeadOffset
1826
1871
  });
1827
- cues.push({
1828
- time: 0,
1829
- clusterPosition: clusterOffset - seekHeadOffset,
1830
- trackNumbers
1831
- });
1832
1872
  const trackNumberProgresses = {};
1833
- const getClusterOrMakeNew = async (chunk) => {
1873
+ const getClusterOrMakeNew = async ({
1874
+ chunk,
1875
+ isVideo
1876
+ }) => {
1834
1877
  const smallestProgress = Math.min(...Object.values(trackNumberProgresses));
1835
- if (!currentCluster.shouldMakeNewCluster(smallestProgress, chunk.type === "key")) {
1836
- return currentCluster;
1837
- }
1838
- const newCluster = w.getWrittenByteCount();
1839
- cues.push({
1840
- time: smallestProgress,
1841
- clusterPosition: newCluster - seekHeadOffset,
1842
- trackNumbers
1843
- });
1878
+ if (!currentCluster.shouldMakeNewCluster({
1879
+ newT: smallestProgress,
1880
+ keyframe: chunk.type === "key",
1881
+ isVideo
1882
+ })) {
1883
+ return { cluster: currentCluster, isNew: false, smallestProgress };
1884
+ }
1844
1885
  currentCluster = await makeCluster(w, smallestProgress);
1845
- return currentCluster;
1886
+ return { cluster: currentCluster, isNew: true, smallestProgress };
1846
1887
  };
1847
- const addSample = async (chunk, trackNumber2) => {
1888
+ const addSample = async (chunk, trackNumber2, isVideo) => {
1848
1889
  trackNumberProgresses[trackNumber2] = chunk.timestamp;
1849
- const cluster3 = await getClusterOrMakeNew(chunk);
1890
+ const { cluster: cluster2, isNew, smallestProgress } = await getClusterOrMakeNew({
1891
+ chunk,
1892
+ isVideo
1893
+ });
1850
1894
  const newDuration = Math.round((chunk.timestamp + (chunk.duration ?? 0)) / 1000);
1851
1895
  await updateDuration(newDuration);
1852
- return cluster3.addSample(chunk, trackNumber2);
1896
+ const { timecodeRelativeToCluster } = await cluster2.addSample(chunk, trackNumber2);
1897
+ if (isNew) {
1898
+ const newCluster = w.getWrittenByteCount();
1899
+ cues.push({
1900
+ time: timestampToClusterTimestamp(smallestProgress) + timecodeRelativeToCluster,
1901
+ clusterPosition: newCluster - seekHeadOffset,
1902
+ trackNumber: trackNumber2
1903
+ });
1904
+ }
1853
1905
  };
1854
1906
  const updateDuration = async (newDuration) => {
1855
1907
  const blocks = makeDurationWithPadding(newDuration);
@@ -1870,8 +1922,8 @@ var createMedia = async (writer) => {
1870
1922
  remove: async () => {
1871
1923
  await w.remove();
1872
1924
  },
1873
- addSample: (chunk, trackNumber2) => {
1874
- operationProm.current = operationProm.current.then(() => addSample(chunk, trackNumber2));
1925
+ addSample: (chunk, trackNumber2, isVideo) => {
1926
+ operationProm.current = operationProm.current.then(() => addSample(chunk, trackNumber2, isVideo));
1875
1927
  return operationProm.current;
1876
1928
  },
1877
1929
  updateDuration: (duration2) => {
@@ -1897,8 +1949,8 @@ var createMedia = async (writer) => {
1897
1949
  });
1898
1950
  await updateSeekWrite();
1899
1951
  await w.write(createMatroskaCues(cues).bytes);
1900
- const segmentSize = w.getWrittenByteCount() - segmentOffset;
1901
1952
  await w.waitForFinish();
1953
+ const segmentSize = w.getWrittenByteCount() - segmentOffset - matroskaToHex(matroskaElements.Segment).byteLength - MATROSKA_SEGMENT_MIN_VINT_WIDTH;
1902
1954
  await updateSegmentSize(segmentSize);
1903
1955
  }
1904
1956
  };
@@ -2058,25 +2110,15 @@ var getTrunBoxes = (segment) => {
2058
2110
  const trunBoxes = segment.children.filter((c) => c.type === "trun-box");
2059
2111
  return trunBoxes;
2060
2112
  };
2061
- var hasSkippedMdatProcessing = (anySegment) => {
2113
+ var getMdatBox = (anySegment) => {
2062
2114
  const mdat = anySegment.find((b) => b.type === "mdat-box");
2063
2115
  if (!mdat) {
2064
- return {
2065
- skipped: false
2066
- };
2116
+ return null;
2067
2117
  }
2068
2118
  if (mdat.type !== "mdat-box") {
2069
2119
  throw new Error("Expected mdat-box");
2070
2120
  }
2071
- if (mdat.samplesProcessed) {
2072
- return {
2073
- skipped: false
2074
- };
2075
- }
2076
- return {
2077
- skipped: true,
2078
- fileOffset: mdat.fileOffset
2079
- };
2121
+ return mdat;
2080
2122
  };
2081
2123
 
2082
2124
  // src/get-fps.ts
@@ -2270,6 +2312,9 @@ var getAudioCodecStringFromTrak = (trak) => {
2270
2312
  };
2271
2313
  };
2272
2314
  var getAudioCodecFromAudioCodecInfo = (codec) => {
2315
+ if (codec.format === "twos") {
2316
+ return "pcm-s16";
2317
+ }
2273
2318
  if (codec.format === "sowt") {
2274
2319
  return "aiff";
2275
2320
  }
@@ -2285,7 +2330,7 @@ var getAudioCodecFromAudioCodecInfo = (codec) => {
2285
2330
  }
2286
2331
  throw new Error("Unknown mp4a codec: " + codec.primarySpecificator);
2287
2332
  }
2288
- throw new Error("Unknown audio format: " + codec.format);
2333
+ throw new Error(`Unknown audio format: ${codec.format}`);
2289
2334
  };
2290
2335
  var getAudioCodecFromTrack = (track) => {
2291
2336
  const audioSample = getAudioCodecFromTrak(track);
@@ -2464,6 +2509,9 @@ class OffsetCounter {
2464
2509
  setDiscardedOffset(offset) {
2465
2510
  this.#discardedBytes = offset;
2466
2511
  }
2512
+ getDiscardedBytes() {
2513
+ return this.#discardedBytes;
2514
+ }
2467
2515
  discardBytes(amount) {
2468
2516
  this.#discardedBytes += amount;
2469
2517
  }
@@ -2496,11 +2544,18 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
2496
2544
  data.set(initialData);
2497
2545
  let view = new DataView(data.buffer);
2498
2546
  const counter = makeOffsetCounter();
2547
+ let discardAllowed = true;
2499
2548
  const getSlice = (amount) => {
2500
2549
  const value = data.slice(counter.getDiscardedOffset(), counter.getDiscardedOffset() + amount);
2501
2550
  counter.increment(amount);
2502
2551
  return value;
2503
2552
  };
2553
+ const disallowDiscard = () => {
2554
+ discardAllowed = false;
2555
+ };
2556
+ const allowDiscard = () => {
2557
+ discardAllowed = true;
2558
+ };
2504
2559
  const getUint8 = () => {
2505
2560
  const val = view.getUint8(counter.getDiscardedOffset());
2506
2561
  counter.increment(1);
@@ -2606,6 +2661,9 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
2606
2661
  return matchesPattern(mpegPattern)(data.subarray(0, 4));
2607
2662
  };
2608
2663
  const removeBytesRead = () => {
2664
+ if (!discardAllowed) {
2665
+ return;
2666
+ }
2609
2667
  const bytesToRemove = counter.getDiscardedOffset();
2610
2668
  if (bytesToRemove < 1e5) {
2611
2669
  return;
@@ -2616,12 +2674,22 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
2616
2674
  buf.resize(newData.byteLength);
2617
2675
  view = new DataView(data.buffer);
2618
2676
  };
2619
- const skipTo = (offset) => {
2677
+ const skipTo = (offset, reset) => {
2620
2678
  const becomesSmaller = offset < counter.getOffset();
2621
2679
  if (becomesSmaller) {
2622
- buf.resize(0);
2623
- counter.decrement(counter.getOffset() - offset);
2624
- counter.setDiscardedOffset(offset);
2680
+ if (reset) {
2681
+ buf.resize(0);
2682
+ counter.decrement(counter.getOffset() - offset);
2683
+ counter.setDiscardedOffset(offset);
2684
+ } else {
2685
+ const toDecrement = counter.getOffset() - offset;
2686
+ const newOffset = counter.getOffset() - toDecrement;
2687
+ counter.decrement(toDecrement);
2688
+ const c = counter.getDiscardedBytes();
2689
+ if (c > newOffset) {
2690
+ throw new Error("already discarded too many bytes");
2691
+ }
2692
+ }
2625
2693
  } else {
2626
2694
  const currentOffset = counter.getOffset();
2627
2695
  counter.increment(offset - currentOffset);
@@ -2842,7 +2910,9 @@ var getArrayBufferIterator = (initialData, maxBytes) => {
2842
2910
  getInt32Le,
2843
2911
  getInt32,
2844
2912
  destroy,
2845
- isMp3
2913
+ isMp3,
2914
+ disallowDiscard,
2915
+ allowDiscard
2846
2916
  };
2847
2917
  };
2848
2918
 
@@ -4201,15 +4271,26 @@ var parseMdat = async ({
4201
4271
  fileOffset,
4202
4272
  existingBoxes,
4203
4273
  options,
4204
- signal
4274
+ signal,
4275
+ maySkipSampleProcessing
4205
4276
  }) => {
4206
4277
  const alreadyHas = hasTracks(existingBoxes);
4207
4278
  if (!alreadyHas) {
4279
+ if (maySkipSampleProcessing) {
4280
+ data.discard(size - (data.counter.getOffset() - fileOffset));
4281
+ return Promise.resolve({
4282
+ type: "mdat-box",
4283
+ boxSize: size,
4284
+ status: "samples-skipped",
4285
+ fileOffset
4286
+ });
4287
+ }
4208
4288
  data.discard(size - (data.counter.getOffset() - fileOffset));
4289
+ data.disallowDiscard();
4209
4290
  return Promise.resolve({
4210
4291
  type: "mdat-box",
4211
4292
  boxSize: size,
4212
- samplesProcessed: false,
4293
+ status: "samples-buffered",
4213
4294
  fileOffset
4214
4295
  });
4215
4296
  }
@@ -4284,7 +4365,7 @@ var parseMdat = async ({
4284
4365
  return Promise.resolve({
4285
4366
  type: "mdat-box",
4286
4367
  boxSize: size,
4287
- samplesProcessed: true,
4368
+ status: "samples-processed",
4288
4369
  fileOffset
4289
4370
  });
4290
4371
  };
@@ -5361,9 +5442,10 @@ var parseMdatPartially = async ({
5361
5442
  fileOffset,
5362
5443
  existingBoxes: parsedBoxes,
5363
5444
  options,
5364
- signal
5445
+ signal,
5446
+ maySkipSampleProcessing: options.supportsContentRange
5365
5447
  });
5366
- if (box.samplesProcessed && box.fileOffset + boxSize === iterator.counter.getOffset()) {
5448
+ if ((box.status === "samples-processed" || box.status === "samples-buffered") && box.fileOffset + boxSize === iterator.counter.getOffset()) {
5367
5449
  return {
5368
5450
  type: "complete",
5369
5451
  box,
@@ -5412,7 +5494,7 @@ var processBox = async ({
5412
5494
  const boxSize = boxSizeRaw === 1 ? iterator.getEightByteNumber(littleEndian) : boxSizeRaw;
5413
5495
  if (bytesRemaining < boxSize) {
5414
5496
  if (boxType === "mdat") {
5415
- const shouldSkip = options.canSkipVideoData || !hasTracks(parsedBoxes);
5497
+ const shouldSkip = (options.canSkipVideoData || !hasTracks(parsedBoxes)) && options.supportsContentRange;
5416
5498
  if (shouldSkip) {
5417
5499
  const skipTo = fileOffset + boxSize;
5418
5500
  const bytesToSkip = skipTo - iterator.counter.getOffset();
@@ -5423,7 +5505,7 @@ var processBox = async ({
5423
5505
  type: "mdat-box",
5424
5506
  boxSize,
5425
5507
  fileOffset,
5426
- samplesProcessed: false
5508
+ status: "samples-skipped"
5427
5509
  },
5428
5510
  size: boxSize,
5429
5511
  skipTo: fileOffset + boxSize
@@ -5751,8 +5833,12 @@ var processBox = async ({
5751
5833
  fileOffset,
5752
5834
  existingBoxes: parsedBoxes,
5753
5835
  options,
5754
- signal
5836
+ signal,
5837
+ maySkipSampleProcessing: options.supportsContentRange
5755
5838
  });
5839
+ if (box === null) {
5840
+ throw new Error("Unexpected null");
5841
+ }
5756
5842
  return {
5757
5843
  type: "complete",
5758
5844
  box,
@@ -5855,11 +5941,18 @@ var parseBoxes = async ({
5855
5941
  if (result.box.type === "mdat-box" && alreadyHasMdat) {
5856
5942
  boxes = boxes.filter((b) => b.type !== "mdat-box");
5857
5943
  boxes.push(result.box);
5944
+ iterator.allowDiscard();
5945
+ if (result.box.status !== "samples-processed") {
5946
+ throw new Error("unexpected");
5947
+ }
5858
5948
  break;
5859
5949
  } else {
5860
5950
  boxes.push(result.box);
5861
5951
  }
5862
5952
  if (result.skipTo !== null) {
5953
+ if (!options.supportsContentRange) {
5954
+ throw new Error("Content-Range header is not supported by the reader, but was asked to seek");
5955
+ }
5863
5956
  return {
5864
5957
  status: "incomplete",
5865
5958
  segments: boxes,
@@ -5878,18 +5971,42 @@ var parseBoxes = async ({
5878
5971
  skipTo: result.skipTo
5879
5972
  };
5880
5973
  }
5974
+ if (iterator.bytesRemaining() < 0) {
5975
+ return {
5976
+ status: "incomplete",
5977
+ segments: boxes,
5978
+ continueParsing: () => {
5979
+ return parseBoxes({
5980
+ iterator,
5981
+ maxBytes,
5982
+ allowIncompleteBoxes,
5983
+ initialBoxes: boxes,
5984
+ options,
5985
+ continueMdat: false,
5986
+ littleEndian,
5987
+ signal
5988
+ });
5989
+ },
5990
+ skipTo: null
5991
+ };
5992
+ }
5881
5993
  iterator.removeBytesRead();
5882
5994
  }
5883
- const mdatState = hasSkippedMdatProcessing(boxes);
5884
- if (mdatState.skipped && !options.canSkipVideoData) {
5995
+ const mdatState = getMdatBox(boxes);
5996
+ const skipped = mdatState?.status === "samples-skipped" && !options.canSkipVideoData && options.supportsContentRange;
5997
+ const buffered = mdatState?.status === "samples-buffered" && !options.canSkipVideoData;
5998
+ if (skipped || buffered) {
5885
5999
  return {
5886
6000
  status: "incomplete",
5887
6001
  segments: boxes,
5888
6002
  continueParsing: () => {
6003
+ if (buffered) {
6004
+ iterator.skipTo(mdatState.fileOffset, false);
6005
+ }
5889
6006
  return parseBoxes({
5890
6007
  iterator,
5891
6008
  maxBytes,
5892
- allowIncompleteBoxes,
6009
+ allowIncompleteBoxes: false,
5893
6010
  initialBoxes: boxes,
5894
6011
  options,
5895
6012
  continueMdat: false,
@@ -5897,7 +6014,7 @@ var parseBoxes = async ({
5897
6014
  signal
5898
6015
  });
5899
6016
  },
5900
- skipTo: mdatState.fileOffset
6017
+ skipTo: skipped ? mdatState.fileOffset : null
5901
6018
  };
5902
6019
  }
5903
6020
  return {
@@ -6338,6 +6455,9 @@ var continueParsingfunction = ({
6338
6455
  const offset = iterator.counter.getOffset();
6339
6456
  const continued = await result.continueParsing();
6340
6457
  if (continued.status === "incomplete") {
6458
+ if (!parserContext.supportsContentRange) {
6459
+ throw new Error("Content-Range header is not supported by the reader, but was asked to seek");
6460
+ }
6341
6461
  return {
6342
6462
  status: "incomplete",
6343
6463
  continueParsing: continueParsingfunction({
@@ -6380,6 +6500,9 @@ var expectChildren = async ({
6380
6500
  wrap
6381
6501
  });
6382
6502
  if (child.status === "incomplete") {
6503
+ if (!parserContext.supportsContentRange) {
6504
+ throw new Error("Content-Range header is not supported by the reader, but was asked to seek");
6505
+ }
6383
6506
  return {
6384
6507
  status: "incomplete",
6385
6508
  continueParsing: continueParsingfunction({
@@ -6595,8 +6718,14 @@ var parseMedia = async ({
6595
6718
  hasVideoCallbacks: onVideoTrack !== null,
6596
6719
  signal
6597
6720
  });
6598
- const { reader, contentLength, name } = await readerInterface.read(src, null, signal);
6721
+ const {
6722
+ reader,
6723
+ contentLength,
6724
+ name,
6725
+ supportsContentRange: readerSupportsContentRange
6726
+ } = await readerInterface.read(src, null, signal);
6599
6727
  let currentReader = reader;
6728
+ const supportsContentRange = readerSupportsContentRange && !(typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.DISABLE_CONTENT_RANGE === "true");
6600
6729
  const returnValue = {};
6601
6730
  const moreFields = more;
6602
6731
  let iterator = null;
@@ -6606,22 +6735,34 @@ var parseMedia = async ({
6606
6735
  onAudioTrack: onAudioTrack ?? null,
6607
6736
  onVideoTrack: onVideoTrack ?? null,
6608
6737
  parserState: state,
6609
- nullifySamples: !(typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.KEEP_SAMPLES === "true")
6738
+ nullifySamples: !(typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.KEEP_SAMPLES === "true"),
6739
+ supportsContentRange
6610
6740
  };
6611
6741
  while (parseResult === null || parseResult.status === "incomplete") {
6612
6742
  if (signal?.aborted) {
6613
6743
  throw new Error("Aborted");
6614
6744
  }
6615
- const result = await currentReader.reader.read();
6616
- if (iterator) {
6617
- if (!result.done) {
6618
- iterator.addData(result.value);
6745
+ while (true) {
6746
+ const result = await currentReader.reader.read();
6747
+ if (iterator) {
6748
+ if (!result.done) {
6749
+ iterator.addData(result.value);
6750
+ }
6751
+ } else {
6752
+ if (result.done) {
6753
+ throw new Error("Unexpectedly reached EOF");
6754
+ }
6755
+ iterator = getArrayBufferIterator(result.value, contentLength ?? 1e9);
6756
+ }
6757
+ if (iterator.bytesRemaining() >= 0) {
6758
+ break;
6619
6759
  }
6620
- } else {
6621
6760
  if (result.done) {
6622
- throw new Error("Unexpectedly reached EOF");
6761
+ break;
6623
6762
  }
6624
- iterator = getArrayBufferIterator(result.value, contentLength ?? 1e9);
6763
+ }
6764
+ if (!iterator) {
6765
+ throw new Error("Unexpected null");
6625
6766
  }
6626
6767
  if (parseResult && parseResult.status === "incomplete") {
6627
6768
  parseResult = await parseResult.continueParsing();
@@ -6647,27 +6788,19 @@ var parseMedia = async ({
6647
6788
  break;
6648
6789
  }
6649
6790
  if (parseResult && parseResult.status === "incomplete" && parseResult.skipTo !== null) {
6791
+ if (!supportsContentRange) {
6792
+ throw new Error("Content-Range header is not supported by the reader, but was asked to seek");
6793
+ }
6650
6794
  const { reader: newReader } = await readerInterface.read(src, parseResult.skipTo, signal);
6651
6795
  currentReader = newReader;
6652
- iterator.skipTo(parseResult.skipTo);
6796
+ iterator.skipTo(parseResult.skipTo, true);
6653
6797
  }
6654
6798
  }
6655
6799
  emitAvailableInfo({
6656
- hasInfo: {
6657
- boxes: true,
6658
- durationInSeconds: true,
6659
- dimensions: true,
6660
- fps: true,
6661
- videoCodec: true,
6662
- audioCodec: true,
6663
- tracks: true,
6664
- rotation: true,
6665
- unrotatedDimensions: true,
6666
- internalStats: true,
6667
- size: true,
6668
- name: true,
6669
- container: true
6670
- },
6800
+ hasInfo: Object.keys(fields ?? {}).reduce((acc, key) => {
6801
+ acc[key] = true;
6802
+ return acc;
6803
+ }, {}),
6671
6804
  moreFields,
6672
6805
  parseResult,
6673
6806
  state,