@remotion/media-parser 4.0.202 → 4.0.205

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 (106) 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/esds/decoder-specific-config.js +6 -1
  4. package/dist/boxes/iso-base-media/ftype.d.ts +9 -0
  5. package/dist/boxes/iso-base-media/ftype.js +31 -0
  6. package/dist/boxes/iso-base-media/get-sample-positions-from-track.d.ts +4 -0
  7. package/dist/boxes/iso-base-media/get-sample-positions-from-track.js +48 -0
  8. package/dist/boxes/iso-base-media/make-track.js +3 -30
  9. package/dist/boxes/iso-base-media/mdat/mdat.js +25 -22
  10. package/dist/boxes/iso-base-media/mdhd.d.ts +2 -0
  11. package/dist/boxes/iso-base-media/mdhd.js +6 -7
  12. package/dist/boxes/iso-base-media/mvhd.js +11 -13
  13. package/dist/boxes/iso-base-media/process-box.js +36 -0
  14. package/dist/boxes/iso-base-media/tfdt.d.ts +12 -0
  15. package/dist/boxes/iso-base-media/tfdt.js +20 -0
  16. package/dist/boxes/iso-base-media/tfhd.d.ts +16 -0
  17. package/dist/boxes/iso-base-media/tfhd.js +41 -0
  18. package/dist/boxes/iso-base-media/tkhd.js +9 -12
  19. package/dist/boxes/iso-base-media/trun.d.ts +21 -0
  20. package/dist/boxes/iso-base-media/trun.js +44 -0
  21. package/dist/boxes/webm/bitstream/av1.js +1 -10
  22. package/dist/boxes/webm/ebml.d.ts +1 -1
  23. package/dist/boxes/webm/get-track.js +2 -2
  24. package/dist/boxes/webm/segments/seek-position.js +1 -1
  25. package/dist/boxes/webm/segments/seek.d.ts +1 -1
  26. package/dist/boxes/webm/segments/seek.js +8 -2
  27. package/dist/boxes/webm/segments/timestamp-scale.js +1 -1
  28. package/dist/boxes/webm/tracks.d.ts +8 -0
  29. package/dist/boxes/webm/tracks.js +21 -0
  30. package/dist/buffer-iterator.d.ts +1 -0
  31. package/dist/buffer-iterator.js +3 -2
  32. package/dist/create/cluster-segment.d.ts +10 -0
  33. package/dist/create/cluster-segment.js +41 -0
  34. package/dist/create/create-media.d.ts +6 -1
  35. package/dist/create/create-media.js +58 -4
  36. package/dist/create/matroska-header.d.ts +1 -1
  37. package/dist/create/matroska-info.d.ts +1 -1
  38. package/dist/create/matroska-info.js +2 -2
  39. package/dist/create/matroska-segment.d.ts +2 -1
  40. package/dist/create/matroska-trackentry.d.ts +5 -4
  41. package/dist/from-web.js +6 -15
  42. package/dist/get-duration.d.ts +3 -2
  43. package/dist/get-duration.js +24 -4
  44. package/dist/get-tracks.d.ts +4 -4
  45. package/dist/get-video-metadata.d.ts +2 -0
  46. package/dist/get-video-metadata.js +44 -0
  47. package/dist/has-all-info.js +1 -1
  48. package/dist/parse-media.js +1 -1
  49. package/dist/parse-result.d.ts +4 -1
  50. package/dist/read-and-increment-offset.d.ts +28 -0
  51. package/dist/read-and-increment-offset.js +177 -0
  52. package/dist/samples-from-moof.d.ts +6 -0
  53. package/dist/samples-from-moof.js +75 -0
  54. package/dist/traversal.d.ts +8 -1
  55. package/dist/traversal.js +39 -1
  56. package/dist/understand-vorbis.d.ts +1 -0
  57. package/dist/understand-vorbis.js +12 -0
  58. package/dist/writers/web-fs.js +21 -5
  59. package/dist/writers/writer.d.ts +3 -1
  60. package/package.json +2 -2
  61. package/src/boxes/iso-base-media/esds/decoder-specific-config.ts +8 -1
  62. package/src/boxes/iso-base-media/get-sample-positions-from-track.ts +69 -0
  63. package/src/boxes/iso-base-media/make-track.ts +4 -45
  64. package/src/boxes/iso-base-media/mdat/mdat.ts +33 -24
  65. package/src/boxes/iso-base-media/mdhd.ts +10 -7
  66. package/src/boxes/iso-base-media/mvhd.ts +15 -14
  67. package/src/boxes/iso-base-media/process-box.ts +42 -0
  68. package/src/boxes/iso-base-media/tfdt.ts +37 -0
  69. package/src/boxes/iso-base-media/tfhd.ts +66 -0
  70. package/src/boxes/iso-base-media/tkhd.ts +11 -13
  71. package/src/boxes/iso-base-media/trun.ts +74 -0
  72. package/src/boxes/webm/get-track.ts +2 -2
  73. package/src/buffer-iterator.ts +3 -2
  74. package/src/get-duration.ts +40 -5
  75. package/src/get-tracks.ts +4 -4
  76. package/src/has-all-info.ts +1 -1
  77. package/src/parse-media.ts +1 -1
  78. package/src/parse-result.ts +7 -1
  79. package/src/samples-from-moof.ts +102 -0
  80. package/src/test/samples-from-moof.test.ts +2496 -0
  81. package/src/test/stream-local.test.ts +28 -30
  82. package/src/test/stream-samples.test.ts +153 -231
  83. package/src/traversal.ts +56 -1
  84. package/tsconfig.tsbuildinfo +1 -1
  85. package/dist/bitstream/av1.d.ts +0 -2
  86. package/dist/bitstream/av1.js +0 -12
  87. package/dist/boxes/iso-base-media/avcc-hvcc.d.ts +0 -20
  88. package/dist/boxes/iso-base-media/avcc-hvcc.js +0 -73
  89. package/dist/boxes/iso-base-media/avcc.d.ts +0 -18
  90. package/dist/boxes/iso-base-media/avcc.js +0 -27
  91. package/dist/boxes/iso-base-media/esds-descriptors.d.ts +0 -21
  92. package/dist/boxes/iso-base-media/esds-descriptors.js +0 -62
  93. package/dist/boxes/iso-base-media/esds.d.ts +0 -15
  94. package/dist/boxes/iso-base-media/esds.js +0 -27
  95. package/dist/create-media.d.ts +0 -1
  96. package/dist/create-media.js +0 -78
  97. package/dist/from-input-type-file.d.ts +0 -2
  98. package/dist/from-input-type-file.js +0 -37
  99. package/dist/get-codec.d.ts +0 -4
  100. package/dist/get-codec.js +0 -22
  101. package/dist/web-file.d.ts +0 -2
  102. package/dist/web-file.js +0 -37
  103. /package/dist/{get-samples.d.ts → boxes/webm/bitstream/av1/frame.d.ts} +0 -0
  104. /package/dist/{get-samples.js → boxes/webm/bitstream/av1/frame.js} +0 -0
  105. /package/dist/{sample-aspect-ratio.d.ts → boxes/webm/bitstream/h264/get-h264-descriptor.d.ts} +0 -0
  106. /package/dist/{sample-aspect-ratio.js → boxes/webm/bitstream/h264/get-h264-descriptor.js} +0 -0
@@ -15,31 +15,15 @@ import {
15
15
  getSampleAspectRatio,
16
16
  getVideoSample,
17
17
  } from '../../get-sample-aspect-ratio';
18
- import {getSamplePositions} from '../../get-sample-positions';
19
18
  import type {AudioTrack, OtherTrack, VideoTrack} from '../../get-tracks';
20
19
  import {getVideoCodecString} from '../../get-video-codec';
21
- import {
22
- getCttsBox,
23
- getStcoBox,
24
- getStscBox,
25
- getStssBox,
26
- getStszBox,
27
- getSttsBox,
28
- getTkhdBox,
29
- getVideoDescriptors,
30
- } from '../../traversal';
20
+ import {getTkhdBox, getVideoDescriptors} from '../../traversal';
31
21
  import type {TrakBox} from './trak/trak';
32
22
 
33
23
  export const makeBaseMediaTrack = (
34
24
  trakBox: TrakBox,
35
25
  ): VideoTrack | AudioTrack | OtherTrack | null => {
36
- const stszBox = getStszBox(trakBox);
37
- const stcoBox = getStcoBox(trakBox);
38
- const stscBox = getStscBox(trakBox);
39
- const stssBox = getStssBox(trakBox);
40
- const sttsBox = getSttsBox(trakBox);
41
26
  const tkhdBox = getTkhdBox(trakBox);
42
- const cttsBox = getCttsBox(trakBox);
43
27
 
44
28
  const videoDescriptors = getVideoDescriptors(trakBox);
45
29
  const timescaleAndDuration = getTimescaleAndDuration(trakBox);
@@ -48,35 +32,10 @@ export const makeBaseMediaTrack = (
48
32
  throw new Error('Expected tkhd box in trak box');
49
33
  }
50
34
 
51
- if (!stszBox) {
52
- throw new Error('Expected stsz box in trak box');
53
- }
54
-
55
- if (!stcoBox) {
56
- throw new Error('Expected stco box in trak box');
57
- }
58
-
59
- if (!stscBox) {
60
- throw new Error('Expected stsc box in trak box');
61
- }
62
-
63
- if (!sttsBox) {
64
- throw new Error('Expected stts box in trak box');
65
- }
66
-
67
35
  if (!timescaleAndDuration) {
68
36
  throw new Error('Expected timescale and duration in trak box');
69
37
  }
70
38
 
71
- const samplePositions = getSamplePositions({
72
- stcoBox,
73
- stscBox,
74
- stszBox,
75
- stssBox,
76
- sttsBox,
77
- cttsBox,
78
- });
79
-
80
39
  if (trakBoxContainsAudio(trakBox)) {
81
40
  const numberOfChannels = getNumberOfChannelsFromTrak(trakBox);
82
41
  if (numberOfChannels === null) {
@@ -92,22 +51,22 @@ export const makeBaseMediaTrack = (
92
51
 
93
52
  return {
94
53
  type: 'audio',
95
- samplePositions,
96
54
  trackId: tkhdBox.trackId,
97
55
  timescale: timescaleAndDuration.timescale,
98
56
  codec: codecString,
99
57
  numberOfChannels,
100
58
  sampleRate,
101
59
  description,
60
+ trakBox,
102
61
  };
103
62
  }
104
63
 
105
64
  if (!trakBoxContainsVideo(trakBox)) {
106
65
  return {
107
66
  type: 'other',
108
- samplePositions,
109
67
  trackId: tkhdBox.trackId,
110
68
  timescale: timescaleAndDuration.timescale,
69
+ trakBox,
111
70
  };
112
71
  }
113
72
 
@@ -138,7 +97,6 @@ export const makeBaseMediaTrack = (
138
97
 
139
98
  const track: VideoTrack = {
140
99
  type: 'video',
141
- samplePositions,
142
100
  trackId: tkhdBox.trackId,
143
101
  description: videoDescriptors ?? undefined,
144
102
  timescale: timescaleAndDuration.timescale,
@@ -152,6 +110,7 @@ export const makeBaseMediaTrack = (
152
110
  displayAspectWidth,
153
111
  displayAspectHeight,
154
112
  rotation,
113
+ trakBox,
155
114
  };
156
115
  return track;
157
116
  };
@@ -3,6 +3,9 @@ import type {BufferIterator} from '../../../buffer-iterator';
3
3
  import {getTracks, hasTracks} from '../../../get-tracks';
4
4
  import type {AnySegment} from '../../../parse-result';
5
5
  import type {ParserContext} from '../../../parser-context';
6
+ import {getMoofBox} from '../../../traversal';
7
+ import {getSamplePositionsFromTrack} from '../get-sample-positions-from-track';
8
+ import type {TrakBox} from '../trak/trak';
6
9
 
7
10
  export interface MdatBox {
8
11
  type: 'mdat-box';
@@ -44,13 +47,17 @@ export const parseMdat = async ({
44
47
 
45
48
  const flatSamples = allTracks
46
49
  .map((track) => {
47
- if (!track.samplePositions) {
50
+ const samplePositions = getSamplePositionsFromTrack(
51
+ track.trakBox as TrakBox,
52
+ getMoofBox(existingBoxes),
53
+ );
54
+ if (!samplePositions) {
48
55
  throw new Error('No sample positions');
49
56
  }
50
57
 
51
- return track.samplePositions.map((samplePosition) => {
58
+ return samplePositions.map((samplePosition) => {
52
59
  return {
53
- track,
60
+ track: {...track},
54
61
  samplePosition,
55
62
  };
56
63
  });
@@ -59,10 +66,10 @@ export const parseMdat = async ({
59
66
 
60
67
  // eslint-disable-next-line no-constant-condition
61
68
  while (true) {
62
- const sampleWithIndex = flatSamples.find((sample) => {
69
+ const samplesWithIndex = flatSamples.find((sample) => {
63
70
  return sample.samplePosition.offset === data.counter.getOffset();
64
71
  });
65
- if (!sampleWithIndex) {
72
+ if (!samplesWithIndex) {
66
73
  // There are various reasons why in mdat we find weird stuff:
67
74
  // - iphonevideo.hevc has a fake hoov atom which is not mapped
68
75
  // - corrupted.mp4 has a corrupt table
@@ -81,37 +88,39 @@ export const parseMdat = async ({
81
88
  }
82
89
  }
83
90
 
84
- if (data.bytesRemaining() < sampleWithIndex.samplePosition.size) {
91
+ if (data.bytesRemaining() < samplesWithIndex.samplePosition.size) {
85
92
  break;
86
93
  }
87
94
 
88
- const bytes = data.getSlice(sampleWithIndex.samplePosition.size);
95
+ const bytes = data.getSlice(samplesWithIndex.samplePosition.size);
89
96
 
90
- if (sampleWithIndex.track.type === 'audio') {
91
- await options.parserState.onAudioSample(sampleWithIndex.track.trackId, {
97
+ if (samplesWithIndex.track.type === 'audio') {
98
+ await options.parserState.onAudioSample(samplesWithIndex.track.trackId, {
92
99
  data: bytes,
93
- timestamp: sampleWithIndex.samplePosition.offset,
94
- trackId: sampleWithIndex.track.trackId,
95
- type: sampleWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
100
+ timestamp: samplesWithIndex.samplePosition.offset,
101
+ trackId: samplesWithIndex.track.trackId,
102
+ type: samplesWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
96
103
  });
97
104
  }
98
105
 
99
- if (sampleWithIndex.track.type === 'video') {
100
- const timestamp =
101
- (sampleWithIndex.samplePosition.cts * 1_000_000) /
102
- sampleWithIndex.track.timescale;
103
- const duration =
104
- (sampleWithIndex.samplePosition.duration * 1_000_000) /
105
- sampleWithIndex.track.timescale;
106
+ if (samplesWithIndex.track.type === 'video') {
107
+ const timestamp = Math.floor(
108
+ (samplesWithIndex.samplePosition.cts * 1_000_000) /
109
+ samplesWithIndex.track.timescale,
110
+ );
111
+ const duration = Math.floor(
112
+ (samplesWithIndex.samplePosition.duration * 1_000_000) /
113
+ samplesWithIndex.track.timescale,
114
+ );
106
115
 
107
- await options.parserState.onVideoSample(sampleWithIndex.track.trackId, {
116
+ await options.parserState.onVideoSample(samplesWithIndex.track.trackId, {
108
117
  data: bytes,
109
118
  timestamp,
110
119
  duration,
111
- cts: sampleWithIndex.samplePosition.cts,
112
- dts: sampleWithIndex.samplePosition.dts,
113
- trackId: sampleWithIndex.track.trackId,
114
- type: sampleWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
120
+ cts: samplesWithIndex.samplePosition.cts,
121
+ dts: samplesWithIndex.samplePosition.dts,
122
+ trackId: samplesWithIndex.track.trackId,
123
+ type: samplesWithIndex.samplePosition.isKeyframe ? 'key' : 'delta',
115
124
  });
116
125
  }
117
126
 
@@ -7,6 +7,8 @@ export interface MdhdBox {
7
7
  duration: number;
8
8
  language: number;
9
9
  quality: number;
10
+ creationTime: number | null;
11
+ modificationTime: number | null;
10
12
  }
11
13
 
12
14
  export const parseMdhd = ({
@@ -19,21 +21,20 @@ export const parseMdhd = ({
19
21
  fileOffset: number;
20
22
  }): MdhdBox => {
21
23
  const version = data.getUint8();
22
- if (version !== 0) {
23
- throw new Error(`Unsupported MDHD version ${version}`);
24
- }
25
24
 
26
25
  // flags, we discard them
27
26
  data.discard(3);
28
27
 
29
28
  // creation time
30
- data.discard(4);
29
+ const creationTime =
30
+ version === 1 ? Number(data.getUint64()) : data.getUint32();
31
31
 
32
32
  // modification time
33
- data.discard(4);
33
+ const modificationTime =
34
+ version === 1 ? Number(data.getUint64()) : data.getUint32();
34
35
 
35
36
  const timescale = data.getUint32();
36
- const duration = data.getUint32();
37
+ const duration = version === 1 ? data.getUint64() : data.getUint32();
37
38
 
38
39
  const language = data.getUint16();
39
40
 
@@ -47,10 +48,12 @@ export const parseMdhd = ({
47
48
 
48
49
  return {
49
50
  type: 'mdhd-box',
50
- duration,
51
+ duration: Number(duration),
51
52
  timescale,
52
53
  version,
53
54
  language,
54
55
  quality,
56
+ creationTime,
57
+ modificationTime,
55
58
  };
56
59
  };
@@ -38,25 +38,21 @@ export const parseMvhd = ({
38
38
  size: number;
39
39
  }): MvhdBox => {
40
40
  const version = iterator.getUint8();
41
- if (version !== 0) {
42
- throw new Error(`Unsupported MVHD version ${version}`);
43
- }
44
-
45
- if (size !== 108) {
46
- throw new Error(`Expected mvhd size of version 0 to be 108, got ${size}`);
47
- }
48
41
 
49
42
  // Flags, we discard them
50
43
  iterator.discard(3);
51
44
 
52
- const creationTime = iterator.getUint32();
45
+ const creationTime =
46
+ version === 1 ? iterator.getUint64() : iterator.getUint32();
53
47
 
54
- const modificationTime = iterator.getUint32();
48
+ const modificationTime =
49
+ version === 1 ? iterator.getUint64() : iterator.getUint32();
55
50
 
56
51
  const timeScale = iterator.getUint32();
57
52
 
58
- const durationInUnits = iterator.getUint32();
59
- const durationInSeconds = durationInUnits / timeScale;
53
+ const durationInUnits =
54
+ version === 1 ? iterator.getUint64() : iterator.getUint32();
55
+ const durationInSeconds = Number(durationInUnits) / timeScale;
60
56
 
61
57
  const rateArray = iterator.getSlice(4);
62
58
  const rateView = getArrayBufferIterator(rateArray, rateArray.length);
@@ -96,11 +92,16 @@ export const parseMvhd = ({
96
92
 
97
93
  volumeView.destroy();
98
94
 
95
+ const bytesRemaining = size - (iterator.counter.getOffset() - offset);
96
+ if (bytesRemaining !== 0) {
97
+ throw new Error('expected 0 bytes ' + bytesRemaining);
98
+ }
99
+
99
100
  return {
100
- creationTime: toUnixTimestamp(creationTime),
101
- modificationTime: toUnixTimestamp(modificationTime),
101
+ creationTime: toUnixTimestamp(Number(creationTime)),
102
+ modificationTime: toUnixTimestamp(Number(modificationTime)),
102
103
  timeScale,
103
- durationInUnits,
104
+ durationInUnits: Number(durationInUnits),
104
105
  durationInSeconds,
105
106
  rate,
106
107
  volume,
@@ -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
  };