VorbisInfo.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.xnap.plugin.viewer.vorbisviewer |
![]() |
![]() |
XNap 3 |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
package org.xnap.plugin.viewer.vorbisviewer;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
/**
*
* A utility for reading information and comments from the header
* packets of an Ogg Vorbis stream.
*
* <tt>
* <p>
* Copyright (C) 2001 Matthew Elder
* </p>
* <p>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* </p>
* <p>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
* </p>
* <p>
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
* </p>
* </tt>
*
* @author Matthew M. Elder
* @version 0.1
*
*/
public class VorbisInfo {
// stuff from info header
private int channels;
private long rate=0;
private long bitrate_upper;
private long bitrate_nominal;
private long bitrate_lower;
// stuff from comment header
String vendor;
int comments=0;
Hashtable comment;
// for doing checksum
private long[] crc_lookup = new long[256];
private int crc_ready=0;
// for reading data
private byte[] header;
private byte[] packet;
/**
* Initializes Ogg Vorbis information based on the header from a stream.
* @param s an Ogg Vorbis data stream
*/
public VorbisInfo(InputStream s) throws IOException {
/* initialize the crc_lookup table */
for (int i=0;i<256;i++)
crc_lookup[i]=_ogg_crc_entry(i);
fetch_header_and_packet(s);
interpret_header_packet();
fetch_header_and_packet(s);
interpret_header_packet();
}
/**
* Returns the number of channels in the bitstream.
* @return the number of channels in the bitstream.
*/
public int getChannels() {
return channels;
}
/**
* Returns the rate of the stream in Hz.
* @return the rate of the stream in Hz.
*/
public long getRate() {
return rate;
}
/**
* Returns the <em>average</em> bitrate of the stream in kbps.
* @return the <em>average</em> bitrate of the stream in kbps.
*/
public long getBitrate() {
return bitrate_nominal;
}
/**
* Returns a <code>Vector</code> containing values from the comment header.
* Vorbis comments take the form:
* <blockquote><tt>FIELD=SOME STRING VALUE.</tt></blockquote>
* Since there is no requirement for FIELD to be unique there may
* be multiple values for one field.
* <code>getComments</code> returns a <code>Vector</code> of
* <code>String</code> for all of the strings associated with
* a field.
* <br>Note: <code>field</code> is case insensitive.<br>
*
* @param field a case insensitive string for a field name in an Ogg Vorbis
* comment header
*
* @return a Vector of strings containing field values from an Ogg Vorbis
comment header
*/
public Vector getComments(String field) {
return (Vector)comment.get(field.toLowerCase());
}
/**
* Returns a <code>Set</code> of comment field names in no particular order.
* @return a <code>Set</code> of comment field names in no particular order.
*/
public Set getFields() {
return comment.keySet();
}
private void fetch_header_and_packet(InputStream s) throws IOException {
// read in the minimal packet header
byte[] head = new byte[27];
int bytes = s.read(head);
//System.err.println("\nDEBUG: bytes = "+bytes);
if(bytes < 27)
throw new IOException("Not enough bytes in header");
if(!"OggS".equals(new String(head, 0, 4)))
throw new IOException("Not a valid Ogg Vorbis file");
int headerbytes = (touint(head[26])) + 27;
//System.err.println("DEBUG: headerbytes = "+headerbytes);
// get the rest of the header
byte[] head_rest = new byte[touint(head[26])]; // :-) that's a pun
bytes += s.read(head_rest);
//System.err.println("DEBUG: bytes = "+bytes);
header = new byte[headerbytes];
Arrays.fill(header,(byte)0);
// copy the whole header into header
System.arraycopy(head,0,header,0,27);
System.arraycopy(head_rest,0,header,27,headerbytes-27);
if(bytes<headerbytes) {
String error =
"Error reading vorbis file: " +
"Not enough bytes for header + seg table";
throw new IOException(error);
}
int bodybytes = 0;
for(int i=0; i<header[26]; i++)
bodybytes += touint(header[27+i]);
//System.err.println("DEBUG: bodybytes = "+bodybytes);
packet = new byte[bodybytes];
Arrays.fill(packet,(byte)0);
bytes += s.read(packet);
//System.err.println("DEBUG: bytes = "+bytes);
if(bytes<headerbytes+bodybytes) {
String error =
"Error reading vorbis file: " +
"Not enough bytes for header + body";
throw new IOException(error);
}
byte[] oldsum = new byte[4];
System.arraycopy(header,22,oldsum,0,4); // read existing checksum
Arrays.fill(header, 22, 22+4, (byte)0); // clear for calculation of checksum
byte[] newsum = checksum();
if(!(new String(oldsum)).equals(new String(newsum)) ) {
System.err.println("checksum failed");
System.err.println("old checksum: " +
oldsum[0]+"|"+oldsum[1]+"|"+oldsum[2]+"|"+oldsum[3]);
System.err.println("new checksum: " +
newsum[0]+"|"+newsum[1]+"|"+newsum[2]+"|"+newsum[3]);
}
}
private void interpret_header_packet() throws IOException {
byte packet_type = packet[0];
switch(packet_type) {
case 1:
//System.err.println("DEBUG: got header packet");
if(rate != 0)
throw new IOException("Invalid vorbis file: info already fetched");
fetch_info_info();
break;
case 3:
//System.err.println("DEBUG: got comment packet");
if(rate == 0)
throw new IOException("Invalid vorbis file: header not complete");
fetch_comment_info();
break;
case 5:
throw new IOException("Invalid vorbis file: header not complete");
default:
throw new IOException("Invalid vorbis file: bad packet header");
}
}
/**
* pull the fields from the info header
*/
private void fetch_info_info() throws IOException {
// keep track of location in packet
int dataptr=1; // should have already read packet[0] for packet type
String str = new String(packet,dataptr,6);
dataptr += 6;
if(!"vorbis".equals(str))
throw new IOException("Not a vorbis header");
dataptr += 4; // skip version (4 bytes)
channels = packet[dataptr++]; // 1 byte
rate = toulong(read32(packet, dataptr));
dataptr += 4; // just read 4 bytes
bitrate_upper = toulong(read32(packet, dataptr));
dataptr += 4; // just read 4 bytes
bitrate_nominal = toulong(read32(packet, dataptr));
dataptr += 4; // just read 4 bytes
bitrate_lower = toulong(read32(packet, dataptr));
dataptr += 4; // just read 4 bytes
dataptr++; // skip block sizes (4 bits each for a total of 1 byte)
byte eop = packet[dataptr++];
if(eop!=1) throw new IOException("End of packet expected but not found");
}
private void fetch_comment_info() throws IOException {
int dataptr=1;
String str = new String(packet,dataptr,6);
dataptr += 6;
if(!"vorbis".equals(str))
throw new IOException("Not a vorbis header");
comment = new Hashtable();
long len = toulong(read32(packet,dataptr));
//System.err.println("DEBUG: vendor string length = "+len);
dataptr += 4;
/*
* FIXME: Casting len to int here means possible loss of data.
* I don't know who would have a comment header big
* enough to cause this, but the spec says this is
* an unsigned 32 bit int.
* Damn java for not having unsigned ints (or it should
* at least allow the use of 64 bit longs more frequently)
* If I get a chance i'll write a wrapper for this(maybe).
*/
vendor = new String(packet,dataptr,(int)len);
dataptr += len;
// FIXME: similar problem to the vendor string length above
comments = (int)toulong(read32(packet,dataptr));
dataptr += 4;
for(int i=0; i<comments; i++) {
// read comment
len = toulong(read32(packet,dataptr));
dataptr += 4;
// FIXME: same problem as vendor string
String cmnt = new String(packet,dataptr,(int)len);
dataptr += len;
// parse and store
String name = cmnt.substring(0,cmnt.indexOf('='));
String value = cmnt.substring(cmnt.indexOf('=')+1);
if(comment.containsKey(name)) {
Vector tmp = (Vector)comment.get(name.toLowerCase());
tmp.add(value);
} else {
Vector tmp = new Vector();
tmp.add(value);
comment.put(name.toLowerCase(),tmp);
}
}
}
private int read32 (byte[] data, int ptr) {
int val = 0;
val = ( touint(data[ptr]) & 0x000000ff);
val |= ((touint(data[ptr+1]) << 8) & 0x0000ff00);
val |= ((touint(data[ptr+2]) << 16) & 0x00ff0000);
val |= ((touint(data[ptr+3]) << 24) & 0xff000000);
return val;
}
private byte[] checksum() {
long crc_reg=0;
for(int i=0;i<header.length;i++) {
int tmp = (int)(((crc_reg >>> 24)&0xff) ^ touint(header[i]));
crc_reg=(crc_reg<<8)^crc_lookup[tmp];
crc_reg &= 0xffffffff;
}
for(int i=0;i<packet.length;i++) {
int tmp = (int)(((crc_reg >>> 24)&0xff) ^ touint(packet[i]));
crc_reg=(crc_reg<<8)^crc_lookup[tmp];
crc_reg &= 0xffffffff;
}
byte[] sum = new byte[4];
sum[0]=(byte)(crc_reg & 0xffL);
sum[1]=(byte)((crc_reg>>>8) & 0xffL);
sum[2]=(byte)((crc_reg>>>16) & 0xffL);
sum[3]=(byte)((crc_reg>>>24) & 0xffL);
return sum;
}
private long _ogg_crc_entry(long index){
long r;
r = index << 24;
for (int i=0; i<8; i++) {
if ((r & 0x80000000L) != 0) {
r = (r << 1) ^ 0x04c11db7L;
} else {
r<<=1;
}
}
return (r & 0xffffffff);
}
private long toulong(int n) {
return (n & 0xffffffffL);
}
private int touint(byte n) {
return (n & 0xff);
}
/**
* Prints out the information from an Ogg Vorbis stream in a
* nice, humanly-readable format.
*/
public String toString() {
String str = "";
str += channels+" channels at "+rate+"Hz\n";
str += bitrate_nominal/1000+"kbps (average bitrate)\n";
Iterator fields = comment.keySet().iterator();
while(fields.hasNext()) {
String name = (String)fields.next();
Vector values = (Vector)comment.get(name);
Iterator vi = values.iterator();
str += name+"=";
boolean dumb=false;
while(vi.hasNext()) {
if(dumb) str += ", ";
str += vi.next();
dumb=true;
}
str+="\n";
}
return str;
}
//tester main
static void main(String[] args) throws Exception {
if(args.length!=1) {
System.err.println("usage:\tjava VorbisInfo <ogg vorbis file>");
System.exit(1);
}
BufferedInputStream b =
new BufferedInputStream(new FileInputStream(args[0]));
VorbisInfo vi = new VorbisInfo(b);
System.out.println("\n"+vi);
b.close();
}
}
The table below shows all metrics for VorbisInfo.java.




