gstreamer/ext/mplex/bits.cc

359 lines
8 KiB
C++
Raw Normal View History

/** @file bits.cc, bit-level output */
/* Copyright (C) 2001, Andrew Stevens <andrew.stevens@philips.com> *
*
* 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 <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/param.h>
#include <assert.h>
#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:
*/