mirror of
https://github.com/ultravideo/uvg266.git
synced 2024-11-27 19:24:06 +00:00
313 lines
8.2 KiB
C
313 lines
8.2 KiB
C
/*****************************************************************************
|
|
* This file is part of uvg266 VVC encoder.
|
|
*
|
|
* Copyright (c) 2021, Tampere University, ITU/ISO/IEC, project contributors
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright notice, this
|
|
* list of conditions and the following disclaimer in the documentation and/or
|
|
* other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of the Tampere University or ITU/ISO/IEC nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
****************************************************************************/
|
|
|
|
#include "bitstream.h"
|
|
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
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 kvz_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 kvz_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;
|
|
kvz_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)
|
|
{
|
|
#if VERBOSE
|
|
printf("%-50s u(%d) : %d\n", "rbsp_stop_one_bit", 1, 1);
|
|
#endif
|
|
kvz_bitstream_put(stream, 1, 1);
|
|
kvz_bitstream_align_zero(stream);
|
|
}
|
|
|
|
/**
|
|
* \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) {
|
|
#if VERBOSE
|
|
for (int i = 0; i < ((8 - stream->cur_bit) & 7); i++) {
|
|
printf("%-50s u(%d) : %d\n", "rbsp_alignment_zero_bit", 1, 0);
|
|
}
|
|
#endif
|
|
kvz_bitstream_put(stream, 0, (8 - stream->cur_bit) & 7);
|
|
}
|
|
}
|