@remotion/media-parser 4.0.192 → 4.0.194
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/esds-descriptors.d.ts +21 -0
- package/dist/boxes/iso-base-media/esds-descriptors.js +62 -0
- package/dist/boxes/iso-base-media/esds.d.ts +15 -0
- package/dist/boxes/iso-base-media/esds.js +27 -0
- package/dist/boxes/iso-base-media/mdat/mdat.d.ts +12 -0
- package/dist/boxes/iso-base-media/mdat/mdat.js +13 -0
- package/dist/boxes/iso-base-media/process-box.js +67 -0
- package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -0
- package/dist/boxes/iso-base-media/stsd/samples.js +27 -8
- package/dist/boxes/iso-base-media/stsd/stco.d.ts +14 -0
- package/dist/boxes/iso-base-media/stsd/stco.js +30 -0
- package/dist/boxes/iso-base-media/stsd/stsc.d.ts +19 -0
- package/dist/boxes/iso-base-media/stsd/stsc.js +34 -0
- package/dist/boxes/iso-base-media/stsd/stsz.d.ts +15 -0
- package/dist/boxes/iso-base-media/stsd/stsz.js +32 -0
- package/dist/boxes/webm/parse-webm-header.js +3 -3
- package/dist/boxes/webm/segments/track-entry.d.ts +20 -0
- package/dist/boxes/webm/segments/track-entry.js +37 -2
- package/dist/boxes/webm/segments.d.ts +2 -2
- package/dist/boxes/webm/segments.js +12 -0
- package/dist/buffer-iterator.d.ts +3 -1
- package/dist/buffer-iterator.js +41 -8
- package/dist/from-node.js +12 -4
- package/dist/from-web.js +12 -4
- 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 +1 -0
- package/dist/get-fps.js +34 -1
- package/dist/get-sample-positions.d.ts +12 -0
- package/dist/get-sample-positions.js +25 -0
- package/dist/get-samples.d.ts +0 -0
- package/dist/get-samples.js +1 -0
- package/dist/get-tracks.d.ts +10 -0
- package/dist/get-tracks.js +66 -0
- package/dist/get-video-codec.js +3 -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 +37 -9
- package/dist/parse-result.d.ts +3 -1
- package/dist/parse-video.d.ts +1 -0
- package/dist/parse-video.js +1 -0
- package/dist/reader.d.ts +5 -1
- package/dist/traversal.d.ts +19 -0
- package/dist/traversal.js +88 -0
- 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/process-box.ts +75 -0
- package/src/boxes/iso-base-media/stsd/samples.ts +35 -8
- package/src/boxes/webm/parse-webm-header.ts +3 -3
- package/src/boxes/webm/segments/track-entry.ts +66 -1
- package/src/boxes/webm/segments.ts +29 -1
- package/src/buffer-iterator.ts +54 -10
- package/src/from-node.ts +13 -6
- package/src/from-web.ts +14 -4
- package/src/get-audio-codec.ts +143 -0
- package/src/get-dimensions.ts +11 -4
- package/src/get-fps.ts +48 -0
- package/src/get-video-codec.ts +4 -0
- package/src/has-all-info.ts +14 -2
- package/src/options.ts +18 -3
- package/src/parse-media.ts +51 -10
- package/src/parse-result.ts +4 -1
- package/src/parse-video.ts +2 -0
- package/src/reader.ts +6 -2
- package/src/test/matroska.test.ts +6 -7
- package/src/test/parse-esds.test.ts +75 -0
- package/src/test/stream-local.test.ts +80 -3
- package/src/test/stsd.test.ts +52 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* eslint-disable max-depth */
|
|
2
|
+
import type {Sample} from './boxes/iso-base-media/stsd/samples';
|
|
3
|
+
import {trakBoxContainsAudio} from './get-fps';
|
|
4
|
+
import type {KnownAudioCodecs} from './options';
|
|
5
|
+
import type {AnySegment} from './parse-result';
|
|
6
|
+
|
|
7
|
+
export const hasAudioCodec = (boxes: AnySegment[]): boolean => {
|
|
8
|
+
try {
|
|
9
|
+
return getAudioCodec(boxes) !== null;
|
|
10
|
+
} catch (e) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const onEsdsBox = (child: AnySegment): KnownAudioCodecs | null => {
|
|
16
|
+
if (child && child.type === 'esds-box') {
|
|
17
|
+
const descriptor = child.descriptors.find(
|
|
18
|
+
(d) => d.type === 'decoder-config-descriptor',
|
|
19
|
+
);
|
|
20
|
+
if (descriptor && descriptor.type === 'decoder-config-descriptor') {
|
|
21
|
+
return descriptor.objectTypeIndication;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const onSample = (sample: Sample): KnownAudioCodecs | null | undefined => {
|
|
29
|
+
if (!sample) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (sample.type !== 'audio') {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (sample.format === 'sowt') {
|
|
38
|
+
return 'aiff';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const child = sample.children.find((c) => c.type === 'esds-box');
|
|
42
|
+
|
|
43
|
+
if (child && child.type === 'esds-box') {
|
|
44
|
+
const ret = onEsdsBox(child);
|
|
45
|
+
if (ret) {
|
|
46
|
+
return ret;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const getAudioCodec = (boxes: AnySegment[]): KnownAudioCodecs | null => {
|
|
52
|
+
const moovBox = boxes.find((b) => b.type === 'moov-box');
|
|
53
|
+
if (moovBox && moovBox.type === 'moov-box') {
|
|
54
|
+
const trakBox = moovBox.children.find(
|
|
55
|
+
(b) => b.type === 'trak-box' && trakBoxContainsAudio(b),
|
|
56
|
+
);
|
|
57
|
+
if (trakBox && trakBox.type === 'trak-box') {
|
|
58
|
+
const mdiaBox = trakBox.children.find(
|
|
59
|
+
(b) => b.type === 'regular-box' && b.boxType === 'mdia',
|
|
60
|
+
);
|
|
61
|
+
if (
|
|
62
|
+
mdiaBox &&
|
|
63
|
+
mdiaBox.type === 'regular-box' &&
|
|
64
|
+
mdiaBox.boxType === 'mdia'
|
|
65
|
+
) {
|
|
66
|
+
const minfBox = mdiaBox?.children.find(
|
|
67
|
+
(b) => b.type === 'regular-box' && b.boxType === 'minf',
|
|
68
|
+
);
|
|
69
|
+
if (
|
|
70
|
+
minfBox &&
|
|
71
|
+
minfBox.type === 'regular-box' &&
|
|
72
|
+
minfBox.boxType === 'minf'
|
|
73
|
+
) {
|
|
74
|
+
const stblBox = minfBox?.children.find(
|
|
75
|
+
(b) => b.type === 'regular-box' && b.boxType === 'stbl',
|
|
76
|
+
);
|
|
77
|
+
if (stblBox && stblBox.type === 'regular-box') {
|
|
78
|
+
const stsdBox = stblBox?.children.find(
|
|
79
|
+
(b) => b.type === 'stsd-box',
|
|
80
|
+
);
|
|
81
|
+
if (stsdBox && stsdBox.type === 'stsd-box') {
|
|
82
|
+
const sample = stsdBox.samples.find((s) => s.type === 'audio');
|
|
83
|
+
if (sample && sample.type === 'audio') {
|
|
84
|
+
const ret = onSample(sample);
|
|
85
|
+
if (ret) {
|
|
86
|
+
return ret;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const waveBox = sample.children.find(
|
|
90
|
+
(b) => b.type === 'regular-box' && b.boxType === 'wave',
|
|
91
|
+
);
|
|
92
|
+
if (
|
|
93
|
+
waveBox &&
|
|
94
|
+
waveBox.type === 'regular-box' &&
|
|
95
|
+
waveBox.boxType === 'wave'
|
|
96
|
+
) {
|
|
97
|
+
const esdsBox = waveBox.children.find(
|
|
98
|
+
(b) => b.type === 'esds-box',
|
|
99
|
+
);
|
|
100
|
+
if (esdsBox && esdsBox.type === 'esds-box') {
|
|
101
|
+
const ret2 = onEsdsBox(esdsBox);
|
|
102
|
+
if (ret2) {
|
|
103
|
+
return ret2;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const mainSegment = boxes.find((b) => b.type === 'main-segment');
|
|
116
|
+
if (!mainSegment || mainSegment.type !== 'main-segment') {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const tracksSegment = mainSegment.children.find(
|
|
121
|
+
(b) => b.type === 'tracks-segment',
|
|
122
|
+
);
|
|
123
|
+
if (!tracksSegment || tracksSegment.type !== 'tracks-segment') {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const track of tracksSegment.children) {
|
|
128
|
+
if (track.type === 'track-entry-segment') {
|
|
129
|
+
const trackType = track.children.find((b) => b.type === 'codec-segment');
|
|
130
|
+
if (trackType && trackType.type === 'codec-segment') {
|
|
131
|
+
if (trackType.codec === 'A_OPUS') {
|
|
132
|
+
return 'opus';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (trackType.codec === 'A_PCM/INT/LIT') {
|
|
136
|
+
return 'pcm';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return null;
|
|
143
|
+
};
|
package/src/get-dimensions.ts
CHANGED
|
@@ -14,10 +14,17 @@ const getDimensionsFromMatroska = (segments: MainSegment): Dimensions => {
|
|
|
14
14
|
throw new Error('No tracks segment');
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const trackEntrySegment = tracksSegment.children.find((b) => {
|
|
18
|
+
if (b.type !== 'track-entry-segment') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
b.children.find(
|
|
24
|
+
(c) => c.type === 'codec-segment' && c.codec.startsWith('V_'),
|
|
25
|
+
) !== undefined
|
|
26
|
+
);
|
|
27
|
+
});
|
|
21
28
|
if (!trackEntrySegment || trackEntrySegment.type !== 'track-entry-segment') {
|
|
22
29
|
throw new Error('No track entry segment');
|
|
23
30
|
}
|
package/src/get-fps.ts
CHANGED
|
@@ -27,6 +27,54 @@ type TimescaleAndDuration = {
|
|
|
27
27
|
duration: number;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
export const trakBoxContainsAudio = (trakBox: AnySegment): boolean => {
|
|
31
|
+
if (trakBox.type !== 'trak-box') {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const {children} = trakBox;
|
|
36
|
+
const mediaBoxes = children.filter(
|
|
37
|
+
(c) => c.type === 'regular-box' && c.boxType === 'mdia',
|
|
38
|
+
);
|
|
39
|
+
if (!mediaBoxes || mediaBoxes.length === 0) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const firstMediaBox = mediaBoxes[0];
|
|
44
|
+
if (
|
|
45
|
+
firstMediaBox.type !== 'regular-box' ||
|
|
46
|
+
firstMediaBox.boxType !== 'mdia'
|
|
47
|
+
) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const minf = firstMediaBox.children.find(
|
|
52
|
+
(c) => c.type === 'regular-box' && c.boxType === 'minf',
|
|
53
|
+
);
|
|
54
|
+
if (!minf || minf.type !== 'regular-box' || minf.boxType !== 'minf') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const stbl = minf.children.find(
|
|
59
|
+
(c) => c.type === 'regular-box' && c.boxType === 'stbl',
|
|
60
|
+
);
|
|
61
|
+
if (!stbl || stbl.type !== 'regular-box' || stbl.boxType !== 'stbl') {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const stsd = stbl.children.find((c) => c.type === 'stsd-box');
|
|
66
|
+
if (!stsd || stsd.type !== 'stsd-box') {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const videoSample = stsd.samples.find((s) => s.type === 'audio');
|
|
71
|
+
if (!videoSample || videoSample.type !== 'audio') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
|
|
30
78
|
export const trakBoxContainsVideo = (trakBox: AnySegment): boolean => {
|
|
31
79
|
if (trakBox.type !== 'trak-box') {
|
|
32
80
|
return false;
|
package/src/get-video-codec.ts
CHANGED
package/src/has-all-info.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {hasAudioCodec} from './get-audio-codec';
|
|
1
2
|
import {hasDimensions} from './get-dimensions';
|
|
2
3
|
import {hasDuration} from './get-duration';
|
|
3
4
|
import {hasFps} from './get-fps';
|
|
@@ -6,12 +7,19 @@ import type {Options} from './options';
|
|
|
6
7
|
import type {ParseResult} from './parse-result';
|
|
7
8
|
|
|
8
9
|
export const hasAllInfo = (
|
|
9
|
-
options: Options<boolean, boolean, boolean, boolean, boolean>,
|
|
10
|
+
options: Options<boolean, boolean, boolean, boolean, boolean, boolean>,
|
|
10
11
|
parseResult: ParseResult,
|
|
11
12
|
) => {
|
|
12
13
|
const keys = Object.entries(options)
|
|
13
14
|
.filter(([, value]) => value)
|
|
14
|
-
.map(([key]) => key) as (keyof Options<
|
|
15
|
+
.map(([key]) => key) as (keyof Options<
|
|
16
|
+
true,
|
|
17
|
+
true,
|
|
18
|
+
true,
|
|
19
|
+
true,
|
|
20
|
+
true,
|
|
21
|
+
true
|
|
22
|
+
>)[];
|
|
15
23
|
|
|
16
24
|
return keys.every((key) => {
|
|
17
25
|
if (key === 'boxes') {
|
|
@@ -34,6 +42,10 @@ export const hasAllInfo = (
|
|
|
34
42
|
return hasVideoCodec(parseResult.segments) !== null;
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
if (key === 'audioCodec') {
|
|
46
|
+
return hasAudioCodec(parseResult.segments) !== null;
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
throw new Error(`Unknown key: ${key satisfies never}`);
|
|
38
50
|
});
|
|
39
51
|
};
|
package/src/options.ts
CHANGED
|
@@ -10,18 +10,28 @@ export type KnownVideoCodecs =
|
|
|
10
10
|
| 'av1'
|
|
11
11
|
| 'prores';
|
|
12
12
|
|
|
13
|
+
export type KnownAudioCodecs =
|
|
14
|
+
| 'aac'
|
|
15
|
+
| 'mp3'
|
|
16
|
+
| 'aiff'
|
|
17
|
+
| 'opus'
|
|
18
|
+
| 'pcm'
|
|
19
|
+
| 'unknown';
|
|
20
|
+
|
|
13
21
|
export type Options<
|
|
14
22
|
EnableDimensions extends boolean,
|
|
15
23
|
EnableDuration extends boolean,
|
|
16
24
|
EnableBoxes extends boolean,
|
|
17
25
|
EnableFps extends boolean,
|
|
18
26
|
EnableVideoCodec extends boolean,
|
|
27
|
+
EnableAudioCodec extends boolean,
|
|
19
28
|
> = {
|
|
20
29
|
dimensions?: EnableDimensions;
|
|
21
30
|
durationInSeconds?: EnableDuration;
|
|
22
31
|
boxes?: EnableBoxes;
|
|
23
32
|
fps?: EnableFps;
|
|
24
33
|
videoCodec?: EnableVideoCodec;
|
|
34
|
+
audioCodec?: EnableAudioCodec;
|
|
25
35
|
};
|
|
26
36
|
|
|
27
37
|
export type Metadata<
|
|
@@ -30,11 +40,13 @@ export type Metadata<
|
|
|
30
40
|
EnableBoxes extends boolean,
|
|
31
41
|
EnableFps extends boolean,
|
|
32
42
|
EnableVideoCodec extends boolean,
|
|
43
|
+
EnableAudioCodec extends boolean,
|
|
33
44
|
> = (EnableDimensions extends true ? {dimensions: Dimensions} : {}) &
|
|
34
45
|
(EnableDuration extends true ? {durationInSeconds: number | null} : {}) &
|
|
35
46
|
(EnableBoxes extends true ? {boxes: AnySegment[]} : {}) &
|
|
36
47
|
(EnableFps extends true ? {fps: number | null} : {}) &
|
|
37
|
-
(EnableVideoCodec extends true ? {videoCodec: KnownVideoCodecs | null} : {})
|
|
48
|
+
(EnableVideoCodec extends true ? {videoCodec: KnownVideoCodecs | null} : {}) &
|
|
49
|
+
(EnableAudioCodec extends true ? {audioCodec: KnownAudioCodecs | null} : {});
|
|
38
50
|
|
|
39
51
|
export type ParseMedia = <
|
|
40
52
|
EnableDimensions extends boolean,
|
|
@@ -42,6 +54,7 @@ export type ParseMedia = <
|
|
|
42
54
|
EnableBoxes extends boolean,
|
|
43
55
|
EnableFps extends boolean,
|
|
44
56
|
EnableVideoCodec extends boolean,
|
|
57
|
+
EnableAudioCodec extends boolean,
|
|
45
58
|
>(
|
|
46
59
|
src: string,
|
|
47
60
|
options: Options<
|
|
@@ -49,7 +62,8 @@ export type ParseMedia = <
|
|
|
49
62
|
EnableDuration,
|
|
50
63
|
EnableBoxes,
|
|
51
64
|
EnableFps,
|
|
52
|
-
EnableVideoCodec
|
|
65
|
+
EnableVideoCodec,
|
|
66
|
+
EnableAudioCodec
|
|
53
67
|
>,
|
|
54
68
|
readerInterface?: ReaderInterface,
|
|
55
69
|
) => Promise<
|
|
@@ -58,6 +72,7 @@ export type ParseMedia = <
|
|
|
58
72
|
EnableDuration,
|
|
59
73
|
EnableBoxes,
|
|
60
74
|
EnableFps,
|
|
61
|
-
EnableVideoCodec
|
|
75
|
+
EnableVideoCodec,
|
|
76
|
+
EnableAudioCodec
|
|
62
77
|
>
|
|
63
78
|
>;
|
package/src/parse-media.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import type {BufferIterator} from './buffer-iterator';
|
|
1
2
|
import {getArrayBufferIterator} from './buffer-iterator';
|
|
2
3
|
import {webReader} from './from-web';
|
|
4
|
+
import {getAudioCodec} from './get-audio-codec';
|
|
3
5
|
import {getDimensions} from './get-dimensions';
|
|
4
6
|
import {getDuration} from './get-duration';
|
|
5
7
|
import {getFps} from './get-fps';
|
|
6
8
|
import {getVideoCodec} from './get-video-codec';
|
|
7
9
|
import {hasAllInfo} from './has-all-info';
|
|
8
10
|
import type {Metadata, ParseMedia} from './options';
|
|
11
|
+
import type {ParseResult} from './parse-result';
|
|
9
12
|
import {parseVideo} from './parse-video';
|
|
10
13
|
|
|
11
14
|
export const parseMedia: ParseMedia = async (
|
|
@@ -13,29 +16,63 @@ export const parseMedia: ParseMedia = async (
|
|
|
13
16
|
options,
|
|
14
17
|
readerInterface = webReader,
|
|
15
18
|
) => {
|
|
16
|
-
const reader = await readerInterface.read(src, null);
|
|
19
|
+
const {reader, contentLength} = await readerInterface.read(src, null);
|
|
20
|
+
let currentReader = reader;
|
|
17
21
|
|
|
18
|
-
const returnValue = {} as Metadata<true, true, true, true, true>;
|
|
22
|
+
const returnValue = {} as Metadata<true, true, true, true, true, true>;
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
let parseResult =
|
|
24
|
+
let iterator: BufferIterator | null = null;
|
|
25
|
+
let parseResult: ParseResult | null = null;
|
|
22
26
|
|
|
23
|
-
while (parseResult.status === 'incomplete') {
|
|
24
|
-
const result = await
|
|
27
|
+
while (parseResult === null || parseResult.status === 'incomplete') {
|
|
28
|
+
const result = await currentReader.read();
|
|
25
29
|
if (result.done) {
|
|
26
30
|
break;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
iterator
|
|
30
|
-
|
|
33
|
+
if (iterator) {
|
|
34
|
+
iterator.addData(result.value);
|
|
35
|
+
} else {
|
|
36
|
+
iterator = getArrayBufferIterator(
|
|
37
|
+
result.value,
|
|
38
|
+
contentLength ?? undefined,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (parseResult) {
|
|
43
|
+
parseResult = parseResult.continueParsing();
|
|
44
|
+
} else {
|
|
45
|
+
parseResult = parseVideo(iterator);
|
|
46
|
+
}
|
|
31
47
|
|
|
32
48
|
if (hasAllInfo(options, parseResult)) {
|
|
33
|
-
if (!
|
|
34
|
-
|
|
49
|
+
if (!currentReader.closed) {
|
|
50
|
+
currentReader.cancel(new Error('has all information'));
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
break;
|
|
38
54
|
}
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
parseResult &&
|
|
58
|
+
parseResult.status === 'incomplete' &&
|
|
59
|
+
parseResult.skipTo !== null
|
|
60
|
+
) {
|
|
61
|
+
if (!currentReader.closed) {
|
|
62
|
+
currentReader.cancel(new Error('skipped ahead'));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const {reader: newReader} = await readerInterface.read(
|
|
66
|
+
src,
|
|
67
|
+
parseResult.skipTo,
|
|
68
|
+
);
|
|
69
|
+
currentReader = newReader;
|
|
70
|
+
iterator.skipTo(parseResult.skipTo);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!parseResult) {
|
|
75
|
+
throw new Error('Could not parse video');
|
|
39
76
|
}
|
|
40
77
|
|
|
41
78
|
if (options.dimensions) {
|
|
@@ -54,6 +91,10 @@ export const parseMedia: ParseMedia = async (
|
|
|
54
91
|
returnValue.videoCodec = getVideoCodec(parseResult.segments);
|
|
55
92
|
}
|
|
56
93
|
|
|
94
|
+
if (options.audioCodec) {
|
|
95
|
+
returnValue.audioCodec = getAudioCodec(parseResult.segments);
|
|
96
|
+
}
|
|
97
|
+
|
|
57
98
|
if (options.boxes) {
|
|
58
99
|
returnValue.boxes = parseResult.segments;
|
|
59
100
|
}
|
package/src/parse-result.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {BaseBox} from './boxes/iso-base-media/base-type';
|
|
2
|
+
import type {EsdsBox} from './boxes/iso-base-media/esds/esds';
|
|
2
3
|
import type {FtypBox} from './boxes/iso-base-media/ftyp';
|
|
3
4
|
import type {MdhdBox} from './boxes/iso-base-media/mdhd';
|
|
4
5
|
import type {MoovBox} from './boxes/iso-base-media/moov/moov';
|
|
@@ -30,7 +31,8 @@ export type IsoBaseMediaBox =
|
|
|
30
31
|
| MoovBox
|
|
31
32
|
| TrakBox
|
|
32
33
|
| SttsBox
|
|
33
|
-
| MdhdBox
|
|
34
|
+
| MdhdBox
|
|
35
|
+
| EsdsBox;
|
|
34
36
|
|
|
35
37
|
export type AnySegment = MatroskaSegment | IsoBaseMediaBox;
|
|
36
38
|
|
|
@@ -42,5 +44,6 @@ export type ParseResult =
|
|
|
42
44
|
| {
|
|
43
45
|
status: 'incomplete';
|
|
44
46
|
segments: AnySegment[];
|
|
47
|
+
skipTo: number | null;
|
|
45
48
|
continueParsing: () => ParseResult;
|
|
46
49
|
};
|
package/src/parse-video.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type BoxAndNext =
|
|
|
8
8
|
type: 'complete';
|
|
9
9
|
box: IsoBaseMediaBox;
|
|
10
10
|
size: number;
|
|
11
|
+
skipTo: number | null;
|
|
11
12
|
}
|
|
12
13
|
| {
|
|
13
14
|
type: 'incomplete';
|
|
@@ -21,6 +22,7 @@ export const parseVideo = (iterator: BufferIterator): ParseResult => {
|
|
|
21
22
|
continueParsing: () => {
|
|
22
23
|
return parseVideo(iterator);
|
|
23
24
|
},
|
|
25
|
+
skipTo: null,
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
|
package/src/reader.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
type ReadResult = {
|
|
2
|
+
reader: ReadableStreamDefaultReader<Uint8Array>;
|
|
3
|
+
contentLength: number | null;
|
|
4
|
+
};
|
|
1
5
|
type ReadContent = (
|
|
2
6
|
src: string,
|
|
3
|
-
range: [number, number] | null,
|
|
4
|
-
) => Promise<
|
|
7
|
+
range: [number, number] | number | null,
|
|
8
|
+
) => Promise<ReadResult>;
|
|
5
9
|
type GetLength = (src: string) => Promise<number>;
|
|
6
10
|
|
|
7
11
|
export type ReaderInterface = {
|
|
@@ -137,22 +137,21 @@ test('Should get duration of AV1 video', async () => {
|
|
|
137
137
|
{
|
|
138
138
|
type: 'color-segment',
|
|
139
139
|
},
|
|
140
|
-
{
|
|
141
|
-
id: '0x0163a294',
|
|
142
|
-
type: 'unknown-segment',
|
|
143
|
-
},
|
|
144
140
|
],
|
|
145
141
|
},
|
|
146
142
|
{
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
codecPrivateData: [
|
|
144
|
+
129, 8, 12, 0, 10, 14, 0, 0, 0, 66, 171, 191, 195, 118, 0,
|
|
145
|
+
8, 8, 8, 8, 32,
|
|
146
|
+
],
|
|
147
|
+
type: 'codec-private-segment',
|
|
149
148
|
},
|
|
150
149
|
],
|
|
151
150
|
},
|
|
152
151
|
],
|
|
153
152
|
},
|
|
154
153
|
{
|
|
155
|
-
id: '
|
|
154
|
+
id: '0x1254c367',
|
|
156
155
|
type: 'unknown-segment',
|
|
157
156
|
},
|
|
158
157
|
],
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {expect, test} from 'bun:test';
|
|
2
|
+
import {parseEsds} from '../boxes/iso-base-media/esds/esds';
|
|
3
|
+
import {getArrayBufferIterator} from '../buffer-iterator';
|
|
4
|
+
|
|
5
|
+
test('Parse ESDS box', () => {
|
|
6
|
+
const buf = new Uint8Array([
|
|
7
|
+
// mock header
|
|
8
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
9
|
+
// actual box
|
|
10
|
+
0, 0, 0, 0, 3, 128, 128, 128, 27, 0, 2, 0, 4, 128, 128, 128, 13, 107, 21, 0,
|
|
11
|
+
0, 0, 0, 4, 226, 0, 0, 4, 226, 0, 6, 128, 128, 128, 1, 2,
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
const iter = getArrayBufferIterator(buf);
|
|
15
|
+
iter.counter.increment(8);
|
|
16
|
+
|
|
17
|
+
expect(
|
|
18
|
+
parseEsds({
|
|
19
|
+
data: iter,
|
|
20
|
+
fileOffset: 8,
|
|
21
|
+
size: buf.length - 8,
|
|
22
|
+
}),
|
|
23
|
+
).toEqual({
|
|
24
|
+
type: 'esds-box',
|
|
25
|
+
version: 0,
|
|
26
|
+
tag: 3,
|
|
27
|
+
sizeOfInstance: 27,
|
|
28
|
+
esId: 2,
|
|
29
|
+
descriptors: [
|
|
30
|
+
{
|
|
31
|
+
type: 'decoder-config-descriptor',
|
|
32
|
+
objectTypeIndication: 'mp3',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'sl-config-descriptor',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Parse two ESDS', () => {
|
|
42
|
+
const buf = new Uint8Array([
|
|
43
|
+
// mock header
|
|
44
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
45
|
+
// actual box
|
|
46
|
+
0, 0, 0, 0, 3, 25, 0, 0, 0, 4, 17, 64, 21, 0, 24, 0, 0, 4, 226, 0, 0, 4,
|
|
47
|
+
226, 0, 5, 2, 17, 144, 6, 1, 2, 0, 0, 0, 24, 115, 116, 116, 115,
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const iter = getArrayBufferIterator(buf);
|
|
51
|
+
iter.counter.increment(8);
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
parseEsds({
|
|
55
|
+
data: iter,
|
|
56
|
+
fileOffset: 8,
|
|
57
|
+
size: buf.length - 8,
|
|
58
|
+
}),
|
|
59
|
+
).toEqual({
|
|
60
|
+
type: 'esds-box',
|
|
61
|
+
version: 0,
|
|
62
|
+
tag: 3,
|
|
63
|
+
sizeOfInstance: 25,
|
|
64
|
+
esId: 0,
|
|
65
|
+
descriptors: [
|
|
66
|
+
{
|
|
67
|
+
objectTypeIndication: 'aac',
|
|
68
|
+
type: 'decoder-config-descriptor',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'sl-config-descriptor',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
});
|