/*! * 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" // C POSIX library #ifndef _MSC_VER #include #endif // C standard libraries #include #include #include /* ************************************************************************** */ /*! * \brief Print various statistics about the current bitstream and its buffer. * \param *bitstr The bitstream to check. */ void bitstream_print_stats(Bitstream_t *bitstr) { TRACE_1(BITS, " bitstream offset (byte) : " BLD_BLUE "%lli" CLR_RESET " / " BLD_BLUE "%lli" CLR_RESET, bitstr->bitstream_offset, bitstr->bitstream_size); TRACE_1(BITS, " buffer offset (byte) : " BLD_BLUE "%u" CLR_RESET " / " BLD_BLUE "%u" CLR_RESET, bitstr->buffer_offset/8, bitstr->buffer_size); TRACE_1(BITS, " buffer offset (bit) : " BLD_BLUE "%u" CLR_RESET " / " BLD_BLUE "%u" CLR_RESET, bitstr->buffer_offset, bitstr->buffer_size*8); TRACE_1(BITS, " buffer offset % 8 (bit) : " BLD_BLUE "%u" CLR_RESET, bitstr->buffer_offset%8); TRACE_1(BITS, " buffer discarded byte : " BLD_BLUE "%u" CLR_RESET, bitstr->buffer_discarded_bytes); } /* ************************************************************************** */ /*! * \brief Print the bitstream buffer content. * \param *bitstr The bitstream to use. * * Print the bitstream buffer content, by chunk of 21*16 to be easily readable. */ void bitstream_print_buffer(Bitstream_t *bitstr) { TRACE_1(BITS, " " BLD_BLUE "bitstream_print_buffer()" CLR_RESET); unsigned int i = 0, j = 0; unsigned int row = 21, line = 16; while (i < (bitstr->buffer_size/row)) { for (j = 0; j < row; j++) { printf("%02X ", bitstr->buffer[j + i*row]); } if (i != 0 && (i % (line-1)) == 0) { printf("\n"); } printf("\n"); i++; } for (j = 0; j < (bitstr->buffer_size % row); j++) { printf("%02X ", bitstr->buffer[j + i*row]); } printf("\n"); } /* ************************************************************************** */ /*! * \brief Print the absolute byte offset into the bitstream. * \param *bitstr The bitstream to use. */ void bitstream_print_absolute_byte_offset(Bitstream_t *bitstr) { TRACE_INFO(BITS, " " BLD_BLUE "Current byte offset is %lli" CLR_RESET, bitstream_get_absolute_byte_offset(bitstr)); } /* ************************************************************************** */ /*! * \brief Print the absolute bit offset into the bitstream. * \param *bitstr The bitstream to use. */ void bitstream_print_absolute_bit_offset(Bitstream_t *bitstr) { TRACE_INFO(BITS, " " BLD_BLUE "Current bit offset is %lli" CLR_RESET, bitstream_get_absolute_bit_offset(bitstr)); } /* ************************************************************************** */ /* ************************************************************************** */ /*! * \brief Determine if the bitstream is on a byte boundary. * \param *bitstr The bitstream to check. * \return true if the bitstream is byte-aligned, false otherwise. */ bool bitstream_check_alignment(Bitstream_t *bitstr) { bool alignment = false; if ((bitstr->buffer_offset % 8) == 0) { TRACE_1(BITS, " " BLD_BLUE "Bitstream is aligned" CLR_RESET " at current byte offset %lli", bitstream_get_absolute_byte_offset(bitstr)); alignment = true; } else { TRACE_1(BITS, " " BLD_BLUE "Bitstream is NOT aligned" CLR_RESET " at current byte offset %lli + %i bit(s)", bitstream_get_absolute_byte_offset(bitstr), bitstream_get_absolute_bit_offset(bitstr)%8); } return alignment; } /* ************************************************************************** */ /*! * \brief Determine if the bitstream is on a byte boundary. * \param *bitstr The bitstream to align. * \return true if the bitstream has been aligned on byte boundarie, false otherwise. * * Warning: Up to 7 bits will be lost in the process. * The alignment is done forward, to avoid re-read some bits, and to avoid * backward buffer reallocation. */ bool bitstream_force_alignment(Bitstream_t *bitstr) { TRACE_1(BITS, " " BLD_BLUE "bitstream_force_alignment()" CLR_RESET); bool alignment = true; // Check if bitstream is already aligned if ((bitstr->buffer_offset % 8) != 0) { // Compute bit alignment int bit_alignment = 8 - (bitstr->buffer_offset % 8); TRACE_2(BITS, " +%i bit(s) alignment needed", bit_alignment); // Load next data if needed if ((bitstr->buffer_offset + bit_alignment) > (bitstr->buffer_size * 8)) { if (buffer_feed_dynamic(bitstr, -1) == FAILURE) { TRACE_ERROR(BITS, " Bitstream cannot be aligned"); alignment = false; } } else { // Stats bitstream_print_stats(bitstr); // Alignment operation bitstr->buffer_offset += bit_alignment; TRACE_1(BITS, " Bitstream now aligned: %i bit(s) offset applied", bit_alignment); } } return alignment; } /* ************************************************************************** */ /* ************************************************************************** */ /*! * \brief Determine if there is additional data in the bitstream. * \param *bitstr The bitstream to check. * \return true if at least one byte of data follow in the bitstream, false otherwise. * * This function do not read bitstream content, but just check if the current * offset into the bitstream is smaller than the bitstream size. * More than 8 bits must follow in order to return true. */ bool more_bitstream_data(Bitstream_t *bitstr) { TRACE_2(BITS, " " BLD_BLUE "more_bitstream_data()" CLR_RESET); bool retcode = true; //if (bitstr->sample_map == nullptr) //{ // if ((bitstr->bitstream_offset + (bitstr->buffer_offset / 8)) >= bitstr->bitstream_size) // { // TRACE_2(BITS, " No more data in the bitstream!"); // retcode = false; // } //} //else //{ // if ((uint32_t)bitstr->sample_index == bitstr->sample_map->sample_count) // { // if ((bitstr->buffer_size - bitstr->buffer_offset) < 8) // { // TRACE_2(BITS, " No more data in the bitstream!"); // retcode = false; // } // } //} return retcode; } /* ************************************************************************** */ /* ************************************************************************** */ /*! * \brief h264_rbsp_trailing_bits * \param *bitstr The bitstream to check. * \return true everything went as expected, false otherwise. * * More informations available at 7.3.2.11 'RBSP trailing bits syntax' of H.264 standard. */ bool h264_rbsp_trailing_bits(Bitstream_t *bitstr) { TRACE_3(BITS, " " BLD_BLUE "h264_rbsp_trailing_bits()" CLR_RESET); bool retcode = true; if (read_bit(bitstr) == 1) // rbsp_stop_one_bit { while (bitstream_check_alignment(bitstr) == false) { if (read_bit(bitstr) != 0) // rbsp_alignment_zero_bit { retcode = false; break; } } } else { retcode = false; } return retcode; } /* ************************************************************************** */ /*! * \brief Determine if there is additional data in the RBSP. * \param *bitstr The bitstream to check. * \return true if more data have been found in the RBSP, false otherwise. * * This function check for the immediate presence of a start_code_prefix, then * for a trailing bits structure (1 rbsp_stop_one_bit + less than 7 rbsp_alignment_zero_bit). * In this function trailing bits are NOT considered as data. * * More informations available at 7.4.1 'NAL unit semantics' of H.264 standard. */ bool h264_more_rbsp_data(Bitstream_t *bitstr) { TRACE_3(BITS, " " BLD_BLUE "h264_more_rbsp_data()" CLR_RESET); bool retcode = true; //if (bitstr->sample_map == nullptr) //{ // if (more_bitstream_data(bitstr)) // { // // Load next data if needed // if ((bitstr->buffer_offset + 48) > (bitstr->buffer_size * 8)) // { // if (buffer_feed_dynamic(bitstr, -1) == FAILURE) // { // return FAILURE; // } // } // // Save current position // unsigned int buffer_offset_saved = bitstr->buffer_offset; // // Try to find a start_code_prefix in the next 6 byte, which would mean that we are near the end of the NAL Unit // unsigned int current_byte = 0; // unsigned int search_window = 6; // unsigned int startcode_pos = 0; // unsigned int startcode_size = 0; // bitstream_force_alignment(bitstr); // while (startcode_pos == 0 && search_window > 0) // { // current_byte = read_byte_aligned(bitstr); // if (current_byte == 0) // { // startcode_size++; // } // else if ((startcode_size > 1) && (current_byte == 0x01)) // { // startcode_size++; // startcode_pos = bitstream_get_absolute_byte_offset(bitstr) - startcode_size; // TRACE_3(BITS, " %uB start_code_prefix found at byte offset %u", startcode_size, startcode_pos); // } // else // { // startcode_size = 0; // } // search_window--; // } // // Rewind to original position // bitstr->buffer_offset = buffer_offset_saved; // // Try to find trailing bits // if (startcode_pos > 0) // { // // Count bits left in NAL Unit // unsigned int bits_left = startcode_pos*8 - buffer_offset_saved; // if (bits_left < 8) // { // TRACE_3(BITS, " %u bits left in current NAL Unit! Checking for trailing data.", bits_left); // // Count trailing bits // if (next_bits(bitstr, bits_left) == 1) // or == (pow(2, bits_left) -1) ?? // { // TRACE_3(BITS, " These bits are trailing bits: no more useful data in current NAL Unit"); // retcode = false; // } // } // else // { // TRACE_3(BITS, " %u bits left in current NAL Unit! No need to check for trailing data", bits_left); // } // } // } // else // { // retcode = false; // } //} //else //{ // // Count bits left in NAL Unit // unsigned int bits_left = bitstr->buffer_size*8 - bitstr->buffer_offset; // if (bits_left < 8) // { // if (bits_left == 0) // { // retcode = false; // } // else // Count trailing bits // { // TRACE_3(BITS, " %u bits left in current NAL Unit! Checking for trailing data.", bits_left); // if (next_bits(bitstr, bits_left) == 1) // or == (pow(2, bits_left) -1) ?? // { // TRACE_3(BITS, " These bits are trailing bits: no more useful data in current NAL Unit"); // retcode = false; // } // } // } // else // { // TRACE_3(BITS, " %u bits left in current NAL Unit! No need to check for trailing data", bits_left); // } //} return retcode; } /* ************************************************************************** */ /*! * \brief Determine if there is additional data or trailing data in the RBSP. * \param *bitstr The bitstream to check. * \return true if more data have been found, false otherwise. * * This function check for the immediate presence of a start_code_prefix. If there * is no 3 or 4 bytes start_code_prefix, we can conclude that there is more data * left in the RBSP. * In this function trailing bits are considered as data. * * More informations available at 7.4.1 'NAL unit semantics' of H.264 standard. */ bool h264_more_rbsp_trailing_data(Bitstream_t *bitstr) { TRACE_3(BITS, " " BLD_BLUE "h264_more_rbsp_trailing_data()" CLR_RESET); bool retcode = true; //if (bitstr->sample_map == nullptr) //{ // if (bitstream_check_alignment(bitstr)) // { // if (next_bits(bitstr, 24) == 0x000001 && next_bits(bitstr, 32) == 0x00000001) // { // TRACE_3(BITS, " No more data or trailing data in rbsp"); // retcode = false; // } // } //} //else //{ // if (bitstr->buffer_offset >= bitstr->buffer_size*8) // { // TRACE_3(BITS, " No more data or trailing data in rbsp"); // retcode = false; // } //} return retcode; } /* ************************************************************************** */ /* ************************************************************************** */ /*! * \brief flip endianness variable content. * \param src The source variable to flip. * \return The destination variable, with flipped endianness. * * Only useful for variable bigger than 1 byte! */ uint16_t endian_flip_16(uint16_t src) { TRACE_3(BITS, " endian_flip_16()"); return ( ((src & 0x00FF) << 8) | ((src & 0xFF00) >> 8) ); } /* ************************************************************************** */ /*! * \brief flip endianness variable content. * \param src The source variable to flip. * \param n The number size in bits. * \return The destination variable, with flipped endianness. * * Only useful for variable bigger than 1 byte! */ uint16_t endian_flip_cut_16(uint16_t src, const int n) { TRACE_3(BITS, " endian_flip_cut_16()"); if (n > 0 && n < 16) { return ( ((src & 0x00FF) << 8) | ((src & 0xFF00) >> 8) ) >> (16 - n); } else { return src; } } /* ************************************************************************** */ /*! * \brief flip endianness variable content. * \param src The source variable to flip. * \return The destination variable, with flipped endianness. * * Only useful for variable bigger than 1 byte! */ uint32_t endian_flip_32(uint32_t src) { TRACE_3(BITS, " endian_flip_32()"); return ( ((src & 0x000000FF) << 24) | ((src & 0x0000FF00) << 8) | ((src & 0x00FF0000) >> 8) | ((src & 0xFF000000) >> 24) ); } /* ************************************************************************** */ /*! * \brief flip endianness variable content. * \param src The source variable to flip. * \param n The size in bits of the wanted value. * \return The destination variable, with flipped endianness. * * Only useful for variable bigger than 1 byte! */ uint32_t endian_flip_cut_32(uint32_t src, const int n) { TRACE_3(BITS, " endian_flip_cut_32()"); if (n > 0 && n < 32) { return ( ((src & 0x000000FF) << 24) | ((src & 0x0000FF00) << 8) | ((src & 0x00FF0000) >> 8) | ((src & 0xFF000000) >> 24) ) >> (32 - n); } else { return src; } } /* ************************************************************************** */ /*! * \brief flip endianness variable content. * \param src The source variable to flip. * \return The destination variable, with flipped endianness. * * Only useful for variable bigger than 1 byte! */ uint64_t endian_flip_64(uint64_t src) { TRACE_3(BITS, " endian_flip_64()"); return ( ((src & 0x00000000000000FFULL) << 56) | ((src & 0x000000000000FF00ULL) << 40) | ((src & 0x0000000000FF0000ULL) << 24) | ((src & 0x00000000FF000000ULL) << 8) | ((src & 0x000000FF00000000ULL) >> 8) | ((src & 0x0000FF0000000000ULL) >> 24) | ((src & 0x00FF000000000000ULL) >> 40) | ((src & 0xFF00000000000000ULL) >> 56) ); } /* ************************************************************************** */ /*! * \brief flip endianness variable content. * \param src The source variable to flip. * \param n The size in bits of the wanted value. * \return The destination variable, with flipped endianness. * * Only useful for variable bigger than 1 byte! */ uint64_t endian_flip_cut_64(uint64_t src, const int n) { TRACE_3(BITS, " endian_flip_cut_64()"); if (n > 0 && n < 64) { return ( ((src & 0x00000000000000FFULL) << 56) | ((src & 0x000000000000FF00ULL) << 40) | ((src & 0x0000000000FF0000ULL) << 24) | ((src & 0x00000000FF000000ULL) << 8) | ((src & 0x000000FF00000000ULL) >> 8) | ((src & 0x0000FF0000000000ULL) >> 24) | ((src & 0x00FF000000000000ULL) >> 40) | ((src & 0xFF00000000000000ULL) >> 56) ) >> (64 - n); } else { return src; } } /* ************************************************************************** */