package com.limegroup.gnutella.metadata.video.reader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import org.limewire.io.IOUtils;
import org.limewire.util.ByteUtils;
import com.limegroup.gnutella.metadata.MetaReader;
import com.limegroup.gnutella.metadata.video.VideoMetaData;
/**
* Reads MetaData from Ogg Media Formats.
*/
public class OGMMetaData implements MetaReader {
public static final String TITLE_TAG = "title";
public static final String COMMENT_TAG = "comment";
public static final String LICENSE_TAG = "license";
private static final String DATE_TAG = "date";
private static final String LANGUAGE_TAG = "language";
@Override
public VideoMetaData parse(File file) throws IOException {
InputStream is = null;
try {
is = new FileInputStream(file);
DataInputStream dis = new DataInputStream(is);
Set<String> set = readMetaData(dis);
VideoMetaData videoData = new VideoMetaData();
parseMetaData(videoData, set);
return videoData;
} finally {
IOUtils.close(is);
}
}
/**
* Reads the first pages of the Ogg container, extracts all Vorbis comments.
*
* @param dis a DataInputStream
* @return Set of String containing Vorbis comments
* @throws IOException
*/
private Set<String> readMetaData(DataInputStream dis) throws IOException {
Set<String> set = new HashSet<String>();
boolean shouldStop = false;
do {
int pageSize = readHeader(dis);
shouldStop = parseCommentBlock(pageSize, dis, set);
} while (!shouldStop);
return set;
}
/**
* Reads the header of an Ogg page.
*
* @param dis the DataInputStream to read from
* @return size of the rest of the page.
* @throws IOException
*/
private int readHeader(DataInputStream dis) throws IOException {
// read pageHeader
if (dis.readByte() != 'O')
throw new IOException("not an ogg file");
if (dis.readByte() != 'g')
throw new IOException("not an ogg file");
if (dis.readByte() != 'g')
throw new IOException("not an ogg file");
if (dis.readByte() != 'S')
throw new IOException("not an ogg file");
// boring data
IOUtils.ensureSkip(dis, 22);
// number of page segments
int segments = dis.readUnsignedByte();
int size = 0;
for (int i = 0; i < segments; i++) {
size += dis.readUnsignedByte();
}
return size;
}
/*
* parse what we hope is a comment block. If that's not the case, we mostly
* skip the data.
*/
private boolean parseCommentBlock(int pageSize, DataInputStream dis, Set<String> comments)
throws IOException {
int type = dis.readByte();
pageSize--;
if ((type & 1) != 1) {
// we are reading a data block, stop.
IOUtils.ensureSkip(dis, pageSize);
return true;
} else if (type != 3) {
IOUtils.ensureSkip(dis, pageSize);
// reading some header block
return false;
}
byte[] vorbis = new byte[6];
dis.readFully(vorbis);
pageSize -= 6;
if (vorbis[0] != 'v' || vorbis[1] != 'o' || vorbis[2] != 'r' || vorbis[3] != 'b'
|| vorbis[4] != 'i' || vorbis[5] != 's') {
// not a vorbis comment
IOUtils.ensureSkip(dis, pageSize);
return true;
}
// read size of vendor string
byte[] dword = new byte[4];
dis.readFully(dword);
int vendorStringSize = ByteUtils.leb2int(dword, 0);
// read vendor string
byte[] vendorString = new byte[vendorStringSize];
dis.readFully(vendorString);
// read number of comments
dis.readFully(dword);
int numComments = ByteUtils.leb2int(dword, 0);
// read comments
for (int i = 0; i < numComments; i++) {
dis.readFully(dword);
int commentSize = ByteUtils.leb2int(dword, 0);
byte[] comment = new byte[commentSize];
dis.readFully(comment);
comments.add(new String(comment, "UTF-8"));
}
// last bit marker missing -> error
if ((dis.readByte() & 1) != 1)
return true;
return false;
}
/**
* Extracts usable information from a Set of Vorbis comments.
*
* @param data a Set of String containing Vorbis comments
*/
private void parseMetaData(VideoMetaData videoData, Set<String> data) {
for (String comment : data) {
int index = comment.indexOf('=');
if (index <= 0)
continue;
String key = comment.substring(0, index);
String value = comment.substring(index + 1);
if (key.equalsIgnoreCase(COMMENT_TAG)) {
if (videoData.getComment() != null)
videoData.setComment(videoData.getComment() + "\n" + value);
else
videoData.setComment(value);
} else if (key.equalsIgnoreCase(LANGUAGE_TAG)) {
if (videoData.getLanguage() != null)
videoData.setLanguage(videoData.getLanguage() + ";" + value);
else
videoData.setLanguage(value);
} else if (key.equalsIgnoreCase(LICENSE_TAG)) {
videoData.setLicense(value);
} else if (key.equalsIgnoreCase(TITLE_TAG)) {
videoData.setTitle(value);
} else if (key.equalsIgnoreCase(DATE_TAG)) {
videoData.setYear(value);
}
}
}
@Override
public String[] getSupportedExtensions() {
return new String[] { "ogm" };
}
}