@remotion/media-parser 4.0.191 → 4.0.193
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/boxes.json +1 -0
- package/dist/boxes/iso-base-media/esds/esds-descriptors.d.ts +21 -0
- package/dist/boxes/iso-base-media/esds/esds-descriptors.js +62 -0
- package/dist/boxes/iso-base-media/esds/esds.d.ts +15 -0
- package/dist/boxes/iso-base-media/esds/esds.js +27 -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/mdhd.d.ts +14 -0
- package/dist/boxes/iso-base-media/mdhd.js +33 -0
- package/dist/boxes/iso-base-media/process-box.js +30 -0
- package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -0
- package/dist/boxes/iso-base-media/stsd/samples.js +28 -8
- package/dist/boxes/webm/parse-webm-header.js +4 -4
- package/dist/boxes/webm/segments/track-entry.d.ts +30 -0
- package/dist/boxes/webm/segments/track-entry.js +59 -8
- package/dist/boxes/webm/segments.d.ts +2 -2
- package/dist/boxes/webm/segments.js +18 -0
- package/dist/buffer-iterator.d.ts +2 -1
- package/dist/buffer-iterator.js +29 -8
- package/dist/from-node.js +6 -2
- package/dist/from-web.js +6 -1
- package/dist/get-audio-codec.d.ts +4 -0
- package/dist/get-audio-codec.js +106 -0
- package/dist/get-dimensions.js +6 -2
- package/dist/get-fps.d.ts +8 -0
- package/dist/get-fps.js +117 -9
- package/dist/get-video-codec.d.ts +4 -0
- package/dist/get-video-codec.js +79 -0
- package/dist/get-video-metadata.d.ts +2 -0
- package/dist/get-video-metadata.js +44 -0
- package/dist/has-all-info.d.ts +1 -1
- package/dist/has-all-info.js +8 -0
- package/dist/options.d.ts +11 -3
- package/dist/parse-media.js +27 -6
- package/dist/parse-result.d.ts +3 -1
- package/dist/read-and-increment-offset.d.ts +28 -0
- package/dist/read-and-increment-offset.js +177 -0
- package/dist/reader.d.ts +5 -1
- package/package.json +2 -2
- package/src/boxes/iso-base-media/esds/esds-descriptors.ts +104 -0
- package/src/boxes/iso-base-media/esds/esds.ts +49 -0
- package/src/boxes/iso-base-media/mdhd.ts +56 -0
- package/src/boxes/iso-base-media/process-box.ts +35 -0
- package/src/boxes/iso-base-media/stsd/samples.ts +36 -8
- package/src/boxes/webm/parse-webm-header.ts +4 -4
- package/src/boxes/webm/segments/track-entry.ts +103 -11
- package/src/boxes/webm/segments.ts +43 -1
- package/src/buffer-iterator.ts +36 -10
- package/src/from-node.ts +6 -4
- package/src/from-web.ts +8 -1
- package/src/get-audio-codec.ts +143 -0
- package/src/get-dimensions.ts +11 -4
- package/src/get-fps.ts +175 -9
- package/src/get-video-codec.ts +104 -0
- package/src/has-all-info.ts +19 -2
- package/src/options.ts +43 -3
- package/src/parse-media.ts +35 -7
- package/src/parse-result.ts +5 -1
- package/src/reader.ts +5 -1
- package/src/test/matroska.test.ts +6 -7
- package/src/test/parse-esds.test.ts +75 -0
- package/src/test/parse-webm.test.ts +2 -0
- package/src/test/stream-local.test.ts +93 -5
- package/src/test/stream-remote.test.ts +41 -0
- package/src/test/stsd.test.ts +52 -5
- package/tsconfig.tsbuildinfo +1 -1
package/dist/buffer-iterator.js
CHANGED
|
@@ -54,8 +54,12 @@ const matchesPattern = (pattern) => {
|
|
|
54
54
|
const makeOffsetCounter = () => {
|
|
55
55
|
return new OffsetCounter(0);
|
|
56
56
|
};
|
|
57
|
-
const getArrayBufferIterator = (initialData) => {
|
|
58
|
-
|
|
57
|
+
const getArrayBufferIterator = (initialData, maxBytes) => {
|
|
58
|
+
const buf = new ArrayBuffer(initialData.byteLength, {
|
|
59
|
+
maxByteLength: maxBytes !== null && maxBytes !== void 0 ? maxBytes : 1000000000,
|
|
60
|
+
});
|
|
61
|
+
let data = new Uint8Array(buf);
|
|
62
|
+
data.set(initialData);
|
|
59
63
|
let view = new DataView(data.buffer);
|
|
60
64
|
const counter = makeOffsetCounter();
|
|
61
65
|
const getSlice = (amount) => {
|
|
@@ -71,15 +75,24 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
71
75
|
const getFourByteNumber = () => {
|
|
72
76
|
return ((getUint8() << 24) | (getUint8() << 16) | (getUint8() << 8) | getUint8());
|
|
73
77
|
};
|
|
78
|
+
const getPaddedFourByteNumber = () => {
|
|
79
|
+
let lastInt = 128;
|
|
80
|
+
while (((lastInt = getUint8()), lastInt === 128)) {
|
|
81
|
+
// Do nothing
|
|
82
|
+
}
|
|
83
|
+
return lastInt;
|
|
84
|
+
};
|
|
74
85
|
const getUint32 = () => {
|
|
75
86
|
const val = view.getUint32(counter.getDiscardedOffset());
|
|
76
87
|
counter.increment(4);
|
|
77
88
|
return val;
|
|
78
89
|
};
|
|
79
90
|
const addData = (newData) => {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
const oldLength = buf.byteLength;
|
|
92
|
+
const newLength = oldLength + newData.byteLength;
|
|
93
|
+
buf.resize(newLength);
|
|
94
|
+
const newArray = new Uint8Array(buf);
|
|
95
|
+
newArray.set(newData, oldLength);
|
|
83
96
|
data = newArray;
|
|
84
97
|
view = new DataView(data.buffer);
|
|
85
98
|
};
|
|
@@ -98,9 +111,9 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
98
111
|
const removeBytesRead = () => {
|
|
99
112
|
const bytesToRemove = counter.getDiscardedOffset();
|
|
100
113
|
counter.discardBytes(bytesToRemove);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
const newData = data.slice(bytesToRemove);
|
|
115
|
+
data.set(newData);
|
|
116
|
+
buf.resize(newData.byteLength);
|
|
104
117
|
view = new DataView(data.buffer);
|
|
105
118
|
};
|
|
106
119
|
return {
|
|
@@ -120,6 +133,7 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
120
133
|
const atom = getSlice(4);
|
|
121
134
|
return new TextDecoder().decode(atom);
|
|
122
135
|
},
|
|
136
|
+
getPaddedFourByteNumber,
|
|
123
137
|
getMatroskaSegmentId: () => {
|
|
124
138
|
const first = getSlice(1);
|
|
125
139
|
const firstOneString = `0x${Array.from(new Uint8Array(first))
|
|
@@ -139,6 +153,10 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
139
153
|
'0xe0',
|
|
140
154
|
'0xb0',
|
|
141
155
|
'0xba',
|
|
156
|
+
'0x9a',
|
|
157
|
+
'0xe1',
|
|
158
|
+
'0xbf',
|
|
159
|
+
'0x88',
|
|
142
160
|
];
|
|
143
161
|
if (knownIdsWithOneLength.includes(firstOneString)) {
|
|
144
162
|
return firstOneString;
|
|
@@ -155,6 +173,9 @@ const getArrayBufferIterator = (initialData) => {
|
|
|
155
173
|
'0x4489',
|
|
156
174
|
'0x55ee',
|
|
157
175
|
'0x55b0',
|
|
176
|
+
'0x7ba9',
|
|
177
|
+
'0x63a2',
|
|
178
|
+
'0x73a4',
|
|
158
179
|
];
|
|
159
180
|
const firstTwoString = `${firstOneString}${Array.from(new Uint8Array(firstTwo))
|
|
160
181
|
.map((b) => {
|
package/dist/from-node.js
CHANGED
|
@@ -5,12 +5,16 @@ const fs_1 = require("fs");
|
|
|
5
5
|
const promises_1 = require("node:fs/promises");
|
|
6
6
|
const stream_1 = require("stream");
|
|
7
7
|
exports.nodeReader = {
|
|
8
|
-
read: (src, range) => {
|
|
8
|
+
read: async (src, range) => {
|
|
9
9
|
const stream = (0, fs_1.createReadStream)(src, {
|
|
10
10
|
start: range === null ? 0 : range[0],
|
|
11
11
|
end: range === null ? Infinity : range[1],
|
|
12
12
|
});
|
|
13
|
-
|
|
13
|
+
const stats = await (0, promises_1.stat)(src);
|
|
14
|
+
return {
|
|
15
|
+
reader: stream_1.Readable.toWeb(stream).getReader(),
|
|
16
|
+
contentLength: stats.size,
|
|
17
|
+
};
|
|
14
18
|
},
|
|
15
19
|
getLength: async (src) => {
|
|
16
20
|
const stats = await (0, promises_1.stat)(src);
|
package/dist/from-web.js
CHANGED
|
@@ -21,8 +21,13 @@ exports.webReader = {
|
|
|
21
21
|
if (!res.body) {
|
|
22
22
|
throw new Error('No body');
|
|
23
23
|
}
|
|
24
|
+
const length = res.headers.get('content-length');
|
|
25
|
+
if (!length) {
|
|
26
|
+
throw new Error('No content-length');
|
|
27
|
+
}
|
|
28
|
+
const contentLength = length === null ? null : parseInt(length, 10);
|
|
24
29
|
const reader = res.body.getReader();
|
|
25
|
-
return reader;
|
|
30
|
+
return { reader, contentLength };
|
|
26
31
|
},
|
|
27
32
|
getLength: async (src) => {
|
|
28
33
|
const res = await fetch(src, {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAudioCodec = exports.hasAudioCodec = void 0;
|
|
4
|
+
const get_fps_1 = require("./get-fps");
|
|
5
|
+
const hasAudioCodec = (boxes) => {
|
|
6
|
+
try {
|
|
7
|
+
return (0, exports.getAudioCodec)(boxes) !== null;
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
exports.hasAudioCodec = hasAudioCodec;
|
|
14
|
+
const onEsdsBox = (child) => {
|
|
15
|
+
if (child && child.type === 'esds-box') {
|
|
16
|
+
const descriptor = child.descriptors.find((d) => d.type === 'decoder-config-descriptor');
|
|
17
|
+
if (descriptor && descriptor.type === 'decoder-config-descriptor') {
|
|
18
|
+
return descriptor.objectTypeIndication;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
const onSample = (sample) => {
|
|
24
|
+
if (!sample) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (sample.type !== 'audio') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (sample.format === 'sowt') {
|
|
31
|
+
return 'aiff';
|
|
32
|
+
}
|
|
33
|
+
const child = sample.children.find((c) => c.type === 'esds-box');
|
|
34
|
+
if (child && child.type === 'esds-box') {
|
|
35
|
+
const ret = onEsdsBox(child);
|
|
36
|
+
if (ret) {
|
|
37
|
+
return ret;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const getAudioCodec = (boxes) => {
|
|
42
|
+
const moovBox = boxes.find((b) => b.type === 'moov-box');
|
|
43
|
+
if (moovBox && moovBox.type === 'moov-box') {
|
|
44
|
+
const trakBox = moovBox.children.find((b) => b.type === 'trak-box' && (0, get_fps_1.trakBoxContainsAudio)(b));
|
|
45
|
+
if (trakBox && trakBox.type === 'trak-box') {
|
|
46
|
+
const mdiaBox = trakBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'mdia');
|
|
47
|
+
if (mdiaBox &&
|
|
48
|
+
mdiaBox.type === 'regular-box' &&
|
|
49
|
+
mdiaBox.boxType === 'mdia') {
|
|
50
|
+
const minfBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'minf');
|
|
51
|
+
if (minfBox &&
|
|
52
|
+
minfBox.type === 'regular-box' &&
|
|
53
|
+
minfBox.boxType === 'minf') {
|
|
54
|
+
const stblBox = minfBox === null || minfBox === void 0 ? void 0 : minfBox.children.find((b) => b.type === 'regular-box' && b.boxType === 'stbl');
|
|
55
|
+
if (stblBox && stblBox.type === 'regular-box') {
|
|
56
|
+
const stsdBox = stblBox === null || stblBox === void 0 ? void 0 : stblBox.children.find((b) => b.type === 'stsd-box');
|
|
57
|
+
if (stsdBox && stsdBox.type === 'stsd-box') {
|
|
58
|
+
const sample = stsdBox.samples.find((s) => s.type === 'audio');
|
|
59
|
+
if (sample && sample.type === 'audio') {
|
|
60
|
+
const ret = onSample(sample);
|
|
61
|
+
if (ret) {
|
|
62
|
+
return ret;
|
|
63
|
+
}
|
|
64
|
+
const waveBox = sample.children.find((b) => b.type === 'regular-box' && b.boxType === 'wave');
|
|
65
|
+
if (waveBox &&
|
|
66
|
+
waveBox.type === 'regular-box' &&
|
|
67
|
+
waveBox.boxType === 'wave') {
|
|
68
|
+
const esdsBox = waveBox.children.find((b) => b.type === 'esds-box');
|
|
69
|
+
if (esdsBox && esdsBox.type === 'esds-box') {
|
|
70
|
+
const ret2 = onEsdsBox(esdsBox);
|
|
71
|
+
if (ret2) {
|
|
72
|
+
return ret2;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const mainSegment = boxes.find((b) => b.type === 'main-segment');
|
|
84
|
+
if (!mainSegment || mainSegment.type !== 'main-segment') {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const tracksSegment = mainSegment.children.find((b) => b.type === 'tracks-segment');
|
|
88
|
+
if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
for (const track of tracksSegment.children) {
|
|
92
|
+
if (track.type === 'track-entry-segment') {
|
|
93
|
+
const trackType = track.children.find((b) => b.type === 'codec-segment');
|
|
94
|
+
if (trackType && trackType.type === 'codec-segment') {
|
|
95
|
+
if (trackType.codec === 'A_OPUS') {
|
|
96
|
+
return 'opus';
|
|
97
|
+
}
|
|
98
|
+
if (trackType.codec === 'A_PCM/INT/LIT') {
|
|
99
|
+
return 'pcm';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
exports.getAudioCodec = getAudioCodec;
|
package/dist/get-dimensions.js
CHANGED
|
@@ -6,8 +6,12 @@ const getDimensionsFromMatroska = (segments) => {
|
|
|
6
6
|
if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
|
|
7
7
|
throw new Error('No tracks segment');
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const trackEntrySegment = tracksSegment.children.find((b) => {
|
|
10
|
+
if (b.type !== 'track-entry-segment') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return (b.children.find((c) => c.type === 'codec-segment' && c.codec.startsWith('V_')) !== undefined);
|
|
14
|
+
});
|
|
11
15
|
if (!trackEntrySegment || trackEntrySegment.type !== 'track-entry-segment') {
|
|
12
16
|
throw new Error('No track entry segment');
|
|
13
17
|
}
|
package/dist/get-fps.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { AnySegment } from './parse-result';
|
|
2
|
+
type TimescaleAndDuration = {
|
|
3
|
+
timescale: number;
|
|
4
|
+
duration: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const trakBoxContainsAudio: (trakBox: AnySegment) => boolean;
|
|
7
|
+
export declare const trakBoxContainsVideo: (trakBox: AnySegment) => boolean;
|
|
8
|
+
export declare const getTimescaleAndDuration: (boxes: AnySegment[]) => TimescaleAndDuration | null;
|
|
2
9
|
export declare const getFps: (segments: AnySegment[]) => number | null;
|
|
3
10
|
export declare const hasFps: (boxes: AnySegment[]) => boolean;
|
|
11
|
+
export {};
|
package/dist/get-fps.js
CHANGED
|
@@ -1,16 +1,122 @@
|
|
|
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 = exports.trakBoxContainsAudio = 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 trakBoxContainsAudio = (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 === 'audio');
|
|
40
|
+
if (!videoSample || videoSample.type !== 'audio') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
exports.trakBoxContainsAudio = trakBoxContainsAudio;
|
|
46
|
+
const trakBoxContainsVideo = (trakBox) => {
|
|
47
|
+
if (trakBox.type !== 'trak-box') {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const { children } = trakBox;
|
|
51
|
+
const mediaBoxes = children.filter((c) => c.type === 'regular-box' && c.boxType === 'mdia');
|
|
52
|
+
if (!mediaBoxes || mediaBoxes.length === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const firstMediaBox = mediaBoxes[0];
|
|
56
|
+
if (firstMediaBox.type !== 'regular-box' ||
|
|
57
|
+
firstMediaBox.boxType !== 'mdia') {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const minf = firstMediaBox.children.find((c) => c.type === 'regular-box' && c.boxType === 'minf');
|
|
61
|
+
if (!minf || minf.type !== 'regular-box' || minf.boxType !== 'minf') {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const stbl = minf.children.find((c) => c.type === 'regular-box' && c.boxType === 'stbl');
|
|
65
|
+
if (!stbl || stbl.type !== 'regular-box' || stbl.boxType !== 'stbl') {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const stsd = stbl.children.find((c) => c.type === 'stsd-box');
|
|
69
|
+
if (!stsd || stsd.type !== 'stsd-box') {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const videoSample = stsd.samples.find((s) => s.type === 'video');
|
|
73
|
+
if (!videoSample || videoSample.type !== 'video') {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
};
|
|
78
|
+
exports.trakBoxContainsVideo = trakBoxContainsVideo;
|
|
79
|
+
const getTimescaleAndDuration = (boxes) => {
|
|
80
|
+
const moovBox = boxes.find((s) => s.type === 'moov-box');
|
|
81
|
+
if (!moovBox || moovBox.type !== 'moov-box') {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const { children } = moovBox;
|
|
85
|
+
const trackBoxes = children.filter((c) => c.type === 'trak-box');
|
|
86
|
+
if (!trackBoxes || trackBoxes.length === 0) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const trackBox = trackBoxes.find(exports.trakBoxContainsVideo);
|
|
90
|
+
if (!trackBox || trackBox.type !== 'trak-box') {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const trackBoxChildren = trackBox.children;
|
|
94
|
+
if (!trackBoxChildren || trackBoxChildren.length === 0) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const mdiaBox = trackBoxChildren.find((c) => c.type === 'regular-box' && c.boxType === 'mdia');
|
|
98
|
+
if (!mdiaBox ||
|
|
99
|
+
mdiaBox.type !== 'regular-box' ||
|
|
100
|
+
mdiaBox.boxType !== 'mdia') {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const mdhdBox = mdiaBox === null || mdiaBox === void 0 ? void 0 : mdiaBox.children.find((c) => c.type === 'mdhd-box');
|
|
104
|
+
if (mdhdBox && mdhdBox.type === 'mdhd-box') {
|
|
105
|
+
return { timescale: mdhdBox.timescale, duration: mdhdBox.duration };
|
|
106
|
+
}
|
|
107
|
+
const mvhdBox = moovBox.children.find((c) => c.type === 'mvhd-box');
|
|
108
|
+
if (!mvhdBox || mvhdBox.type !== 'mvhd-box') {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const { timeScale, durationInUnits } = mvhdBox;
|
|
112
|
+
return { timescale: timeScale, duration: durationInUnits };
|
|
113
|
+
};
|
|
114
|
+
exports.getTimescaleAndDuration = getTimescaleAndDuration;
|
|
13
115
|
const getFps = (segments) => {
|
|
116
|
+
const timescaleAndDuration = (0, exports.getTimescaleAndDuration)(segments);
|
|
117
|
+
if (!timescaleAndDuration) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
14
120
|
const moovBox = segments.find((s) => s.type === 'moov-box');
|
|
15
121
|
if (!moovBox || moovBox.type !== 'moov-box') {
|
|
16
122
|
return null;
|
|
@@ -19,14 +125,12 @@ const getFps = (segments) => {
|
|
|
19
125
|
if (!mvhdBox || mvhdBox.type !== 'mvhd-box') {
|
|
20
126
|
return null;
|
|
21
127
|
}
|
|
22
|
-
const { timeScale } = mvhdBox;
|
|
23
128
|
const { children } = moovBox;
|
|
24
129
|
const trackBoxes = children.filter((c) => c.type === 'trak-box');
|
|
25
130
|
if (!trackBoxes || trackBoxes.length === 0) {
|
|
26
131
|
return null;
|
|
27
132
|
}
|
|
28
|
-
|
|
29
|
-
const trackBox = trackBoxes[0];
|
|
133
|
+
const trackBox = trackBoxes.find(exports.trakBoxContainsVideo);
|
|
30
134
|
if (!trackBox || trackBox.type !== 'trak-box') {
|
|
31
135
|
return null;
|
|
32
136
|
}
|
|
@@ -56,7 +160,11 @@ const getFps = (segments) => {
|
|
|
56
160
|
if (!sttsBox || sttsBox.type !== 'stts-box') {
|
|
57
161
|
return null;
|
|
58
162
|
}
|
|
59
|
-
return calculateFps(
|
|
163
|
+
return calculateFps({
|
|
164
|
+
sttsBox,
|
|
165
|
+
timeScale: timescaleAndDuration.timescale,
|
|
166
|
+
durationInSamples: timescaleAndDuration.duration,
|
|
167
|
+
});
|
|
60
168
|
};
|
|
61
169
|
exports.getFps = getFps;
|
|
62
170
|
const hasFps = (boxes) => {
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
if (trackType.codec === 'V_MPEG4/ISO/AVC') {
|
|
72
|
+
return 'h264';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
exports.getVideoCodec = getVideoCodec;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseMedia = void 0;
|
|
4
|
+
const buffer_iterator_1 = require("./buffer-iterator");
|
|
5
|
+
const from_web_1 = require("./from-web");
|
|
6
|
+
const get_dimensions_1 = require("./get-dimensions");
|
|
7
|
+
const get_duration_1 = require("./get-duration");
|
|
8
|
+
const get_fps_1 = require("./get-fps");
|
|
9
|
+
const has_all_info_1 = require("./has-all-info");
|
|
10
|
+
const parse_video_1 = require("./parse-video");
|
|
11
|
+
const parseMedia = async (src, options, readerInterface = from_web_1.webReader) => {
|
|
12
|
+
const reader = await readerInterface.read(src, null);
|
|
13
|
+
const returnValue = {};
|
|
14
|
+
const iterator = (0, buffer_iterator_1.getArrayBufferIterator)(new Uint8Array([]));
|
|
15
|
+
let parseResult = (0, parse_video_1.parseVideo)(iterator);
|
|
16
|
+
while (parseResult.status === 'incomplete') {
|
|
17
|
+
const result = await reader.read();
|
|
18
|
+
if (result.done) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
iterator.addData(result.value);
|
|
22
|
+
parseResult = parseResult.continueParsing();
|
|
23
|
+
if ((0, has_all_info_1.hasAllInfo)(options, parseResult)) {
|
|
24
|
+
if (!reader.closed) {
|
|
25
|
+
reader.cancel(new Error('has all information'));
|
|
26
|
+
}
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (options.dimensions) {
|
|
31
|
+
returnValue.dimensions = (0, get_dimensions_1.getDimensions)(parseResult.segments);
|
|
32
|
+
}
|
|
33
|
+
if (options.durationInSeconds) {
|
|
34
|
+
returnValue.durationInSeconds = (0, get_duration_1.getDuration)(parseResult.segments);
|
|
35
|
+
}
|
|
36
|
+
if (options.fps) {
|
|
37
|
+
returnValue.fps = (0, get_fps_1.getFps)(parseResult.segments);
|
|
38
|
+
}
|
|
39
|
+
if (options.boxes) {
|
|
40
|
+
returnValue.boxes = parseResult.segments;
|
|
41
|
+
}
|
|
42
|
+
return returnValue;
|
|
43
|
+
};
|
|
44
|
+
exports.parseMedia = parseMedia;
|
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, boolean>, parseResult: ParseResult) => boolean;
|
package/dist/has-all-info.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.hasAllInfo = void 0;
|
|
4
|
+
const get_audio_codec_1 = require("./get-audio-codec");
|
|
4
5
|
const get_dimensions_1 = require("./get-dimensions");
|
|
5
6
|
const get_duration_1 = require("./get-duration");
|
|
6
7
|
const get_fps_1 = require("./get-fps");
|
|
8
|
+
const get_video_codec_1 = require("./get-video-codec");
|
|
7
9
|
const hasAllInfo = (options, parseResult) => {
|
|
8
10
|
const keys = Object.entries(options)
|
|
9
11
|
.filter(([, value]) => value)
|
|
@@ -21,6 +23,12 @@ const hasAllInfo = (options, parseResult) => {
|
|
|
21
23
|
if (key === 'fps') {
|
|
22
24
|
return (0, get_fps_1.hasFps)(parseResult.segments) !== null;
|
|
23
25
|
}
|
|
26
|
+
if (key === 'videoCodec') {
|
|
27
|
+
return (0, get_video_codec_1.hasVideoCodec)(parseResult.segments) !== null;
|
|
28
|
+
}
|
|
29
|
+
if (key === 'audioCodec') {
|
|
30
|
+
return (0, get_audio_codec_1.hasAudioCodec)(parseResult.segments) !== null;
|
|
31
|
+
}
|
|
24
32
|
throw new Error(`Unknown key: ${key}`);
|
|
25
33
|
});
|
|
26
34
|
};
|
package/dist/options.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
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 KnownAudioCodecs = 'aac' | 'mp3' | 'aiff' | 'opus' | 'pcm' | 'unknown';
|
|
6
|
+
export type Options<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean, EnableAudioCodec extends boolean> = {
|
|
5
7
|
dimensions?: EnableDimensions;
|
|
6
8
|
durationInSeconds?: EnableDuration;
|
|
7
9
|
boxes?: EnableBoxes;
|
|
8
10
|
fps?: EnableFps;
|
|
11
|
+
videoCodec?: EnableVideoCodec;
|
|
12
|
+
audioCodec?: EnableAudioCodec;
|
|
9
13
|
};
|
|
10
|
-
export type Metadata<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean> = (EnableDimensions extends true ? {
|
|
14
|
+
export type Metadata<EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean, EnableAudioCodec extends boolean> = (EnableDimensions extends true ? {
|
|
11
15
|
dimensions: Dimensions;
|
|
12
16
|
} : {}) & (EnableDuration extends true ? {
|
|
13
17
|
durationInSeconds: number | null;
|
|
@@ -15,5 +19,9 @@ export type Metadata<EnableDimensions extends boolean, EnableDuration extends bo
|
|
|
15
19
|
boxes: AnySegment[];
|
|
16
20
|
} : {}) & (EnableFps extends true ? {
|
|
17
21
|
fps: number | null;
|
|
22
|
+
} : {}) & (EnableVideoCodec extends true ? {
|
|
23
|
+
videoCodec: KnownVideoCodecs | null;
|
|
24
|
+
} : {}) & (EnableAudioCodec extends true ? {
|
|
25
|
+
audioCodec: KnownAudioCodecs | null;
|
|
18
26
|
} : {});
|
|
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>>;
|
|
27
|
+
export type ParseMedia = <EnableDimensions extends boolean, EnableDuration extends boolean, EnableBoxes extends boolean, EnableFps extends boolean, EnableVideoCodec extends boolean, EnableAudioCodec extends boolean>(src: string, options: Options<EnableDimensions, EnableDuration, EnableBoxes, EnableFps, EnableVideoCodec, EnableAudioCodec>, readerInterface?: ReaderInterface) => Promise<Metadata<EnableDimensions, EnableDuration, EnableBoxes, EnableFps, EnableVideoCodec, EnableAudioCodec>>;
|