@remotion/media-parser 4.0.248 → 4.0.249

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 (92) hide show
  1. package/dist/boxes/avc/key.d.ts +1 -1
  2. package/dist/boxes/iso-base-media/get-children.d.ts +14 -0
  3. package/dist/boxes/iso-base-media/get-children.js +39 -0
  4. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +2 -1
  5. package/dist/boxes/iso-base-media/mdat/mdat.js +10 -1
  6. package/dist/boxes/iso-base-media/moov/moov.js +2 -3
  7. package/dist/boxes/iso-base-media/parse-boxes.d.ts +15 -0
  8. package/dist/boxes/iso-base-media/parse-boxes.js +129 -0
  9. package/dist/boxes/iso-base-media/parse-mdat-partially.d.ts +12 -0
  10. package/dist/boxes/iso-base-media/parse-mdat-partially.js +33 -0
  11. package/dist/boxes/iso-base-media/process-box.d.ts +2 -21
  12. package/dist/boxes/iso-base-media/process-box.js +53 -270
  13. package/dist/boxes/iso-base-media/stsd/mebx.js +2 -3
  14. package/dist/boxes/iso-base-media/stsd/samples.js +5 -9
  15. package/dist/boxes/iso-base-media/trak/trak.js +2 -3
  16. package/dist/boxes/mp3/get-duration.d.ts +2 -0
  17. package/dist/boxes/mp3/get-duration.js +30 -0
  18. package/dist/boxes/mp3/get-frame-length.d.ts +13 -0
  19. package/dist/boxes/mp3/get-frame-length.js +33 -0
  20. package/dist/boxes/mp3/get-metadata-from-mp3.d.ts +3 -0
  21. package/dist/boxes/mp3/get-metadata-from-mp3.js +8 -0
  22. package/dist/boxes/mp3/get-tracks-from-mp3.d.ts +4 -0
  23. package/dist/boxes/mp3/get-tracks-from-mp3.js +25 -0
  24. package/dist/boxes/mp3/id3-v1.d.ts +2 -0
  25. package/dist/boxes/mp3/id3-v1.js +12 -0
  26. package/dist/boxes/mp3/id3-v2.d.ts +0 -0
  27. package/dist/boxes/mp3/id3-v2.js +1 -0
  28. package/dist/boxes/mp3/id3.d.ts +8 -0
  29. package/dist/boxes/mp3/id3.js +78 -0
  30. package/dist/boxes/mp3/parse-mp3.d.ts +8 -0
  31. package/dist/boxes/mp3/parse-mp3.js +57 -0
  32. package/dist/boxes/mp3/parse-mpeg-header.d.ts +6 -0
  33. package/dist/boxes/mp3/parse-mpeg-header.js +274 -0
  34. package/dist/boxes/mp3/samples-per-mpeg-file.d.ts +4 -0
  35. package/dist/boxes/mp3/samples-per-mpeg-file.js +26 -0
  36. package/dist/boxes/riff/continue-after-riff-result.d.ts +13 -0
  37. package/dist/boxes/riff/continue-after-riff-result.js +34 -0
  38. package/dist/boxes/riff/expect-riff-box.d.ts +3 -1
  39. package/dist/boxes/riff/expect-riff-box.js +4 -3
  40. package/dist/boxes/riff/parse-box.d.ts +1 -7
  41. package/dist/boxes/riff/parse-box.js +4 -120
  42. package/dist/boxes/riff/parse-list-box.d.ts +3 -1
  43. package/dist/boxes/riff/parse-list-box.js +4 -3
  44. package/dist/boxes/riff/parse-riff-body.d.ts +11 -0
  45. package/dist/boxes/riff/parse-riff-body.js +105 -0
  46. package/dist/boxes/riff/parse-riff-box.d.ts +3 -1
  47. package/dist/boxes/riff/parse-riff-box.js +2 -2
  48. package/dist/boxes/transport-stream/parse-transport-stream.js +30 -41
  49. package/dist/buffer-iterator.d.ts +6 -0
  50. package/dist/buffer-iterator.js +21 -0
  51. package/dist/continue-mdat-routine.d.ts +17 -0
  52. package/dist/continue-mdat-routine.js +92 -0
  53. package/dist/emit-available-info.js +38 -24
  54. package/dist/esm/index.mjs +1820 -1046
  55. package/dist/file-types/detect-file-type.js +4 -2
  56. package/dist/get-audio-codec.js +1 -1
  57. package/dist/get-container.js +5 -1
  58. package/dist/get-dimensions.d.ts +1 -1
  59. package/dist/get-dimensions.js +3 -0
  60. package/dist/get-duration.js +5 -1
  61. package/dist/get-fields-from-callbacks.js +1 -0
  62. package/dist/get-fps.js +3 -0
  63. package/dist/get-is-hdr.js +1 -1
  64. package/dist/get-keyframes.js +1 -1
  65. package/dist/get-tracks.d.ts +1 -1
  66. package/dist/get-tracks.js +10 -3
  67. package/dist/get-video-codec.js +1 -1
  68. package/dist/has-all-info.js +4 -3
  69. package/dist/index.d.ts +20 -2
  70. package/dist/may-skip-video-data/need-samples-for-fields.js +1 -0
  71. package/dist/metadata/get-metadata.d.ts +1 -0
  72. package/dist/metadata/get-metadata.js +16 -1
  73. package/dist/options.d.ts +12 -5
  74. package/dist/parse-media.js +48 -43
  75. package/dist/parse-result.d.ts +16 -1
  76. package/dist/parse-video.js +9 -10
  77. package/dist/state/can-skip-tracks.js +1 -0
  78. package/dist/state/emitted-fields.js +1 -0
  79. package/dist/state/images.d.ts +9 -0
  80. package/dist/state/images.js +14 -0
  81. package/dist/state/mp3.d.ts +11 -0
  82. package/dist/state/mp3.js +13 -0
  83. package/dist/state/parser-state.d.ts +13 -2
  84. package/dist/state/parser-state.js +8 -1
  85. package/dist/state/sample-callbacks.js +4 -1
  86. package/dist/state/slow-duration-fps.d.ts +2 -1
  87. package/dist/state/slow-duration-fps.js +52 -18
  88. package/dist/throttled-progress.d.ts +14 -0
  89. package/dist/throttled-progress.js +44 -0
  90. package/dist/version.d.ts +1 -1
  91. package/dist/version.js +1 -1
  92. package/package.json +3 -3
@@ -43,10 +43,12 @@ const isTransportStream = (data) => {
43
43
  exports.isTransportStream = isTransportStream;
44
44
  const isMp3 = (data) => {
45
45
  const mpegPattern = new Uint8Array([0xff, 0xf3, 0xe4, 0x64]);
46
- const id3Pattern = new Uint8Array([73, 68, 51, 3]);
46
+ const id3v3Pattern = new Uint8Array([73, 68, 51, 3]);
47
+ const id3v2Pattern = new Uint8Array([73, 68, 51, 2]);
47
48
  const subarray = data.subarray(0, 4);
48
49
  return ((0, exports.matchesPattern)(mpegPattern)(subarray) ||
49
- (0, exports.matchesPattern)(id3Pattern)(subarray));
50
+ (0, exports.matchesPattern)(id3v3Pattern)(subarray) ||
51
+ (0, exports.matchesPattern)(id3v2Pattern)(subarray));
50
52
  };
51
53
  exports.isMp3 = isMp3;
52
54
  const isGif = (data) => {
@@ -23,7 +23,7 @@ const getAudioCodec = (boxes, parserState) => {
23
23
  };
24
24
  exports.getAudioCodec = getAudioCodec;
25
25
  const hasAudioCodec = (boxes, state) => {
26
- return (0, get_tracks_1.hasTracks)(boxes, state);
26
+ return (0, get_tracks_1.getHasTracks)(boxes, state);
27
27
  };
28
28
  exports.hasAudioCodec = hasAudioCodec;
29
29
  const getCodecSpecificatorFromEsdsBox = ({ child, }) => {
@@ -12,12 +12,16 @@ const getContainer = (segments) => {
12
12
  if (segments.type === 'transport-stream') {
13
13
  return 'transport-stream';
14
14
  }
15
+ if (segments.type === 'mp3') {
16
+ return 'mp3';
17
+ }
15
18
  if (segments.type === 'riff') {
16
19
  if ((0, traversal_1.isRiffAvi)(segments)) {
17
20
  return 'avi';
18
21
  }
22
+ throw new Error('Unknown RIFF container ' + segments.type);
19
23
  }
20
- throw new Error('Unknown container');
24
+ throw new Error('Unknown container ' + segments);
21
25
  };
22
26
  exports.getContainer = getContainer;
23
27
  const hasContainer = (boxes) => {
@@ -9,5 +9,5 @@ export type ExpandedDimensions = Dimensions & {
9
9
  unrotatedWidth: number;
10
10
  unrotatedHeight: number;
11
11
  };
12
- export declare const getDimensions: (boxes: Structure, state: ParserState) => ExpandedDimensions;
12
+ export declare const getDimensions: (boxes: Structure, state: ParserState) => ExpandedDimensions | null;
13
13
  export declare const hasDimensions: (boxes: Structure, state: ParserState) => boolean;
@@ -5,6 +5,9 @@ const get_tracks_1 = require("./get-tracks");
5
5
  const getDimensions = (boxes, state) => {
6
6
  const { videoTracks } = (0, get_tracks_1.getTracks)(boxes, state);
7
7
  if (!videoTracks.length) {
8
+ if (boxes.type === 'mp3') {
9
+ return null;
10
+ }
8
11
  throw new Error('Expected video track');
9
12
  }
10
13
  const firstVideoTrack = videoTracks[0];
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hasSlowDuration = exports.hasDuration = exports.getDuration = exports.isMatroska = void 0;
4
4
  const get_sample_positions_from_track_1 = require("./boxes/iso-base-media/get-sample-positions-from-track");
5
5
  const traversal_1 = require("./boxes/iso-base-media/traversal");
6
+ const get_duration_1 = require("./boxes/mp3/get-duration");
6
7
  const traversal_2 = require("./boxes/riff/traversal");
7
8
  const get_tracks_1 = require("./get-tracks");
8
9
  const getDurationFromMatroska = (segments) => {
@@ -92,13 +93,16 @@ const getDuration = (structure, parserState) => {
92
93
  if (structure.type === 'transport-stream') {
93
94
  return null;
94
95
  }
96
+ if (structure.type === 'mp3') {
97
+ return (0, get_duration_1.getDurationFromMp3)(parserState);
98
+ }
95
99
  throw new Error('Has no duration ' + structure);
96
100
  };
97
101
  exports.getDuration = getDuration;
98
102
  // `duration` just grabs from metadata, and otherwise returns null
99
103
  // Therefore just checking if we have tracks
100
104
  const hasDuration = (structure, parserState) => {
101
- return (0, get_tracks_1.hasTracks)(structure, parserState);
105
+ return (0, get_tracks_1.getHasTracks)(structure, parserState);
102
106
  };
103
107
  exports.hasDuration = hasDuration;
104
108
  // `slowDuration` does through everything, and therefore is false
@@ -25,6 +25,7 @@ const getFieldsFromCallback = ({ fields, callbacks, }) => {
25
25
  slowFps: Boolean(callbacks.onSlowFps),
26
26
  slowNumberOfFrames: Boolean(callbacks.onSlowNumberOfFrames),
27
27
  keyframes: Boolean(callbacks.onKeyframes),
28
+ images: Boolean(callbacks.onImages),
28
29
  ...fields,
29
30
  };
30
31
  return newFields;
package/dist/get-fps.js CHANGED
@@ -101,6 +101,9 @@ const getFps = (segments) => {
101
101
  if (segments.type === 'transport-stream') {
102
102
  return null;
103
103
  }
104
+ if (segments.type === 'mp3') {
105
+ return null;
106
+ }
104
107
  throw new Error('Cannot get fps, not implemented');
105
108
  };
106
109
  exports.getFps = getFps;
@@ -13,6 +13,6 @@ const getIsHdr = (boxes, state) => {
13
13
  };
14
14
  exports.getIsHdr = getIsHdr;
15
15
  const hasHdr = (boxes, state) => {
16
- return (0, get_tracks_1.hasTracks)(boxes, state);
16
+ return (0, get_tracks_1.getHasTracks)(boxes, state);
17
17
  };
18
18
  exports.hasHdr = hasHdr;
@@ -12,7 +12,7 @@ const getKeyframes = (structure) => {
12
12
  exports.getKeyframes = getKeyframes;
13
13
  const hasKeyframes = (structure, parserState) => {
14
14
  if (structure.type === 'iso-base-media') {
15
- return (0, get_tracks_1.hasTracks)(structure, parserState);
15
+ return (0, get_tracks_1.getHasTracks)(structure, parserState);
16
16
  }
17
17
  // Has, but will be null
18
18
  return true;
@@ -57,7 +57,7 @@ export type OtherTrack = {
57
57
  export type Track = VideoTrack | AudioTrack | OtherTrack;
58
58
  export declare const getNumberOfTracks: (moovBox: MoovBox) => number;
59
59
  export declare const isoBaseMediaHasTracks: (structure: IsoBaseMediaStructure) => boolean;
60
- export declare const hasTracks: (structure: Structure, state: ParserState) => boolean;
60
+ export declare const getHasTracks: (structure: Structure, state: ParserState) => boolean;
61
61
  export declare const getTracksFromIsoBaseMedia: (segments: IsoBaseMediaBox[]) => {
62
62
  videoTracks: VideoTrack[];
63
63
  audioTracks: AudioTrack[];
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getTracks = exports.getTracksFromIsoBaseMedia = exports.hasTracks = exports.isoBaseMediaHasTracks = exports.getNumberOfTracks = void 0;
3
+ exports.getTracks = exports.getTracksFromIsoBaseMedia = exports.getHasTracks = exports.isoBaseMediaHasTracks = exports.getNumberOfTracks = void 0;
4
4
  const make_track_1 = require("./boxes/iso-base-media/make-track");
5
5
  const traversal_1 = require("./boxes/iso-base-media/traversal");
6
+ const get_tracks_from_mp3_1 = require("./boxes/mp3/get-tracks-from-mp3");
6
7
  const get_tracks_from_avi_1 = require("./boxes/riff/get-tracks-from-avi");
7
8
  const get_tracks_1 = require("./boxes/transport-stream/get-tracks");
8
9
  const get_ready_tracks_1 = require("./boxes/webm/get-ready-tracks");
@@ -25,7 +26,7 @@ const isoBaseMediaHasTracks = (structure) => {
25
26
  return tracks.length === numberOfTracks;
26
27
  };
27
28
  exports.isoBaseMediaHasTracks = isoBaseMediaHasTracks;
28
- const hasTracks = (structure, state) => {
29
+ const getHasTracks = (structure, state) => {
29
30
  if (structure.type === 'matroska') {
30
31
  const mainSegment = (0, traversal_2.getMainSegment)(structure.boxes);
31
32
  if (!mainSegment) {
@@ -42,9 +43,12 @@ const hasTracks = (structure, state) => {
42
43
  if (structure.type === 'transport-stream') {
43
44
  return (0, get_tracks_1.hasAllTracksFromTransportStream)(structure, state);
44
45
  }
46
+ if (structure.type === 'mp3') {
47
+ return state.callbacks.tracks.getTracks().length > 0;
48
+ }
45
49
  throw new Error('Unknown container ' + structure);
46
50
  };
47
- exports.hasTracks = hasTracks;
51
+ exports.getHasTracks = getHasTracks;
48
52
  const getTracksFromMa = (segments, state) => {
49
53
  const videoTracks = [];
50
54
  const audioTracks = [];
@@ -119,6 +123,9 @@ const getTracks = (segments, state) => {
119
123
  if (segments.type === 'transport-stream') {
120
124
  return (0, get_tracks_1.getTracksFromTransportStream)(segments, state);
121
125
  }
126
+ if (segments.type === 'mp3') {
127
+ return (0, get_tracks_from_mp3_1.getTracksFromMp3)(state);
128
+ }
122
129
  throw new Error(`Unknown container${segments}`);
123
130
  };
124
131
  exports.getTracks = getTracks;
@@ -12,7 +12,7 @@ const getVideoCodec = (boxes, state) => {
12
12
  };
13
13
  exports.getVideoCodec = getVideoCodec;
14
14
  const hasVideoCodec = (boxes, state) => {
15
- return (0, get_tracks_1.hasTracks)(boxes, state);
15
+ return (0, get_tracks_1.getHasTracks)(boxes, state);
16
16
  };
17
17
  exports.hasVideoCodec = hasVideoCodec;
18
18
  const getVideoPrivateData = (trakBox) => {
@@ -11,6 +11,7 @@ const get_keyframes_1 = require("./get-keyframes");
11
11
  const get_tracks_1 = require("./get-tracks");
12
12
  const get_video_codec_1 = require("./get-video-codec");
13
13
  const may_skip_video_data_1 = require("./may-skip-video-data/may-skip-video-data");
14
+ const get_metadata_1 = require("./metadata/get-metadata");
14
15
  const getAvailableInfo = ({ fieldsToFetch, state, }) => {
15
16
  const keys = Object.entries(fieldsToFetch).filter(([, value]) => value);
16
17
  const structure = state.structure.getStructureOrNull();
@@ -47,7 +48,7 @@ const getAvailableInfo = ({ fieldsToFetch, state, }) => {
47
48
  return Boolean(structure && (0, get_audio_codec_1.hasAudioCodec)(structure, state));
48
49
  }
49
50
  if (key === 'tracks') {
50
- return Boolean(structure && (0, get_tracks_1.hasTracks)(structure, state));
51
+ return Boolean(structure && (0, get_tracks_1.getHasTracks)(structure, state));
51
52
  }
52
53
  if (key === 'keyframes') {
53
54
  return Boolean(structure && (0, get_keyframes_1.hasKeyframes)(structure, state));
@@ -67,8 +68,8 @@ const getAvailableInfo = ({ fieldsToFetch, state, }) => {
67
68
  if (key === 'container') {
68
69
  return Boolean(structure && (0, get_container_1.hasContainer)(structure));
69
70
  }
70
- if (key === 'metadata' || key === 'location') {
71
- return false;
71
+ if (key === 'metadata' || key === 'location' || key === 'images') {
72
+ return Boolean(structure && (0, get_metadata_1.hasMetadata)(structure));
72
73
  }
73
74
  if (key === 'slowKeyframes') {
74
75
  return false;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { IsAGifError, IsAnImageError, IsAnUnsupportedAudioTypeError, IsAnUnsuppo
5
5
  export type { SamplePosition } from './get-sample-positions';
6
6
  export { MetadataEntry } from './metadata/get-metadata';
7
7
  export { MediaParserKeyframe } from './options';
8
+ export type { MediaParserEmbeddedImage } from './state/images';
8
9
  export { AudioTrack, MediaParserAudioCodec, MediaParserVideoCodec, OtherTrack, Track, VideoTrack, VideoTrackColorParams, } from './get-tracks';
9
10
  export type { Options, ParseMediaContainer, ParseMediaDynamicOptions, ParseMediaFields, ParseMediaOnProgress, ParseMediaOptions, ParseMediaProgress, ParseMediaResult, TracksField, } from './options';
10
11
  export { parseMedia } from './parse-media';
@@ -694,6 +695,9 @@ export declare const MediaParserInternals: {
694
695
  getPascalString: () => number[];
695
696
  getUint(length: number): number;
696
697
  getByteString(length: number, trimTrailingZeroes: boolean): string;
698
+ planBytes: (size: number) => {
699
+ discardRest: () => Uint8Array;
700
+ };
697
701
  getFloat64: () => number;
698
702
  readUntilNullTerminator: () => string;
699
703
  getFloat32: () => number;
@@ -708,6 +712,9 @@ export declare const MediaParserInternals: {
708
712
  expectNoMoreBytes: () => void;
709
713
  };
710
714
  readExpGolomb: () => number;
715
+ startCheckpoint: () => {
716
+ returnToCheckpoint: () => void;
717
+ };
711
718
  };
712
719
  parseStsd: ({ iterator, offset, size, state, signal, fields, }: {
713
720
  iterator: import("./buffer-iterator").BufferIterator;
@@ -717,7 +724,7 @@ export declare const MediaParserInternals: {
717
724
  signal: AbortSignal | null;
718
725
  fields: import("./options").Options<import("./options").ParseMediaFields>;
719
726
  }) => Promise<import("./boxes/iso-base-media/stsd/stsd").StsdBox>;
720
- makeParserState: ({ hasAudioTrackHandlers, hasVideoTrackHandlers, signal, getIterator, fields, onAudioTrack, onVideoTrack, supportsContentRange, }: {
727
+ makeParserState: ({ hasAudioTrackHandlers, hasVideoTrackHandlers, signal, getIterator, fields, onAudioTrack, onVideoTrack, supportsContentRange, contentLength, }: {
721
728
  hasAudioTrackHandlers: boolean;
722
729
  hasVideoTrackHandlers: boolean;
723
730
  signal: AbortSignal | undefined;
@@ -726,6 +733,7 @@ export declare const MediaParserInternals: {
726
733
  supportsContentRange: boolean;
727
734
  onAudioTrack: import("./webcodec-sample-types").OnAudioTrack | null;
728
735
  onVideoTrack: import("./webcodec-sample-types").OnVideoTrack | null;
736
+ contentLength: number | null;
729
737
  }) => {
730
738
  riff: {
731
739
  getAvcProfile: () => import("./state/parser-state").SpsAndPps | null;
@@ -780,11 +788,21 @@ export declare const MediaParserInternals: {
780
788
  emittedFields: import("./options").AllOptions<import("./options").ParseMediaFields>;
781
789
  fields: Partial<import("./options").AllOptions<import("./options").ParseMediaFields>>;
782
790
  slowDurationAndFps: {
783
- addSample: (videoSample: import("./webcodec-sample-types").AudioOrVideoSample) => void;
791
+ addVideoSample: (videoSample: import("./webcodec-sample-types").AudioOrVideoSample) => void;
792
+ addAudioSample: (audioSample: import("./webcodec-sample-types").AudioOrVideoSample) => void;
784
793
  getSlowDurationInSeconds: () => number;
785
794
  getFps: () => number;
786
795
  getSlowNumberOfFrames: () => number;
787
796
  };
797
+ mp3Info: {
798
+ getMp3Info: () => import("./state/mp3").Mp3Info | null;
799
+ setMp3Info: (info: import("./state/mp3").Mp3Info) => void;
800
+ };
801
+ contentLength: number | null;
802
+ images: {
803
+ images: import("./state/images").MediaParserEmbeddedImage[];
804
+ addImage: (image: import("./state/images").MediaParserEmbeddedImage) => void;
805
+ };
788
806
  };
789
807
  processSample: ({ iterator, state: options, signal, logLevel, fields, }: {
790
808
  iterator: import("./buffer-iterator").BufferIterator;
@@ -24,6 +24,7 @@ const needsSamples = {
24
24
  location: false,
25
25
  mimeType: false,
26
26
  keyframes: false,
27
+ images: false,
27
28
  };
28
29
  const needsToIterateOverSamples = ({ fields, emittedFields, }) => {
29
30
  const keys = Object.keys(fields !== null && fields !== void 0 ? fields : {});
@@ -5,3 +5,4 @@ export type MetadataEntry = {
5
5
  trackId: number | null;
6
6
  };
7
7
  export declare const getMetadata: (structure: Structure) => MetadataEntry[];
8
+ export declare const hasMetadata: (structure: Structure) => boolean;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getMetadata = void 0;
3
+ exports.hasMetadata = exports.getMetadata = void 0;
4
+ const get_metadata_from_mp3_1 = require("../boxes/mp3/get-metadata-from-mp3");
4
5
  const metadata_from_iso_1 = require("./metadata-from-iso");
5
6
  const metadata_from_matroska_1 = require("./metadata-from-matroska");
6
7
  const metadata_from_riff_1 = require("./metadata-from-riff");
@@ -14,6 +15,20 @@ const getMetadata = (structure) => {
14
15
  if (structure.type === 'transport-stream') {
15
16
  return [];
16
17
  }
18
+ if (structure.type === 'mp3') {
19
+ const tags = (0, get_metadata_from_mp3_1.getMetadataFromMp3)(structure);
20
+ if (tags === null) {
21
+ throw new Error('Failed to get metadata from mp3');
22
+ }
23
+ return tags;
24
+ }
17
25
  return (0, metadata_from_iso_1.getMetadataFromIsoBase)(structure);
18
26
  };
19
27
  exports.getMetadata = getMetadata;
28
+ const hasMetadata = (structure) => {
29
+ if (structure.type === 'mp3') {
30
+ return (0, get_metadata_from_mp3_1.getMetadataFromMp3)(structure) !== null;
31
+ }
32
+ return false;
33
+ };
34
+ exports.hasMetadata = hasMetadata;
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,7 @@ 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);
21
23
  const supportsContentRange = readerSupportsContentRange &&
22
24
  !(typeof process !== 'undefined' &&
23
25
  typeof process.env !== 'undefined' &&
@@ -31,10 +33,17 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
31
33
  onAudioTrack: onAudioTrack !== null && onAudioTrack !== void 0 ? onAudioTrack : null,
32
34
  onVideoTrack: onVideoTrack !== null && onVideoTrack !== void 0 ? onVideoTrack : null,
33
35
  supportsContentRange,
36
+ contentLength,
34
37
  });
35
38
  let currentReader = reader;
36
39
  const returnValue = {};
37
40
  const moreFields = more;
41
+ const throttledState = (0, throttled_progress_1.throttledStateUpdate)({
42
+ updateFn: onParseProgressDoNotCallDirectly !== null && onParseProgressDoNotCallDirectly !== void 0 ? onParseProgressDoNotCallDirectly : null,
43
+ everyMilliseconds: progressIntervalInMs !== null && progressIntervalInMs !== void 0 ? progressIntervalInMs : 100,
44
+ signal,
45
+ totalBytes: contentLength,
46
+ });
38
47
  const triggerInfoEmit = () => {
39
48
  const availableInfo = (0, has_all_info_1.getAvailableInfo)({
40
49
  fieldsToFetch: fields,
@@ -52,35 +61,41 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
52
61
  mimeType: contentType,
53
62
  });
54
63
  };
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');
64
+ const checkIfDone = () => {
65
+ if ((0, has_all_info_1.hasAllInfo)({
66
+ fields,
67
+ state,
68
+ })) {
69
+ log_1.Log.verbose(logLevel, 'Got all info, skipping to the end.');
70
+ if (contentLength !== null) {
71
+ state.increaseSkippedBytes(contentLength - iterator.counter.getOffset());
60
72
  }
73
+ return true;
74
+ }
75
+ return false;
76
+ };
77
+ triggerInfoEmit();
78
+ let didProgress = false;
79
+ while (!checkIfDone() &&
80
+ (parseResult === null || parseResult.status === 'incomplete')) {
81
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
82
+ throw new Error('Aborted');
83
+ }
84
+ const offsetBefore = iterator.counter.getOffset();
85
+ const fetchMoreData = async () => {
61
86
  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;
87
+ if (result.value) {
88
+ iterator.addData(result.value);
78
89
  }
90
+ };
91
+ while (iterator.bytesRemaining() < 0) {
92
+ await fetchMoreData();
79
93
  }
80
- if (!iterator) {
81
- throw new Error('Unexpected null');
94
+ const hasBigBuffer = iterator.bytesRemaining() > 100000;
95
+ if (!didProgress || !hasBigBuffer) {
96
+ await fetchMoreData();
82
97
  }
83
- await (onParseProgress === null || onParseProgress === void 0 ? void 0 : onParseProgress({
98
+ (_a = throttledState.update) === null || _a === void 0 ? void 0 : _a.call(throttledState, () => ({
84
99
  bytes: iterator.counter.getOffset(),
85
100
  percentage: contentLength
86
101
  ? iterator.counter.getOffset() / contentLength
@@ -107,16 +122,6 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
107
122
  if (parseResult.status === 'incomplete' && parseResult.skipTo !== null) {
108
123
  state.increaseSkippedBytes(parseResult.skipTo - iterator.counter.getOffset());
109
124
  }
110
- if ((0, has_all_info_1.hasAllInfo)({
111
- fields,
112
- 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;
119
- }
120
125
  if (parseResult.status === 'incomplete' && parseResult.skipTo !== null) {
121
126
  if (!supportsContentRange) {
122
127
  throw new Error('Content-Range header is not supported by the reader, but was asked to seek');
@@ -131,17 +136,17 @@ const parseMedia = async function ({ src, fields: _fieldsInReturnValue, reader:
131
136
  currentReader = newReader;
132
137
  iterator.skipTo(parseResult.skipTo, true);
133
138
  }
139
+ didProgress = iterator.counter.getOffset() > offsetBefore;
134
140
  }
135
141
  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
142
  // Force assign
143
143
  (0, emit_available_info_1.emitAvailableInfo)({
144
- hasInfo,
144
+ hasInfo: Object.keys(fields).reduce((acc, key) => {
145
+ if (fields === null || fields === void 0 ? void 0 : fields[key]) {
146
+ acc[key] = true;
147
+ }
148
+ return acc;
149
+ }, {}),
145
150
  callbacks: moreFields,
146
151
  fieldsInReturnValue,
147
152
  parseResult,
@@ -30,6 +30,7 @@ import type { VoidBox } from './boxes/iso-base-media/void-box';
30
30
  import type { RiffBox } from './boxes/riff/riff-box';
31
31
  import type { TransportStreamBox } from './boxes/transport-stream/boxes';
32
32
  import type { MatroskaSegment } from './boxes/webm/segments';
33
+ import type { MetadataEntry } from './metadata/get-metadata';
33
34
  export interface RegularBox extends BaseBox {
34
35
  boxType: string;
35
36
  boxSize: number;
@@ -38,6 +39,15 @@ export interface RegularBox extends BaseBox {
38
39
  type: 'regular-box';
39
40
  }
40
41
  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;
42
+ type Mp3Id3Header = {
43
+ type: 'id3-header';
44
+ versionMajor: number;
45
+ versionMinor: number;
46
+ flags: number;
47
+ size: number;
48
+ metatags: MetadataEntry[];
49
+ };
50
+ export type Mp3Box = Mp3Id3Header;
41
51
  export type AnySegment = MatroskaSegment | IsoBaseMediaBox | RiffBox | TransportStreamBox;
42
52
  export type IsoBaseMediaStructure = {
43
53
  type: 'iso-base-media';
@@ -55,7 +65,11 @@ export type TransportStreamStructure = {
55
65
  type: 'transport-stream';
56
66
  boxes: TransportStreamBox[];
57
67
  };
58
- export type Structure = IsoBaseMediaStructure | RiffStructure | MatroskaStructure | TransportStreamStructure;
68
+ export type Mp3Structure = {
69
+ type: 'mp3';
70
+ boxes: Mp3Box[];
71
+ };
72
+ export type Structure = IsoBaseMediaStructure | RiffStructure | MatroskaStructure | TransportStreamStructure | Mp3Structure;
59
73
  export type ParseResult = {
60
74
  status: 'done';
61
75
  } | {
@@ -78,3 +92,4 @@ export type ExpectSegmentParseResult = {
78
92
  segment: MatroskaSegment | null;
79
93
  continueParsing: () => Promise<ExpectSegmentParseResult>;
80
94
  };
95
+ export {};