/***************************************************************************** * This file is part of Kvazaar HEVC encoder. * * Copyright (C) 2013-2015 Tampere University of Technology and others (see * COPYING file). * * Kvazaar 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 2.1 of the License, or (at your * option) any later version. * * Kvazaar 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 General Public License along * with Kvazaar. If not, see . ****************************************************************************/ #include "bitstream.h" #include #include #include #include "kvz_math.h" const uint32_t kvz_bit_set_mask[] = { 0x00000001,0x00000002,0x00000004,0x00000008, 0x00000010,0x00000020,0x00000040,0x00000080, 0x00000100,0x00000200,0x00000400,0x00000800, 0x00001000,0x00002000,0x00004000,0x00008000, 0x00010000,0x00020000,0x00040000,0x00080000, 0x00100000,0x00200000,0x00400000,0x00800000, 0x01000000,0x02000000,0x04000000,0x08000000, 0x10000000,0x20000000,0x40000000,0x80000000 }; //#define VERBOSE #ifdef VERBOSE void printf_bitstream(char *msg, ...) { va_list fmtargs; char buffer[1024]; va_start(fmtargs,msg); vsnprintf(buffer,sizeof(buffer)-1,msg,fmtargs); va_end(fmtargs); printf("%s",buffer); } #endif /** * \brief Initialize a new bitstream. */ void kvz_bitstream_init(bitstream_t *const stream) { memset(stream, 0, sizeof(bitstream_t)); } /** * \brief Take chunks from a bitstream. * * Move ownership of the chunks to the caller and clear the bitstream. * * The bitstream must be byte-aligned. */ kvz_data_chunk * kvz_bitstream_take_chunks(bitstream_t *const stream) { assert(stream->cur_bit == 0); kvz_data_chunk *chunks = stream->first; stream->first = stream->last = NULL; stream->len = 0; return chunks; } /** * \brief Allocates a new bitstream chunk. * * \return Pointer to the new chunk, or NULL. */ kvz_data_chunk * kvz_bitstream_alloc_chunk() { kvz_data_chunk *chunk = malloc(sizeof(kvz_data_chunk)); if (chunk) { chunk->len = 0; chunk->next = NULL; } return chunk; } /** * \brief Free a list of chunks. */ void kvz_bitstream_free_chunks(kvz_data_chunk *chunk) { while (chunk != NULL) { kvz_data_chunk *next = chunk->next; free(chunk); chunk = next; } } /** * \brief Free resources used by a bitstream. */ void kvz_bitstream_finalize(bitstream_t *const stream) { kvz_bitstream_clear(stream); } /** * \brief Get the number of bits written. * \param stream bitstream * \return position */ uint64_t kvz_bitstream_tell(const bitstream_t *const stream) { uint64_t position = stream->len; return position * 8 + stream->cur_bit; } /** * \brief Write a byte to bitstream * * The stream must be byte-aligned. * * \param stream pointer bitstream to put the data * \param byte byte to write */ void kvz_bitstream_writebyte(bitstream_t *const stream, const uint8_t byte) { assert(stream->cur_bit == 0); if (stream->last == NULL || stream->last->len == KVZ_DATA_CHUNK_SIZE) { // Need to allocate a new chunk. kvz_data_chunk *new_chunk = kvz_bitstream_alloc_chunk(); assert(new_chunk); if (!stream->first) stream->first = new_chunk; if (stream->last) stream->last->next = new_chunk; stream->last = new_chunk; } assert(stream->last->len < KVZ_DATA_CHUNK_SIZE); stream->last->data[stream->last->len] = byte; stream->last->len += 1; stream->len += 1; } /** * \brief Move data from one stream to another. * * Destination stream must be byte-aligned. Source stream will be cleared. */ void kvz_bitstream_move(bitstream_t *const dst, bitstream_t *const src) { assert(dst->cur_bit == 0); if (src->len > 0) { if (dst->first == NULL) { dst->first = src->first; dst->last = src->last; dst->len = src->len; } else { dst->last->next = src->first; dst->last = src->last; dst->len += src->len; } } // Move the leftover bits. dst->data = src->data; dst->cur_bit = src->cur_bit; dst->zerocount = src->zerocount; src->first = src->last = NULL; kvz_bitstream_clear(src); } /** * Reset stream. */ void kvz_bitstream_clear(bitstream_t *const stream) { kvz_bitstream_free_chunks(stream->first); kvz_bitstream_init(stream); } /** * \brief Write a byte to a byte aligned bitstream * \param stream stream the data is to be appended to * \param data input data */ void kvz_bitstream_put_byte(bitstream_t *const stream, uint32_t data) { assert(stream->cur_bit == 0); const uint8_t emulation_prevention_three_byte = 0x03; if ((stream->zerocount == 2) && (data < 4)) { kvz_bitstream_writebyte(stream, emulation_prevention_three_byte); stream->zerocount = 0; } stream->zerocount = data == 0 ? stream->zerocount + 1 : 0; kvz_bitstream_writebyte(stream, data); } /** * \brief Write bits to bitstream * Buffers individual bits untill they make a full byte. * \param stream stream the data is to be appended to * \param data input data * \param bits number of bits to write from data to stream */ void kvz_bitstream_put(bitstream_t *const stream, const uint32_t data, uint8_t bits) { while (bits--) { stream->data <<= 1; if (data & kvz_bit_set_mask[bits]) { stream->data |= 1; } stream->cur_bit++; // write byte to output if (stream->cur_bit == 8) { stream->cur_bit = 0; kvz_bitstream_put_byte(stream, stream->data); } } } /** * \brief Write unsigned Exp-Golomb bit string */ void bitstream_put_ue(bitstream_t *stream, uint32_t code_num) { unsigned code_num_log2 = kvz_math_floor_log2(code_num + 1); unsigned prefix = 1 << code_num_log2; unsigned suffix = code_num + 1 - prefix; unsigned num_bits = code_num_log2 * 2 + 1; unsigned value = prefix | suffix; kvz_bitstream_put(stream, value, num_bits); } /** * \brief Write signed Exp-Golomb bit string */ void bitstream_put_se(bitstream_t *stream, int32_t data) { // Map positive values to even and negative to odd values. uint32_t code_num = data <= 0 ? (-data) << 1 : (data << 1) - 1; bitstream_put_ue(stream, code_num); } /** * \brief Add rbsp_trailing_bits syntax element, which aligns the bitstream. */ void kvz_bitstream_add_rbsp_trailing_bits(bitstream_t * const stream) { kvz_bitstream_put(stream, 1, 1); if ((stream->cur_bit & 7) != 0) { kvz_bitstream_put(stream, 0, 8 - (stream->cur_bit & 7)); } } /** * \brief Align the bitstream, unless it's already aligned. */ void kvz_bitstream_align(bitstream_t * const stream) { if ((stream->cur_bit & 7) != 0) { kvz_bitstream_add_rbsp_trailing_bits(stream); } } /** * \brief Align the bitstream with zero */ void kvz_bitstream_align_zero(bitstream_t * const stream) { if ((stream->cur_bit & 7) != 0) { kvz_bitstream_put(stream, 0, 8 - (stream->cur_bit & 7)); } }