@remotion/media-parser 4.0.199 → 4.0.201

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 (117) hide show
  1. package/dist/av1-codec-string.d.ts +5 -0
  2. package/dist/av1-codec-string.js +18 -1
  3. package/dist/bitstream/av1.d.ts +2 -0
  4. package/dist/bitstream/av1.js +12 -0
  5. package/dist/boxes/iso-base-media/avcc-hvcc.d.ts +20 -0
  6. package/dist/boxes/iso-base-media/avcc-hvcc.js +73 -0
  7. package/dist/boxes/iso-base-media/avcc.d.ts +18 -0
  8. package/dist/boxes/iso-base-media/avcc.js +27 -0
  9. package/dist/boxes/iso-base-media/esds-descriptors.d.ts +21 -0
  10. package/dist/boxes/iso-base-media/esds-descriptors.js +62 -0
  11. package/dist/boxes/iso-base-media/esds.d.ts +15 -0
  12. package/dist/boxes/iso-base-media/esds.js +27 -0
  13. package/dist/boxes/iso-base-media/mdat/mdat.js +2 -2
  14. package/dist/boxes/iso-base-media/moov/moov.js +1 -0
  15. package/dist/boxes/iso-base-media/process-box.d.ts +4 -2
  16. package/dist/boxes/iso-base-media/process-box.js +56 -40
  17. package/dist/boxes/iso-base-media/stsd/mebx.d.ts +2 -1
  18. package/dist/boxes/iso-base-media/stsd/mebx.js +2 -1
  19. package/dist/boxes/iso-base-media/stsd/samples.js +3 -0
  20. package/dist/boxes/iso-base-media/stsd/stco.d.ts +3 -2
  21. package/dist/boxes/iso-base-media/stsd/stco.js +2 -2
  22. package/dist/boxes/iso-base-media/trak/trak.js +1 -0
  23. package/dist/boxes/webm/bitstream/av1.js +10 -1
  24. package/dist/boxes/webm/ebml.d.ts +2 -0
  25. package/dist/boxes/webm/ebml.js +72 -0
  26. package/dist/boxes/webm/make-header.d.ts +9 -0
  27. package/dist/boxes/webm/make-header.js +79 -0
  28. package/dist/boxes/webm/parse-ebml.d.ts +7 -0
  29. package/dist/boxes/webm/parse-ebml.js +66 -0
  30. package/dist/boxes/webm/parse-webm-header.js +8 -9
  31. package/dist/boxes/webm/segments/all-segments.d.ts +262 -0
  32. package/dist/boxes/webm/segments/all-segments.js +130 -1
  33. package/dist/boxes/webm/segments/block-simple-block-flags.d.ts +9 -0
  34. package/dist/boxes/webm/segments/block-simple-block-flags.js +38 -0
  35. package/dist/boxes/webm/segments/seek-position.js +1 -1
  36. package/dist/boxes/webm/segments/seek.d.ts +1 -1
  37. package/dist/boxes/webm/segments/seek.js +8 -2
  38. package/dist/boxes/webm/segments/timestamp-scale.js +1 -1
  39. package/dist/boxes/webm/segments/track-entry.d.ts +25 -9
  40. package/dist/boxes/webm/segments/track-entry.js +73 -33
  41. package/dist/boxes/webm/segments.d.ts +3 -3
  42. package/dist/boxes/webm/segments.js +64 -30
  43. package/dist/boxes/webm/traversal.d.ts +1 -0
  44. package/dist/boxes/webm/traversal.js +12 -1
  45. package/dist/buffer-iterator.d.ts +10 -6
  46. package/dist/buffer-iterator.js +92 -9
  47. package/dist/from-fetch.js +13 -3
  48. package/dist/from-input-type-file.d.ts +2 -0
  49. package/dist/from-input-type-file.js +37 -0
  50. package/dist/from-node.js +9 -2
  51. package/dist/from-web-file.js +6 -1
  52. package/dist/from-web.js +15 -6
  53. package/dist/get-codec.d.ts +4 -0
  54. package/dist/get-codec.js +22 -0
  55. package/dist/get-sample-positions.js +1 -1
  56. package/dist/has-all-info.js +1 -1
  57. package/dist/options.d.ts +3 -2
  58. package/dist/parse-media.js +13 -9
  59. package/dist/parse-video.js +16 -0
  60. package/dist/parser-state.d.ts +8 -9
  61. package/dist/parser-state.js +39 -19
  62. package/dist/reader.d.ts +1 -1
  63. package/dist/web-file.d.ts +2 -0
  64. package/dist/web-file.js +37 -0
  65. package/dist/webcodec-sample-types.d.ts +0 -1
  66. package/package.json +2 -2
  67. package/src/boxes/iso-base-media/mdat/mdat.ts +2 -2
  68. package/src/boxes/iso-base-media/moov/moov.ts +1 -0
  69. package/src/boxes/iso-base-media/process-box.ts +70 -40
  70. package/src/boxes/iso-base-media/stsd/mebx.ts +3 -0
  71. package/src/boxes/iso-base-media/stsd/samples.ts +3 -0
  72. package/src/boxes/iso-base-media/stsd/stco.ts +5 -3
  73. package/src/boxes/iso-base-media/trak/trak.ts +1 -0
  74. package/src/boxes/webm/ebml.ts +78 -0
  75. package/src/boxes/webm/make-header.ts +138 -0
  76. package/src/boxes/webm/parse-ebml.ts +93 -0
  77. package/src/boxes/webm/parse-webm-header.ts +8 -12
  78. package/src/boxes/webm/segments/all-segments.ts +226 -0
  79. package/src/boxes/webm/segments/block-simple-block-flags.ts +52 -0
  80. package/src/boxes/webm/segments/seek-position.ts +1 -1
  81. package/src/boxes/webm/segments/seek.ts +12 -2
  82. package/src/boxes/webm/segments/timestamp-scale.ts +1 -1
  83. package/src/boxes/webm/segments/track-entry.ts +125 -41
  84. package/src/boxes/webm/segments.ts +107 -40
  85. package/src/boxes/webm/traversal.ts +13 -0
  86. package/src/buffer-iterator.ts +110 -10
  87. package/src/from-fetch.ts +22 -3
  88. package/src/from-node.ts +18 -4
  89. package/src/from-web-file.ts +11 -1
  90. package/src/get-sample-positions.ts +1 -1
  91. package/src/has-all-info.ts +1 -1
  92. package/src/options.ts +3 -2
  93. package/src/parse-media.ts +14 -8
  94. package/src/parse-video.ts +17 -0
  95. package/src/parser-state.ts +52 -25
  96. package/src/reader.ts +1 -0
  97. package/src/test/create-matroska.test.ts +48 -0
  98. package/src/test/matroska.test.ts +144 -127
  99. package/src/test/parse-stco.test.ts +2 -0
  100. package/src/test/stream-local.test.ts +70 -14
  101. package/src/test/stream-remote.test.ts +23 -19
  102. package/src/test/stsd.test.ts +2 -0
  103. package/src/webcodec-sample-types.ts +0 -1
  104. package/tsconfig.tsbuildinfo +1 -1
  105. package/dist/boxes/iso-base-media/ftype.d.ts +0 -9
  106. package/dist/boxes/iso-base-media/ftype.js +0 -31
  107. package/dist/get-video-metadata.d.ts +0 -2
  108. package/dist/get-video-metadata.js +0 -44
  109. package/dist/read-and-increment-offset.d.ts +0 -28
  110. package/dist/read-and-increment-offset.js +0 -177
  111. package/dist/understand-vorbis.d.ts +0 -1
  112. package/dist/understand-vorbis.js +0 -12
  113. package/src/boxes/webm/segments/unknown.ts +0 -19
  114. /package/dist/{boxes/webm/bitstream/av1/frame.d.ts → get-samples.d.ts} +0 -0
  115. /package/dist/{boxes/webm/bitstream/av1/frame.js → get-samples.js} +0 -0
  116. /package/dist/{boxes/webm/bitstream/h264/get-h264-descriptor.d.ts → sample-aspect-ratio.d.ts} +0 -0
  117. /package/dist/{boxes/webm/bitstream/h264/get-h264-descriptor.js → sample-aspect-ratio.js} +0 -0
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.makeParserState = void 0;
4
4
  const traversal_1 = require("./boxes/webm/traversal");
5
5
  const traversal_2 = require("./traversal");
6
- const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, }) => {
6
+ const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, signal, }) => {
7
7
  const trackEntries = {};
8
8
  const onTrackEntrySegment = (trackEntry) => {
9
9
  const trackId = (0, traversal_2.getTrackId)(trackEntry);
@@ -17,24 +17,48 @@ const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, }) => {
17
17
  if (!codec) {
18
18
  throw new Error('Expected codec');
19
19
  }
20
- trackEntries[trackId] = codec;
20
+ const trackTimescale = (0, traversal_1.getTrackTimestampScale)(trackEntry);
21
+ trackEntries[trackId] = {
22
+ codec: codec.codec,
23
+ trackTimescale,
24
+ };
21
25
  };
22
26
  const videoSampleCallbacks = {};
23
27
  const audioSampleCallbacks = {};
24
- let samplesThatHadToBeQueued = 0;
25
28
  const queuedAudioSamples = {};
26
29
  const queuedVideoSamples = {};
27
30
  const declinedTrackNumbers = [];
28
31
  let timescale = null;
29
32
  const getTimescale = () => {
33
+ // https://www.matroska.org/technical/notes.html
34
+ // When using the default value of TimestampScale of “1,000,000”, one Segment Tick represents one millisecond.
30
35
  if (timescale === null) {
31
- throw new Error('Timescale not set');
36
+ return 1000000;
32
37
  }
33
38
  return timescale;
34
39
  };
35
40
  const setTimescale = (newTimescale) => {
36
41
  timescale = newTimescale;
37
42
  };
43
+ const timestampMap = new Map();
44
+ const setTimestampOffset = (byteOffset, timestamp) => {
45
+ timestampMap.set(byteOffset, timestamp);
46
+ };
47
+ const getTimestampOffsetForByteOffset = (byteOffset) => {
48
+ const entries = Array.from(timestampMap.entries());
49
+ const sortedByByteOffset = entries
50
+ .sort((a, b) => {
51
+ return a[0] - b[0];
52
+ })
53
+ .reverse();
54
+ for (const [offset, timestamp] of sortedByByteOffset) {
55
+ if (offset >= byteOffset) {
56
+ continue;
57
+ }
58
+ return timestamp;
59
+ }
60
+ return timestampMap.get(byteOffset);
61
+ };
38
62
  return {
39
63
  onTrackEntrySegment,
40
64
  getTrackInfoByNumber: (id) => trackEntries[id],
@@ -51,6 +75,8 @@ const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, }) => {
51
75
  }
52
76
  queuedVideoSamples[id] = [];
53
77
  },
78
+ setTimestampOffset,
79
+ getTimestampOffsetForByteOffset,
54
80
  registerAudioSampleCallback: async (id, callback) => {
55
81
  var _a;
56
82
  if (callback === null) {
@@ -65,7 +91,9 @@ const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, }) => {
65
91
  queuedAudioSamples[id] = [];
66
92
  },
67
93
  onAudioSample: async (trackId, audioSample) => {
68
- var _a;
94
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
95
+ throw new Error('Aborted');
96
+ }
69
97
  const callback = audioSampleCallbacks[trackId];
70
98
  if (callback) {
71
99
  await callback(audioSample);
@@ -75,15 +103,14 @@ const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, }) => {
75
103
  return;
76
104
  }
77
105
  if (!hasAudioCallbacks) {
78
- return;
106
+ throw new Error('No audio callbacks registered');
79
107
  }
80
- (_a = queuedAudioSamples[trackId]) !== null && _a !== void 0 ? _a : (queuedAudioSamples[trackId] = []);
81
- queuedAudioSamples[trackId].push(audioSample);
82
- samplesThatHadToBeQueued++;
83
108
  }
84
109
  },
85
110
  onVideoSample: async (trackId, videoSample) => {
86
- var _a;
111
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
112
+ throw new Error('Aborted');
113
+ }
87
114
  const callback = videoSampleCallbacks[trackId];
88
115
  if (callback) {
89
116
  await callback(videoSample);
@@ -93,18 +120,11 @@ const makeParserState = ({ hasAudioCallbacks, hasVideoCallbacks, }) => {
93
120
  return;
94
121
  }
95
122
  if (!hasVideoCallbacks) {
96
- return;
123
+ throw new Error('No video callbacks registered');
97
124
  }
98
- (_a = queuedVideoSamples[trackId]) !== null && _a !== void 0 ? _a : (queuedVideoSamples[trackId] = []);
99
- queuedVideoSamples[trackId].push(videoSample);
100
- samplesThatHadToBeQueued++;
101
125
  }
102
126
  },
103
- getInternalStats: () => {
104
- return {
105
- samplesThatHadToBeQueued,
106
- };
107
- },
127
+ getInternalStats: () => ({}),
108
128
  getTimescale,
109
129
  setTimescale,
110
130
  };
package/dist/reader.d.ts CHANGED
@@ -2,7 +2,7 @@ type ReadResult = {
2
2
  reader: ReadableStreamDefaultReader<Uint8Array>;
3
3
  contentLength: number | null;
4
4
  };
5
- type ReadContent = (src: string | File, range: [number, number] | number | null) => Promise<ReadResult>;
5
+ type ReadContent = (src: string | File, range: [number, number] | number | null, signal: AbortSignal | undefined) => Promise<ReadResult>;
6
6
  type GetLength = (src: string | File) => Promise<number>;
7
7
  export type ReaderInterface = {
8
8
  read: ReadContent;
@@ -0,0 +1,2 @@
1
+ import type { ReaderInterface } from './reader';
2
+ export declare const inputTypeFileReader: ReaderInterface;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inputTypeFileReader = void 0;
4
+ exports.inputTypeFileReader = {
5
+ read: (file, range) => {
6
+ if (typeof file === 'string') {
7
+ throw new Error('`inputTypeFileReader` only supports `File` objects');
8
+ }
9
+ if (range !== null) {
10
+ throw new Error('`inputTypeFileReader` does not support `range`');
11
+ }
12
+ const part = range === null
13
+ ? file
14
+ : typeof range === 'number'
15
+ ? file.slice(range)
16
+ : file.slice(range[0], range[1]);
17
+ const reader = new FileReader();
18
+ reader.readAsArrayBuffer(file);
19
+ return new Promise((resolve, reject) => {
20
+ reader.onload = () => {
21
+ resolve({
22
+ reader: part.stream().getReader(),
23
+ contentLength: file.size,
24
+ });
25
+ };
26
+ reader.onerror = (error) => {
27
+ reject(error);
28
+ };
29
+ });
30
+ },
31
+ getLength: (src) => {
32
+ if (typeof src === 'string') {
33
+ throw new Error('`inputTypeFileReader` only supports `File` objects');
34
+ }
35
+ return Promise.resolve(src.size);
36
+ },
37
+ };
@@ -2,7 +2,6 @@ import type { AudioTrack, VideoTrack } from './get-tracks';
2
2
  export type AudioSample = {
3
3
  data: Uint8Array;
4
4
  timestamp: number;
5
- offset: number;
6
5
  trackId: number;
7
6
  type: 'key' | 'delta';
8
7
  };
package/package.json CHANGED
@@ -3,11 +3,11 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-parser"
4
4
  },
5
5
  "name": "@remotion/media-parser",
6
- "version": "4.0.199",
6
+ "version": "4.0.201",
7
7
  "main": "dist/index.js",
8
8
  "sideEffects": false,
9
9
  "devDependencies": {
10
- "@remotion/renderer": "4.0.199"
10
+ "@remotion/renderer": "4.0.201"
11
11
  },
12
12
  "publishConfig": {
13
13
  "access": "public"
@@ -26,7 +26,7 @@ export const parseMdat = async ({
26
26
  }): Promise<MdatBox> => {
27
27
  const alreadyHas = hasTracks(existingBoxes);
28
28
  if (!alreadyHas) {
29
- data.discard(size - 8);
29
+ data.discard(size - (data.counter.getOffset() - fileOffset));
30
30
  return Promise.resolve({
31
31
  type: 'mdat-box',
32
32
  boxSize: size,
@@ -91,7 +91,6 @@ export const parseMdat = async ({
91
91
  await options.parserState.onAudioSample(sampleWithIndex.track.trackId, {
92
92
  data: bytes,
93
93
  timestamp: sampleWithIndex.samplePosition.offset,
94
- offset: data.counter.getOffset(),
95
94
  trackId: sampleWithIndex.track.trackId,
96
95
  type: sampleWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
97
96
  });
@@ -117,6 +116,7 @@ export const parseMdat = async ({
117
116
  }
118
117
 
119
118
  const remaining = size - (data.counter.getOffset() - fileOffset);
119
+ data.removeBytesRead();
120
120
  if (remaining === 0) {
121
121
  break;
122
122
  }
@@ -27,6 +27,7 @@ export const parseMoov = async ({
27
27
  initialBoxes: [],
28
28
  options,
29
29
  continueMdat: false,
30
+ littleEndian: false,
30
31
  });
31
32
 
32
33
  if (children.status === 'incomplete') {
@@ -37,11 +37,13 @@ const getChildren = async ({
37
37
  iterator,
38
38
  bytesRemainingInBox,
39
39
  options,
40
+ littleEndian,
40
41
  }: {
41
42
  boxType: string;
42
43
  iterator: BufferIterator;
43
44
  bytesRemainingInBox: number;
44
45
  options: ParserContext;
46
+ littleEndian: boolean;
45
47
  }) => {
46
48
  const parseChildren =
47
49
  boxType === 'mdia' ||
@@ -59,6 +61,7 @@ const getChildren = async ({
59
61
  initialBoxes: [],
60
62
  options,
61
63
  continueMdat: false,
64
+ littleEndian,
62
65
  });
63
66
 
64
67
  if (parsed.status === 'incomplete') {
@@ -121,17 +124,37 @@ export const processBox = async ({
121
124
  allowIncompleteBoxes,
122
125
  parsedBoxes,
123
126
  options,
127
+ littleEndian,
124
128
  }: {
125
129
  iterator: BufferIterator;
126
130
  allowIncompleteBoxes: boolean;
127
131
  parsedBoxes: AnySegment[];
128
132
  options: ParserContext;
133
+ littleEndian: boolean;
129
134
  }): Promise<BoxAndNext> => {
130
135
  const fileOffset = iterator.counter.getOffset();
131
136
  const bytesRemaining = iterator.bytesRemaining();
132
137
 
133
- const boxSize = iterator.getFourByteNumber();
134
- if (boxSize === 0) {
138
+ const boxSizeRaw = iterator.getFourByteNumber(littleEndian);
139
+
140
+ // If `boxSize === 1`, the 8 bytes after the box type are the size of the box.
141
+ if (
142
+ (boxSizeRaw === 1 && iterator.bytesRemaining() < 12) ||
143
+ iterator.bytesRemaining() < 4
144
+ ) {
145
+ iterator.counter.decrement(iterator.counter.getOffset() - fileOffset);
146
+ if (allowIncompleteBoxes) {
147
+ return {
148
+ type: 'incomplete',
149
+ };
150
+ }
151
+
152
+ throw new Error(
153
+ `Expected box size of ${bytesRemaining}, got ${boxSizeRaw}. Incomplete boxes are not allowed.`,
154
+ );
155
+ }
156
+
157
+ if (boxSizeRaw === 0) {
135
158
  return {
136
159
  type: 'complete',
137
160
  box: {
@@ -143,42 +166,41 @@ export const processBox = async ({
143
166
  };
144
167
  }
145
168
 
169
+ const boxType = iterator.getByteString(4);
170
+
171
+ const boxSize =
172
+ boxSizeRaw === 1 ? iterator.getEightByteNumber(littleEndian) : boxSizeRaw;
173
+
146
174
  if (bytesRemaining < boxSize) {
147
- if (bytesRemaining >= 4) {
148
- const type = iterator.getByteString(4);
149
- iterator.counter.decrement(4);
150
-
151
- if (type === 'mdat') {
152
- const shouldSkip = options.canSkipVideoData || !hasTracks(parsedBoxes);
153
-
154
- if (shouldSkip) {
155
- const skipTo = fileOffset + boxSize;
156
- const bytesToSkip = skipTo - iterator.counter.getOffset();
157
-
158
- // If there is a huge mdat chunk, we can skip it because we don't need it for the metadata
159
- if (bytesToSkip > 1_000_000) {
160
- return {
161
- type: 'complete',
162
- box: {
163
- type: 'mdat-box',
164
- boxSize,
165
- fileOffset,
166
- samplesProcessed: false,
167
- },
168
- size: boxSize,
169
- skipTo: fileOffset + boxSize,
170
- };
171
- }
172
- } else {
173
- iterator.discard(4);
174
- return parseMdatPartially({
175
- iterator,
176
- boxSize,
177
- fileOffset,
178
- parsedBoxes,
179
- options,
180
- });
175
+ if (boxType === 'mdat') {
176
+ const shouldSkip = options.canSkipVideoData || !hasTracks(parsedBoxes);
177
+
178
+ if (shouldSkip) {
179
+ const skipTo = fileOffset + boxSize;
180
+ const bytesToSkip = skipTo - iterator.counter.getOffset();
181
+
182
+ // If there is a huge mdat chunk, we can skip it because we don't need it for the metadata
183
+ if (bytesToSkip > 1_000_000) {
184
+ return {
185
+ type: 'complete',
186
+ box: {
187
+ type: 'mdat-box',
188
+ boxSize,
189
+ fileOffset,
190
+ samplesProcessed: false,
191
+ },
192
+ size: boxSize,
193
+ skipTo: fileOffset + boxSize,
194
+ };
181
195
  }
196
+ } else {
197
+ return parseMdatPartially({
198
+ iterator,
199
+ boxSize,
200
+ fileOffset,
201
+ parsedBoxes,
202
+ options,
203
+ });
182
204
  }
183
205
  }
184
206
 
@@ -194,8 +216,6 @@ export const processBox = async ({
194
216
  );
195
217
  }
196
218
 
197
- const boxType = iterator.getByteString(4);
198
-
199
219
  if (boxType === 'ftyp') {
200
220
  const box = parseFtyp({iterator, size: boxSize, offset: fileOffset});
201
221
  return {
@@ -271,11 +291,12 @@ export const processBox = async ({
271
291
  };
272
292
  }
273
293
 
274
- if (boxType === 'stco') {
294
+ if (boxType === 'stco' || boxType === 'co64') {
275
295
  const box = parseStco({
276
296
  iterator,
277
297
  offset: fileOffset,
278
298
  size: boxSize,
299
+ mode64Bit: boxType === 'co64',
279
300
  });
280
301
 
281
302
  return {
@@ -352,6 +373,7 @@ export const processBox = async ({
352
373
  offset: fileOffset,
353
374
  size: boxSize,
354
375
  options,
376
+ littleEndian,
355
377
  });
356
378
 
357
379
  return {
@@ -525,6 +547,7 @@ export const processBox = async ({
525
547
  iterator,
526
548
  bytesRemainingInBox,
527
549
  options,
550
+ littleEndian,
528
551
  });
529
552
 
530
553
  return {
@@ -548,6 +571,7 @@ export const parseBoxes = async ({
548
571
  initialBoxes,
549
572
  options,
550
573
  continueMdat,
574
+ littleEndian,
551
575
  }: {
552
576
  iterator: BufferIterator;
553
577
  maxBytes: number;
@@ -555,6 +579,7 @@ export const parseBoxes = async ({
555
579
  initialBoxes: IsoBaseMediaBox[];
556
580
  options: ParserContext;
557
581
  continueMdat: false | PartialMdatBox;
582
+ littleEndian: boolean;
558
583
  }): Promise<ParseResult> => {
559
584
  let boxes: IsoBaseMediaBox[] = initialBoxes;
560
585
  const initialOffset = iterator.counter.getOffset();
@@ -577,6 +602,7 @@ export const parseBoxes = async ({
577
602
  allowIncompleteBoxes,
578
603
  parsedBoxes: initialBoxes,
579
604
  options,
605
+ littleEndian,
580
606
  });
581
607
 
582
608
  if (result.type === 'incomplete') {
@@ -595,6 +621,7 @@ export const parseBoxes = async ({
595
621
  initialBoxes: boxes,
596
622
  options,
597
623
  continueMdat: false,
624
+ littleEndian,
598
625
  });
599
626
  },
600
627
  skipTo: null,
@@ -614,6 +641,7 @@ export const parseBoxes = async ({
614
641
  initialBoxes: boxes,
615
642
  options,
616
643
  continueMdat: result,
644
+ littleEndian,
617
645
  }),
618
646
  );
619
647
  },
@@ -641,13 +669,14 @@ export const parseBoxes = async ({
641
669
  initialBoxes: boxes,
642
670
  options,
643
671
  continueMdat: false,
672
+ littleEndian,
644
673
  });
645
674
  },
646
675
  skipTo: result.skipTo,
647
676
  };
648
677
  }
649
678
 
650
- iterator.discardFirstBytes();
679
+ iterator.removeBytesRead();
651
680
  }
652
681
 
653
682
  const mdatState = hasSkippedMdatProcessing(boxes);
@@ -663,6 +692,7 @@ export const parseBoxes = async ({
663
692
  initialBoxes: boxes,
664
693
  options,
665
694
  continueMdat: false,
695
+ littleEndian,
666
696
  });
667
697
  },
668
698
  skipTo: mdatState.fileOffset,
@@ -16,11 +16,13 @@ export const parseMebx = async ({
16
16
  offset,
17
17
  size,
18
18
  options,
19
+ littleEndian,
19
20
  }: {
20
21
  iterator: BufferIterator;
21
22
  offset: number;
22
23
  size: number;
23
24
  options: ParserContext;
25
+ littleEndian: boolean;
24
26
  }): Promise<MebxBox> => {
25
27
  // reserved, 6 bit
26
28
  iterator.discard(6);
@@ -34,6 +36,7 @@ export const parseMebx = async ({
34
36
  initialBoxes: [],
35
37
  options,
36
38
  continueMdat: false,
39
+ littleEndian,
37
40
  });
38
41
 
39
42
  if (children.status === 'incomplete') {
@@ -179,6 +179,7 @@ export const processSample = async ({
179
179
  initialBoxes: [],
180
180
  options,
181
181
  continueMdat: false,
182
+ littleEndian: false,
182
183
  });
183
184
 
184
185
  if (children.status === 'incomplete') {
@@ -232,6 +233,7 @@ export const processSample = async ({
232
233
  initialBoxes: [],
233
234
  options,
234
235
  continueMdat: false,
236
+ littleEndian: false,
235
237
  });
236
238
 
237
239
  if (children.status === 'incomplete') {
@@ -288,6 +290,7 @@ export const processSample = async ({
288
290
  initialBoxes: [],
289
291
  options,
290
292
  continueMdat: false,
293
+ littleEndian: false,
291
294
  });
292
295
 
293
296
  if (children.status === 'incomplete') {
@@ -6,17 +6,19 @@ export interface StcoBox extends BaseBox {
6
6
  version: number;
7
7
  flags: number[];
8
8
  entryCount: number;
9
- entries: number[];
9
+ entries: (number | bigint)[];
10
10
  }
11
11
 
12
12
  export const parseStco = ({
13
13
  iterator,
14
14
  offset,
15
15
  size,
16
+ mode64Bit,
16
17
  }: {
17
18
  iterator: BufferIterator;
18
19
  offset: number;
19
20
  size: number;
21
+ mode64Bit: boolean;
20
22
  }): StcoBox => {
21
23
  const version = iterator.getUint8();
22
24
  if (version !== 0) {
@@ -26,14 +28,14 @@ export const parseStco = ({
26
28
  const flags = iterator.getSlice(3);
27
29
  const entryCount = iterator.getUint32();
28
30
 
29
- const entries: number[] = [];
31
+ const entries: (number | bigint)[] = [];
30
32
  for (let i = 0; i < entryCount; i++) {
31
33
  const bytesRemaining = size - (iterator.counter.getOffset() - offset);
32
34
  if (bytesRemaining < 4) {
33
35
  break;
34
36
  }
35
37
 
36
- entries.push(iterator.getUint32());
38
+ entries.push(mode64Bit ? iterator.getUint64() : iterator.getUint32());
37
39
  }
38
40
 
39
41
  iterator.discard(size - (iterator.counter.getOffset() - offset));
@@ -27,6 +27,7 @@ export const parseTrak = async ({
27
27
  initialBoxes: [],
28
28
  options,
29
29
  continueMdat: false,
30
+ littleEndian: false,
30
31
  });
31
32
 
32
33
  if (children.status === 'incomplete') {
@@ -0,0 +1,78 @@
1
+ // https://github.com/Vanilagy/webm-muxer/blob/main/src/ebml.ts#L101
2
+
3
+ export const measureEBMLVarInt = (value: number) => {
4
+ if (value < (1 << 7) - 1) {
5
+ /** Top bit is set, leaving 7 bits to hold the integer, but we can't store
6
+ * 127 because "all bits set to one" is a reserved value. Same thing for the
7
+ * other cases below:
8
+ */
9
+ return 1;
10
+ }
11
+
12
+ if (value < (1 << 14) - 1) {
13
+ return 2;
14
+ }
15
+
16
+ if (value < (1 << 21) - 1) {
17
+ return 3;
18
+ }
19
+
20
+ if (value < (1 << 28) - 1) {
21
+ return 4;
22
+ }
23
+
24
+ if (value < 2 ** 35 - 1) {
25
+ return 5;
26
+ }
27
+
28
+ if (value < 2 ** 42 - 1) {
29
+ return 6;
30
+ }
31
+
32
+ throw new Error('EBML VINT size not supported ' + value);
33
+ };
34
+
35
+ export const getVariableInt = (
36
+ value: number,
37
+ width: number = measureEBMLVarInt(value),
38
+ ) => {
39
+ switch (width) {
40
+ case 1:
41
+ return new Uint8Array([(1 << 7) | value]);
42
+ case 2:
43
+ return new Uint8Array([(1 << 6) | (value >> 8), value]);
44
+ case 3:
45
+ return new Uint8Array([(1 << 5) | (value >> 16), value >> 8, value]);
46
+ case 4:
47
+ return new Uint8Array([
48
+ (1 << 4) | (value >> 24),
49
+ value >> 16,
50
+ value >> 8,
51
+ value,
52
+ ]);
53
+ case 5:
54
+ /**
55
+ * JavaScript converts its doubles to 32-bit integers for bitwise
56
+ * operations, so we need to do a division by 2^32 instead of a
57
+ * right-shift of 32 to retain those top 3 bits
58
+ */
59
+ return new Uint8Array([
60
+ (1 << 3) | ((value / 2 ** 32) & 0x7),
61
+ value >> 24,
62
+ value >> 16,
63
+ value >> 8,
64
+ value,
65
+ ]);
66
+ case 6:
67
+ return new Uint8Array([
68
+ (1 << 2) | ((value / 2 ** 40) & 0x3),
69
+ (value / 2 ** 32) | 0,
70
+ value >> 24,
71
+ value >> 16,
72
+ value >> 8,
73
+ value,
74
+ ]);
75
+ default:
76
+ throw new Error('Bad EBML VINT size ' + width);
77
+ }
78
+ };