@remotion/media-parser 4.0.200 → 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.
- package/dist/av1-codec-string.d.ts +5 -0
- package/dist/av1-codec-string.js +18 -1
- package/dist/bitstream/av1.d.ts +2 -0
- package/dist/bitstream/av1.js +12 -0
- package/dist/boxes/iso-base-media/avcc-hvcc.d.ts +20 -0
- package/dist/boxes/iso-base-media/avcc-hvcc.js +73 -0
- package/dist/boxes/iso-base-media/avcc.d.ts +18 -0
- package/dist/boxes/iso-base-media/avcc.js +27 -0
- package/dist/boxes/iso-base-media/esds-descriptors.d.ts +21 -0
- package/dist/boxes/iso-base-media/esds-descriptors.js +62 -0
- package/dist/boxes/iso-base-media/esds.d.ts +15 -0
- package/dist/boxes/iso-base-media/esds.js +27 -0
- package/dist/boxes/iso-base-media/mdat/mdat.js +2 -1
- package/dist/boxes/iso-base-media/moov/moov.js +1 -0
- package/dist/boxes/iso-base-media/process-box.d.ts +4 -2
- package/dist/boxes/iso-base-media/process-box.js +56 -40
- package/dist/boxes/iso-base-media/stsd/mebx.d.ts +2 -1
- package/dist/boxes/iso-base-media/stsd/mebx.js +2 -1
- package/dist/boxes/iso-base-media/stsd/samples.js +3 -0
- package/dist/boxes/iso-base-media/stsd/stco.d.ts +3 -2
- package/dist/boxes/iso-base-media/stsd/stco.js +2 -2
- package/dist/boxes/iso-base-media/trak/trak.js +1 -0
- package/dist/boxes/webm/bitstream/av1.js +10 -1
- package/dist/boxes/webm/ebml.d.ts +1 -1
- package/dist/boxes/webm/make-header.d.ts +8 -1
- package/dist/boxes/webm/make-header.js +65 -30
- package/dist/boxes/webm/parse-ebml.d.ts +7 -0
- package/dist/boxes/webm/parse-ebml.js +66 -0
- package/dist/boxes/webm/parse-webm-header.js +8 -9
- package/dist/boxes/webm/segments/all-segments.d.ts +258 -1
- package/dist/boxes/webm/segments/all-segments.js +126 -2
- package/dist/boxes/webm/segments/seek-position.js +1 -1
- package/dist/boxes/webm/segments/seek.d.ts +1 -1
- package/dist/boxes/webm/segments/seek.js +8 -2
- package/dist/boxes/webm/segments/timestamp-scale.js +1 -1
- package/dist/boxes/webm/segments/track-entry.d.ts +5 -1
- package/dist/boxes/webm/segments/track-entry.js +19 -20
- package/dist/boxes/webm/segments.d.ts +2 -2
- package/dist/boxes/webm/segments.js +30 -25
- package/dist/boxes/webm/traversal.d.ts +1 -0
- package/dist/boxes/webm/traversal.js +12 -1
- package/dist/buffer-iterator.d.ts +9 -6
- package/dist/buffer-iterator.js +83 -7
- package/dist/from-fetch.js +13 -3
- package/dist/from-input-type-file.d.ts +2 -0
- package/dist/from-input-type-file.js +37 -0
- package/dist/from-node.js +9 -2
- package/dist/from-web-file.js +6 -1
- package/dist/from-web.js +15 -6
- package/dist/get-audio-codec.d.ts +1 -1
- package/dist/get-codec.d.ts +4 -0
- package/dist/get-codec.js +22 -0
- package/dist/get-sample-positions.js +1 -1
- package/dist/has-all-info.js +1 -1
- package/dist/options.d.ts +3 -2
- package/dist/parse-media.js +13 -9
- package/dist/parse-video.js +16 -0
- package/dist/parser-state.d.ts +4 -3
- package/dist/parser-state.js +15 -3
- package/dist/reader.d.ts +1 -1
- package/dist/web-file.d.ts +2 -0
- package/dist/web-file.js +37 -0
- package/package.json +2 -2
- package/src/boxes/iso-base-media/mdat/mdat.ts +2 -1
- package/src/boxes/iso-base-media/moov/moov.ts +1 -0
- package/src/boxes/iso-base-media/process-box.ts +70 -40
- package/src/boxes/iso-base-media/stsd/mebx.ts +3 -0
- package/src/boxes/iso-base-media/stsd/samples.ts +3 -0
- package/src/boxes/iso-base-media/stsd/stco.ts +5 -3
- package/src/boxes/iso-base-media/trak/trak.ts +1 -0
- package/src/boxes/webm/make-header.ts +122 -32
- package/src/boxes/webm/parse-ebml.ts +93 -0
- package/src/boxes/webm/parse-webm-header.ts +8 -12
- package/src/boxes/webm/segments/all-segments.ts +222 -1
- package/src/boxes/webm/segments/seek-position.ts +1 -1
- package/src/boxes/webm/segments/seek.ts +12 -2
- package/src/boxes/webm/segments/timestamp-scale.ts +1 -1
- package/src/boxes/webm/segments/track-entry.ts +31 -26
- package/src/boxes/webm/segments.ts +37 -32
- package/src/boxes/webm/traversal.ts +13 -0
- package/src/buffer-iterator.ts +102 -9
- package/src/from-fetch.ts +22 -3
- package/src/from-node.ts +18 -4
- package/src/from-web-file.ts +11 -1
- package/src/get-sample-positions.ts +1 -1
- package/src/has-all-info.ts +1 -1
- package/src/options.ts +3 -2
- package/src/parse-media.ts +14 -8
- package/src/parse-video.ts +17 -0
- package/src/parser-state.ts +22 -5
- package/src/reader.ts +1 -0
- package/src/test/create-matroska.test.ts +36 -2
- package/src/test/matroska.test.ts +69 -27
- package/src/test/parse-stco.test.ts +2 -0
- package/src/test/stream-local.test.ts +23 -9
- package/src/test/stream-remote.test.ts +23 -19
- package/src/test/stsd.test.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/boxes/iso-base-media/ftype.d.ts +0 -9
- package/dist/boxes/iso-base-media/ftype.js +0 -31
- package/dist/get-video-metadata.d.ts +0 -2
- package/dist/get-video-metadata.js +0 -44
- package/dist/read-and-increment-offset.d.ts +0 -28
- package/dist/read-and-increment-offset.js +0 -177
- package/dist/understand-vorbis.d.ts +0 -1
- package/dist/understand-vorbis.js +0 -12
- package/src/boxes/webm/segments/unknown.ts +0 -19
- /package/dist/{boxes/webm/bitstream/av1/frame.d.ts → get-samples.d.ts} +0 -0
- /package/dist/{boxes/webm/bitstream/av1/frame.js → get-samples.js} +0 -0
- /package/dist/{boxes/webm/bitstream/h264/get-h264-descriptor.d.ts → sample-aspect-ratio.d.ts} +0 -0
- /package/dist/{boxes/webm/bitstream/h264/get-h264-descriptor.js → sample-aspect-ratio.js} +0 -0
package/dist/web-file.js
ADDED
|
@@ -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
|
+
};
|
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.
|
|
6
|
+
"version": "4.0.201",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@remotion/renderer": "4.0.
|
|
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 -
|
|
29
|
+
data.discard(size - (data.counter.getOffset() - fileOffset));
|
|
30
30
|
return Promise.resolve({
|
|
31
31
|
type: 'mdat-box',
|
|
32
32
|
boxSize: size,
|
|
@@ -116,6 +116,7 @@ export const parseMdat = async ({
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
const remaining = size - (data.counter.getOffset() - fileOffset);
|
|
119
|
+
data.removeBytesRead();
|
|
119
120
|
if (remaining === 0) {
|
|
120
121
|
break;
|
|
121
122
|
}
|
|
@@ -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
|
|
134
|
-
|
|
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 (
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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.
|
|
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));
|
|
@@ -1,48 +1,138 @@
|
|
|
1
1
|
import {getVariableInt} from './ebml';
|
|
2
|
-
import {
|
|
2
|
+
import type {
|
|
3
|
+
Ebml,
|
|
4
|
+
EbmlWithChildren,
|
|
5
|
+
EbmlWithHexString,
|
|
6
|
+
EbmlWithString,
|
|
7
|
+
EbmlWithUint8,
|
|
8
|
+
EmblTypes,
|
|
9
|
+
HeaderStructure,
|
|
10
|
+
matroskaElements,
|
|
11
|
+
} from './segments/all-segments';
|
|
12
|
+
import {getIdForName} from './segments/all-segments';
|
|
3
13
|
|
|
4
14
|
export const webmPattern = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]);
|
|
5
15
|
|
|
6
16
|
const matroskaToHex = (
|
|
7
17
|
matrId: (typeof matroskaElements)[keyof typeof matroskaElements],
|
|
8
18
|
) => {
|
|
9
|
-
const numbers:
|
|
19
|
+
const numbers: Uint8Array = new Uint8Array((matrId.length - 2) / 2);
|
|
20
|
+
|
|
10
21
|
for (let i = 2; i < matrId.length; i += 2) {
|
|
11
|
-
const hex = matrId.
|
|
12
|
-
numbers
|
|
22
|
+
const hex = matrId.substring(i, i + 2);
|
|
23
|
+
numbers[(i - 2) / 2] = parseInt(hex, 16);
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
return numbers;
|
|
16
27
|
};
|
|
17
28
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
type Numbers = '0' | '1' | '2' | '3' | '4' | '5' | '6';
|
|
30
|
+
|
|
31
|
+
type ChildFields<Struct extends HeaderStructure> = {
|
|
32
|
+
[key in keyof Struct &
|
|
33
|
+
Numbers as Struct[key]['name']]: EmblTypes[Struct[key]['type']];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type SerializeValue<Struct extends Ebml> = Struct extends EbmlWithChildren
|
|
37
|
+
? ChildFields<Struct['children']>
|
|
38
|
+
: Struct extends EbmlWithString
|
|
39
|
+
? string
|
|
40
|
+
: Struct extends EbmlWithUint8
|
|
41
|
+
? number
|
|
42
|
+
: Struct extends EbmlWithHexString
|
|
43
|
+
? string
|
|
44
|
+
: undefined;
|
|
45
|
+
|
|
46
|
+
function putUintDynamic(number: number) {
|
|
47
|
+
if (number < 0) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'This function is designed for non-negative integers only.',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Calculate the minimum number of bytes needed to store the integer
|
|
54
|
+
const length = Math.ceil(Math.log2(number + 1) / 8);
|
|
55
|
+
const bytes = new Uint8Array(length);
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < length; i++) {
|
|
58
|
+
// Extract each byte from the number
|
|
59
|
+
bytes[length - 1 - i] = (number >> (8 * i)) & 0xff;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return bytes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const makeFromHeaderStructure = <Struct extends Ebml>(
|
|
66
|
+
struct: Struct,
|
|
67
|
+
fields: SerializeValue<Struct>,
|
|
68
|
+
): Uint8Array => {
|
|
69
|
+
const arrays: Uint8Array[] = [];
|
|
70
|
+
|
|
71
|
+
if (struct.type === 'children') {
|
|
72
|
+
for (const item of struct.children) {
|
|
73
|
+
arrays.push(
|
|
74
|
+
makeMatroskaHeader(
|
|
75
|
+
item,
|
|
76
|
+
// @ts-expect-error
|
|
77
|
+
fields[item.name],
|
|
78
|
+
),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return combineUint8Arrays(arrays);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (struct.type === 'string') {
|
|
86
|
+
return new TextEncoder().encode(fields as string);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (struct.type === 'uint') {
|
|
90
|
+
return putUintDynamic(fields as number);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (struct.type === 'hex-string') {
|
|
94
|
+
const hex = (fields as string).substring(2);
|
|
95
|
+
const arr = new Uint8Array(hex.length / 2);
|
|
96
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
97
|
+
const byte = parseInt(hex.substring(i, i + 2), 16);
|
|
98
|
+
arr[i / 2] = byte;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return arr;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (struct.type === 'void') {
|
|
105
|
+
throw new Error('Serializing Void is not implemented');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
throw new Error('Unexpected type');
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const makeMatroskaHeader = <Struct extends Ebml>(
|
|
112
|
+
struct: Struct,
|
|
113
|
+
fields: SerializeValue<Struct>,
|
|
114
|
+
) => {
|
|
115
|
+
const value = makeFromHeaderStructure(struct, fields);
|
|
116
|
+
|
|
117
|
+
return combineUint8Arrays([
|
|
118
|
+
matroskaToHex(getIdForName(struct.name)),
|
|
119
|
+
getVariableInt(value.length),
|
|
120
|
+
value,
|
|
45
121
|
]);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const combineUint8Arrays = (arrays: Uint8Array[]) => {
|
|
125
|
+
let totalLength = 0;
|
|
126
|
+
for (const array of arrays) {
|
|
127
|
+
totalLength += array.length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const result = new Uint8Array(totalLength);
|
|
131
|
+
let offset = 0;
|
|
132
|
+
for (const array of arrays) {
|
|
133
|
+
result.set(array, offset);
|
|
134
|
+
offset += array.length;
|
|
135
|
+
}
|
|
46
136
|
|
|
47
|
-
return
|
|
137
|
+
return result;
|
|
48
138
|
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type {BufferIterator} from '../../buffer-iterator';
|
|
2
|
+
import type {PossibleEbml} from './segments/all-segments';
|
|
3
|
+
import {ebmlMap, type Ebml, type EbmlParsed} from './segments/all-segments';
|
|
4
|
+
|
|
5
|
+
type Prettify<T> = {
|
|
6
|
+
[K in keyof T]: T[K];
|
|
7
|
+
} & {};
|
|
8
|
+
|
|
9
|
+
export const parseEbml = (iterator: BufferIterator): Prettify<PossibleEbml> => {
|
|
10
|
+
const hex = iterator.getMatroskaSegmentId();
|
|
11
|
+
if (hex === null) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'Not enough bytes left to parse EBML - this should not happen',
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const hasInMap = ebmlMap[hex as keyof typeof ebmlMap];
|
|
18
|
+
if (!hasInMap) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Don't know how to parse EBML hex ID ${JSON.stringify(hex)}`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const size = iterator.getVint();
|
|
25
|
+
if (size === null) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
'Not enough bytes left to parse EBML - this should not happen',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (hasInMap.type === 'uint') {
|
|
32
|
+
const value = iterator.getUint(size);
|
|
33
|
+
|
|
34
|
+
return {type: hasInMap.name, value, hex};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (hasInMap.type === 'string') {
|
|
38
|
+
const value = iterator.getByteString(size);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
type: hasInMap.name,
|
|
42
|
+
value,
|
|
43
|
+
hex,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (hasInMap.type === 'float') {
|
|
48
|
+
const value = size === 4 ? iterator.getFloat32() : iterator.getFloat64();
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
type: hasInMap.name,
|
|
52
|
+
value,
|
|
53
|
+
hex,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (hasInMap.type === 'void') {
|
|
58
|
+
iterator.discard(size);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
type: hasInMap.name,
|
|
62
|
+
value: undefined,
|
|
63
|
+
hex,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (hasInMap.type === 'children') {
|
|
68
|
+
const children: EbmlParsed<Ebml>[] = [];
|
|
69
|
+
const startOffset = iterator.counter.getOffset();
|
|
70
|
+
|
|
71
|
+
// eslint-disable-next-line no-constant-condition
|
|
72
|
+
while (true) {
|
|
73
|
+
const value = parseEbml(iterator);
|
|
74
|
+
children.push(value);
|
|
75
|
+
const offsetNow = iterator.counter.getOffset();
|
|
76
|
+
|
|
77
|
+
if (offsetNow - startOffset > size) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Offset ${offsetNow - startOffset} is larger than the length of the hex ${size}`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (offsetNow - startOffset === size) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {type: hasInMap.name, value: children as EbmlParsed<Ebml>[], hex};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// @ts-expect-error
|
|
92
|
+
throw new Error(`Unknown segment type ${hasInMap.type}`);
|
|
93
|
+
};
|