@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
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _OffsetCounter_offset;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getArrayBufferIterator = exports.OffsetCounter = void 0;
16
+ class OffsetCounter {
17
+ constructor(initial) {
18
+ _OffsetCounter_offset.set(this, void 0);
19
+ __classPrivateFieldSet(this, _OffsetCounter_offset, initial, "f");
20
+ }
21
+ increment(amount) {
22
+ if (amount < 0) {
23
+ throw new Error('Cannot increment by a negative amount');
24
+ }
25
+ __classPrivateFieldSet(this, _OffsetCounter_offset, __classPrivateFieldGet(this, _OffsetCounter_offset, "f") + amount, "f");
26
+ }
27
+ getOffset() {
28
+ return __classPrivateFieldGet(this, _OffsetCounter_offset, "f");
29
+ }
30
+ }
31
+ exports.OffsetCounter = OffsetCounter;
32
+ _OffsetCounter_offset = new WeakMap();
33
+ const makeOffsetCounter = (initial) => {
34
+ return new OffsetCounter(initial);
35
+ };
36
+ const getArrayBufferIterator = (data, initialOffset) => {
37
+ const view = new DataView(data);
38
+ const counter = makeOffsetCounter(initialOffset);
39
+ const getSlice = (amount) => {
40
+ const value = data.slice(counter.getOffset(), counter.getOffset() + amount);
41
+ counter.increment(amount);
42
+ return value;
43
+ };
44
+ const getUint8 = () => {
45
+ const val = view.getUint8(counter.getOffset());
46
+ counter.increment(1);
47
+ return val;
48
+ };
49
+ const getUint32 = () => {
50
+ const val = view.getUint32(counter.getOffset());
51
+ counter.increment(4);
52
+ return val;
53
+ };
54
+ return {
55
+ view,
56
+ counter,
57
+ data,
58
+ discard: (length) => {
59
+ counter.increment(length);
60
+ },
61
+ getSlice,
62
+ getAtom: () => {
63
+ const atom = getSlice(4);
64
+ return new TextDecoder().decode(atom);
65
+ },
66
+ getMatroskaSegmentId: () => {
67
+ const first = getSlice(1);
68
+ const firstOneString = `0x${Array.from(new Uint8Array(first))
69
+ .map((b) => {
70
+ return b.toString(16).padStart(2, '0');
71
+ })
72
+ .join('')}`;
73
+ // Catch void block
74
+ const knownIdsWithOneLength = ['0xec', '0xae'];
75
+ if (knownIdsWithOneLength.includes(firstOneString)) {
76
+ return firstOneString;
77
+ }
78
+ const firstTwo = getSlice(1);
79
+ const knownIdsWithTwoLength = ['0x4dbb', '0x53ac', '0xec01'];
80
+ const firstTwoString = `${firstOneString}${Array.from(new Uint8Array(firstTwo))
81
+ .map((b) => {
82
+ return b.toString(16).padStart(2, '0');
83
+ })
84
+ .join('')}`;
85
+ if (knownIdsWithTwoLength.includes(firstTwoString)) {
86
+ return firstTwoString;
87
+ }
88
+ const knownIdsWithThreeLength = ['0x4d808c', '0x57418c', '0x448988'];
89
+ const firstThree = getSlice(1);
90
+ const firstThreeString = `${firstTwoString}${Array.from(new Uint8Array(firstThree))
91
+ .map((b) => {
92
+ return b.toString(16).padStart(2, '0');
93
+ })
94
+ .join('')}`;
95
+ if (knownIdsWithThreeLength.includes(firstThreeString)) {
96
+ return firstThreeString;
97
+ }
98
+ const segmentId = getSlice(1);
99
+ return `${firstThreeString}${Array.from(new Uint8Array(segmentId))
100
+ .map((b) => {
101
+ return b.toString(16).padStart(2, '0');
102
+ })
103
+ .join('')}`;
104
+ },
105
+ getVint: (bytes) => {
106
+ const slice = getSlice(bytes);
107
+ const d = [...Array.from(new Uint8Array(slice))];
108
+ const totalLength = d[0];
109
+ if (totalLength === 0) {
110
+ return 0;
111
+ }
112
+ // Calculate the actual length of the data based on the first set bit
113
+ let actualLength = 0;
114
+ while (((totalLength >> (7 - actualLength)) & 0x01) === 0) {
115
+ actualLength++;
116
+ }
117
+ actualLength += 1; // Include the first byte set as 1
118
+ // Combine the numbers to form the integer value
119
+ let value = 0;
120
+ // Mask the first byte properly then start combining
121
+ value = totalLength & (0xff >> actualLength);
122
+ for (let i = 1; i < actualLength; i++) {
123
+ value = (value << 8) | d[i];
124
+ }
125
+ return value;
126
+ },
127
+ getUint8,
128
+ getEBML: () => {
129
+ const val = getUint8();
130
+ // https://darkcoding.net/software/reading-mediarecorders-webm-opus-output/#:~:text=The%20first%20four%20bytes%20(%201A,%E2%80%93%20read%20on%20for%20why).
131
+ // You drop the initial 0 bits and the first 1 bit to get the value. 0x81 is 0b10000001, so there are zero inital 0 bits, meaning length one byte, and the value is 1. The 0x9F value for length of the EBML header we saw earlier is 0b10011111, still one byte, value is 0b0011111, which is 31 (the python repl is very helpful for these conversions).
132
+ const actualValue = val & 0x7f; // 0x7F is binary 01111111, which masks out the first bit
133
+ return actualValue;
134
+ },
135
+ getInt8: () => {
136
+ const val = view.getInt8(counter.getOffset());
137
+ counter.increment(1);
138
+ return val;
139
+ },
140
+ getUint16: () => {
141
+ const val = view.getUint16(counter.getOffset());
142
+ counter.increment(2);
143
+ return val;
144
+ },
145
+ getInt16: () => {
146
+ const val = view.getInt16(counter.getOffset());
147
+ counter.increment(2);
148
+ return val;
149
+ },
150
+ getUint32,
151
+ // https://developer.apple.com/documentation/quicktime-file-format/sound_sample_description_version_1
152
+ // A 32-bit unsigned fixed-point number (16.16) that indicates the rate at which the sound samples were obtained.
153
+ getFixedPoint1616Number: () => {
154
+ const val = getUint32();
155
+ return val / 2 ** 16;
156
+ },
157
+ getPascalString: () => {
158
+ const val = getSlice(32);
159
+ return [...Array.from(new Uint8Array(val))];
160
+ },
161
+ getDecimalBytes(length) {
162
+ const bytes = getSlice(length);
163
+ const numbers = [...Array.from(new Uint8Array(bytes))];
164
+ return numbers.reduce((acc, byte, index) => acc + (byte << (8 * (numbers.length - index - 1))), 0);
165
+ },
166
+ getByteString(length) {
167
+ const bytes = getSlice(length);
168
+ return new TextDecoder().decode(bytes);
169
+ },
170
+ getFloat64: () => {
171
+ const val = view.getFloat64(counter.getOffset());
172
+ counter.increment(8);
173
+ return val;
174
+ },
175
+ };
176
+ };
177
+ exports.getArrayBufferIterator = getArrayBufferIterator;
@@ -0,0 +1,6 @@
1
+ import type { SamplePosition } from './get-sample-positions';
2
+ import type { AnySegment } from './parse-result';
3
+ export declare const getSamplesFromMoof: ({ moofBox, trackId, }: {
4
+ moofBox: AnySegment;
5
+ trackId: number;
6
+ }) => SamplePosition[];
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSamplesFromMoof = void 0;
4
+ const traversal_1 = require("./traversal");
5
+ const getSamplesFromTraf = (trafSegment, moofOffset) => {
6
+ var _a, _b, _c, _d, _e, _f;
7
+ if (trafSegment.type !== 'regular-box' || trafSegment.boxType !== 'traf') {
8
+ throw new Error('Expected traf-box');
9
+ }
10
+ const tfhdBox = (0, traversal_1.getTfhdBox)(trafSegment);
11
+ const defaultSampleDuration = (_a = tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.defaultSampleDuration) !== null && _a !== void 0 ? _a : null;
12
+ const defaultSampleSize = (_b = tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.defaultSampleSize) !== null && _b !== void 0 ? _b : null;
13
+ const defaultSampleFlags = (_c = tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.defaultSampleFlags) !== null && _c !== void 0 ? _c : null;
14
+ const tfdtBox = (0, traversal_1.getTfdtBox)(trafSegment);
15
+ const trunBoxes = (0, traversal_1.getTrunBoxes)(trafSegment);
16
+ let time = 0;
17
+ let offset = 0;
18
+ let dataOffset = 0;
19
+ const samples = [];
20
+ for (const trunBox of trunBoxes) {
21
+ let i = -1;
22
+ if (dataOffset === 0 && trunBox.dataOffset) {
23
+ dataOffset = trunBox.dataOffset;
24
+ }
25
+ for (const sample of trunBox.samples) {
26
+ i++;
27
+ const duration = (_d = sample.sampleDuration) !== null && _d !== void 0 ? _d : defaultSampleDuration;
28
+ if (duration === null) {
29
+ throw new Error('Expected duration');
30
+ }
31
+ const size = (_e = sample.sampleSize) !== null && _e !== void 0 ? _e : defaultSampleSize;
32
+ if (size === null) {
33
+ throw new Error('Expected size');
34
+ }
35
+ const isFirstSample = i === 0;
36
+ const sampleFlags = sample.sampleFlags
37
+ ? sample.sampleFlags
38
+ : isFirstSample && trunBox.firstSampleFlags !== null
39
+ ? trunBox.firstSampleFlags
40
+ : defaultSampleFlags;
41
+ if (sampleFlags === null) {
42
+ throw new Error('Expected sample flags');
43
+ }
44
+ const keyframe = !((sampleFlags >> 16) & 0x1);
45
+ const dts = time + ((_f = tfdtBox === null || tfdtBox === void 0 ? void 0 : tfdtBox.baseMediaDecodeTime) !== null && _f !== void 0 ? _f : 0);
46
+ const samplePosition = {
47
+ offset: offset + (moofOffset !== null && moofOffset !== void 0 ? moofOffset : 0) + (dataOffset !== null && dataOffset !== void 0 ? dataOffset : 0),
48
+ dts,
49
+ cts: dts,
50
+ duration,
51
+ isKeyframe: keyframe,
52
+ size,
53
+ };
54
+ samples.push(samplePosition);
55
+ offset += size;
56
+ time += duration;
57
+ }
58
+ }
59
+ return samples;
60
+ };
61
+ const getSamplesFromMoof = ({ moofBox, trackId, }) => {
62
+ if (moofBox.type !== 'regular-box') {
63
+ throw new Error('Expected moof-box');
64
+ }
65
+ const trafs = moofBox.children.filter((c) => c.type === 'regular-box' && c.boxType === 'traf');
66
+ const mapped = trafs.map((traf) => {
67
+ const tfhdBox = (0, traversal_1.getTfhdBox)(traf);
68
+ return (tfhdBox === null || tfhdBox === void 0 ? void 0 : tfhdBox.trackId) === trackId
69
+ ? getSamplesFromTraf(traf, moofBox.offset)
70
+ : [];
71
+ });
72
+ return mapped.flat(1);
73
+ };
74
+ exports.getSamplesFromMoof = getSamplesFromMoof;
@@ -0,0 +1 @@
1
+ declare const understand: (src: string) => Promise<void>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ const understand = async (src) => {
3
+ const content = await Bun.file(src).arrayBuffer();
4
+ const firstBit = new DataView(content).getUint8(0);
5
+ const offset = new DataView(content).getUint8(1);
6
+ const offset2 = new DataView(content).getUint8(2);
7
+ const globalOffset = firstBit + 1;
8
+ const header = new TextDecoder().decode(content.slice(globalOffset, globalOffset + offset));
9
+ const comment = new TextDecoder().decode(content.slice(globalOffset + offset, globalOffset + offset2));
10
+ const rest = new TextDecoder().decode(content.slice(globalOffset + offset2 + offset, globalOffset + offset2 + offset + 50));
11
+ console.log({ firstBit, offset, offset2, header, comment, rest });
12
+ };
package/package.json CHANGED
@@ -3,11 +3,11 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-parser"
4
4
  },
5
5
  "name": "@remotion/media-parser",
6
- "version": "4.0.202",
6
+ "version": "4.0.204",
7
7
  "main": "dist/index.js",
8
8
  "sideEffects": false,
9
9
  "devDependencies": {
10
- "@remotion/renderer": "4.0.202"
10
+ "@remotion/renderer": "4.0.204"
11
11
  },
12
12
  "publishConfig": {
13
13
  "access": "public"
@@ -0,0 +1,69 @@
1
+ import {getTimescaleAndDuration} from '../../get-fps';
2
+ import type {SamplePosition} from '../../get-sample-positions';
3
+ import {getSamplePositions} from '../../get-sample-positions';
4
+ import type {IsoBaseMediaBox} from '../../parse-result';
5
+ import {getSamplesFromMoof} from '../../samples-from-moof';
6
+ import {
7
+ getCttsBox,
8
+ getStcoBox,
9
+ getStscBox,
10
+ getStssBox,
11
+ getStszBox,
12
+ getSttsBox,
13
+ getTkhdBox,
14
+ } from '../../traversal';
15
+ import type {TrakBox} from './trak/trak';
16
+
17
+ export const getSamplePositionsFromTrack = (
18
+ trakBox: TrakBox,
19
+ moofBox: IsoBaseMediaBox | null,
20
+ ): SamplePosition[] => {
21
+ const stszBox = getStszBox(trakBox);
22
+ const stcoBox = getStcoBox(trakBox);
23
+ const stscBox = getStscBox(trakBox);
24
+ const stssBox = getStssBox(trakBox);
25
+ const sttsBox = getSttsBox(trakBox);
26
+ const tkhdBox = getTkhdBox(trakBox);
27
+ const cttsBox = getCttsBox(trakBox);
28
+
29
+ const timescaleAndDuration = getTimescaleAndDuration(trakBox);
30
+
31
+ if (!tkhdBox) {
32
+ throw new Error('Expected tkhd box in trak box');
33
+ }
34
+
35
+ if (!stszBox) {
36
+ throw new Error('Expected stsz box in trak box');
37
+ }
38
+
39
+ if (!stcoBox) {
40
+ throw new Error('Expected stco box in trak box');
41
+ }
42
+
43
+ if (!stscBox) {
44
+ throw new Error('Expected stsc box in trak box');
45
+ }
46
+
47
+ if (!sttsBox) {
48
+ throw new Error('Expected stts box in trak box');
49
+ }
50
+
51
+ if (!timescaleAndDuration) {
52
+ throw new Error('Expected timescale and duration in trak box');
53
+ }
54
+
55
+ let samplePositions = getSamplePositions({
56
+ stcoBox,
57
+ stscBox,
58
+ stszBox,
59
+ stssBox,
60
+ sttsBox,
61
+ cttsBox,
62
+ });
63
+
64
+ if (samplePositions.length === 0 && moofBox) {
65
+ samplePositions = getSamplesFromMoof({moofBox, trackId: tkhdBox.trackId});
66
+ }
67
+
68
+ return samplePositions;
69
+ };
@@ -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,