/* * Bitstream reader. * Copyright (c) 2003 Jonathan Hueber. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 * * See Credits file and Readme for details */ package net.sourceforge.jffmpeg.codecs.utils; import java.io.InputStream; import java.io.IOException; import java.io.ByteArrayInputStream; /** * This class provides functionality to read bits * from a byte array. The basic functionality is * provided by the methods getBits/showBits and getVLC. */ public class BitStream { public static final int KEEP_HISTORIC_BITS = 512 * 8; /** * Byte data with pointers to bytes and bits */ private byte[] data = new byte[ 2048 ]; private int bitIndex; private int sizeInBits; /** * Set frame data - pass in data to read */ public final void setData( byte[] data, int dataLength ) { this.data = data; bitIndex = 0; sizeInBits = dataLength * 8; } public void addData( byte[] extraData, int start, int length ) { if ( getPos() > KEEP_HISTORIC_BITS ) { binData( (getPos() - KEEP_HISTORIC_BITS)/8 ); } if ( (data.length - (sizeInBits/8)) < length + 8 ) { byte[] newArray = new byte[ ( data.length + length ) * 2 ]; System.arraycopy( data, 0, newArray, 0, sizeInBits/8 ); System.arraycopy( extraData, start, newArray, sizeInBits/8, length ); data = newArray; } else { System.arraycopy( extraData, start, data, sizeInBits/8, length ); } sizeInBits += length * 8; } public final byte[] getDataArray() { return data; } public final void binData( int numberOfBytes ) { System.arraycopy( data, numberOfBytes, data, 0, data.length - numberOfBytes ); sizeInBits -= numberOfBytes * 8; bitIndex -= numberOfBytes * 8; } public void seek( int bitNumber ) { bitIndex = bitNumber; } /** * The number of bits remaining in this stream */ public final int availableBits() { return sizeInBits - bitIndex; } public final int getPos() { return bitIndex; } /** * Return a single bit as a true or false * array access + 2 shift + 2 and + inc + comparison */ public final boolean getTrueFalse() { return ((data[ bitIndex >> 3 ] << ( bitIndex++ & 0x07 )) & 0x80) != 0; } /** * Read up to 24 bits - note must handle read 0 bits == 0 */ public final int getBits( int numberOfBits ) { int numberOfBitsToGo = numberOfBits; /* Temporary byte pointer for speed */ int byteIndex = bitIndex >> 3; /* Read the number of bits in byte 0 */ int value = data[ byteIndex++ ]; numberOfBitsToGo -= (8 - (bitIndex & 0x07)); /* Until we have enough, read bytes */ for ( ; numberOfBitsToGo > 0; numberOfBitsToGo -= 8 ) { value = (value << 8)|(data[ byteIndex++ ] & 0xff); } /* number Of bits <=0 "too many" + mask */ value = (value >> -numberOfBitsToGo ) & ( (1 << numberOfBits) - 1); /* skip data */ bitIndex += numberOfBits; if (availableBits() < 0 ) throw new Error( "Buffer underflow" ); return value; } /** * Read up to 24 bits */ public final int showBits( int numberOfBits ) { int numberOfBitsToGo = numberOfBits; /* Temporary byte pointer for speed */ int byteIndex = bitIndex >> 3; /* Read the number of bits in byte 0 */ int value = data[ byteIndex++ ]; numberOfBitsToGo -= (8 - (bitIndex & 0x07)); /* Until we have enough, read bytes */ for ( ; numberOfBitsToGo > 0; numberOfBitsToGo -= 8 ) { value = (value << 8)|(data[ byteIndex++ ] & 0xff); } /* number Of bits <=0 "too many" + mask */ value = (value >> -numberOfBitsToGo ) & ( (1 << numberOfBits) - 1); return value; } /** * Show 12 bits. * This value is chosen as the optimal read value */ public final int show12Bits() { if ( availableBits() < 12 ) { if (availableBits() < 0 ) throw new Error( "Buffer underflow" ); return showBits( availableBits() ) << ( 12 - availableBits() ); } /* Temporary byte pointer for speed */ int byteIndex = bitIndex >> 3; int bitNumber = (bitIndex & 0x07); /* Read the number of bits in byte 0 */ int value = (data[ byteIndex++ ] << 16)|(data[ byteIndex++ ] & 0xff)<<8|(data[ byteIndex++ ] & 0xff); /* shift right + mask */ return (value >> (24 - 12 - bitNumber) ) & 0xfff; } /** * Read 0,1,2 for 0 / 10 / 11 */ public final int decode012() { return getTrueFalse() ? (getTrueFalse() ? 2 : 1) : 0; } /** * Read a variable length code */ public final int getVLC( VLCTable table ) throws FFMpegException { int code = show12Bits(); int length = table.getCodeLength( code ); if ( length > 0 ) { bitIndex += length; if (availableBits() < 0 ) throw new FFMpegException( "Buffer underflow" ); return table.getCodeValue( code ); } else if ( length < 0 ) { bitIndex += 12; return getVLC( table.getNextLevel( code ) ); } if (availableBits() < 12 ) throw new FFMpegException( "Buffer underflow" ); throw new FFMpegException( "Illegal VLC code " + table ); } }