@remotion/media-parser 4.0.207 → 4.0.209
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 +3 -0
- package/dist/av1-codec-string.js +91 -0
- package/dist/boxes/iso-base-media/ftype.d.ts +9 -0
- package/dist/boxes/iso-base-media/ftype.js +31 -0
- package/dist/boxes/iso-base-media/stsd/avcc-hvcc.d.ts +20 -0
- package/dist/boxes/iso-base-media/stsd/avcc-hvcc.js +73 -0
- package/dist/boxes/iso-base-media/stts/stts.d.ts +15 -0
- package/dist/boxes/iso-base-media/stts/stts.js +35 -0
- package/dist/boxes/webm/bitstream/av1/bitstream-frame-header.d.ts +14 -0
- package/dist/boxes/webm/bitstream/av1/bitstream-frame-header.js +67 -0
- package/dist/boxes/webm/bitstream/av1/bitstream-frame.d.ts +11 -0
- package/dist/boxes/webm/bitstream/av1/bitstream-frame.js +14 -0
- package/dist/boxes/webm/bitstream/av1/chroma-sample-position.d.ts +6 -0
- package/dist/boxes/webm/bitstream/av1/chroma-sample-position.js +9 -0
- package/dist/boxes/webm/bitstream/av1/color-config.d.ts +16 -0
- package/dist/boxes/webm/bitstream/av1/color-config.js +103 -0
- package/dist/boxes/webm/bitstream/av1/color-primaries.d.ts +14 -0
- package/dist/boxes/webm/bitstream/av1/color-primaries.js +17 -0
- package/dist/boxes/webm/bitstream/av1/decoder-model-info.d.ts +9 -0
- package/dist/boxes/webm/bitstream/av1/decoder-model-info.js +17 -0
- package/dist/boxes/webm/bitstream/av1/frame.d.ts +0 -0
- package/dist/boxes/webm/bitstream/av1/frame.js +1 -0
- package/dist/boxes/webm/bitstream/av1/header-segment.d.ts +51 -0
- package/dist/boxes/webm/bitstream/av1/header-segment.js +183 -0
- package/dist/boxes/webm/bitstream/av1/matrix-coefficients.d.ts +17 -0
- package/dist/boxes/webm/bitstream/av1/matrix-coefficients.js +20 -0
- package/dist/boxes/webm/bitstream/av1/operating-parameters-info.d.ts +10 -0
- package/dist/boxes/webm/bitstream/av1/operating-parameters-info.js +15 -0
- package/dist/boxes/webm/bitstream/av1/temporal-point-info.d.ts +5 -0
- package/dist/boxes/webm/bitstream/av1/temporal-point-info.js +8 -0
- package/dist/boxes/webm/bitstream/av1/timing-info.d.ts +8 -0
- package/dist/boxes/webm/bitstream/av1/timing-info.js +20 -0
- package/dist/boxes/webm/bitstream/av1/transfer-characteristics.d.ts +21 -0
- package/dist/boxes/webm/bitstream/av1/transfer-characteristics.js +24 -0
- package/dist/boxes/webm/bitstream/av1/uvlc.d.ts +2 -0
- package/dist/boxes/webm/bitstream/av1/uvlc.js +20 -0
- package/dist/boxes/webm/bitstream/av1.d.ts +20 -0
- package/dist/boxes/webm/bitstream/av1.js +118 -0
- package/dist/boxes/webm/bitstream/h264/get-h264-descriptor.d.ts +0 -0
- package/dist/boxes/webm/bitstream/h264/get-h264-descriptor.js +1 -0
- package/dist/boxes/webm/ebml.d.ts +1 -1
- package/dist/boxes/webm/parse-ebml.js +3 -0
- package/dist/boxes/webm/segments/duration.d.ts +6 -0
- package/dist/boxes/webm/segments/duration.js +19 -0
- package/dist/boxes/webm/segments/info.d.ts +9 -0
- package/dist/boxes/webm/segments/info.js +22 -0
- package/dist/boxes/webm/segments/main.d.ts +5 -0
- package/dist/boxes/webm/segments/main.js +2 -0
- package/dist/boxes/webm/segments/muxing.d.ts +6 -0
- package/dist/boxes/webm/segments/muxing.js +11 -0
- package/dist/boxes/webm/segments/seek-head.d.ts +9 -0
- package/dist/boxes/webm/segments/seek-head.js +22 -0
- package/dist/boxes/webm/segments/seek-position.d.ts +6 -0
- package/dist/boxes/webm/segments/seek-position.js +11 -0
- package/dist/boxes/webm/segments/seek.d.ts +13 -0
- package/dist/boxes/webm/segments/seek.js +35 -0
- package/dist/boxes/webm/segments/timestamp-scale.d.ts +6 -0
- package/dist/boxes/webm/segments/timestamp-scale.js +11 -0
- package/dist/boxes/webm/segments/tracks.d.ts +8 -0
- package/dist/boxes/webm/segments/tracks.js +21 -0
- package/dist/boxes/webm/segments/unknown.d.ts +6 -0
- package/dist/boxes/webm/segments/unknown.js +11 -0
- package/dist/boxes/webm/segments/void.d.ts +6 -0
- package/dist/boxes/webm/segments/void.js +11 -0
- package/dist/boxes/webm/segments/writing.d.ts +6 -0
- package/dist/boxes/webm/segments/writing.js +11 -0
- package/dist/boxes/webm/tracks.d.ts +8 -0
- package/dist/boxes/webm/tracks.js +21 -0
- package/dist/buffer-iterator.js +6 -0
- package/dist/combine-uint8array.d.ts +1 -0
- package/dist/combine-uint8array.js +13 -0
- package/dist/create/cluster-segment.d.ts +1 -1
- package/dist/create/cluster-segment.js +3 -3
- package/dist/create/cluster.d.ts +5 -0
- package/dist/create/cluster.js +48 -0
- package/dist/create/create-media.js +15 -37
- package/dist/create/make-duration-with-padding.d.ts +1 -0
- package/dist/create/make-duration-with-padding.js +15 -0
- package/dist/create/matroska-info.d.ts +1 -2
- package/dist/create/matroska-info.js +3 -11
- package/dist/create/timescale.d.ts +1 -0
- package/dist/create/timescale.js +4 -0
- package/dist/from-web.d.ts +2 -0
- package/dist/from-web.js +45 -0
- package/dist/get-audio-codec.d.ts +1 -1
- package/dist/get-video-metadata.d.ts +2 -0
- package/dist/get-video-metadata.js +44 -0
- package/dist/read-and-increment-offset.d.ts +28 -0
- package/dist/read-and-increment-offset.js +177 -0
- package/dist/readers/from-fetch.js +1 -1
- package/dist/understand-vorbis.d.ts +1 -0
- package/dist/understand-vorbis.js +12 -0
- package/package.json +2 -2
- package/src/boxes/webm/parse-ebml.ts +4 -0
- package/src/buffer-iterator.ts +7 -0
- package/src/create/cluster-segment.ts +3 -3
- package/src/create/cluster.ts +64 -0
- package/src/create/create-media.ts +17 -53
- package/src/create/make-duration-with-padding.ts +15 -0
- package/src/create/matroska-info.ts +4 -20
- package/src/create/timescale.ts +1 -0
- package/src/readers/from-fetch.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _OffsetCounter_offset;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.getArrayBufferIterator = exports.OffsetCounter = void 0;
|
|
16
|
+
class OffsetCounter {
|
|
17
|
+
constructor(initial) {
|
|
18
|
+
_OffsetCounter_offset.set(this, void 0);
|
|
19
|
+
__classPrivateFieldSet(this, _OffsetCounter_offset, initial, "f");
|
|
20
|
+
}
|
|
21
|
+
increment(amount) {
|
|
22
|
+
if (amount < 0) {
|
|
23
|
+
throw new Error('Cannot increment by a negative amount');
|
|
24
|
+
}
|
|
25
|
+
__classPrivateFieldSet(this, _OffsetCounter_offset, __classPrivateFieldGet(this, _OffsetCounter_offset, "f") + amount, "f");
|
|
26
|
+
}
|
|
27
|
+
getOffset() {
|
|
28
|
+
return __classPrivateFieldGet(this, _OffsetCounter_offset, "f");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.OffsetCounter = OffsetCounter;
|
|
32
|
+
_OffsetCounter_offset = new WeakMap();
|
|
33
|
+
const makeOffsetCounter = (initial) => {
|
|
34
|
+
return new OffsetCounter(initial);
|
|
35
|
+
};
|
|
36
|
+
const getArrayBufferIterator = (data, initialOffset) => {
|
|
37
|
+
const view = new DataView(data);
|
|
38
|
+
const counter = makeOffsetCounter(initialOffset);
|
|
39
|
+
const getSlice = (amount) => {
|
|
40
|
+
const value = data.slice(counter.getOffset(), counter.getOffset() + amount);
|
|
41
|
+
counter.increment(amount);
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
const getUint8 = () => {
|
|
45
|
+
const val = view.getUint8(counter.getOffset());
|
|
46
|
+
counter.increment(1);
|
|
47
|
+
return val;
|
|
48
|
+
};
|
|
49
|
+
const getUint32 = () => {
|
|
50
|
+
const val = view.getUint32(counter.getOffset());
|
|
51
|
+
counter.increment(4);
|
|
52
|
+
return val;
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
view,
|
|
56
|
+
counter,
|
|
57
|
+
data,
|
|
58
|
+
discard: (length) => {
|
|
59
|
+
counter.increment(length);
|
|
60
|
+
},
|
|
61
|
+
getSlice,
|
|
62
|
+
getAtom: () => {
|
|
63
|
+
const atom = getSlice(4);
|
|
64
|
+
return new TextDecoder().decode(atom);
|
|
65
|
+
},
|
|
66
|
+
getMatroskaSegmentId: () => {
|
|
67
|
+
const first = getSlice(1);
|
|
68
|
+
const firstOneString = `0x${Array.from(new Uint8Array(first))
|
|
69
|
+
.map((b) => {
|
|
70
|
+
return b.toString(16).padStart(2, '0');
|
|
71
|
+
})
|
|
72
|
+
.join('')}`;
|
|
73
|
+
// Catch void block
|
|
74
|
+
const knownIdsWithOneLength = ['0xec', '0xae'];
|
|
75
|
+
if (knownIdsWithOneLength.includes(firstOneString)) {
|
|
76
|
+
return firstOneString;
|
|
77
|
+
}
|
|
78
|
+
const firstTwo = getSlice(1);
|
|
79
|
+
const knownIdsWithTwoLength = ['0x4dbb', '0x53ac', '0xec01'];
|
|
80
|
+
const firstTwoString = `${firstOneString}${Array.from(new Uint8Array(firstTwo))
|
|
81
|
+
.map((b) => {
|
|
82
|
+
return b.toString(16).padStart(2, '0');
|
|
83
|
+
})
|
|
84
|
+
.join('')}`;
|
|
85
|
+
if (knownIdsWithTwoLength.includes(firstTwoString)) {
|
|
86
|
+
return firstTwoString;
|
|
87
|
+
}
|
|
88
|
+
const knownIdsWithThreeLength = ['0x4d808c', '0x57418c', '0x448988'];
|
|
89
|
+
const firstThree = getSlice(1);
|
|
90
|
+
const firstThreeString = `${firstTwoString}${Array.from(new Uint8Array(firstThree))
|
|
91
|
+
.map((b) => {
|
|
92
|
+
return b.toString(16).padStart(2, '0');
|
|
93
|
+
})
|
|
94
|
+
.join('')}`;
|
|
95
|
+
if (knownIdsWithThreeLength.includes(firstThreeString)) {
|
|
96
|
+
return firstThreeString;
|
|
97
|
+
}
|
|
98
|
+
const segmentId = getSlice(1);
|
|
99
|
+
return `${firstThreeString}${Array.from(new Uint8Array(segmentId))
|
|
100
|
+
.map((b) => {
|
|
101
|
+
return b.toString(16).padStart(2, '0');
|
|
102
|
+
})
|
|
103
|
+
.join('')}`;
|
|
104
|
+
},
|
|
105
|
+
getVint: (bytes) => {
|
|
106
|
+
const slice = getSlice(bytes);
|
|
107
|
+
const d = [...Array.from(new Uint8Array(slice))];
|
|
108
|
+
const totalLength = d[0];
|
|
109
|
+
if (totalLength === 0) {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
// Calculate the actual length of the data based on the first set bit
|
|
113
|
+
let actualLength = 0;
|
|
114
|
+
while (((totalLength >> (7 - actualLength)) & 0x01) === 0) {
|
|
115
|
+
actualLength++;
|
|
116
|
+
}
|
|
117
|
+
actualLength += 1; // Include the first byte set as 1
|
|
118
|
+
// Combine the numbers to form the integer value
|
|
119
|
+
let value = 0;
|
|
120
|
+
// Mask the first byte properly then start combining
|
|
121
|
+
value = totalLength & (0xff >> actualLength);
|
|
122
|
+
for (let i = 1; i < actualLength; i++) {
|
|
123
|
+
value = (value << 8) | d[i];
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
},
|
|
127
|
+
getUint8,
|
|
128
|
+
getEBML: () => {
|
|
129
|
+
const val = getUint8();
|
|
130
|
+
// https://darkcoding.net/software/reading-mediarecorders-webm-opus-output/#:~:text=The%20first%20four%20bytes%20(%201A,%E2%80%93%20read%20on%20for%20why).
|
|
131
|
+
// You drop the initial 0 bits and the first 1 bit to get the value. 0x81 is 0b10000001, so there are zero inital 0 bits, meaning length one byte, and the value is 1. The 0x9F value for length of the EBML header we saw earlier is 0b10011111, still one byte, value is 0b0011111, which is 31 (the python repl is very helpful for these conversions).
|
|
132
|
+
const actualValue = val & 0x7f; // 0x7F is binary 01111111, which masks out the first bit
|
|
133
|
+
return actualValue;
|
|
134
|
+
},
|
|
135
|
+
getInt8: () => {
|
|
136
|
+
const val = view.getInt8(counter.getOffset());
|
|
137
|
+
counter.increment(1);
|
|
138
|
+
return val;
|
|
139
|
+
},
|
|
140
|
+
getUint16: () => {
|
|
141
|
+
const val = view.getUint16(counter.getOffset());
|
|
142
|
+
counter.increment(2);
|
|
143
|
+
return val;
|
|
144
|
+
},
|
|
145
|
+
getInt16: () => {
|
|
146
|
+
const val = view.getInt16(counter.getOffset());
|
|
147
|
+
counter.increment(2);
|
|
148
|
+
return val;
|
|
149
|
+
},
|
|
150
|
+
getUint32,
|
|
151
|
+
// https://developer.apple.com/documentation/quicktime-file-format/sound_sample_description_version_1
|
|
152
|
+
// A 32-bit unsigned fixed-point number (16.16) that indicates the rate at which the sound samples were obtained.
|
|
153
|
+
getFixedPoint1616Number: () => {
|
|
154
|
+
const val = getUint32();
|
|
155
|
+
return val / 2 ** 16;
|
|
156
|
+
},
|
|
157
|
+
getPascalString: () => {
|
|
158
|
+
const val = getSlice(32);
|
|
159
|
+
return [...Array.from(new Uint8Array(val))];
|
|
160
|
+
},
|
|
161
|
+
getDecimalBytes(length) {
|
|
162
|
+
const bytes = getSlice(length);
|
|
163
|
+
const numbers = [...Array.from(new Uint8Array(bytes))];
|
|
164
|
+
return numbers.reduce((acc, byte, index) => acc + (byte << (8 * (numbers.length - index - 1))), 0);
|
|
165
|
+
},
|
|
166
|
+
getByteString(length) {
|
|
167
|
+
const bytes = getSlice(length);
|
|
168
|
+
return new TextDecoder().decode(bytes);
|
|
169
|
+
},
|
|
170
|
+
getFloat64: () => {
|
|
171
|
+
const val = view.getFloat64(counter.getOffset());
|
|
172
|
+
counter.increment(8);
|
|
173
|
+
return val;
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
exports.getArrayBufferIterator = getArrayBufferIterator;
|
|
@@ -12,7 +12,7 @@ exports.fetchReader = {
|
|
|
12
12
|
if (!resolvedUrl.startsWith('https://') &&
|
|
13
13
|
!resolvedUrl.startsWith('http://')) {
|
|
14
14
|
return Promise.reject(new Error(resolvedUrl +
|
|
15
|
-
' is not a URL - needs to start with http:// or https://. If you want to read a local file, pass `nodeReader` to parseMedia().'));
|
|
15
|
+
' is not a URL - needs to start with http:// or https://. If you want to read a local file, pass `reader: nodeReader` to parseMedia().'));
|
|
16
16
|
}
|
|
17
17
|
const res = await fetch(resolvedUrl, {
|
|
18
18
|
headers: range === null
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare const understand: (src: string) => Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const understand = async (src) => {
|
|
3
|
+
const content = await Bun.file(src).arrayBuffer();
|
|
4
|
+
const firstBit = new DataView(content).getUint8(0);
|
|
5
|
+
const offset = new DataView(content).getUint8(1);
|
|
6
|
+
const offset2 = new DataView(content).getUint8(2);
|
|
7
|
+
const globalOffset = firstBit + 1;
|
|
8
|
+
const header = new TextDecoder().decode(content.slice(globalOffset, globalOffset + offset));
|
|
9
|
+
const comment = new TextDecoder().decode(content.slice(globalOffset + offset, globalOffset + offset2));
|
|
10
|
+
const rest = new TextDecoder().decode(content.slice(globalOffset + offset2 + offset, globalOffset + offset2 + offset + 50));
|
|
11
|
+
console.log({ firstBit, offset, offset2, header, comment, rest });
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
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.209",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@types/wicg-file-system-access": "2023.10.5",
|
|
11
|
-
"@remotion/renderer": "4.0.
|
|
11
|
+
"@remotion/renderer": "4.0.209"
|
|
12
12
|
},
|
|
13
13
|
"publishConfig": {
|
|
14
14
|
"access": "public"
|
|
@@ -110,6 +110,10 @@ export const parseEbml = async (
|
|
|
110
110
|
|
|
111
111
|
// eslint-disable-next-line no-constant-condition
|
|
112
112
|
while (true) {
|
|
113
|
+
if (size === 0) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
|
|
113
117
|
const offset = iterator.counter.getOffset();
|
|
114
118
|
const value = await parseEbml(iterator, parserContext);
|
|
115
119
|
const remapped = await postprocessEbml({
|
package/src/buffer-iterator.ts
CHANGED
|
@@ -459,6 +459,13 @@ export const getArrayBufferIterator = (
|
|
|
459
459
|
value = (value << 8) | d[i];
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
// Livestreamed segments sometimes have a Cluster length of 0xFFFFFFFFFFFFFF
|
|
463
|
+
// which we parse as -1
|
|
464
|
+
// this should be treated as Infinity
|
|
465
|
+
if (value === -1) {
|
|
466
|
+
return Infinity;
|
|
467
|
+
}
|
|
468
|
+
|
|
462
469
|
return value;
|
|
463
470
|
},
|
|
464
471
|
getUint8,
|
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
|
|
9
9
|
export const CLUSTER_MIN_VINT_WIDTH = 8;
|
|
10
10
|
|
|
11
|
-
export const createClusterSegment = () => {
|
|
11
|
+
export const createClusterSegment = (timestamp: number) => {
|
|
12
12
|
return makeMatroskaBytes({
|
|
13
13
|
type: 'Cluster',
|
|
14
14
|
value: [
|
|
15
15
|
{
|
|
16
16
|
type: 'Timestamp',
|
|
17
|
-
minVintWidth:
|
|
17
|
+
minVintWidth: null,
|
|
18
18
|
value: {
|
|
19
|
-
value:
|
|
19
|
+
value: timestamp,
|
|
20
20
|
byteLength: null,
|
|
21
21
|
},
|
|
22
22
|
},
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {getVariableInt} from '../boxes/webm/ebml';
|
|
2
|
+
import {matroskaToHex} from '../boxes/webm/make-header';
|
|
3
|
+
import {matroskaElements} from '../boxes/webm/segments/all-segments';
|
|
4
|
+
import type {Writer} from '../writers/writer';
|
|
5
|
+
import {
|
|
6
|
+
CLUSTER_MIN_VINT_WIDTH,
|
|
7
|
+
createClusterSegment,
|
|
8
|
+
makeSimpleBlock,
|
|
9
|
+
} from './cluster-segment';
|
|
10
|
+
import {CREATE_TIME_SCALE} from './timescale';
|
|
11
|
+
|
|
12
|
+
const maxClusterTimestamp = 2 ** 15;
|
|
13
|
+
|
|
14
|
+
const timestampToClusterTimestamp = (timestamp: number) => {
|
|
15
|
+
return Math.round((timestamp / CREATE_TIME_SCALE) * 1000);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const makeCluster = async (w: Writer, timestamp: number) => {
|
|
19
|
+
const cluster = createClusterSegment(timestampToClusterTimestamp(timestamp));
|
|
20
|
+
const clusterVIntPosition =
|
|
21
|
+
w.getWrittenByteCount() +
|
|
22
|
+
cluster.offsets.offset +
|
|
23
|
+
matroskaToHex(matroskaElements.Cluster).byteLength;
|
|
24
|
+
|
|
25
|
+
let clusterSize = cluster.bytes.byteLength;
|
|
26
|
+
await w.write(cluster.bytes);
|
|
27
|
+
|
|
28
|
+
const addSample = async (chunk: EncodedVideoChunk, trackNumber: number) => {
|
|
29
|
+
const arr = new Uint8Array(chunk.byteLength);
|
|
30
|
+
chunk.copyTo(arr);
|
|
31
|
+
const timecodeRelativeToCluster =
|
|
32
|
+
timestampToClusterTimestamp(chunk.timestamp) -
|
|
33
|
+
timestampToClusterTimestamp(timestamp);
|
|
34
|
+
if (timecodeRelativeToCluster > maxClusterTimestamp) {
|
|
35
|
+
throw new Error('timecodeRelativeToCluster is too big');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const keyframe = chunk.type === 'key';
|
|
39
|
+
const simpleBlock = makeSimpleBlock({
|
|
40
|
+
bytes: arr,
|
|
41
|
+
invisible: false,
|
|
42
|
+
keyframe,
|
|
43
|
+
lacing: 0,
|
|
44
|
+
trackNumber,
|
|
45
|
+
timecodeRelativeToCluster,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
clusterSize += simpleBlock.byteLength;
|
|
49
|
+
await w.updateDataAt(
|
|
50
|
+
clusterVIntPosition,
|
|
51
|
+
getVariableInt(clusterSize, CLUSTER_MIN_VINT_WIDTH),
|
|
52
|
+
);
|
|
53
|
+
await w.write(simpleBlock);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const shouldMakeNewCluster = (chunk: EncodedVideoChunk) => {
|
|
57
|
+
const newTimestamp = timestampToClusterTimestamp(chunk.timestamp);
|
|
58
|
+
const oldTimestamp = timestampToClusterTimestamp(timestamp);
|
|
59
|
+
|
|
60
|
+
return newTimestamp - oldTimestamp >= 2000 && chunk.type === 'key';
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {addSample, shouldMakeNewCluster};
|
|
64
|
+
};
|
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
combineUint8Arrays,
|
|
4
|
-
matroskaToHex,
|
|
5
|
-
padMatroskaBytes,
|
|
6
|
-
} from '../boxes/webm/make-header';
|
|
1
|
+
import {combineUint8Arrays} from '../boxes/webm/make-header';
|
|
7
2
|
import type {BytesAndOffset} from '../boxes/webm/segments/all-segments';
|
|
8
|
-
import {matroskaElements} from '../boxes/webm/segments/all-segments';
|
|
9
3
|
import type {WriterInterface} from '../writers/writer';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
createClusterSegment,
|
|
13
|
-
makeSimpleBlock,
|
|
14
|
-
} from './cluster-segment';
|
|
4
|
+
import {makeCluster} from './cluster';
|
|
5
|
+
import {makeDurationWithPadding} from './make-duration-with-padding';
|
|
15
6
|
import {makeMatroskaHeader} from './matroska-header';
|
|
16
7
|
import {makeMatroskaInfo} from './matroska-info';
|
|
17
8
|
import {createMatroskaSegment} from './matroska-segment';
|
|
@@ -21,6 +12,7 @@ import {
|
|
|
21
12
|
makeMatroskaTracks,
|
|
22
13
|
makeMatroskaVideoTrackEntryBytes,
|
|
23
14
|
} from './matroska-trackentry';
|
|
15
|
+
import {CREATE_TIME_SCALE} from './timescale';
|
|
24
16
|
|
|
25
17
|
export type MediaFn = {
|
|
26
18
|
save: () => Promise<void>;
|
|
@@ -43,9 +35,7 @@ export const createMedia = async (
|
|
|
43
35
|
const w = await writer.createContent();
|
|
44
36
|
await w.write(header.bytes);
|
|
45
37
|
const matroskaInfo = makeMatroskaInfo({
|
|
46
|
-
timescale:
|
|
47
|
-
// TODO: Hardcoded
|
|
48
|
-
duration: 2658,
|
|
38
|
+
timescale: CREATE_TIME_SCALE,
|
|
49
39
|
});
|
|
50
40
|
|
|
51
41
|
const currentTracks: BytesAndOffset[] = [];
|
|
@@ -74,50 +64,24 @@ export const createMedia = async (
|
|
|
74
64
|
|
|
75
65
|
await w.write(matroskaSegment.bytes);
|
|
76
66
|
|
|
77
|
-
|
|
78
|
-
const clusterVIntPosition =
|
|
79
|
-
w.getWrittenByteCount() +
|
|
80
|
-
cluster.offsets.offset +
|
|
81
|
-
matroskaToHex(matroskaElements.Cluster).byteLength;
|
|
67
|
+
let currentCluster = await makeCluster(w, 0);
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
const getClusterOrMakeNew = async (chunk: EncodedVideoChunk) => {
|
|
70
|
+
if (!currentCluster.shouldMakeNewCluster(chunk)) {
|
|
71
|
+
return currentCluster;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
currentCluster = await makeCluster(w, chunk.timestamp);
|
|
75
|
+
return currentCluster;
|
|
76
|
+
};
|
|
85
77
|
|
|
86
78
|
const addSample = async (chunk: EncodedVideoChunk, trackNumber: number) => {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
const simpleBlock = makeSimpleBlock({
|
|
90
|
-
bytes: arr,
|
|
91
|
-
invisible: false,
|
|
92
|
-
keyframe: chunk.type === 'key',
|
|
93
|
-
lacing: 0,
|
|
94
|
-
trackNumber,
|
|
95
|
-
// TODO: Maybe this is bad, because it's in microseconds, but should be in timescale
|
|
96
|
-
// Maybe it only works by coincidence
|
|
97
|
-
timecodeRelativeToCluster: Math.round(chunk.timestamp / 1000),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
clusterSize += simpleBlock.byteLength;
|
|
101
|
-
await w.updateDataAt(
|
|
102
|
-
clusterVIntPosition,
|
|
103
|
-
getVariableInt(clusterSize, CLUSTER_MIN_VINT_WIDTH),
|
|
104
|
-
);
|
|
105
|
-
await w.write(simpleBlock);
|
|
79
|
+
const cluster = await getClusterOrMakeNew(chunk);
|
|
80
|
+
return cluster.addSample(chunk, trackNumber);
|
|
106
81
|
};
|
|
107
82
|
|
|
108
83
|
const updateDuration = async (newDuration: number) => {
|
|
109
|
-
const blocks =
|
|
110
|
-
{
|
|
111
|
-
type: 'Duration',
|
|
112
|
-
value: {
|
|
113
|
-
value: newDuration,
|
|
114
|
-
size: '64',
|
|
115
|
-
},
|
|
116
|
-
minVintWidth: null,
|
|
117
|
-
},
|
|
118
|
-
// TODO: That's too much padding
|
|
119
|
-
1000,
|
|
120
|
-
);
|
|
84
|
+
const blocks = makeDurationWithPadding(newDuration);
|
|
121
85
|
await w.updateDataAt(
|
|
122
86
|
durationOffset,
|
|
123
87
|
combineUint8Arrays(blocks.map((b) => b.bytes)),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {padMatroskaBytes} from '../boxes/webm/make-header';
|
|
2
|
+
|
|
3
|
+
export const makeDurationWithPadding = (newDuration: number) => {
|
|
4
|
+
return padMatroskaBytes(
|
|
5
|
+
{
|
|
6
|
+
type: 'Duration',
|
|
7
|
+
value: {
|
|
8
|
+
value: newDuration,
|
|
9
|
+
size: '64',
|
|
10
|
+
},
|
|
11
|
+
minVintWidth: null,
|
|
12
|
+
},
|
|
13
|
+
100,
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import {makeMatroskaBytes
|
|
1
|
+
import {makeMatroskaBytes} from '../boxes/webm/make-header';
|
|
2
|
+
import {makeDurationWithPadding} from './make-duration-with-padding';
|
|
2
3
|
|
|
3
|
-
export const makeMatroskaInfo = ({
|
|
4
|
-
timescale,
|
|
5
|
-
duration,
|
|
6
|
-
}: {
|
|
7
|
-
timescale: number;
|
|
8
|
-
duration: number;
|
|
9
|
-
}) => {
|
|
4
|
+
export const makeMatroskaInfo = ({timescale}: {timescale: number}) => {
|
|
10
5
|
return makeMatroskaBytes({
|
|
11
6
|
type: 'Info',
|
|
12
7
|
value: [
|
|
@@ -28,18 +23,7 @@ export const makeMatroskaInfo = ({
|
|
|
28
23
|
value: '@remotion/media-parser',
|
|
29
24
|
minVintWidth: null,
|
|
30
25
|
},
|
|
31
|
-
...
|
|
32
|
-
{
|
|
33
|
-
type: 'Duration',
|
|
34
|
-
value: {
|
|
35
|
-
value: duration,
|
|
36
|
-
size: '64',
|
|
37
|
-
},
|
|
38
|
-
minVintWidth: null,
|
|
39
|
-
},
|
|
40
|
-
// TODO: That's too much padding
|
|
41
|
-
1000,
|
|
42
|
-
),
|
|
26
|
+
...makeDurationWithPadding(0),
|
|
43
27
|
],
|
|
44
28
|
minVintWidth: null,
|
|
45
29
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CREATE_TIME_SCALE = 1_000_000;
|
|
@@ -18,7 +18,7 @@ export const fetchReader: ReaderInterface = {
|
|
|
18
18
|
return Promise.reject(
|
|
19
19
|
new Error(
|
|
20
20
|
resolvedUrl +
|
|
21
|
-
' is not a URL - needs to start with http:// or https://. If you want to read a local file, pass `nodeReader` to parseMedia().',
|
|
21
|
+
' is not a URL - needs to start with http:// or https://. If you want to read a local file, pass `reader: nodeReader` to parseMedia().',
|
|
22
22
|
),
|
|
23
23
|
);
|
|
24
24
|
}
|