LibVideo: Allow bit stream reads to throw errors

Errors are propagated to the user of the decoder so that they can be
aware of specific places where a read failed.
This commit is contained in:
Zaggy1024 2022-10-08 19:13:02 -05:00 committed by Andrew Kaster
parent af0584ea53
commit b37ea6b414
Notes: sideshowbarker 2024-07-17 06:08:34 +09:00
10 changed files with 529 additions and 509 deletions

View file

@ -42,10 +42,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto const& frame = block.frame(0);
dbgln("Reading frame 0 from block @ {}", block.timestamp());
bool failed = !vp9_decoder.decode_frame(frame);
auto result = vp9_decoder.decode_frame(frame);
vp9_decoder.dump_frame_info();
if (failed)
if (result.is_error()) {
outln("Error: {}", result.error().string_literal());
return 1;
}
}
}

View file

@ -1,24 +1,29 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Error.h>
#include "BitStream.h"
namespace Video::VP9 {
u8 BitStream::read_byte()
ErrorOr<u8> BitStream::read_byte()
{
if (m_bytes_remaining < 1)
return Error::from_string_literal("read_byte: Out of data.");
VERIFY(m_bytes_remaining >= 1);
m_bytes_remaining--;
return *(m_data_ptr++);
}
bool BitStream::read_bit()
ErrorOr<bool> BitStream::read_bit()
{
if (!m_current_byte.has_value()) {
m_current_byte = read_byte();
m_current_byte = TRY(read_byte());
m_current_bit_position = 7;
}
@ -28,47 +33,49 @@ bool BitStream::read_bit()
return bit_value;
}
u8 BitStream::read_f(size_t n)
ErrorOr<u8> BitStream::read_f(size_t n)
{
u8 result = 0;
for (size_t i = 0; i < n; i++) {
result = (2 * result) + read_bit();
result = (2 * result) + TRY(read_bit());
}
return result;
}
u8 BitStream::read_f8()
ErrorOr<u8> BitStream::read_f8()
{
if (!m_current_byte.has_value())
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 = read_byte();
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;
}
u16 BitStream::read_f16()
ErrorOr<u16> BitStream::read_f16()
{
return (read_f8() << 8u) | read_f8();
return (TRY(read_f8()) << 8u) | TRY(read_f8());
}
/* 9.2.1 */
bool BitStream::init_bool(size_t bytes)
ErrorOr<void> BitStream::init_bool(size_t bytes)
{
if (bytes < 1)
return false;
m_bool_value = read_f8();
if (bytes == 0)
return Error::from_string_literal("Range coder size cannot be zero.");
m_bool_value = TRY(read_f8());
m_bool_range = 255;
m_bool_max_bits = (8 * bytes) - 8;
return !read_bool(128);
if (TRY(read_bool(128)))
return Error::from_string_literal("Range coder's first bool was non-zero.");
return {};
}
/* 9.2.2 */
bool BitStream::read_bool(u8 probability)
ErrorOr<bool> BitStream::read_bool(u8 probability)
{
auto split = 1u + (((m_bool_range - 1u) * probability) >> 8u);
bool return_bool;
@ -85,7 +92,7 @@ bool BitStream::read_bool(u8 probability)
while (m_bool_range < 128) {
bool new_bit;
if (m_bool_max_bits) {
new_bit = read_bit();
new_bit = TRY(read_bit());
m_bool_max_bits--;
} else {
new_bit = false;
@ -98,29 +105,31 @@ bool BitStream::read_bool(u8 probability)
}
/* 9.2.3 */
bool 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.
auto padding_element = read_f(min(m_bool_max_bits, (u64)bits_remaining()));
auto padding_element = TRY(read_f(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.
// 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.
return padding_element == 0;
if (padding_element != 0)
return Error::from_string_literal("Range coder padding was non-zero.");
return {};
}
u8 BitStream::read_literal(size_t n)
ErrorOr<u8> BitStream::read_literal(size_t n)
{
u8 return_value = 0;
for (size_t i = 0; i < n; i++) {
return_value = (2 * return_value) + read_bool(128);
return_value = (2 * return_value) + TRY(read_bool(128));
}
return return_value;
}
i8 BitStream::read_s(size_t n)
ErrorOr<i8> BitStream::read_s(size_t n)
{
auto value = read_f(n);
auto sign = read_bit();
auto value = TRY(read_f(n));
auto sign = TRY(read_bit());
return sign ? -value : value;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -19,22 +20,22 @@ public:
{
}
u8 read_byte();
bool read_bit();
ErrorOr<u8> read_byte();
ErrorOr<bool> read_bit();
/* (9.1) */
u8 read_f(size_t n);
u8 read_f8();
u16 read_f16();
ErrorOr<u8> read_f(size_t n);
ErrorOr<u8> read_f8();
ErrorOr<u16> read_f16();
/* (9.2) */
bool init_bool(size_t bytes);
bool read_bool(u8 probability);
bool exit_bool();
u8 read_literal(size_t n);
ErrorOr<void> init_bool(size_t bytes);
ErrorOr<bool> read_bool(u8 probability);
ErrorOr<void> exit_bool();
ErrorOr<u8> read_literal(size_t n);
/* (4.9.2) */
i8 read_s(size_t n);
ErrorOr<i8> read_s(size_t n);
u64 get_position();
size_t bytes_remaining();

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -14,15 +15,16 @@ Decoder::Decoder()
{
}
bool Decoder::decode_frame(ByteBuffer const& frame_data)
ErrorOr<void> Decoder::decode_frame(ByteBuffer const& frame_data)
{
SAFE_CALL(m_parser->parse_frame(frame_data));
TRY(m_parser->parse_frame(frame_data));
// TODO:
// - #2
// - #3
// - #4
SAFE_CALL(update_reference_frames());
return true;
TRY(update_reference_frames());
return {};
}
void Decoder::dump_frame_info()
@ -49,7 +51,7 @@ u8 Decoder::merge_probs(int const* tree, int index, u8* probs, u8* counts, u8 co
return left_count + right_count;
}
bool Decoder::adapt_coef_probs()
ErrorOr<void> Decoder::adapt_coef_probs()
{
u8 update_factor;
if (m_parser->m_frame_is_intra || m_parser->m_last_frame_type != KeyFrame)
@ -76,7 +78,7 @@ bool Decoder::adapt_coef_probs()
}
}
return true;
return {};
}
#define ADAPT_PROB_TABLE(name, size) \
@ -94,7 +96,7 @@ bool Decoder::adapt_coef_probs()
} \
} while (0)
bool Decoder::adapt_non_coef_probs()
ErrorOr<void> Decoder::adapt_non_coef_probs()
{
auto& probs = *m_parser->m_probability_tables;
auto& counter = *m_parser->m_syntax_element_counter;
@ -137,7 +139,7 @@ bool Decoder::adapt_non_coef_probs()
probs.mv_hp_prob()[i] = adapt_prob(probs.mv_hp_prob()[i], counter.m_counts_mv_hp[i]);
}
}
return true;
return {};
}
void Decoder::adapt_probs(int const* tree, u8* probs, u8* counts)
@ -150,25 +152,25 @@ u8 Decoder::adapt_prob(u8 prob, u8 counts[2])
return merge_prob(prob, counts[0], counts[1], COUNT_SAT, MAX_UPDATE_FACTOR);
}
bool Decoder::predict_intra(size_t, u32, u32, bool, bool, bool, TXSize, u32)
ErrorOr<void> Decoder::predict_intra(size_t, u32, u32, bool, bool, bool, TXSize, u32)
{
// TODO: Implement
return true;
return Error::from_string_literal("predict_intra not implemented");
}
bool Decoder::predict_inter(size_t, u32, u32, u32, u32, u32)
ErrorOr<void> Decoder::predict_inter(size_t, u32, u32, u32, u32, u32)
{
// TODO: Implement
return true;
return Error::from_string_literal("predict_inter not implemented");
}
bool Decoder::reconstruct(size_t, u32, u32, TXSize)
ErrorOr<void> Decoder::reconstruct(size_t, u32, u32, TXSize)
{
// TODO: Implement
return true;
return Error::from_string_literal("reconstruct not implemented");
}
bool Decoder::update_reference_frames()
ErrorOr<void> Decoder::update_reference_frames()
{
for (auto i = 0; i < NUM_REF_FRAMES; i++) {
dbgln("updating frame {}? {}", i, (m_parser->m_refresh_frame_flags & (1 << i)) == 1);
@ -179,7 +181,7 @@ bool Decoder::update_reference_frames()
// TODO: 1.3-1.7
}
// TODO: 2.1-2.2
return true;
return {};
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -8,6 +9,7 @@
#include "Parser.h"
#include <AK/ByteBuffer.h>
#include <AK/Error.h>
namespace Video::VP9 {
@ -16,27 +18,27 @@ class Decoder {
public:
Decoder();
bool decode_frame(ByteBuffer const&);
ErrorOr<void> decode_frame(ByteBuffer const&);
void dump_frame_info();
private:
/* (8.4) Probability Adaptation Process */
u8 merge_prob(u8 pre_prob, u8 count_0, u8 count_1, u8 count_sat, u8 max_update_factor);
u8 merge_probs(int const* tree, int index, u8* probs, u8* counts, u8 count_sat, u8 max_update_factor);
bool adapt_coef_probs();
bool adapt_non_coef_probs();
ErrorOr<void> adapt_coef_probs();
ErrorOr<void> adapt_non_coef_probs();
void adapt_probs(int const* tree, u8* probs, u8* counts);
u8 adapt_prob(u8 prob, u8 counts[2]);
/* (8.5) Prediction Processes */
bool predict_intra(size_t plane, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index);
bool predict_inter(size_t plane, u32 x, u32 y, u32 w, u32 h, u32 block_index);
ErrorOr<void> predict_intra(size_t plane, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index);
ErrorOr<void> predict_inter(size_t plane, u32 x, u32 y, u32 w, u32 h, u32 block_index);
/* (8.6) Reconstruction and Dequantization */
bool reconstruct(size_t plane, u32 x, u32 y, TXSize size);
ErrorOr<void> reconstruct(size_t plane, u32 x, u32 y, TXSize size);
/* (8.10) Reference Frame Update Process */
bool update_reference_frames();
ErrorOr<void> update_reference_frames();
NonnullOwnPtr<Parser> m_parser;
};

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -26,20 +27,20 @@ class Parser {
public:
explicit Parser(Decoder&);
~Parser();
bool parse_frame(ByteBuffer const&);
ErrorOr<void> parse_frame(ByteBuffer const&);
void dump_info();
private:
FrameType read_frame_type()
ErrorOr<FrameType> read_frame_type()
{
if (m_bit_stream->read_bit())
if (TRY(m_bit_stream->read_bit()))
return NonKeyFrame;
return KeyFrame;
}
ColorRange read_color_range()
ErrorOr<ColorRange> read_color_range()
{
if (m_bit_stream->read_bit())
if (TRY(m_bit_stream->read_bit()))
return FullSwing;
return StudioSwing;
}
@ -52,83 +53,83 @@ private:
/* (6.1) Frame Syntax */
bool trailing_bits();
bool refresh_probs();
ErrorOr<void> refresh_probs();
/* (6.2) Uncompressed Header Syntax */
bool uncompressed_header();
bool frame_sync_code();
bool color_config();
bool frame_size();
bool render_size();
bool frame_size_with_refs();
bool compute_image_size();
bool read_interpolation_filter();
bool loop_filter_params();
bool quantization_params();
i8 read_delta_q();
bool segmentation_params();
u8 read_prob();
bool tile_info();
ErrorOr<void> uncompressed_header();
ErrorOr<void> frame_sync_code();
ErrorOr<void> color_config();
ErrorOr<void> frame_size();
ErrorOr<void> render_size();
ErrorOr<void> frame_size_with_refs();
void compute_image_size();
ErrorOr<void> read_interpolation_filter();
ErrorOr<void> loop_filter_params();
ErrorOr<void> quantization_params();
ErrorOr<i8> read_delta_q();
ErrorOr<void> segmentation_params();
ErrorOr<u8> read_prob();
ErrorOr<void> tile_info();
u16 calc_min_log2_tile_cols();
u16 calc_max_log2_tile_cols();
bool setup_past_independence();
void setup_past_independence();
/* (6.3) Compressed Header Syntax */
bool compressed_header();
bool read_tx_mode();
bool tx_mode_probs();
u8 diff_update_prob(u8 prob);
u8 decode_term_subexp();
ErrorOr<void> compressed_header();
ErrorOr<void> read_tx_mode();
ErrorOr<void> tx_mode_probs();
ErrorOr<u8> diff_update_prob(u8 prob);
ErrorOr<u8> decode_term_subexp();
u8 inv_remap_prob(u8 delta_prob, u8 prob);
u8 inv_recenter_nonneg(u8 v, u8 m);
bool read_coef_probs();
bool read_skip_prob();
bool read_inter_mode_probs();
bool read_interp_filter_probs();
bool read_is_inter_probs();
bool frame_reference_mode();
bool frame_reference_mode_probs();
bool read_y_mode_probs();
bool read_partition_probs();
bool mv_probs();
u8 update_mv_prob(u8 prob);
bool setup_compound_reference_mode();
ErrorOr<void> read_coef_probs();
ErrorOr<void> read_skip_prob();
ErrorOr<void> read_inter_mode_probs();
ErrorOr<void> read_interp_filter_probs();
ErrorOr<void> read_is_inter_probs();
ErrorOr<void> frame_reference_mode();
ErrorOr<void> frame_reference_mode_probs();
ErrorOr<void> read_y_mode_probs();
ErrorOr<void> read_partition_probs();
ErrorOr<void> mv_probs();
ErrorOr<u8> update_mv_prob(u8 prob);
void setup_compound_reference_mode();
/* (6.4) Decode Tiles Syntax */
bool decode_tiles();
bool clear_above_context();
ErrorOr<void> decode_tiles();
void clear_above_context();
u32 get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2);
bool decode_tile();
bool clear_left_context();
bool decode_partition(u32 row, u32 col, u8 block_subsize);
bool decode_block(u32 row, u32 col, u8 subsize);
bool mode_info();
bool intra_frame_mode_info();
bool intra_segment_id();
bool read_skip();
ErrorOr<void> decode_tile();
void clear_left_context();
ErrorOr<void> decode_partition(u32 row, u32 col, u8 block_subsize);
ErrorOr<void> decode_block(u32 row, u32 col, u8 subsize);
ErrorOr<void> mode_info();
ErrorOr<void> intra_frame_mode_info();
ErrorOr<void> intra_segment_id();
ErrorOr<void> read_skip();
bool seg_feature_active(u8 feature);
bool read_tx_size(bool allow_select);
bool inter_frame_mode_info();
bool inter_segment_id();
ErrorOr<void> read_tx_size(bool allow_select);
ErrorOr<void> inter_frame_mode_info();
ErrorOr<void> inter_segment_id();
u8 get_segment_id();
bool read_is_inter();
bool intra_block_mode_info();
bool inter_block_mode_info();
bool read_ref_frames();
bool assign_mv(bool is_compound);
bool read_mv(u8 ref);
i32 read_mv_component(u8 component);
bool residual();
ErrorOr<void> read_is_inter();
ErrorOr<void> intra_block_mode_info();
ErrorOr<void> inter_block_mode_info();
ErrorOr<void> read_ref_frames();
ErrorOr<void> assign_mv(bool is_compound);
ErrorOr<void> read_mv(u8 ref);
ErrorOr<i32> read_mv_component(u8 component);
ErrorOr<void> residual();
TXSize get_uv_tx_size();
BlockSubsize get_plane_block_size(u32 subsize, u8 plane);
bool tokens(size_t plane, u32 x, u32 y, TXSize tx_size, u32 block_index);
ErrorOr<bool> tokens(size_t plane, u32 x, u32 y, TXSize tx_size, u32 block_index);
u32 const* get_scan(size_t plane, TXSize tx_size, u32 block_index);
u32 read_coef(Token token);
ErrorOr<i32> read_coef(Token token);
/* (6.5) Motion Vector Prediction */
bool find_mv_refs(ReferenceFrame, int block);
bool find_best_ref_mvs(int ref_list);
bool append_sub8x8_mvs(u8 block, u8 ref_list);
ErrorOr<void> find_mv_refs(ReferenceFrame, int block);
ErrorOr<void> find_best_ref_mvs(int ref_list);
ErrorOr<void> append_sub8x8_mvs(u8 block, u8 ref_list);
bool use_mv_hp(MV const& delta_mv);
u8 m_profile { 0 };

View file

@ -11,7 +11,7 @@
namespace Video::VP9 {
template<typename T>
T TreeParser::parse_tree(SyntaxElementType type)
ErrorOr<T> TreeParser::parse_tree(SyntaxElementType type)
{
auto tree_selection = select_tree(type);
int value;
@ -21,7 +21,7 @@ T TreeParser::parse_tree(SyntaxElementType type)
auto tree = tree_selection.get_tree_value();
int n = 0;
do {
n = tree[n + m_decoder.m_bit_stream->read_bool(select_tree_probability(type, n >> 1))];
n = tree[n + TRY(m_decoder.m_bit_stream->read_bool(select_tree_probability(type, n >> 1)))];
} while (n > 0);
value = -n;
}
@ -29,17 +29,17 @@ T TreeParser::parse_tree(SyntaxElementType type)
return static_cast<T>(value);
}
template int TreeParser::parse_tree(SyntaxElementType);
template bool TreeParser::parse_tree(SyntaxElementType);
template u8 TreeParser::parse_tree(SyntaxElementType);
template u32 TreeParser::parse_tree(SyntaxElementType);
template IntraMode TreeParser::parse_tree(SyntaxElementType);
template TXSize TreeParser::parse_tree(SyntaxElementType);
template InterpolationFilter TreeParser::parse_tree(SyntaxElementType);
template ReferenceMode TreeParser::parse_tree(SyntaxElementType);
template Token TreeParser::parse_tree(SyntaxElementType);
template MvClass TreeParser::parse_tree(SyntaxElementType);
template MvJoint TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<int> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<bool> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<u8> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<u32> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<IntraMode> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<TXSize> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<InterpolationFilter> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<ReferenceMode> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<Token> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<MvClass> TreeParser::parse_tree(SyntaxElementType);
template ErrorOr<MvJoint> TreeParser::parse_tree(SyntaxElementType);
/*
* Select a tree value based on the type of syntax element being parsed, as well as some parser state, as specified in section 9.3.1

View file

@ -43,7 +43,7 @@ public:
/* (9.3.3) */
template<typename T = int>
T parse_tree(SyntaxElementType type);
ErrorOr<T> parse_tree(SyntaxElementType type);
/* (9.3.1) */
TreeSelection select_tree(SyntaxElementType type);
/* (9.3.2) */

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
* Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -10,14 +11,6 @@
namespace Video::VP9 {
#define SAFE_CALL(call) \
do { \
if (!(call)) [[unlikely]] { \
dbgln("FAILED " #call); \
return false; \
} \
} while (0)
u8 clip_3(u8 x, u8 y, u8 z);
u8 round_2(u8 x, u8 n);