@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.
- package/dist/containers/aac/get-seeking-byte.d.ts +6 -0
- package/dist/containers/aac/get-seeking-byte.js +30 -0
- package/dist/containers/aac/parse-aac.js +23 -18
- package/dist/containers/aac/seeking-hints.d.ts +13 -0
- package/dist/containers/aac/seeking-hints.js +14 -0
- package/dist/containers/flac/get-channel-count.d.ts +1 -1
- package/dist/containers/flac/get-seeking-byte.d.ts +1 -2
- package/dist/containers/flac/get-seeking-byte.js +6 -2
- package/dist/containers/flac/parse-flac-frame.js +18 -17
- package/dist/containers/flac/parse-flac.js +5 -25
- package/dist/containers/flac/seeking-hints.d.ts +4 -1
- package/dist/containers/flac/seeking-hints.js +2 -1
- package/dist/containers/iso-base-media/get-children.d.ts +2 -1
- package/dist/containers/iso-base-media/get-children.js +2 -1
- package/dist/containers/iso-base-media/get-mfra-seeking-box.js +1 -0
- package/dist/containers/iso-base-media/get-moov-atom.js +2 -1
- package/dist/containers/iso-base-media/get-video-codec-from-iso-track.d.ts +1 -1
- package/dist/containers/iso-base-media/mdat/mdat.js +26 -33
- package/dist/containers/iso-base-media/moov/moov.d.ts +2 -1
- package/dist/containers/iso-base-media/moov/moov.js +2 -1
- package/dist/containers/iso-base-media/parse-boxes.js +1 -0
- package/dist/containers/iso-base-media/process-box.d.ts +2 -1
- package/dist/containers/iso-base-media/process-box.js +10 -4
- package/dist/containers/iso-base-media/stsd/mebx.d.ts +2 -1
- package/dist/containers/iso-base-media/stsd/mebx.js +2 -1
- package/dist/containers/iso-base-media/stsd/samples.d.ts +4 -2
- package/dist/containers/iso-base-media/stsd/samples.js +7 -2
- package/dist/containers/iso-base-media/stsd/stsd.d.ts +2 -1
- package/dist/containers/iso-base-media/stsd/stsd.js +2 -1
- package/dist/containers/iso-base-media/trak/trak.d.ts +2 -1
- package/dist/containers/iso-base-media/trak/trak.js +2 -1
- package/dist/containers/mp3/audio-sample-from-cbr.d.ts +11 -0
- package/dist/containers/mp3/audio-sample-from-cbr.js +35 -0
- package/dist/containers/mp3/get-duration.js +33 -6
- package/dist/containers/mp3/get-seeking-byte.d.ts +6 -0
- package/dist/containers/mp3/get-seeking-byte.js +49 -0
- package/dist/containers/mp3/parse-mp3.js +9 -0
- package/dist/containers/mp3/parse-mpeg-header.js +74 -263
- package/dist/containers/mp3/parse-packet-header.d.ts +30 -0
- package/dist/containers/mp3/parse-packet-header.js +258 -0
- package/dist/containers/mp3/parse-xing.d.ts +19 -0
- package/dist/containers/mp3/parse-xing.js +120 -0
- package/dist/containers/mp3/seek/audio-sample-from-cbr.d.ts +16 -0
- package/dist/containers/mp3/seek/audio-sample-from-cbr.js +35 -0
- package/dist/containers/mp3/seek/audio-sample-from-vbr.d.ts +8 -0
- package/dist/containers/mp3/seek/audio-sample-from-vbr.js +47 -0
- package/dist/containers/mp3/seek/get-approximate-byte-from-bitrate.d.ts +9 -0
- package/dist/containers/mp3/seek/get-approximate-byte-from-bitrate.js +28 -0
- package/dist/containers/mp3/seek/get-byte-from-observed-samples.d.ts +6 -0
- package/dist/containers/mp3/seek/get-byte-from-observed-samples.js +27 -0
- package/dist/containers/mp3/seek/get-seek-point-from-xing.d.ts +7 -0
- package/dist/containers/mp3/seek/get-seek-point-from-xing.js +29 -0
- package/dist/containers/mp3/seek/wait-until-syncword.d.ts +4 -0
- package/dist/containers/mp3/seek/wait-until-syncword.js +25 -0
- package/dist/containers/mp3/seeking-hints.d.ts +24 -0
- package/dist/containers/mp3/seeking-hints.js +21 -0
- package/dist/containers/riff/expect-riff-box.d.ts +6 -1
- package/dist/containers/riff/expect-riff-box.js +37 -27
- package/dist/containers/riff/get-seeking-byte.d.ts +8 -0
- package/dist/containers/riff/get-seeking-byte.js +56 -0
- package/dist/containers/riff/has-index.d.ts +2 -0
- package/dist/containers/riff/has-index.js +8 -0
- package/dist/containers/riff/parse-avih.js +3 -0
- package/dist/containers/riff/parse-idx1.d.ts +6 -0
- package/dist/containers/riff/parse-idx1.js +47 -0
- package/dist/containers/riff/parse-list-box.d.ts +4 -2
- package/dist/containers/riff/parse-list-box.js +8 -3
- package/dist/containers/riff/parse-movi.js +35 -40
- package/dist/containers/riff/parse-riff-body.js +5 -1
- package/dist/containers/riff/parse-riff-box.d.ts +4 -2
- package/dist/containers/riff/parse-riff-box.js +10 -3
- package/dist/containers/riff/riff-box.d.ts +14 -1
- package/dist/containers/riff/seek/fetch-idx1.d.ts +15 -0
- package/dist/containers/riff/seek/fetch-idx1.js +38 -0
- package/dist/containers/riff/seeking-hints.d.ts +23 -0
- package/dist/containers/riff/seeking-hints.js +36 -0
- package/dist/containers/transport-stream/handle-aac-packet.js +4 -8
- package/dist/containers/transport-stream/handle-avc-packet.js +4 -8
- package/dist/containers/wav/get-duration-from-wav.js +1 -10
- package/dist/containers/wav/parse-media-section.js +14 -18
- package/dist/containers/webm/parse-ebml.js +3 -16
- package/dist/containers/webm/seek/seeking-hints.js +1 -1
- package/dist/emit-available-info.js +8 -8
- package/dist/esm/index.mjs +1479 -383
- package/dist/esm/worker-server-entry.mjs +1475 -379
- package/dist/esm/worker-web-entry.mjs +1475 -379
- package/dist/find-last-keyframe.d.ts +5 -0
- package/dist/find-last-keyframe.js +18 -0
- package/dist/get-seeking-byte.d.ts +3 -1
- package/dist/get-seeking-byte.js +45 -7
- package/dist/get-seeking-hints.d.ts +12 -1
- package/dist/get-seeking-hints.js +40 -9
- package/dist/index.d.ts +56 -8
- package/dist/internal-parse-media.js +6 -0
- package/dist/parse-loop.js +15 -0
- package/dist/seeking-hints.d.ts +5 -1
- package/dist/set-seeking-hints.js +28 -8
- package/dist/state/aac-state.d.ts +6 -0
- package/dist/state/aac-state.js +7 -2
- package/dist/state/flac-state.d.ts +6 -0
- package/dist/state/flac-state.js +3 -0
- package/dist/state/keyframes.d.ts +1 -2
- package/dist/state/keyframes.js +2 -2
- package/dist/state/matroska/lazy-cues-fetch.js +13 -1
- package/dist/state/parser-state.d.ts +52 -6
- package/dist/state/parser-state.js +6 -6
- package/dist/state/riff/lazy-idx1-fetch.d.ts +30 -0
- package/dist/state/riff/lazy-idx1-fetch.js +63 -0
- package/dist/state/riff/riff-keyframes.d.ts +10 -0
- package/dist/state/riff/riff-keyframes.js +26 -0
- package/dist/state/riff/sample-counter.d.ts +12 -0
- package/dist/state/riff/sample-counter.js +52 -0
- package/dist/state/riff.d.ts +41 -1
- package/dist/state/riff.js +12 -1
- package/dist/state/sample-callbacks.d.ts +3 -4
- package/dist/state/sample-callbacks.js +3 -16
- package/dist/state/samples-observed/slow-duration-fps.d.ts +3 -1
- package/dist/state/samples-observed/slow-duration-fps.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/work-on-seek-request.d.ts +10 -0
- package/dist/work-on-seek-request.js +20 -2
- 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.
|
|
8
|
-
const
|
|
9
|
-
if (!mp3Info || !
|
|
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:
|
|
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 -
|
|
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,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
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
234
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
bitrateKbit,
|
|
45
|
+
state.mp3.setMp3BitrateInfo({
|
|
46
|
+
type: 'variable',
|
|
47
|
+
xingData,
|
|
241
48
|
});
|
|
49
|
+
return;
|
|
242
50
|
}
|
|
243
|
-
if (!
|
|
244
|
-
state.
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
70
|
+
trakBox: null,
|
|
295
71
|
},
|
|
296
|
-
|
|
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;
|