/** @file bits.cc, bit-level output */ /* Copyright (C) 2001, Andrew Stevens * * * Disclaimer of Warranty * * These software programs are available to the user without any license fee or * royalty on an "as is" basis. The MPEG Software Simulation Group disclaims * any and all warranties, whether express, implied, or statuary, including any * implied warranties or merchantability or of fitness for a particular * purpose. In no event shall the copyright-holder be liable for any * incidental, punitive, or consequential damages of any kind whatsoever * arising from the use of these programs. * * This disclaimer of warranty extends to the user of these programs and user's * customers, employees, agents, transferees, successors, and assigns. * * The MPEG Software Simulation Group does not represent or warrant that the * programs furnished hereunder are free of infringement of any third-party * patents. * * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware, * are subject to royalty fees to patent holders. Many of these patents are * general enough such that they are unavoidable regardless of implementation * design. * */ #include "config.h" #include #include #include #include #include "mjpeg_logging.h" #include "bits.hpp" const unsigned int BitStreamBuffering::BUFFER_SIZE = 64 * 1024; const unsigned int BitStreamBuffering::BUFFER_CEILING = 32 * 1024 * 1024; BitStreamBuffering::BitStreamBuffering() : bfr(0), bfr_size(0), buffered(0) { } /***** * * Pseudo-destructor. Frees the internal buffer and marks buffer as * empty. * *****/ void BitStreamBuffering::Release() { if( bfr != 0) delete bfr; bfr = 0; bfr_size = 0; buffered = 0; } /***** * * Empty the buffer. * *****/ void BitStreamBuffering::Empty() { buffered = 0; } /** sets the internal buffer size. @param new_buf_size requests new internal buffer size */ void BitStreamBuffering::SetBufSize( unsigned int new_buf_size) { // // If size has changed and we won't lose buffered data // we adjust the buffer size, otherwise we ignore the request // if( new_buf_size > BUFFER_CEILING ) { mjpeg_error_exit1("INTERNAL ERROR: additional data required but " " input buffer size would exceed ceiling"); } if( new_buf_size > buffered && bfr_size != new_buf_size ) { uint8_t *new_buf = new uint8_t[new_buf_size]; memcpy( new_buf, bfr, static_cast(buffered) ); if( bfr != 0 ) delete [] bfr; bfr_size = new_buf_size; bfr = new_buf; } } /**************************** * * Return the pointer to where (after, if necessary extending the * buffer) up to to_append bytes may be appended to the buffered bytes * ****************************/ uint8_t *BitStreamBuffering::StartAppendPoint( unsigned int to_append ) { unsigned int resize_size = bfr_size; assert( resize_size != 0 ); while( resize_size - buffered < to_append ) { resize_size *= 2; } if( resize_size != bfr_size ) SetBufSize( resize_size ); return bfr+buffered; } /** Refills an IBitStream's input buffer based on the internal variables buffered and bfr_size. Strategy: we read 1/4 of a buffer, always. */ bool IBitStream::ReadIntoBuffer(unsigned int to_read) { size_t i; unsigned int read_pow2 = BUFFER_SIZE/4; while( read_pow2 < to_read ) read_pow2 <<= 1; i = ReadStreamBytes( StartAppendPoint(read_pow2), static_cast(read_pow2) ); //i = fread(StartAppendPoint(read_pow2), sizeof(uint8_t), // static_cast(read_pow2), fileh); Appended(static_cast(i)); if ( i == 0 ) { eobs = true; return false; } return true; } #define masks(idx) (1<<(idx)) /*read 1 bit from the bit stream @returns the read bit, 0 on EOF */ uint32_t IBitStream::Get1Bit() { unsigned int bit; if (eobs) return 0; bit = (bfr[byteidx] & masks(bitidx - 1)) >> (bitidx - 1); bitreadpos++; bitidx--; if (!bitidx) { bitidx = 8; byteidx++; if (byteidx == buffered) { ReadIntoBuffer(); } } return bit; } /*read N bits from the bit stream @returns the read bits, 0 on EOF */ uint32_t IBitStream::GetBits(int N) { uint32_t val = 0; int i = N; unsigned int j; // Optimize: we are on byte boundary and want to read multiple of bytes! if ((bitidx == 8) && ((N & 7) == 0)) { i = N >> 3; while (i > 0) { if (eobs) return 0; val = (val << 8) | bfr[byteidx]; byteidx++; bitreadpos += 8; if (byteidx == buffered) { ReadIntoBuffer(); } i--; } } else { while (i > 0) { if (eobs) return 0; j = (bfr[byteidx] & masks(bitidx - 1)) >> (bitidx - 1); bitreadpos++; bitidx--; if (!bitidx) { bitidx = 8; byteidx++; if (byteidx == buffered) { ReadIntoBuffer(); } } val = (val << 1) | j; i--; } } return val; } /** This function seeks for a byte aligned sync word (max 32 bits) in the bit stream and places the bit stream pointer right after the sync. This function returns 1 if the sync was found otherwise it returns 0 @param sync the sync word to search for @param N the number of bits to retrieve @param lim number of bytes to search through @returns false on error */ bool IBitStream::SeekSync(uint32_t sync, int N, int lim) { uint32_t val, val1; uint32_t maxi = ((1U<= buffered && !eobs) { ReadIntoBuffer( req_byteidx - (buffered-1) ); } eobs = ( req_byteidx >= buffered ); if( eobs ) bitreadpos += (buffered - byteidx)*8; else bitreadpos += bytes_to_seek_fwd*8; byteidx = req_byteidx; } /** Flushes all read input up-to *but not including* byte flush_upto. @param flush_to the number of bits to flush */ void IBitStream::Flush(bitcount_t flush_upto ) { if( flush_upto > bfr_start+buffered ) mjpeg_error_exit1("INTERNAL ERROR: attempt to flush input beyond buffered amount" ); if( flush_upto < bfr_start ) mjpeg_error_exit1("INTERNAL ERROR: attempt to flush input stream before first buffered byte %lld last is %lld", flush_upto, bfr_start ); unsigned int bytes_to_flush = static_cast(flush_upto - bfr_start); // // Don't bother actually flushing until a good fraction of a buffer // will be cleared. // if( bytes_to_flush < bfr_size/2 ) return; buffered -= bytes_to_flush; bfr_start = flush_upto; byteidx -= bytes_to_flush; memmove( bfr, bfr+bytes_to_flush, static_cast(buffered)); } /** Undo scanning / reading N.b buffer *must not* be flushed between prepareundo and undochanges. @param undo handle to store the undo information */ void IBitStream::PrepareUndo( IBitStreamUndo &undo) { undo = *(static_cast(this)); } /** Undoes changes committed to an IBitStream. @param undo handle to retrieve the undo information */ void IBitStream::UndoChanges( IBitStreamUndo &undo) { *(static_cast(this)) = undo; } /** Read a number bytes over an IBitStream, using the buffer. @param dst buffer to read to @param length the number of bytes to read */ unsigned int IBitStream::GetBytes(uint8_t *dst, unsigned int length) { unsigned int to_read = length; if( bytereadpos < bfr_start) mjpeg_error_exit1("INTERNAL ERROR: access to input stream buffer @ %lld: before first buffered byte (%lld)", bytereadpos, bfr_start ); if( bytereadpos+length > bfr_start+buffered ) { if( !EndOfStream() ) { mjpeg_error("INTERNAL ERROR: access to input stream buffer beyond last buffered byte @POS=%lld END=%d REQ=%lld + %d bytes", bytereadpos, buffered, bytereadpos-bfr_start, length ); abort(); } to_read = static_cast( (bfr_start+buffered)- bytereadpos ); } memcpy( dst, bfr+(static_cast( bytereadpos-bfr_start)), to_read); // We only ever flush up to the start of a read as we // have only scanned up to a header *beginning* a block that is then // read //flush( bytereadpos ); bytereadpos += to_read; return to_read; } #ifdef REDUNDANT_CODE /** Initialize buffer, call once before first putbits or alignbits. @param bs_filename output filename @param buf_size size of the temporary output buffer to use */ void OBitStream::open(char *bs_filename, unsigned int buf_size) { if ((fileh = fopen(bs_filename, "wb")) == NULL) { mjpeg_error_exit1( "Unable to open file %s for writing; %s", bs_filename, strerror(errno)); } filename = strcpy( new char[strlen(bs_filename)+1], bs_filename ); SetBufSize(buf_size); // Save multiple buffering... setvbuf(fileh, 0, _IONBF, 0 ); bitidx = 8; byteidx = 0; bitwritepos = 0LL; outbyte = 0; } /** Closes the OBitStream. Flushes the output buffer and closes the output file if one was open. */ void OBitStream::close() { if (fileh) { if (byteidx != 0) fwrite(bfr, sizeof(uint8_t), byteidx, fileh); fclose(fileh); delete filename; fileh = NULL; } } /** Puts a byte into the OBitStream. Puts the byte into the internal buffer; if the buffer is full, it flushes the buffer to disk. */ void OBitStream::putbyte() { bfr[byteidx++] = outbyte; if (byteidx == bfr_size) { if (fwrite(bfr, sizeof(uint8_t), bfr_size, fileh) != bfr_size) mjpeg_error_exit1( "Write failed: %s", strerror(errno)); byteidx = 0; } bitidx = 8; } /** write rightmost n (0<=n<=32) bits of val to outfile @param val value to write bitwise @param n number of bits to write */ void OBitStream::putbits(uint32_t val, int n) { int i; unsigned int mask; bitwritepos += n; while( n >= bitidx ) { outbyte = (outbyte<> (n-bitidx)) & ((1u< 0 ) { bitidx -= n; outbyte = val & ((1u<>= 1; /* select next bit */ bitidx--; if (bitidx == 0) /* 8 bit buffer full */ putbyte(); } #endif } /** write rightmost bit of val to outfile @param val value to write one bit of */ void OBitStream::put1bit(int val) { ++bitwritepos; outbyte = (outbyte<<1) | (val & 0x1); --bitidx; if (bitidx == 0) /* 8 bit buffer full */ putbyte(); } /** zero bit stuffing to next byte boundary (5.2.3, 6.2.1) */ void OBitStream::alignbits() { if (bitidx != 8) putbits( 0, bitidx); flush_bytes(); } #endif /* * Local variables: * c-file-style: "stroustrup" * tab-width: 4 * indent-tabs-mode: nil * End: */