@remotion/media-parser 4.0.191

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 (143) hide show
  1. package/.eslintrc +8 -0
  2. package/LICENSE.md +49 -0
  3. package/README.md +18 -0
  4. package/dist/boxes/iso-base-media/base-type.d.ts +4 -0
  5. package/dist/boxes/iso-base-media/base-type.js +2 -0
  6. package/dist/boxes/iso-base-media/ftyp.d.ts +13 -0
  7. package/dist/boxes/iso-base-media/ftyp.js +22 -0
  8. package/dist/boxes/iso-base-media/moov/moov.d.ts +12 -0
  9. package/dist/boxes/iso-base-media/moov/moov.js +22 -0
  10. package/dist/boxes/iso-base-media/mvhd.d.ts +30 -0
  11. package/dist/boxes/iso-base-media/mvhd.js +59 -0
  12. package/dist/boxes/iso-base-media/process-box.d.ts +8 -0
  13. package/dist/boxes/iso-base-media/process-box.js +174 -0
  14. package/dist/boxes/iso-base-media/stsd/keys.d.ts +5 -0
  15. package/dist/boxes/iso-base-media/stsd/keys.js +21 -0
  16. package/dist/boxes/iso-base-media/stsd/mebx.d.ts +14 -0
  17. package/dist/boxes/iso-base-media/stsd/mebx.js +27 -0
  18. package/dist/boxes/iso-base-media/stsd/samples.d.ts +48 -0
  19. package/dist/boxes/iso-base-media/stsd/samples.js +215 -0
  20. package/dist/boxes/iso-base-media/stsd/stsd.d.ts +13 -0
  21. package/dist/boxes/iso-base-media/stsd/stsd.js +26 -0
  22. package/dist/boxes/iso-base-media/stts/stts.d.ts +15 -0
  23. package/dist/boxes/iso-base-media/stts/stts.js +35 -0
  24. package/dist/boxes/iso-base-media/tkhd.d.ts +22 -0
  25. package/dist/boxes/iso-base-media/tkhd.js +63 -0
  26. package/dist/boxes/iso-base-media/to-date.d.ts +1 -0
  27. package/dist/boxes/iso-base-media/to-date.js +11 -0
  28. package/dist/boxes/iso-base-media/trak/trak.d.ts +12 -0
  29. package/dist/boxes/iso-base-media/trak/trak.js +22 -0
  30. package/dist/boxes/webm/parse-webm-header.d.ts +3 -0
  31. package/dist/boxes/webm/parse-webm-header.js +16 -0
  32. package/dist/boxes/webm/segments/duration.d.ts +6 -0
  33. package/dist/boxes/webm/segments/duration.js +15 -0
  34. package/dist/boxes/webm/segments/info.d.ts +8 -0
  35. package/dist/boxes/webm/segments/info.js +14 -0
  36. package/dist/boxes/webm/segments/main.d.ts +7 -0
  37. package/dist/boxes/webm/segments/main.js +13 -0
  38. package/dist/boxes/webm/segments/muxing.d.ts +6 -0
  39. package/dist/boxes/webm/segments/muxing.js +12 -0
  40. package/dist/boxes/webm/segments/parse-children.d.ts +3 -0
  41. package/dist/boxes/webm/segments/parse-children.js +17 -0
  42. package/dist/boxes/webm/segments/seek-head.d.ts +8 -0
  43. package/dist/boxes/webm/segments/seek-head.js +13 -0
  44. package/dist/boxes/webm/segments/seek-position.d.ts +6 -0
  45. package/dist/boxes/webm/segments/seek-position.js +12 -0
  46. package/dist/boxes/webm/segments/seek.d.ts +8 -0
  47. package/dist/boxes/webm/segments/seek.js +16 -0
  48. package/dist/boxes/webm/segments/timestamp-scale.d.ts +6 -0
  49. package/dist/boxes/webm/segments/timestamp-scale.js +11 -0
  50. package/dist/boxes/webm/segments/track-entry.d.ts +71 -0
  51. package/dist/boxes/webm/segments/track-entry.js +175 -0
  52. package/dist/boxes/webm/segments/tracks.d.ts +7 -0
  53. package/dist/boxes/webm/segments/tracks.js +13 -0
  54. package/dist/boxes/webm/segments/unknown.d.ts +6 -0
  55. package/dist/boxes/webm/segments/unknown.js +11 -0
  56. package/dist/boxes/webm/segments/void.d.ts +6 -0
  57. package/dist/boxes/webm/segments/void.js +12 -0
  58. package/dist/boxes/webm/segments/writing.d.ts +6 -0
  59. package/dist/boxes/webm/segments/writing.js +12 -0
  60. package/dist/boxes/webm/segments.d.ts +16 -0
  61. package/dist/boxes/webm/segments.js +106 -0
  62. package/dist/buffer-iterator.d.ts +36 -0
  63. package/dist/buffer-iterator.js +263 -0
  64. package/dist/from-node.d.ts +2 -0
  65. package/dist/from-node.js +19 -0
  66. package/dist/from-web.d.ts +2 -0
  67. package/dist/from-web.js +40 -0
  68. package/dist/get-dimensions.d.ts +7 -0
  69. package/dist/get-dimensions.js +104 -0
  70. package/dist/get-duration.d.ts +3 -0
  71. package/dist/get-duration.js +61 -0
  72. package/dist/get-fps.d.ts +3 -0
  73. package/dist/get-fps.js +70 -0
  74. package/dist/has-all-info.d.ts +3 -0
  75. package/dist/has-all-info.js +27 -0
  76. package/dist/index.d.ts +1 -0
  77. package/dist/index.js +5 -0
  78. package/dist/options.d.ts +19 -0
  79. package/dist/options.js +2 -0
  80. package/dist/parse-media.d.ts +2 -0
  81. package/dist/parse-media.js +44 -0
  82. package/dist/parse-result.d.ts +29 -0
  83. package/dist/parse-result.js +2 -0
  84. package/dist/parse-video.d.ts +10 -0
  85. package/dist/parse-video.js +29 -0
  86. package/dist/reader.d.ts +7 -0
  87. package/dist/reader.js +2 -0
  88. package/package.json +47 -0
  89. package/src/boxes/iso-base-media/base-type.ts +4 -0
  90. package/src/boxes/iso-base-media/ftyp.ts +39 -0
  91. package/src/boxes/iso-base-media/moov/moov.ts +37 -0
  92. package/src/boxes/iso-base-media/mvhd.ts +107 -0
  93. package/src/boxes/iso-base-media/process-box.ts +236 -0
  94. package/src/boxes/iso-base-media/stsd/keys.ts +25 -0
  95. package/src/boxes/iso-base-media/stsd/mebx.ts +46 -0
  96. package/src/boxes/iso-base-media/stsd/samples.ts +298 -0
  97. package/src/boxes/iso-base-media/stsd/stsd.ts +48 -0
  98. package/src/boxes/iso-base-media/stts/stts.ts +62 -0
  99. package/src/boxes/iso-base-media/tkhd.ts +105 -0
  100. package/src/boxes/iso-base-media/to-date.ts +9 -0
  101. package/src/boxes/iso-base-media/trak/trak.ts +37 -0
  102. package/src/boxes/webm/parse-webm-header.ts +18 -0
  103. package/src/boxes/webm/segments/duration.ts +22 -0
  104. package/src/boxes/webm/segments/info.ts +20 -0
  105. package/src/boxes/webm/segments/main.ts +19 -0
  106. package/src/boxes/webm/segments/muxing.ts +18 -0
  107. package/src/boxes/webm/segments/parse-children.ts +21 -0
  108. package/src/boxes/webm/segments/seek-head.ts +21 -0
  109. package/src/boxes/webm/segments/seek-position.ts +19 -0
  110. package/src/boxes/webm/segments/seek.ts +23 -0
  111. package/src/boxes/webm/segments/timestamp-scale.ts +17 -0
  112. package/src/boxes/webm/segments/track-entry.ts +295 -0
  113. package/src/boxes/webm/segments/tracks.ts +22 -0
  114. package/src/boxes/webm/segments/unknown.ts +19 -0
  115. package/src/boxes/webm/segments/void.ts +16 -0
  116. package/src/boxes/webm/segments/writing.ts +18 -0
  117. package/src/boxes/webm/segments.ts +206 -0
  118. package/src/buffer-iterator.ts +305 -0
  119. package/src/from-node.ts +22 -0
  120. package/src/from-web.ts +53 -0
  121. package/src/get-dimensions.ts +147 -0
  122. package/src/get-duration.ts +73 -0
  123. package/src/get-fps.ts +92 -0
  124. package/src/has-all-info.ts +34 -0
  125. package/src/index.ts +1 -0
  126. package/src/options.ts +38 -0
  127. package/src/parse-media.ts +57 -0
  128. package/src/parse-result.ts +44 -0
  129. package/src/parse-video.ts +41 -0
  130. package/src/reader.ts +10 -0
  131. package/src/test/duration.test.ts +34 -0
  132. package/src/test/keys.test.ts +47 -0
  133. package/src/test/matroska.test.ts +163 -0
  134. package/src/test/mvhd.test.ts +89 -0
  135. package/src/test/parse-stts.test.ts +38 -0
  136. package/src/test/parse-video.test.ts +113 -0
  137. package/src/test/parse-webm.test.ts +15 -0
  138. package/src/test/stream-local.test.ts +105 -0
  139. package/src/test/stream-remote.test.ts +12 -0
  140. package/src/test/stsd.test.ts +162 -0
  141. package/src/test/tkhd.test.ts +84 -0
  142. package/tsconfig.json +10 -0
  143. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMedia = void 0;
4
+ const buffer_iterator_1 = require("./buffer-iterator");
5
+ const from_web_1 = require("./from-web");
6
+ const get_dimensions_1 = require("./get-dimensions");
7
+ const get_duration_1 = require("./get-duration");
8
+ const get_fps_1 = require("./get-fps");
9
+ const has_all_info_1 = require("./has-all-info");
10
+ const parse_video_1 = require("./parse-video");
11
+ const parseMedia = async (src, options, readerInterface = from_web_1.webReader) => {
12
+ const reader = await readerInterface.read(src, null);
13
+ const returnValue = {};
14
+ const iterator = (0, buffer_iterator_1.getArrayBufferIterator)(new Uint8Array([]));
15
+ let parseResult = (0, parse_video_1.parseVideo)(iterator);
16
+ while (parseResult.status === 'incomplete') {
17
+ const result = await reader.read();
18
+ if (result.done) {
19
+ break;
20
+ }
21
+ iterator.addData(result.value);
22
+ parseResult = parseResult.continueParsing();
23
+ if ((0, has_all_info_1.hasAllInfo)(options, parseResult)) {
24
+ if (!reader.closed) {
25
+ reader.cancel(new Error('has all information'));
26
+ }
27
+ break;
28
+ }
29
+ }
30
+ if (options.dimensions) {
31
+ returnValue.dimensions = (0, get_dimensions_1.getDimensions)(parseResult.segments);
32
+ }
33
+ if (options.durationInSeconds) {
34
+ returnValue.durationInSeconds = (0, get_duration_1.getDuration)(parseResult.segments);
35
+ }
36
+ if (options.fps) {
37
+ returnValue.fps = (0, get_fps_1.getFps)(parseResult.segments);
38
+ }
39
+ if (options.boxes) {
40
+ returnValue.boxes = parseResult.segments;
41
+ }
42
+ return returnValue;
43
+ };
44
+ exports.parseMedia = parseMedia;
@@ -0,0 +1,29 @@
1
+ import type { BaseBox } from './boxes/iso-base-media/base-type';
2
+ import type { FtypBox } from './boxes/iso-base-media/ftyp';
3
+ import type { MoovBox } from './boxes/iso-base-media/moov/moov';
4
+ import type { MvhdBox } from './boxes/iso-base-media/mvhd';
5
+ import type { KeysBox } from './boxes/iso-base-media/stsd/keys';
6
+ import type { MebxBox } from './boxes/iso-base-media/stsd/mebx';
7
+ import type { StsdBox } from './boxes/iso-base-media/stsd/stsd';
8
+ import type { SttsBox } from './boxes/iso-base-media/stts/stts';
9
+ import type { TkhdBox } from './boxes/iso-base-media/tkhd';
10
+ import type { TrakBox } from './boxes/iso-base-media/trak/trak';
11
+ import type { MatroskaSegment } from './boxes/webm/segments';
12
+ interface RegularBox extends BaseBox {
13
+ boxType: string;
14
+ boxSize: number;
15
+ children: AnySegment[];
16
+ offset: number;
17
+ type: 'regular-box';
18
+ }
19
+ export type IsoBaseMediaBox = RegularBox | FtypBox | MvhdBox | TkhdBox | StsdBox | MebxBox | KeysBox | MoovBox | TrakBox | SttsBox;
20
+ export type AnySegment = MatroskaSegment | IsoBaseMediaBox;
21
+ export type ParseResult = {
22
+ status: 'done';
23
+ segments: AnySegment[];
24
+ } | {
25
+ status: 'incomplete';
26
+ segments: AnySegment[];
27
+ continueParsing: () => ParseResult;
28
+ };
29
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ import type { BufferIterator } from './buffer-iterator';
2
+ import type { IsoBaseMediaBox, ParseResult } from './parse-result';
3
+ export type BoxAndNext = {
4
+ type: 'complete';
5
+ box: IsoBaseMediaBox;
6
+ size: number;
7
+ } | {
8
+ type: 'incomplete';
9
+ };
10
+ export declare const parseVideo: (iterator: BufferIterator) => ParseResult;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseVideo = void 0;
4
+ const process_box_1 = require("./boxes/iso-base-media/process-box");
5
+ const parse_webm_header_1 = require("./boxes/webm/parse-webm-header");
6
+ const parseVideo = (iterator) => {
7
+ if (iterator.bytesRemaining() === 0) {
8
+ return {
9
+ status: 'incomplete',
10
+ segments: [],
11
+ continueParsing: () => {
12
+ return (0, exports.parseVideo)(iterator);
13
+ },
14
+ };
15
+ }
16
+ if (iterator.isIsoBaseMedia()) {
17
+ return (0, process_box_1.parseBoxes)({
18
+ iterator,
19
+ maxBytes: Infinity,
20
+ allowIncompleteBoxes: true,
21
+ initialBoxes: [],
22
+ });
23
+ }
24
+ if (iterator.isWebm()) {
25
+ return (0, parse_webm_header_1.parseWebm)(iterator);
26
+ }
27
+ throw new Error('Unknown video format');
28
+ };
29
+ exports.parseVideo = parseVideo;
@@ -0,0 +1,7 @@
1
+ type ReadContent = (src: string, range: [number, number] | null) => Promise<ReadableStreamDefaultReader<Uint8Array>>;
2
+ type GetLength = (src: string) => Promise<number>;
3
+ export type ReaderInterface = {
4
+ read: ReadContent;
5
+ getLength: GetLength;
6
+ };
7
+ export {};
package/dist/reader.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "repository": {
3
+ "url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-parser"
4
+ },
5
+ "name": "@remotion/media-parser",
6
+ "version": "4.0.191",
7
+ "main": "dist/index.js",
8
+ "sideEffects": false,
9
+ "devDependencies": {
10
+ "@remotion/renderer": "4.0.191"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/remotion-dev/remotion/issues"
17
+ },
18
+ "exports": {
19
+ ".": "./dist/index.js",
20
+ "./node": "./dist/from-node.js",
21
+ "./package.json": "./package.json"
22
+ },
23
+ "typesVersions": {
24
+ ">=1.0": {
25
+ "node": [
26
+ "dist/from-node.d.ts"
27
+ ]
28
+ }
29
+ },
30
+ "author": "Jonny Burger <jonny@remotion.dev>",
31
+ "license": "SEE LICENSE IN LICENSE.md",
32
+ "keywords": [
33
+ "remotion",
34
+ "ffmpeg",
35
+ "video",
36
+ "react",
37
+ "player"
38
+ ],
39
+ "homepage": "https://www.remotion.dev/docs/media-parser",
40
+ "description": "A pure JavaScript library for parsing video files",
41
+ "scripts": {
42
+ "formatting": "prettier src --check",
43
+ "lint": "eslint src --ext ts,tsx",
44
+ "test": "bun test src/test",
45
+ "watch": "tsc -w"
46
+ }
47
+ }
@@ -0,0 +1,4 @@
1
+ export interface BaseBox {
2
+ boxSize: number;
3
+ offset: number;
4
+ }
@@ -0,0 +1,39 @@
1
+ import type {BufferIterator} from '../../buffer-iterator';
2
+ import type {BaseBox} from './base-type';
3
+
4
+ export interface FtypBox extends BaseBox {
5
+ type: 'ftyp-box';
6
+ majorBrand: string;
7
+ minorVersion: number;
8
+ compatibleBrands: string[];
9
+ }
10
+
11
+ export const parseFtyp = ({
12
+ iterator,
13
+ size,
14
+ offset,
15
+ }: {
16
+ iterator: BufferIterator;
17
+ size: number;
18
+ offset: number;
19
+ }): FtypBox => {
20
+ const majorBrand = iterator.getByteString(4);
21
+ const minorVersion = iterator.getFourByteNumber();
22
+
23
+ const types = (size - iterator.counter.getOffset()) / 4;
24
+ const compatibleBrands: string[] = [];
25
+ for (let i = 0; i < types; i++) {
26
+ compatibleBrands.push(iterator.getByteString(4).trim());
27
+ }
28
+
29
+ const offsetAtEnd = iterator.counter.getOffset();
30
+
31
+ return {
32
+ type: 'ftyp-box',
33
+ majorBrand,
34
+ minorVersion,
35
+ compatibleBrands,
36
+ offset,
37
+ boxSize: offsetAtEnd - offset,
38
+ };
39
+ };
@@ -0,0 +1,37 @@
1
+ import type {BufferIterator} from '../../../buffer-iterator';
2
+ import type {AnySegment} from '../../../parse-result';
3
+ import type {BaseBox} from '../base-type';
4
+ import {parseBoxes} from '../process-box';
5
+
6
+ export interface MoovBox extends BaseBox {
7
+ type: 'moov-box';
8
+ children: AnySegment[];
9
+ }
10
+
11
+ export const parseMoov = ({
12
+ iterator,
13
+ offset,
14
+ size,
15
+ }: {
16
+ iterator: BufferIterator;
17
+ offset: number;
18
+ size: number;
19
+ }): MoovBox => {
20
+ const children = parseBoxes({
21
+ iterator,
22
+ maxBytes: size - (iterator.counter.getOffset() - offset),
23
+ allowIncompleteBoxes: false,
24
+ initialBoxes: [],
25
+ });
26
+
27
+ if (children.status === 'incomplete') {
28
+ throw new Error('Incomplete boxes are not allowed');
29
+ }
30
+
31
+ return {
32
+ offset,
33
+ boxSize: size,
34
+ type: 'moov-box',
35
+ children: children.segments,
36
+ };
37
+ };
@@ -0,0 +1,107 @@
1
+ import type {BufferIterator} from '../../buffer-iterator';
2
+ import {getArrayBufferIterator} from '../../buffer-iterator';
3
+ import type {BaseBox} from './base-type';
4
+ import {toUnixTimestamp} from './to-date';
5
+
6
+ export type ThreeDMatrix = [
7
+ number,
8
+ number,
9
+ number,
10
+ number,
11
+ number,
12
+ number,
13
+ number,
14
+ number,
15
+ number,
16
+ ];
17
+
18
+ export interface MvhdBox extends BaseBox {
19
+ durationInUnits: number;
20
+ durationInSeconds: number;
21
+ creationTime: number | null;
22
+ modificationTime: number | null;
23
+ timeScale: number;
24
+ rate: number;
25
+ volume: number;
26
+ matrix: ThreeDMatrix;
27
+ nextTrackId: number;
28
+ type: 'mvhd-box';
29
+ }
30
+
31
+ export const parseMvhd = ({
32
+ iterator,
33
+ offset,
34
+ size,
35
+ }: {
36
+ iterator: BufferIterator;
37
+ offset: number;
38
+ size: number;
39
+ }): MvhdBox => {
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
+
49
+ // Flags, we discard them
50
+ iterator.discard(3);
51
+
52
+ const creationTime = iterator.getUint32();
53
+
54
+ const modificationTime = iterator.getUint32();
55
+
56
+ const timeScale = iterator.getUint32();
57
+
58
+ const durationInUnits = iterator.getUint32();
59
+ const durationInSeconds = durationInUnits / timeScale;
60
+
61
+ const rateArray = iterator.getSlice(4);
62
+ const rateView = getArrayBufferIterator(rateArray);
63
+ const rate =
64
+ rateView.getInt8() * 10 +
65
+ rateView.getInt8() +
66
+ rateView.getInt8() * 0.1 +
67
+ rateView.getInt8() * 0.01;
68
+
69
+ const volumeArray = iterator.getSlice(2);
70
+ const volumeView = getArrayBufferIterator(volumeArray);
71
+
72
+ const volume = volumeView.getInt8() + volumeView.getInt8() * 0.1;
73
+
74
+ // reserved 16bit
75
+ iterator.discard(2);
76
+
77
+ // reserved 32bit x2
78
+ iterator.discard(4);
79
+ iterator.discard(4);
80
+
81
+ // matrix
82
+ const matrix: number[] = [];
83
+ for (let i = 0; i < 9; i++) {
84
+ matrix.push(iterator.getUint32());
85
+ }
86
+
87
+ // pre-defined
88
+ iterator.discard(4 * 6);
89
+
90
+ // next track id
91
+ const nextTrackId = iterator.getUint32();
92
+
93
+ return {
94
+ creationTime: toUnixTimestamp(creationTime),
95
+ modificationTime: toUnixTimestamp(modificationTime),
96
+ timeScale,
97
+ durationInUnits,
98
+ durationInSeconds,
99
+ rate,
100
+ volume,
101
+ matrix: matrix as ThreeDMatrix,
102
+ nextTrackId,
103
+ type: 'mvhd-box',
104
+ boxSize: size,
105
+ offset,
106
+ };
107
+ };
@@ -0,0 +1,236 @@
1
+ import type {BufferIterator} from '../../buffer-iterator';
2
+ import type {IsoBaseMediaBox, ParseResult} from '../../parse-result';
3
+ import type {BoxAndNext} from '../../parse-video';
4
+ import {parseFtyp} from './ftyp';
5
+ import {parseMoov} from './moov/moov';
6
+ import {parseMvhd} from './mvhd';
7
+ import {parseMebx} from './stsd/mebx';
8
+ import {parseStsd} from './stsd/stsd';
9
+ import {parseStts} from './stts/stts';
10
+ import {parseTkhd} from './tkhd';
11
+ import {parseTrak} from './trak/trak';
12
+
13
+ const getChildren = ({
14
+ boxType,
15
+ iterator,
16
+ bytesRemainingInBox,
17
+ }: {
18
+ boxType: string;
19
+ iterator: BufferIterator;
20
+ bytesRemainingInBox: number;
21
+ }) => {
22
+ const parseChildren =
23
+ boxType === 'mdia' ||
24
+ boxType === 'minf' ||
25
+ boxType === 'stbl' ||
26
+ boxType === 'dims' ||
27
+ boxType === 'stsb';
28
+
29
+ if (parseChildren) {
30
+ const parsed = parseBoxes({
31
+ iterator,
32
+ maxBytes: bytesRemainingInBox,
33
+ allowIncompleteBoxes: false,
34
+ initialBoxes: [],
35
+ });
36
+
37
+ if (parsed.status === 'incomplete') {
38
+ throw new Error('Incomplete boxes are not allowed');
39
+ }
40
+
41
+ return parsed.segments;
42
+ }
43
+
44
+ iterator.discard(bytesRemainingInBox);
45
+ return [];
46
+ };
47
+
48
+ const processBox = ({
49
+ iterator,
50
+ allowIncompleteBoxes,
51
+ }: {
52
+ iterator: BufferIterator;
53
+ allowIncompleteBoxes: boolean;
54
+ }): BoxAndNext => {
55
+ const fileOffset = iterator.counter.getOffset();
56
+ const bytesRemaining = iterator.bytesRemaining();
57
+
58
+ const boxSize = iterator.getFourByteNumber();
59
+ if (boxSize === 0) {
60
+ throw new Error(`Expected box size of not 0, got ${boxSize}`);
61
+ }
62
+
63
+ if (bytesRemaining < boxSize) {
64
+ iterator.counter.decrement(iterator.counter.getOffset() - fileOffset);
65
+ if (allowIncompleteBoxes) {
66
+ return {
67
+ type: 'incomplete',
68
+ };
69
+ }
70
+
71
+ throw new Error(
72
+ `Expected box size of ${bytesRemaining}, got ${boxSize}. Incomplete boxes are not allowed.`,
73
+ );
74
+ }
75
+
76
+ const boxType = iterator.getByteString(4);
77
+
78
+ if (boxType === 'ftyp') {
79
+ const box = parseFtyp({iterator, size: boxSize, offset: fileOffset});
80
+ return {
81
+ type: 'complete',
82
+ box,
83
+ size: boxSize,
84
+ };
85
+ }
86
+
87
+ if (boxType === 'mvhd') {
88
+ const box = parseMvhd({iterator, offset: fileOffset, size: boxSize});
89
+
90
+ return {
91
+ type: 'complete',
92
+ box,
93
+ size: boxSize,
94
+ };
95
+ }
96
+
97
+ if (boxType === 'tkhd') {
98
+ const box = parseTkhd({iterator, offset: fileOffset, size: boxSize});
99
+
100
+ return {
101
+ type: 'complete',
102
+ box,
103
+ size: boxSize,
104
+ };
105
+ }
106
+
107
+ if (boxType === 'stsd') {
108
+ const box = parseStsd({iterator, offset: fileOffset, size: boxSize});
109
+
110
+ return {
111
+ type: 'complete',
112
+ box,
113
+ size: boxSize,
114
+ };
115
+ }
116
+
117
+ if (boxType === 'mebx') {
118
+ const box = parseMebx({iterator, offset: fileOffset, size: boxSize});
119
+
120
+ return {
121
+ type: 'complete',
122
+ box,
123
+ size: boxSize,
124
+ };
125
+ }
126
+
127
+ if (boxType === 'moov') {
128
+ const box = parseMoov({iterator, offset: fileOffset, size: boxSize});
129
+
130
+ return {
131
+ type: 'complete',
132
+ box,
133
+ size: boxSize,
134
+ };
135
+ }
136
+
137
+ if (boxType === 'trak') {
138
+ const box = parseTrak({
139
+ data: iterator,
140
+ size: boxSize,
141
+ offsetAtStart: fileOffset,
142
+ });
143
+
144
+ return {
145
+ type: 'complete',
146
+ box,
147
+ size: boxSize,
148
+ };
149
+ }
150
+
151
+ if (boxType === 'stts') {
152
+ const box = parseStts({
153
+ data: iterator,
154
+ size: boxSize,
155
+ fileOffset,
156
+ });
157
+
158
+ return {
159
+ type: 'complete',
160
+ box,
161
+ size: boxSize,
162
+ };
163
+ }
164
+
165
+ const bytesRemainingInBox =
166
+ boxSize - (iterator.counter.getOffset() - fileOffset);
167
+
168
+ const children = getChildren({
169
+ boxType,
170
+ iterator,
171
+ bytesRemainingInBox,
172
+ });
173
+
174
+ return {
175
+ type: 'complete',
176
+ box: {
177
+ type: 'regular-box',
178
+ boxType,
179
+ boxSize,
180
+ children,
181
+ offset: fileOffset,
182
+ },
183
+ size: boxSize,
184
+ };
185
+ };
186
+
187
+ export const parseBoxes = ({
188
+ iterator,
189
+ maxBytes,
190
+ allowIncompleteBoxes,
191
+ initialBoxes,
192
+ }: {
193
+ iterator: BufferIterator;
194
+ maxBytes: number;
195
+ allowIncompleteBoxes: boolean;
196
+ initialBoxes: IsoBaseMediaBox[];
197
+ }): ParseResult => {
198
+ const boxes: IsoBaseMediaBox[] = initialBoxes;
199
+ const initialOffset = iterator.counter.getOffset();
200
+
201
+ while (
202
+ iterator.bytesRemaining() > 0 &&
203
+ iterator.counter.getOffset() - initialOffset < maxBytes
204
+ ) {
205
+ const result = processBox({
206
+ iterator,
207
+ allowIncompleteBoxes,
208
+ });
209
+ if (result.type === 'incomplete') {
210
+ if (Number.isFinite(maxBytes)) {
211
+ throw new Error('maxBytes must be Infinity for top-level boxes');
212
+ }
213
+
214
+ return {
215
+ status: 'incomplete',
216
+ segments: boxes,
217
+ continueParsing: () => {
218
+ return parseBoxes({
219
+ iterator,
220
+ maxBytes,
221
+ allowIncompleteBoxes,
222
+ initialBoxes: boxes,
223
+ });
224
+ },
225
+ };
226
+ }
227
+
228
+ boxes.push(result.box);
229
+ iterator.discardFirstBytes();
230
+ }
231
+
232
+ return {
233
+ status: 'done',
234
+ segments: boxes,
235
+ };
236
+ };
@@ -0,0 +1,25 @@
1
+ import {getArrayBufferIterator} from '../../../buffer-iterator';
2
+ import type {BaseBox} from '../base-type';
3
+
4
+ export interface KeysBox extends BaseBox {
5
+ type: 'keys-box';
6
+ }
7
+
8
+ export const parseKeys = (data: Uint8Array, offset: number): KeysBox => {
9
+ const iterator = getArrayBufferIterator(data);
10
+ const size = iterator.getUint32();
11
+ if (size !== data.byteLength) {
12
+ throw new Error(`Expected keys size of ${data.byteLength}, got ${size}`);
13
+ }
14
+
15
+ const atom = iterator.getAtom();
16
+ if (atom !== 'keys') {
17
+ throw new Error(`Expected keys type of keys, got ${atom}`);
18
+ }
19
+
20
+ return {
21
+ type: 'keys-box',
22
+ boxSize: data.byteLength,
23
+ offset,
24
+ };
25
+ };
@@ -0,0 +1,46 @@
1
+ import type {BufferIterator} from '../../../buffer-iterator';
2
+ import type {AnySegment} from '../../../parse-result';
3
+ import type {BaseBox} from '../base-type';
4
+ import {parseBoxes} from '../process-box';
5
+
6
+ export interface MebxBox extends BaseBox {
7
+ type: 'mebx-box';
8
+ dataReferenceIndex: number;
9
+ format: string;
10
+ children: AnySegment[];
11
+ }
12
+
13
+ export const parseMebx = ({
14
+ iterator,
15
+ offset,
16
+ size,
17
+ }: {
18
+ iterator: BufferIterator;
19
+ offset: number;
20
+ size: number;
21
+ }): MebxBox => {
22
+ // reserved, 6 bit
23
+ iterator.discard(6);
24
+
25
+ const dataReferenceIndex = iterator.getUint16();
26
+
27
+ const children = parseBoxes({
28
+ iterator,
29
+ maxBytes: iterator.counter.getOffset() - offset,
30
+ allowIncompleteBoxes: false,
31
+ initialBoxes: [],
32
+ });
33
+
34
+ if (children.status === 'incomplete') {
35
+ throw new Error('Incomplete boxes are not allowed');
36
+ }
37
+
38
+ return {
39
+ type: 'mebx-box',
40
+ boxSize: size,
41
+ offset,
42
+ dataReferenceIndex,
43
+ format: 'mebx',
44
+ children: children.segments,
45
+ };
46
+ };