@remotion/media-parser 4.0.288 → 4.0.289

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 (123) hide show
  1. package/dist/containers/aac/get-seeking-byte.d.ts +6 -0
  2. package/dist/containers/aac/get-seeking-byte.js +30 -0
  3. package/dist/containers/aac/parse-aac.js +23 -18
  4. package/dist/containers/aac/seeking-hints.d.ts +13 -0
  5. package/dist/containers/aac/seeking-hints.js +14 -0
  6. package/dist/containers/flac/get-channel-count.d.ts +1 -1
  7. package/dist/containers/flac/get-seeking-byte.d.ts +1 -2
  8. package/dist/containers/flac/get-seeking-byte.js +6 -2
  9. package/dist/containers/flac/parse-flac-frame.js +18 -17
  10. package/dist/containers/flac/parse-flac.js +5 -25
  11. package/dist/containers/flac/seeking-hints.d.ts +4 -1
  12. package/dist/containers/flac/seeking-hints.js +2 -1
  13. package/dist/containers/iso-base-media/get-children.d.ts +2 -1
  14. package/dist/containers/iso-base-media/get-children.js +2 -1
  15. package/dist/containers/iso-base-media/get-mfra-seeking-box.js +1 -0
  16. package/dist/containers/iso-base-media/get-moov-atom.js +2 -1
  17. package/dist/containers/iso-base-media/get-video-codec-from-iso-track.d.ts +1 -1
  18. package/dist/containers/iso-base-media/mdat/mdat.js +26 -33
  19. package/dist/containers/iso-base-media/moov/moov.d.ts +2 -1
  20. package/dist/containers/iso-base-media/moov/moov.js +2 -1
  21. package/dist/containers/iso-base-media/parse-boxes.js +1 -0
  22. package/dist/containers/iso-base-media/process-box.d.ts +2 -1
  23. package/dist/containers/iso-base-media/process-box.js +10 -4
  24. package/dist/containers/iso-base-media/stsd/mebx.d.ts +2 -1
  25. package/dist/containers/iso-base-media/stsd/mebx.js +2 -1
  26. package/dist/containers/iso-base-media/stsd/samples.d.ts +4 -2
  27. package/dist/containers/iso-base-media/stsd/samples.js +7 -2
  28. package/dist/containers/iso-base-media/stsd/stsd.d.ts +2 -1
  29. package/dist/containers/iso-base-media/stsd/stsd.js +2 -1
  30. package/dist/containers/iso-base-media/trak/trak.d.ts +2 -1
  31. package/dist/containers/iso-base-media/trak/trak.js +2 -1
  32. package/dist/containers/mp3/audio-sample-from-cbr.d.ts +11 -0
  33. package/dist/containers/mp3/audio-sample-from-cbr.js +35 -0
  34. package/dist/containers/mp3/get-duration.js +33 -6
  35. package/dist/containers/mp3/get-seeking-byte.d.ts +6 -0
  36. package/dist/containers/mp3/get-seeking-byte.js +49 -0
  37. package/dist/containers/mp3/parse-mp3.js +9 -0
  38. package/dist/containers/mp3/parse-mpeg-header.js +74 -263
  39. package/dist/containers/mp3/parse-packet-header.d.ts +30 -0
  40. package/dist/containers/mp3/parse-packet-header.js +258 -0
  41. package/dist/containers/mp3/parse-xing.d.ts +19 -0
  42. package/dist/containers/mp3/parse-xing.js +120 -0
  43. package/dist/containers/mp3/seek/audio-sample-from-cbr.d.ts +16 -0
  44. package/dist/containers/mp3/seek/audio-sample-from-cbr.js +35 -0
  45. package/dist/containers/mp3/seek/audio-sample-from-vbr.d.ts +8 -0
  46. package/dist/containers/mp3/seek/audio-sample-from-vbr.js +47 -0
  47. package/dist/containers/mp3/seek/get-approximate-byte-from-bitrate.d.ts +9 -0
  48. package/dist/containers/mp3/seek/get-approximate-byte-from-bitrate.js +28 -0
  49. package/dist/containers/mp3/seek/get-byte-from-observed-samples.d.ts +6 -0
  50. package/dist/containers/mp3/seek/get-byte-from-observed-samples.js +27 -0
  51. package/dist/containers/mp3/seek/get-seek-point-from-xing.d.ts +7 -0
  52. package/dist/containers/mp3/seek/get-seek-point-from-xing.js +29 -0
  53. package/dist/containers/mp3/seek/wait-until-syncword.d.ts +4 -0
  54. package/dist/containers/mp3/seek/wait-until-syncword.js +25 -0
  55. package/dist/containers/mp3/seeking-hints.d.ts +24 -0
  56. package/dist/containers/mp3/seeking-hints.js +21 -0
  57. package/dist/containers/riff/expect-riff-box.d.ts +6 -1
  58. package/dist/containers/riff/expect-riff-box.js +37 -27
  59. package/dist/containers/riff/get-seeking-byte.d.ts +8 -0
  60. package/dist/containers/riff/get-seeking-byte.js +56 -0
  61. package/dist/containers/riff/has-index.d.ts +2 -0
  62. package/dist/containers/riff/has-index.js +8 -0
  63. package/dist/containers/riff/parse-avih.js +3 -0
  64. package/dist/containers/riff/parse-idx1.d.ts +6 -0
  65. package/dist/containers/riff/parse-idx1.js +47 -0
  66. package/dist/containers/riff/parse-list-box.d.ts +4 -2
  67. package/dist/containers/riff/parse-list-box.js +8 -3
  68. package/dist/containers/riff/parse-movi.js +35 -40
  69. package/dist/containers/riff/parse-riff-body.js +5 -1
  70. package/dist/containers/riff/parse-riff-box.d.ts +4 -2
  71. package/dist/containers/riff/parse-riff-box.js +10 -3
  72. package/dist/containers/riff/riff-box.d.ts +14 -1
  73. package/dist/containers/riff/seek/fetch-idx1.d.ts +15 -0
  74. package/dist/containers/riff/seek/fetch-idx1.js +38 -0
  75. package/dist/containers/riff/seeking-hints.d.ts +23 -0
  76. package/dist/containers/riff/seeking-hints.js +36 -0
  77. package/dist/containers/transport-stream/handle-aac-packet.js +4 -8
  78. package/dist/containers/transport-stream/handle-avc-packet.js +4 -8
  79. package/dist/containers/wav/get-duration-from-wav.js +1 -10
  80. package/dist/containers/wav/parse-media-section.js +14 -18
  81. package/dist/containers/webm/parse-ebml.js +3 -16
  82. package/dist/containers/webm/seek/seeking-hints.js +1 -1
  83. package/dist/emit-available-info.js +8 -8
  84. package/dist/esm/index.mjs +1479 -383
  85. package/dist/esm/worker-server-entry.mjs +1475 -379
  86. package/dist/esm/worker-web-entry.mjs +1475 -379
  87. package/dist/find-last-keyframe.d.ts +5 -0
  88. package/dist/find-last-keyframe.js +18 -0
  89. package/dist/get-seeking-byte.d.ts +3 -1
  90. package/dist/get-seeking-byte.js +45 -7
  91. package/dist/get-seeking-hints.d.ts +12 -1
  92. package/dist/get-seeking-hints.js +40 -9
  93. package/dist/index.d.ts +56 -8
  94. package/dist/internal-parse-media.js +6 -0
  95. package/dist/parse-loop.js +15 -0
  96. package/dist/seeking-hints.d.ts +5 -1
  97. package/dist/set-seeking-hints.js +28 -8
  98. package/dist/state/aac-state.d.ts +6 -0
  99. package/dist/state/aac-state.js +7 -2
  100. package/dist/state/flac-state.d.ts +6 -0
  101. package/dist/state/flac-state.js +3 -0
  102. package/dist/state/keyframes.d.ts +1 -2
  103. package/dist/state/keyframes.js +2 -2
  104. package/dist/state/matroska/lazy-cues-fetch.js +13 -1
  105. package/dist/state/parser-state.d.ts +52 -6
  106. package/dist/state/parser-state.js +6 -6
  107. package/dist/state/riff/lazy-idx1-fetch.d.ts +30 -0
  108. package/dist/state/riff/lazy-idx1-fetch.js +63 -0
  109. package/dist/state/riff/riff-keyframes.d.ts +10 -0
  110. package/dist/state/riff/riff-keyframes.js +26 -0
  111. package/dist/state/riff/sample-counter.d.ts +12 -0
  112. package/dist/state/riff/sample-counter.js +52 -0
  113. package/dist/state/riff.d.ts +41 -1
  114. package/dist/state/riff.js +12 -1
  115. package/dist/state/sample-callbacks.d.ts +3 -4
  116. package/dist/state/sample-callbacks.js +3 -16
  117. package/dist/state/samples-observed/slow-duration-fps.d.ts +3 -1
  118. package/dist/state/samples-observed/slow-duration-fps.js +7 -0
  119. package/dist/version.d.ts +1 -1
  120. package/dist/version.js +1 -1
  121. package/dist/work-on-seek-request.d.ts +10 -0
  122. package/dist/work-on-seek-request.js +20 -2
  123. package/package.json +3 -3
@@ -63,7 +63,7 @@ const audioTags = [
63
63
  'mp4a',
64
64
  'ac-3',
65
65
  ];
66
- const processIsoFormatBox = async ({ iterator, logLevel, }) => {
66
+ const processIsoFormatBox = async ({ iterator, logLevel, contentLength, }) => {
67
67
  const fileOffset = iterator.counter.getOffset();
68
68
  const bytesRemaining = iterator.bytesRemaining();
69
69
  const boxSize = iterator.getUint32();
@@ -104,6 +104,7 @@ const processIsoFormatBox = async ({ iterator, logLevel, }) => {
104
104
  logLevel,
105
105
  size: boxSize - (iterator.counter.getOffset() - fileOffset),
106
106
  onlyIfMoovAtomExpected: null,
107
+ contentLength,
107
108
  });
108
109
  return {
109
110
  sample: {
@@ -143,6 +144,7 @@ const processIsoFormatBox = async ({ iterator, logLevel, }) => {
143
144
  logLevel,
144
145
  size: boxSize - (iterator.counter.getOffset() - fileOffset),
145
146
  onlyIfMoovAtomExpected: null,
147
+ contentLength,
146
148
  });
147
149
  return {
148
150
  sample: {
@@ -186,6 +188,7 @@ const processIsoFormatBox = async ({ iterator, logLevel, }) => {
186
188
  logLevel,
187
189
  size: boxSize - (iterator.counter.getOffset() - fileOffset),
188
190
  onlyIfMoovAtomExpected: null,
191
+ contentLength,
189
192
  });
190
193
  return {
191
194
  sample: {
@@ -234,6 +237,7 @@ const processIsoFormatBox = async ({ iterator, logLevel, }) => {
234
237
  iterator,
235
238
  logLevel,
236
239
  size: bytesRemainingInBox,
240
+ contentLength,
237
241
  })
238
242
  : (iterator.discard(bytesRemainingInBox), []);
239
243
  return {
@@ -264,7 +268,7 @@ const processIsoFormatBox = async ({ iterator, logLevel, }) => {
264
268
  throw new Error(`Unknown sample format ${boxFormat}`);
265
269
  };
266
270
  exports.processIsoFormatBox = processIsoFormatBox;
267
- const parseIsoFormatBoxes = async ({ maxBytes, logLevel, iterator, }) => {
271
+ const parseIsoFormatBoxes = async ({ maxBytes, logLevel, iterator, contentLength, }) => {
268
272
  const samples = [];
269
273
  const initialOffset = iterator.counter.getOffset();
270
274
  while (iterator.bytesRemaining() > 0 &&
@@ -272,6 +276,7 @@ const parseIsoFormatBoxes = async ({ maxBytes, logLevel, iterator, }) => {
272
276
  const { sample } = await (0, exports.processIsoFormatBox)({
273
277
  iterator,
274
278
  logLevel,
279
+ contentLength,
275
280
  });
276
281
  if (sample) {
277
282
  samples.push(sample);
@@ -7,9 +7,10 @@ export interface StsdBox extends BaseBox {
7
7
  numberOfEntries: number;
8
8
  samples: Sample[];
9
9
  }
10
- export declare const parseStsd: ({ offset, size, iterator, logLevel, }: {
10
+ export declare const parseStsd: ({ offset, size, iterator, logLevel, contentLength, }: {
11
11
  offset: number;
12
12
  size: number;
13
13
  iterator: BufferIterator;
14
14
  logLevel: LogLevel;
15
+ contentLength: number;
15
16
  }) => Promise<StsdBox>;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseStsd = void 0;
4
4
  const samples_1 = require("./samples");
5
- const parseStsd = async ({ offset, size, iterator, logLevel, }) => {
5
+ const parseStsd = async ({ offset, size, iterator, logLevel, contentLength, }) => {
6
6
  const version = iterator.getUint8();
7
7
  if (version !== 0) {
8
8
  throw new Error(`Unsupported STSD version ${version}`);
@@ -15,6 +15,7 @@ const parseStsd = async ({ offset, size, iterator, logLevel, }) => {
15
15
  maxBytes: bytesRemainingInBox,
16
16
  logLevel,
17
17
  iterator,
18
+ contentLength,
18
19
  });
19
20
  if (boxes.length !== numberOfEntries) {
20
21
  throw new Error(`Expected ${numberOfEntries} sample descriptions, got ${boxes.length}`);
@@ -6,9 +6,10 @@ export interface TrakBox extends BaseBox {
6
6
  type: 'trak-box';
7
7
  children: AnySegment[];
8
8
  }
9
- export declare const parseTrak: ({ size, offsetAtStart, iterator, logLevel, }: {
9
+ export declare const parseTrak: ({ size, offsetAtStart, iterator, logLevel, contentLength, }: {
10
10
  size: number;
11
11
  offsetAtStart: number;
12
12
  iterator: BufferIterator;
13
13
  logLevel: LogLevel;
14
+ contentLength: number;
14
15
  }) => Promise<TrakBox>;
@@ -2,12 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseTrak = void 0;
4
4
  const get_children_1 = require("../get-children");
5
- const parseTrak = async ({ size, offsetAtStart, iterator, logLevel, }) => {
5
+ const parseTrak = async ({ size, offsetAtStart, iterator, logLevel, contentLength, }) => {
6
6
  const children = await (0, get_children_1.getIsoBaseMediaChildren)({
7
7
  onlyIfMoovAtomExpected: null,
8
8
  size: size - 8,
9
9
  iterator,
10
10
  logLevel,
11
+ contentLength,
11
12
  });
12
13
  return {
13
14
  offset: offsetAtStart,
@@ -0,0 +1,11 @@
1
+ import type { ParserState } from '../../state/parser-state';
2
+ import type { AudioOrVideoSample } from '../../webcodec-sample-types';
3
+ export declare const getAudioSampleFromCbr: ({ bitrateInKbit, initialOffset, layer, sampleRate, samplesPerFrame, data, state, }: {
4
+ bitrateInKbit: number;
5
+ layer: number;
6
+ samplesPerFrame: number;
7
+ sampleRate: number;
8
+ initialOffset: number;
9
+ data: Uint8Array;
10
+ state: ParserState;
11
+ }) => AudioOrVideoSample;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAudioSampleFromCbr = void 0;
4
+ const get_frame_length_1 = require("./get-frame-length");
5
+ const getAudioSampleFromCbr = ({ bitrateInKbit, initialOffset, layer, sampleRate, samplesPerFrame, data, state, }) => {
6
+ const avgLength = (0, get_frame_length_1.getAverageMpegFrameLength)({
7
+ bitrateKbit: bitrateInKbit,
8
+ layer,
9
+ samplesPerFrame,
10
+ samplingFrequency: sampleRate,
11
+ });
12
+ const mp3Info = state.mp3.getMp3Info();
13
+ if (!mp3Info) {
14
+ throw new Error('No MP3 info');
15
+ }
16
+ const nthFrame = Math.round((initialOffset - state.mediaSection.getMediaSectionAssertOnlyOne().start) /
17
+ avgLength);
18
+ const durationInSeconds = samplesPerFrame / sampleRate;
19
+ const timeInSeconds = (nthFrame * samplesPerFrame) / sampleRate;
20
+ const timestamp = Math.round(timeInSeconds * 1000000);
21
+ const duration = Math.round(durationInSeconds * 1000000);
22
+ const audioSample = {
23
+ data,
24
+ cts: timestamp,
25
+ dts: timestamp,
26
+ duration,
27
+ offset: initialOffset,
28
+ timescale: 1000000,
29
+ timestamp,
30
+ trackId: 0,
31
+ type: 'key',
32
+ };
33
+ return audioSample;
34
+ };
35
+ exports.getAudioSampleFromCbr = getAudioSampleFromCbr;
@@ -1,26 +1,53 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDurationFromMp3 = void 0;
3
+ exports.getDurationFromMp3 = exports.getDurationFromMp3Xing = void 0;
4
4
  const get_frame_length_1 = require("./get-frame-length");
5
5
  const samples_per_mpeg_file_1 = require("./samples-per-mpeg-file");
6
+ const getDurationFromMp3Xing = ({ xingData, samplesPerFrame, }) => {
7
+ const xingFrames = xingData.numberOfFrames;
8
+ if (!xingFrames) {
9
+ throw new Error('Cannot get duration of VBR MP3 file - no frames');
10
+ }
11
+ const { sampleRate } = xingData;
12
+ if (!sampleRate) {
13
+ throw new Error('Cannot get duration of VBR MP3 file - no sample rate');
14
+ }
15
+ const xingSamples = xingFrames * samplesPerFrame;
16
+ return xingSamples / sampleRate;
17
+ };
18
+ exports.getDurationFromMp3Xing = getDurationFromMp3Xing;
6
19
  const getDurationFromMp3 = (state) => {
7
- const mp3Info = state.mp3Info.getMp3Info();
8
- const mp3CbrInfo = state.mp3Info.getCbrMp3Info();
9
- if (!mp3Info || !mp3CbrInfo) {
20
+ const mp3Info = state.mp3.getMp3Info();
21
+ const mp3BitrateInfo = state.mp3.getMp3BitrateInfo();
22
+ if (!mp3Info || !mp3BitrateInfo) {
10
23
  return null;
11
24
  }
12
25
  const samplesPerFrame = (0, samples_per_mpeg_file_1.getSamplesPerMpegFrame)({
13
26
  layer: mp3Info.layer,
14
27
  mpegVersion: mp3Info.mpegVersion,
15
28
  });
29
+ if (mp3BitrateInfo.type === 'variable') {
30
+ return (0, exports.getDurationFromMp3Xing)({
31
+ xingData: mp3BitrateInfo.xingData,
32
+ samplesPerFrame,
33
+ });
34
+ }
35
+ /**
36
+ * sonnet: The variation between 1044 and 1045 bytes in MP3 frames occurs due to the bit reservoir mechanism in MP3 encoding. Here's the typical distribution:
37
+ * • 1044 bytes (99% of frames)
38
+ * • 1045 bytes (1% of frames)
39
+ */
40
+ // we ignore that fact for now
16
41
  const frameLengthInBytes = (0, get_frame_length_1.getMpegFrameLength)({
17
- bitrateKbit: mp3CbrInfo.bitrateKbit,
42
+ bitrateKbit: mp3BitrateInfo.bitrateInKbit,
18
43
  padding: false,
19
44
  samplesPerFrame,
20
45
  samplingFrequency: mp3Info.sampleRate,
21
46
  layer: mp3Info.layer,
22
47
  });
23
- const frames = Math.floor((state.contentLength - mp3Info.startOfMpegStream) / frameLengthInBytes);
48
+ const frames = Math.floor((state.contentLength -
49
+ state.mediaSection.getMediaSectionAssertOnlyOne().start) /
50
+ frameLengthInBytes);
24
51
  const samples = frames * samplesPerFrame;
25
52
  const durationInSeconds = samples / mp3Info.sampleRate;
26
53
  return durationInSeconds;
@@ -0,0 +1,6 @@
1
+ import type { SeekResolution } from '../../work-on-seek-request';
2
+ import type { Mp3SeekingHints } from './seeking-hints';
3
+ export declare const getSeekingByteForMp3: ({ time, info, }: {
4
+ time: number;
5
+ info: Mp3SeekingHints;
6
+ }) => SeekResolution;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSeekingByteForMp3 = void 0;
4
+ const get_approximate_byte_from_bitrate_1 = require("./seek/get-approximate-byte-from-bitrate");
5
+ const get_byte_from_observed_samples_1 = require("./seek/get-byte-from-observed-samples");
6
+ const get_seek_point_from_xing_1 = require("./seek/get-seek-point-from-xing");
7
+ const getSeekingByteForMp3 = ({ time, info, }) => {
8
+ var _a;
9
+ if (info.mp3BitrateInfo === null ||
10
+ info.mp3Info === null ||
11
+ info.mediaSection === null) {
12
+ return {
13
+ type: 'valid-but-must-wait',
14
+ };
15
+ }
16
+ const approximateByte = (0, get_approximate_byte_from_bitrate_1.getApproximateByteFromBitrate)({
17
+ mp3BitrateInfo: info.mp3BitrateInfo,
18
+ timeInSeconds: time,
19
+ mp3Info: info.mp3Info,
20
+ mediaSection: info.mediaSection,
21
+ contentLength: info.contentLength,
22
+ });
23
+ const bestAudioSample = (0, get_byte_from_observed_samples_1.getByteFromObservedSamples)({
24
+ info,
25
+ timeInSeconds: time,
26
+ });
27
+ const xingSeekPoint = info.mp3BitrateInfo.type === 'variable'
28
+ ? (0, get_seek_point_from_xing_1.getSeekPointFromXing)({
29
+ mp3Info: info.mp3Info,
30
+ timeInSeconds: time,
31
+ xingData: info.mp3BitrateInfo.xingData,
32
+ })
33
+ : null;
34
+ const candidates = [
35
+ approximateByte,
36
+ (_a = bestAudioSample === null || bestAudioSample === void 0 ? void 0 : bestAudioSample.offset) !== null && _a !== void 0 ? _a : null,
37
+ xingSeekPoint,
38
+ ].filter((b) => b !== null);
39
+ if (candidates.length === 0) {
40
+ return {
41
+ type: 'valid-but-must-wait',
42
+ };
43
+ }
44
+ return {
45
+ type: 'do-seek',
46
+ byte: Math.max(...candidates),
47
+ };
48
+ };
49
+ exports.getSeekingByteForMp3 = getSeekingByteForMp3;
@@ -4,11 +4,20 @@ exports.parseMp3 = void 0;
4
4
  const id3_1 = require("./id3");
5
5
  const id3_v1_1 = require("./id3-v1");
6
6
  const parse_mpeg_header_1 = require("./parse-mpeg-header");
7
+ const wait_until_syncword_1 = require("./seek/wait-until-syncword");
7
8
  const parseMp3 = async (state) => {
8
9
  const { iterator } = state;
9
10
  if (iterator.bytesRemaining() < 3) {
10
11
  return null;
11
12
  }
13
+ // When coming from a seek, we need to discard until the syncword
14
+ if (state.mediaSection.isCurrentByteInMediaSection(iterator) === 'in-section') {
15
+ (0, wait_until_syncword_1.discardUntilSyncword)({ iterator });
16
+ await (0, parse_mpeg_header_1.parseMpegHeader)({
17
+ state,
18
+ });
19
+ return null;
20
+ }
12
21
  const { returnToCheckpoint } = iterator.startCheckpoint();
13
22
  const bytes = iterator.getSlice(3);
14
23
  returnToCheckpoint();
@@ -2,299 +2,110 @@
2
2
  // spec: http://www.mp3-tech.org/programmer/frame_header.html
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.parseMpegHeader = void 0;
5
- const emit_audio_sample_1 = require("../../emit-audio-sample");
6
5
  const log_1 = require("../../log");
7
6
  const register_track_1 = require("../../register-track");
8
- const get_frame_length_1 = require("./get-frame-length");
9
- const samples_per_mpeg_file_1 = require("./samples-per-mpeg-file");
10
- function getSamplingFrequency({ bits, mpegVersion, }) {
11
- const samplingTable = {
12
- 0b00: { MPEG1: 44100, MPEG2: 22050 },
13
- 0b01: { MPEG1: 48000, MPEG2: 24000 },
14
- 0b10: { MPEG1: 32000, MPEG2: 16000 },
15
- 0b11: { MPEG1: 'reserved', MPEG2: 'reserved' },
16
- };
17
- const key = `MPEG${mpegVersion}`;
18
- const value = samplingTable[bits][key];
19
- if (value === 'reserved') {
20
- throw new Error('Reserved sampling frequency');
21
- }
22
- if (!value) {
23
- throw new Error('Invalid sampling frequency for MPEG version: ' +
24
- JSON.stringify({ bits, version: mpegVersion }));
25
- }
26
- return value;
27
- }
28
- function getBitrateKB({ bits, mpegVersion, level, }) {
29
- const bitrateTable = {
30
- 0b0000: {
31
- 'V1,L1': 'free',
32
- 'V1,L2': 'free',
33
- 'V1,L3': 'free',
34
- 'V2,L1': 'free',
35
- 'V2,L2&L3': 'free',
36
- },
37
- 0b0001: { 'V1,L1': 32, 'V1,L2': 32, 'V1,L3': 32, 'V2,L1': 32, 'V2,L2&L3': 8 },
38
- 0b0010: {
39
- 'V1,L1': 64,
40
- 'V1,L2': 48,
41
- 'V1,L3': 40,
42
- 'V2,L1': 48,
43
- 'V2,L2&L3': 16,
44
- },
45
- 0b0011: {
46
- 'V1,L1': 96,
47
- 'V1,L2': 56,
48
- 'V1,L3': 48,
49
- 'V2,L1': 56,
50
- 'V2,L2&L3': 24,
51
- },
52
- 0b0100: {
53
- 'V1,L1': 128,
54
- 'V1,L2': 64,
55
- 'V1,L3': 56,
56
- 'V2,L1': 64,
57
- 'V2,L2&L3': 32,
58
- },
59
- 0b0101: {
60
- 'V1,L1': 160,
61
- 'V1,L2': 80,
62
- 'V1,L3': 64,
63
- 'V2,L1': 80,
64
- 'V2,L2&L3': 40,
65
- },
66
- 0b0110: {
67
- 'V1,L1': 192,
68
- 'V1,L2': 96,
69
- 'V1,L3': 80,
70
- 'V2,L1': 96,
71
- 'V2,L2&L3': 48,
72
- },
73
- 0b0111: {
74
- 'V1,L1': 224,
75
- 'V1,L2': 112,
76
- 'V1,L3': 96,
77
- 'V2,L1': 112,
78
- 'V2,L2&L3': 56,
79
- },
80
- 0b1000: {
81
- 'V1,L1': 256,
82
- 'V1,L2': 128,
83
- 'V1,L3': 112,
84
- 'V2,L1': 128,
85
- 'V2,L2&L3': 64,
86
- },
87
- 0b1001: {
88
- 'V1,L1': 288,
89
- 'V1,L2': 160,
90
- 'V1,L3': 128,
91
- 'V2,L1': 144,
92
- 'V2,L2&L3': 80,
93
- },
94
- 0b1010: {
95
- 'V1,L1': 320,
96
- 'V1,L2': 192,
97
- 'V1,L3': 160,
98
- 'V2,L1': 160,
99
- 'V2,L2&L3': 96,
100
- },
101
- 0b1011: {
102
- 'V1,L1': 352,
103
- 'V1,L2': 224,
104
- 'V1,L3': 192,
105
- 'V2,L1': 176,
106
- 'V2,L2&L3': 112,
107
- },
108
- 0b1100: {
109
- 'V1,L1': 384,
110
- 'V1,L2': 256,
111
- 'V1,L3': 224,
112
- 'V2,L1': 192,
113
- 'V2,L2&L3': 128,
114
- },
115
- 0b1101: {
116
- 'V1,L1': 416,
117
- 'V1,L2': 320,
118
- 'V1,L3': 256,
119
- 'V2,L1': 224,
120
- 'V2,L2&L3': 144,
121
- },
122
- 0b1110: {
123
- 'V1,L1': 448,
124
- 'V1,L2': 384,
125
- 'V1,L3': 320,
126
- 'V2,L1': 256,
127
- 'V2,L2&L3': 160,
128
- },
129
- 0b1111: {
130
- 'V1,L1': 'bad',
131
- 'V1,L2': 'bad',
132
- 'V1,L3': 'bad',
133
- 'V2,L1': 'bad',
134
- 'V2,L2&L3': 'bad',
135
- },
136
- };
137
- // Determine the correct key based on version and level
138
- let key;
139
- if (mpegVersion === 2 && (level === 2 || level === 3)) {
140
- key = 'V2,L2&L3';
141
- }
142
- else {
143
- key = `V${mpegVersion},L${level}`;
144
- }
145
- // Return the corresponding bitrate
146
- return bitrateTable[bits][key];
147
- }
7
+ const parse_packet_header_1 = require("./parse-packet-header");
8
+ const parse_xing_1 = require("./parse-xing");
9
+ const audio_sample_from_cbr_1 = require("./seek/audio-sample-from-cbr");
10
+ const audio_sample_from_vbr_1 = require("./seek/audio-sample-from-vbr");
148
11
  const parseMpegHeader = async ({ state, }) => {
149
12
  const { iterator } = state;
150
13
  const initialOffset = iterator.counter.getOffset();
151
14
  if (iterator.bytesRemaining() < 32) {
152
15
  return;
153
16
  }
154
- iterator.startReadingBits();
155
- for (let i = 0; i < 11; i++) {
156
- const expectToBe1 = iterator.getBits(1);
157
- if (expectToBe1 !== 1) {
158
- throw new Error('Expected 1');
17
+ // parse header
18
+ const { frameLength, bitrateInKbit, layer, mpegVersion, numberOfChannels, sampleRate, samplesPerFrame, } = (0, parse_packet_header_1.parseMp3PacketHeader)(iterator);
19
+ const cbrMp3Info = state.mp3.getMp3BitrateInfo();
20
+ if (cbrMp3Info && cbrMp3Info.type === 'constant') {
21
+ if (bitrateInKbit !== cbrMp3Info.bitrateInKbit) {
22
+ throw new Error(`Bitrate mismatch at offset ${initialOffset}: ${bitrateInKbit} !== ${cbrMp3Info.bitrateInKbit}`);
159
23
  }
160
24
  }
161
- const audioVersionId = iterator.getBits(2);
162
- /**
163
- * 00 - MPEG Version 2.5 (later extension of MPEG 2)
164
- 01 - reserved
165
- 10 - MPEG Version 2 (ISO/IEC 13818-3)
166
- 11 - MPEG Version 1 (ISO/IEC 11172-3)
167
- */
168
- if (audioVersionId !== 0b11 && audioVersionId !== 0b10) {
169
- throw new Error('Expected MPEG Version 1 or 2');
170
- }
171
- const mpegVersion = audioVersionId === 0b11 ? 1 : 2;
172
- const layerBits = iterator.getBits(2);
173
- /**
174
- * 00 - reserved
175
- 01 - Layer III
176
- 10 - Layer II
177
- 11 - Layer I
178
- */
179
- if (layerBits === 0b00) {
180
- throw new Error('Expected Layer I, II or III');
181
- }
182
- const layer = layerBits === 0b11 ? 1 : layerBits === 0b10 ? 2 : 3;
183
- const protectionBit = iterator.getBits(1);
184
- if (protectionBit !== 0b1) {
185
- throw new Error('Does not support CRC yet');
186
- }
187
- const bitrateIndex = iterator.getBits(4);
188
- const bitrateKbit = getBitrateKB({
189
- bits: bitrateIndex,
190
- mpegVersion,
191
- level: audioVersionId,
192
- });
193
- if (bitrateKbit === 'bad') {
194
- throw new Error('Invalid bitrate');
195
- }
196
- if (bitrateKbit === 'free') {
197
- throw new Error('Free bitrate not supported');
198
- }
199
- const samplingFrequencyIndex = iterator.getBits(2);
200
- const sampleRate = getSamplingFrequency({
201
- bits: samplingFrequencyIndex,
202
- mpegVersion,
203
- });
204
- const padding = Boolean(iterator.getBits(1));
205
- iterator.getBits(1); // private bit
206
- const channelMode = iterator.getBits(2); // channel mode
207
- iterator.getBits(2); // mode extension
208
- iterator.getBits(1); // copyright
209
- iterator.getBits(1); // original
210
- iterator.getBits(2); // emphasis
211
- const numberOfChannels = channelMode === 0b11 ? 1 : 2;
212
- const samplesPerFrame = (0, samples_per_mpeg_file_1.getSamplesPerMpegFrame)({ mpegVersion, layer });
213
- const frameLength = (0, get_frame_length_1.getMpegFrameLength)({
214
- bitrateKbit,
215
- padding,
216
- samplesPerFrame,
217
- samplingFrequency: sampleRate,
218
- layer,
219
- });
220
- iterator.stopReadingBits();
221
25
  const offsetNow = iterator.counter.getOffset();
222
26
  iterator.counter.decrement(offsetNow - initialOffset);
223
27
  const data = iterator.getSlice(frameLength);
224
- let isInfoTag = false;
225
28
  if (state.callbacks.tracks.getTracks().length === 0) {
226
29
  const info = {
227
30
  layer,
228
31
  mpegVersion,
229
32
  sampleRate,
230
- startOfMpegStream: initialOffset,
231
33
  };
232
34
  const asText = new TextDecoder().decode(data);
233
- const isVbr = asText.includes('Xing') || asText.includes('VBRI');
234
- isInfoTag = isVbr || asText.includes('Info');
35
+ if (asText.includes('VBRI')) {
36
+ throw new Error('MP3 files with VBRI are currently unsupported because we have no sample file. Submit this file at remotion.dev/report if you would like us to support this file.');
37
+ }
38
+ if (asText.includes('Info')) {
39
+ return;
40
+ }
41
+ const isVbr = asText.includes('Xing');
235
42
  if (isVbr) {
43
+ const xingData = (0, parse_xing_1.parseXing)(data);
236
44
  log_1.Log.verbose(state.logLevel, 'MP3 has variable bit rate. Requiring whole file to be read');
237
- }
238
- else {
239
- state.mp3Info.setCbrMp3Info({
240
- bitrateKbit,
45
+ state.mp3.setMp3BitrateInfo({
46
+ type: 'variable',
47
+ xingData,
241
48
  });
49
+ return;
242
50
  }
243
- if (!isInfoTag) {
244
- state.mp3Info.setMp3Info(info);
245
- await (0, register_track_1.registerAudioTrack)({
246
- container: 'mp3',
247
- track: {
248
- type: 'audio',
249
- codec: 'mp3',
250
- codecPrivate: null,
251
- codecWithoutConfig: 'mp3',
252
- description: undefined,
253
- numberOfChannels,
254
- sampleRate,
255
- timescale: 1000000,
256
- trackId: 0,
257
- trakBox: null,
258
- },
259
- registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
260
- tracks: state.callbacks.tracks,
261
- logLevel: state.logLevel,
262
- onAudioTrack: state.onAudioTrack,
51
+ if (!state.mp3.getMp3BitrateInfo()) {
52
+ state.mp3.setMp3BitrateInfo({
53
+ bitrateInKbit,
54
+ type: 'constant',
263
55
  });
264
- state.callbacks.tracks.setIsDone(state.logLevel);
265
- }
266
- }
267
- const avgLength = (0, get_frame_length_1.getAverageMpegFrameLength)({
268
- bitrateKbit,
269
- layer,
270
- samplesPerFrame,
271
- samplingFrequency: sampleRate,
272
- });
273
- if (!isInfoTag) {
274
- const mp3Info = state.mp3Info.getMp3Info();
275
- if (!mp3Info) {
276
- throw new Error('No MP3 info');
277
56
  }
278
- const nthFrame = Math.round((initialOffset - mp3Info.startOfMpegStream) / avgLength);
279
- const durationInSeconds = samplesPerFrame / sampleRate;
280
- const timeInSeconds = (nthFrame * samplesPerFrame) / sampleRate;
281
- const timestamp = Math.round(timeInSeconds * 1000000);
282
- const duration = Math.round(durationInSeconds * 1000000);
283
- await (0, emit_audio_sample_1.emitAudioSample)({
284
- trackId: 0,
285
- audioSample: {
286
- data,
287
- cts: timestamp,
288
- dts: timestamp,
289
- duration,
290
- offset: initialOffset,
57
+ state.mp3.setMp3Info(info);
58
+ await (0, register_track_1.registerAudioTrack)({
59
+ container: 'mp3',
60
+ track: {
61
+ type: 'audio',
62
+ codec: 'mp3',
63
+ codecPrivate: null,
64
+ codecWithoutConfig: 'mp3',
65
+ description: undefined,
66
+ numberOfChannels,
67
+ sampleRate,
291
68
  timescale: 1000000,
292
- timestamp,
293
69
  trackId: 0,
294
- type: 'key',
70
+ trakBox: null,
295
71
  },
296
- callbacks: state.callbacks,
72
+ registerAudioSampleCallback: state.callbacks.registerAudioSampleCallback,
73
+ tracks: state.callbacks.tracks,
74
+ logLevel: state.logLevel,
75
+ onAudioTrack: state.onAudioTrack,
297
76
  });
77
+ state.callbacks.tracks.setIsDone(state.logLevel);
78
+ state.mediaSection.addMediaSection({
79
+ start: initialOffset,
80
+ size: state.contentLength - initialOffset,
81
+ });
82
+ }
83
+ const bitrateInfo = state.mp3.getMp3BitrateInfo();
84
+ if (!bitrateInfo) {
85
+ throw new Error('No bitrate info');
298
86
  }
87
+ const sample = bitrateInfo.type === 'constant'
88
+ ? (0, audio_sample_from_cbr_1.getAudioSampleFromCbr)({
89
+ bitrateInKbit,
90
+ data,
91
+ initialOffset,
92
+ layer,
93
+ sampleRate,
94
+ samplesPerFrame,
95
+ state,
96
+ })
97
+ : (0, audio_sample_from_vbr_1.getAudioSampleFromVbr)({
98
+ data,
99
+ info: bitrateInfo,
100
+ mp3Info: state.mp3.getMp3Info(),
101
+ position: initialOffset,
102
+ });
103
+ const { audioSample, timeInSeconds, durationInSeconds } = sample;
104
+ state.mp3.audioSamples.addSample({
105
+ timeInSeconds,
106
+ offset: initialOffset,
107
+ durationInSeconds,
108
+ });
109
+ await state.callbacks.onAudioSample(0, audioSample);
299
110
  };
300
111
  exports.parseMpegHeader = parseMpegHeader;