Module: Mesa Branch: main Commit: 1782ab4d8bab0b0e1b57a334b8f802bd821421c0 URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=1782ab4d8bab0b0e1b57a334b8f802bd821421c0
Author: Dave Airlie <airl...@redhat.com> Date: Mon Oct 16 17:44:59 2023 +1000 util: add a bitstream encoder for video stream headers. This is based on the d3d12 code, and is mostly a rewrite in C, these are just some helpers to use for writing h264 and h265 headers for vulkan encode. Acked-by: Hyunjun Ko <zz...@igalia.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25874> --- src/util/vl_bitstream.h | 204 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/src/util/vl_bitstream.h b/src/util/vl_bitstream.h new file mode 100644 index 00000000000..11574830818 --- /dev/null +++ b/src/util/vl_bitstream.h @@ -0,0 +1,204 @@ +/* + * Copyright 2023 Red Hat + * SPDX-License-Identifier: MIT + * + * This is a C rewrite of pieces of the d3d12 frontend which is: + * Copyright © Microsoft Corporation + */ +#ifndef VL_BITSTREAM_H +#define VL_BITSTREAM_H + +struct vl_bitstream_encoder { + uint8_t *bits; + uint32_t bits_buffer_size; + uint32_t offset; + + uint32_t enc_buffer; + + int32_t bits_to_go; + bool prevent_start_code; + + bool internal_buffer; + bool overflow; +}; + +#define VL_BITSTREAM_MAX_BUFFER 256 + +static inline void +vl_bitstream_encoder_clear(struct vl_bitstream_encoder *enc, + void *buffer_base, + size_t buffer_offset, + size_t buffer_limit) +{ + memset(enc, 0, sizeof(*enc)); + enc->bits_to_go = 32; + + if (!buffer_base) { + enc->bits = malloc(VL_BITSTREAM_MAX_BUFFER); + enc->bits_buffer_size = VL_BITSTREAM_MAX_BUFFER; + enc->internal_buffer = true; + } else { + enc->bits = (uint8_t *)buffer_base + buffer_offset; + enc->bits_buffer_size = buffer_limit; + } +} + +static inline void +vl_bitstream_encoder_free(struct vl_bitstream_encoder *enc) +{ + if (enc->internal_buffer) + free(enc->bits); +} + +static inline int +vl_bitstream_get_num_bits_for_byte_align(struct vl_bitstream_encoder *enc) +{ + return enc->bits_to_go & 7; +} + +static inline int +vl_bitstream_get_byte_count(struct vl_bitstream_encoder *enc) +{ + return enc->offset + ((32 - enc->bits_to_go) >> 3); +} + +static inline bool +vl_bitstream_is_byte_aligned(struct vl_bitstream_encoder *enc) +{ + if (enc->overflow) + enc->bits_to_go = 32; + return !(enc->bits_to_go & 7); +} + +static inline void +vl_bitstream_write_byte_start_code(struct vl_bitstream_encoder *enc, uint8_t val) +{ + int offset = enc->offset; + uint8_t *buffer = enc->bits + enc->offset; + if (enc->prevent_start_code && enc->offset > 1) { + if (((val & 0xfc) | buffer[-2] | buffer[-1]) == 0) { + *buffer++ = 3; + offset++; + } + } + + *buffer = val; + offset++; + enc->offset = offset; +} + +static inline bool +vl_bitstream_verify_buffer(struct vl_bitstream_encoder *enc, uint32_t bytes_to_write) +{ + if (enc->overflow) + return false; + + if (enc->offset + bytes_to_write > enc->bits_buffer_size) { + enc->overflow = true; + return false; + } + return true; +} + +static inline void +vl_bitstream_flush(struct vl_bitstream_encoder *enc) +{ + ASSERTED bool is_aligned = vl_bitstream_is_byte_aligned(enc); + assert (is_aligned); + + uint32_t temp = (uint32_t)(32 - enc->bits_to_go); + + if (!vl_bitstream_verify_buffer(enc, temp >> 3)) { + return; + } + + while (temp > 0) { + vl_bitstream_write_byte_start_code(enc, (uint8_t)(enc->enc_buffer >> 24)); + enc->enc_buffer <<= 8; + temp -= 8; + } + + enc->bits_to_go = 32; + enc->enc_buffer = 0; +} + +static inline void +vl_bitstream_put_bits(struct vl_bitstream_encoder *enc, int bits_count, uint32_t bits_val) +{ + if (bits_count < enc->bits_to_go) { + enc->enc_buffer |= (bits_val << (enc->bits_to_go - bits_count)); + enc->bits_to_go -= bits_count; + } else if (vl_bitstream_verify_buffer(enc, 4)) { + int left_over_bits = bits_count - enc->bits_to_go; + enc->enc_buffer |= (bits_val >> left_over_bits); + + { + uint8_t *temp = (uint8_t *)&enc->enc_buffer; + vl_bitstream_write_byte_start_code(enc, *(temp + 3)); + vl_bitstream_write_byte_start_code(enc, *(temp + 2)); + vl_bitstream_write_byte_start_code(enc, *(temp + 1)); + vl_bitstream_write_byte_start_code(enc, *temp); + } + + enc->enc_buffer = 0; + enc->bits_to_go = 32 - left_over_bits; + + if (left_over_bits > 0) + enc->enc_buffer = (bits_val << (32 - left_over_bits)); + } +} + +static inline int +vl_bitstream_get_exp_golomb0_code_len(uint32_t val) +{ + int len = 0; + val++; + + if (val >= 0x10000) { + val >>= 16; + len += 16; + } + if (val >= 0x100) { + val >>= 8; + len += 8; + } + assert(val < 256); + + return len + util_logbase2(val); +} + +static inline void +vl_bitstream_exp_golomb_ue(struct vl_bitstream_encoder *enc, uint32_t val) +{ + if (val != UINT32_MAX) { + int len = vl_bitstream_get_exp_golomb0_code_len(val); + vl_bitstream_put_bits(enc, (len << 1) + 1, val + 1); + } else { + vl_bitstream_put_bits(enc, 32, 0); + vl_bitstream_put_bits(enc, 1, 1); + vl_bitstream_put_bits(enc, 32, 1); + } +} + +static inline void +vl_bitstream_exp_golomb_se(struct vl_bitstream_encoder *enc, uint32_t val) +{ + if (val > 0) + vl_bitstream_exp_golomb_ue(enc, (val << 1) - 1); + else + vl_bitstream_exp_golomb_ue(enc, ((-val) << 1) - (val == INT_MIN)); +} + +static inline void +vl_bitstream_rbsp_trailing(struct vl_bitstream_encoder *enc) +{ + vl_bitstream_put_bits(enc, 1, 1); + int left = vl_bitstream_get_num_bits_for_byte_align(enc); + + if (left) + vl_bitstream_put_bits(enc, left, 0); + + ASSERTED bool is_aligned = vl_bitstream_is_byte_aligned(enc); + assert(is_aligned); +} +#endif