diff options
Diffstat (limited to 'src/de/danoeh/antennapod/util/id3reader/ID3Reader.java')
-rw-r--r-- | src/de/danoeh/antennapod/util/id3reader/ID3Reader.java | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java new file mode 100644 index 000000000..8164170bf --- /dev/null +++ b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java @@ -0,0 +1,182 @@ +package de.danoeh.antennapod.util.id3reader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; + +import de.danoeh.antennapod.util.id3reader.model.FrameHeader; +import de.danoeh.antennapod.util.id3reader.model.TagHeader; + +/** + * Reads the ID3 Tag of a given file. In order to use this class, you should + * create a subclass of it and overwrite the onStart* - or onEnd* - methods. + */ +public class ID3Reader { + private static final int HEADER_LENGTH = 10; + private static final int ID3_LENGTH = 3; + private static final int FRAME_ID_LENGTH = 4; + + protected static final int ACTION_SKIP = 1; + protected static final int ACTION_DONT_SKIP = 2; + + protected int readerPosition; + + private static final char[] LITTLE_ENDIAN_BOM = { 0xff, 0xfe }; + private static final char[] BIG_ENDIAN_BOM = { 0xfe, 0xff }; + + public ID3Reader() { + } + + public final void readInputStream(InputStream input) throws IOException, + ID3ReaderException { + int rc; + readerPosition = 0; + char[] tagHeaderSource = readBytes(input, HEADER_LENGTH); + TagHeader tagHeader = createTagHeader(tagHeaderSource); + if (tagHeader == null) { + onNoTagHeaderFound(); + } else { + rc = onStartTagHeader(tagHeader); + if (rc == ACTION_SKIP) { + onEndTag(); + } else { + while (readerPosition < tagHeader.getSize()) { + FrameHeader frameHeader = createFrameHeader(readBytes( + input, HEADER_LENGTH)); + rc = onStartFrameHeader(frameHeader, input); + if (rc == ACTION_SKIP) { + skipBytes(input, frameHeader.getSize()); + } + } + onEndTag(); + } + } + } + + /** + * Read a certain number of bytes from the given input stream. This method + * changes the readerPosition-attribute. + */ + protected char[] readBytes(InputStream input, int number) + throws IOException, ID3ReaderException { + char[] header = new char[number]; + for (int i = 0; i < number; i++) { + int b = input.read(); + readerPosition++; + if (b != -1) { + header[i] = (char) b; + } else { + throw new ID3ReaderException("Unexpected end of stream"); + } + } + return header; + } + + /** + * Skip a certain number of bytes on the given input stream. This method + * changes the readerPosition-attribute. + */ + protected void skipBytes(InputStream input, int number) throws IOException { + int skipped = 0; + while (skipped < number) { + skipped += input.skip(number - skipped); + System.out.println("Skipped = " + skipped); + } + + readerPosition += number; + } + + private TagHeader createTagHeader(char[] source) throws ID3ReaderException { + boolean hasTag = (source[0] == 0x49) && (source[1] == 0x44) + && (source[2] == 0x33); + if (source.length != HEADER_LENGTH) { + throw new ID3ReaderException("Length of header must be " + + HEADER_LENGTH); + } + if (hasTag) { + String id = null; + id = new String(source, 0, ID3_LENGTH); + char version = (char) ((source[3] << 8) | source[4]); + byte flags = (byte) source[5]; + int size = (source[6] << 24) | (source[7] << 16) | (source[8] << 8) + | source[9]; + return new TagHeader(id, size, version, flags); + } else { + return null; + } + } + + private FrameHeader createFrameHeader(char[] source) + throws ID3ReaderException { + if (source.length != HEADER_LENGTH) { + throw new ID3ReaderException("Length of header must be " + + HEADER_LENGTH); + } + String id = null; + id = new String(source, 0, FRAME_ID_LENGTH); + int size = (((int) source[4]) << 24) | (((int) source[5]) << 16) + | (((int) source[6]) << 8) | source[7]; + char flags = (char) ((source[8] << 8) | source[9]); + return new FrameHeader(id, size, flags); + } + + protected String readString(InputStream input, int max) throws IOException, + ID3ReaderException { + char[] bom = readBytes(input, 2); + if (bom == LITTLE_ENDIAN_BOM || bom == BIG_ENDIAN_BOM) { + return readUnicodeString(input, bom, max); + } else { + PushbackInputStream pi = new PushbackInputStream(input, 2); + pi.unread(bom[1]); + pi.unread(bom[0]); + return readISOString(pi, max); + } + } + + private String readISOString(InputStream input, int max) + throws IOException, ID3ReaderException { + int bytesRead = 0; + StringBuilder builder = new StringBuilder(); + char c; + while (++bytesRead <= max && (c = (char) input.read()) > 0) { + builder.append(c); + } + return builder.toString(); + } + + private String readUnicodeString(InputStream input, char[] bom, int max) + throws IOException, ID3ReaderException { + StringBuffer builder = new StringBuffer(); + char c1 = (char) input.read(); + char c2 = (char) input.read(); + int bytesRead = 2; + while ((c1 > 0 && c2 > 0) && ++bytesRead <= max) { + + builder.append(c1); + c1 = c2; + c2 = (char) input.read(); + } + if (bom == LITTLE_ENDIAN_BOM) { + builder.reverse(); + } + return builder.toString(); + } + + public int onStartTagHeader(TagHeader header) { + return ACTION_SKIP; + } + + public int onStartFrameHeader(FrameHeader header, InputStream input) + throws IOException, ID3ReaderException { + return ACTION_SKIP; + } + + public void onEndTag() { + + } + + public void onNoTagHeaderFound() { + + } + +} |