diff options
Diffstat (limited to 'gpr/source/lib/vc5_common/bitstream.c')
-rwxr-xr-x | gpr/source/lib/vc5_common/bitstream.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/gpr/source/lib/vc5_common/bitstream.c b/gpr/source/lib/vc5_common/bitstream.c new file mode 100755 index 0000000..cef4312 --- /dev/null +++ b/gpr/source/lib/vc5_common/bitstream.c @@ -0,0 +1,445 @@ +/*! @file bitstream.c + * + * @brief Implementation of a bitstream for reading bits from a byte stream. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/*! + @brief Return a mask with the specified number of bits set to one + */ +BITWORD BitMask(int n) +{ + if (n < bit_word_count) + { + BITWORD mask = 0; + if (n > 0) { + mask = ((1 << n) - 1); + } + return mask; + } + return BIT_WORD_MAX; +} + +/*! + @brief Initialize a bitstream data structure + + This routine is the constructor for the bitstream data type. + + The sample offset stack is used to mark the offset to a position + in the bitstream for computing the size field of sample chunks. + */ +CODEC_ERROR InitBitstream(BITSTREAM *bitstream) +{ + if (bitstream != NULL) + { + bitstream->error = BITSTREAM_ERROR_OKAY; + bitstream->stream = NULL; + bitstream->buffer = 0; + bitstream->count = 0; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + // Initialize the stack of sample offsets + memset(bitstream->sample_offset_stack, 0, sizeof(bitstream->sample_offset_stack)); + bitstream->sample_offset_count = 0; +#endif + + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_NULLPTR; +} + +/*! + @brief Attach a bitstream to a byte stream. + + It is permitted for the byte stream to be NULL, in which case the + bitstream will not be able to replenish its internal buffer, but + the consequences are undefined. + */ +CODEC_ERROR AttachBitstream(struct _bitstream *bitstream, struct _stream *stream) +{ + assert(bitstream != NULL); + bitstream->stream = stream; + return CODEC_ERROR_OKAY; +} + +/*! + @brief Detach a bitstream from a byte stream. + + Any resources allocated by the bitstream are released without deallocating + the bitstream data structure itself. The byte stream associated with the + bitstream is not closed by this routine. The byte stream must be closed, + if and when appropriate, by the caller. + */ +CODEC_ERROR ReleaseBitstream(BITSTREAM *bitstream) +{ + //TODO: What cleanup needs to be performed? + (void) bitstream; + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return the specified number of bits from the bitstream + */ +BITWORD GetBits(BITSTREAM *stream, BITCOUNT count) +{ + // Return zero if the request cannot be satisfied + BITWORD bits = 0; + + // Check that the number of requested bits is valid + //assert(0 <= count && count <= bit_word_count); + assert(count <= bit_word_count); + + // Check that the unused portion of the bit buffer is empty + assert((stream->buffer & BitMask(bit_word_count - stream->count)) == 0); + + if (count == 0) goto finish; + + // Are there enough bits in the buffer to satisfy the request? + if (count <= stream->count) + { + // Right align the requested number of bits in the bit buffer + bits = (stream->buffer >> (bit_word_count - count)); + + // Reduce the number of bits in the bit buffer + stream->buffer <<= count; + stream->count = (stream->count - count); + } + else + { + BITCOUNT low_bit_count; + + // Use the remaining bits in the bit buffer + assert(stream->count > 0 || stream->buffer == 0); + bits = (stream->buffer >> (bit_word_count - count)); + + // Compute the number of bits to be used from the next word + low_bit_count = count - stream->count; + stream->count = 0; + assert(low_bit_count > 0); + + // Fill the bit buffer + assert(stream->count == 0); + GetBuffer(stream); + assert(stream->count >= low_bit_count); + + // Use the new bits in the bit buffer + bits |= (stream->buffer >> (bit_word_count - low_bit_count)); + + // Reduce the number of bits in the bit buffer + if (low_bit_count < bit_word_count) { + stream->buffer <<= low_bit_count; + } + else { + stream->buffer = 0; + } + assert(low_bit_count <= stream->count); + stream->count = (stream->count - low_bit_count); + } + +finish: + // The bit count should never be negative or larger than the size of the bit buffer + //assert(0 <= stream->count && stream->count <= bit_word_count); + assert(stream->count <= bit_word_count); + + // The unused bits in the bit buffer should all be zero + assert((stream->buffer & BitMask(bit_word_count - stream->count)) == 0); + + // The unused bits in the result should all be zero + assert((bits & ~BitMask(count)) == 0); + + return bits; +} + +/*! + @brief Read more bits and append to an existing word of bits + + Read the specified number of bits from the bitstream and append + the new bits to the right end of the word supplied as an argument. + This is a convenience routine that is used to accumulate bits that + may match a codeword. + */ +BITWORD AddBits(BITSTREAM *bitstream, BITWORD bits, BITCOUNT count) +{ + BITWORD new_bits = GetBits(bitstream, count); + assert((new_bits & ~BitMask(count)) == 0); + + bits = (bits << count) | new_bits; + + return bits; +} + +/*! + @brief Write a longword (32 bits) to the stream + */ +CODEC_ERROR PutLong(BITSTREAM *stream, BITWORD longword) +{ + return PutBits(stream, longword, bit_word_count); +} + +/*! + @brief Write the specified number of bits to the bitstream + */ +CODEC_ERROR PutBits(BITSTREAM *stream, BITWORD bits, BITCOUNT count) +{ + BITCOUNT unused_bit_count; + + if (count == 0) { + return CODEC_ERROR_OKAY; + } + + // Check that the unused portion of the input bits is empty + assert((bits & (BitMask(bit_word_count - count) << count)) == 0); + + // Check that the number of input bits is valid + //assert(0 <= count && count <= bit_word_count); + assert(count <= bit_word_count); + + // Check that the unused portion of the bit buffer is empty + unused_bit_count = bit_word_count - stream->count; + assert((stream->buffer & BitMask(unused_bit_count)) == 0); + + // Is there room in the bit buffer for the new bits? + if (count <= unused_bit_count) + { + // Fill the remaining space in the bit buffer + stream->buffer |= (bits << (unused_bit_count - count)); + + // Reduce the number of unused bits in the bit buffer + stream->count += count; + } + else + { + // Any room in the bit buffer? + if (unused_bit_count > 0) + { + // Use the number of input bits that will fit in the bit buffer + stream->buffer |= (bits >> (count - unused_bit_count)); + + // Reduce the number of input bits by the amount used + count -= unused_bit_count; + //assert(count >= 0); + } + + // Write the bit buffer to the byte stream + PutWord(stream->stream, stream->buffer ); + + // Insert the remaining input bits into the bit buffer + stream->buffer = (bits << (bit_word_count - count)); + + // Increment the number of bits in the bit buffer + stream->count = count; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Fill the internal bitstream buffer by reading a byte stream + + @todo Need to modify this routine to return an error code if it cannot + read from the byte stream associated with the bitstream. + */ +CODEC_ERROR GetBuffer(BITSTREAM *bitstream) +{ + // Need to signal an underflow error? + if (! (bitstream != NULL && bitstream->stream != NULL)) { + assert(0); + + if( bitstream->error == BITSTREAM_ERROR_UNDERFLOW ) + return CODEC_ERROR_OUTOFMEMORY; + } + + // The bit buffer should be empty + assert(bitstream->count == 0); + + // Fill the bit buffer with a word from the byte stream + bitstream->buffer = Swap32(GetWord(bitstream->stream)); + bitstream->count = bit_word_count; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the internal bitstream buffer to a byte stream + + @todo Need to modify this routine to return an error code if it cannot + write to the byte stream associated with the bitstream. + */ +CODEC_ERROR PutBuffer(BITSTREAM *bitstream) +{ + //TODO: Need to signal an overflow error + assert(bitstream != NULL && bitstream->stream != NULL); + + // The bit buffer should be full + assert(bitstream->count == bit_word_count); + + // Write the bit buffer to the byte stream + PutWord(bitstream->stream, bitstream->buffer ); + + // Empty the bit buffer + bitstream->buffer = 0; + bitstream->count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Convert a bitstream error code to a codec error codec + + The bitstream and byte stream modules use a separate enumeration + for error codes since these modules are used in other applications. + The bistream error code is embedded in a range of codec error codes + that are reserved for bitstream errors. + */ +CODEC_ERROR CodecErrorBitstream(BITSTREAM_ERROR error) +{ + uint32_t codec_error = CODEC_ERROR_BITSTREAM; + codec_error |= (uint32_t)error; + return (CODEC_ERROR)codec_error; +} + +/*! + @brief Convert a bitstream error code into a codec error code + + The bitstream and byte stream modules might be used in other + applications and have their own errors codes. This routine + embeds a bitstream error code into a codec error code. The + bitstream error code is carried in the low bits of the codec + error code. + + @todo Eliminate separate error codes for bitstreams? + */ +CODEC_ERROR BitstreamCodecError(BITSTREAM *bitstream) +{ + assert(bitstream != NULL); + if (! (bitstream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + return CodecErrorBitstream(bitstream->error); +} + +/*! + @brief Read a block of bytes from the bitstream + */ +CODEC_ERROR GetByteArray(BITSTREAM *bitstream, uint8_t *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + { + array[i] = GetBits(bitstream, 8); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write a block of bytes into the bitstream + */ +CODEC_ERROR PutByteArray(BITSTREAM *bitstream, const uint8_t *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + { + PutBits(bitstream, array[i], 8); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write any bits in the buffer to the byte stream + */ +CODEC_ERROR FlushBitstream(BITSTREAM *bitstream) +{ + // Any bits remaining in the bit buffer? + if (bitstream->count > 0) + { + // Write the bit buffer to the output stream + PutBuffer(bitstream); + } + + // Indicate that the bitstream buffer is empty + bitstream->count = 0; + bitstream->buffer = 0; + + // Flush the output stream + FlushStream(bitstream->stream); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Get the current position in the byte stream + + The current position in the byte stream associated with the + bitstream is returned. The intent is to allow the bitstream + (and the associated byte stream) to be restored to the saved + position. + + @todo Need to record the state of the bitstream buffer and + bit count so that the entire bitstream state can be restored. + */ +size_t GetBitstreamPosition(BITSTREAM *bitstream) +{ + if (bitstream->count == bit_word_count) { + PutBuffer(bitstream); + } + + // The bit buffer must be empty + assert(bitstream->count == 0); + + // The bit buffer must be empty + return (bitstream->stream->byte_count); +} + +/*! + @brief Rewind the bitstream + + This routine rewinds the bitstream to the beginning of the byte stream + that has been attached to the bitstream. The byte stream is also reset. + + If the byte stream could not be reset, then the internal bitstream state + is not reset. + */ +CODEC_ERROR RewindBitstream(BITSTREAM *bitstream) +{ + assert(bitstream != NULL); + if (! (bitstream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + if (bitstream->stream != NULL) { + CODEC_ERROR error = RewindStream(bitstream->stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + } + + // Reset the bitstream internal state + bitstream->buffer = 0; + bitstream->count = 0; + bitstream->error = BITSTREAM_ERROR_OKAY; + + return CODEC_ERROR_OKAY; +} + |