summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_common/bitstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/vc5_common/bitstream.c')
-rwxr-xr-xgpr/source/lib/vc5_common/bitstream.c445
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;
+}
+