/** @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 #include #include #include #include #include #include "mjpeg_logging.h" #include "bits.hh" /// Initializes the bitstream, sets internal variables. // TODO: The buffer size should be set dynamically to sensible sizes. // BitStream::BitStream (): user_data (NULL) { totbits = 0LL; buffer_start = 0LL; eobs = true; readpos = 0LL; bfr = 0; bfr_size = 0; } /// Deconstructor. Deletes the internal buffer. BitStream::~BitStream () { delete bfr; } /** Refills an IBitStream's input buffer based on the internal variables bufcount and bfr_size. */ bool IBitStream::refill_buffer () { size_t i; if (bufcount >= bfr_size) { SetBufSize (bfr_size + 4096); } i = read_callback (this, bfr + bufcount, static_cast < size_t > (bfr_size - bufcount), user_data); bufcount += i; if (i == 0) { eobs = true; return false; } return true; } /** Flushes all read input up-to *but not including* bit unbuffer_upto. @param flush_to the number of bits to flush */ void IBitStream::flush (bitcount_t flush_upto) { if (flush_upto > buffer_start + bufcount) mjpeg_error_exit1 ("INTERNAL ERROR: attempt to flush input beyond buffered amount"); if (flush_upto < buffer_start) mjpeg_error_exit1 ("INTERNAL ERROR: attempt to flush input stream before first buffered byte %d last is %d", (int) flush_upto, (int) buffer_start); unsigned int bytes_to_flush = static_cast < unsigned int >(flush_upto - buffer_start); // // Don't bother actually flushing until a good fraction of a buffer // will be cleared. // if (bytes_to_flush < bfr_size * 3 / 4) return; bufcount -= bytes_to_flush; buffer_start = flush_upto; byteidx -= bytes_to_flush; memmove (bfr, bfr + bytes_to_flush, static_cast < size_t > (bufcount)); } /** 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 (BitStreamUndo & undo) { undo = *(static_cast < BitStreamUndo * >(this)); } /** Undoes changes committed to an IBitStream. @param undo handle to retrieve the undo information */ void IBitStream::undochanges (BitStreamUndo & undo) { *(static_cast < BitStreamUndo * >(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::read_buffered_bytes (uint8_t * dst, unsigned int length) { unsigned int to_read = length; if (readpos < buffer_start) mjpeg_error_exit1 ("INTERNAL ERROR: access to input stream buffer @ %d: before first buffered byte (%d)", (int) readpos, (int) buffer_start); if (readpos + length > buffer_start + bufcount) { /* if (!feof (fileh)) { mjpeg_error ("INTERNAL ERROR: access to input stream buffer beyond last buffered byte @POS=%lld END=%d REQ=%lld + %d bytes", readpos, bufcount, readpos - (bitcount_t) buffer_start, length); abort (); } */ to_read = static_cast < unsigned int >((buffer_start + bufcount) - readpos); } memcpy (dst, bfr + (static_cast < unsigned int >(readpos - buffer_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 (readpos); readpos += to_read; return to_read; } /** open the device to read the bit stream from it @param bs_filename filename to open @param buf_size size of the internal buffer */ void IBitStream::open (ReadCallback read_callback, void *user_data, unsigned int buf_size) { this->read_callback = read_callback; this->user_data = user_data; bfr_size = buf_size; if (bfr == NULL) bfr = new uint8_t[buf_size]; else { delete bfr; bfr = new uint8_t[buf_size]; } byteidx = 0; bitidx = 8; totbits = 0LL; bufcount = 0; eobs = false; if (!refill_buffer ()) { if (bufcount == 0) { mjpeg_error_exit1 ("Unable to read."); } } } /** sets the internal buffer size. @param new_buf_size the new internal buffer size */ void IBitStream::SetBufSize (unsigned int new_buf_size) { assert (bfr != NULL); // Must be open first! assert (new_buf_size >= bfr_size); // Can only be increased in size... if (bfr_size != new_buf_size) { uint8_t *new_buf = new uint8_t[new_buf_size]; memcpy (new_buf, bfr, static_cast < size_t > (bfr_size)); delete bfr; bfr_size = new_buf_size; bfr = new_buf; } } /** close the device containing the bit stream after a read process */ void IBitStream::close () { } // TODO replace with shift ops! uint8_t IBitStream::masks[8] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 }; /*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); totbits++; bitidx--; if (!bitidx) { bitidx = 8; byteidx++; if (byteidx == bufcount) { refill_buffer (); } } 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++; totbits += 8; if (byteidx == bufcount) { refill_buffer (); } i--; } } else { while (i > 0) { if (eobs) return 0; j = (bfr[byteidx] & masks[bitidx - 1]) >> (bitidx - 1); totbits++; bitidx--; if (!bitidx) { bitidx = 8; byteidx++; if (byteidx == bufcount) { refill_buffer (); } } 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::seek_sync (uint32_t sync, int N, int lim) { uint32_t val, val1; uint32_t maxi = ((1U << N) - 1); /* pow(2.0, (double)N) - 1 */ ; if (maxi == 0) { maxi = 0xffffffff; } while (bitidx != 8) { get1bit (); } val = getbits (N); if (eobs) return false; while ((val & maxi) != sync && --lim) { val <<= 8; val1 = getbits (8); val |= val1; if (eobs) return false; } return (!!lim); } /* * Local variables: * c-file-style: "stroustrup" * tab-width: 4 * indent-tabs-mode: nil * End: */