123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- /*
- * 11/19/04 1.0 moved to LGPL.
- *
- * 11/17/04 Uncomplete frames discarded. E.B, javalayer@javazoom.net
- *
- * 12/05/03 ID3v2 tag returned. E.B, javalayer@javazoom.net
- *
- * 12/12/99 Based on Ibitstream. Exceptions thrown on errors,
- * Temporary removed seek functionality. mdm@techie.com
- *
- * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net
- *
- * 04/14/97 : Added function prototypes for new syncing and seeking
- * mechanisms. Also made this file portable. Changes made by Jeff Tsay
- *
- * @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34
- * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
- * @(#) Berlin University of Technology
- *-----------------------------------------------------------------------
- * This program 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.
- *
- * This program 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.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *----------------------------------------------------------------------
- */
- package javazoom.jl.decoder;
- import java.io.BufferedInputStream;
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PushbackInputStream;
- /**
- * The <code>Bistream</code> class is responsible for parsing
- * an MPEG audio bitstream.
- *
- * <b>REVIEW:</b> much of the parsing currently occurs in the
- * various decoders. This should be moved into this class and associated
- * inner classes.
- */
- public final class Bitstream implements BitstreamErrors
- {
- /**
- * Synchronization control constant for the initial
- * synchronization to the start of a frame.
- */
- static byte INITIAL_SYNC = 0;
- /**
- * Synchronization control constant for non-initial frame
- * synchronizations.
- */
- static byte STRICT_SYNC = 1;
- // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
- /**
- * Maximum size of the frame buffer.
- */
- private static final int BUFFER_INT_SIZE = 433;
- /**
- * The frame buffer that holds the data for the current frame.
- */
- private final int[] framebuffer = new int[BUFFER_INT_SIZE];
- /**
- * Number of valid bytes in the frame buffer.
- */
- private int framesize;
- /**
- * The bytes read from the stream.
- */
- private byte[] frame_bytes = new byte[BUFFER_INT_SIZE*4];
- /**
- * Index into <code>framebuffer</code> where the next bits are
- * retrieved.
- */
- private int wordpointer;
- /**
- * Number (0-31, from MSB to LSB) of next bit for get_bits()
- */
- private int bitindex;
- /**
- * The current specified syncword
- */
- private int syncword;
-
- /**
- * Audio header position in stream.
- */
- private int header_pos = 0;
- /**
- *
- */
- private boolean single_ch_mode;
- //private int current_frame_number;
- //private int last_frame_number;
- private final int bitmask[] = {0, // dummy
- 0x00000001, 0x00000003, 0x00000007, 0x0000000F,
- 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
- 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
- 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
- 0x0001FFFF };
- private final PushbackInputStream source;
- private final Header header = new Header();
- private final byte syncbuf[] = new byte[4];
- private Crc16[] crc = new Crc16[1];
- private byte[] rawid3v2 = null;
- private boolean firstframe = true;
- /**
- * Construct a IBitstream that reads data from a
- * given InputStream.
- *
- * @param in The InputStream to read from.
- */
- public Bitstream(InputStream in)
- {
- if (in==null) throw new NullPointerException("in");
- in = new BufferedInputStream(in);
- loadID3v2(in);
- firstframe = true;
- //source = new PushbackInputStream(in, 1024);
- source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);
-
- closeFrame();
- //current_frame_number = -1;
- //last_frame_number = -1;
- }
- /**
- * Return position of the first audio header.
- * @return size of ID3v2 tag frames.
- */
- public int header_pos()
- {
- return header_pos;
- }
-
- /**
- * Load ID3v2 frames.
- * @param in MP3 InputStream.
- * @author JavaZOOM
- */
- private void loadID3v2(InputStream in)
- {
- int size = -1;
- try
- {
- // Read ID3v2 header (10 bytes).
- in.mark(10);
- size = readID3v2Header(in);
- header_pos = size;
- }
- catch (IOException e)
- {}
- finally
- {
- try
- {
- // Unread ID3v2 header (10 bytes).
- in.reset();
- }
- catch (IOException e)
- {}
- }
- // Load ID3v2 tags.
- try
- {
- if (size > 0)
- {
- rawid3v2 = new byte[size];
- in.read(rawid3v2,0,rawid3v2.length);
- }
- }
- catch (IOException e)
- {}
- }
-
- /**
- * Parse ID3v2 tag header to find out size of ID3v2 frames.
- * @param in MP3 InputStream
- * @return size of ID3v2 frames + header
- * @throws IOException
- * @author JavaZOOM
- */
- private int readID3v2Header(InputStream in) throws IOException
- {
- byte[] id3header = new byte[4];
- int size = -10;
- in.read(id3header,0,3);
- // Look for ID3v2
- if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))
- {
- in.read(id3header,0,3);
- int majorVersion = id3header[0];
- int revision = id3header[1];
- in.read(id3header,0,4);
- size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);
- }
- return (size+10);
- }
-
- /**
- * Return raw ID3v2 frames + header.
- * @return ID3v2 InputStream or null if ID3v2 frames are not available.
- */
- public InputStream getRawID3v2()
- {
- if (rawid3v2 == null) return null;
- else
- {
- ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);
- return bain;
- }
- }
- /**
- * Close the Bitstream.
- * @throws BitstreamException
- */
- public void close() throws BitstreamException
- {
- try
- {
- source.close();
- }
- catch (IOException ex)
- {
- throw newBitstreamException(STREAM_ERROR, ex);
- }
- }
- /**
- * Reads and parses the next frame from the input source.
- * @return the Header describing details of the frame read,
- * or null if the end of the stream has been reached.
- */
- public Header readFrame() throws BitstreamException
- {
- Header result = null;
- try
- {
- result = readNextFrame();
- // E.B, Parse VBR (if any) first frame.
- if (firstframe == true)
- {
- result.parseVBR(frame_bytes);
- firstframe = false;
- }
- }
- catch (BitstreamException ex)
- {
- if ((ex.getErrorCode()==INVALIDFRAME))
- {
- // Try to skip this frame.
- //System.out.println("INVALIDFRAME");
- try
- {
- closeFrame();
- result = readNextFrame();
- }
- catch (BitstreamException e)
- {
- if ((e.getErrorCode()!=STREAM_EOF))
- {
- // wrap original exception so stack trace is maintained.
- throw newBitstreamException(e.getErrorCode(), e);
- }
- }
- }
- else if ((ex.getErrorCode()!=STREAM_EOF))
- {
- // wrap original exception so stack trace is maintained.
- throw newBitstreamException(ex.getErrorCode(), ex);
- }
- }
- return result;
- }
- /**
- * Read next MP3 frame.
- * @return MP3 frame header.
- * @throws BitstreamException
- */
- private Header readNextFrame() throws BitstreamException
- {
- if (framesize == -1)
- {
- nextFrame();
- }
- return header;
- }
- /**
- * Read next MP3 frame.
- * @throws BitstreamException
- */
- private void nextFrame() throws BitstreamException
- {
- // entire frame is read by the header class.
- header.read_header(this, crc);
- }
- /**
- * Unreads the bytes read from the frame.
- * @throws BitstreamException
- */
- // REVIEW: add new error codes for this.
- public void unreadFrame() throws BitstreamException
- {
- if (wordpointer==-1 && bitindex==-1 && (framesize>0))
- {
- try
- {
- source.unread(frame_bytes, 0, framesize);
- }
- catch (IOException ex)
- {
- throw newBitstreamException(STREAM_ERROR);
- }
- }
- }
- /**
- * Close MP3 frame.
- */
- public void closeFrame()
- {
- framesize = -1;
- wordpointer = -1;
- bitindex = -1;
- }
- /**
- * Determines if the next 4 bytes of the stream represent a
- * frame header.
- */
- public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException
- {
- int read = readBytes(syncbuf, 0, 4);
- int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);
- try
- {
- source.unread(syncbuf, 0, read);
- }
- catch (IOException ex)
- {
- }
- boolean sync = false;
- switch (read)
- {
- case 0:
- sync = true;
- break;
- case 4:
- sync = isSyncMark(headerstring, syncmode, syncword);
- break;
- }
- return sync;
- }
- // REVIEW: this class should provide inner classes to
- // parse the frame contents. Eventually, readBits will
- // be removed.
- public int readBits(int n)
- {
- return get_bits(n);
- }
- public int readCheckedBits(int n)
- {
- // REVIEW: implement CRC check.
- return get_bits(n);
- }
- protected BitstreamException newBitstreamException(int errorcode)
- {
- return new BitstreamException(errorcode, null);
- }
- protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)
- {
- return new BitstreamException(errorcode, throwable);
- }
- /**
- * Get next 32 bits from bitstream.
- * They are stored in the headerstring.
- * syncmod allows Synchro flag ID
- * The returned value is False at the end of stream.
- */
- int syncHeader(byte syncmode) throws BitstreamException
- {
- boolean sync;
- int headerstring;
- // read additional 2 bytes
- int bytesRead = readBytes(syncbuf, 0, 3);
- if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);
- headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);
- do
- {
- headerstring <<= 8;
- if (readBytes(syncbuf, 3, 1)!=1)
- throw newBitstreamException(STREAM_EOF, null);
- headerstring |= (syncbuf[3] & 0x000000FF);
- sync = isSyncMark(headerstring, syncmode, syncword);
- }
- while (!sync);
- //current_frame_number++;
- //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;
- return headerstring;
- }
- public boolean isSyncMark(int headerstring, int syncmode, int word)
- {
- boolean sync = false;
- if (syncmode == INITIAL_SYNC)
- {
- //sync = ((headerstring & 0xFFF00000) == 0xFFF00000);
- sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5
- }
- else
- {
- sync = ((headerstring & 0xFFF80C00) == word) &&
- (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);
- }
- // filter out invalid sample rate
- if (sync)
- sync = (((headerstring >>> 10) & 3)!=3);
- // filter out invalid layer
- if (sync)
- sync = (((headerstring >>> 17) & 3)!=0);
- // filter out invalid version
- if (sync)
- sync = (((headerstring >>> 19) & 3)!=1);
- return sync;
- }
- /**
- * Reads the data for the next frame. The frame is not parsed
- * until parse frame is called.
- */
- int read_frame_data(int bytesize) throws BitstreamException
- {
- int numread = 0;
- numread = readFully(frame_bytes, 0, bytesize);
- framesize = bytesize;
- wordpointer = -1;
- bitindex = -1;
- return numread;
- }
- /**
- * Parses the data previously read with read_frame_data().
- */
- void parse_frame() throws BitstreamException
- {
- // Convert Bytes read to int
- int b=0;
- byte[] byteread = frame_bytes;
- int bytesize = framesize;
- // Check ID3v1 TAG (True only if last frame).
- //for (int t=0;t<(byteread.length)-2;t++)
- //{
- // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))
- // {
- // System.out.println("ID3v1 detected at offset "+t);
- // throw newBitstreamException(INVALIDFRAME, null);
- // }
- //}
-
- for (int k=0;k<bytesize;k=k+4)
- {
- int convert = 0;
- byte b0 = 0;
- byte b1 = 0;
- byte b2 = 0;
- byte b3 = 0;
- b0 = byteread[k];
- if (k+1<bytesize) b1 = byteread[k+1];
- if (k+2<bytesize) b2 = byteread[k+2];
- if (k+3<bytesize) b3 = byteread[k+3];
- framebuffer[b++] = ((b0 << 24) &0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00) | (b3 & 0x000000FF);
- }
- wordpointer = 0;
- bitindex = 0;
- }
- /**
- * Read bits from buffer into the lower bits of an unsigned int.
- * The LSB contains the latest read bit of the stream.
- * (1 <= number_of_bits <= 16)
- */
- public int get_bits(int number_of_bits)
- {
- int returnvalue = 0;
- int sum = bitindex + number_of_bits;
- // E.B
- // There is a problem here, wordpointer could be -1 ?!
- if (wordpointer < 0) wordpointer = 0;
- // E.B : End.
- if (sum <= 32)
- {
- // all bits contained in *wordpointer
- returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];
- // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];
- if ((bitindex += number_of_bits) == 32)
- {
- bitindex = 0;
- wordpointer++; // added by me!
- }
- return returnvalue;
- }
- // E.B : Check that ?
- //((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];
- //wordpointer++; // Added by me!
- //((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];
- int Right = (framebuffer[wordpointer] & 0x0000FFFF);
- wordpointer++;
- int Left = (framebuffer[wordpointer] & 0xFFFF0000);
- returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16)& 0x0000FFFF);
- returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32 - bitindex))
- returnvalue &= bitmask[number_of_bits];
- bitindex = sum - 32;
- return returnvalue;
- }
- /**
- * Set the word we want to sync the header to.
- * In Big-Endian byte order
- */
- void set_syncword(int syncword0)
- {
- syncword = syncword0 & 0xFFFFFF3F;
- single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);
- }
- /**
- * Reads the exact number of bytes from the source
- * input stream into a byte array.
- *
- * @param b The byte array to read the specified number
- * of bytes into.
- * @param offs The index in the array where the first byte
- * read should be stored.
- * @param len the number of bytes to read.
- *
- * @exception BitstreamException is thrown if the specified
- * number of bytes could not be read from the stream.
- */
- private int readFully(byte[] b, int offs, int len)
- throws BitstreamException
- {
- int nRead = 0;
- try
- {
- while (len > 0)
- {
- int bytesread = source.read(b, offs, len);
- if (bytesread == -1)
- {
- while (len-->0)
- {
- b[offs++] = 0;
- }
- break;
- //throw newBitstreamException(UNEXPECTED_EOF, new EOFException());
- }
- nRead = nRead + bytesread;
- offs += bytesread;
- len -= bytesread;
- }
- }
- catch (IOException ex)
- {
- throw newBitstreamException(STREAM_ERROR, ex);
- }
- return nRead;
- }
- /**
- * Simlar to readFully, but doesn't throw exception when
- * EOF is reached.
- */
- private int readBytes(byte[] b, int offs, int len)
- throws BitstreamException
- {
- int totalBytesRead = 0;
- try
- {
- while (len > 0)
- {
- int bytesread = source.read(b, offs, len);
- if (bytesread == -1)
- {
- break;
- }
- totalBytesRead += bytesread;
- offs += bytesread;
- len -= bytesread;
- }
- }
- catch (IOException ex)
- {
- throw newBitstreamException(STREAM_ERROR, ex);
- }
- return totalBytesRead;
- }
- }
|