/*!
* COPYRIGHT (C) 2020 Emeric Grange - All Rights Reserved
*
* This file is part of MiniVideo.
*
* MiniVideo 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 3 of the License, or
* (at your option) any later version.
*
* MiniVideo 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 MiniVideo. If not, see .
*
* \author Emeric Grange
* \date 2012
*/
// minivideo headers
#include "bitstream.h"
#include "minitraces.h"
#include "minivideo_typedef.h"
#include "import.h"
#include "minivideo_mediafile.h"
// C POSIX library
#ifndef _MSC_VER
#include
#endif
// C standard libraries
#include
#include
#include
#include
#include "minitraces.h"
/* ************************************************************************** */
/*!
* \brief Default size for the bitstream data buffer memory cache.
*/
#define DEFAULT_BUFFER_SIZE 1024
/* ************************************************************************** */
/*!
* \brief init_bitstream0
* \param media
* \param bitstream_offset
* \param buffer_size
* \return
*/
Bitstream_t *init_bitstream0(MediaFile_t *media, int64_t bitstream_offset, uint32_t buffer_size)
{
TRACE_INFO(BITS, " " BLD_BLUE "init_bitstream()" CLR_RESET);
Bitstream_t *bitstr = NULL;
if (media == NULL ||
media->file_pointer == NULL)
{
TRACE_ERROR(BITS, " Unable to use MediaFile_t structure!");
}
else
{
// Bitstream structure allocation
bitstr = (Bitstream_t*)calloc(1, sizeof(Bitstream_t));
if (bitstr == NULL)
{
TRACE_ERROR(BITS, " Unable to allocate bitstream structure!");
}
else
{
// Bitstream structure initialization
bitstr->bitstream_file = media->file_pointer;
bitstr->bitstream_size = media->file_size;
bitstr->bitstream_offset = 0;
if (bitstream_offset > 0)
bitstr->bitstream_offset = bitstream_offset;
bitstr->buffer = NULL;
bitstr->buffer_offset = 0;
if (buffer_size > 0)
{
bitstr->buffer_size_saved = buffer_size;
bitstr->buffer_size = buffer_size;
}
else
{
bitstr->buffer_size_saved = DEFAULT_BUFFER_SIZE;
bitstr->buffer_size = DEFAULT_BUFFER_SIZE;
}
// Bitstream buffer allocation
bitstr->buffer = (uint8_t*)calloc(bitstr->buffer_size_saved, sizeof(uint8_t));
if (bitstr->buffer == NULL)
{
TRACE_ERROR(BITS, " Unable to allocate the bitstream buffer!");
free(bitstr);
bitstr = NULL;
}
else
{
// Initial buffer filling
buffer_feed_dynamic(bitstr, bitstr->bitstream_offset);
}
}
}
// Return the bitstream structure pointer
return bitstr;
}
/* ************************************************************************** */
/*!
* \brief Init a new bitstream.
* \param *media: A pointer to a MediaFile_t structure, containing every informations available about the current media file.
* \param *stream: A pointer to a MediaStream_t structure, containing informations about video payload data.
* \return *bitstr: A pointer to our newly allocated bitstream structure.
*
* If no MediaStream_t is available, it mean we have continuous video data,
* starting at byte offset 0 and until the end of file.
* Otherwise, an available MediaStream_t structure mean that we have encapsulated
* video data, and we must bufferize data sample by sample.
*/
Bitstream_t *init_bitstream(MediaFile_t *media)
{
TRACE_INFO(BITS, " " BLD_BLUE "init_bitstream()" CLR_RESET);
Bitstream_t *bitstr = NULL;
if (media == NULL ||
media->file_pointer == NULL)
{
TRACE_ERROR(BITS, " Unable to use MediaFile_t structure!");
}
else
{
// Bitstream structure allocation
bitstr = (Bitstream_t*)calloc(1, sizeof(Bitstream_t));
if (bitstr == NULL)
{
TRACE_ERROR(BITS, " Unable to calloc bitstream unit!");
}
else
{
// Bitstream structure initialization
bitstr->bitstream_file = media->file_pointer;
bitstr->bitstream_size = media->file_size;
bitstr->bitstream_offset = 0;
bitstr->buffer = NULL;
bitstr->buffer_size = DEFAULT_BUFFER_SIZE;
bitstr->buffer_size_saved = DEFAULT_BUFFER_SIZE;
bitstr->buffer_offset = 0;
// Bitstream buffer allocation
if ((bitstr->buffer = (uint8_t*)calloc(bitstr->buffer_size, sizeof(uint8_t))) == NULL)
{
TRACE_ERROR(BITS, " Unable to calloc the bitstream buffer!");
free(bitstr);
bitstr = NULL;
}
else
{
// Rewind file pointer
rewind(bitstr->bitstream_file);
// Feed bitstream buffer
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
free(bitstr->buffer);
bitstr->buffer = NULL;
free(bitstr);
bitstr = NULL;
}
}
}
}
if (bitstr != NULL)
{
TRACE_1(BITS, " Bitstream initialization success");
}
else
{
TRACE_ERROR(BITS, " Bitstream initialization FAILED!");
}
// Return the bitstream structure pointer
return bitstr;
}
/* ************************************************************************** */
/*!
* \brief Feed the bitstream buffer with fresh data.
* \param *bitstr The bitstream to freed.
* \return 1 if success, 0 otherwise.
*
* This function is only used by the H.264 video decoder.
*/
int buffer_feed_manual(Bitstream_t *bitstr, int64_t bitstream_offset, int64_t size)
{
TRACE_INFO(BITS, " " BLD_BLUE "buffer_feed_manual()" CLR_RESET);
int retcode = SUCCESS;
// Print stats?
//TRACE_1(BITS, " Status before reallocation:");
//bitstream_print_stats(bitstr);
// Reset parameters
bitstr->buffer_offset = 0;
bitstr->buffer_discarded_bytes = 0;
// Deallocate buffer (if needed)
if (bitstr->buffer != NULL &&
bitstr->buffer_size != size)
{
free(bitstr->buffer);
bitstr->buffer = NULL;
}
// Allocate new buffer (if needed)
if (bitstr->buffer == NULL)
{
bitstr->buffer_size = size;
bitstr->buffer = (uint8_t *)malloc(bitstr->buffer_size);
}
if (bitstr->buffer == NULL)
{
TRACE_ERROR(BITS, " Unable to realloc bitstream buffer!");
retcode = FAILURE;
}
else
{
// Move file pointer
bitstr->bitstream_offset = bitstream_offset;
if (fseek(bitstr->bitstream_file, bitstr->bitstream_offset, SEEK_SET) != 0)
{
TRACE_ERROR(BITS, " Unable to seek through the input file!");
retcode = FAILURE;
}
else
{
// Feed buffer
if (fread(bitstr->buffer, sizeof(uint8_t), bitstr->buffer_size, bitstr->bitstream_file) != bitstr->buffer_size)
{
TRACE_ERROR(BITS, " Unable to read from the input file!");
retcode = FAILURE;
}
else
{
TRACE_1(BITS, " Bitstream buffer reallocation succeed!");
retcode = SUCCESS;
}
}
// Print stats?
//TRACE_1(BITS, " Status after reallocation:");
//bitstream_print_stats(bitstr);
}
return retcode;
}
/* ************************************************************************** */
/*!
* \brief Feed the bitstream buffer with fresh data.
* \param *bitstr The bitstream to freed.
* \param new_bitstream_offset The byte offset of the data we want to bufferize.
* \return 1 if success, 0 otherwise.
*
* If the bitstream_offset is a negative value, just load the data following the
* data from the current buffer.
* Otherwise, load data starting at the given new_bitstream_offset.
*/
int buffer_feed_dynamic(Bitstream_t *bitstr, int64_t new_bitstream_offset)
{
TRACE_INFO(BITS, " " BLD_BLUE "buffer_feed_dynamic()" CLR_RESET);
int retcode = FAILURE;
// Print stats?
//TRACE_1(BITS, " Status before reallocation:");
//bitstream_print_stats(bitstr);
// Update current offset into the bitstream
if (new_bitstream_offset > -1)
{
bitstr->bitstream_offset = new_bitstream_offset;
bitstr->buffer_offset = 0;
}
else
{
bitstr->bitstream_offset += (int64_t)(bitstr->buffer_offset/8) + bitstr->buffer_discarded_bytes;
bitstr->buffer_offset %= 8;
}
// Check for premature end of file
if (bitstr->bitstream_offset >= bitstr->bitstream_size)
{
TRACE_ERROR(BITS, " Fatal error: premature end of file reached!");
retcode = FAILURE;
}
else
{
// Reset buffer size (necessary if some data have been dynamically removed from previous buffer)
bitstr->buffer_size += bitstr->buffer_discarded_bytes;
bitstr->buffer_discarded_bytes = 0;
// Cut buffer size if the end of file is almost reached
if ((bitstr->bitstream_offset + bitstr->buffer_size) > bitstr->bitstream_size)
{
unsigned int buffer_size_saved = bitstr->buffer_size;
bitstr->buffer_size = (unsigned int)(bitstr->bitstream_size - bitstr->bitstream_offset);
bitstr->buffer = (uint8_t*)realloc(bitstr->buffer, bitstr->buffer_size);
if (bitstr->buffer == nullptr)
{
TRACE_ERROR(BITS, " Unable to realloc bitstream buffer!");
retcode = FAILURE;
}
else
{
TRACE_1(BITS, " Bitstream buffer resized from %uB to %uB", buffer_size_saved, bitstr->buffer_size);
}
}
// Move file pointer
if (fseek(bitstr->bitstream_file, bitstr->bitstream_offset, SEEK_SET) != 0)
{
TRACE_ERROR(BITS, " Unable to seek through the input file!");
retcode = FAILURE;
}
else
{
// Feed buffer
size_t b = fread(bitstr->buffer, sizeof(uint8_t), bitstr->buffer_size, bitstr->bitstream_file);
if (b != bitstr->buffer_size)
{
TRACE_ERROR(BITS, " Unable to read from the input file! (%u read instead of %u)", b, bitstr->buffer_size);
retcode = FAILURE;
}
else
{
TRACE_1(BITS, " Bitstream buffer feeded!");
retcode = SUCCESS;
}
// Print stats?
//TRACE_1(BITS, " Status after reallocation:");
//bitstream_print_stats(bitstr);
}
}
return retcode;
}
/* ************************************************************************** */
/*!
* \brief Destroy a bitstream and it's buffer.
* \param **bitstr_ptr The bitstream to freed.
*
* This function do not freed MediaFile_t structure.
*/
void free_bitstream(Bitstream_t **bitstr_ptr)
{
TRACE_INFO(BITS, " " BLD_BLUE "free_bitstream()" CLR_RESET);
if (*bitstr_ptr != NULL)
{
if ((*bitstr_ptr)->buffer != NULL)
{
free((*bitstr_ptr)->buffer);
(*bitstr_ptr)->buffer = NULL;
TRACE_1(BITS, " Bitstream buffer freed");
}
{
free(*bitstr_ptr);
*bitstr_ptr = NULL;
TRACE_1(BITS, " Bitstream freed");
}
}
}
/* ************************************************************************** */
/* ************************************************************************** */
/*!
* \brief Read 1 bit from a bitstream, return it then move the bitstream position.
* \param *bitstr The bitstream to read.
* \return bit The bit read from the bitstream.
*
* First step is to check if reading 1 bit is possible, then if there is 1 bit
* left in the bitstream buffer.
*/
uint32_t read_bit(Bitstream_t *bitstr)
{
TRACE_3(BITS, " " BLD_BLUE "read_bit()" CLR_RESET " starting at bit offset %lli", bitstream_get_absolute_bit_offset(bitstr));
uint32_t start_byte = (uint32_t)(bitstr->buffer_offset / 8);
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if (start_byte >= bitstr->buffer_size)
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
start_byte = (uint32_t)(bitstr->buffer_offset / 8);
}
// Read one bit
////////////////////////////////////////////////////////////////////////
uint32_t bp = 7 - (uint32_t)(bitstr->buffer_offset % 8); // back padding, in bit
uint32_t bit = (uint32_t)(bitstr->buffer[start_byte] >> bp);
bit &= 0x01;
// Update bit offset
bitstr->buffer_offset++;
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " bit : %i", bit);
return bit;
}
/* ************************************************************************** */
int read_chunk(Bitstream_t* bitstr, uint8_t* chunk, const uint32_t size) {
uint32_t nread = 0;
while (nread != size) {
uint32_t byte_current = bitstr->buffer_offset / 8;
uint32_t buffer_size = bitstr->buffer_size - byte_current;
if (size - nread >= buffer_size) {
memcpy(chunk + nread,
bitstr->buffer + byte_current,
buffer_size);
nread += buffer_size;
bitstr->buffer_offset += buffer_size * 8;
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
}
else {
memcpy(chunk + nread,
bitstr->buffer + byte_current,
size - nread);
bitstr->buffer_offset += (size - nread) * 8;
nread += size - nread;
}
}
return 1;
}
/*!
* \brief Read n bit(s) from a bitstream, return it then move the bitstream position.
* \param *bitstr The bitstream to read.
* \param n The number of bits to read, up to 32.
* \return bits The content read from the bitstream.
*
* First step is to check if reading n bits is possible, then if there is n bits
* left in the bitstream buffer.
*/
uint32_t read_bits(Bitstream_t *bitstr, const unsigned int n)
{
TRACE_3(BITS, " " BLD_BLUE "read_bits(%u)" CLR_RESET " starting at bit offset %lli", n, bitstream_get_absolute_bit_offset(bitstr));
uint32_t bits = 0;
uint32_t fp = (uint32_t)(bitstr->buffer_offset % 8); // front padding, in bit
uint32_t byte_current = (uint32_t)floor(bitstr->buffer_offset / 8.0);
uint32_t tbr = (uint32_t)ceil((n + fp) / 8.0);
uint32_t tbr_current = tbr;
// Debug traces
////////////////////////////////////////////////////////////////////////
#if ENABLE_DEBUG
TRACE_2(BITS, " n : %u", n);
TRACE_2(BITS, " fp : %u", fp);
TRACE_2(BITS, " bo : %u", bitstr->buffer_offset);
TRACE_2(BITS, " start : %u", byte_current);
TRACE_2(BITS, " tbr : %u", tbr);
// Check if we can read n bits
////////////////////////////////////////////////////////////////////////
if (n == 0)
{
TRACE_WARNING(BITS, "This function cannot read 0 bits!");
return FAILURE;
}
else if (n > 32)
{
TRACE_WARNING(BITS, "You want to read %i bits, but this function can only read up to 32 bits!", n);
return FAILURE;
}
#endif // ENABLE_DEBUG
if (n == 1)
{
return read_bit(bitstr);
}
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if ((byte_current + tbr) > bitstr->buffer_size)
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
fp = bitstr->buffer_offset % 8; // front padding, in bit
byte_current = (uint32_t)floor(bitstr->buffer_offset / 8.0);
tbr = (uint32_t)ceil((n + fp) / 8.0);
tbr_current = tbr;
}
// Read
////////////////////////////////////////////////////////////////////////
if (fp > 0)
{
// Read un-aligned bits
bits += bitstr->buffer[byte_current++];
bits &= 0xFF >> fp;
tbr_current--;
while (tbr_current > 0)
{
if (tbr > 4 &&
tbr_current == 1)
{
bits <<= fp;
bits += bitstr->buffer[byte_current++] & (0xFF >> fp);
}
else
{
bits <<= 8;
bits += bitstr->buffer[byte_current++];
}
tbr_current--;
}
bits >>= ((tbr*8) - n - fp);
}
else
{
// Read aligned bits
while (tbr_current > 0)
{
bits <<= 8;
bits += bitstr->buffer[byte_current++];
tbr_current--;
}
bits >>= (32 - n) % 8;
}
// Update bit offset
bitstr->buffer_offset += n;
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " content = 0d%u", bits);
TRACE_2(BITS, " content = 0x%08X", bits);
return bits;
}
/* ************************************************************************** */
/*!
* \brief Read n bit(s) from a bitstream, return it then move the bitstream position.
* \param *bitstr The bitstream to read.
* \param n The number of bits to read, up to 64.
* \return bits The content read from the bitstream.
*
* First step is to check if reading n bits is possible, then if there is n bits
* left in the bitstream buffer.
*/
uint64_t read_bits_64(Bitstream_t *bitstr, const unsigned int n)
{
TRACE_3(BITS, " " BLD_BLUE "read_bits_64(%u)" CLR_RESET " starting at bit offset %lli", n, bitstream_get_absolute_bit_offset(bitstr));
uint64_t bits = 0;
uint32_t fp = (uint32_t)(bitstr->buffer_offset % 8); // front padding, in bit
uint32_t byte_current = (uint32_t)floor(bitstr->buffer_offset / 8.0);
uint32_t tbr = (uint32_t)ceil((n + fp) / 8.0);
uint32_t tbr_current = tbr;
// Debug traces
////////////////////////////////////////////////////////////////////////
#if ENABLE_DEBUG
TRACE_2(BITS, " n : %u", n);
TRACE_2(BITS, " fp : %u", fp);
TRACE_2(BITS, " bo : %u", bitstr->buffer_offset);
TRACE_2(BITS, " start : %u", byte_current);
TRACE_2(BITS, " tbr : %u", tbr);
// Check if we can read n bits
////////////////////////////////////////////////////////////////////////
if (n == 0)
{
TRACE_WARNING(BITS, "This function cannot read 0 bits!");
return FAILURE;
}
else if (n > 64)
{
TRACE_WARNING(BITS, "You want to read %i bits, but this function can only read up to 64 bits!", n);
return FAILURE;
}
#endif // ENABLE_DEBUG
if (n == 1)
{
return read_bit(bitstr);
}
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if ((byte_current + tbr) > bitstr->buffer_size)
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
fp = (uint32_t)(bitstr->buffer_offset % 8); // front padding, in bit
byte_current = (uint32_t)floor(bitstr->buffer_offset / 8.0);
tbr = (uint32_t)ceil((n + fp) / 8.0);
tbr_current = tbr;
}
// Read
////////////////////////////////////////////////////////////////////////
if (fp > 0)
{
// Read un-aligned bits
bits += bitstr->buffer[byte_current++];
bits &= 0xFF >> fp;
tbr_current--;
while (tbr_current > 0)
{
if (tbr > 4 &&
tbr_current == 1)
{
bits <<= fp;
bits += bitstr->buffer[byte_current++] & (0xFF >> fp);
}
else
{
bits <<= 8;
bits += bitstr->buffer[byte_current++];
}
tbr_current--;
}
bits >>= ((tbr*8) - n - fp);
}
else
{
// Read aligned bits
while (tbr_current > 0)
{
bits <<= 8;
bits += bitstr->buffer[byte_current++];
tbr_current--;
}
bits >>= (64 - n) % 8;
}
// Update bit offset
bitstr->buffer_offset += n;
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " content = 0d%LLu", bits);
TRACE_2(BITS, " content = 0x%16X", bits);
return bits;
}
/* ************************************************************************** */
/* ************************************************************************** */
/*!
* \brief Read 1 byte from a bitstream, return it, then move the bitstream position.
* \param *bitstr The bitstream to read.
* \return bit The bit read from the bitstream.
*
* First step is to check if reading one byte is possible, if there is 8 bits
* left in the bitstream buffer, then move the bitstream offset.
*/
uint32_t read_byte_aligned(Bitstream_t *bitstr)
{
TRACE_3(BITS, " " BLD_BLUE "read_byte_aligned()" CLR_RESET " starting at bit offset %lli", bitstream_get_absolute_bit_offset(bitstr));
uint32_t byte = 0;
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if ((bitstr->buffer_offset + 8) > (bitstr->buffer_size * 8))
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
}
// Check byte alignment
////////////////////////////////////////////////////////////////////////
#if ENABLE_DEBUG
if ((bitstr->buffer_offset % 8) != 0)
{
TRACE_ERROR(BITS, " " BLD_BLUE "read_byte_aligned() on unaligned offset" CLR_RESET " at current byte offset %lli + %i bit(s)", bitstream_get_absolute_byte_offset(bitstr), bitstream_get_absolute_bit_offset(bitstr)%8);
return FAILURE;
}
#endif // ENABLE_DEBUG
// Read one byte
////////////////////////////////////////////////////////////////////////
byte = (uint32_t)(bitstr->buffer[bitstr->buffer_offset / 8]);
// Update bit offset
bitstr->buffer_offset += 8;
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " byte : %02X", byte);
return byte;
}
/* ************************************************************************** */
/*!
* \brief Read one byte from a bitstream and return it, but DO NOT move the bitstream position.
* \param *bitstr The bitstream to read.
* \return bit The bit read from the bitstream.
*
* This function is basically the same as read_byte_aligned() but it DO NOT move
* the buffer position after reading one byte.
*/
uint32_t next_byte_aligned(Bitstream_t *bitstr)
{
TRACE_3(BITS, " " BLD_BLUE "next_byte_aligned()" CLR_RESET " starting at bit offset %lli", bitstream_get_absolute_bit_offset(bitstr));
uint32_t byte = 0;
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if ((bitstr->buffer_offset + 8) > (bitstr->buffer_size * 8))
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
}
// Check byte alignment
////////////////////////////////////////////////////////////////////////
#if ENABLE_DEBUG
if ((bitstr->buffer_offset % 8) != 0)
{
TRACE_ERROR(BITS, " " BLD_BLUE "read_byte_aligned() on unaligned offset" CLR_RESET " at current byte offset %lli + %i bit(s)", bitstream_get_absolute_byte_offset(bitstr), bitstream_get_absolute_bit_offset(bitstr)%8);
return FAILURE;
}
#endif // ENABLE_DEBUG
// Read one byte
////////////////////////////////////////////////////////////////////////
byte = (uint32_t)(bitstr->buffer[bitstr->buffer_offset / 8]);
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " byte : %02X", byte);
return byte;
}
/* ************************************************************************** */
/* ************************************************************************** */
/*!
* \brief Read one bit from a bitstream and return it, but DO NOT move the bitstream position.
* \param *bitstr The bitstream to read.
* \return bit The bit read from the bitstream.
*
* This function is basically the same as read_bit() but it DO NOT move the
* buffer position after reading one bit.
*/
uint32_t next_bit(Bitstream_t *bitstr)
{
TRACE_3(BITS, " " BLD_BLUE "next_bit()" CLR_RESET " starting at bit offset %lli", bitstream_get_absolute_bit_offset(bitstr));
uint32_t start_byte = (uint32_t)(bitstr->buffer_offset / 8);
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if (start_byte >= bitstr->buffer_size)
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
start_byte = (uint32_t)(bitstr->buffer_offset / 8);
}
// Read one bit
////////////////////////////////////////////////////////////////////////
uint32_t bp = 7 - (uint32_t)(bitstr->buffer_offset % 8); // back padding, in bit
uint32_t bit = (uint32_t)(bitstr->buffer[start_byte] >> bp);
bit &= 0x01;
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " bit = %i", bit);
return bit;
}
/* ************************************************************************** */
/*!
* \brief Read n bit(s) from a bitstream and return it, but DO NOT move the bitstream position.
* \param *bitstr The bitstream to read.
* \param n The number of bit(s) to read. Up to 32 bits.
* \return bits The content read from the bitstream.
*
* This function is basically the same as read_bits() but it DO NOT move the
* buffer position after reading n bits.
*/
uint32_t next_bits(Bitstream_t *bitstr, const unsigned int n)
{
TRACE_3(BITS, " " BLD_BLUE "next_bits(%u)" CLR_RESET " starting at bit offset %lli", n, bitstream_get_absolute_bit_offset(bitstr));
uint32_t bits = 0;
uint32_t fp = (uint32_t)(bitstr->buffer_offset % 8); // front padding, in bit
uint32_t byte_current = (uint32_t)floor(bitstr->buffer_offset / 8.0);
uint32_t tbr = (uint32_t)ceil((n + fp) / 8.0);
uint32_t tbr_current = tbr;
// Debug traces
////////////////////////////////////////////////////////////////////////
#if ENABLE_DEBUG
TRACE_2(BITS, " n : %u", n);
TRACE_2(BITS, " fp : %u", fp);
TRACE_2(BITS, " bo : %u", bitstr->buffer_offset);
TRACE_2(BITS, " start : %u", byte_current);
TRACE_2(BITS, " tbr : %u", tbr);
// Check if we can read n bits
////////////////////////////////////////////////////////////////////////
if (n == 0)
{
TRACE_WARNING(BITS, "This function cannot read 0 bits!");
return FAILURE;
}
else if (n > 32)
{
TRACE_WARNING(BITS, "You want to read %i bits, but this function can only read up to 32 bits!", n);
return FAILURE;
}
#endif // ENABLE_DEBUG
if (n == 1)
{
return next_bit(bitstr);
}
// Fill new data into the bitstream buffer, if needed
////////////////////////////////////////////////////////////////////////
if ((byte_current + tbr) > bitstr->buffer_size)
{
if (buffer_feed_dynamic(bitstr, -1) == FAILURE)
{
return FAILURE;
}
fp = (uint32_t)(bitstr->buffer_offset % 8); // front padding, in bit
byte_current = (uint32_t)floor(bitstr->buffer_offset / 8.0);
tbr = (uint32_t)ceil((n + fp) / 8.0);
tbr_current = tbr;
}
// Read
////////////////////////////////////////////////////////////////////////
if (fp > 0)
{
// Read un-aligned bits
bits += bitstr->buffer[byte_current++];
bits &= 0xFF >> fp;
tbr_current--;
while (tbr_current > 0)
{
if (tbr > 4 &&
tbr_current == 1)
{
bits <<= fp;
bits += bitstr->buffer[byte_current++] & (0xFF >> fp);
}
else
{
bits <<= 8;
bits += bitstr->buffer[byte_current++];
}
tbr_current--;
}
bits >>= ((tbr*8) - n - fp);
}
else
{
// Read aligned bits
while (tbr_current > 0)
{
bits <<= 8;
bits += bitstr->buffer[byte_current];
byte_current++;
tbr_current--;
}
bits >>= (32 - n) % 8;
}
// Return result
////////////////////////////////////////////////////////////////////////
TRACE_2(BITS, " content = 0d%u", bits);
TRACE_2(BITS, " content = 0x%08X", bits);
return bits;
}
/* ************************************************************************** */
/* ************************************************************************** */
/*!
* \brief Skip n bits in a bitstream.
* \param *bitstr The bitstream to use.
* \param n The number of bits to skip.
* \return 1 if success, 0 otherwise.
*
* skip_bits() is designed and optimized to handle small bit jumps INSIDE the bitstream buffer.
*
* However:
* - If jumping 'n' bits takes us outside of the remaining bitstream buffer space,
* bitstream_goto_offset() will be used (triggering a buffer refresh).
* - If your jump is bigger than the bitsteam buffer size, save some time and use
* bitstream_goto_offset() directy.
* - If your jump is bigger than 2^32 bits, don't trigger an integer overflow
* and use bitstream_goto_offset() directy.
*/
int skip_bits(Bitstream_t *bitstr, const unsigned int n)
{
int retcode = FAILURE;
// Check if destination is outside the current buffer
if ((bitstr->buffer_offset + n) > (bitstr->buffer_size * 8))
{
// If it is, check if its in the next one
if (n < (bitstr->buffer_size * 8))
{
// Refresh buffer
retcode = buffer_feed_dynamic(bitstr, -1);
// Skip n bits
bitstr->buffer_offset += n;
}
else // Or somewhere else entierly
{
int64_t new_bit_offset = (int64_t)(bitstr->bitstream_offset*8 + bitstr->buffer_offset + n);
// Do not jump to the last byte of the file?
//if (new_bit_offset/8 >= bitstr->bitstream_size)
{
// Reload bitstream buffer from the byte offset we want
retcode = bitstream_goto_offset(bitstr, new_bit_offset/8);
// Then skip x bits ?
if ((new_bit_offset % 8) != 0)
{
bitstr->buffer_offset += new_bit_offset % 8;
}
}
}
}
else
{
// Skip n bits
bitstr->buffer_offset += n;
retcode = SUCCESS;
}
#if ENABLE_DEBUG
if (retcode == SUCCESS)
{
TRACE_2(BITS, " " BLD_BLUE "skip_bits(%u)" CLR_RESET " SUCCESS", n);
}
else
{
TRACE_ERROR(BITS, " " BLD_BLUE "skip_bits(%u)" CLR_RESET " Cannot skip bits at bit offset", n, bitstr->buffer_offset);
}
#endif // ENABLE_DEBUG
return retcode;
}
/* ************************************************************************** */
/*!
* \brief Rewind n bits in a bitstream.
* \param *bitstr The bitstream to use.
* \param n The number of bits to rewind.
* \return 1 if success, 0 otherwise.
*
* If rewinding is impossible due to the current buffer_offset being smaller
* than 'n', we do a jump directly to the offset we want. That will trigger a
* buffer refresh.
*/
int rewind_bits(Bitstream_t *bitstr, const unsigned int n)
{
int retcode = FAILURE;
// Check if rewinding inside the buffer is possible
if (n <= bitstr->buffer_offset)
{
// Rewind n bits
bitstr->buffer_offset -= n;
retcode = SUCCESS;
}
else
{
// Must reload previous data and go directly to the offset we want
int64_t new_offset = (int64_t)(bitstr->bitstream_offset*8 + bitstr->buffer_offset - n);
retcode = bitstream_goto_offset(bitstr, new_offset/8);
}
#if ENABLE_DEBUG
if (retcode == SUCCESS)
{
TRACE_2(BITS, " " BLD_BLUE "rewind_bits(%u)" CLR_RESET " SUCCESS", n);
}
else
{
TRACE_ERROR(BITS, " " BLD_BLUE "rewind_bits(%u)" CLR_RESET " Cannot rewind bits at bit offset %u", n, bitstr->buffer_offset);
}
#endif // ENABLE_DEBUG
return retcode;
}
/* ************************************************************************** */
/*!
* \brief Go to the n byte of a file, if possible.
* \param *bitstr The bitstream to use.
* \param n The position to go to, in byte.
* \return 1 if success, 0 otherwise.
*
* Note that it is NOT possible to jump to the last byte of the bitstream,
* because it would cause a buffer reload, and incidentally a "premature end
* of file" error.
*/
int bitstream_goto_offset(Bitstream_t *bitstr, const int64_t n)
{
int retcode = FAILURE;
if (n > 0 && n < bitstr->bitstream_size)
{
retcode = buffer_feed_dynamic(bitstr, n);
}
else
{
retcode = FAILURE;
}
#if ENABLE_DEBUG
if (retcode == SUCCESS)
{
TRACE_2(BITS, " " BLD_BLUE "bitstream_goto_offset(%lli)" CLR_RESET " Success", n);
}
else if (bitstream_get_absolute_byte_offset(bitstr) != n)
{
TRACE_ERROR(BITS, " " BLD_BLUE "bitstream_goto_offset() at %lli instead of %lli" CLR_RESET, bitstream_get_absolute_byte_offset(bitstr), n);
}
else
{
TRACE_ERROR(BITS, " " BLD_BLUE "bitstream_goto_offset(%lli)" CLR_RESET " Cannot jump outside bitstream boundaries!", n);
}
#endif // ENABLE_DEBUG
return retcode;
}
/* ************************************************************************** */
/* ************************************************************************** */
int64_t bitstream_get_full_size(Bitstream_t *bitstr)
{
return bitstr->bitstream_size;
}
/* ************************************************************************** */
/*!
* \brief Return the current byte offset in the bitstream (may left a few bits behind...).
* \param *bitstr The bitstream to use.
* \return The absolute byte offset into the bitstream.
*/
int64_t bitstream_get_absolute_byte_offset(Bitstream_t *bitstr)
{
return (int64_t)(bitstr->bitstream_offset + bitstr->buffer_offset/8 + bitstr->buffer_discarded_bytes);
}
/* ************************************************************************** */
/*!
* \brief Return the current absolute bit offset in the bitstream.
* \param *bitstr The bitstream to use.
* \return The absolute bit offset into the bitstream.
*
* Be careful of integer overflow if file is more than 134217728 GiB?
*/
int64_t bitstream_get_absolute_bit_offset(Bitstream_t *bitstr)
{
return (int64_t)(bitstr->bitstream_offset*8 + bitstr->buffer_offset + bitstr->buffer_discarded_bytes*8);
}
/* ************************************************************************** */