2 * 11/19/04 1.0 moved to LGPL.
\r
4 * 11/17/04 Uncomplete frames discarded. E.B, javalayer@javazoom.net
\r
6 * 12/05/03 ID3v2 tag returned. E.B, javalayer@javazoom.net
\r
8 * 12/12/99 Based on Ibitstream. Exceptions thrown on errors,
\r
9 * Temporary removed seek functionality. mdm@techie.com
\r
11 * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net
\r
13 * 04/14/97 : Added function prototypes for new syncing and seeking
\r
14 * mechanisms. Also made this file portable. Changes made by Jeff Tsay
\r
16 * @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34
\r
17 * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
\r
18 * @(#) Berlin University of Technology
\r
19 *-----------------------------------------------------------------------
\r
20 * This program is free software; you can redistribute it and/or modify
\r
21 * it under the terms of the GNU Library General Public License as published
\r
22 * by the Free Software Foundation; either version 2 of the License, or
\r
23 * (at your option) any later version.
\r
25 * This program is distributed in the hope that it will be useful,
\r
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
28 * GNU Library General Public License for more details.
\r
30 * You should have received a copy of the GNU Library General Public
\r
31 * License along with this program; if not, write to the Free Software
\r
32 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\r
33 *----------------------------------------------------------------------
\r
36 import java.io.BufferedInputStream;
\r
37 import java.io.ByteArrayInputStream;
\r
38 import java.io.IOException;
\r
39 import java.io.InputStream;
\r
40 import java.io.PushbackInputStream;
\r
44 * The <code>Bistream</code> class is responsible for parsing
\r
45 * an MPEG audio bitstream.
\r
47 * <b>REVIEW:</b> much of the parsing currently occurs in the
\r
48 * various decoders. This should be moved into this class and associated
\r
51 public final class Bitstream implements BitstreamErrors
\r
54 * Synchronization control constant for the initial
\r
55 * synchronization to the start of a frame.
\r
57 static byte INITIAL_SYNC = 0;
\r
60 * Synchronization control constant for non-initial frame
\r
63 static byte STRICT_SYNC = 1;
\r
65 // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
\r
67 * Maximum size of the frame buffer.
\r
69 private static final int BUFFER_INT_SIZE = 433;
\r
72 * The frame buffer that holds the data for the current frame.
\r
74 private final int[] framebuffer = new int[BUFFER_INT_SIZE];
\r
77 * Number of valid bytes in the frame buffer.
\r
79 private int framesize;
\r
82 * The bytes read from the stream.
\r
84 private byte[] frame_bytes = new byte[BUFFER_INT_SIZE*4];
\r
87 * Index into <code>framebuffer</code> where the next bits are
\r
90 private int wordpointer;
\r
93 * Number (0-31, from MSB to LSB) of next bit for get_bits()
\r
95 private int bitindex;
\r
98 * The current specified syncword
\r
100 private int syncword;
\r
103 * Audio header position in stream.
\r
105 private int header_pos = 0;
\r
110 private boolean single_ch_mode;
\r
111 //private int current_frame_number;
\r
112 //private int last_frame_number;
\r
114 private final int bitmask[] = {0, // dummy
\r
115 0x00000001, 0x00000003, 0x00000007, 0x0000000F,
\r
116 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
\r
117 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
\r
118 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
\r
121 private final PushbackInputStream source;
\r
123 private final Header header = new Header();
\r
125 private final byte syncbuf[] = new byte[4];
\r
127 private Crc16[] crc = new Crc16[1];
\r
129 private byte[] rawid3v2 = null;
\r
131 private boolean firstframe = true;
\r
135 * Construct a IBitstream that reads data from a
\r
136 * given InputStream.
\r
138 * @param in The InputStream to read from.
\r
140 public Bitstream(InputStream in)
\r
142 if (in==null) throw new NullPointerException("in");
\r
143 in = new BufferedInputStream(in);
\r
146 //source = new PushbackInputStream(in, 1024);
\r
147 source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);
\r
150 //current_frame_number = -1;
\r
151 //last_frame_number = -1;
\r
155 * Return position of the first audio header.
\r
156 * @return size of ID3v2 tag frames.
\r
158 public int header_pos()
\r
164 * Load ID3v2 frames.
\r
165 * @param in MP3 InputStream.
\r
168 private void loadID3v2(InputStream in)
\r
173 // Read ID3v2 header (10 bytes).
\r
175 size = readID3v2Header(in);
\r
176 header_pos = size;
\r
178 catch (IOException e)
\r
184 // Unread ID3v2 header (10 bytes).
\r
187 catch (IOException e)
\r
190 // Load ID3v2 tags.
\r
195 rawid3v2 = new byte[size];
\r
196 in.read(rawid3v2,0,rawid3v2.length);
\r
199 catch (IOException e)
\r
204 * Parse ID3v2 tag header to find out size of ID3v2 frames.
\r
205 * @param in MP3 InputStream
\r
206 * @return size of ID3v2 frames + header
\r
207 * @throws IOException
\r
210 private int readID3v2Header(InputStream in) throws IOException
\r
212 byte[] id3header = new byte[4];
\r
214 in.read(id3header,0,3);
\r
216 if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))
\r
218 in.read(id3header,0,3);
\r
219 int majorVersion = id3header[0];
\r
220 int revision = id3header[1];
\r
221 in.read(id3header,0,4);
\r
222 size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);
\r
228 * Return raw ID3v2 frames + header.
\r
229 * @return ID3v2 InputStream or null if ID3v2 frames are not available.
\r
231 public InputStream getRawID3v2()
\r
233 if (rawid3v2 == null) return null;
\r
236 ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);
\r
242 * Close the Bitstream.
\r
243 * @throws BitstreamException
\r
245 public void close() throws BitstreamException
\r
251 catch (IOException ex)
\r
253 throw newBitstreamException(STREAM_ERROR, ex);
\r
258 * Reads and parses the next frame from the input source.
\r
259 * @return the Header describing details of the frame read,
\r
260 * or null if the end of the stream has been reached.
\r
262 public Header readFrame() throws BitstreamException
\r
264 Header result = null;
\r
267 result = readNextFrame();
\r
268 // E.B, Parse VBR (if any) first frame.
\r
269 if (firstframe == true)
\r
271 result.parseVBR(frame_bytes);
\r
272 firstframe = false;
\r
275 catch (BitstreamException ex)
\r
277 if ((ex.getErrorCode()==INVALIDFRAME))
\r
279 // Try to skip this frame.
\r
280 //System.out.println("INVALIDFRAME");
\r
284 result = readNextFrame();
\r
286 catch (BitstreamException e)
\r
288 if ((e.getErrorCode()!=STREAM_EOF))
\r
290 // wrap original exception so stack trace is maintained.
\r
291 throw newBitstreamException(e.getErrorCode(), e);
\r
295 else if ((ex.getErrorCode()!=STREAM_EOF))
\r
297 // wrap original exception so stack trace is maintained.
\r
298 throw newBitstreamException(ex.getErrorCode(), ex);
\r
305 * Read next MP3 frame.
\r
306 * @return MP3 frame header.
\r
307 * @throws BitstreamException
\r
309 private Header readNextFrame() throws BitstreamException
\r
311 if (framesize == -1)
\r
320 * Read next MP3 frame.
\r
321 * @throws BitstreamException
\r
323 private void nextFrame() throws BitstreamException
\r
325 // entire frame is read by the header class.
\r
326 header.read_header(this, crc);
\r
330 * Unreads the bytes read from the frame.
\r
331 * @throws BitstreamException
\r
333 // REVIEW: add new error codes for this.
\r
334 public void unreadFrame() throws BitstreamException
\r
336 if (wordpointer==-1 && bitindex==-1 && (framesize>0))
\r
340 source.unread(frame_bytes, 0, framesize);
\r
342 catch (IOException ex)
\r
344 throw newBitstreamException(STREAM_ERROR);
\r
352 public void closeFrame()
\r
360 * Determines if the next 4 bytes of the stream represent a
\r
363 public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException
\r
365 int read = readBytes(syncbuf, 0, 4);
\r
366 int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);
\r
370 source.unread(syncbuf, 0, read);
\r
372 catch (IOException ex)
\r
376 boolean sync = false;
\r
383 sync = isSyncMark(headerstring, syncmode, syncword);
\r
391 // REVIEW: this class should provide inner classes to
\r
392 // parse the frame contents. Eventually, readBits will
\r
394 public int readBits(int n)
\r
396 return get_bits(n);
\r
399 public int readCheckedBits(int n)
\r
401 // REVIEW: implement CRC check.
\r
402 return get_bits(n);
\r
405 protected BitstreamException newBitstreamException(int errorcode)
\r
407 return new BitstreamException(errorcode, null);
\r
409 protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)
\r
411 return new BitstreamException(errorcode, throwable);
\r
415 * Get next 32 bits from bitstream.
\r
416 * They are stored in the headerstring.
\r
417 * syncmod allows Synchro flag ID
\r
418 * The returned value is False at the end of stream.
\r
421 int syncHeader(byte syncmode) throws BitstreamException
\r
425 // read additional 2 bytes
\r
426 int bytesRead = readBytes(syncbuf, 0, 3);
\r
428 if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);
\r
430 headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);
\r
434 headerstring <<= 8;
\r
436 if (readBytes(syncbuf, 3, 1)!=1)
\r
437 throw newBitstreamException(STREAM_EOF, null);
\r
439 headerstring |= (syncbuf[3] & 0x000000FF);
\r
441 sync = isSyncMark(headerstring, syncmode, syncword);
\r
445 //current_frame_number++;
\r
446 //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;
\r
448 return headerstring;
\r
451 public boolean isSyncMark(int headerstring, int syncmode, int word)
\r
453 boolean sync = false;
\r
455 if (syncmode == INITIAL_SYNC)
\r
457 //sync = ((headerstring & 0xFFF00000) == 0xFFF00000);
\r
458 sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5
\r
462 sync = ((headerstring & 0xFFF80C00) == word) &&
\r
463 (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);
\r
466 // filter out invalid sample rate
\r
468 sync = (((headerstring >>> 10) & 3)!=3);
\r
469 // filter out invalid layer
\r
471 sync = (((headerstring >>> 17) & 3)!=0);
\r
472 // filter out invalid version
\r
474 sync = (((headerstring >>> 19) & 3)!=1);
\r
480 * Reads the data for the next frame. The frame is not parsed
\r
481 * until parse frame is called.
\r
483 int read_frame_data(int bytesize) throws BitstreamException
\r
486 numread = readFully(frame_bytes, 0, bytesize);
\r
487 framesize = bytesize;
\r
494 * Parses the data previously read with read_frame_data().
\r
496 void parse_frame() throws BitstreamException
\r
498 // Convert Bytes read to int
\r
500 byte[] byteread = frame_bytes;
\r
501 int bytesize = framesize;
\r
503 // Check ID3v1 TAG (True only if last frame).
\r
504 //for (int t=0;t<(byteread.length)-2;t++)
\r
506 // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))
\r
508 // System.out.println("ID3v1 detected at offset "+t);
\r
509 // throw newBitstreamException(INVALIDFRAME, null);
\r
513 for (int k=0;k<bytesize;k=k+4)
\r
521 if (k+1<bytesize) b1 = byteread[k+1];
\r
522 if (k+2<bytesize) b2 = byteread[k+2];
\r
523 if (k+3<bytesize) b3 = byteread[k+3];
\r
524 framebuffer[b++] = ((b0 << 24) &0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00) | (b3 & 0x000000FF);
\r
531 * Read bits from buffer into the lower bits of an unsigned int.
\r
532 * The LSB contains the latest read bit of the stream.
\r
533 * (1 <= number_of_bits <= 16)
\r
535 public int get_bits(int number_of_bits)
\r
537 int returnvalue = 0;
\r
538 int sum = bitindex + number_of_bits;
\r
541 // There is a problem here, wordpointer could be -1 ?!
\r
542 if (wordpointer < 0) wordpointer = 0;
\r
547 // all bits contained in *wordpointer
\r
548 returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];
\r
549 // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];
\r
550 if ((bitindex += number_of_bits) == 32)
\r
553 wordpointer++; // added by me!
\r
555 return returnvalue;
\r
558 // E.B : Check that ?
\r
559 //((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];
\r
560 //wordpointer++; // Added by me!
\r
561 //((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];
\r
562 int Right = (framebuffer[wordpointer] & 0x0000FFFF);
\r
564 int Left = (framebuffer[wordpointer] & 0xFFFF0000);
\r
565 returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16)& 0x0000FFFF);
\r
567 returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32 - bitindex))
\r
568 returnvalue &= bitmask[number_of_bits];
\r
569 bitindex = sum - 32;
\r
570 return returnvalue;
\r
574 * Set the word we want to sync the header to.
\r
575 * In Big-Endian byte order
\r
577 void set_syncword(int syncword0)
\r
579 syncword = syncword0 & 0xFFFFFF3F;
\r
580 single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);
\r
583 * Reads the exact number of bytes from the source
\r
584 * input stream into a byte array.
\r
586 * @param b The byte array to read the specified number
\r
588 * @param offs The index in the array where the first byte
\r
589 * read should be stored.
\r
590 * @param len the number of bytes to read.
\r
592 * @exception BitstreamException is thrown if the specified
\r
593 * number of bytes could not be read from the stream.
\r
595 private int readFully(byte[] b, int offs, int len)
\r
596 throws BitstreamException
\r
603 int bytesread = source.read(b, offs, len);
\r
604 if (bytesread == -1)
\r
611 //throw newBitstreamException(UNEXPECTED_EOF, new EOFException());
\r
613 nRead = nRead + bytesread;
\r
618 catch (IOException ex)
\r
620 throw newBitstreamException(STREAM_ERROR, ex);
\r
626 * Simlar to readFully, but doesn't throw exception when
\r
629 private int readBytes(byte[] b, int offs, int len)
\r
630 throws BitstreamException
\r
632 int totalBytesRead = 0;
\r
637 int bytesread = source.read(b, offs, len);
\r
638 if (bytesread == -1)
\r
642 totalBytesRead += bytesread;
\r
647 catch (IOException ex)
\r
649 throw newBitstreamException(STREAM_ERROR, ex);
\r
651 return totalBytesRead;
\r