Commit 588d5a6e by andrewlewis Committed by Oliver Woodman

Parse encoder delay and padding from ID3 metadata in MP3.

Based on AOSP's MP3Extractor.cpp and ID3.cpp.

Issue: #497
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=112685664
parent 25fb2a82
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer.util; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer.util;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
/** /**
...@@ -415,4 +416,11 @@ public class ParsableByteArrayTest extends TestCase { ...@@ -415,4 +416,11 @@ public class ParsableByteArrayTest extends TestCase {
assertNull(parser.readLine()); assertNull(parser.readLine());
} }
public void testReadString() {
byte[] bytes = new byte[] {'t', 'e', 's', 't'};
ParsableByteArray testArray = new ParsableByteArray(bytes);
assertEquals("test", testArray.readString(bytes.length, Charset.forName("UTF-8")));
assertEquals(bytes.length, testArray.getPosition());
}
} }
...@@ -49,7 +49,6 @@ public final class Mp3Extractor implements Extractor { ...@@ -49,7 +49,6 @@ public final class Mp3Extractor implements Extractor {
* Mask that includes the audio header values that must match between frames. * Mask that includes the audio header values that must match between frames.
*/ */
private static final int HEADER_MASK = 0xFFFE0C00; private static final int HEADER_MASK = 0xFFFE0C00;
private static final int ID3_TAG = Util.getIntegerCodeForString("ID3");
private static final int XING_HEADER = Util.getIntegerCodeForString("Xing"); private static final int XING_HEADER = Util.getIntegerCodeForString("Xing");
private static final int INFO_HEADER = Util.getIntegerCodeForString("Info"); private static final int INFO_HEADER = Util.getIntegerCodeForString("Info");
private static final int VBRI_HEADER = Util.getIntegerCodeForString("VBRI"); private static final int VBRI_HEADER = Util.getIntegerCodeForString("VBRI");
...@@ -57,6 +56,7 @@ public final class Mp3Extractor implements Extractor { ...@@ -57,6 +56,7 @@ public final class Mp3Extractor implements Extractor {
private final long forcedFirstSampleTimestampUs; private final long forcedFirstSampleTimestampUs;
private final ParsableByteArray scratch; private final ParsableByteArray scratch;
private final MpegAudioHeader synchronizedHeader; private final MpegAudioHeader synchronizedHeader;
private final Id3Util.Metadata metadata;
// Extractor outputs. // Extractor outputs.
private ExtractorOutput extractorOutput; private ExtractorOutput extractorOutput;
...@@ -86,6 +86,7 @@ public final class Mp3Extractor implements Extractor { ...@@ -86,6 +86,7 @@ public final class Mp3Extractor implements Extractor {
this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs; this.forcedFirstSampleTimestampUs = forcedFirstSampleTimestampUs;
scratch = new ParsableByteArray(4); scratch = new ParsableByteArray(4);
synchronizedHeader = new MpegAudioHeader(); synchronizedHeader = new MpegAudioHeader();
metadata = new Id3Util.Metadata();
basisTimeUs = -1; basisTimeUs = -1;
} }
...@@ -194,30 +195,10 @@ public final class Mp3Extractor implements Extractor { ...@@ -194,30 +195,10 @@ public final class Mp3Extractor implements Extractor {
private boolean synchronize(ExtractorInput input, boolean sniffing) private boolean synchronize(ExtractorInput input, boolean sniffing)
throws IOException, InterruptedException { throws IOException, InterruptedException {
input.resetPeekPosition(); input.resetPeekPosition();
int peekedId3Bytes = 0;
if (input.getPosition() == 0) {
// Skip any ID3 headers at the start of the file.
while (true) {
input.peekFully(scratch.data, 0, 3);
scratch.setPosition(0);
if (scratch.readUnsignedInt24() != ID3_TAG) {
break;
}
input.advancePeekPosition(2 + 1); // version, flags
input.peekFully(scratch.data, 0, 4);
scratch.setPosition(0);
int headerLength = scratch.readSynchSafeInt();
input.advancePeekPosition(headerLength);
peekedId3Bytes += 10 + headerLength;
}
input.resetPeekPosition();
input.advancePeekPosition(peekedId3Bytes);
}
// For sniffing, limit the search range to the length of an audio frame after any ID3 metadata.
int searched = 0; int searched = 0;
int validFrameCount = 0; int validFrameCount = 0;
int candidateSynchronizedHeaderData = 0; int candidateSynchronizedHeaderData = 0;
int peekedId3Bytes = input.getPosition() == 0 ? Id3Util.parseId3(input, metadata) : 0;
while (true) { while (true) {
if (sniffing && searched == MAX_SNIFF_BYTES) { if (sniffing && searched == MAX_SNIFF_BYTES) {
return false; return false;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer.util; package com.google.android.exoplayer.util;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset;
/** /**
* Wraps a byte array, providing a set of methods for parsing data from it. Numerical values are * Wraps a byte array, providing a set of methods for parsing data from it. Numerical values are
...@@ -358,4 +359,17 @@ public final class ParsableByteArray { ...@@ -358,4 +359,17 @@ public final class ParsableByteArray {
return line; return line;
} }
/**
* Reads the next {@code bytes} bytes as characters in the specified {@link Charset}.
*
* @param bytes The number of bytes to read.
* @param charset The character set of the encoded characters.
* @return The string encoded by the bytes in the specified character set.
*/
public String readString(int bytes, Charset charset) {
String result = new String(data, position, bytes, charset);
position += bytes;
return result;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment