@remotion/media-parser 4.0.248 → 4.0.250

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 (162) hide show
  1. package/dist/boxes/avc/key.d.ts +1 -1
  2. package/dist/boxes/iso-base-media/continue-mdat-routine.d.ts +14 -0
  3. package/dist/boxes/iso-base-media/continue-mdat-routine.js +74 -0
  4. package/dist/boxes/iso-base-media/get-children.d.ts +8 -0
  5. package/dist/boxes/iso-base-media/get-children.js +23 -0
  6. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +3 -22
  7. package/dist/boxes/iso-base-media/mdat/mdat.js +80 -121
  8. package/dist/boxes/iso-base-media/moov/moov.d.ts +1 -6
  9. package/dist/boxes/iso-base-media/moov/moov.js +5 -15
  10. package/dist/boxes/iso-base-media/parse-all-children.d.ts +8 -0
  11. package/dist/boxes/iso-base-media/parse-all-children.js +20 -0
  12. package/dist/boxes/iso-base-media/parse-boxes.d.ts +7 -0
  13. package/dist/boxes/iso-base-media/parse-boxes.js +46 -0
  14. package/dist/boxes/iso-base-media/parse-mdat-partially.d.ts +9 -0
  15. package/dist/boxes/iso-base-media/parse-mdat-partially.js +24 -0
  16. package/dist/boxes/iso-base-media/process-box.d.ts +2 -29
  17. package/dist/boxes/iso-base-media/process-box.js +56 -367
  18. package/dist/boxes/iso-base-media/stsd/mebx.d.ts +1 -4
  19. package/dist/boxes/iso-base-media/stsd/mebx.js +5 -15
  20. package/dist/boxes/iso-base-media/stsd/samples.d.ts +4 -12
  21. package/dist/boxes/iso-base-media/stsd/samples.js +24 -70
  22. package/dist/boxes/iso-base-media/stsd/stsd.d.ts +1 -4
  23. package/dist/boxes/iso-base-media/stsd/stsd.js +2 -5
  24. package/dist/boxes/iso-base-media/trak/trak.d.ts +1 -6
  25. package/dist/boxes/iso-base-media/trak/trak.js +5 -15
  26. package/dist/boxes/iso-base-media/traversal.d.ts +0 -2
  27. package/dist/boxes/iso-base-media/traversal.js +1 -12
  28. package/dist/boxes/mp3/get-duration.d.ts +2 -0
  29. package/dist/boxes/mp3/get-duration.js +30 -0
  30. package/dist/boxes/mp3/get-frame-length.d.ts +13 -0
  31. package/dist/boxes/mp3/get-frame-length.js +33 -0
  32. package/dist/boxes/mp3/get-metadata-from-mp3.d.ts +3 -0
  33. package/dist/boxes/mp3/get-metadata-from-mp3.js +8 -0
  34. package/dist/boxes/mp3/get-tracks-from-mp3.d.ts +4 -0
  35. package/dist/boxes/mp3/get-tracks-from-mp3.js +25 -0
  36. package/dist/boxes/mp3/id3-v1.d.ts +2 -0
  37. package/dist/boxes/mp3/id3-v1.js +12 -0
  38. package/dist/boxes/mp3/id3-v2.d.ts +0 -0
  39. package/dist/boxes/mp3/id3-v2.js +1 -0
  40. package/dist/boxes/mp3/id3.d.ts +6 -0
  41. package/dist/boxes/mp3/id3.js +80 -0
  42. package/dist/boxes/mp3/parse-mp3.d.ts +7 -0
  43. package/dist/boxes/mp3/parse-mp3.js +41 -0
  44. package/dist/boxes/mp3/parse-mpeg-header.d.ts +6 -0
  45. package/dist/boxes/mp3/parse-mpeg-header.js +274 -0
  46. package/dist/boxes/mp3/samples-per-mpeg-file.d.ts +4 -0
  47. package/dist/boxes/mp3/samples-per-mpeg-file.js +26 -0
  48. package/dist/boxes/riff/continue-after-riff-result.d.ts +13 -0
  49. package/dist/boxes/riff/continue-after-riff-result.js +34 -0
  50. package/dist/boxes/riff/expect-riff-box.d.ts +1 -7
  51. package/dist/boxes/riff/expect-riff-box.js +47 -24
  52. package/dist/boxes/riff/get-tracks-from-avi.d.ts +1 -1
  53. package/dist/boxes/riff/get-tracks-from-avi.js +6 -10
  54. package/dist/boxes/riff/parse-box.d.ts +1 -7
  55. package/dist/boxes/riff/parse-box.js +4 -120
  56. package/dist/boxes/riff/parse-fmt-box.d.ts +3 -2
  57. package/dist/boxes/riff/parse-fmt-box.js +7 -5
  58. package/dist/boxes/riff/parse-list-box.js +15 -14
  59. package/dist/boxes/riff/parse-movi.d.ts +2 -5
  60. package/dist/boxes/riff/parse-movi.js +34 -56
  61. package/dist/boxes/riff/parse-riff-body.d.ts +7 -0
  62. package/dist/boxes/riff/parse-riff-body.js +25 -0
  63. package/dist/boxes/riff/parse-riff-box.d.ts +1 -2
  64. package/dist/boxes/riff/parse-riff-box.js +2 -6
  65. package/dist/boxes/riff/parse-riff-header.d.ts +7 -0
  66. package/dist/boxes/riff/parse-riff-header.js +23 -0
  67. package/dist/boxes/riff/parse-riff.d.ts +7 -0
  68. package/dist/boxes/riff/parse-riff.js +15 -0
  69. package/dist/boxes/riff/parse-strf.d.ts +4 -4
  70. package/dist/boxes/riff/parse-strf.js +4 -8
  71. package/dist/boxes/riff/parse-strh.js +11 -0
  72. package/dist/boxes/riff/parse-video-section.d.ts +6 -0
  73. package/dist/boxes/riff/parse-video-section.js +20 -0
  74. package/dist/boxes/riff/riff-box.d.ts +4 -5
  75. package/dist/boxes/riff/traversal.d.ts +1 -2
  76. package/dist/boxes/riff/traversal.js +1 -6
  77. package/dist/boxes/transport-stream/get-tracks.d.ts +2 -3
  78. package/dist/boxes/transport-stream/get-tracks.js +4 -3
  79. package/dist/boxes/transport-stream/parse-packet.d.ts +1 -7
  80. package/dist/boxes/transport-stream/parse-packet.js +3 -4
  81. package/dist/boxes/transport-stream/parse-stream-packet.d.ts +1 -5
  82. package/dist/boxes/transport-stream/parse-stream-packet.js +10 -12
  83. package/dist/boxes/transport-stream/parse-transport-stream.d.ts +1 -7
  84. package/dist/boxes/transport-stream/parse-transport-stream.js +19 -49
  85. package/dist/boxes/transport-stream/process-stream-buffers.d.ts +1 -2
  86. package/dist/boxes/transport-stream/process-stream-buffers.js +3 -3
  87. package/dist/boxes/webm/parse-ebml.js +3 -0
  88. package/dist/boxes/webm/parse-webm-header.d.ts +2 -4
  89. package/dist/boxes/webm/parse-webm-header.js +41 -25
  90. package/dist/boxes/webm/segments/parse-children.d.ts +2 -16
  91. package/dist/boxes/webm/segments/parse-children.js +7 -130
  92. package/dist/boxes/webm/segments.d.ts +4 -8
  93. package/dist/boxes/webm/segments.js +41 -123
  94. package/dist/buffer-iterator.d.ts +6 -3
  95. package/dist/buffer-iterator.js +27 -16
  96. package/dist/bun-reader.d.ts +1 -0
  97. package/dist/bun-reader.js +17 -0
  98. package/dist/continue-mdat-routine.d.ts +17 -0
  99. package/dist/continue-mdat-routine.js +92 -0
  100. package/dist/emit-available-info.js +42 -28
  101. package/dist/esm/from-node.mjs +8 -9
  102. package/dist/esm/index.mjs +5133 -5085
  103. package/dist/file-types/detect-file-type.js +6 -2
  104. package/dist/get-audio-codec.d.ts +1 -1
  105. package/dist/get-audio-codec.js +3 -3
  106. package/dist/get-container.js +5 -1
  107. package/dist/get-dimensions.d.ts +1 -1
  108. package/dist/get-dimensions.js +4 -1
  109. package/dist/get-duration.js +6 -2
  110. package/dist/get-fields-from-callbacks.js +1 -0
  111. package/dist/get-fps.js +3 -0
  112. package/dist/get-is-hdr.d.ts +1 -1
  113. package/dist/get-is-hdr.js +3 -3
  114. package/dist/get-keyframes.js +1 -1
  115. package/dist/get-tracks.d.ts +2 -2
  116. package/dist/get-tracks.js +23 -15
  117. package/dist/get-video-codec.d.ts +1 -1
  118. package/dist/get-video-codec.js +3 -3
  119. package/dist/has-all-info.js +4 -3
  120. package/dist/index.d.ts +65 -21
  121. package/dist/index.js +1 -1
  122. package/dist/may-skip-video-data/may-skip-video-data.js +6 -2
  123. package/dist/may-skip-video-data/need-samples-for-fields.js +1 -0
  124. package/dist/metadata/get-metadata.d.ts +1 -0
  125. package/dist/metadata/get-metadata.js +16 -1
  126. package/dist/options.d.ts +12 -5
  127. package/dist/parse-media.js +88 -68
  128. package/dist/parse-result.d.ts +17 -19
  129. package/dist/parse-video.d.ts +3 -17
  130. package/dist/parse-video.js +52 -40
  131. package/dist/readers/from-node.js +7 -8
  132. package/dist/state/can-skip-tracks.d.ts +8 -1
  133. package/dist/state/can-skip-tracks.js +38 -26
  134. package/dist/state/emitted-fields.js +1 -0
  135. package/dist/state/images.d.ts +9 -0
  136. package/dist/state/images.js +14 -0
  137. package/dist/state/iso-base-media/cached-sample-positions.d.ts +15 -0
  138. package/dist/state/iso-base-media/cached-sample-positions.js +42 -0
  139. package/dist/state/iso-base-media/iso-state.d.ts +8 -0
  140. package/dist/state/iso-base-media/iso-state.js +15 -0
  141. package/dist/state/iso-state.d.ts +4 -0
  142. package/dist/state/iso-state.js +13 -0
  143. package/dist/state/mp3.d.ts +11 -0
  144. package/dist/state/mp3.js +13 -0
  145. package/dist/state/parser-state.d.ts +57 -11
  146. package/dist/state/parser-state.js +17 -2
  147. package/dist/state/sample-callbacks.d.ts +5 -1
  148. package/dist/state/sample-callbacks.js +8 -2
  149. package/dist/state/slow-duration-fps.d.ts +2 -1
  150. package/dist/state/slow-duration-fps.js +52 -18
  151. package/dist/state/transport-stream.d.ts +8 -0
  152. package/dist/state/transport-stream.js +11 -0
  153. package/dist/state/video-section.d.ts +16 -0
  154. package/dist/state/video-section.js +37 -0
  155. package/dist/state/webm.d.ts +15 -0
  156. package/dist/state/webm.js +32 -0
  157. package/dist/throttled-progress.d.ts +14 -0
  158. package/dist/throttled-progress.js +44 -0
  159. package/dist/version.d.ts +1 -1
  160. package/dist/version.js +1 -1
  161. package/package.json +3 -3
  162. package/test.json +663 -0
package/dist/options.d.ts CHANGED
@@ -5,6 +5,7 @@ import type { LogLevel } from './log';
5
5
  import type { MetadataEntry } from './metadata/get-metadata';
6
6
  import type { Structure } from './parse-result';
7
7
  import type { ReaderInterface } from './readers/reader';
8
+ import type { MediaParserEmbeddedImage } from './state/images';
8
9
  import type { InternalStats } from './state/parser-state';
9
10
  import type { OnAudioTrack, OnVideoTrack } from './webcodec-sample-types';
10
11
  export type KnownAudioCodecs = 'aac' | 'mp3' | 'aiff' | 'opus' | 'pcm' | 'vorbis' | 'unknown';
@@ -31,6 +32,7 @@ export type ParseMediaFields = {
31
32
  keyframes: boolean;
32
33
  slowKeyframes: boolean;
33
34
  slowNumberOfFrames: boolean;
35
+ images: boolean;
34
36
  };
35
37
  export type AllParseMediaFields = {
36
38
  dimensions: true;
@@ -55,6 +57,7 @@ export type AllParseMediaFields = {
55
57
  mimeType: true;
56
58
  keyframes: true;
57
59
  slowKeyframes: true;
60
+ images: true;
58
61
  };
59
62
  export type AllOptions<Fields extends ParseMediaFields> = {
60
63
  dimensions: Fields['dimensions'];
@@ -79,13 +82,14 @@ export type AllOptions<Fields extends ParseMediaFields> = {
79
82
  keyframes: Fields['keyframes'];
80
83
  slowKeyframes: Fields['slowKeyframes'];
81
84
  slowNumberOfFrames: Fields['slowNumberOfFrames'];
85
+ images: Fields['images'];
82
86
  };
83
87
  export type Options<Fields extends ParseMediaFields> = Partial<AllOptions<Fields>>;
84
88
  export type TracksField = {
85
89
  videoTracks: VideoTrack[];
86
90
  audioTracks: AudioTrack[];
87
91
  };
88
- export type ParseMediaContainer = 'mp4' | 'webm' | 'avi' | 'transport-stream';
92
+ export type ParseMediaContainer = 'mp4' | 'webm' | 'avi' | 'transport-stream' | 'mp3';
89
93
  export type MediaParserKeyframe = {
90
94
  positionInBytes: number;
91
95
  sizeInBytes: number;
@@ -94,7 +98,7 @@ export type MediaParserKeyframe = {
94
98
  trackId: number;
95
99
  };
96
100
  export interface ParseMediaCallbacks {
97
- onDimensions?: (dimensions: Dimensions) => void;
101
+ onDimensions?: (dimensions: Dimensions | null) => void;
98
102
  onDurationInSeconds?: (durationInSeconds: number | null) => void;
99
103
  onSlowDurationInSeconds?: (durationInSeconds: number) => void;
100
104
  onSlowFps?: (fps: number) => void;
@@ -104,7 +108,7 @@ export interface ParseMediaCallbacks {
104
108
  onAudioCodec?: (codec: MediaParserAudioCodec | null) => void;
105
109
  onTracks?: (tracks: TracksField) => void;
106
110
  onRotation?: (rotation: number | null) => void;
107
- onUnrotatedDimensions?: (dimensions: Dimensions) => void;
111
+ onUnrotatedDimensions?: (dimensions: Dimensions | null) => void;
108
112
  onInternalStats?: (internalStats: InternalStats) => void;
109
113
  onSize?: (size: number | null) => void;
110
114
  onName?: (name: string) => void;
@@ -116,9 +120,10 @@ export interface ParseMediaCallbacks {
116
120
  onKeyframes?: (keyframes: MediaParserKeyframe[] | null) => void;
117
121
  onSlowKeyframes?: (keyframes: MediaParserKeyframe[]) => void;
118
122
  onSlowNumberOfFrames?: (samples: number) => void;
123
+ onImages?: (images: MediaParserEmbeddedImage[]) => void;
119
124
  }
120
125
  export interface ParseMediaData {
121
- dimensions: Dimensions;
126
+ dimensions: Dimensions | null;
122
127
  durationInSeconds: number | null;
123
128
  slowDurationInSeconds: number;
124
129
  slowFps: number;
@@ -128,7 +133,7 @@ export interface ParseMediaData {
128
133
  audioCodec: MediaParserAudioCodec | null;
129
134
  tracks: TracksField;
130
135
  rotation: number | null;
131
- unrotatedDimensions: Dimensions;
136
+ unrotatedDimensions: Dimensions | null;
132
137
  isHdr: boolean;
133
138
  internalStats: InternalStats;
134
139
  size: number | null;
@@ -140,6 +145,7 @@ export interface ParseMediaData {
140
145
  keyframes: MediaParserKeyframe[] | null;
141
146
  slowKeyframes: MediaParserKeyframe[];
142
147
  slowNumberOfFrames: number;
148
+ images: MediaParserEmbeddedImage[];
143
149
  }
144
150
  export type ParseMediaResult<T extends Partial<ParseMediaFields>> = {
145
151
  [K in keyof T]: T[K] extends true ? K extends keyof ParseMediaData ? ParseMediaData[K] : never : never;
@@ -161,5 +167,6 @@ export type ParseMediaOptions<F extends Options<ParseMediaFields>> = {
161
167
  signal?: AbortSignal;
162
168
  logLevel?: LogLevel;
163
169
  onParseProgress?: ParseMediaOnProgress;
170
+ progressIntervalInMs?: number;
164
171
  } & ParseMediaDynamicOptions<F>;
165
172
  export type ParseMedia = <F extends Options<ParseMediaFields>>(options: ParseMediaOptions<F>) => Promise<ParseMediaResult<F>>;
@@ -9,8 +9,9 @@ const log_1 = require("./log");
9
9
  const parse_video_1 = require("./parse-video");
10
10
  const from_fetch_1 = require("./readers/from-fetch");
11
11
  const parser_state_1 = require("./state/parser-state");
12
- const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader: readerInterface = from_fetch_1.fetchReader, onAudioTrack, onVideoTrack, signal, logLevel = 'info', onParseProgress, ...more }) {
13
- let iterator = null;
12
+ const throttled_progress_1 = require("./throttled-progress");
13
+ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader: readerInterface = from_fetch_1.fetchReader, onAudioTrack, onVideoTrack, signal, logLevel = 'info', onParseProgress: onParseProgressDoNotCallDirectly, progressIntervalInMs, ...more }) {
14
+ var _a;
14
15
  let parseResult = null;
15
16
  const fieldsInReturnValue = _fieldsInReturnValue !== null && _fieldsInReturnValue !== void 0 ? _fieldsInReturnValue : {};
16
17
  const fields = (0, get_fields_from_callbacks_1.getFieldsFromCallback)({
@@ -18,6 +19,10 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
18
19
  callbacks: more,
19
20
  });
20
21
  const { reader, contentLength, name, contentType, supportsContentRange: readerSupportsContentRange, } = await readerInterface.read(src, null, signal);
22
+ const iterator = (0, buffer_iterator_1.getArrayBufferIterator)(new Uint8Array([]), contentLength !== null && contentLength !== void 0 ? contentLength : 1000000000);
23
+ if (contentLength === null) {
24
+ throw new Error('Media was passed with no content length. This is currently not supported. Ensure the media has a "Content-Length" HTTP header.');
25
+ }
21
26
  const supportsContentRange = readerSupportsContentRange &&
22
27
  !(typeof process !== 'undefined' &&
23
28
  typeof process.env !== 'undefined' &&
@@ -31,10 +36,18 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
31
36
  onAudioTrack: onAudioTrack !== null && onAudioTrack !== void 0 ? onAudioTrack : null,
32
37
  onVideoTrack: onVideoTrack !== null && onVideoTrack !== void 0 ? onVideoTrack : null,
33
38
  supportsContentRange,
39
+ contentLength,
40
+ logLevel,
34
41
  });
35
42
  let currentReader = reader;
36
43
  const returnValue = {};
37
44
  const moreFields = more;
45
+ const throttledState = (0, throttled_progress_1.throttledStateUpdate)({
46
+ updateFn: onParseProgressDoNotCallDirectly !== null && onParseProgressDoNotCallDirectly !== void 0 ? onParseProgressDoNotCallDirectly : null,
47
+ everyMilliseconds: progressIntervalInMs !== null && progressIntervalInMs !== void 0 ? progressIntervalInMs : 100,
48
+ signal,
49
+ totalBytes: contentLength,
50
+ });
38
51
  const triggerInfoEmit = () => {
39
52
  const availableInfo = (0, has_all_info_1.getAvailableInfo)({
40
53
  fieldsToFetch: fields,
@@ -52,35 +65,44 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
52
65
  mimeType: contentType,
53
66
  });
54
67
  };
55
- triggerInfoEmit();
56
- while (parseResult === null || parseResult.status === 'incomplete') {
57
- while (true) {
58
- if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
59
- throw new Error('Aborted');
68
+ const checkIfDone = () => {
69
+ if ((0, has_all_info_1.hasAllInfo)({
70
+ fields,
71
+ state,
72
+ })) {
73
+ log_1.Log.verbose(logLevel, 'Got all info, skipping to the end.');
74
+ if (contentLength !== null) {
75
+ state.increaseSkippedBytes(contentLength - iterator.counter.getOffset());
60
76
  }
77
+ return true;
78
+ }
79
+ if (iterator.counter.getOffset() === contentLength) {
80
+ log_1.Log.verbose(logLevel, 'Reached end of file');
81
+ return true;
82
+ }
83
+ return false;
84
+ };
85
+ triggerInfoEmit();
86
+ let iterationWithThisOffset = 0;
87
+ while (!checkIfDone()) {
88
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
89
+ throw new Error('Aborted');
90
+ }
91
+ const offsetBefore = iterator.counter.getOffset();
92
+ const fetchMoreData = async () => {
61
93
  const result = await currentReader.reader.read();
62
- if (iterator) {
63
- if (!result.done) {
64
- iterator.addData(result.value);
65
- }
66
- }
67
- else {
68
- if (result.done) {
69
- throw new Error('Unexpectedly reached EOF');
70
- }
71
- iterator = (0, buffer_iterator_1.getArrayBufferIterator)(result.value, contentLength !== null && contentLength !== void 0 ? contentLength : 1000000000);
72
- }
73
- if (iterator.bytesRemaining() >= 0) {
74
- break;
75
- }
76
- if (result.done) {
77
- break;
94
+ if (result.value) {
95
+ iterator.addData(result.value);
78
96
  }
97
+ };
98
+ while (iterator.bytesRemaining() < 0) {
99
+ await fetchMoreData();
79
100
  }
80
- if (!iterator) {
81
- throw new Error('Unexpected null');
101
+ const hasBigBuffer = iterator.bytesRemaining() > 100000;
102
+ if (iterationWithThisOffset > 0 || !hasBigBuffer) {
103
+ await fetchMoreData();
82
104
  }
83
- await (onParseProgress === null || onParseProgress === void 0 ? void 0 : onParseProgress({
105
+ (_a = throttledState.update) === null || _a === void 0 ? void 0 : _a.call(throttledState, () => ({
84
106
  bytes: iterator.counter.getOffset(),
85
107
  percentage: contentLength
86
108
  ? iterator.counter.getOffset() / contentLength
@@ -88,60 +110,58 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
88
110
  totalBytes: contentLength,
89
111
  }));
90
112
  triggerInfoEmit();
91
- if (parseResult && parseResult.status === 'incomplete') {
92
- log_1.Log.trace(logLevel, 'Continuing parsing of file, currently at position', iterator.counter.getOffset());
93
- parseResult = await parseResult.continueParsing();
94
- }
95
- else {
96
- parseResult = await (0, parse_video_1.parseVideo)({
97
- iterator,
98
- state,
99
- signal: signal !== null && signal !== void 0 ? signal : null,
100
- logLevel,
101
- fields,
102
- mimeType: contentType,
103
- contentLength,
104
- name,
105
- });
113
+ if (iterationWithThisOffset > 300) {
114
+ throw new Error('Infinite loop detected. The parser is not progressing. This is likely a bug in the parser.');
106
115
  }
107
- if (parseResult.status === 'incomplete' && parseResult.skipTo !== null) {
108
- state.increaseSkippedBytes(parseResult.skipTo - iterator.counter.getOffset());
109
- }
110
- if ((0, has_all_info_1.hasAllInfo)({
111
- fields,
116
+ log_1.Log.trace(logLevel, `Continuing parsing of file, currently at position ${iterator.counter.getOffset()}/${contentLength}`);
117
+ parseResult = await (0, parse_video_1.parseVideo)({
118
+ iterator,
112
119
  state,
113
- })) {
114
- log_1.Log.verbose(logLevel, 'Got all info, skipping to the end.');
115
- if (contentLength !== null) {
116
- state.increaseSkippedBytes(contentLength - iterator.counter.getOffset());
117
- }
118
- break;
120
+ mimeType: contentType,
121
+ contentLength,
122
+ name,
123
+ });
124
+ if (parseResult.skipTo !== null) {
125
+ state.increaseSkippedBytes(parseResult.skipTo - iterator.counter.getOffset());
119
126
  }
120
- if (parseResult.status === 'incomplete' && parseResult.skipTo !== null) {
121
- if (!supportsContentRange) {
122
- throw new Error('Content-Range header is not supported by the reader, but was asked to seek');
123
- }
127
+ if (parseResult.skipTo !== null) {
124
128
  if (parseResult.skipTo === contentLength) {
125
129
  log_1.Log.verbose(logLevel, 'Skipped to end of file, not fetching.');
126
130
  break;
127
131
  }
128
- log_1.Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${parseResult.skipTo}`);
129
- currentReader.abort();
130
- const { reader: newReader } = await readerInterface.read(src, parseResult.skipTo, signal);
131
- currentReader = newReader;
132
- iterator.skipTo(parseResult.skipTo, true);
132
+ const skippingAhead = parseResult.skipTo > iterator.counter.getOffset();
133
+ if (!skippingAhead && !supportsContentRange) {
134
+ throw new Error('Content-Range header is not supported by the reader, but was asked to seek');
135
+ }
136
+ if (skippingAhead &&
137
+ iterator.counter.getOffset() + iterator.bytesRemaining() >=
138
+ parseResult.skipTo) {
139
+ log_1.Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${parseResult.skipTo}. Data already fetched`);
140
+ iterator.discard(parseResult.skipTo - iterator.counter.getOffset());
141
+ }
142
+ else {
143
+ log_1.Log.verbose(logLevel, `Skipping over video data from position ${iterator.counter.getOffset()} -> ${parseResult.skipTo}. Re-reading because this portion is not available`);
144
+ currentReader.abort();
145
+ const { reader: newReader } = await readerInterface.read(src, parseResult.skipTo, signal);
146
+ currentReader = newReader;
147
+ iterator.skipTo(parseResult.skipTo, true);
148
+ }
133
149
  }
150
+ const didProgress = iterator.counter.getOffset() > offsetBefore;
151
+ if (!didProgress) {
152
+ iterationWithThisOffset++;
153
+ }
154
+ iterator.removeBytesRead();
134
155
  }
135
156
  log_1.Log.verbose(logLevel, 'Finished parsing file');
136
- const hasInfo = Object.keys(fields).reduce((acc, key) => {
137
- if (fields === null || fields === void 0 ? void 0 : fields[key]) {
138
- acc[key] = true;
139
- }
140
- return acc;
141
- }, {});
142
157
  // Force assign
143
158
  (0, emit_available_info_1.emitAvailableInfo)({
144
- hasInfo,
159
+ hasInfo: Object.keys(fields).reduce((acc, key) => {
160
+ if (fields === null || fields === void 0 ? void 0 : fields[key]) {
161
+ acc[key] = true;
162
+ }
163
+ return acc;
164
+ }, {}),
145
165
  callbacks: moreFields,
146
166
  fieldsInReturnValue,
147
167
  parseResult,
@@ -1,7 +1,6 @@
1
1
  import type { BaseBox } from './boxes/iso-base-media/base-type';
2
2
  import type { EsdsBox } from './boxes/iso-base-media/esds/esds';
3
3
  import type { FtypBox } from './boxes/iso-base-media/ftyp';
4
- import type { MdatBox } from './boxes/iso-base-media/mdat/mdat';
5
4
  import type { MdhdBox } from './boxes/iso-base-media/mdhd';
6
5
  import type { HdlrBox } from './boxes/iso-base-media/meta/hdlr';
7
6
  import type { IlstBox } from './boxes/iso-base-media/meta/ilst';
@@ -30,6 +29,7 @@ import type { VoidBox } from './boxes/iso-base-media/void-box';
30
29
  import type { RiffBox } from './boxes/riff/riff-box';
31
30
  import type { TransportStreamBox } from './boxes/transport-stream/boxes';
32
31
  import type { MatroskaSegment } from './boxes/webm/segments';
32
+ import type { MetadataEntry } from './metadata/get-metadata';
33
33
  export interface RegularBox extends BaseBox {
34
34
  boxType: string;
35
35
  boxSize: number;
@@ -37,7 +37,16 @@ export interface RegularBox extends BaseBox {
37
37
  offset: number;
38
38
  type: 'regular-box';
39
39
  }
40
- export type IsoBaseMediaBox = RegularBox | FtypBox | MvhdBox | TkhdBox | StsdBox | MebxBox | KeysBox | MoovBox | TrakBox | SttsBox | MdhdBox | IlstBox | EsdsBox | MdatBox | StszBox | StcoBox | StscBox | AvccBox | HvccBox | VoidBox | StssBox | PaspBox | CttsBox | Av1CBox | TrunBox | HdlrBox | ColorParameterBox | TfdtBox | TfhdBox;
40
+ export type IsoBaseMediaBox = RegularBox | FtypBox | MvhdBox | TkhdBox | StsdBox | MebxBox | KeysBox | MoovBox | TrakBox | SttsBox | MdhdBox | IlstBox | EsdsBox | StszBox | StcoBox | StscBox | AvccBox | HvccBox | VoidBox | StssBox | PaspBox | CttsBox | Av1CBox | TrunBox | HdlrBox | ColorParameterBox | TfdtBox | TfhdBox;
41
+ type Mp3Id3Header = {
42
+ type: 'id3-header';
43
+ versionMajor: number;
44
+ versionMinor: number;
45
+ flags: number;
46
+ size: number;
47
+ metatags: MetadataEntry[];
48
+ };
49
+ export type Mp3Box = Mp3Id3Header;
41
50
  export type AnySegment = MatroskaSegment | IsoBaseMediaBox | RiffBox | TransportStreamBox;
42
51
  export type IsoBaseMediaStructure = {
43
52
  type: 'iso-base-media';
@@ -55,26 +64,15 @@ export type TransportStreamStructure = {
55
64
  type: 'transport-stream';
56
65
  boxes: TransportStreamBox[];
57
66
  };
58
- export type Structure = IsoBaseMediaStructure | RiffStructure | MatroskaStructure | TransportStreamStructure;
67
+ export type Mp3Structure = {
68
+ type: 'mp3';
69
+ boxes: Mp3Box[];
70
+ };
71
+ export type Structure = IsoBaseMediaStructure | RiffStructure | MatroskaStructure | TransportStreamStructure | Mp3Structure;
59
72
  export type ParseResult = {
60
- status: 'done';
61
- } | {
62
- status: 'incomplete';
63
73
  skipTo: number | null;
64
- continueParsing: () => Promise<ParseResult>;
65
74
  };
66
75
  export type MatroskaParseResult = {
67
- status: 'done';
68
- } | {
69
- status: 'incomplete';
70
76
  skipTo: number | null;
71
- continueParsing: () => Promise<MatroskaParseResult>;
72
- };
73
- export type ExpectSegmentParseResult = {
74
- status: 'done';
75
- segment: MatroskaSegment;
76
- } | {
77
- status: 'incomplete';
78
- segment: MatroskaSegment | null;
79
- continueParsing: () => Promise<ExpectSegmentParseResult>;
80
77
  };
78
+ export {};
@@ -1,27 +1,13 @@
1
1
  import type { BufferIterator } from './buffer-iterator';
2
- import { type LogLevel } from './log';
3
- import type { Options, ParseMediaFields } from './options';
4
2
  import type { IsoBaseMediaBox, ParseResult } from './parse-result';
5
3
  import type { ParserState } from './state/parser-state';
6
- export type PartialMdatBox = {
7
- type: 'partial-mdat-box';
8
- boxSize: number;
9
- fileOffset: number;
10
- };
11
4
  export type BoxAndNext = {
12
- type: 'complete';
13
- box: IsoBaseMediaBox;
14
- size: number;
5
+ box: IsoBaseMediaBox | null;
15
6
  skipTo: number | null;
16
- } | {
17
- type: 'incomplete';
18
- } | PartialMdatBox;
19
- export declare const parseVideo: ({ iterator, state, signal, logLevel, fields, mimeType, contentLength, name, }: {
7
+ };
8
+ export declare const parseVideo: ({ iterator, state, mimeType, contentLength, name, }: {
20
9
  iterator: BufferIterator;
21
10
  state: ParserState;
22
- signal: AbortSignal | null;
23
- logLevel: LogLevel;
24
- fields: Options<ParseMediaFields>;
25
11
  mimeType: string | null;
26
12
  contentLength: number | null;
27
13
  name: string | null;
@@ -1,75 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseVideo = void 0;
4
- const process_box_1 = require("./boxes/iso-base-media/process-box");
5
- const parse_box_1 = require("./boxes/riff/parse-box");
6
- const next_pes_header_store_1 = require("./boxes/transport-stream/next-pes-header-store");
4
+ const parse_boxes_1 = require("./boxes/iso-base-media/parse-boxes");
5
+ const parse_mp3_1 = require("./boxes/mp3/parse-mp3");
6
+ const parse_riff_1 = require("./boxes/riff/parse-riff");
7
7
  const parse_transport_stream_1 = require("./boxes/transport-stream/parse-transport-stream");
8
8
  const parse_webm_header_1 = require("./boxes/webm/parse-webm-header");
9
9
  const errors_1 = require("./errors");
10
10
  const log_1 = require("./log");
11
- const parseVideo = ({ iterator, state, signal, logLevel, fields, mimeType, contentLength, name, }) => {
12
- if (iterator.bytesRemaining() === 0) {
13
- return Promise.reject(new Error('no bytes'));
14
- }
11
+ const initVideo = ({ iterator, state, mimeType, name, contentLength, }) => {
15
12
  const fileType = iterator.detectFileType();
16
13
  if (fileType.type === 'riff') {
17
- log_1.Log.verbose(logLevel, 'Detected RIFF container');
14
+ log_1.Log.verbose(state.logLevel, 'Detected RIFF container');
18
15
  state.structure.setStructure({
19
16
  type: 'riff',
20
17
  boxes: [],
21
18
  });
22
- return Promise.resolve((0, parse_box_1.parseRiff)({ iterator, state, fields }));
19
+ return;
23
20
  }
24
21
  if (fileType.type === 'iso-base-media') {
25
- log_1.Log.verbose(logLevel, 'Detected ISO Base Media container');
26
- const initialBoxes = [];
22
+ log_1.Log.verbose(state.logLevel, 'Detected ISO Base Media container');
27
23
  state.structure.setStructure({
28
24
  type: 'iso-base-media',
29
- boxes: initialBoxes,
30
- });
31
- return (0, process_box_1.parseIsoBaseMediaBoxes)({
32
- iterator,
33
- maxBytes: Infinity,
34
- allowIncompleteBoxes: true,
35
- initialBoxes,
36
- state,
37
- continueMdat: false,
38
- signal,
39
- logLevel,
40
- fields,
25
+ boxes: [],
41
26
  });
27
+ return;
42
28
  }
43
29
  if (fileType.type === 'webm') {
44
- log_1.Log.verbose(logLevel, 'Detected Matroska container');
30
+ log_1.Log.verbose(state.logLevel, 'Detected Matroska container');
45
31
  state.structure.setStructure({
46
32
  boxes: [],
47
33
  type: 'matroska',
48
34
  });
49
- return (0, parse_webm_header_1.parseWebm)({ counter: iterator, state, fields });
35
+ return;
50
36
  }
51
37
  if (fileType.type === 'transport-stream') {
52
- log_1.Log.verbose(logLevel, 'Detected MPEG-2 Transport Stream');
38
+ log_1.Log.verbose(state.logLevel, 'Detected MPEG-2 Transport Stream');
53
39
  state.structure.setStructure({
54
40
  boxes: [],
55
41
  type: 'transport-stream',
56
42
  });
57
- return (0, parse_transport_stream_1.parseTransportStream)({
58
- iterator,
59
- state,
60
- streamBuffers: new Map(),
61
- fields,
62
- nextPesHeaderStore: (0, next_pes_header_store_1.makeNextPesHeaderStore)(),
63
- });
43
+ return;
64
44
  }
65
45
  if (fileType.type === 'mp3') {
66
- return Promise.reject(new errors_1.IsAnUnsupportedAudioTypeError({
67
- message: 'MP3 files are not yet supported',
68
- mimeType,
69
- sizeInBytes: contentLength,
70
- fileName: name,
71
- audioType: 'mp3',
72
- }));
46
+ log_1.Log.verbose(state.logLevel, 'Detected MP3');
47
+ const structure = {
48
+ boxes: [],
49
+ type: 'mp3',
50
+ };
51
+ state.structure.setStructure(structure);
52
+ return;
73
53
  }
74
54
  if (fileType.type === 'wav') {
75
55
  return Promise.reject(new errors_1.IsAnUnsupportedAudioTypeError({
@@ -128,4 +108,36 @@ const parseVideo = ({ iterator, state, signal, logLevel, fields, mimeType, conte
128
108
  }
129
109
  return Promise.reject(new Error('Unknown video format ' + fileType));
130
110
  };
111
+ const parseVideo = async ({ iterator, state, mimeType, contentLength, name, }) => {
112
+ if (iterator.bytesRemaining() === 0) {
113
+ return Promise.reject(new Error('no bytes'));
114
+ }
115
+ const structure = state.structure.getStructureOrNull();
116
+ if (structure === null) {
117
+ await initVideo({ iterator, state, mimeType, name, contentLength });
118
+ return { skipTo: null };
119
+ }
120
+ if (structure.type === 'riff') {
121
+ return (0, parse_riff_1.parseRiff)({ iterator, state });
122
+ }
123
+ if (structure.type === 'mp3') {
124
+ return (0, parse_mp3_1.parseMp3)({ iterator, state });
125
+ }
126
+ if (structure.type === 'iso-base-media') {
127
+ return (0, parse_boxes_1.parseIsoBaseMedia)({
128
+ iterator,
129
+ state,
130
+ });
131
+ }
132
+ if (structure.type === 'matroska') {
133
+ return (0, parse_webm_header_1.parseWebm)({ iterator, state });
134
+ }
135
+ if (structure.type === 'transport-stream') {
136
+ return (0, parse_transport_stream_1.parseTransportStream)({
137
+ iterator,
138
+ state,
139
+ });
140
+ }
141
+ return Promise.reject(new Error('Unknown video format ' + structure));
142
+ };
131
143
  exports.parseVideo = parseVideo;
@@ -2,11 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.nodeReader = void 0;
4
4
  const fs_1 = require("fs");
5
- const promises_1 = require("node:fs/promises");
6
5
  const path_1 = require("path");
7
6
  const stream_1 = require("stream");
8
7
  exports.nodeReader = {
9
- read: async (src, range, signal) => {
8
+ read: (src, range, signal) => {
10
9
  if (typeof src !== 'string') {
11
10
  throw new Error('src must be a string when using `nodeReader`');
12
11
  }
@@ -23,14 +22,14 @@ exports.nodeReader = {
23
22
  signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', () => {
24
23
  controller.abort();
25
24
  }, { once: true });
26
- const stats = await (0, promises_1.stat)(src);
25
+ const stats = (0, fs_1.statSync)(src);
27
26
  const reader = stream_1.Readable.toWeb(stream).getReader();
28
27
  if (signal) {
29
28
  signal.addEventListener('abort', () => {
30
29
  reader.cancel().catch(() => { });
31
30
  }, { once: true });
32
31
  }
33
- return {
32
+ return Promise.resolve({
34
33
  reader: {
35
34
  reader,
36
35
  abort: () => {
@@ -41,13 +40,13 @@ exports.nodeReader = {
41
40
  contentType: null,
42
41
  name: src.split(path_1.sep).pop(),
43
42
  supportsContentRange: true,
44
- };
43
+ });
45
44
  },
46
- getLength: async (src) => {
45
+ getLength: (src) => {
47
46
  if (typeof src !== 'string') {
48
47
  throw new Error('src must be a string when using `nodeReader`');
49
48
  }
50
- const stats = await (0, promises_1.stat)(src);
51
- return stats.size;
49
+ const stats = (0, fs_1.statSync)(src);
50
+ return Promise.resolve(stats.size);
52
51
  },
53
52
  };
@@ -1,8 +1,15 @@
1
1
  import type { Options, ParseMediaFields } from '../options';
2
- export declare const makeCanSkipTracksState: ({ hasAudioTrackHandlers, fields, hasVideoTrackHandlers, }: {
2
+ import type { Structure } from '../parse-result';
3
+ import type { StructureState } from './structure';
4
+ export declare const needsTracksForField: ({ field, structure, }: {
5
+ field: keyof Options<ParseMediaFields>;
6
+ structure: Structure;
7
+ }) => boolean;
8
+ export declare const makeCanSkipTracksState: ({ hasAudioTrackHandlers, fields, hasVideoTrackHandlers, structure, }: {
3
9
  hasAudioTrackHandlers: boolean;
4
10
  hasVideoTrackHandlers: boolean;
5
11
  fields: Options<ParseMediaFields>;
12
+ structure: StructureState;
6
13
  }) => {
7
14
  canSkipTracks: () => boolean;
8
15
  };