summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2012-11-06 15:38:59 +0100
committerdaniel oeh <daniel.oeh@gmail.com>2012-11-06 15:38:59 +0100
commite6292aec79c4ebc5b3facb71018b8ca709d75d72 (patch)
tree8d3e402e95daf920641c6f6600f51f0ad23d7b70 /src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java
parent09a3957f603a42c8cb4d0d45cca77ed6eab467d9 (diff)
downloadAntennaPod-e6292aec79c4ebc5b3facb71018b8ca709d75d72.zip
Added classes for reading vorbis comment chapters
Diffstat (limited to 'src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java')
-rw-r--r--src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java194
1 files changed, 194 insertions, 0 deletions
diff --git a/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java
new file mode 100644
index 000000000..2ffe3c05f
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/vorbiscommentreader/VorbisCommentReader.java
@@ -0,0 +1,194 @@
+package de.danoeh.antennapod.util.vorbiscommentreader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import org.apache.commons.io.EndianUtils;
+import org.apache.commons.io.IOUtils;
+
+
+public abstract class VorbisCommentReader {
+ /** Length of first page in an ogg file in bytes. */
+ private static final int FIRST_PAGE_LENGTH = 58;
+ private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024;
+ private static final int PACKET_TYPE_IDENTIFICATION = 1;
+ private static final int PACKET_TYPE_COMMENT = 3;
+
+ /** Called when Reader finds identification header. */
+ public abstract void onVorbisCommentFound();
+
+ public abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
+
+ /**
+ * Is called every time the Reader finds a content vector. The handler
+ * should return true if it wants to handle the content vector.
+ */
+ public abstract boolean onContentVectorKey(String content);
+
+ /**
+ * Is called if onContentVectorKey returned true for the key.
+ *
+ * @throws VorbisCommentReaderException
+ */
+ public abstract void onContentVectorValue(String key, String value)
+ throws VorbisCommentReaderException;
+
+ public abstract void onNoVorbisCommentFound();
+
+ public abstract void onEndOfComment();
+
+ public abstract void onError(VorbisCommentReaderException exception);
+
+ public void readInputStream(InputStream input)
+ throws VorbisCommentReaderException {
+ try {
+ // look for identification header
+ if (findIdentificationHeader(input)) {
+
+ onVorbisCommentFound();
+ input = new OggInputStream(input);
+ if (findCommentHeader(input)) {
+ VorbisCommentHeader commentHeader = readCommentHeader(input);
+ if (commentHeader != null) {
+ onVorbisCommentHeaderFound(commentHeader);
+ for (int i = 0; i < commentHeader
+ .getUserCommentLength(); i++) {
+ try {
+ long vectorLength = EndianUtils
+ .readSwappedUnsignedInteger(input);
+ String key = readContentVectorKey(input,
+ vectorLength).toLowerCase();
+ boolean readValue = onContentVectorKey(key);
+ if (readValue) {
+ String value = readUTF8String(
+ input,
+ (int) (vectorLength - key.length() - 1))
+ .toLowerCase();
+ onContentVectorValue(key, value);
+ } else {
+ IOUtils.skipFully(input,
+ vectorLength - key.length() - 1);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ onEndOfComment();
+ }
+
+ } else {
+ onError(new VorbisCommentReaderException(
+ "No comment header found"));
+ }
+ } else {
+ onNoVorbisCommentFound();
+ }
+ } catch (IOException e) {
+ onError(new VorbisCommentReaderException(e));
+ }
+ }
+
+ private String readUTF8String(InputStream input, long length)
+ throws IOException {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ char c = (char) input.read();
+ buffer.append(c);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Looks for an identification header in the first page of the file. If an
+ * identification header is found, it will be skipped completely and the
+ * method will return true, otherwise false.
+ *
+ * @throws IOException
+ */
+ private boolean findIdentificationHeader(InputStream input)
+ throws IOException {
+ byte[] buffer = new byte[FIRST_PAGE_LENGTH];
+ IOUtils.readFully(input, buffer);
+ int i;
+ for (i = 6; i < buffer.length; i++) {
+ if (buffer[i - 5] == 'v' && buffer[i - 4] == 'o'
+ && buffer[i - 3] == 'r' && buffer[i - 2] == 'b'
+ && buffer[i - 1] == 'i' && buffer[i] == 's'
+ && buffer[i - 6] == PACKET_TYPE_IDENTIFICATION) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean findCommentHeader(InputStream input) throws IOException {
+ char[] buffer = new char["vorbis".length() + 1];
+ for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
+ char c = (char) input.read();
+ int dest = -1;
+ switch (c) {
+ case PACKET_TYPE_COMMENT:
+ dest = 0;
+ break;
+ case 'v':
+ dest = 1;
+ break;
+ case 'o':
+ dest = 2;
+ break;
+ case 'r':
+ dest = 3;
+ break;
+ case 'b':
+ dest = 4;
+ break;
+ case 'i':
+ dest = 5;
+ break;
+ case 's':
+ dest = 6;
+ break;
+ }
+ if (dest >= 0) {
+ buffer[dest] = c;
+ if (buffer[1] == 'v' && buffer[2] == 'o' && buffer[3] == 'r'
+ && buffer[4] == 'b' && buffer[5] == 'i'
+ && buffer[6] == 's' && buffer[0] == PACKET_TYPE_COMMENT) {
+ return true;
+ }
+ } else {
+ Arrays.fill(buffer, (char) 0);
+ }
+ }
+ return false;
+ }
+
+ private VorbisCommentHeader readCommentHeader(InputStream input)
+ throws IOException, VorbisCommentReaderException {
+ try {
+ long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
+ String vendorName = readUTF8String(input, vendorLength);
+ long userCommentLength = EndianUtils
+ .readSwappedUnsignedInteger(input);
+ return new VorbisCommentHeader(vendorName, userCommentLength);
+ } catch (UnsupportedEncodingException e) {
+ throw new VorbisCommentReaderException(e);
+ }
+ }
+
+ private String readContentVectorKey(InputStream input, long vectorLength)
+ throws IOException {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < vectorLength; i++) {
+ char c = (char) input.read();
+ if (c == '=') {
+ return buffer.toString();
+ } else {
+ buffer.append(c);
+ }
+ }
+ return null; // no key found
+ }
+}