@remotion/media-parser 4.0.191 → 4.0.192
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/boxes/iso-base-media/mdhd.d.ts +14 -0
- package/dist/boxes/iso-base-media/mdhd.js +33 -0
- package/dist/boxes/iso-base-media/process-box.js +13 -0
- package/dist/boxes/iso-base-media/stsd/samples.js +1 -0
- package/dist/boxes/webm/parse-webm-header.js +1 -1
- package/dist/boxes/webm/segments/track-entry.d.ts +10 -0
- package/dist/boxes/webm/segments/track-entry.js +23 -7
- package/dist/boxes/webm/segments.d.ts +2 -2
- package/dist/boxes/webm/segments.js +6 -0
- package/dist/buffer-iterator.js +2 -0
- package/dist/get-codec.d.ts +4 -0
- package/dist/get-codec.js +22 -0
- package/dist/get-fps.d.ts +7 -0
- package/dist/get-fps.js +84 -9
- package/dist/get-video-codec.d.ts +4 -0
- package/dist/get-video-codec.js +76 -0
- package/dist/has-all-info.d.ts +1 -1
- package/dist/has-all-info.js +4 -0
- package/dist/options.d.ts +7 -3
- package/dist/parse-media.js +4 -0
- package/dist/parse-result.d.ts +2 -1
- package/package.json +2 -2
- package/src/boxes/iso-base-media/mdhd.ts +56 -0
- package/src/boxes/iso-base-media/process-box.ts +15 -0
- package/src/boxes/iso-base-media/stsd/samples.ts +1 -0
- package/src/boxes/webm/parse-webm-header.ts +1 -1
- package/src/boxes/webm/segments/track-entry.ts +37 -10
- package/src/boxes/webm/segments.ts +15 -1
- package/src/buffer-iterator.ts +2 -0
- package/src/get-fps.ts +127 -9
- package/src/get-video-codec.ts +100 -0
- package/src/has-all-info.ts +7 -2
- package/src/options.ts +28 -3
- package/src/parse-media.ts +6 -1
- package/src/parse-result.ts +3 -1
- package/src/test/matroska.test.ts +5 -5
- package/src/test/parse-webm.test.ts +2 -0
- package/src/test/stream-local.test.ts +16 -5
- package/src/test/stream-remote.test.ts +41 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BufferIterator } from '../../buffer-iterator';
|
|
2
|
+
export interface MdhdBox {
|
|
3
|
+
type: 'mdhd-box';
|
|
4
|
+
version: number;
|
|
5
|
+
timescale: number;
|
|
6
|
+
duration: number;
|
|
7
|
+
language: number;
|
|
8
|
+
quality: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const parseMdhd: ({ data, size, fileOffset, }: {
|
|
11
|
+
data: BufferIterator;
|
|
12
|
+
size: number;
|
|
13
|
+
fileOffset: number;
|
|
14
|
+
}) => MdhdBox;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMdhd = void 0;
|
|
4
|
+
const parseMdhd = ({ data, size, fileOffset, }) => {
|
|
5
|
+
const version = data.getUint8();
|
|
6
|
+
if (version !== 0) {
|
|
7
|
+
throw new Error(`Unsupported MDHD version ${version}`);
|
|
8
|
+
}
|
|
9
|
+
// flags, we discard them
|
|
10
|
+
data.discard(3);
|
|
11
|
+
// creation time
|
|
12
|
+
data.discard(4);
|
|
13
|
+
// modification time
|
|
14
|
+
data.discard(4);
|
|
15
|
+
const timescale = data.getUint32();
|
|
16
|
+
const duration = data.getUint32();
|
|
17
|
+
const language = data.getUint16();
|
|
18
|
+
// quality
|
|
19
|
+
const quality = data.getUint16();
|
|
20
|
+
const remaining = size - (data.counter.getOffset() - fileOffset);
|
|
21
|
+
if (remaining !== 0) {
|
|
22
|
+
throw new Error(`Expected remaining bytes to be 0, got ${remaining}`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
type: 'mdhd-box',
|
|
26
|
+
duration,
|
|
27
|
+
timescale,
|
|
28
|
+
version,
|
|
29
|
+
language,
|
|
30
|
+
quality,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
exports.parseMdhd = parseMdhd;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseBoxes = void 0;
|
|
4
4
|
const ftyp_1 = require("./ftyp");
|
|
5
|
+
const mdhd_1 = require("./mdhd");
|
|
5
6
|
const moov_1 = require("./moov/moov");
|
|
6
7
|
const mvhd_1 = require("./mvhd");
|
|
7
8
|
const mebx_1 = require("./stsd/mebx");
|
|
@@ -119,6 +120,18 @@ const processBox = ({ iterator, allowIncompleteBoxes, }) => {
|
|
|
119
120
|
size: boxSize,
|
|
120
121
|
};
|
|
121
122
|
}
|
|
123
|
+
if (boxType === 'mdhd') {
|
|
124
|
+
const box = (0, mdhd_1.parseMdhd)({
|
|
125
|
+
data: iterator,
|
|
126
|
+
size: boxSize,
|
|
127
|
+
fileOffset,
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
type: 'complete',
|
|
131
|
+
box,
|
|
132
|
+
size: boxSize,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
122
135
|
const bytesRemainingInBox = boxSize - (iterator.counter.getOffset() - fileOffset);
|
|
123
136
|
const children = getChildren({
|
|
124
137
|
boxType,
|
|
@@ -5,7 +5,7 @@ const segments_1 = require("./segments");
|
|
|
5
5
|
// Parsing according to https://darkcoding.net/software/reading-mediarecorders-webm-opus-output/
|
|
6
6
|
const parseWebm = (counter) => {
|
|
7
7
|
counter.discard(4);
|
|
8
|
-
const length = counter.
|
|
8
|
+
const length = counter.getVint();
|
|
9
9
|
if (length !== 31) {
|
|
10
10
|
throw new Error(`Expected header length 31, got ${length}`);
|
|
11
11
|
}
|
|
@@ -69,3 +69,13 @@ export type ColorSegment = {
|
|
|
69
69
|
type: 'color-segment';
|
|
70
70
|
};
|
|
71
71
|
export declare const parseColorSegment: (iterator: BufferIterator) => ColorSegment;
|
|
72
|
+
export type TitleSegment = {
|
|
73
|
+
type: 'title-segment';
|
|
74
|
+
title: string;
|
|
75
|
+
};
|
|
76
|
+
export declare const parseTitleSegment: (iterator: BufferIterator) => TitleSegment;
|
|
77
|
+
export type InterlacedSegment = {
|
|
78
|
+
type: 'interlaced-segment';
|
|
79
|
+
interlaced: boolean;
|
|
80
|
+
};
|
|
81
|
+
export declare const parseInterlacedSegment: (iterator: BufferIterator) => InterlacedSegment;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseColorSegment = exports.parseMaxBlockAdditionId = exports.parseAlphaModeSegment = exports.parseHeightSegment = exports.parseWidthSegment = exports.parseVideoSegment = exports.parseDefaultDurationSegment = exports.parseTrackTypeSegment = exports.parseCodecSegment = exports.parseLanguageSegment = exports.parseFlagLacing = exports.parseTrackUID = exports.parseTrackNumber = exports.parseTrackEntry = void 0;
|
|
3
|
+
exports.parseInterlacedSegment = exports.parseTitleSegment = exports.parseColorSegment = exports.parseMaxBlockAdditionId = exports.parseAlphaModeSegment = exports.parseHeightSegment = exports.parseWidthSegment = exports.parseVideoSegment = exports.parseDefaultDurationSegment = exports.parseTrackTypeSegment = exports.parseCodecSegment = exports.parseLanguageSegment = exports.parseFlagLacing = exports.parseTrackUID = exports.parseTrackNumber = exports.parseTrackEntry = void 0;
|
|
4
4
|
const parse_children_1 = require("./parse-children");
|
|
5
5
|
const parseTrackEntry = (iterator) => {
|
|
6
6
|
const offset = iterator.counter.getOffset();
|
|
@@ -25,10 +25,6 @@ const parseTrackNumber = (iterator) => {
|
|
|
25
25
|
exports.parseTrackNumber = parseTrackNumber;
|
|
26
26
|
const parseTrackUID = (iterator) => {
|
|
27
27
|
const length = iterator.getVint();
|
|
28
|
-
// Observation: AV1 has 8 bytes, WebM has 7
|
|
29
|
-
if (length !== 8 && length !== 7) {
|
|
30
|
-
throw new Error('Expected track number to be 8 byte');
|
|
31
|
-
}
|
|
32
28
|
const bytes = iterator.getSlice(length);
|
|
33
29
|
const asString = [...bytes]
|
|
34
30
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
@@ -109,11 +105,10 @@ const parseDefaultDurationSegment = (iterator) => {
|
|
|
109
105
|
};
|
|
110
106
|
exports.parseDefaultDurationSegment = parseDefaultDurationSegment;
|
|
111
107
|
const parseVideoSegment = (iterator) => {
|
|
112
|
-
const offset = iterator.counter.getOffset();
|
|
113
108
|
const length = iterator.getVint();
|
|
114
109
|
return {
|
|
115
110
|
type: 'video-segment',
|
|
116
|
-
children: (0, parse_children_1.expectChildren)(iterator, length
|
|
111
|
+
children: (0, parse_children_1.expectChildren)(iterator, length),
|
|
117
112
|
};
|
|
118
113
|
};
|
|
119
114
|
exports.parseVideoSegment = parseVideoSegment;
|
|
@@ -173,3 +168,24 @@ const parseColorSegment = (iterator) => {
|
|
|
173
168
|
};
|
|
174
169
|
};
|
|
175
170
|
exports.parseColorSegment = parseColorSegment;
|
|
171
|
+
const parseTitleSegment = (iterator) => {
|
|
172
|
+
const length = iterator.getVint();
|
|
173
|
+
const title = iterator.getByteString(length);
|
|
174
|
+
return {
|
|
175
|
+
type: 'title-segment',
|
|
176
|
+
title,
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
exports.parseTitleSegment = parseTitleSegment;
|
|
180
|
+
const parseInterlacedSegment = (iterator) => {
|
|
181
|
+
const length = iterator.getVint();
|
|
182
|
+
if (length !== 1) {
|
|
183
|
+
throw new Error('Expected interlaced segment to be 1 byte');
|
|
184
|
+
}
|
|
185
|
+
const interlaced = iterator.getUint8();
|
|
186
|
+
return {
|
|
187
|
+
type: 'interlaced-segment',
|
|
188
|
+
interlaced: Boolean(interlaced),
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
exports.parseInterlacedSegment = parseInterlacedSegment;
|
|
@@ -7,10 +7,10 @@ import { type SeekSegment } from './segments/seek';
|
|
|
7
7
|
import type { SeekHeadSegment } from './segments/seek-head';
|
|
8
8
|
import { type SeekPositionSegment } from './segments/seek-position';
|
|
9
9
|
import type { TimestampScaleSegment } from './segments/timestamp-scale';
|
|
10
|
-
import type { AlphaModeSegment, CodecSegment, ColorSegment, DefaultDurationSegment, FlagLacingSegment, HeightSegment, LanguageSegment, MaxBlockAdditionId, TrackEntrySegment, TrackNumberSegment, TrackTypeSegment, TrackUIDSegment, VideoSegment, WidthSegment } from './segments/track-entry';
|
|
10
|
+
import type { AlphaModeSegment, CodecSegment, ColorSegment, DefaultDurationSegment, FlagLacingSegment, HeightSegment, InterlacedSegment, LanguageSegment, MaxBlockAdditionId, TitleSegment, TrackEntrySegment, TrackNumberSegment, TrackTypeSegment, TrackUIDSegment, VideoSegment, WidthSegment } from './segments/track-entry';
|
|
11
11
|
import type { TracksSegment } from './segments/tracks';
|
|
12
12
|
import type { UnknownSegment } from './segments/unknown';
|
|
13
13
|
import type { VoidSegment } from './segments/void';
|
|
14
14
|
import type { WritingAppSegment } from './segments/writing';
|
|
15
|
-
export type MatroskaSegment = MainSegment | UnknownSegment | SeekHeadSegment | SeekSegment | SeekPositionSegment | VoidSegment | InfoSegment | TimestampScaleSegment | MuxingAppSegment | WritingAppSegment | DurationSegment | TracksSegment | TrackEntrySegment | TrackNumberSegment | TrackUIDSegment | FlagLacingSegment | LanguageSegment | CodecSegment | TrackTypeSegment | DefaultDurationSegment | VideoSegment | WidthSegment | HeightSegment | AlphaModeSegment | MaxBlockAdditionId | ColorSegment;
|
|
15
|
+
export type MatroskaSegment = MainSegment | UnknownSegment | SeekHeadSegment | SeekSegment | SeekPositionSegment | VoidSegment | InfoSegment | TimestampScaleSegment | MuxingAppSegment | WritingAppSegment | DurationSegment | TracksSegment | TrackEntrySegment | TrackNumberSegment | TrackUIDSegment | FlagLacingSegment | LanguageSegment | CodecSegment | TrackTypeSegment | DefaultDurationSegment | VideoSegment | WidthSegment | HeightSegment | AlphaModeSegment | MaxBlockAdditionId | ColorSegment | TitleSegment | InterlacedSegment;
|
|
16
16
|
export declare const expectSegment: (iterator: BufferIterator) => MatroskaSegment;
|
|
@@ -94,9 +94,15 @@ const expectSegment = (iterator) => {
|
|
|
94
94
|
if (segmentId === '0xba') {
|
|
95
95
|
return (0, track_entry_1.parseHeightSegment)(iterator);
|
|
96
96
|
}
|
|
97
|
+
if (segmentId === '0x9a') {
|
|
98
|
+
return (0, track_entry_1.parseInterlacedSegment)(iterator);
|
|
99
|
+
}
|
|
97
100
|
if (segmentId === '0x53c0') {
|
|
98
101
|
return (0, track_entry_1.parseAlphaModeSegment)(iterator);
|
|
99
102
|
}
|
|
103
|
+
if (segmentId === '0x7ba9') {
|
|
104
|
+
return (0, track_entry_1.parseTitleSegment)(iterator);
|
|
105
|
+
}
|
|
100
106
|
const length = iterator.getVint();
|
|
101
107
|
const bytesRemaining = iterator.byteLength() - iterator.counter.getOffset();
|
|
102
108
|
const toDiscard = Math.min(bytesRemaining, length > 0 ? length : bytesRemaining);
|
package/dist/buffer-iterator.js
CHANGED
|
@@ -139,6 +139,7 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
139
139
|
'0xe0',
|
|
140
140
|
'0xb0',
|
|
141
141
|
'0xba',
|
|
142
|
+
'0x9a',
|
|
142
143
|
];
|
|
143
144
|
if (knownIdsWithOneLength.includes(firstOneString)) {
|
|
144
145
|
return firstOneString;
|
|
@@ -155,6 +156,7 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
155
156
|
'0x4489',
|
|
156
157
|
'0x55ee',
|
|
157
158
|
'0x55b0',
|
|
159
|
+
'0x7ba9',
|
|
158
160
|
];
|
|
159
161
|
const firstTwoString = `${firstOneString}${Array.from(new Uint8Array(firstTwo))
|
|
160
162
|
.map((b) => {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVideoCodec = exports.hasVideoCodec = void 0;
|
|
4
|
+
const hasVideoCodec = (boxes) => {
|
|
5
|
+
try {
|
|
6
|
+
return boxes.some((b) => b.type === 'ftyp-box');
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
exports.hasVideoCodec = hasVideoCodec;
|
|
13
|
+
const getVideoCodec = (boxes) => {
|
|
14
|
+
const ftypBox = boxes.find((b) => b.type === 'ftyp-box');
|
|
15
|
+
if (ftypBox && ftypBox.type === 'ftyp-box') {
|
|
16
|
+
if (ftypBox.compatibleBrands.find((b) => b === 'avc1')) {
|
|
17
|
+
return 'h264';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
exports.getVideoCodec = getVideoCodec;
|
package/dist/get-fps.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import type { AnySegment } from './parse-result';
|
|
2
|
+
type TimescaleAndDuration = {
|
|
3
|
+
timescale: number;
|
|
4
|
+
duration: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const trakBoxContainsVideo: (trakBox: AnySegment) => boolean;
|
|
7
|
+
export declare const getTimescaleAndDuration: (boxes: AnySegment[]) => TimescaleAndDuration | null;
|
|
2
8
|
export declare const getFps: (segments: AnySegment[]) => number | null;
|
|
3
9
|
export declare const hasFps: (boxes: AnySegment[]) => boolean;
|
|
10
|
+
export {};
|
package/dist/get-fps.js
CHANGED
|
@@ -1,16 +1,89 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasFps = exports.getFps = void 0;
|
|
4
|
-
const calculateFps = (sttsBox, timeScale) => {
|
|
5
|
-
let sum = 0;
|
|
3
|
+
exports.hasFps = exports.getFps = exports.getTimescaleAndDuration = exports.trakBoxContainsVideo = void 0;
|
|
4
|
+
const calculateFps = ({ sttsBox, timeScale, durationInSamples, }) => {
|
|
6
5
|
let totalSamples = 0;
|
|
7
6
|
for (const sample of sttsBox.sampleDistribution) {
|
|
8
|
-
sum += sample.sampleCount * sample.sampleDelta;
|
|
9
7
|
totalSamples += sample.sampleCount;
|
|
10
8
|
}
|
|
11
|
-
|
|
9
|
+
const durationInSeconds = durationInSamples / timeScale;
|
|
10
|
+
const fps = totalSamples / durationInSeconds;
|
|
11
|
+
return fps;
|
|
12
12
|
};
|
|
13
|
+
const trakBoxContainsVideo = (trakBox) => {
|
|
14
|
+
if (trakBox.type !== 'trak-box') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const { children } = trakBox;
|
|
18
|
+
const mediaBoxes = children.filter((c) => c.type === 'regular-box' && c.boxType === 'mdia');
|
|
19
|
+
if (!mediaBoxes || mediaBoxes.length === 0) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const firstMediaBox = mediaBoxes[0];
|
|
23
|
+
if (firstMediaBox.type !== 'regular-box' ||
|
|
24
|
+
firstMediaBox.boxType !== 'mdia') {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const minf = firstMediaBox.children.find((c) => c.type === 'regular-box' && c.boxType === 'minf');
|
|
28
|
+
if (!minf || minf.type !== 'regular-box' || minf.boxType !== 'minf') {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const stbl = minf.children.find((c) => c.type === 'regular-box' && c.boxType === 'stbl');
|
|
32
|
+
if (!stbl || stbl.type !== 'regular-box' || stbl.boxType !== 'stbl') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const stsd = stbl.children.find((c) => c.type === 'stsd-box');
|
|
36
|
+
if (!stsd || stsd.type !== 'stsd-box') {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const videoSample = stsd.samples.find((s) => s.type === 'video');
|
|
40
|
+
if (!videoSample || videoSample.type !== 'video') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
exports.trakBoxContainsVideo = trakBoxContainsVideo;
|
|
46
|
+
const getTimescaleAndDuration = (boxes) => {
|
|
47
|
+
const moovBox = boxes.find((s) => s.type === 'moov-box');
|
|
48
|
+
if (!moovBox || moovBox.type !== 'moov-box') {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const { children } = moovBox;
|
|
52
|
+
const trackBoxes = children.filter((c) => c.type === 'trak-box');
|
|
53
|
+
if (!trackBoxes || trackBoxes.length === 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const trackBox = trackBoxes.find(exports.trakBoxContainsVideo);
|
|
57
|
+
if (!trackBox || trackBox.type !== 'trak-box') {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const trackBoxChildren = trackBox.children;
|
|
61
|
+
if (!trackBoxChildren || trackBoxChildren.length === 0) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const mdiaBox = trackBoxChildren.find((c) => c.type === 'regular-box' && c.boxType === 'mdia');
|
|
65
|
+
if (!mdiaBox ||
|
|
66
|
+
mdiaBox.type !== 'regular-box' ||
|
|
67
|
+
mdiaBox.boxType !== 'mdia') {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const mdhdBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((c) => c.type === 'mdhd-box');
|
|
71
|
+
if (mdhdBox && mdhdBox.type === 'mdhd-box') {
|
|
72
|
+
return { timescale: mdhdBox.timescale, duration: mdhdBox.duration };
|
|
73
|
+
}
|
|
74
|
+
const mvhdBox = moovBox.children.find((c) => c.type === 'mvhd-box');
|
|
75
|
+
if (!mvhdBox || mvhdBox.type !== 'mvhd-box') {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const { timeScale, durationInUnits } = mvhdBox;
|
|
79
|
+
return { timescale: timeScale, duration: durationInUnits };
|
|
80
|
+
};
|
|
81
|
+
exports.getTimescaleAndDuration = getTimescaleAndDuration;
|
|
13
82
|
const getFps = (segments) => {
|
|
83
|
+
const timescaleAndDuration = (0, exports.getTimescaleAndDuration)(segments);
|
|
84
|
+
if (!timescaleAndDuration) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
14
87
|
const moovBox = segments.find((s) => s.type === 'moov-box');
|
|
15
88
|
if (!moovBox || moovBox.type !== 'moov-box') {
|
|
16
89
|
return null;
|
|
@@ -19,14 +92,12 @@ const getFps = (segments) => {
|
|
|
19
92
|
if (!mvhdBox || mvhdBox.type !== 'mvhd-box') {
|
|
20
93
|
return null;
|
|
21
94
|
}
|
|
22
|
-
const { timeScale } = mvhdBox;
|
|
23
95
|
const { children } = moovBox;
|
|
24
96
|
const trackBoxes = children.filter((c) => c.type === 'trak-box');
|
|
25
97
|
if (!trackBoxes || trackBoxes.length === 0) {
|
|
26
98
|
return null;
|
|
27
99
|
}
|
|
28
|
-
|
|
29
|
-
const trackBox = trackBoxes[0];
|
|
100
|
+
const trackBox = trackBoxes.find(exports.trakBoxContainsVideo);
|
|
30
101
|
if (!trackBox || trackBox.type !== 'trak-box') {
|
|
31
102
|
return null;
|
|
32
103
|
}
|
|
@@ -56,7 +127,11 @@ const getFps = (segments) => {
|
|
|
56
127
|
if (!sttsBox || sttsBox.type !== 'stts-box') {
|
|
57
128
|
return null;
|
|
58
129
|
}
|
|
59
|
-
return calculateFps(
|
|
130
|
+
return calculateFps({
|
|
131
|
+
sttsBox,
|
|
132
|
+
timeScale: timescaleAndDuration.timescale,
|
|
133
|
+
durationInSamples: timescaleAndDuration.duration,
|
|
134
|
+
});
|
|
60
135
|
};
|
|
61
136
|
exports.getFps = getFps;
|
|
62
137
|
const hasFps = (boxes) => {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVideoCodec = exports.hasVideoCodec = void 0;
|
|
4
|
+
/* eslint-disable max-depth */
|
|
5
|
+
const get_fps_1 = require("./get-fps");
|
|
6
|
+
const hasVideoCodec = (boxes) => {
|
|
7
|
+
try {
|
|
8
|
+
return (0, exports.getVideoCodec)(boxes) !== null;
|
|
9
|
+
}
|
|
10
|
+
catch (e) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
exports.hasVideoCodec = hasVideoCodec;
|
|
15
|
+
const getVideoCodec = (boxes) => {
|
|
16
|
+
const moovBox = boxes.find((b) => b.type === 'moov-box');
|
|
17
|
+
if (moovBox && moovBox.type === 'moov-box') {
|
|
18
|
+
const trakBox = moovBox.children.find((b) => b.type === 'trak-box' && (0, get_fps_1.trakBoxContainsVideo)(b));
|
|
19
|
+
if (trakBox && trakBox.type === 'trak-box') {
|
|
20
|
+
const mdiaBox = trakBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'mdia');
|
|
21
|
+
if (mdiaBox &&
|
|
22
|
+
mdiaBox.type === 'regular-box' &&
|
|
23
|
+
mdiaBox.boxType === 'mdia') {
|
|
24
|
+
const minfBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'minf');
|
|
25
|
+
if (minfBox &&
|
|
26
|
+
minfBox.type === 'regular-box' &&
|
|
27
|
+
minfBox.boxType === 'minf') {
|
|
28
|
+
const stblBox = minfBox === null || minfBox === void 0 ? void 0 : minfBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'stbl');
|
|
29
|
+
if (stblBox && stblBox.type === 'regular-box') {
|
|
30
|
+
const stsdBox = stblBox === null || stblBox === void 0 ? void 0 : stblBox.children.find((b) => b.type === 'stsd-box');
|
|
31
|
+
if (stsdBox && stsdBox.type === 'stsd-box') {
|
|
32
|
+
const videoSample = stsdBox.samples.find((s) => s.type === 'video');
|
|
33
|
+
if (videoSample && videoSample.type === 'video') {
|
|
34
|
+
if (videoSample.format === 'hvc1') {
|
|
35
|
+
return 'h265';
|
|
36
|
+
}
|
|
37
|
+
if (videoSample.format === 'avc1') {
|
|
38
|
+
return 'h264';
|
|
39
|
+
}
|
|
40
|
+
if (videoSample.format === 'ap4h') {
|
|
41
|
+
return 'prores';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const mainSegment = boxes.find((b) => b.type === 'main-segment');
|
|
51
|
+
if (!mainSegment || mainSegment.type !== 'main-segment') {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const tracksSegment = mainSegment.children.find((b) => b.type === 'tracks-segment');
|
|
55
|
+
if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
for (const track of tracksSegment.children) {
|
|
59
|
+
if (track.type === 'track-entry-segment') {
|
|
60
|
+
const trackType = track.children.find((b) => b.type === 'codec-segment');
|
|
61
|
+
if (trackType && trackType.type === 'codec-segment') {
|
|
62
|
+
if (trackType.codec === 'V_VP8') {
|
|
63
|
+
return 'vp8';
|
|
64
|
+
}
|
|
65
|
+
if (trackType.codec === 'V_VP9') {
|
|
66
|
+
return 'vp9';
|
|
67
|
+
}
|
|
68
|
+
if (trackType.codec === 'V_AV1') {
|
|
69
|
+
return 'av1';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
exports.getVideoCodec = getVideoCodec;
|
package/dist/has-all-info.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Options } from './options';
|
|
2
2
|
import type { ParseResult } from './parse-result';
|
|
3
|
-
export declare const hasAllInfo: (options: Options<boolean, boolean, boolean, boolean>, parseResult: ParseResult) => boolean;
|
|
3
|
+
export declare const hasAllInfo: (options: Options<boolean, boolean, boolean, boolean, boolean>, parseResult: ParseResult) => boolean;
|
package/dist/has-all-info.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.hasAllInfo = void 0;
|
|
|
4
4
|
const get_dimensions_1 = require("./get-dimensions");
|
|
5
5
|
const get_duration_1 = require("./get-duration");
|
|
6
6
|
const get_fps_1 = require("./get-fps");
|
|
7
|
+
const get_video_codec_1 = require("./get-video-codec");
|
|
7
8
|
const hasAllInfo = (options, parseResult) => {
|
|
8
9
|
const keys = Object.entries(options)
|
|
9
10
|
.filter(([, value]) => value)
|
|
@@ -21,6 +22,9 @@ const hasAllInfo = (options, parseResult) => {
|
|
|
21
22
|
if (key === 'fps') {
|
|
22
23
|
return (0, get_fps_1.hasFps)(parseResult.segments) !== null;
|
|
23
24
|
}
|
|
25
|
+
if (key === 'videoCodec') {
|
|
26
|
+
return (0, get_video_codec_1.hasVideoCodec)(parseResult.segments) !== null;
|
|
27
|
+
}
|
|
24
28
|
throw new Error(`Unknown key: ${key}`);
|
|
25
29
|
});
|
|
26
30
|
};
|
package/dist/options.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { Dimensions } from './get-dimensions';
|
|
2
2
|
import type { AnySegment } from './parse-result';
|
|
3
3
|
import type { ReaderInterface } from './reader';
|
|
4
|
-
export type
|
|
4
|
+
export type KnownVideoCodecs = 'h264' | 'h265' | 'vp8' | 'vp9' | 'av1' | 'prores';
|
|
5
|
+
export type Options<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean> = {
|
|
5
6
|
dimensions?: EnableDimensions;
|
|
6
7
|
durationInSeconds?: EnableDuration;
|
|
7
8
|
boxes?: EnableBoxes;
|
|
8
9
|
fps?: EnableFps;
|
|
10
|
+
videoCodec?: EnableVideoCodec;
|
|
9
11
|
};
|
|
10
|
-
export type Metadata<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean> = (EnableDimensions extends true ? {
|
|
12
|
+
export type Metadata<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean> = (EnableDimensions extends true ? {
|
|
11
13
|
dimensions: Dimensions;
|
|
12
14
|
} : {}) & (EnableDuration extends true ? {
|
|
13
15
|
durationInSeconds: number | null;
|
|
@@ -15,5 +17,7 @@ export type Metadata<EnableDimensions extends boolean, EnableDuration extends bo
|
|
|
15
17
|
boxes: AnySegment[];
|
|
16
18
|
} : {}) & (EnableFps extends true ? {
|
|
17
19
|
fps: number | null;
|
|
20
|
+
} : {}) & (EnableVideoCodec extends true ? {
|
|
21
|
+
videoCodec: KnownVideoCodecs | null;
|
|
18
22
|
} : {});
|
|
19
|
-
export type ParseMedia = <EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean>(src: string, options: Options<EnableDimensions, EnableDuration, EnableBoxes, EnableFps>, readerInterface?: ReaderInterface) => Promise<Metadata<EnableDimensions, EnableDuration, EnableBoxes, EnableFps>>;
|
|
23
|
+
export type ParseMedia = <EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean>(src: string, options: Options<EnableDimensions, EnableDuration, EnableBoxes, EnableFps, EnableVideoCodec>, readerInterface?: ReaderInterface) => Promise<Metadata<EnableDimensions, EnableDuration, EnableBoxes, EnableFps, EnableVideoCodec>>;
|
package/dist/parse-media.js
CHANGED
|
@@ -6,6 +6,7 @@ const from_web_1 = require("./from-web");
|
|
|
6
6
|
const get_dimensions_1 = require("./get-dimensions");
|
|
7
7
|
const get_duration_1 = require("./get-duration");
|
|
8
8
|
const get_fps_1 = require("./get-fps");
|
|
9
|
+
const get_video_codec_1 = require("./get-video-codec");
|
|
9
10
|
const has_all_info_1 = require("./has-all-info");
|
|
10
11
|
const parse_video_1 = require("./parse-video");
|
|
11
12
|
const parseMedia = async (src, options, readerInterface = from_web_1.webReader) => {
|
|
@@ -36,6 +37,9 @@ const parseMedia = async (src, options, readerInterface = from_web_1.webReader)
|
|
|
36
37
|
if (options.fps) {
|
|
37
38
|
returnValue.fps = (0, get_fps_1.getFps)(parseResult.segments);
|
|
38
39
|
}
|
|
40
|
+
if (options.videoCodec) {
|
|
41
|
+
returnValue.videoCodec = (0, get_video_codec_1.getVideoCodec)(parseResult.segments);
|
|
42
|
+
}
|
|
39
43
|
if (options.boxes) {
|
|
40
44
|
returnValue.boxes = parseResult.segments;
|
|
41
45
|
}
|
package/dist/parse-result.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BaseBox } from './boxes/iso-base-media/base-type';
|
|
2
2
|
import type { FtypBox } from './boxes/iso-base-media/ftyp';
|
|
3
|
+
import type { MdhdBox } from './boxes/iso-base-media/mdhd';
|
|
3
4
|
import type { MoovBox } from './boxes/iso-base-media/moov/moov';
|
|
4
5
|
import type { MvhdBox } from './boxes/iso-base-media/mvhd';
|
|
5
6
|
import type { KeysBox } from './boxes/iso-base-media/stsd/keys';
|
|
@@ -16,7 +17,7 @@ interface RegularBox extends BaseBox {
|
|
|
16
17
|
offset: number;
|
|
17
18
|
type: 'regular-box';
|
|
18
19
|
}
|
|
19
|
-
export type IsoBaseMediaBox = RegularBox | FtypBox | MvhdBox | TkhdBox | StsdBox | MebxBox | KeysBox | MoovBox | TrakBox | SttsBox;
|
|
20
|
+
export type IsoBaseMediaBox = RegularBox | FtypBox | MvhdBox | TkhdBox | StsdBox | MebxBox | KeysBox | MoovBox | TrakBox | SttsBox | MdhdBox;
|
|
20
21
|
export type AnySegment = MatroskaSegment | IsoBaseMediaBox;
|
|
21
22
|
export type ParseResult = {
|
|
22
23
|
status: 'done';
|
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.192",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@remotion/renderer": "4.0.
|
|
10
|
+
"@remotion/renderer": "4.0.192"
|
|
11
11
|
},
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type {BufferIterator} from '../../buffer-iterator';
|
|
2
|
+
|
|
3
|
+
export interface MdhdBox {
|
|
4
|
+
type: 'mdhd-box';
|
|
5
|
+
version: number;
|
|
6
|
+
timescale: number;
|
|
7
|
+
duration: number;
|
|
8
|
+
language: number;
|
|
9
|
+
quality: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const parseMdhd = ({
|
|
13
|
+
data,
|
|
14
|
+
size,
|
|
15
|
+
fileOffset,
|
|
16
|
+
}: {
|
|
17
|
+
data: BufferIterator;
|
|
18
|
+
size: number;
|
|
19
|
+
fileOffset: number;
|
|
20
|
+
}): MdhdBox => {
|
|
21
|
+
const version = data.getUint8();
|
|
22
|
+
if (version !== 0) {
|
|
23
|
+
throw new Error(`Unsupported MDHD version ${version}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// flags, we discard them
|
|
27
|
+
data.discard(3);
|
|
28
|
+
|
|
29
|
+
// creation time
|
|
30
|
+
data.discard(4);
|
|
31
|
+
|
|
32
|
+
// modification time
|
|
33
|
+
data.discard(4);
|
|
34
|
+
|
|
35
|
+
const timescale = data.getUint32();
|
|
36
|
+
const duration = data.getUint32();
|
|
37
|
+
|
|
38
|
+
const language = data.getUint16();
|
|
39
|
+
|
|
40
|
+
// quality
|
|
41
|
+
const quality = data.getUint16();
|
|
42
|
+
|
|
43
|
+
const remaining = size - (data.counter.getOffset() - fileOffset);
|
|
44
|
+
if (remaining !== 0) {
|
|
45
|
+
throw new Error(`Expected remaining bytes to be 0, got ${remaining}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
type: 'mdhd-box',
|
|
50
|
+
duration,
|
|
51
|
+
timescale,
|
|
52
|
+
version,
|
|
53
|
+
language,
|
|
54
|
+
quality,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
@@ -2,6 +2,7 @@ import type {BufferIterator} from '../../buffer-iterator';
|
|
|
2
2
|
import type {IsoBaseMediaBox, ParseResult} from '../../parse-result';
|
|
3
3
|
import type {BoxAndNext} from '../../parse-video';
|
|
4
4
|
import {parseFtyp} from './ftyp';
|
|
5
|
+
import {parseMdhd} from './mdhd';
|
|
5
6
|
import {parseMoov} from './moov/moov';
|
|
6
7
|
import {parseMvhd} from './mvhd';
|
|
7
8
|
import {parseMebx} from './stsd/mebx';
|
|
@@ -162,6 +163,20 @@ const processBox = ({
|
|
|
162
163
|
};
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
if (boxType === 'mdhd') {
|
|
167
|
+
const box = parseMdhd({
|
|
168
|
+
data: iterator,
|
|
169
|
+
size: boxSize,
|
|
170
|
+
fileOffset,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
type: 'complete',
|
|
175
|
+
box,
|
|
176
|
+
size: boxSize,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
165
180
|
const bytesRemainingInBox =
|
|
166
181
|
boxSize - (iterator.counter.getOffset() - fileOffset);
|
|
167
182
|
|