1 7 8 package org.jboss.media.format.audio.oggvorbis; 9 10 import java.io.BufferedInputStream ; 11 import java.io.FileInputStream ; 12 import java.io.IOException ; 13 import java.io.InputStream ; 14 import java.util.Arrays ; 15 import java.util.Hashtable ; 16 import java.util.Iterator ; 17 import java.util.Set ; 18 import java.util.Vector ; 19 20 30 class VorbisInfo 31 { 32 private int channels; 34 private long rate = 0; 35 private long bitrate_upper; 36 private long bitrate_nominal; 37 private long bitrate_lower; 38 39 String vendor; 41 int comments = 0; 42 Hashtable comment; 43 44 private long[] crc_lookup = new long[256]; 46 private int crc_ready = 0; 47 48 private byte[] header; 50 private byte[] packet; 51 52 56 public VorbisInfo(InputStream s) throws IOException 57 { 58 59 for (int i = 0; i < 256; i++) 60 crc_lookup[i] = _ogg_crc_entry(i); 61 62 fetch_header_and_packet(s); 63 interpret_header_packet(); 64 65 fetch_header_and_packet(s); 66 interpret_header_packet(); 67 } 68 69 73 public int getChannels() 74 { 75 return channels; 76 } 77 78 82 public long getRate() 83 { 84 return rate; 85 } 86 87 91 public long getBitrate() 92 { 93 return bitrate_nominal; 94 } 95 96 113 public Vector getComments(String field) 114 { 115 return (Vector ) comment.get(field.toLowerCase()); 116 } 117 118 122 public Set getFields() 123 { 124 return comment.keySet(); 125 } 126 127 private void fetch_header_and_packet(InputStream s) throws IOException 128 { 129 byte[] head = new byte[27]; 131 int bytes = s.read(head); 132 if (bytes < 27) 134 throw new IOException ("Not enough bytes in header"); 135 136 if (!"OggS".equals(new String (head, 0, 4))) 137 throw new IOException ("Not a valid Ogg Vorbis file"); 138 139 int headerbytes = (touint(head[26])) + 27; 140 142 byte[] head_rest = new byte[touint(head[26])]; bytes += s.read(head_rest); 145 header = new byte[headerbytes]; 147 Arrays.fill(header, (byte) 0); 148 149 System.arraycopy(head, 0, header, 0, 27); 151 System.arraycopy(head_rest, 0, header, 27, headerbytes - 27); 152 153 if (bytes < headerbytes) 154 { 155 String error = 156 "Error reading vorbis file: " 157 + "Not enough bytes for header + seg table"; 158 throw new IOException (error); 159 } 160 161 int bodybytes = 0; 162 for (int i = 0; i < header[26]; i++) 163 bodybytes += touint(header[27 + i]); 164 166 packet = new byte[bodybytes]; 167 Arrays.fill(packet, (byte) 0); 168 bytes += s.read(packet); 169 171 if (bytes < headerbytes + bodybytes) 172 { 173 String error = 174 "Error reading vorbis file: " 175 + "Not enough bytes for header + body"; 176 throw new IOException (error); 177 } 178 179 byte[] oldsum = new byte[4]; 180 System.arraycopy(header, 22, oldsum, 0, 4); Arrays.fill(header, 22, 22 + 4, (byte) 0); 182 184 byte[] newsum = checksum(); 185 if (!(new String (oldsum)).equals(new String (newsum))) 186 { 187 System.err.println("checksum failed"); 188 System.err.println( 189 "old checksum: " 190 + oldsum[0] 191 + "|" 192 + oldsum[1] 193 + "|" 194 + oldsum[2] 195 + "|" 196 + oldsum[3]); 197 System.err.println( 198 "new checksum: " 199 + newsum[0] 200 + "|" 201 + newsum[1] 202 + "|" 203 + newsum[2] 204 + "|" 205 + newsum[3]); 206 } 207 } 208 209 private void interpret_header_packet() throws IOException 210 { 211 byte packet_type = packet[0]; 212 switch (packet_type) 213 { 214 case 1 : 215 if (rate != 0) 217 throw new IOException ("Invalid vorbis file: info already fetched"); 218 fetch_info_info(); 219 break; 220 case 3 : 221 if (rate == 0) 223 throw new IOException ("Invalid vorbis file: header not complete"); 224 fetch_comment_info(); 225 break; 226 case 5 : 227 throw new IOException ("Invalid vorbis file: header not complete"); 228 default : 229 throw new IOException ("Invalid vorbis file: bad packet header"); 230 } 231 } 232 233 236 private void fetch_info_info() throws IOException 237 { 238 int dataptr = 1; 241 String str = new String (packet, dataptr, 6); 242 dataptr += 6; 243 if (!"vorbis".equals(str)) 244 throw new IOException ("Not a vorbis header"); 245 dataptr += 4; 247 channels = packet[dataptr++]; 249 rate = toulong(read32(packet, dataptr)); 250 dataptr += 4; 252 bitrate_upper = toulong(read32(packet, dataptr)); 253 dataptr += 4; 255 bitrate_nominal = toulong(read32(packet, dataptr)); 256 dataptr += 4; 258 bitrate_lower = toulong(read32(packet, dataptr)); 259 dataptr += 4; 261 dataptr++; 263 byte eop = packet[dataptr++]; 264 if (eop != 1) 265 throw new IOException ("End of packet expected but not found"); 266 } 267 268 private void fetch_comment_info() throws IOException 269 { 270 int dataptr = 1; 271 272 String str = new String (packet, dataptr, 6); 273 dataptr += 6; 274 if (!"vorbis".equals(str)) 275 throw new IOException ("Not a vorbis header"); 276 277 comment = new Hashtable (); 278 279 long len = toulong(read32(packet, dataptr)); 280 dataptr += 4; 282 283 292 vendor = new String (packet, dataptr, (int) len); 293 dataptr += len; 294 295 comments = (int) toulong(read32(packet, dataptr)); 297 dataptr += 4; 298 299 for (int i = 0; i < comments; i++) 300 { 301 302 len = toulong(read32(packet, dataptr)); 304 dataptr += 4; 305 String cmnt = new String (packet, dataptr, (int) len); 307 dataptr += len; 308 309 String name = cmnt.substring(0, cmnt.indexOf('=')); 311 String value = cmnt.substring(cmnt.indexOf('=') + 1); 312 if (comment.containsKey(name)) 313 { 314 Vector tmp = (Vector ) comment.get(name.toLowerCase()); 315 tmp.add(value); 316 } 317 else 318 { 319 Vector tmp = new Vector (); 320 tmp.add(value); 321 comment.put(name.toLowerCase(), tmp); 322 } 323 } 324 } 325 326 private int read32(byte[] data, int ptr) 327 { 328 int val = 0; 329 val = (touint(data[ptr]) & 0x000000ff); 330 val |= ((touint(data[ptr + 1]) << 8) & 0x0000ff00); 331 val |= ((touint(data[ptr + 2]) << 16) & 0x00ff0000); 332 val |= ((touint(data[ptr + 3]) << 24) & 0xff000000); 333 return val; 334 } 335 336 private byte[] checksum() 337 { 338 long crc_reg = 0; 339 340 for (int i = 0; i < header.length; i++) 341 { 342 int tmp = (int) (((crc_reg >>> 24) & 0xff) ^ touint(header[i])); 343 crc_reg = (crc_reg << 8) ^ crc_lookup[tmp]; 344 crc_reg &= 0xffffffff; 345 } 346 for (int i = 0; i < packet.length; i++) 347 { 348 int tmp = (int) (((crc_reg >>> 24) & 0xff) ^ touint(packet[i])); 349 crc_reg = (crc_reg << 8) ^ crc_lookup[tmp]; 350 crc_reg &= 0xffffffff; 351 } 352 353 byte[] sum = new byte[4]; 354 sum[0] = (byte) (crc_reg & 0xffL); 355 sum[1] = (byte) ((crc_reg >>> 8) & 0xffL); 356 sum[2] = (byte) ((crc_reg >>> 16) & 0xffL); 357 sum[3] = (byte) ((crc_reg >>> 24) & 0xffL); 358 359 return sum; 360 } 361 362 private long _ogg_crc_entry(long index) 363 { 364 long r; 365 366 r = index << 24; 367 for (int i = 0; i < 8; i++) 368 { 369 if ((r & 0x80000000L) != 0) 370 { 371 r = (r << 1) ^ 0x04c11db7L; 372 } 373 else 374 { 375 r <<= 1; 376 } 377 } 378 return (r & 0xffffffff); 379 } 380 381 private long toulong(int n) 382 { 383 return (n & 0xffffffffL); 384 } 385 386 private int touint(byte n) 387 { 388 return (n & 0xff); 389 } 390 391 395 public String toString() 396 { 397 398 String str = ""; 399 400 str += channels + " channels at " + rate + "Hz\n"; 401 str += bitrate_nominal / 1000 + "kbps (average bitrate)\n"; 402 403 Iterator fields = comment.keySet().iterator(); 404 while (fields.hasNext()) 405 { 406 String name = (String ) fields.next(); 407 Vector values = (Vector ) comment.get(name); 408 Iterator vi = values.iterator(); 409 str += name + "="; 410 boolean dumb = false; 411 while (vi.hasNext()) 412 { 413 if (dumb) 414 str += ", "; 415 str += vi.next(); 416 dumb = true; 417 } 418 str += "\n"; 419 } 420 421 return str; 422 } 423 424 static void main(String [] args) throws Exception 426 { 427 if (args.length != 1) 428 { 429 System.err.println("usage:\tjava VorbisInfo <ogg vorbis file>"); 430 System.exit(1); 431 } 432 BufferedInputStream b = 433 new BufferedInputStream (new FileInputStream (args[0])); 434 VorbisInfo vi = new VorbisInfo(b); 435 System.out.println("\n" + vi); 436 437 b.close(); 438 } 439 440 public Hashtable getComments() { 442 return comment; 443 } 444 } | Popular Tags |