@remotion/media-parser 4.0.202 → 4.0.204
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 +0 -5
- package/dist/av1-codec-string.js +1 -18
- 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/get-sample-positions-from-track.d.ts +4 -0
- package/dist/boxes/iso-base-media/get-sample-positions-from-track.js +48 -0
- package/dist/boxes/iso-base-media/tfdt.d.ts +12 -0
- package/dist/boxes/iso-base-media/tfdt.js +20 -0
- package/dist/boxes/iso-base-media/tfhd.d.ts +16 -0
- package/dist/boxes/iso-base-media/tfhd.js +41 -0
- package/dist/boxes/iso-base-media/trun.d.ts +21 -0
- package/dist/boxes/iso-base-media/trun.js +44 -0
- package/dist/boxes/webm/bitstream/av1.js +1 -10
- package/dist/boxes/webm/ebml.d.ts +1 -1
- 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/tracks.d.ts +8 -0
- package/dist/boxes/webm/tracks.js +21 -0
- package/dist/from-web.js +6 -15
- 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/samples-from-moof.d.ts +6 -0
- package/dist/samples-from-moof.js +74 -0
- package/dist/understand-vorbis.d.ts +1 -0
- package/dist/understand-vorbis.js +12 -0
- package/package.json +2 -2
- package/src/boxes/iso-base-media/get-sample-positions-from-track.ts +69 -0
- package/src/boxes/iso-base-media/make-track.ts +4 -45
- package/src/boxes/iso-base-media/mdat/mdat.ts +33 -24
- package/src/boxes/iso-base-media/mdhd.ts +10 -7
- package/src/boxes/iso-base-media/mvhd.ts +15 -14
- package/src/boxes/iso-base-media/process-box.ts +42 -0
- package/src/boxes/iso-base-media/tfdt.ts +37 -0
- package/src/boxes/iso-base-media/tfhd.ts +66 -0
- package/src/boxes/iso-base-media/tkhd.ts +11 -13
- package/src/boxes/iso-base-media/trun.ts +74 -0
- package/src/boxes/webm/get-track.ts +2 -2
- package/src/buffer-iterator.ts +3 -2
- package/src/get-duration.ts +40 -5
- package/src/get-tracks.ts +4 -4
- package/src/has-all-info.ts +1 -1
- package/src/parse-media.ts +1 -1
- package/src/parse-result.ts +7 -1
- package/src/samples-from-moof.ts +101 -0
- package/src/test/samples-from-moof.test.ts +2496 -0
- package/src/test/stream-local.test.ts +28 -30
- package/src/test/stream-samples.test.ts +153 -231
- package/src/traversal.ts +56 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/bitstream/av1.d.ts +0 -2
- package/dist/bitstream/av1.js +0 -12
- package/dist/boxes/iso-base-media/avcc-hvcc.d.ts +0 -20
- package/dist/boxes/iso-base-media/avcc-hvcc.js +0 -73
- package/dist/boxes/iso-base-media/avcc.d.ts +0 -18
- package/dist/boxes/iso-base-media/avcc.js +0 -27
- package/dist/boxes/iso-base-media/esds-descriptors.d.ts +0 -21
- package/dist/boxes/iso-base-media/esds-descriptors.js +0 -62
- package/dist/boxes/iso-base-media/esds.d.ts +0 -15
- package/dist/boxes/iso-base-media/esds.js +0 -27
- package/dist/create/create-media.d.ts +0 -2
- package/dist/create/create-media.js +0 -36
- package/dist/create/matroska-header.d.ts +0 -1
- package/dist/create/matroska-header.js +0 -66
- package/dist/create/matroska-info.d.ts +0 -4
- package/dist/create/matroska-info.js +0 -39
- package/dist/create/matroska-segment.d.ts +0 -1
- package/dist/create/matroska-segment.js +0 -12
- package/dist/create/matroska-trackentry.d.ts +0 -21
- package/dist/create/matroska-trackentry.js +0 -191
- package/dist/create-media.d.ts +0 -1
- package/dist/create-media.js +0 -78
- package/dist/from-input-type-file.d.ts +0 -2
- package/dist/from-input-type-file.js +0 -37
- package/dist/get-codec.d.ts +0 -4
- package/dist/get-codec.js +0 -22
- package/dist/readers/from-fetch.d.ts +0 -2
- package/dist/readers/from-fetch.js +0 -64
- package/dist/readers/from-node.d.ts +0 -2
- package/dist/readers/from-node.js +0 -40
- package/dist/readers/from-web-file.d.ts +0 -2
- package/dist/readers/from-web-file.js +0 -39
- package/dist/readers/reader.d.ts +0 -11
- package/dist/readers/reader.js +0 -2
- package/dist/web-file.d.ts +0 -2
- package/dist/web-file.js +0 -37
- package/dist/writers/web-fs.d.ts +0 -2
- package/dist/writers/web-fs.js +0 -28
- package/dist/writers/writer.d.ts +0 -9
- package/dist/writers/writer.js +0 -2
- /package/dist/{get-samples.d.ts → boxes/webm/bitstream/av1/frame.d.ts} +0 -0
- /package/dist/{get-samples.js → boxes/webm/bitstream/av1/frame.js} +0 -0
- /package/dist/{sample-aspect-ratio.d.ts → boxes/webm/bitstream/h264/get-h264-descriptor.d.ts} +0 -0
- /package/dist/{sample-aspect-ratio.js → boxes/webm/bitstream/h264/get-h264-descriptor.js} +0 -0
|
@@ -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;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSamplesFromMoof = void 0;
|
|
4
|
+
const traversal_1 = require("./traversal");
|
|
5
|
+
const getSamplesFromTraf = (trafSegment, moofOffset) => {
|
|
6
|
+
var _a, _b, _c, _d, _e, _f;
|
|
7
|
+
if (trafSegment.type !== 'regular-box' || trafSegment.boxType !== 'traf') {
|
|
8
|
+
throw new Error('Expected traf-box');
|
|
9
|
+
}
|
|
10
|
+
const tfhdBox = (0, traversal_1.getTfhdBox)(trafSegment);
|
|
11
|
+
const defaultSampleDuration = (_a = tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.defaultSampleDuration) !== null && _a !== void 0 ? _a : null;
|
|
12
|
+
const defaultSampleSize = (_b = tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.defaultSampleSize) !== null && _b !== void 0 ? _b : null;
|
|
13
|
+
const defaultSampleFlags = (_c = tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.defaultSampleFlags) !== null && _c !== void 0 ? _c : null;
|
|
14
|
+
const tfdtBox = (0, traversal_1.getTfdtBox)(trafSegment);
|
|
15
|
+
const trunBoxes = (0, traversal_1.getTrunBoxes)(trafSegment);
|
|
16
|
+
let time = 0;
|
|
17
|
+
let offset = 0;
|
|
18
|
+
let dataOffset = 0;
|
|
19
|
+
const samples = [];
|
|
20
|
+
for (const trunBox of trunBoxes) {
|
|
21
|
+
let i = -1;
|
|
22
|
+
if (dataOffset === 0 && trunBox.dataOffset) {
|
|
23
|
+
dataOffset = trunBox.dataOffset;
|
|
24
|
+
}
|
|
25
|
+
for (const sample of trunBox.samples) {
|
|
26
|
+
i++;
|
|
27
|
+
const duration = (_d = sample.sampleDuration) !== null && _d !== void 0 ? _d : defaultSampleDuration;
|
|
28
|
+
if (duration === null) {
|
|
29
|
+
throw new Error('Expected duration');
|
|
30
|
+
}
|
|
31
|
+
const size = (_e = sample.sampleSize) !== null && _e !== void 0 ? _e : defaultSampleSize;
|
|
32
|
+
if (size === null) {
|
|
33
|
+
throw new Error('Expected size');
|
|
34
|
+
}
|
|
35
|
+
const isFirstSample = i === 0;
|
|
36
|
+
const sampleFlags = sample.sampleFlags
|
|
37
|
+
? sample.sampleFlags
|
|
38
|
+
: isFirstSample && trunBox.firstSampleFlags !== null
|
|
39
|
+
? trunBox.firstSampleFlags
|
|
40
|
+
: defaultSampleFlags;
|
|
41
|
+
if (sampleFlags === null) {
|
|
42
|
+
throw new Error('Expected sample flags');
|
|
43
|
+
}
|
|
44
|
+
const keyframe = !((sampleFlags >> 16) & 0x1);
|
|
45
|
+
const dts = time + ((_f = tfdtBox === null || tfdtBox === void 0 ? void 0 : tfdtBox.baseMediaDecodeTime) !== null && _f !== void 0 ? _f : 0);
|
|
46
|
+
const samplePosition = {
|
|
47
|
+
offset: offset + (moofOffset !== null && moofOffset !== void 0 ? moofOffset : 0) + (dataOffset !== null && dataOffset !== void 0 ? dataOffset : 0),
|
|
48
|
+
dts,
|
|
49
|
+
cts: dts,
|
|
50
|
+
duration,
|
|
51
|
+
isKeyframe: keyframe,
|
|
52
|
+
size,
|
|
53
|
+
};
|
|
54
|
+
samples.push(samplePosition);
|
|
55
|
+
offset += size;
|
|
56
|
+
time += duration;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return samples;
|
|
60
|
+
};
|
|
61
|
+
const getSamplesFromMoof = ({ moofBox, trackId, }) => {
|
|
62
|
+
if (moofBox.type !== 'regular-box') {
|
|
63
|
+
throw new Error('Expected moof-box');
|
|
64
|
+
}
|
|
65
|
+
const trafs = moofBox.children.filter((c) => c.type === 'regular-box' && c.boxType === 'traf');
|
|
66
|
+
const mapped = trafs.map((traf) => {
|
|
67
|
+
const tfhdBox = (0, traversal_1.getTfhdBox)(traf);
|
|
68
|
+
return (tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.trackId) === trackId
|
|
69
|
+
? getSamplesFromTraf(traf, moofBox.offset)
|
|
70
|
+
: [];
|
|
71
|
+
});
|
|
72
|
+
return mapped.flat(1);
|
|
73
|
+
};
|
|
74
|
+
exports.getSamplesFromMoof = getSamplesFromMoof;
|
|
@@ -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,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.204",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@remotion/renderer": "4.0.
|
|
10
|
+
"@remotion/renderer": "4.0.204"
|
|
11
11
|
},
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {getTimescaleAndDuration} from '../../get-fps';
|
|
2
|
+
import type {SamplePosition} from '../../get-sample-positions';
|
|
3
|
+
import {getSamplePositions} from '../../get-sample-positions';
|
|
4
|
+
import type {IsoBaseMediaBox} from '../../parse-result';
|
|
5
|
+
import {getSamplesFromMoof} from '../../samples-from-moof';
|
|
6
|
+
import {
|
|
7
|
+
getCttsBox,
|
|
8
|
+
getStcoBox,
|
|
9
|
+
getStscBox,
|
|
10
|
+
getStssBox,
|
|
11
|
+
getStszBox,
|
|
12
|
+
getSttsBox,
|
|
13
|
+
getTkhdBox,
|
|
14
|
+
} from '../../traversal';
|
|
15
|
+
import type {TrakBox} from './trak/trak';
|
|
16
|
+
|
|
17
|
+
export const getSamplePositionsFromTrack = (
|
|
18
|
+
trakBox: TrakBox,
|
|
19
|
+
moofBox: IsoBaseMediaBox | null,
|
|
20
|
+
): SamplePosition[] => {
|
|
21
|
+
const stszBox = getStszBox(trakBox);
|
|
22
|
+
const stcoBox = getStcoBox(trakBox);
|
|
23
|
+
const stscBox = getStscBox(trakBox);
|
|
24
|
+
const stssBox = getStssBox(trakBox);
|
|
25
|
+
const sttsBox = getSttsBox(trakBox);
|
|
26
|
+
const tkhdBox = getTkhdBox(trakBox);
|
|
27
|
+
const cttsBox = getCttsBox(trakBox);
|
|
28
|
+
|
|
29
|
+
const timescaleAndDuration = getTimescaleAndDuration(trakBox);
|
|
30
|
+
|
|
31
|
+
if (!tkhdBox) {
|
|
32
|
+
throw new Error('Expected tkhd box in trak box');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!stszBox) {
|
|
36
|
+
throw new Error('Expected stsz box in trak box');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!stcoBox) {
|
|
40
|
+
throw new Error('Expected stco box in trak box');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!stscBox) {
|
|
44
|
+
throw new Error('Expected stsc box in trak box');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!sttsBox) {
|
|
48
|
+
throw new Error('Expected stts box in trak box');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!timescaleAndDuration) {
|
|
52
|
+
throw new Error('Expected timescale and duration in trak box');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let samplePositions = getSamplePositions({
|
|
56
|
+
stcoBox,
|
|
57
|
+
stscBox,
|
|
58
|
+
stszBox,
|
|
59
|
+
stssBox,
|
|
60
|
+
sttsBox,
|
|
61
|
+
cttsBox,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (samplePositions.length === 0 && moofBox) {
|
|
65
|
+
samplePositions = getSamplesFromMoof({moofBox, trackId: tkhdBox.trackId});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return samplePositions;
|
|
69
|
+
};
|
|
@@ -15,31 +15,15 @@ import {
|
|
|
15
15
|
getSampleAspectRatio,
|
|
16
16
|
getVideoSample,
|
|
17
17
|
} from '../../get-sample-aspect-ratio';
|
|
18
|
-
import {getSamplePositions} from '../../get-sample-positions';
|
|
19
18
|
import type {AudioTrack, OtherTrack, VideoTrack} from '../../get-tracks';
|
|
20
19
|
import {getVideoCodecString} from '../../get-video-codec';
|
|
21
|
-
import {
|
|
22
|
-
getCttsBox,
|
|
23
|
-
getStcoBox,
|
|
24
|
-
getStscBox,
|
|
25
|
-
getStssBox,
|
|
26
|
-
getStszBox,
|
|
27
|
-
getSttsBox,
|
|
28
|
-
getTkhdBox,
|
|
29
|
-
getVideoDescriptors,
|
|
30
|
-
} from '../../traversal';
|
|
20
|
+
import {getTkhdBox, getVideoDescriptors} from '../../traversal';
|
|
31
21
|
import type {TrakBox} from './trak/trak';
|
|
32
22
|
|
|
33
23
|
export const makeBaseMediaTrack = (
|
|
34
24
|
trakBox: TrakBox,
|
|
35
25
|
): VideoTrack | AudioTrack | OtherTrack | null => {
|
|
36
|
-
const stszBox = getStszBox(trakBox);
|
|
37
|
-
const stcoBox = getStcoBox(trakBox);
|
|
38
|
-
const stscBox = getStscBox(trakBox);
|
|
39
|
-
const stssBox = getStssBox(trakBox);
|
|
40
|
-
const sttsBox = getSttsBox(trakBox);
|
|
41
26
|
const tkhdBox = getTkhdBox(trakBox);
|
|
42
|
-
const cttsBox = getCttsBox(trakBox);
|
|
43
27
|
|
|
44
28
|
const videoDescriptors = getVideoDescriptors(trakBox);
|
|
45
29
|
const timescaleAndDuration = getTimescaleAndDuration(trakBox);
|
|
@@ -48,35 +32,10 @@ export const makeBaseMediaTrack = (
|
|
|
48
32
|
throw new Error('Expected tkhd box in trak box');
|
|
49
33
|
}
|
|
50
34
|
|
|
51
|
-
if (!stszBox) {
|
|
52
|
-
throw new Error('Expected stsz box in trak box');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!stcoBox) {
|
|
56
|
-
throw new Error('Expected stco box in trak box');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!stscBox) {
|
|
60
|
-
throw new Error('Expected stsc box in trak box');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!sttsBox) {
|
|
64
|
-
throw new Error('Expected stts box in trak box');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
35
|
if (!timescaleAndDuration) {
|
|
68
36
|
throw new Error('Expected timescale and duration in trak box');
|
|
69
37
|
}
|
|
70
38
|
|
|
71
|
-
const samplePositions = getSamplePositions({
|
|
72
|
-
stcoBox,
|
|
73
|
-
stscBox,
|
|
74
|
-
stszBox,
|
|
75
|
-
stssBox,
|
|
76
|
-
sttsBox,
|
|
77
|
-
cttsBox,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
39
|
if (trakBoxContainsAudio(trakBox)) {
|
|
81
40
|
const numberOfChannels = getNumberOfChannelsFromTrak(trakBox);
|
|
82
41
|
if (numberOfChannels === null) {
|
|
@@ -92,22 +51,22 @@ export const makeBaseMediaTrack = (
|
|
|
92
51
|
|
|
93
52
|
return {
|
|
94
53
|
type: 'audio',
|
|
95
|
-
samplePositions,
|
|
96
54
|
trackId: tkhdBox.trackId,
|
|
97
55
|
timescale: timescaleAndDuration.timescale,
|
|
98
56
|
codec: codecString,
|
|
99
57
|
numberOfChannels,
|
|
100
58
|
sampleRate,
|
|
101
59
|
description,
|
|
60
|
+
trakBox,
|
|
102
61
|
};
|
|
103
62
|
}
|
|
104
63
|
|
|
105
64
|
if (!trakBoxContainsVideo(trakBox)) {
|
|
106
65
|
return {
|
|
107
66
|
type: 'other',
|
|
108
|
-
samplePositions,
|
|
109
67
|
trackId: tkhdBox.trackId,
|
|
110
68
|
timescale: timescaleAndDuration.timescale,
|
|
69
|
+
trakBox,
|
|
111
70
|
};
|
|
112
71
|
}
|
|
113
72
|
|
|
@@ -138,7 +97,6 @@ export const makeBaseMediaTrack = (
|
|
|
138
97
|
|
|
139
98
|
const track: VideoTrack = {
|
|
140
99
|
type: 'video',
|
|
141
|
-
samplePositions,
|
|
142
100
|
trackId: tkhdBox.trackId,
|
|
143
101
|
description: videoDescriptors ?? undefined,
|
|
144
102
|
timescale: timescaleAndDuration.timescale,
|
|
@@ -152,6 +110,7 @@ export const makeBaseMediaTrack = (
|
|
|
152
110
|
displayAspectWidth,
|
|
153
111
|
displayAspectHeight,
|
|
154
112
|
rotation,
|
|
113
|
+
trakBox,
|
|
155
114
|
};
|
|
156
115
|
return track;
|
|
157
116
|
};
|
|
@@ -3,6 +3,9 @@ import type {BufferIterator} from '../../../buffer-iterator';
|
|
|
3
3
|
import {getTracks, hasTracks} from '../../../get-tracks';
|
|
4
4
|
import type {AnySegment} from '../../../parse-result';
|
|
5
5
|
import type {ParserContext} from '../../../parser-context';
|
|
6
|
+
import {getMoofBox} from '../../../traversal';
|
|
7
|
+
import {getSamplePositionsFromTrack} from '../get-sample-positions-from-track';
|
|
8
|
+
import type {TrakBox} from '../trak/trak';
|
|
6
9
|
|
|
7
10
|
export interface MdatBox {
|
|
8
11
|
type: 'mdat-box';
|
|
@@ -44,13 +47,17 @@ export const parseMdat = async ({
|
|
|
44
47
|
|
|
45
48
|
const flatSamples = allTracks
|
|
46
49
|
.map((track) => {
|
|
47
|
-
|
|
50
|
+
const samplePositions = getSamplePositionsFromTrack(
|
|
51
|
+
track.trakBox as TrakBox,
|
|
52
|
+
getMoofBox(existingBoxes),
|
|
53
|
+
);
|
|
54
|
+
if (!samplePositions) {
|
|
48
55
|
throw new Error('No sample positions');
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
return
|
|
58
|
+
return samplePositions.map((samplePosition) => {
|
|
52
59
|
return {
|
|
53
|
-
track,
|
|
60
|
+
track: {...track},
|
|
54
61
|
samplePosition,
|
|
55
62
|
};
|
|
56
63
|
});
|
|
@@ -59,10 +66,10 @@ export const parseMdat = async ({
|
|
|
59
66
|
|
|
60
67
|
// eslint-disable-next-line no-constant-condition
|
|
61
68
|
while (true) {
|
|
62
|
-
const
|
|
69
|
+
const samplesWithIndex = flatSamples.find((sample) => {
|
|
63
70
|
return sample.samplePosition.offset === data.counter.getOffset();
|
|
64
71
|
});
|
|
65
|
-
if (!
|
|
72
|
+
if (!samplesWithIndex) {
|
|
66
73
|
// There are various reasons why in mdat we find weird stuff:
|
|
67
74
|
// - iphonevideo.hevc has a fake hoov atom which is not mapped
|
|
68
75
|
// - corrupted.mp4 has a corrupt table
|
|
@@ -81,37 +88,39 @@ export const parseMdat = async ({
|
|
|
81
88
|
}
|
|
82
89
|
}
|
|
83
90
|
|
|
84
|
-
if (data.bytesRemaining() <
|
|
91
|
+
if (data.bytesRemaining() < samplesWithIndex.samplePosition.size) {
|
|
85
92
|
break;
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
const bytes = data.getSlice(
|
|
95
|
+
const bytes = data.getSlice(samplesWithIndex.samplePosition.size);
|
|
89
96
|
|
|
90
|
-
if (
|
|
91
|
-
await options.parserState.onAudioSample(
|
|
97
|
+
if (samplesWithIndex.track.type === 'audio') {
|
|
98
|
+
await options.parserState.onAudioSample(samplesWithIndex.track.trackId, {
|
|
92
99
|
data: bytes,
|
|
93
|
-
timestamp:
|
|
94
|
-
trackId:
|
|
95
|
-
type:
|
|
100
|
+
timestamp: samplesWithIndex.samplePosition.offset,
|
|
101
|
+
trackId: samplesWithIndex.track.trackId,
|
|
102
|
+
type: samplesWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
|
|
96
103
|
});
|
|
97
104
|
}
|
|
98
105
|
|
|
99
|
-
if (
|
|
100
|
-
const timestamp =
|
|
101
|
-
(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
if (samplesWithIndex.track.type === 'video') {
|
|
107
|
+
const timestamp = Math.floor(
|
|
108
|
+
(samplesWithIndex.samplePosition.cts * 1_000_000) /
|
|
109
|
+
samplesWithIndex.track.timescale,
|
|
110
|
+
);
|
|
111
|
+
const duration = Math.floor(
|
|
112
|
+
(samplesWithIndex.samplePosition.duration * 1_000_000) /
|
|
113
|
+
samplesWithIndex.track.timescale,
|
|
114
|
+
);
|
|
106
115
|
|
|
107
|
-
await options.parserState.onVideoSample(
|
|
116
|
+
await options.parserState.onVideoSample(samplesWithIndex.track.trackId, {
|
|
108
117
|
data: bytes,
|
|
109
118
|
timestamp,
|
|
110
119
|
duration,
|
|
111
|
-
cts:
|
|
112
|
-
dts:
|
|
113
|
-
trackId:
|
|
114
|
-
type:
|
|
120
|
+
cts: samplesWithIndex.samplePosition.cts,
|
|
121
|
+
dts: samplesWithIndex.samplePosition.dts,
|
|
122
|
+
trackId: samplesWithIndex.track.trackId,
|
|
123
|
+
type: samplesWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
|
|
115
124
|
});
|
|
116
125
|
}
|
|
117
126
|
|
|
@@ -7,6 +7,8 @@ export interface MdhdBox {
|
|
|
7
7
|
duration: number;
|
|
8
8
|
language: number;
|
|
9
9
|
quality: number;
|
|
10
|
+
creationTime: number | null;
|
|
11
|
+
modificationTime: number | null;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export const parseMdhd = ({
|
|
@@ -19,21 +21,20 @@ export const parseMdhd = ({
|
|
|
19
21
|
fileOffset: number;
|
|
20
22
|
}): MdhdBox => {
|
|
21
23
|
const version = data.getUint8();
|
|
22
|
-
if (version !== 0) {
|
|
23
|
-
throw new Error(`Unsupported MDHD version ${version}`);
|
|
24
|
-
}
|
|
25
24
|
|
|
26
25
|
// flags, we discard them
|
|
27
26
|
data.discard(3);
|
|
28
27
|
|
|
29
28
|
// creation time
|
|
30
|
-
|
|
29
|
+
const creationTime =
|
|
30
|
+
version === 1 ? Number(data.getUint64()) : data.getUint32();
|
|
31
31
|
|
|
32
32
|
// modification time
|
|
33
|
-
|
|
33
|
+
const modificationTime =
|
|
34
|
+
version === 1 ? Number(data.getUint64()) : data.getUint32();
|
|
34
35
|
|
|
35
36
|
const timescale = data.getUint32();
|
|
36
|
-
const duration = data.getUint32();
|
|
37
|
+
const duration = version === 1 ? data.getUint64() : data.getUint32();
|
|
37
38
|
|
|
38
39
|
const language = data.getUint16();
|
|
39
40
|
|
|
@@ -47,10 +48,12 @@ export const parseMdhd = ({
|
|
|
47
48
|
|
|
48
49
|
return {
|
|
49
50
|
type: 'mdhd-box',
|
|
50
|
-
duration,
|
|
51
|
+
duration: Number(duration),
|
|
51
52
|
timescale,
|
|
52
53
|
version,
|
|
53
54
|
language,
|
|
54
55
|
quality,
|
|
56
|
+
creationTime,
|
|
57
|
+
modificationTime,
|
|
55
58
|
};
|
|
56
59
|
};
|
|
@@ -38,25 +38,21 @@ export const parseMvhd = ({
|
|
|
38
38
|
size: number;
|
|
39
39
|
}): MvhdBox => {
|
|
40
40
|
const version = iterator.getUint8();
|
|
41
|
-
if (version !== 0) {
|
|
42
|
-
throw new Error(`Unsupported MVHD version ${version}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (size !== 108) {
|
|
46
|
-
throw new Error(`Expected mvhd size of version 0 to be 108, got ${size}`);
|
|
47
|
-
}
|
|
48
41
|
|
|
49
42
|
// Flags, we discard them
|
|
50
43
|
iterator.discard(3);
|
|
51
44
|
|
|
52
|
-
const creationTime =
|
|
45
|
+
const creationTime =
|
|
46
|
+
version === 1 ? iterator.getUint64() : iterator.getUint32();
|
|
53
47
|
|
|
54
|
-
const modificationTime =
|
|
48
|
+
const modificationTime =
|
|
49
|
+
version === 1 ? iterator.getUint64() : iterator.getUint32();
|
|
55
50
|
|
|
56
51
|
const timeScale = iterator.getUint32();
|
|
57
52
|
|
|
58
|
-
const durationInUnits =
|
|
59
|
-
|
|
53
|
+
const durationInUnits =
|
|
54
|
+
version === 1 ? iterator.getUint64() : iterator.getUint32();
|
|
55
|
+
const durationInSeconds = Number(durationInUnits) / timeScale;
|
|
60
56
|
|
|
61
57
|
const rateArray = iterator.getSlice(4);
|
|
62
58
|
const rateView = getArrayBufferIterator(rateArray, rateArray.length);
|
|
@@ -96,11 +92,16 @@ export const parseMvhd = ({
|
|
|
96
92
|
|
|
97
93
|
volumeView.destroy();
|
|
98
94
|
|
|
95
|
+
const bytesRemaining = size - (iterator.counter.getOffset() - offset);
|
|
96
|
+
if (bytesRemaining !== 0) {
|
|
97
|
+
throw new Error('expected 0 bytes ' + bytesRemaining);
|
|
98
|
+
}
|
|
99
|
+
|
|
99
100
|
return {
|
|
100
|
-
creationTime: toUnixTimestamp(creationTime),
|
|
101
|
-
modificationTime: toUnixTimestamp(modificationTime),
|
|
101
|
+
creationTime: toUnixTimestamp(Number(creationTime)),
|
|
102
|
+
modificationTime: toUnixTimestamp(Number(modificationTime)),
|
|
102
103
|
timeScale,
|
|
103
|
-
durationInUnits,
|
|
104
|
+
durationInUnits: Number(durationInUnits),
|
|
104
105
|
durationInSeconds,
|
|
105
106
|
rate,
|
|
106
107
|
volume,
|