@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.
Files changed (97) hide show
  1. package/dist/av1-codec-string.d.ts +0 -5
  2. package/dist/av1-codec-string.js +1 -18
  3. package/dist/boxes/iso-base-media/ftype.d.ts +9 -0
  4. package/dist/boxes/iso-base-media/ftype.js +31 -0
  5. package/dist/boxes/iso-base-media/get-sample-positions-from-track.d.ts +4 -0
  6. package/dist/boxes/iso-base-media/get-sample-positions-from-track.js +48 -0
  7. package/dist/boxes/iso-base-media/tfdt.d.ts +12 -0
  8. package/dist/boxes/iso-base-media/tfdt.js +20 -0
  9. package/dist/boxes/iso-base-media/tfhd.d.ts +16 -0
  10. package/dist/boxes/iso-base-media/tfhd.js +41 -0
  11. package/dist/boxes/iso-base-media/trun.d.ts +21 -0
  12. package/dist/boxes/iso-base-media/trun.js +44 -0
  13. package/dist/boxes/webm/bitstream/av1.js +1 -10
  14. package/dist/boxes/webm/ebml.d.ts +1 -1
  15. package/dist/boxes/webm/segments/seek-position.js +1 -1
  16. package/dist/boxes/webm/segments/seek.d.ts +1 -1
  17. package/dist/boxes/webm/segments/seek.js +8 -2
  18. package/dist/boxes/webm/segments/timestamp-scale.js +1 -1
  19. package/dist/boxes/webm/tracks.d.ts +8 -0
  20. package/dist/boxes/webm/tracks.js +21 -0
  21. package/dist/from-web.js +6 -15
  22. package/dist/get-video-metadata.d.ts +2 -0
  23. package/dist/get-video-metadata.js +44 -0
  24. package/dist/read-and-increment-offset.d.ts +28 -0
  25. package/dist/read-and-increment-offset.js +177 -0
  26. package/dist/samples-from-moof.d.ts +6 -0
  27. package/dist/samples-from-moof.js +74 -0
  28. package/dist/understand-vorbis.d.ts +1 -0
  29. package/dist/understand-vorbis.js +12 -0
  30. package/package.json +2 -2
  31. package/src/boxes/iso-base-media/get-sample-positions-from-track.ts +69 -0
  32. package/src/boxes/iso-base-media/make-track.ts +4 -45
  33. package/src/boxes/iso-base-media/mdat/mdat.ts +33 -24
  34. package/src/boxes/iso-base-media/mdhd.ts +10 -7
  35. package/src/boxes/iso-base-media/mvhd.ts +15 -14
  36. package/src/boxes/iso-base-media/process-box.ts +42 -0
  37. package/src/boxes/iso-base-media/tfdt.ts +37 -0
  38. package/src/boxes/iso-base-media/tfhd.ts +66 -0
  39. package/src/boxes/iso-base-media/tkhd.ts +11 -13
  40. package/src/boxes/iso-base-media/trun.ts +74 -0
  41. package/src/boxes/webm/get-track.ts +2 -2
  42. package/src/buffer-iterator.ts +3 -2
  43. package/src/get-duration.ts +40 -5
  44. package/src/get-tracks.ts +4 -4
  45. package/src/has-all-info.ts +1 -1
  46. package/src/parse-media.ts +1 -1
  47. package/src/parse-result.ts +7 -1
  48. package/src/samples-from-moof.ts +101 -0
  49. package/src/test/samples-from-moof.test.ts +2496 -0
  50. package/src/test/stream-local.test.ts +28 -30
  51. package/src/test/stream-samples.test.ts +153 -231
  52. package/src/traversal.ts +56 -1
  53. package/tsconfig.tsbuildinfo +1 -1
  54. package/dist/bitstream/av1.d.ts +0 -2
  55. package/dist/bitstream/av1.js +0 -12
  56. package/dist/boxes/iso-base-media/avcc-hvcc.d.ts +0 -20
  57. package/dist/boxes/iso-base-media/avcc-hvcc.js +0 -73
  58. package/dist/boxes/iso-base-media/avcc.d.ts +0 -18
  59. package/dist/boxes/iso-base-media/avcc.js +0 -27
  60. package/dist/boxes/iso-base-media/esds-descriptors.d.ts +0 -21
  61. package/dist/boxes/iso-base-media/esds-descriptors.js +0 -62
  62. package/dist/boxes/iso-base-media/esds.d.ts +0 -15
  63. package/dist/boxes/iso-base-media/esds.js +0 -27
  64. package/dist/create/create-media.d.ts +0 -2
  65. package/dist/create/create-media.js +0 -36
  66. package/dist/create/matroska-header.d.ts +0 -1
  67. package/dist/create/matroska-header.js +0 -66
  68. package/dist/create/matroska-info.d.ts +0 -4
  69. package/dist/create/matroska-info.js +0 -39
  70. package/dist/create/matroska-segment.d.ts +0 -1
  71. package/dist/create/matroska-segment.js +0 -12
  72. package/dist/create/matroska-trackentry.d.ts +0 -21
  73. package/dist/create/matroska-trackentry.js +0 -191
  74. package/dist/create-media.d.ts +0 -1
  75. package/dist/create-media.js +0 -78
  76. package/dist/from-input-type-file.d.ts +0 -2
  77. package/dist/from-input-type-file.js +0 -37
  78. package/dist/get-codec.d.ts +0 -4
  79. package/dist/get-codec.js +0 -22
  80. package/dist/readers/from-fetch.d.ts +0 -2
  81. package/dist/readers/from-fetch.js +0 -64
  82. package/dist/readers/from-node.d.ts +0 -2
  83. package/dist/readers/from-node.js +0 -40
  84. package/dist/readers/from-web-file.d.ts +0 -2
  85. package/dist/readers/from-web-file.js +0 -39
  86. package/dist/readers/reader.d.ts +0 -11
  87. package/dist/readers/reader.js +0 -2
  88. package/dist/web-file.d.ts +0 -2
  89. package/dist/web-file.js +0 -37
  90. package/dist/writers/web-fs.d.ts +0 -2
  91. package/dist/writers/web-fs.js +0 -28
  92. package/dist/writers/writer.d.ts +0 -9
  93. package/dist/writers/writer.js +0 -2
  94. /package/dist/{get-samples.d.ts → boxes/webm/bitstream/av1/frame.d.ts} +0 -0
  95. /package/dist/{get-samples.js → boxes/webm/bitstream/av1/frame.js} +0 -0
  96. /package/dist/{sample-aspect-ratio.d.ts → boxes/webm/bitstream/h264/get-h264-descriptor.d.ts} +0 -0
  97. /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 = iterator.getUint32();
77
+ const creationTime =
78
+ version === 1 ? iterator.getUint64() : iterator.getUint32();
82
79
 
83
- const modificationTime = iterator.getUint32();
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
 
@@ -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
  };
@@ -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 {getMoovBox, getMvhdBox} from './traversal';
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 = (boxes: AnySegment[]): number | null => {
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
- return mvhdBox.durationInSeconds;
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 = (boxes: AnySegment[]): boolean => {
93
+ export const hasDuration = (
94
+ boxes: AnySegment[],
95
+ parserState: ParserState,
96
+ ): boolean => {
63
97
  try {
64
- return getDuration(boxes) !== null;
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;
@@ -45,7 +45,7 @@ export const hasAllInfo = (
45
45
  }
46
46
 
47
47
  if (key === 'durationInSeconds') {
48
- return hasDuration(parseResult.segments);
48
+ return hasDuration(parseResult.segments, state);
49
49
  }
50
50
 
51
51
  if (
@@ -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) {
@@ -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
- | ColorParameterBox;
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
+ };