mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:07:45 +00:00
LibVideo: Cache 64 bits at a time for reading in BitStream
Reads will now be done in larger chunks at a time. The public read_byte() function was removed in favor of a private fill_reservoir() function which will be used to fill the 64-bit reservoir field which will then be bit-shifted and masked as necessary for subsequent arbitrary bit-sized reads. read_f(n) was renamed to read_bits to be clearer about its use.
This commit is contained in:
parent
b37ea6b414
commit
7f46033c01
3 changed files with 69 additions and 60 deletions
|
@ -5,60 +5,67 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Error.h>
|
|
||||||
|
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
|
|
||||||
namespace Video::VP9 {
|
namespace Video::VP9 {
|
||||||
|
|
||||||
ErrorOr<u8> BitStream::read_byte()
|
ErrorOr<void> BitStream::fill_reservoir()
|
||||||
{
|
{
|
||||||
if (m_bytes_remaining < 1)
|
VERIFY(m_reservoir_bits_remaining == 0);
|
||||||
return Error::from_string_literal("read_byte: Out of data.");
|
if (m_data_ptr == m_end_ptr)
|
||||||
VERIFY(m_bytes_remaining >= 1);
|
return Error::from_string_literal("Stream is out of data");
|
||||||
m_bytes_remaining--;
|
VERIFY(m_data_ptr < m_end_ptr);
|
||||||
return *(m_data_ptr++);
|
m_reservoir = 0;
|
||||||
|
|
||||||
|
size_t byte_index;
|
||||||
|
for (byte_index = 0; m_data_ptr < m_end_ptr && byte_index < sizeof(m_reservoir); byte_index++) {
|
||||||
|
m_reservoir = (m_reservoir << 8) | *m_data_ptr;
|
||||||
|
m_data_ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_reservoir_bits_remaining = byte_index * 8;
|
||||||
|
m_reservoir <<= (sizeof(m_reservoir) - byte_index) * 8;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<u64> BitStream::read_bits(u8 bit_count)
|
||||||
|
{
|
||||||
|
if (bit_count > sizeof(u64) * 8)
|
||||||
|
return Error::from_string_literal("Requested read is too large");
|
||||||
|
u64 result = 0;
|
||||||
|
|
||||||
|
while (bit_count > 0) {
|
||||||
|
if (m_reservoir_bits_remaining == 0)
|
||||||
|
TRY(fill_reservoir());
|
||||||
|
|
||||||
|
u64 batch_bits = min(bit_count, m_reservoir_bits_remaining);
|
||||||
|
u64 bit_shift = (sizeof(m_reservoir) * 8u) - batch_bits;
|
||||||
|
|
||||||
|
result = (result << batch_bits) | m_reservoir >> bit_shift;
|
||||||
|
m_reservoir <<= batch_bits;
|
||||||
|
bit_count -= batch_bits;
|
||||||
|
m_reservoir_bits_remaining -= batch_bits;
|
||||||
|
m_bits_read += batch_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<bool> BitStream::read_bit()
|
ErrorOr<bool> BitStream::read_bit()
|
||||||
{
|
{
|
||||||
if (!m_current_byte.has_value()) {
|
auto value = TRY(read_bits(1));
|
||||||
m_current_byte = TRY(read_byte());
|
VERIFY(value <= 2);
|
||||||
m_current_bit_position = 7;
|
return value != 0;
|
||||||
}
|
|
||||||
|
|
||||||
bool bit_value = m_current_byte.value() & (1u << m_current_bit_position);
|
|
||||||
if (--m_current_bit_position < 0)
|
|
||||||
m_current_byte.clear();
|
|
||||||
return bit_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<u8> BitStream::read_f(size_t n)
|
|
||||||
{
|
|
||||||
u8 result = 0;
|
|
||||||
for (size_t i = 0; i < n; i++) {
|
|
||||||
result = (2 * result) + TRY(read_bit());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<u8> BitStream::read_f8()
|
ErrorOr<u8> BitStream::read_f8()
|
||||||
{
|
{
|
||||||
if (!m_current_byte.has_value())
|
return TRY(read_bits(8));
|
||||||
return read_byte();
|
|
||||||
|
|
||||||
auto high_bits = m_current_byte.value() & ((1u << m_current_bit_position) - 1);
|
|
||||||
u8 remaining_bits = 7 - m_current_bit_position;
|
|
||||||
m_current_byte = TRY(read_byte());
|
|
||||||
m_current_bit_position = 7;
|
|
||||||
auto low_bits = (m_current_byte.value() >> (8u - remaining_bits)) & ((1u << remaining_bits) - 1);
|
|
||||||
m_current_bit_position -= remaining_bits;
|
|
||||||
return (high_bits << remaining_bits) | low_bits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<u16> BitStream::read_f16()
|
ErrorOr<u16> BitStream::read_f16()
|
||||||
{
|
{
|
||||||
return (TRY(read_f8()) << 8u) | TRY(read_f8());
|
return TRY(read_bits(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 9.2.1 */
|
/* 9.2.1 */
|
||||||
|
@ -108,7 +115,7 @@ ErrorOr<bool> BitStream::read_bool(u8 probability)
|
||||||
ErrorOr<void> BitStream::exit_bool()
|
ErrorOr<void> BitStream::exit_bool()
|
||||||
{
|
{
|
||||||
// FIXME: I'm not sure if this call to min is spec compliant, or if there is an issue elsewhere earlier in the parser.
|
// FIXME: I'm not sure if this call to min is spec compliant, or if there is an issue elsewhere earlier in the parser.
|
||||||
auto padding_element = TRY(read_f(min(m_bool_max_bits, (u64)bits_remaining())));
|
auto padding_element = TRY(read_bits(min(m_bool_max_bits, (u64)bits_remaining())));
|
||||||
|
|
||||||
// FIXME: It is a requirement of bitstream conformance that enough padding bits are inserted to ensure that the final coded byte of a frame is not equal to a superframe marker.
|
// FIXME: It is a requirement of bitstream conformance that enough padding bits are inserted to ensure that the final coded byte of a frame is not equal to a superframe marker.
|
||||||
// A byte b is equal to a superframe marker if and only if (b & 0xe0)is equal to 0xc0, i.e. if the most significant 3 bits are equal to 0b110.
|
// A byte b is equal to a superframe marker if and only if (b & 0xe0)is equal to 0xc0, i.e. if the most significant 3 bits are equal to 0b110.
|
||||||
|
@ -128,24 +135,24 @@ ErrorOr<u8> BitStream::read_literal(size_t n)
|
||||||
|
|
||||||
ErrorOr<i8> BitStream::read_s(size_t n)
|
ErrorOr<i8> BitStream::read_s(size_t n)
|
||||||
{
|
{
|
||||||
auto value = TRY(read_f(n));
|
auto value = TRY(read_bits(n));
|
||||||
auto sign = TRY(read_bit());
|
auto sign = TRY(read_bit());
|
||||||
return sign ? -value : value;
|
return sign ? -value : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 BitStream::get_position()
|
u64 BitStream::get_position()
|
||||||
{
|
{
|
||||||
return (m_bytes_read * 8) + (7 - m_current_bit_position);
|
return m_bits_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BitStream::bytes_remaining()
|
size_t BitStream::bytes_remaining()
|
||||||
{
|
{
|
||||||
return m_bytes_remaining;
|
return (m_end_ptr - m_data_ptr) + (m_reservoir_bits_remaining / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t BitStream::bits_remaining()
|
size_t BitStream::bits_remaining()
|
||||||
{
|
{
|
||||||
return (bytes_remaining() * 8) + m_current_bit_position + 1;
|
return ((m_end_ptr - m_data_ptr) * sizeof(m_data_ptr) * 8) + m_reservoir_bits_remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Error.h>
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
@ -16,15 +17,14 @@ class BitStream {
|
||||||
public:
|
public:
|
||||||
BitStream(u8 const* data, size_t size)
|
BitStream(u8 const* data, size_t size)
|
||||||
: m_data_ptr(data)
|
: m_data_ptr(data)
|
||||||
, m_bytes_remaining(size)
|
, m_end_ptr(data + size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<u8> read_byte();
|
|
||||||
ErrorOr<bool> read_bit();
|
ErrorOr<bool> read_bit();
|
||||||
|
|
||||||
|
ErrorOr<u64> read_bits(u8 bit_count);
|
||||||
/* (9.1) */
|
/* (9.1) */
|
||||||
ErrorOr<u8> read_f(size_t n);
|
|
||||||
ErrorOr<u8> read_f8();
|
ErrorOr<u8> read_f8();
|
||||||
ErrorOr<u16> read_f16();
|
ErrorOr<u16> read_f16();
|
||||||
|
|
||||||
|
@ -42,11 +42,13 @@ public:
|
||||||
size_t bits_remaining();
|
size_t bits_remaining();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ErrorOr<void> fill_reservoir();
|
||||||
|
|
||||||
u8 const* m_data_ptr { nullptr };
|
u8 const* m_data_ptr { nullptr };
|
||||||
size_t m_bytes_remaining { 0 };
|
u8 const* m_end_ptr { nullptr };
|
||||||
Optional<u8> m_current_byte;
|
u64 m_reservoir;
|
||||||
i8 m_current_bit_position { 0 };
|
u8 m_reservoir_bits_remaining { 0 };
|
||||||
u64 m_bytes_read { 0 };
|
size_t m_bits_read { 0 };
|
||||||
|
|
||||||
u8 m_bool_value { 0 };
|
u8 m_bool_value { 0 };
|
||||||
u8 m_bool_range { 0 };
|
u8 m_bool_range { 0 };
|
||||||
|
|
|
@ -95,7 +95,7 @@ ErrorOr<void> Parser::refresh_probs()
|
||||||
/* (6.2) */
|
/* (6.2) */
|
||||||
ErrorOr<void> Parser::uncompressed_header()
|
ErrorOr<void> Parser::uncompressed_header()
|
||||||
{
|
{
|
||||||
auto frame_marker = TRY(m_bit_stream->read_f(2));
|
auto frame_marker = TRY(m_bit_stream->read_bits(2));
|
||||||
if (frame_marker != 2)
|
if (frame_marker != 2)
|
||||||
return Error::from_string_literal("uncompressed_header: Frame marker must be 2");
|
return Error::from_string_literal("uncompressed_header: Frame marker must be 2");
|
||||||
auto profile_low_bit = TRY(m_bit_stream->read_bit());
|
auto profile_low_bit = TRY(m_bit_stream->read_bit());
|
||||||
|
@ -105,7 +105,7 @@ ErrorOr<void> Parser::uncompressed_header()
|
||||||
return Error::from_string_literal("uncompressed_header: Profile 3 reserved bit was non-zero");
|
return Error::from_string_literal("uncompressed_header: Profile 3 reserved bit was non-zero");
|
||||||
auto show_existing_frame = TRY(m_bit_stream->read_bit());
|
auto show_existing_frame = TRY(m_bit_stream->read_bit());
|
||||||
if (show_existing_frame) {
|
if (show_existing_frame) {
|
||||||
m_frame_to_show_map_index = TRY(m_bit_stream->read_f(3));
|
m_frame_to_show_map_index = TRY(m_bit_stream->read_bits(3));
|
||||||
m_header_size_in_bytes = 0;
|
m_header_size_in_bytes = 0;
|
||||||
m_refresh_frame_flags = 0;
|
m_refresh_frame_flags = 0;
|
||||||
m_loop_filter_level = 0;
|
m_loop_filter_level = 0;
|
||||||
|
@ -128,7 +128,7 @@ ErrorOr<void> Parser::uncompressed_header()
|
||||||
m_frame_is_intra = !m_show_frame && TRY(m_bit_stream->read_bit());
|
m_frame_is_intra = !m_show_frame && TRY(m_bit_stream->read_bit());
|
||||||
|
|
||||||
if (!m_error_resilient_mode) {
|
if (!m_error_resilient_mode) {
|
||||||
m_reset_frame_context = TRY(m_bit_stream->read_f(2));
|
m_reset_frame_context = TRY(m_bit_stream->read_bits(2));
|
||||||
} else {
|
} else {
|
||||||
m_reset_frame_context = 0;
|
m_reset_frame_context = 0;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ ErrorOr<void> Parser::uncompressed_header()
|
||||||
} else {
|
} else {
|
||||||
m_refresh_frame_flags = TRY(m_bit_stream->read_f8());
|
m_refresh_frame_flags = TRY(m_bit_stream->read_f8());
|
||||||
for (auto i = 0; i < 3; i++) {
|
for (auto i = 0; i < 3; i++) {
|
||||||
m_ref_frame_idx[i] = TRY(m_bit_stream->read_f(3));
|
m_ref_frame_idx[i] = TRY(m_bit_stream->read_bits(3));
|
||||||
m_ref_frame_sign_bias[LastFrame + i] = TRY(m_bit_stream->read_bit());
|
m_ref_frame_sign_bias[LastFrame + i] = TRY(m_bit_stream->read_bit());
|
||||||
}
|
}
|
||||||
TRY(frame_size_with_refs());
|
TRY(frame_size_with_refs());
|
||||||
|
@ -167,7 +167,7 @@ ErrorOr<void> Parser::uncompressed_header()
|
||||||
m_frame_parallel_decoding_mode = true;
|
m_frame_parallel_decoding_mode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_frame_context_idx = TRY(m_bit_stream->read_f(2));
|
m_frame_context_idx = TRY(m_bit_stream->read_bits(2));
|
||||||
if (m_frame_is_intra || m_error_resilient_mode) {
|
if (m_frame_is_intra || m_error_resilient_mode) {
|
||||||
setup_past_independence();
|
setup_past_independence();
|
||||||
if (m_frame_type == KeyFrame || m_error_resilient_mode || m_reset_frame_context == 3) {
|
if (m_frame_type == KeyFrame || m_error_resilient_mode || m_reset_frame_context == 3) {
|
||||||
|
@ -209,7 +209,7 @@ ErrorOr<void> Parser::color_config()
|
||||||
m_bit_depth = 8;
|
m_bit_depth = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto color_space = TRY(m_bit_stream->read_f(3));
|
auto color_space = TRY(m_bit_stream->read_bits(3));
|
||||||
VERIFY(color_space <= RGB);
|
VERIFY(color_space <= RGB);
|
||||||
m_color_space = static_cast<ColorSpace>(color_space);
|
m_color_space = static_cast<ColorSpace>(color_space);
|
||||||
|
|
||||||
|
@ -291,15 +291,15 @@ ErrorOr<void> Parser::read_interpolation_filter()
|
||||||
if (TRY(m_bit_stream->read_bit())) {
|
if (TRY(m_bit_stream->read_bit())) {
|
||||||
m_interpolation_filter = Switchable;
|
m_interpolation_filter = Switchable;
|
||||||
} else {
|
} else {
|
||||||
m_interpolation_filter = literal_to_type[TRY(m_bit_stream->read_f(2))];
|
m_interpolation_filter = literal_to_type[TRY(m_bit_stream->read_bits(2))];
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> Parser::loop_filter_params()
|
ErrorOr<void> Parser::loop_filter_params()
|
||||||
{
|
{
|
||||||
m_loop_filter_level = TRY(m_bit_stream->read_f(6));
|
m_loop_filter_level = TRY(m_bit_stream->read_bits(6));
|
||||||
m_loop_filter_sharpness = TRY(m_bit_stream->read_f(3));
|
m_loop_filter_sharpness = TRY(m_bit_stream->read_bits(3));
|
||||||
m_loop_filter_delta_enabled = TRY(m_bit_stream->read_bit());
|
m_loop_filter_delta_enabled = TRY(m_bit_stream->read_bit());
|
||||||
if (m_loop_filter_delta_enabled) {
|
if (m_loop_filter_delta_enabled) {
|
||||||
if (TRY(m_bit_stream->read_bit())) {
|
if (TRY(m_bit_stream->read_bit())) {
|
||||||
|
@ -362,7 +362,7 @@ ErrorOr<void> Parser::segmentation_params()
|
||||||
m_feature_enabled[i][j] = feature_enabled;
|
m_feature_enabled[i][j] = feature_enabled;
|
||||||
if (feature_enabled) {
|
if (feature_enabled) {
|
||||||
auto bits_to_read = segmentation_feature_bits[j];
|
auto bits_to_read = segmentation_feature_bits[j];
|
||||||
feature_value = TRY(m_bit_stream->read_f(bits_to_read));
|
feature_value = TRY(m_bit_stream->read_bits(bits_to_read));
|
||||||
if (segmentation_feature_signed[j]) {
|
if (segmentation_feature_signed[j]) {
|
||||||
if (TRY(m_bit_stream->read_bit()))
|
if (TRY(m_bit_stream->read_bit()))
|
||||||
feature_value = -feature_value;
|
feature_value = -feature_value;
|
||||||
|
@ -764,7 +764,7 @@ ErrorOr<void> Parser::decode_tiles()
|
||||||
if (last_tile)
|
if (last_tile)
|
||||||
tile_size = m_bit_stream->bytes_remaining();
|
tile_size = m_bit_stream->bytes_remaining();
|
||||||
else
|
else
|
||||||
tile_size = TRY(m_bit_stream->read_f(32));
|
tile_size = TRY(m_bit_stream->read_bits(32));
|
||||||
|
|
||||||
m_mi_row_start = get_tile_offset(tile_row, m_mi_rows, m_tile_rows_log2);
|
m_mi_row_start = get_tile_offset(tile_row, m_mi_rows, m_tile_rows_log2);
|
||||||
m_mi_row_end = get_tile_offset(tile_row + 1, m_mi_rows, m_tile_rows_log2);
|
m_mi_row_end = get_tile_offset(tile_row + 1, m_mi_rows, m_tile_rows_log2);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue