@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
|
@@ -29,8 +29,11 @@ import {parseStsd} from './stsd/stsd';
|
|
|
29
29
|
import {parseStss} from './stsd/stss';
|
|
30
30
|
import {parseStsz} from './stsd/stsz';
|
|
31
31
|
import {parseStts} from './stsd/stts';
|
|
32
|
+
import {parseTfdt} from './tfdt';
|
|
33
|
+
import {getTfhd} from './tfhd';
|
|
32
34
|
import {parseTkhd} from './tkhd';
|
|
33
35
|
import {parseTrak} from './trak/trak';
|
|
36
|
+
import {parseTrun} from './trun';
|
|
34
37
|
|
|
35
38
|
const getChildren = async ({
|
|
36
39
|
boxType,
|
|
@@ -49,8 +52,10 @@ const getChildren = async ({
|
|
|
49
52
|
boxType === 'mdia' ||
|
|
50
53
|
boxType === 'minf' ||
|
|
51
54
|
boxType === 'stbl' ||
|
|
55
|
+
boxType === 'moof' ||
|
|
52
56
|
boxType === 'dims' ||
|
|
53
57
|
boxType === 'wave' ||
|
|
58
|
+
boxType === 'traf' ||
|
|
54
59
|
boxType === 'stsb';
|
|
55
60
|
|
|
56
61
|
if (parseChildren) {
|
|
@@ -260,6 +265,28 @@ export const processBox = async ({
|
|
|
260
265
|
};
|
|
261
266
|
}
|
|
262
267
|
|
|
268
|
+
if (boxType === 'trun') {
|
|
269
|
+
const box = parseTrun({iterator, offset: fileOffset, size: boxSize});
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
type: 'complete',
|
|
273
|
+
box,
|
|
274
|
+
size: boxSize,
|
|
275
|
+
skipTo: null,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (boxType === 'tfdt') {
|
|
280
|
+
const box = parseTfdt({iterator, size: boxSize, offset: fileOffset});
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
type: 'complete',
|
|
284
|
+
box,
|
|
285
|
+
size: boxSize,
|
|
286
|
+
skipTo: null,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
263
290
|
if (boxType === 'stsd') {
|
|
264
291
|
const box = await parseStsd({
|
|
265
292
|
iterator,
|
|
@@ -492,6 +519,21 @@ export const processBox = async ({
|
|
|
492
519
|
};
|
|
493
520
|
}
|
|
494
521
|
|
|
522
|
+
if (boxType === 'tfhd') {
|
|
523
|
+
const box = getTfhd({
|
|
524
|
+
iterator,
|
|
525
|
+
offset: fileOffset,
|
|
526
|
+
size: boxSize,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
type: 'complete',
|
|
531
|
+
box,
|
|
532
|
+
size: boxSize,
|
|
533
|
+
skipTo: null,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
495
537
|
if (boxType === 'mdhd') {
|
|
496
538
|
const box = parseMdhd({
|
|
497
539
|
data: iterator,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {BufferIterator} from '../../buffer-iterator';
|
|
2
|
+
|
|
3
|
+
export interface TfdtBox {
|
|
4
|
+
type: 'tfdt-box';
|
|
5
|
+
version: number;
|
|
6
|
+
baseMediaDecodeTime: number;
|
|
7
|
+
offset: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const parseTfdt = ({
|
|
11
|
+
iterator,
|
|
12
|
+
size,
|
|
13
|
+
offset,
|
|
14
|
+
}: {
|
|
15
|
+
iterator: BufferIterator;
|
|
16
|
+
size: number;
|
|
17
|
+
offset: number;
|
|
18
|
+
}): TfdtBox => {
|
|
19
|
+
const version = iterator.getUint8();
|
|
20
|
+
iterator.discard(3);
|
|
21
|
+
// Flags, discard them
|
|
22
|
+
const num =
|
|
23
|
+
version === 0 ? iterator.getUint32() : Number(iterator.getUint64());
|
|
24
|
+
|
|
25
|
+
const bytesRemaining = size - (iterator.counter.getOffset() - offset);
|
|
26
|
+
|
|
27
|
+
if (bytesRemaining !== 0) {
|
|
28
|
+
throw new Error('expected 0 bytes ' + bytesRemaining);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
type: 'tfdt-box',
|
|
33
|
+
version,
|
|
34
|
+
baseMediaDecodeTime: num,
|
|
35
|
+
offset,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type {BufferIterator} from '../../buffer-iterator';
|
|
2
|
+
|
|
3
|
+
export interface TfhdBox {
|
|
4
|
+
type: 'tfhd-box';
|
|
5
|
+
version: number;
|
|
6
|
+
trackId: number;
|
|
7
|
+
baseDataOffset: number;
|
|
8
|
+
baseSampleDescriptionIndex: number;
|
|
9
|
+
defaultSampleDuration: number;
|
|
10
|
+
defaultSampleSize: number;
|
|
11
|
+
defaultSampleFlags: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const getTfhd = ({
|
|
15
|
+
iterator,
|
|
16
|
+
offset,
|
|
17
|
+
size,
|
|
18
|
+
}: {
|
|
19
|
+
iterator: BufferIterator;
|
|
20
|
+
size: number;
|
|
21
|
+
offset: number;
|
|
22
|
+
}): TfhdBox => {
|
|
23
|
+
const version = iterator.getUint8();
|
|
24
|
+
const flags = iterator.getUint24();
|
|
25
|
+
|
|
26
|
+
const trackId = iterator.getUint32();
|
|
27
|
+
|
|
28
|
+
const baseDataOffsetPresent = flags & 0x01;
|
|
29
|
+
const baseDataOffset = baseDataOffsetPresent
|
|
30
|
+
? Number(iterator.getUint64())
|
|
31
|
+
: 0;
|
|
32
|
+
|
|
33
|
+
const baseSampleDescriptionIndexPresent = flags & 0x02;
|
|
34
|
+
const baseSampleDescriptionIndex = baseSampleDescriptionIndexPresent
|
|
35
|
+
? iterator.getUint32()
|
|
36
|
+
: 0;
|
|
37
|
+
|
|
38
|
+
const defaultSampleDurationPresent = flags & 0x08;
|
|
39
|
+
const defaultSampleDuration = defaultSampleDurationPresent
|
|
40
|
+
? iterator.getUint32()
|
|
41
|
+
: 0;
|
|
42
|
+
|
|
43
|
+
const defaultSampleSizePresent = flags & 0x10;
|
|
44
|
+
const defaultSampleSize = defaultSampleSizePresent ? iterator.getUint32() : 0;
|
|
45
|
+
|
|
46
|
+
const defaultSampleFlagsPresent = flags & 0x20;
|
|
47
|
+
const defaultSampleFlags = defaultSampleFlagsPresent
|
|
48
|
+
? iterator.getUint32()
|
|
49
|
+
: 0;
|
|
50
|
+
|
|
51
|
+
const bytesRemaining = size - (iterator.counter.getOffset() - offset);
|
|
52
|
+
if (bytesRemaining !== 0) {
|
|
53
|
+
throw new Error('expected 0 bytes ' + bytesRemaining);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
type: 'tfhd-box',
|
|
58
|
+
version,
|
|
59
|
+
trackId,
|
|
60
|
+
baseDataOffset,
|
|
61
|
+
baseSampleDescriptionIndex,
|
|
62
|
+
defaultSampleDuration,
|
|
63
|
+
defaultSampleSize,
|
|
64
|
+
defaultSampleFlags,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -26,6 +26,9 @@ type Matrix2x2 = readonly [number, number, number, number];
|
|
|
26
26
|
function getRotationAngleFromMatrix(matrix: Matrix2x2): number {
|
|
27
27
|
// Extract elements from the matrix
|
|
28
28
|
const [a, b, c, d] = matrix;
|
|
29
|
+
if (a === 0 && b === 0 && c === 0 && d === 0) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
29
32
|
|
|
30
33
|
// Check if the matrix is a valid rotation matrix
|
|
31
34
|
if (Math.round(a * a + b * b) !== 1 || Math.round(c * c + d * d) !== 1) {
|
|
@@ -66,28 +69,23 @@ export const parseTkhd = ({
|
|
|
66
69
|
offset: number;
|
|
67
70
|
size: number;
|
|
68
71
|
}): TkhdBox => {
|
|
69
|
-
if (size !== 92) {
|
|
70
|
-
throw new Error(`Expected tkhd size of version 0 to be 92, got ${size}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
72
|
const version = iterator.getUint8();
|
|
74
|
-
if (version !== 0) {
|
|
75
|
-
throw new Error(`Unsupported TKHD version ${version}`);
|
|
76
|
-
}
|
|
77
73
|
|
|
78
74
|
// Flags, we discard them
|
|
79
75
|
iterator.discard(3);
|
|
80
76
|
|
|
81
|
-
const creationTime =
|
|
77
|
+
const creationTime =
|
|
78
|
+
version === 1 ? iterator.getUint64() : iterator.getUint32();
|
|
82
79
|
|
|
83
|
-
const modificationTime =
|
|
80
|
+
const modificationTime =
|
|
81
|
+
version === 1 ? iterator.getUint64() : iterator.getUint32();
|
|
84
82
|
|
|
85
83
|
const trackId = iterator.getUint32();
|
|
86
84
|
|
|
87
85
|
// reserved
|
|
88
86
|
iterator.discard(4);
|
|
89
87
|
|
|
90
|
-
const duration = iterator.getUint32();
|
|
88
|
+
const duration = version === 1 ? iterator.getUint64() : iterator.getUint32();
|
|
91
89
|
|
|
92
90
|
// reserved 2
|
|
93
91
|
iterator.discard(4);
|
|
@@ -134,10 +132,10 @@ export const parseTkhd = ({
|
|
|
134
132
|
offset,
|
|
135
133
|
boxSize: size,
|
|
136
134
|
type: 'tkhd-box',
|
|
137
|
-
creationTime: toUnixTimestamp(creationTime),
|
|
138
|
-
modificationTime: toUnixTimestamp(modificationTime),
|
|
135
|
+
creationTime: toUnixTimestamp(Number(creationTime)),
|
|
136
|
+
modificationTime: toUnixTimestamp(Number(modificationTime)),
|
|
139
137
|
trackId,
|
|
140
|
-
duration,
|
|
138
|
+
duration: Number(duration),
|
|
141
139
|
layer,
|
|
142
140
|
alternateGroup,
|
|
143
141
|
volume,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type {BufferIterator} from '../../buffer-iterator';
|
|
2
|
+
|
|
3
|
+
export interface TrunBox {
|
|
4
|
+
type: 'trun-box';
|
|
5
|
+
version: number;
|
|
6
|
+
sampleCount: number;
|
|
7
|
+
dataOffset: number | null;
|
|
8
|
+
firstSampleFlags: number | null;
|
|
9
|
+
samples: TRunSample[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type TRunSample = {
|
|
13
|
+
sampleDuration: number | null;
|
|
14
|
+
sampleSize: number | null;
|
|
15
|
+
sampleFlags: number | null;
|
|
16
|
+
sampleCompositionTimeOffset: number | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const parseTrun = ({
|
|
20
|
+
iterator,
|
|
21
|
+
offset,
|
|
22
|
+
size,
|
|
23
|
+
}: {
|
|
24
|
+
iterator: BufferIterator;
|
|
25
|
+
offset: number;
|
|
26
|
+
size: number;
|
|
27
|
+
}): TrunBox => {
|
|
28
|
+
const version = iterator.getUint8();
|
|
29
|
+
if (version !== 0) {
|
|
30
|
+
throw new Error(`Unsupported TRUN version ${version}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const flags = iterator.getUint24();
|
|
34
|
+
const sampleCount = iterator.getUint32();
|
|
35
|
+
|
|
36
|
+
const dataOffset = flags & 0x01 ? iterator.getInt32() : null;
|
|
37
|
+
const firstSampleFlags = flags & 0x04 ? iterator.getUint32() : null;
|
|
38
|
+
|
|
39
|
+
const samples: TRunSample[] = [];
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
42
|
+
const sampleDuration = flags & 0x100 ? iterator.getUint32() : null;
|
|
43
|
+
const sampleSize = flags & 0x200 ? iterator.getUint32() : null;
|
|
44
|
+
const sampleFlags = flags & 0x400 ? iterator.getUint32() : null;
|
|
45
|
+
const sampleCompositionTimeOffset =
|
|
46
|
+
flags & 0x800
|
|
47
|
+
? version === 0
|
|
48
|
+
? iterator.getUint32()
|
|
49
|
+
: iterator.getInt32Le()
|
|
50
|
+
: null;
|
|
51
|
+
|
|
52
|
+
samples.push({
|
|
53
|
+
sampleDuration,
|
|
54
|
+
sampleSize,
|
|
55
|
+
sampleFlags,
|
|
56
|
+
sampleCompositionTimeOffset,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const currentOffset = iterator.counter.getOffset();
|
|
61
|
+
const left = size - (currentOffset - offset);
|
|
62
|
+
if (left !== 0) {
|
|
63
|
+
throw new Error(`Unexpected data left in TRUN box: ${left}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
type: 'trun-box',
|
|
68
|
+
version,
|
|
69
|
+
sampleCount,
|
|
70
|
+
dataOffset,
|
|
71
|
+
firstSampleFlags,
|
|
72
|
+
samples,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
@@ -221,7 +221,6 @@ export const getTrack = ({
|
|
|
221
221
|
denominator: 1,
|
|
222
222
|
},
|
|
223
223
|
timescale,
|
|
224
|
-
samplePositions: [],
|
|
225
224
|
codedHeight: height.value.value,
|
|
226
225
|
codedWidth: width.value.value,
|
|
227
226
|
displayAspectHeight: displayHeight
|
|
@@ -231,6 +230,7 @@ export const getTrack = ({
|
|
|
231
230
|
? displayWidth.value.value
|
|
232
231
|
: width.value.value,
|
|
233
232
|
rotation: 0,
|
|
233
|
+
trakBox: null,
|
|
234
234
|
};
|
|
235
235
|
}
|
|
236
236
|
|
|
@@ -245,11 +245,11 @@ export const getTrack = ({
|
|
|
245
245
|
type: 'audio',
|
|
246
246
|
trackId,
|
|
247
247
|
codec: getMatroskaAudioCodecString(track),
|
|
248
|
-
samplePositions: null,
|
|
249
248
|
timescale,
|
|
250
249
|
numberOfChannels,
|
|
251
250
|
sampleRate,
|
|
252
251
|
description: getAudioDescription(track),
|
|
252
|
+
trakBox: null,
|
|
253
253
|
};
|
|
254
254
|
}
|
|
255
255
|
|
package/src/buffer-iterator.ts
CHANGED
|
@@ -483,8 +483,8 @@ export const getArrayBufferIterator = (
|
|
|
483
483
|
},
|
|
484
484
|
getUint24: () => {
|
|
485
485
|
const val1 = view.getUint8(counter.getDiscardedOffset());
|
|
486
|
-
const val2 = view.getUint8(counter.getDiscardedOffset());
|
|
487
|
-
const val3 = view.getUint8(counter.getDiscardedOffset());
|
|
486
|
+
const val2 = view.getUint8(counter.getDiscardedOffset() + 1);
|
|
487
|
+
const val3 = view.getUint8(counter.getDiscardedOffset() + 2);
|
|
488
488
|
counter.increment(3);
|
|
489
489
|
return (val1 << 16) | (val2 << 8) | val3;
|
|
490
490
|
},
|
|
@@ -539,6 +539,7 @@ export const getArrayBufferIterator = (
|
|
|
539
539
|
},
|
|
540
540
|
getUint32Le,
|
|
541
541
|
getInt32Le,
|
|
542
|
+
getInt32,
|
|
542
543
|
destroy,
|
|
543
544
|
isMp3,
|
|
544
545
|
};
|
package/src/get-duration.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import {getSamplePositionsFromTrack} from './boxes/iso-base-media/get-sample-positions-from-track';
|
|
2
|
+
import type {TrakBox} from './boxes/iso-base-media/trak/trak';
|
|
1
3
|
import type {DurationSegment} from './boxes/webm/segments/all-segments';
|
|
4
|
+
import {getTracks} from './get-tracks';
|
|
2
5
|
import type {AnySegment} from './parse-result';
|
|
3
|
-
import {
|
|
6
|
+
import type {ParserState} from './parser-state';
|
|
7
|
+
import {getMoofBox, getMoovBox, getMvhdBox} from './traversal';
|
|
4
8
|
|
|
5
9
|
const getDurationFromMatroska = (segments: AnySegment[]): number | null => {
|
|
6
10
|
const mainSegment = segments.find((s) => s.type === 'Segment');
|
|
@@ -35,7 +39,10 @@ const getDurationFromMatroska = (segments: AnySegment[]): number | null => {
|
|
|
35
39
|
return (duration.value.value / timestampScale.value.value) * 1000;
|
|
36
40
|
};
|
|
37
41
|
|
|
38
|
-
export const getDuration = (
|
|
42
|
+
export const getDuration = (
|
|
43
|
+
boxes: AnySegment[],
|
|
44
|
+
parserState: ParserState,
|
|
45
|
+
): number | null => {
|
|
39
46
|
const matroskaBox = boxes.find((b) => b.type === 'Segment');
|
|
40
47
|
if (matroskaBox) {
|
|
41
48
|
return getDurationFromMatroska(boxes);
|
|
@@ -46,6 +53,7 @@ export const getDuration = (boxes: AnySegment[]): number | null => {
|
|
|
46
53
|
return null;
|
|
47
54
|
}
|
|
48
55
|
|
|
56
|
+
const moofBox = getMoofBox(boxes);
|
|
49
57
|
const mvhdBox = getMvhdBox(moovBox);
|
|
50
58
|
|
|
51
59
|
if (!mvhdBox) {
|
|
@@ -56,12 +64,39 @@ export const getDuration = (boxes: AnySegment[]): number | null => {
|
|
|
56
64
|
throw new Error('Expected mvhd-box');
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
|
|
67
|
+
if (mvhdBox.durationInSeconds > 0) {
|
|
68
|
+
return mvhdBox.durationInSeconds;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const tracks = getTracks(boxes, parserState);
|
|
72
|
+
const allTracks = [
|
|
73
|
+
...tracks.videoTracks,
|
|
74
|
+
...tracks.audioTracks,
|
|
75
|
+
...tracks.otherTracks,
|
|
76
|
+
];
|
|
77
|
+
const allSamples = allTracks.map((t) => {
|
|
78
|
+
const {timescale: ts} = t;
|
|
79
|
+
const samplePositions = getSamplePositionsFromTrack(
|
|
80
|
+
t.trakBox as TrakBox,
|
|
81
|
+
moofBox,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const highest = samplePositions
|
|
85
|
+
?.map((sp) => (sp.cts + sp.duration) / ts)
|
|
86
|
+
.reduce((a, b) => Math.max(a, b), 0);
|
|
87
|
+
return highest ?? 0;
|
|
88
|
+
});
|
|
89
|
+
const highestTimestamp = Math.max(...allSamples);
|
|
90
|
+
return highestTimestamp;
|
|
60
91
|
};
|
|
61
92
|
|
|
62
|
-
export const hasDuration = (
|
|
93
|
+
export const hasDuration = (
|
|
94
|
+
boxes: AnySegment[],
|
|
95
|
+
parserState: ParserState,
|
|
96
|
+
): boolean => {
|
|
63
97
|
try {
|
|
64
|
-
|
|
98
|
+
const duration = getDuration(boxes, parserState);
|
|
99
|
+
return getDuration(boxes, parserState) !== null && duration !== 0;
|
|
65
100
|
} catch (err) {
|
|
66
101
|
return false;
|
|
67
102
|
}
|
package/src/get-tracks.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {makeBaseMediaTrack} from './boxes/iso-base-media/make-track';
|
|
2
2
|
import type {MoovBox} from './boxes/iso-base-media/moov/moov';
|
|
3
|
+
import type {TrakBox} from './boxes/iso-base-media/trak/trak';
|
|
3
4
|
import {getTracksFromMatroska} from './boxes/webm/get-ready-tracks';
|
|
4
5
|
import {getMainSegment} from './boxes/webm/traversal';
|
|
5
|
-
import type {SamplePosition} from './get-sample-positions';
|
|
6
6
|
import type {AnySegment} from './parse-result';
|
|
7
7
|
import type {ParserState} from './parser-state';
|
|
8
8
|
import {getMoovBox, getMvhdBox, getTracksSegment, getTraks} from './traversal';
|
|
@@ -14,7 +14,6 @@ type SampleAspectRatio = {
|
|
|
14
14
|
|
|
15
15
|
export type VideoTrack = {
|
|
16
16
|
type: 'video';
|
|
17
|
-
samplePositions: SamplePosition[] | null;
|
|
18
17
|
trackId: number;
|
|
19
18
|
description: Uint8Array | undefined;
|
|
20
19
|
timescale: number;
|
|
@@ -27,24 +26,25 @@ export type VideoTrack = {
|
|
|
27
26
|
codedWidth: number;
|
|
28
27
|
codedHeight: number;
|
|
29
28
|
rotation: number;
|
|
29
|
+
trakBox: TrakBox | null;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export type AudioTrack = {
|
|
33
33
|
type: 'audio';
|
|
34
|
-
samplePositions: SamplePosition[] | null;
|
|
35
34
|
trackId: number;
|
|
36
35
|
timescale: number;
|
|
37
36
|
codec: string;
|
|
38
37
|
numberOfChannels: number;
|
|
39
38
|
sampleRate: number;
|
|
40
39
|
description: Uint8Array | undefined;
|
|
40
|
+
trakBox: TrakBox | null;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export type OtherTrack = {
|
|
44
44
|
type: 'other';
|
|
45
|
-
samplePositions: SamplePosition[] | null;
|
|
46
45
|
trackId: number;
|
|
47
46
|
timescale: number;
|
|
47
|
+
trakBox: TrakBox | null;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
export type Track = VideoTrack | AudioTrack | OtherTrack;
|
package/src/has-all-info.ts
CHANGED
package/src/parse-media.ts
CHANGED
|
@@ -149,7 +149,7 @@ export const parseMedia: ParseMedia = async ({
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
if (fields?.durationInSeconds) {
|
|
152
|
-
returnValue.durationInSeconds = getDuration(parseResult.segments);
|
|
152
|
+
returnValue.durationInSeconds = getDuration(parseResult.segments, state);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
if (fields?.fps) {
|
package/src/parse-result.ts
CHANGED
|
@@ -19,8 +19,11 @@ import type {StsdBox} from './boxes/iso-base-media/stsd/stsd';
|
|
|
19
19
|
import type {StssBox} from './boxes/iso-base-media/stsd/stss';
|
|
20
20
|
import type {StszBox} from './boxes/iso-base-media/stsd/stsz';
|
|
21
21
|
import type {SttsBox} from './boxes/iso-base-media/stsd/stts';
|
|
22
|
+
import type {TfdtBox} from './boxes/iso-base-media/tfdt';
|
|
23
|
+
import type {TfhdBox} from './boxes/iso-base-media/tfhd';
|
|
22
24
|
import type {TkhdBox} from './boxes/iso-base-media/tkhd';
|
|
23
25
|
import type {TrakBox} from './boxes/iso-base-media/trak/trak';
|
|
26
|
+
import type {TrunBox} from './boxes/iso-base-media/trun';
|
|
24
27
|
import type {VoidBox} from './boxes/iso-base-media/void-box';
|
|
25
28
|
import type {MatroskaSegment} from './boxes/webm/segments';
|
|
26
29
|
|
|
@@ -56,7 +59,10 @@ export type IsoBaseMediaBox =
|
|
|
56
59
|
| PaspBox
|
|
57
60
|
| CttsBox
|
|
58
61
|
| Av1CBox
|
|
59
|
-
|
|
|
62
|
+
| TrunBox
|
|
63
|
+
| ColorParameterBox
|
|
64
|
+
| TfdtBox
|
|
65
|
+
| TfhdBox;
|
|
60
66
|
|
|
61
67
|
export type AnySegment = MatroskaSegment | IsoBaseMediaBox;
|
|
62
68
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type {SamplePosition} from './get-sample-positions';
|
|
2
|
+
import type {AnySegment, IsoBaseMediaBox} from './parse-result';
|
|
3
|
+
import {getTfdtBox, getTfhdBox, getTrunBoxes} from './traversal';
|
|
4
|
+
|
|
5
|
+
const getSamplesFromTraf = (
|
|
6
|
+
trafSegment: IsoBaseMediaBox,
|
|
7
|
+
moofOffset: number,
|
|
8
|
+
): SamplePosition[] => {
|
|
9
|
+
if (trafSegment.type !== 'regular-box' || trafSegment.boxType !== 'traf') {
|
|
10
|
+
throw new Error('Expected traf-box');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const tfhdBox = getTfhdBox(trafSegment);
|
|
14
|
+
const defaultSampleDuration = tfhdBox?.defaultSampleDuration ?? null;
|
|
15
|
+
const defaultSampleSize = tfhdBox?.defaultSampleSize ?? null;
|
|
16
|
+
const defaultSampleFlags = tfhdBox?.defaultSampleFlags ?? null;
|
|
17
|
+
|
|
18
|
+
const tfdtBox = getTfdtBox(trafSegment);
|
|
19
|
+
const trunBoxes = getTrunBoxes(trafSegment);
|
|
20
|
+
|
|
21
|
+
let time = 0;
|
|
22
|
+
let offset = 0;
|
|
23
|
+
|
|
24
|
+
let dataOffset = 0;
|
|
25
|
+
|
|
26
|
+
const samples: SamplePosition[] = [];
|
|
27
|
+
|
|
28
|
+
for (const trunBox of trunBoxes) {
|
|
29
|
+
let i = -1;
|
|
30
|
+
|
|
31
|
+
if (dataOffset === 0 && trunBox.dataOffset) {
|
|
32
|
+
dataOffset = trunBox.dataOffset;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (const sample of trunBox.samples) {
|
|
36
|
+
i++;
|
|
37
|
+
const duration = sample.sampleDuration ?? defaultSampleDuration;
|
|
38
|
+
if (duration === null) {
|
|
39
|
+
throw new Error('Expected duration');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const size = sample.sampleSize ?? defaultSampleSize;
|
|
43
|
+
if (size === null) {
|
|
44
|
+
throw new Error('Expected size');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const isFirstSample = i === 0;
|
|
48
|
+
const sampleFlags = sample.sampleFlags
|
|
49
|
+
? sample.sampleFlags
|
|
50
|
+
: isFirstSample && trunBox.firstSampleFlags !== null
|
|
51
|
+
? trunBox.firstSampleFlags
|
|
52
|
+
: defaultSampleFlags;
|
|
53
|
+
if (sampleFlags === null) {
|
|
54
|
+
throw new Error('Expected sample flags');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const keyframe = !((sampleFlags >> 16) & 0x1);
|
|
58
|
+
|
|
59
|
+
const dts = time + (tfdtBox?.baseMediaDecodeTime ?? 0);
|
|
60
|
+
|
|
61
|
+
const samplePosition: SamplePosition = {
|
|
62
|
+
offset: offset + (moofOffset ?? 0) + (dataOffset ?? 0),
|
|
63
|
+
dts,
|
|
64
|
+
cts: dts,
|
|
65
|
+
duration,
|
|
66
|
+
isKeyframe: keyframe,
|
|
67
|
+
size,
|
|
68
|
+
};
|
|
69
|
+
samples.push(samplePosition);
|
|
70
|
+
offset += size;
|
|
71
|
+
time += duration;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return samples;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const getSamplesFromMoof = ({
|
|
79
|
+
moofBox,
|
|
80
|
+
trackId,
|
|
81
|
+
}: {
|
|
82
|
+
moofBox: AnySegment;
|
|
83
|
+
trackId: number;
|
|
84
|
+
}) => {
|
|
85
|
+
if (moofBox.type !== 'regular-box') {
|
|
86
|
+
throw new Error('Expected moof-box');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const trafs = moofBox.children.filter(
|
|
90
|
+
(c) => c.type === 'regular-box' && c.boxType === 'traf',
|
|
91
|
+
) as IsoBaseMediaBox[];
|
|
92
|
+
|
|
93
|
+
const mapped = trafs.map((traf) => {
|
|
94
|
+
const tfhdBox = getTfhdBox(traf);
|
|
95
|
+
|
|
96
|
+
return tfhdBox?.trackId === trackId
|
|
97
|
+
? getSamplesFromTraf(traf, moofBox.offset)
|
|
98
|
+
: [];
|
|
99
|
+
});
|
|
100
|
+
return mapped.flat(1);
|
|
101
|
+
};
|