@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/src/buffer-iterator.ts
CHANGED
|
@@ -48,6 +48,7 @@ export class OffsetCounter {
|
|
|
48
48
|
|
|
49
49
|
const isoBaseMediaMp4Pattern = new TextEncoder().encode('ftyp');
|
|
50
50
|
const mpegPattern = new Uint8Array([0xff, 0xf3, 0xe4, 0x64]);
|
|
51
|
+
const riffPattern = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
|
|
51
52
|
|
|
52
53
|
const matchesPattern = (pattern: Uint8Array) => {
|
|
53
54
|
return (data: Uint8Array) => {
|
|
@@ -64,7 +65,9 @@ export const getArrayBufferIterator = (
|
|
|
64
65
|
maxBytes?: number,
|
|
65
66
|
) => {
|
|
66
67
|
const buf = new ArrayBuffer(initialData.byteLength, {
|
|
67
|
-
maxByteLength: maxBytes
|
|
68
|
+
maxByteLength: maxBytes
|
|
69
|
+
? Math.min(maxBytes as number, 2 ** 32)
|
|
70
|
+
: 1_000_000_000,
|
|
68
71
|
});
|
|
69
72
|
if (!buf.resize) {
|
|
70
73
|
throw new Error(
|
|
@@ -95,7 +98,61 @@ export const getArrayBufferIterator = (
|
|
|
95
98
|
return val;
|
|
96
99
|
};
|
|
97
100
|
|
|
98
|
-
const
|
|
101
|
+
const getEightByteNumber = (littleEndian = false) => {
|
|
102
|
+
if (littleEndian) {
|
|
103
|
+
const one = getUint8();
|
|
104
|
+
const two = getUint8();
|
|
105
|
+
const three = getUint8();
|
|
106
|
+
const four = getUint8();
|
|
107
|
+
const five = getUint8();
|
|
108
|
+
const six = getUint8();
|
|
109
|
+
const seven = getUint8();
|
|
110
|
+
const eight = getUint8();
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
(eight << 56) |
|
|
114
|
+
(seven << 48) |
|
|
115
|
+
(six << 40) |
|
|
116
|
+
(five << 32) |
|
|
117
|
+
(four << 24) |
|
|
118
|
+
(three << 16) |
|
|
119
|
+
(two << 8) |
|
|
120
|
+
one
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function byteArrayToBigInt(byteArray: number[]): BigInt {
|
|
125
|
+
let result = BigInt(0);
|
|
126
|
+
for (let i = 0; i < byteArray.length; i++) {
|
|
127
|
+
result = (result << BigInt(8)) + BigInt(byteArray[i]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const bigInt = byteArrayToBigInt([
|
|
134
|
+
getUint8(),
|
|
135
|
+
getUint8(),
|
|
136
|
+
getUint8(),
|
|
137
|
+
getUint8(),
|
|
138
|
+
getUint8(),
|
|
139
|
+
getUint8(),
|
|
140
|
+
getUint8(),
|
|
141
|
+
getUint8(),
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
return Number(bigInt);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const getFourByteNumber = (littleEndian = false) => {
|
|
148
|
+
if (littleEndian) {
|
|
149
|
+
const one = getUint8();
|
|
150
|
+
const two = getUint8();
|
|
151
|
+
const three = getUint8();
|
|
152
|
+
const four = getUint8();
|
|
153
|
+
return (four << 24) | (three << 16) | (two << 8) | one;
|
|
154
|
+
}
|
|
155
|
+
|
|
99
156
|
return (
|
|
100
157
|
(getUint8() << 24) | (getUint8() << 16) | (getUint8() << 8) | getUint8()
|
|
101
158
|
);
|
|
@@ -110,12 +167,18 @@ export const getArrayBufferIterator = (
|
|
|
110
167
|
return lastInt;
|
|
111
168
|
};
|
|
112
169
|
|
|
113
|
-
const getUint32 = () => {
|
|
114
|
-
const val = view.getUint32(counter.getDiscardedOffset());
|
|
170
|
+
const getUint32 = (littleEndian = false) => {
|
|
171
|
+
const val = view.getUint32(counter.getDiscardedOffset(), littleEndian);
|
|
115
172
|
counter.increment(4);
|
|
116
173
|
return val;
|
|
117
174
|
};
|
|
118
175
|
|
|
176
|
+
const getUint64 = (littleEndian = false) => {
|
|
177
|
+
const val = view.getBigUint64(counter.getDiscardedOffset(), littleEndian);
|
|
178
|
+
counter.increment(8);
|
|
179
|
+
return val;
|
|
180
|
+
};
|
|
181
|
+
|
|
119
182
|
const getUint32Le = () => {
|
|
120
183
|
const val = view.getUint32(counter.getDiscardedOffset(), true);
|
|
121
184
|
counter.increment(4);
|
|
@@ -156,6 +219,10 @@ export const getArrayBufferIterator = (
|
|
|
156
219
|
return matchesPattern(isoBaseMediaMp4Pattern)(data.subarray(4, 8));
|
|
157
220
|
};
|
|
158
221
|
|
|
222
|
+
const isRiff = () => {
|
|
223
|
+
return matchesPattern(riffPattern)(data.subarray(0, 4));
|
|
224
|
+
};
|
|
225
|
+
|
|
159
226
|
const isWebm = () => {
|
|
160
227
|
return matchesPattern(webmPattern)(data.subarray(0, 4));
|
|
161
228
|
};
|
|
@@ -186,7 +253,6 @@ export const getArrayBufferIterator = (
|
|
|
186
253
|
counter.decrement(counter.getOffset() - offset);
|
|
187
254
|
counter.setDiscardedOffset(offset);
|
|
188
255
|
} else {
|
|
189
|
-
buf.resize(offset);
|
|
190
256
|
const currentOffset = counter.getOffset();
|
|
191
257
|
counter.increment(offset - currentOffset);
|
|
192
258
|
removeBytesRead();
|
|
@@ -276,19 +342,25 @@ export const getArrayBufferIterator = (
|
|
|
276
342
|
bytesRemaining,
|
|
277
343
|
isIsoBaseMedia,
|
|
278
344
|
leb128,
|
|
279
|
-
|
|
345
|
+
removeBytesRead,
|
|
280
346
|
isWebm,
|
|
281
347
|
discard: (length: number) => {
|
|
282
348
|
counter.increment(length);
|
|
283
349
|
},
|
|
350
|
+
getEightByteNumber,
|
|
284
351
|
getFourByteNumber,
|
|
285
352
|
getSlice,
|
|
286
353
|
getAtom: () => {
|
|
287
354
|
const atom = getSlice(4);
|
|
288
355
|
return new TextDecoder().decode(atom);
|
|
289
356
|
},
|
|
357
|
+
isRiff,
|
|
290
358
|
getPaddedFourByteNumber,
|
|
291
|
-
getMatroskaSegmentId: () => {
|
|
359
|
+
getMatroskaSegmentId: (): string | null => {
|
|
360
|
+
if (bytesRemaining() === 0) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
292
364
|
const first = getSlice(1);
|
|
293
365
|
const firstOneString = `0x${Array.from(new Uint8Array(first))
|
|
294
366
|
.map((b) => {
|
|
@@ -303,6 +375,10 @@ export const getArrayBufferIterator = (
|
|
|
303
375
|
return firstOneString;
|
|
304
376
|
}
|
|
305
377
|
|
|
378
|
+
if (bytesRemaining() === 0) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
306
382
|
const firstTwo = getSlice(1);
|
|
307
383
|
|
|
308
384
|
const firstTwoString = `${firstOneString}${Array.from(
|
|
@@ -317,6 +393,10 @@ export const getArrayBufferIterator = (
|
|
|
317
393
|
return firstTwoString;
|
|
318
394
|
}
|
|
319
395
|
|
|
396
|
+
if (bytesRemaining() === 0) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
|
|
320
400
|
const firstThree = getSlice(1);
|
|
321
401
|
|
|
322
402
|
const firstThreeString = `${firstTwoString}${Array.from(
|
|
@@ -331,6 +411,10 @@ export const getArrayBufferIterator = (
|
|
|
331
411
|
return firstThreeString;
|
|
332
412
|
}
|
|
333
413
|
|
|
414
|
+
if (bytesRemaining() === 0) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
334
418
|
const segmentId = getSlice(1);
|
|
335
419
|
|
|
336
420
|
return `${firstThreeString}${Array.from(new Uint8Array(segmentId))
|
|
@@ -339,7 +423,11 @@ export const getArrayBufferIterator = (
|
|
|
339
423
|
})
|
|
340
424
|
.join('')}`;
|
|
341
425
|
},
|
|
342
|
-
getVint: () => {
|
|
426
|
+
getVint: (): number | null => {
|
|
427
|
+
if (bytesRemaining() === 0) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
|
|
343
431
|
const firstByte = getUint8();
|
|
344
432
|
const totalLength = firstByte;
|
|
345
433
|
|
|
@@ -353,6 +441,10 @@ export const getArrayBufferIterator = (
|
|
|
353
441
|
actualLength++;
|
|
354
442
|
}
|
|
355
443
|
|
|
444
|
+
if (bytesRemaining() < actualLength) {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
|
|
356
448
|
const slice = getSlice(actualLength);
|
|
357
449
|
const d = [firstByte, ...Array.from(new Uint8Array(slice))];
|
|
358
450
|
|
|
@@ -402,6 +494,7 @@ export const getArrayBufferIterator = (
|
|
|
402
494
|
return val;
|
|
403
495
|
},
|
|
404
496
|
getUint32,
|
|
497
|
+
getUint64,
|
|
405
498
|
// https://developer.apple.com/documentation/quicktime-file-format/sound_sample_description_version_1
|
|
406
499
|
// A 32-bit unsigned fixed-point number (16.16) that indicates the rate at which the sound samples were obtained.
|
|
407
500
|
getFixedPointUnsigned1616Number: () => {
|
|
@@ -420,7 +513,7 @@ export const getArrayBufferIterator = (
|
|
|
420
513
|
const val = getSlice(32);
|
|
421
514
|
return [...Array.from(new Uint8Array(val))];
|
|
422
515
|
},
|
|
423
|
-
|
|
516
|
+
getUint(length: number): number {
|
|
424
517
|
const bytes = getSlice(length);
|
|
425
518
|
const numbers = [...Array.from(new Uint8Array(bytes))];
|
|
426
519
|
return numbers.reduce(
|
package/src/from-fetch.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type {ReaderInterface} from './reader';
|
|
2
2
|
|
|
3
3
|
export const fetchReader: ReaderInterface = {
|
|
4
|
-
read: async (src, range) => {
|
|
4
|
+
read: async (src, range, signal) => {
|
|
5
5
|
if (typeof src !== 'string') {
|
|
6
|
-
throw new Error('src must be a string when using `
|
|
6
|
+
throw new Error('src must be a string when using `fetchReader`');
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const resolvedUrl =
|
|
@@ -34,9 +34,18 @@ export const fetchReader: ReaderInterface = {
|
|
|
34
34
|
: {
|
|
35
35
|
Range: `bytes=${`${range[0]}-${range[1]}`}`,
|
|
36
36
|
},
|
|
37
|
+
signal,
|
|
37
38
|
// Disable Next.js caching
|
|
38
39
|
cache: 'no-store',
|
|
39
40
|
});
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
res.status.toString().startsWith('4') ||
|
|
44
|
+
res.status.toString().startsWith('5')
|
|
45
|
+
) {
|
|
46
|
+
throw new Error(`Server returned status code ${res.status} for ${src}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
if (!res.body) {
|
|
41
50
|
throw new Error('No body');
|
|
42
51
|
}
|
|
@@ -47,11 +56,21 @@ export const fetchReader: ReaderInterface = {
|
|
|
47
56
|
|
|
48
57
|
const reader = res.body.getReader();
|
|
49
58
|
|
|
59
|
+
if (signal) {
|
|
60
|
+
signal.addEventListener(
|
|
61
|
+
'abort',
|
|
62
|
+
() => {
|
|
63
|
+
reader.cancel();
|
|
64
|
+
},
|
|
65
|
+
{once: true},
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
return {reader, contentLength};
|
|
51
70
|
},
|
|
52
71
|
getLength: async (src) => {
|
|
53
72
|
if (typeof src !== 'string') {
|
|
54
|
-
throw new Error('src must be a string when using `
|
|
73
|
+
throw new Error('src must be a string when using `fetchReader`');
|
|
55
74
|
}
|
|
56
75
|
|
|
57
76
|
const res = await fetch(src, {
|
package/src/from-node.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {Readable} from 'stream';
|
|
|
4
4
|
import type {ReaderInterface} from './reader';
|
|
5
5
|
|
|
6
6
|
export const nodeReader: ReaderInterface = {
|
|
7
|
-
read: async (src, range) => {
|
|
7
|
+
read: async (src, range, signal) => {
|
|
8
8
|
if (typeof src !== 'string') {
|
|
9
9
|
throw new Error('src must be a string when using `nodeReader`');
|
|
10
10
|
}
|
|
@@ -17,12 +17,26 @@ export const nodeReader: ReaderInterface = {
|
|
|
17
17
|
: typeof range === 'number'
|
|
18
18
|
? Infinity
|
|
19
19
|
: range[1],
|
|
20
|
+
signal,
|
|
20
21
|
});
|
|
21
22
|
const stats = await stat(src);
|
|
23
|
+
|
|
24
|
+
const reader = Readable.toWeb(
|
|
25
|
+
stream,
|
|
26
|
+
).getReader() as ReadableStreamDefaultReader<Uint8Array>;
|
|
27
|
+
|
|
28
|
+
if (signal) {
|
|
29
|
+
signal.addEventListener(
|
|
30
|
+
'abort',
|
|
31
|
+
() => {
|
|
32
|
+
reader.cancel();
|
|
33
|
+
},
|
|
34
|
+
{once: true},
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
return {
|
|
23
|
-
reader
|
|
24
|
-
stream,
|
|
25
|
-
).getReader() as ReadableStreamDefaultReader<Uint8Array>,
|
|
39
|
+
reader,
|
|
26
40
|
contentLength: stats.size,
|
|
27
41
|
};
|
|
28
42
|
},
|
package/src/from-web-file.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {ReaderInterface} from './reader';
|
|
2
2
|
|
|
3
3
|
export const webFileReader: ReaderInterface = {
|
|
4
|
-
read: (file, range) => {
|
|
4
|
+
read: (file, range, signal) => {
|
|
5
5
|
if (typeof file === 'string') {
|
|
6
6
|
throw new Error('`inputTypeFileReader` only supports `File` objects');
|
|
7
7
|
}
|
|
@@ -16,6 +16,16 @@ export const webFileReader: ReaderInterface = {
|
|
|
16
16
|
const reader = new FileReader();
|
|
17
17
|
reader.readAsArrayBuffer(file);
|
|
18
18
|
|
|
19
|
+
if (signal) {
|
|
20
|
+
signal.addEventListener(
|
|
21
|
+
'abort',
|
|
22
|
+
() => {
|
|
23
|
+
reader.abort();
|
|
24
|
+
},
|
|
25
|
+
{once: true},
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
return new Promise((resolve, reject) => {
|
|
20
30
|
reader.onload = () => {
|
|
21
31
|
resolve({
|
package/src/has-all-info.ts
CHANGED
package/src/options.ts
CHANGED
|
@@ -43,7 +43,7 @@ export type Options<
|
|
|
43
43
|
audioCodec?: EnableAudioCodec;
|
|
44
44
|
tracks?: EnableTracks;
|
|
45
45
|
rotation?: EnableRotation;
|
|
46
|
-
|
|
46
|
+
unrotatedDimensions?: EnableUnrotatedDimensions;
|
|
47
47
|
internalStats?: EnableInternalStats;
|
|
48
48
|
};
|
|
49
49
|
|
|
@@ -69,7 +69,7 @@ export type Metadata<
|
|
|
69
69
|
: {}) &
|
|
70
70
|
(EnableRotation extends true ? {rotation: number | null} : {}) &
|
|
71
71
|
(EnableUnrotatedDimensions extends true
|
|
72
|
-
? {
|
|
72
|
+
? {unrotatedDimensions: Dimensions}
|
|
73
73
|
: {}) &
|
|
74
74
|
(EnableInternalStats extends true ? {internalStats: InternalStats} : {});
|
|
75
75
|
|
|
@@ -101,6 +101,7 @@ export type ParseMedia = <
|
|
|
101
101
|
reader?: ReaderInterface;
|
|
102
102
|
onAudioTrack?: OnAudioTrack;
|
|
103
103
|
onVideoTrack?: OnVideoTrack;
|
|
104
|
+
signal?: AbortSignal;
|
|
104
105
|
}) => Promise<
|
|
105
106
|
Metadata<
|
|
106
107
|
EnableDimensions,
|
package/src/parse-media.ts
CHANGED
|
@@ -21,8 +21,14 @@ export const parseMedia: ParseMedia = async ({
|
|
|
21
21
|
reader: readerInterface = fetchReader,
|
|
22
22
|
onAudioTrack,
|
|
23
23
|
onVideoTrack,
|
|
24
|
+
signal,
|
|
24
25
|
}) => {
|
|
25
|
-
const
|
|
26
|
+
const state = makeParserState({
|
|
27
|
+
hasAudioCallbacks: onAudioTrack !== null,
|
|
28
|
+
hasVideoCallbacks: onVideoTrack !== null,
|
|
29
|
+
signal,
|
|
30
|
+
});
|
|
31
|
+
const {reader, contentLength} = await readerInterface.read(src, null, signal);
|
|
26
32
|
let currentReader = reader;
|
|
27
33
|
|
|
28
34
|
const returnValue = {} as Metadata<
|
|
@@ -41,11 +47,6 @@ export const parseMedia: ParseMedia = async ({
|
|
|
41
47
|
let iterator: BufferIterator | null = null;
|
|
42
48
|
let parseResult: ParseResult | null = null;
|
|
43
49
|
|
|
44
|
-
const state = makeParserState({
|
|
45
|
-
hasAudioCallbacks: onAudioTrack !== null,
|
|
46
|
-
hasVideoCallbacks: onVideoTrack !== null,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
50
|
const options: ParserContext = {
|
|
50
51
|
canSkipVideoData: !(onAudioTrack || onVideoTrack),
|
|
51
52
|
onAudioTrack: onAudioTrack ?? null,
|
|
@@ -54,6 +55,10 @@ export const parseMedia: ParseMedia = async ({
|
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
while (parseResult === null || parseResult.status === 'incomplete') {
|
|
58
|
+
if (signal?.aborted) {
|
|
59
|
+
throw new Error('Aborted');
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
const result = await currentReader.read();
|
|
58
63
|
|
|
59
64
|
if (iterator) {
|
|
@@ -106,6 +111,7 @@ export const parseMedia: ParseMedia = async ({
|
|
|
106
111
|
const {reader: newReader} = await readerInterface.read(
|
|
107
112
|
src,
|
|
108
113
|
parseResult.skipTo,
|
|
114
|
+
signal,
|
|
109
115
|
);
|
|
110
116
|
currentReader = newReader;
|
|
111
117
|
iterator.skipTo(parseResult.skipTo);
|
|
@@ -124,9 +130,9 @@ export const parseMedia: ParseMedia = async ({
|
|
|
124
130
|
};
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
if (fields?.
|
|
133
|
+
if (fields?.unrotatedDimensions) {
|
|
128
134
|
const dimensions = getDimensions(parseResult.segments, state);
|
|
129
|
-
returnValue.
|
|
135
|
+
returnValue.unrotatedDimensions = {
|
|
130
136
|
width: dimensions.unrotatedWidth,
|
|
131
137
|
height: dimensions.unrotatedHeight,
|
|
132
138
|
};
|
package/src/parse-video.ts
CHANGED
|
@@ -43,6 +43,22 @@ export const parseVideo = ({
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
if (iterator.isRiff()) {
|
|
47
|
+
throw new Error('AVI files are not yet supported');
|
|
48
|
+
/*
|
|
49
|
+
iterator.discard(4);
|
|
50
|
+
return parseBoxes({
|
|
51
|
+
iterator,
|
|
52
|
+
maxBytes: Infinity,
|
|
53
|
+
allowIncompleteBoxes: true,
|
|
54
|
+
initialBoxes: [],
|
|
55
|
+
options,
|
|
56
|
+
continueMdat: false,
|
|
57
|
+
littleEndian: true,
|
|
58
|
+
});
|
|
59
|
+
*/
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
if (iterator.isIsoBaseMedia()) {
|
|
47
63
|
return parseBoxes({
|
|
48
64
|
iterator,
|
|
@@ -51,6 +67,7 @@ export const parseVideo = ({
|
|
|
51
67
|
initialBoxes: [],
|
|
52
68
|
options,
|
|
53
69
|
continueMdat: false,
|
|
70
|
+
littleEndian: false,
|
|
54
71
|
});
|
|
55
72
|
}
|
|
56
73
|
|
package/src/parser-state.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {OnTrackEntrySegment} from './boxes/webm/segments';
|
|
2
|
-
import type {
|
|
3
|
-
import {getTrackCodec} from './boxes/webm/traversal';
|
|
2
|
+
import type {TrackInfo} from './boxes/webm/segments/track-entry';
|
|
3
|
+
import {getTrackCodec, getTrackTimestampScale} from './boxes/webm/traversal';
|
|
4
4
|
import {getTrackId} from './traversal';
|
|
5
5
|
import type {
|
|
6
6
|
AudioSample,
|
|
@@ -14,11 +14,13 @@ export type InternalStats = {};
|
|
|
14
14
|
export const makeParserState = ({
|
|
15
15
|
hasAudioCallbacks,
|
|
16
16
|
hasVideoCallbacks,
|
|
17
|
+
signal,
|
|
17
18
|
}: {
|
|
18
19
|
hasAudioCallbacks: boolean;
|
|
19
20
|
hasVideoCallbacks: boolean;
|
|
21
|
+
signal: AbortSignal | undefined;
|
|
20
22
|
}) => {
|
|
21
|
-
const trackEntries: Record<number,
|
|
23
|
+
const trackEntries: Record<number, TrackInfo> = {};
|
|
22
24
|
|
|
23
25
|
const onTrackEntrySegment: OnTrackEntrySegment = (trackEntry) => {
|
|
24
26
|
const trackId = getTrackId(trackEntry);
|
|
@@ -35,7 +37,12 @@ export const makeParserState = ({
|
|
|
35
37
|
throw new Error('Expected codec');
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
const trackTimescale = getTrackTimestampScale(trackEntry);
|
|
41
|
+
|
|
42
|
+
trackEntries[trackId] = {
|
|
43
|
+
codec: codec.codec,
|
|
44
|
+
trackTimescale,
|
|
45
|
+
};
|
|
39
46
|
};
|
|
40
47
|
|
|
41
48
|
const videoSampleCallbacks: Record<number, OnVideoSample> = {};
|
|
@@ -49,8 +56,10 @@ export const makeParserState = ({
|
|
|
49
56
|
let timescale: number | null = null;
|
|
50
57
|
|
|
51
58
|
const getTimescale = () => {
|
|
59
|
+
// https://www.matroska.org/technical/notes.html
|
|
60
|
+
// When using the default value of TimestampScale of “1,000,000”, one Segment Tick represents one millisecond.
|
|
52
61
|
if (timescale === null) {
|
|
53
|
-
|
|
62
|
+
return 1_000_000;
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
return timescale;
|
|
@@ -125,6 +134,10 @@ export const makeParserState = ({
|
|
|
125
134
|
queuedAudioSamples[id] = [];
|
|
126
135
|
},
|
|
127
136
|
onAudioSample: async (trackId: number, audioSample: AudioSample) => {
|
|
137
|
+
if (signal?.aborted) {
|
|
138
|
+
throw new Error('Aborted');
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
const callback = audioSampleCallbacks[trackId];
|
|
129
142
|
if (callback) {
|
|
130
143
|
await callback(audioSample);
|
|
@@ -139,6 +152,10 @@ export const makeParserState = ({
|
|
|
139
152
|
}
|
|
140
153
|
},
|
|
141
154
|
onVideoSample: async (trackId: number, videoSample: VideoSample) => {
|
|
155
|
+
if (signal?.aborted) {
|
|
156
|
+
throw new Error('Aborted');
|
|
157
|
+
}
|
|
158
|
+
|
|
142
159
|
const callback = videoSampleCallbacks[trackId];
|
|
143
160
|
if (callback) {
|
|
144
161
|
await callback(videoSample);
|
package/src/reader.ts
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
import {RenderInternals} from '@remotion/renderer';
|
|
2
2
|
import {expect, test} from 'bun:test';
|
|
3
3
|
import {makeMatroskaHeader} from '../boxes/webm/make-header';
|
|
4
|
+
import {
|
|
5
|
+
matroskaHeader,
|
|
6
|
+
seek,
|
|
7
|
+
seekId,
|
|
8
|
+
} from '../boxes/webm/segments/all-segments';
|
|
4
9
|
|
|
5
10
|
test('Should make Matroska header that is same as input', async () => {
|
|
6
11
|
const exampleVideo = RenderInternals.exampleVideos.matroskaMp3;
|
|
7
12
|
const file = await Bun.file(exampleVideo).arrayBuffer();
|
|
8
13
|
|
|
9
|
-
const headerInput = new Uint8Array(file.slice(0,
|
|
14
|
+
const headerInput = new Uint8Array(file.slice(0, 0x28));
|
|
10
15
|
|
|
11
|
-
const headerOutput = makeMatroskaHeader(
|
|
16
|
+
const headerOutput = makeMatroskaHeader(matroskaHeader, {
|
|
17
|
+
DocType: 'matroska',
|
|
18
|
+
DocTypeVersion: 4,
|
|
19
|
+
DocTypeReadVersion: 2,
|
|
20
|
+
EBMLMaxIDLength: 4,
|
|
21
|
+
EBMLMaxSizeLength: 8,
|
|
22
|
+
EBMLReadVersion: 1,
|
|
23
|
+
EBMLVersion: 1,
|
|
24
|
+
});
|
|
12
25
|
|
|
13
26
|
expect(headerInput).toEqual(headerOutput);
|
|
14
27
|
});
|
|
28
|
+
|
|
29
|
+
test('Should be able to create SeekIdBox', async () => {
|
|
30
|
+
const file = new Uint8Array(
|
|
31
|
+
await Bun.file('vp8-segments/56-0x53ab').arrayBuffer(),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const custom = makeMatroskaHeader(seekId, '0x1549a966');
|
|
35
|
+
expect(custom).toEqual(file);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('Should be able to create Seek', async () => {
|
|
39
|
+
const file = new Uint8Array(
|
|
40
|
+
await Bun.file('vp8-segments/53-0x4dbb').arrayBuffer(),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const custom = makeMatroskaHeader(seek, {
|
|
44
|
+
SeekID: '0x1549a966',
|
|
45
|
+
SeekPosition: 0x40,
|
|
46
|
+
});
|
|
47
|
+
expect(custom).toEqual(file);
|
|
48
|
+
});
|