Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
([#3088](https://github.com/androidx/media/issues/3088)).
* MP3: Use gapless-aware durations from Xing/Info headers
([#3183](https://github.com/androidx/media/issues/3183)).
* MP3: Adjust LAME/Xing encoder delay and padding metadata to match
decoded PCM trimming.
* Ignore `av1C` data with unsupported version.
* MP4: Add support for big-endian floating point PCM in `fpcm` boxes.
* Matroska: Parse chapter info to `Chapter` entries in a track's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ public void testPlayback_twoIdenticalMp3Files() throws Exception {
int bytesPerFrame = audioTrackListener.getAudioTrackOutputFormat().getFrameSizeInBytes();
int paddingBytes = max(0, playerAudioFormat.encoderPadding) * bytesPerFrame;
int delayBytes = max(0, playerAudioFormat.encoderDelay) * bytesPerFrame;
assertThat(paddingBytes).isEqualTo(2808);
assertThat(delayBytes).isEqualTo(1152);
assertThat(paddingBytes).isEqualTo(1750);
assertThat(delayBytes).isEqualTo(2210);

byte[] decoderOutputBytes = Bytes.concat(mp3Decoder.getAllOutputBytes().toArray(new byte[0][]));
int bytesPerAudioFile = decoderOutputBytes.length / 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@
/** Representation of a LAME Xing or Info frame. */
/* package */ final class XingFrame {

/**
* Offset between LAME/Xing delay/padding fields and decoded PCM trim samples. FFmpeg's MP3 muxer
* subtracts this offset when writing LAME metadata, and its demuxer adds it back when exposing
* decoded PCM skip/discard samples.
*/
private static final int LAME_TO_DECODED_PCM_TRIM_OFFSET_SAMPLES = 528 + 1;

/** The header of the Xing or Info frame. */
public final MpegAudioUtil.Header header;

Expand Down Expand Up @@ -107,12 +114,13 @@ public static XingFrame parse(MpegAudioUtil.Header mpegAudioHeader, ParsableByte
@Nullable Mp3InfoReplayGain replayGain;
int encoderDelay;
int encoderPadding;
// Skip: version string (9), revision & VBR method (1), lowpass filter (1).
int bytesToSkipBeforeReplayGain = 9 + 1 + 1;
// Skip: revision & VBR method (1), lowpass filter (1).
int bytesToSkipBeforeReplayGain = 1 + 1;
// Skip: encoding flags & ATH type (1), bitrate (1).
int bytesToSkipAfterReplayGain = 1 + 1;
// And account for values we parse, ReplayGain (8) and encoder delay & padding (3).
if (frame.bytesLeft() >= bytesToSkipBeforeReplayGain + 8 + bytesToSkipAfterReplayGain + 3) {
// Account for version string (9), values we parse, ReplayGain (8) and encoder delay & padding (3).
if (frame.bytesLeft() >= 9 + bytesToSkipBeforeReplayGain + 8 + bytesToSkipAfterReplayGain + 3) {
String encoderIdentifier = frame.readString(9);
frame.skipBytes(bytesToSkipBeforeReplayGain);
float peak = frame.readFloat();
int field1 = frame.readUnsignedShort();
Expand All @@ -123,6 +131,10 @@ public static XingFrame parse(MpegAudioUtil.Header mpegAudioHeader, ParsableByte
int encoderDelayAndPadding = frame.readUnsignedInt24();
encoderDelay = (encoderDelayAndPadding & 0xFFF000) >> 12;
encoderPadding = (encoderDelayAndPadding & 0xFFF);
if (usesLameGaplessEncoding(encoderIdentifier)) {
encoderDelay += LAME_TO_DECODED_PCM_TRIM_OFFSET_SAMPLES;
encoderPadding = Math.max(0, encoderPadding - LAME_TO_DECODED_PCM_TRIM_OFFSET_SAMPLES);
}
} else {
replayGain = null;
encoderDelay = C.LENGTH_UNSET;
Expand Down Expand Up @@ -161,6 +173,12 @@ public long computeDurationUs() {
return Util.sampleCountToDurationUs(sampleCount - 1, header.sampleRate);
}

private static boolean usesLameGaplessEncoding(String encoderIdentifier) {
return encoderIdentifier.startsWith("LAME")
|| encoderIdentifier.startsWith("Lavf")
|| encoderIdentifier.startsWith("Lavc");
}

/** Provide the metadata derived from this Xing frame, such as ReplayGain data. */
@Nullable
public Metadata getMetadata() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,61 @@ public void computeDurationUs_withInvalidGaplessSampleCount_returnsTimeUnset() {
assertThat(frame.computeDurationUs()).isEqualTo(C.TIME_UNSET);
}

@Test
public void parse_withLameEncoderIdentifier_adjustsDelayAndPaddingForDecodedPcm() {
XingFrame frame =
createXingFrame(
INFO_FRAME_HEADER_DATA,
/* frameCount= */ 40,
/* dataSize= */ 8_541,
/* encoderDelay= */ 576,
/* encoderPadding= */ 1_404,
/* encoderIdentifier= */ "LAME3.99r");

assertThat(frame.encoderDelay).isEqualTo(1_105);
assertThat(frame.encoderPadding).isEqualTo(875);
}

@Test
public void parse_withLameEncoderIdentifierAndSmallPadding_clampsPaddingToZero() {
XingFrame frame =
createXingFrame(
INFO_FRAME_HEADER_DATA,
/* frameCount= */ 40,
/* dataSize= */ 8_541,
/* encoderDelay= */ 576,
/* encoderPadding= */ 398,
/* encoderIdentifier= */ "LAME3.99r");

assertThat(frame.encoderDelay).isEqualTo(1_105);
assertThat(frame.encoderPadding).isEqualTo(0);
}

private static XingFrame createXingFrame(
int headerData, int frameCount, int dataSize, int encoderDelay, int encoderPadding) {
return createXingFrame(
headerData,
frameCount,
dataSize,
encoderDelay,
encoderPadding,
/* encoderIdentifier= */ "");
}

private static XingFrame createXingFrame(
int headerData,
int frameCount,
int dataSize,
int encoderDelay,
int encoderPadding,
String encoderIdentifier) {
int encoderDelayAndPadding = (encoderDelay << 12) | encoderPadding;
ByteBuffer payload = ByteBuffer.allocate(4 + 4 + 4 + 11 + 8 + 2 + 3);
payload.putInt(0x03);
payload.putInt(frameCount);
payload.putInt(dataSize);
payload.position(payload.position() + 11 + 8 + 2);
payload.put(createFixedLengthEncoderIdentifier(encoderIdentifier));
payload.position(payload.position() + 2 + 8 + 2);
payload.put((byte) (encoderDelayAndPadding >> 16));
payload.put((byte) (encoderDelayAndPadding >> 8));
payload.put((byte) encoderDelayAndPadding);
Expand All @@ -93,4 +140,16 @@ private static XingFrame createXingFrame(int headerData, byte[] payload) {
xingFrameHeader.setForHeaderData(headerData);
return XingFrame.parse(xingFrameHeader, new ParsableByteArray(payload));
}

private static byte[] createFixedLengthEncoderIdentifier(String encoderIdentifier) {
byte[] fixedLengthIdentifier = new byte[9];
byte[] identifierBytes = Util.getUtf8Bytes(encoderIdentifier);
System.arraycopy(
identifierBytes,
/* srcPos= */ 0,
fixedLengthIdentifier,
/* destPos= */ 0,
Math.min(identifierBytes.length, fixedLengthIdentifier.length));
return fixedLengthIdentifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[9]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[9]]
sample 0:
time = 943000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[9]]
sample 0:
time = 1879000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[9]]
tracksEnded = true
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[9]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
sample 0:
time = 943000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
sample 0:
time = 1879000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
tracksEnded = true
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[Gorpcore]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[Gorpcore]]
sample 0:
time = 943000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[Gorpcore]]
sample 0:
time = 1879000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[Gorpcore]]
tracksEnded = true
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TIT2: description=null: values=[Test title], TPE1: description=null: values=[Test Artist], TALB: description=null: values=[Test Album], TXXX: description=Test description: values=[Test user info], COMM: language=eng, description=Test description, text=Test comment, WXXX: url=Test URL, TSSE: description=null: values=[Lavf58.29.100], MLLT, PRIV: owner=test@gmail.com, UNKN, GEOB: mimeType=test/mime, filename=Testfilename.txt, description=Test description, CHAP, CHAP, CTOC, APIC: mimeType=image/jpeg, description=Test description, TCON: description=null: values=[Gorpcore]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
sample 0:
time = 934996
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
sample 0:
time = 1862989
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
tracksEnded = true
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
sample 0:
time = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ track 0:
maxInputSize = 4096
channelCount = 2
sampleRate = 48000
encoderDelay = 576
encoderPadding = 576
encoderDelay = 1105
encoderPadding = 47
metadata = entries=[TSSE: description=null: values=[Lavf58.29.100]]
sample 0:
time = 0
Expand Down
Loading