/*! * 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); } /* ************************************************************************** */