Skip to content

Commit 73c8160

Browse files
authored
[TIKA-4791] Use LSF frame size for MPEG2/2.5 Layer III (#2954)
MpegStream computed every non-Layer-I frame with the MPEG1 values: 144 * bitrate / samplerate bytes and 1152 samples. MPEG2 and MPEG2.5 Layer III (the low sampling frequency mode of ISO/IEC 13818-3) carry a single granule of 576 samples per frame, so the coefficient is 72. Reference decoders (FFmpeg, mpg123, libmad) all halve these values for LSF streams. With the doubled length the frame walk skipped over the following frame, visiting at best every second frame (where the doubled skip landed mid-frame, the sync byte scan dropped further frames), and AudioFrame.getLength()/getDuration() were wrong by exactly 2x. The summed duration nearly cancelled for constant bitrate files but came out over 10 percent short on 22050 Hz variable bitrate files. Layer II keeps 1152 samples in every MPEG version and layer I was already correct, so the frame length and duration now branch on both version and layer. Regression tests cover all four combinations plus an end-to-end duration check on a real ffmpeg-encoded 22050 Hz MPEG2 file that walks 79 frames to a clean EOF.
1 parent da35998 commit 73c8160

4 files changed

Lines changed: 112 additions & 6 deletions

File tree

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp3/MpegStream.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,18 @@ class MpegStream extends PushbackInputStream {
9696
private static final int SAMPLE_COUNT_L1 = 384;
9797

9898
/**
99-
* Constant for the number of samples for a layer 2 or 3 frame.
99+
* Constant for the number of samples for a layer 2 frame (all MPEG
100+
* versions) and a layer 3 frame in MPEG1.
100101
*/
101102
private static final int SAMPLE_COUNT_L2 = 1152;
102103

104+
/**
105+
* Constant for the number of samples for a layer 3 frame in MPEG2 and
106+
* MPEG2.5: the low sampling frequency mode of ISO/IEC 13818-3 halves the
107+
* frame to a single granule of 576 samples.
108+
*/
109+
private static final int SAMPLE_COUNT_L3_LSF = 576;
110+
103111
/**
104112
* Constant for the size of an MPEG frame header in bytes.
105113
*/
@@ -172,15 +180,20 @@ private static int calculateSampleRate(int mpegVer, int code) {
172180
/**
173181
* Calculates the length of an MPEG frame based on the given parameters.
174182
*
183+
* @param mpegVer the MPEG version
175184
* @param layer the layer
176185
* @param bitRate the bit rate
177186
* @param sampleRate the sample rate
178187
* @param padding the padding flag
179188
* @return the length of the frame in bytes
180189
*/
181-
private static int calculateFrameLength(int layer, int bitRate, int sampleRate, int padding) {
190+
private static int calculateFrameLength(int mpegVer, int layer, int bitRate, int sampleRate,
191+
int padding) {
182192
if (layer == AudioFrame.LAYER_1) {
183193
return (12 * bitRate / sampleRate + padding) * 4;
194+
} else if (layer == AudioFrame.LAYER_3 && mpegVer != AudioFrame.MPEG_V1) {
195+
//MPEG2/2.5 layer 3 frames carry 576 samples instead of 1152
196+
return 72 * bitRate / sampleRate + padding;
184197
} else {
185198
return 144 * bitRate / sampleRate + padding;
186199
}
@@ -189,12 +202,20 @@ private static int calculateFrameLength(int layer, int bitRate, int sampleRate,
189202
/**
190203
* Calculates the duration of a MPEG frame based on the given parameters.
191204
*
205+
* @param mpegVer the MPEG version
192206
* @param layer the layer
193207
* @param sampleRate the sample rate
194208
* @return the duration of this frame in milliseconds
195209
*/
196-
private static float calculateDuration(int layer, int sampleRate) {
197-
int sampleCount = (layer == AudioFrame.LAYER_1) ? SAMPLE_COUNT_L1 : SAMPLE_COUNT_L2;
210+
private static float calculateDuration(int mpegVer, int layer, int sampleRate) {
211+
int sampleCount;
212+
if (layer == AudioFrame.LAYER_1) {
213+
sampleCount = SAMPLE_COUNT_L1;
214+
} else if (layer == AudioFrame.LAYER_3 && mpegVer != AudioFrame.MPEG_V1) {
215+
sampleCount = SAMPLE_COUNT_L3_LSF;
216+
} else {
217+
sampleCount = SAMPLE_COUNT_L2;
218+
}
198219
return (1000.0f / sampleRate) * sampleCount;
199220
}
200221

@@ -329,8 +350,8 @@ private AudioFrame createHeader(HeaderBitField bits) {
329350

330351
int bitRate = calculateBitRate(mpegVer, layer, bitRateCode);
331352
int sampleRate = calculateSampleRate(mpegVer, sampleRateCode);
332-
int length = calculateFrameLength(layer, bitRate, sampleRate, padding);
333-
float duration = calculateDuration(layer, sampleRate);
353+
int length = calculateFrameLength(mpegVer, layer, bitRate, sampleRate, padding);
354+
float duration = calculateDuration(mpegVer, layer, sampleRate);
334355
int channels = calculateChannels(bits.get(6, 7));
335356
return new AudioFrame(mpegVer, layer, bitRate, sampleRate, channels, length, duration);
336357
}

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/test/java/org/apache/tika/parser/mp3/Mp3ParserTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,4 +424,21 @@ private byte[] mp3WithFrame(String frameId, byte[] body) throws Exception {
424424
mp3.write(audio);
425425
return mp3.toByteArray();
426426
}
427+
428+
/**
429+
* MPEG2 layer 3 frames carry 576 samples instead of 1152, so the frame
430+
* walk must use the halved LSF frame length or it lands mid-frame and
431+
* the summed duration drifts (TIKA-4791). The fixture is a 22050 Hz CBR
432+
* file with 79 frames: 79 * 576 / 22050 = 2.06 seconds.
433+
*/
434+
@Test
435+
public void testMp3Mpeg2LowSamplingFrequency() throws Exception {
436+
Metadata metadata = new Metadata();
437+
getText("testMP3mpeg2.mp3", metadata);
438+
439+
assertEquals("audio/mpeg", metadata.get(Metadata.CONTENT_TYPE));
440+
assertEquals("MPEG 3 Layer III Version 2", metadata.get("version"));
441+
assertEquals("22050", metadata.get(XMPDM.AUDIO_SAMPLE_RATE));
442+
assertEquals(2.0637f, Float.parseFloat(metadata.get(XMPDM.DURATION)), 0.005f);
443+
}
427444
}

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/test/java/org/apache/tika/parser/mp3/MpegStreamTest.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,74 @@ public void testSeachNextFrameEOS() throws IOException {
140140
assertNull(stream.nextFrame(), "Got a frame");
141141
}
142142

143+
/**
144+
* Parses a single frame header from the given three header bytes
145+
* following the initial 0xFF sync byte.
146+
*/
147+
private AudioFrame parseHeader(int b2, int b3, int b4) throws IOException {
148+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
149+
writeFrame(bos, b2, b3, b4);
150+
TikaInputStream tis = TikaInputStream.get(bos.toByteArray());
151+
stream = new MpegStream(tis);
152+
AudioFrame header = stream.nextFrame();
153+
assertNotNull(header, "No header found");
154+
return header;
155+
}
156+
157+
/**
158+
* An MPEG1 layer 3 frame carries 1152 samples: 128 kbps at 44100 Hz
159+
* gives 144 * 128000 / 44100 = 417 bytes and 26.12 ms.
160+
*/
161+
@Test
162+
public void testFrameLengthAndDurationMpeg1Layer3() throws IOException {
163+
AudioFrame header = parseHeader(0xFB, 0x90, 0);
164+
assertEquals(AudioFrame.MPEG_V1, header.getVersionCode(), "Wrong MPEG version");
165+
assertEquals(AudioFrame.LAYER_3, header.getLayer(), "Wrong layer");
166+
assertEquals(417, header.getLength(), "Wrong frame length");
167+
assertEquals(1152000f / 44100, header.getDuration(), 0.01f, "Wrong duration");
168+
}
169+
170+
/**
171+
* An MPEG2 layer 3 frame carries only 576 samples (LSF mode), so
172+
* 80 kbps at 24000 Hz with padding gives 72 * 80000 / 24000 + 1 = 241
173+
* bytes and 24 ms, half of what the MPEG1 formula would report.
174+
*/
175+
@Test
176+
public void testFrameLengthAndDurationMpeg2Layer3() throws IOException {
177+
AudioFrame header = parseHeader(0xF3, 0x96, 0);
178+
assertEquals(AudioFrame.MPEG_V2, header.getVersionCode(), "Wrong MPEG version");
179+
assertEquals(AudioFrame.LAYER_3, header.getLayer(), "Wrong layer");
180+
assertEquals(241, header.getLength(), "Wrong frame length");
181+
assertEquals(24.0f, header.getDuration(), 0.01f, "Wrong duration");
182+
}
183+
184+
/**
185+
* MPEG2.5 layer 3 also uses the halved LSF frame: 80 kbps at 12000 Hz
186+
* with padding gives 72 * 80000 / 12000 + 1 = 481 bytes and 48 ms.
187+
*/
188+
@Test
189+
public void testFrameLengthAndDurationMpeg25Layer3() throws IOException {
190+
AudioFrame header = parseHeader(0xE3, 0x96, 0);
191+
assertEquals(AudioFrame.MPEG_V2_5, header.getVersionCode(), "Wrong MPEG version");
192+
assertEquals(AudioFrame.LAYER_3, header.getLayer(), "Wrong layer");
193+
assertEquals(481, header.getLength(), "Wrong frame length");
194+
assertEquals(48.0f, header.getDuration(), 0.01f, "Wrong duration");
195+
}
196+
197+
/**
198+
* Layer 2 keeps 1152 samples in every MPEG version, so the MPEG2 frame
199+
* stays at the full length: 80 kbps at 24000 Hz with padding gives
200+
* 144 * 80000 / 24000 + 1 = 481 bytes and 48 ms.
201+
*/
202+
@Test
203+
public void testFrameLengthAndDurationMpeg2Layer2() throws IOException {
204+
AudioFrame header = parseHeader(0xF5, 0x96, 0);
205+
assertEquals(AudioFrame.MPEG_V2, header.getVersionCode(), "Wrong MPEG version");
206+
assertEquals(AudioFrame.LAYER_2, header.getLayer(), "Wrong layer");
207+
assertEquals(481, header.getLength(), "Wrong frame length");
208+
assertEquals(48.0f, header.getDuration(), 0.01f, "Wrong duration");
209+
}
210+
143211
/**
144212
* Tries to skip a frame if no current header is available.
145213
*/

0 commit comments

Comments
 (0)