diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Context.h | 103 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Decoder.cpp | 86 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Decoder.h | 18 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Parser.cpp | 462 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Parser.h | 109 |
5 files changed, 423 insertions, 355 deletions
diff --git a/Userland/Libraries/LibVideo/VP9/Context.h b/Userland/Libraries/LibVideo/VP9/Context.h index 7443a96d4b..b37560229f 100644 --- a/Userland/Libraries/LibVideo/VP9/Context.h +++ b/Userland/Libraries/LibVideo/VP9/Context.h @@ -12,9 +12,8 @@ #include <LibGfx/Size.h> #include <LibVideo/Color/CodingIndependentCodePoints.h> -#include <AK/Format.h> - #include "Enums.h" +#include "LookupTables.h" #include "MotionVector.h" namespace Video::VP9 { @@ -153,7 +152,7 @@ public: } template<typename OtherT, typename Function> - void copy_to(Vector2D<OtherT>& other, Function function) + void copy_to(Vector2D<OtherT>& other, Function function) const { VERIFY(width() <= other.width()); VERIFY(height() <= other.height()); @@ -163,7 +162,7 @@ public: } } - void copy_to(Vector2D<T>& other) + void copy_to(Vector2D<T>& other) const { VERIFY(width() <= other.width()); VERIFY(height() <= other.height()); @@ -238,4 +237,100 @@ struct PersistentBlockContext { u8 segment_id { 0 }; }; +struct FrameContext { +public: + u8 profile { 0 }; + + bool shows_existing_frame() const { return m_show_existing_frame; } + u8 existing_frame_index() const { return m_existing_frame_index; } + void set_existing_frame_to_show(u8 index) + { + m_show_existing_frame = true; + m_existing_frame_index = index; + } + + Gfx::Size<u32> size() const { return m_size; } + ErrorOr<void> set_size(Gfx::Size<u32> size) + { + m_size = size; + + // From spec, compute_image_size( ) + m_rows = (size.height() + 7u) >> 3u; + m_columns = (size.width() + 7u) >> 3u; + return m_block_contexts.try_resize(m_rows, m_columns); + } + u32 rows() const { return m_rows; } + u32 columns() const { return m_columns; } + u32 superblock_rows() const { return (rows() + 7u) >> 3u; } + u32 superblock_columns() const { return (columns() + 7u) >> 3u; } + + Vector2D<FrameBlockContext> const& block_contexts() const { return m_block_contexts; } + + Gfx::Size<u32> render_size { 0, 0 }; + + u16 header_size_in_bytes { 0 }; + +private: + friend struct TileContext; + + bool m_show_existing_frame { false }; + u8 m_existing_frame_index { 0 }; + + Gfx::Size<u32> m_size { 0, 0 }; + u32 m_rows { 0 }; + u32 m_columns { 0 }; + // FIXME: From spec: NOTE – We are using a 2D array to store the SubModes for clarity. It is possible to reduce memory + // consumption by only storing one intra mode for each 8x8 horizontal and vertical position, i.e. to use two 1D + // arrays instead. + // I think should also apply to other fields that are only accessed relative to the current block. Worth looking + // into how much of this context needs to be stored for the whole frame vs a row or column from the current tile. + Vector2D<FrameBlockContext> m_block_contexts; +}; + +struct TileContext { +public: + TileContext(FrameContext& frame_context, u32 rows_start, u32 rows_end, u32 columns_start, u32 columns_end) + : frame_context(frame_context) + , rows_start(rows_start) + , rows_end(rows_end) + , columns_start(columns_start) + , columns_end(columns_end) + , block_contexts_view(frame_context.m_block_contexts.view(rows_start, columns_start, rows_end - rows_start, columns_end - columns_start)) + { + } + + Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); } + + FrameContext const& frame_context; + u32 rows_start { 0 }; + u32 rows_end { 0 }; + u32 columns_start { 0 }; + u32 columns_end { 0 }; + Vector2DView<FrameBlockContext> block_contexts_view; +}; + +struct BlockContext { + BlockContext(TileContext& tile_context, u32 row, u32 column, BlockSubsize size) + : frame_context(tile_context.frame_context) + , tile_context(tile_context) + , row(row) + , column(column) + , size(size) + , contexts_view(tile_context.block_contexts_view.view(row - tile_context.rows_start, column - tile_context.columns_start, + min<u32>(num_8x8_blocks_high_lookup[size], tile_context.frame_context.rows() - row), + min<u32>(num_8x8_blocks_wide_lookup[size], tile_context.frame_context.columns() - column))) + { + } + + Vector2D<FrameBlockContext> const& frame_block_contexts() const { return frame_context.block_contexts(); } + + FrameContext const& frame_context; + TileContext const& tile_context; + u32 row { 0 }; + u32 column { 0 }; + BlockSubsize size; + + Vector2DView<FrameBlockContext> contexts_view; +}; + } diff --git a/Userland/Libraries/LibVideo/VP9/Decoder.cpp b/Userland/Libraries/LibVideo/VP9/Decoder.cpp index f4bae29a9c..e03800866d 100644 --- a/Userland/Libraries/LibVideo/VP9/Decoder.cpp +++ b/Userland/Libraries/LibVideo/VP9/Decoder.cpp @@ -55,7 +55,7 @@ DecoderErrorOr<void> Decoder::decode_frame(ReadonlyBytes frame_data) { // 1. The syntax elements for the coded frame are extracted as specified in sections 6 and 7. The syntax // tables include function calls indicating when the block decode processes should be triggered. - TRY(m_parser->parse_frame(frame_data)); + auto frame_context = TRY(m_parser->parse_frame(frame_data)); // 2. If loop_filter_level is not equal to 0, the loop filter process as specified in section 8.8 is invoked once the // coded frame has been decoded. @@ -70,17 +70,17 @@ DecoderErrorOr<void> Decoder::decode_frame(ReadonlyBytes frame_data) // 4. The output process as specified in section 8.9 is invoked. if (m_parser->m_show_frame) - TRY(create_video_frame()); + TRY(create_video_frame(frame_context)); // 5. The reference frame update process as specified in section 8.10 is invoked. - TRY(update_reference_frames()); + TRY(update_reference_frames(frame_context)); return {}; } -DecoderErrorOr<void> Decoder::create_video_frame() +DecoderErrorOr<void> Decoder::create_video_frame(FrameContext const& frame_context) { - u32 decoded_y_width = m_parser->m_mi_cols * 8; - Gfx::Size<u32> output_y_size = m_parser->m_frame_size; + u32 decoded_y_width = frame_context.columns() * 8; + Gfx::Size<u32> output_y_size = frame_context.size(); auto decoded_uv_width = decoded_y_width >> m_parser->m_subsampling_x; Gfx::Size<u32> output_uv_size = { output_y_size.width() >> m_parser->m_subsampling_x, @@ -125,10 +125,10 @@ inline size_t buffer_size(Gfx::Size<size_t> size) return buffer_size(size.width(), size.height()); } -DecoderErrorOr<void> Decoder::allocate_buffers() +DecoderErrorOr<void> Decoder::allocate_buffers(FrameContext const& frame_context) { for (size_t plane = 0; plane < 3; plane++) { - auto size = m_parser->get_decoded_size_for_plane(plane); + auto size = m_parser->get_decoded_size_for_plane(frame_context, plane); auto& output_buffer = get_output_buffer(plane); output_buffer.clear_with_capacity(); @@ -329,7 +329,7 @@ u8 Decoder::adapt_prob(u8 prob, u8 counts[2]) return merge_prob(prob, counts[0], counts[1], COUNT_SAT, MAX_UPDATE_FACTOR); } -DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index) +DecoderErrorOr<void> Decoder::predict_intra(u8 plane, BlockContext const& block_context, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index) { auto& frame_buffer = get_output_buffer(plane); @@ -346,7 +346,7 @@ DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_le PredictionMode mode; if (plane > 0) mode = m_parser->m_uv_mode; - else if (m_parser->m_mi_size >= Block_8x8) + else if (block_context.size >= Block_8x8) mode = m_parser->m_y_mode; else mode = m_parser->m_block_sub_modes[block_index]; @@ -363,8 +363,8 @@ DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_le // − maxY is set equal to ((MiRows * 8) >> subsampling_y) - 1. auto subsampling_x = plane > 0 ? m_parser->m_subsampling_x : false; auto subsampling_y = plane > 0 ? m_parser->m_subsampling_y : false; - auto max_x = ((m_parser->m_mi_cols * 8u) >> subsampling_x) - 1u; - auto max_y = ((m_parser->m_mi_rows * 8u) >> subsampling_y) - 1u; + auto max_x = ((block_context.frame_context.columns() * 8u) >> subsampling_x) - 1u; + auto max_y = ((block_context.frame_context.rows() * 8u) >> subsampling_y) - 1u; auto const frame_buffer_at = [&](u32 row, u32 column) -> u16& { const auto frame_stride = max_x + 1u; @@ -665,7 +665,7 @@ DecoderErrorOr<void> Decoder::predict_intra(u8 plane, u32 x, u32 y, bool have_le return {}; } -MotionVector Decoder::select_motion_vector(u8 plane, u8 ref_list, u32 block_index) +MotionVector Decoder::select_motion_vector(u8 plane, BlockContext const& block_context, u8 ref_list, u32 block_index) { // The inputs to this process are: // − a variable plane specifying which plane is being predicted, @@ -697,7 +697,7 @@ MotionVector Decoder::select_motion_vector(u8 plane, u8 ref_list, u32 block_inde // The motion vector array mv is derived as follows: // − If plane is equal to 0, or MiSize is greater than or equal to BLOCK_8X8, mv is set equal to // BlockMvs[ refList ][ blockIdx ]. - if (plane == 0 || m_parser->m_mi_size >= Block_8x8) + if (plane == 0 || block_context.size >= Block_8x8) return m_parser->m_block_mvs[ref_list][block_index]; // − Otherwise, if subsampling_x is equal to 0 and subsampling_y is equal to 0, mv is set equal to // BlockMvs[ refList ][ blockIdx ]. @@ -721,7 +721,7 @@ MotionVector Decoder::select_motion_vector(u8 plane, u8 ref_list, u32 block_inde + m_parser->m_block_mvs[ref_list][2] + m_parser->m_block_mvs[ref_list][3]); } -MotionVector Decoder::clamp_motion_vector(u8 plane, u32 block_row, u32 block_column, MotionVector vector) +MotionVector Decoder::clamp_motion_vector(u8 plane, BlockContext const& block_context, u32 block_row, u32 block_column, MotionVector vector) { // FIXME: This function is named very similarly to Parser::clamp_mv. Rename one or the other? @@ -734,14 +734,14 @@ MotionVector Decoder::clamp_motion_vector(u8 plane, u32 block_row, u32 block_col bool subsampling_y = plane > 0 ? m_parser->m_subsampling_y : false; // The output array clampedMv is specified by the following steps: - i32 blocks_high = num_8x8_blocks_high_lookup[m_parser->m_mi_size]; + i32 blocks_high = num_8x8_blocks_high_lookup[block_context.size]; // Casts must be done here to prevent subtraction underflow from wrapping the values. i32 mb_to_top_edge = -(static_cast<i32>(block_row * MI_SIZE) * 16) >> subsampling_y; - i32 mb_to_bottom_edge = (((static_cast<i32>(m_parser->m_mi_rows) - blocks_high - static_cast<i32>(block_row)) * MI_SIZE) * 16) >> subsampling_y; + i32 mb_to_bottom_edge = (((static_cast<i32>(block_context.frame_context.rows()) - blocks_high - static_cast<i32>(block_row)) * MI_SIZE) * 16) >> subsampling_y; - i32 blocks_wide = num_8x8_blocks_wide_lookup[m_parser->m_mi_size]; + i32 blocks_wide = num_8x8_blocks_wide_lookup[block_context.size]; i32 mb_to_left_edge = -(static_cast<i32>(block_column * MI_SIZE) * 16) >> subsampling_x; - i32 mb_to_right_edge = (((static_cast<i32>(m_parser->m_mi_cols) - blocks_wide - static_cast<i32>(block_column)) * MI_SIZE) * 16) >> subsampling_x; + i32 mb_to_right_edge = (((static_cast<i32>(block_context.frame_context.columns()) - blocks_wide - static_cast<i32>(block_column)) * MI_SIZE) * 16) >> subsampling_x; i32 subpel_left = (INTERP_EXTEND + ((blocks_wide * MI_SIZE) >> subsampling_x)) << SUBPEL_BITS; i32 subpel_right = subpel_left - SUBPEL_SHIFTS; @@ -753,16 +753,16 @@ MotionVector Decoder::clamp_motion_vector(u8 plane, u32 block_row, u32 block_col }; } -DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer) +DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, BlockContext const& block_context, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer) { VERIFY(width <= maximum_block_dimensions && height <= maximum_block_dimensions); // 2. The motion vector selection process in section 8.5.2.1 is invoked with plane, refList, blockIdx as inputs // and the output being the motion vector mv. - auto motion_vector = select_motion_vector(plane, ref_list, block_index); + auto motion_vector = select_motion_vector(plane, block_context, ref_list, block_index); // 3. The motion vector clamping process in section 8.5.2.2 is invoked with plane, mv as inputs and the output // being the clamped motion vector clampedMv - auto clamped_vector = clamp_motion_vector(plane, block_row, block_column, motion_vector); + auto clamped_vector = clamp_motion_vector(plane, block_context, block_row, block_column, motion_vector); // 4. The motion vector scaling process in section 8.5.2.3 is invoked with plane, refList, x, y, clampedMv as // inputs and the output being the initial location startX, startY, and the step sizes stepX, stepY. @@ -791,10 +791,10 @@ DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 blo if (m_parser->m_frame_store[reference_frame_index][plane].is_empty()) return DecoderError::format(DecoderErrorCategory::Corrupted, "Attempted to use reference frame {} that has not been saved", reference_frame_index); auto ref_frame_size = m_parser->m_ref_frame_size[reference_frame_index]; - auto double_frame_size = m_parser->m_frame_size.scaled_by(2); + auto double_frame_size = block_context.frame_context.size().scaled_by(2); if (double_frame_size.width() < ref_frame_size.width() || double_frame_size.height() < ref_frame_size.height()) return DecoderError::format(DecoderErrorCategory::Corrupted, "Inter frame size is too small relative to reference frame {}", reference_frame_index); - if (!ref_frame_size.scaled_by(16).contains(m_parser->m_frame_size)) + if (!ref_frame_size.scaled_by(16).contains(block_context.frame_context.size())) return DecoderError::format(DecoderErrorCategory::Corrupted, "Inter frame size is too large relative to reference frame {}", reference_frame_index); // FIXME: Convert all the operations in this function to vector operations supported by @@ -804,8 +804,8 @@ DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 blo // A variable yScale is set equal to (RefFrameHeight[ refIdx ] << REF_SCALE_SHIFT) / FrameHeight. // (xScale and yScale specify the size of the reference frame relative to the current frame in units where 16 is // equivalent to the reference frame having the same size.) - i32 x_scale = (ref_frame_size.width() << REF_SCALE_SHIFT) / m_parser->m_frame_size.width(); - i32 y_scale = (ref_frame_size.height() << REF_SCALE_SHIFT) / m_parser->m_frame_size.height(); + i32 x_scale = (ref_frame_size.width() << REF_SCALE_SHIFT) / block_context.frame_context.size().width(); + i32 y_scale = (ref_frame_size.height() << REF_SCALE_SHIFT) / block_context.frame_context.size().height(); // The variable baseX is set equal to (x * xScale) >> REF_SCALE_SHIFT. // The variable baseY is set equal to (y * yScale) >> REF_SCALE_SHIFT. @@ -923,7 +923,7 @@ DecoderErrorOr<void> Decoder::predict_inter_block(u8 plane, u8 ref_list, u32 blo return {}; } -DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index) +DecoderErrorOr<void> Decoder::predict_inter(u8 plane, BlockContext const& block_context, u32 x, u32 y, u32 width, u32 height, u32 block_index) { // The inter prediction process is invoked for inter coded blocks. When MiSize is smaller than BLOCK_8X8, the // prediction is done with a granularity of 4x4 samples, otherwise the whole plane is predicted at the same time. @@ -942,7 +942,7 @@ DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_c // 2. through 5. Array<u16, maximum_block_size> predicted_buffer; auto predicted_span = predicted_buffer.span().trim(width * height); - TRY(predict_inter_block(plane, 0, block_row, block_column, x, y, width, height, block_index, predicted_span)); + TRY(predict_inter_block(plane, block_context, 0, block_context.row, block_context.column, x, y, width, height, block_index, predicted_span)); auto predicted_buffer_at = [&](Span<u16> buffer, u32 row, u32 column) -> u16& { return buffer[row * width + column]; }; @@ -952,8 +952,8 @@ DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_c // The inter predicted samples are then derived as follows: auto& frame_buffer = get_output_buffer(plane); VERIFY(!frame_buffer.is_empty()); - auto frame_width = (m_parser->m_mi_cols * 8u) >> (plane > 0 ? m_parser->m_subsampling_x : false); - auto frame_height = (m_parser->m_mi_rows * 8u) >> (plane > 0 ? m_parser->m_subsampling_y : false); + auto frame_width = (block_context.frame_context.columns() * 8u) >> (plane > 0 ? m_parser->m_subsampling_x : false); + auto frame_height = (block_context.frame_context.rows() * 8u) >> (plane > 0 ? m_parser->m_subsampling_y : false); auto frame_buffer_at = [&](u32 row, u32 column) -> u16& { return frame_buffer[row * frame_width + column]; }; @@ -976,7 +976,7 @@ DecoderErrorOr<void> Decoder::predict_inter(u8 plane, u32 block_row, u32 block_c // for i = 0..h-1 and j = 0..w-1. Array<u16, maximum_block_size> second_predicted_buffer; auto second_predicted_span = second_predicted_buffer.span().trim(width * height); - TRY(predict_inter_block(plane, 1, block_row, block_column, x, y, width, height, block_index, second_predicted_span)); + TRY(predict_inter_block(plane, block_context, 1, block_context.row, block_context.column, x, y, width, height, block_index, second_predicted_span)); for (auto i = 0u; i < height_in_frame_buffer; i++) { for (auto j = 0u; j < width_in_frame_buffer; j++) @@ -1055,7 +1055,7 @@ u16 Decoder::get_ac_quant(u8 plane) return ac_q(static_cast<u8>(get_qindex() + offset)); } -DecoderErrorOr<void> Decoder::reconstruct(u8 plane, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size) +DecoderErrorOr<void> Decoder::reconstruct(u8 plane, BlockContext const& block_context, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size) { // 8.6.2 Reconstruct process @@ -1096,8 +1096,8 @@ DecoderErrorOr<void> Decoder::reconstruct(u8 plane, u32 transform_block_x, u32 t auto& current_buffer = get_output_buffer(plane); auto subsampling_x = (plane > 0 ? m_parser->m_subsampling_x : 0); auto subsampling_y = (plane > 0 ? m_parser->m_subsampling_y : 0); - auto frame_width = (m_parser->m_mi_cols * 8) >> subsampling_x; - auto frame_height = (m_parser->m_mi_rows * 8) >> subsampling_y; + auto frame_width = (block_context.frame_context.columns() * 8) >> subsampling_x; + auto frame_height = (block_context.frame_context.rows() * 8) >> subsampling_y; auto width_in_frame_buffer = min(block_size, frame_width - transform_block_x); auto height_in_frame_buffer = min(block_size, frame_height - transform_block_y); @@ -1742,7 +1742,7 @@ DecoderErrorOr<void> Decoder::inverse_transform_2d(Span<Intermediate> dequantize return {}; } -DecoderErrorOr<void> Decoder::update_reference_frames() +DecoderErrorOr<void> Decoder::update_reference_frames(FrameContext const& frame_context) { // This process is invoked as the final step in decoding a frame. // The inputs to this process are the samples in the current frame CurrFrame[ plane ][ x ][ y ]. @@ -1756,7 +1756,7 @@ DecoderErrorOr<void> Decoder::update_reference_frames() if ((refresh_flags & 1) != 0) { // − RefFrameWidth[ i ] is set equal to FrameWidth. // − RefFrameHeight[ i ] is set equal to FrameHeight. - m_parser->m_ref_frame_size[i] = m_parser->m_frame_size; + m_parser->m_ref_frame_size[i] = frame_context.size(); // − RefSubsamplingX[ i ] is set equal to subsampling_x. m_parser->m_ref_subsampling_x[i] = m_parser->m_subsampling_x; // − RefSubsamplingY[ i ] is set equal to subsampling_y. @@ -1773,9 +1773,9 @@ DecoderErrorOr<void> Decoder::update_reference_frames() // FIXME: Frame width is not equal to the buffer's stride. If we store the stride of the buffer with the reference // frame, we can just copy the framebuffer data instead. Alternatively, we should crop the output framebuffer. for (auto plane = 0u; plane < 3; plane++) { - auto width = m_parser->m_frame_size.width(); - auto height = m_parser->m_frame_size.height(); - auto stride = m_parser->m_mi_cols * 8; + auto width = frame_context.size().width(); + auto height = frame_context.size().height(); + auto stride = frame_context.columns() * 8; if (plane > 0) { width = (width + m_parser->m_subsampling_x) >> m_parser->m_subsampling_x; @@ -1800,8 +1800,8 @@ DecoderErrorOr<void> Decoder::update_reference_frames() } // 2. If show_existing_frame is equal to 0, the following applies: - if (!m_parser->m_show_existing_frame) { - DECODER_TRY_ALLOC(m_parser->m_previous_block_contexts.try_resize_to_match_other_vector2d(m_parser->m_frame_block_contexts)); + if (!frame_context.shows_existing_frame()) { + DECODER_TRY_ALLOC(m_parser->m_previous_block_contexts.try_resize_to_match_other_vector2d(frame_context.block_contexts())); // − PrevRefFrames[ row ][ col ][ list ] is set equal to RefFrames[ row ][ col ][ list ] for row = 0..MiRows-1, // for col = 0..MiCols-1, for list = 0..1. // − PrevMvs[ row ][ col ][ list ][ comp ] is set equal to Mvs[ row ][ col ][ list ][ comp ] for row = 0..MiRows-1, @@ -1812,8 +1812,8 @@ DecoderErrorOr<void> Decoder::update_reference_frames() // − show_existing_frame is equal to 0, // − segmentation_enabled is equal to 1, // − segmentation_update_map is equal to 1. - bool keep_segment_ids = !m_parser->m_show_existing_frame && m_parser->m_segmentation_enabled && m_parser->m_segmentation_update_map; - m_parser->m_frame_block_contexts.copy_to(m_parser->m_previous_block_contexts, [keep_segment_ids](FrameBlockContext context) { + bool keep_segment_ids = !frame_context.shows_existing_frame() && m_parser->m_segmentation_enabled && m_parser->m_segmentation_update_map; + frame_context.block_contexts().copy_to(m_parser->m_previous_block_contexts, [keep_segment_ids](FrameBlockContext context) { auto persistent_context = PersistentBlockContext(context); if (!keep_segment_ids) persistent_context.segment_id = 0; diff --git a/Userland/Libraries/LibVideo/VP9/Decoder.h b/Userland/Libraries/LibVideo/VP9/Decoder.h index 1a3506f33d..94543a8ee9 100644 --- a/Userland/Libraries/LibVideo/VP9/Decoder.h +++ b/Userland/Libraries/LibVideo/VP9/Decoder.h @@ -42,9 +42,9 @@ private: static constexpr size_t maximum_transform_size = 32ULL * 32ULL; DecoderErrorOr<void> decode_frame(ReadonlyBytes); - DecoderErrorOr<void> create_video_frame(); + DecoderErrorOr<void> create_video_frame(FrameContext const&); - DecoderErrorOr<void> allocate_buffers(); + DecoderErrorOr<void> allocate_buffers(FrameContext const&); Vector<Intermediate>& get_temp_buffer(u8 plane); Vector<u16>& get_output_buffer(u8 plane); @@ -58,18 +58,18 @@ private: /* (8.5) Prediction Processes */ // (8.5.1) Intra prediction process - DecoderErrorOr<void> predict_intra(u8 plane, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index); + DecoderErrorOr<void> predict_intra(u8 plane, BlockContext const& block_context, u32 x, u32 y, bool have_left, bool have_above, bool not_on_right, TXSize tx_size, u32 block_index); // (8.5.1) Inter prediction process - DecoderErrorOr<void> predict_inter(u8 plane, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index); + DecoderErrorOr<void> predict_inter(u8 plane, BlockContext const& block_context, u32 x, u32 y, u32 width, u32 height, u32 block_index); // (8.5.2.1) Motion vector selection process - MotionVector select_motion_vector(u8 plane, u8 ref_list, u32 block_index); + MotionVector select_motion_vector(u8 plane, BlockContext const&, u8 ref_list, u32 block_index); // (8.5.2.2) Motion vector clamping process - MotionVector clamp_motion_vector(u8 plane, u32 block_row, u32 block_column, MotionVector vector); + MotionVector clamp_motion_vector(u8 plane, BlockContext const&, u32 block_row, u32 block_column, MotionVector vector); // (8.5.2.3) Motion vector scaling process DecoderErrorOr<MotionVector> scale_motion_vector(u8 plane, u8 ref_list, u32 x, u32 y, MotionVector vector); // From (8.5.1) Inter prediction process, steps 2-5 - DecoderErrorOr<void> predict_inter_block(u8 plane, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer); + DecoderErrorOr<void> predict_inter_block(u8 plane, BlockContext const&, u8 ref_list, u32 block_row, u32 block_column, u32 x, u32 y, u32 width, u32 height, u32 block_index, Span<u16> block_buffer); /* (8.6) Reconstruction and Dequantization */ @@ -84,7 +84,7 @@ private: u16 get_ac_quant(u8 plane); // (8.6.2) Reconstruct process - DecoderErrorOr<void> reconstruct(u8 plane, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size); + DecoderErrorOr<void> reconstruct(u8 plane, BlockContext const&, u32 transform_block_x, u32 transform_block_y, TXSize transform_block_size); // (8.7) Inverse transform process DecoderErrorOr<void> inverse_transform_2d(Span<Intermediate> dequantized, u8 log2_of_block_size); @@ -140,7 +140,7 @@ private: inline DecoderErrorOr<void> inverse_asymmetric_discrete_sine_transform(Span<Intermediate> data, u8 log2_of_block_size); /* (8.10) Reference Frame Update Process */ - DecoderErrorOr<void> update_reference_frames(); + DecoderErrorOr<void> update_reference_frames(FrameContext const&); inline CodingIndependentCodePoints get_cicp_color_space(); diff --git a/Userland/Libraries/LibVideo/VP9/Parser.cpp b/Userland/Libraries/LibVideo/VP9/Parser.cpp index da8fb7311b..89cb7ef8ef 100644 --- a/Userland/Libraries/LibVideo/VP9/Parser.cpp +++ b/Userland/Libraries/LibVideo/VP9/Parser.cpp @@ -79,30 +79,34 @@ Vector<size_t> Parser::parse_superframe_sizes(ReadonlyBytes frame_data) } /* (6.1) */ -DecoderErrorOr<void> Parser::parse_frame(ReadonlyBytes frame_data) +DecoderErrorOr<FrameContext> Parser::parse_frame(ReadonlyBytes frame_data) { m_bit_stream = make<BitStream>(frame_data.data(), frame_data.size()); m_syntax_element_counter = make<SyntaxElementCounter>(); - TRY(uncompressed_header()); + auto frame_context = TRY(uncompressed_header()); if (!trailing_bits()) return DecoderError::corrupted("Trailing bits were non-zero"sv); - if (m_header_size_in_bytes == 0) + // FIXME: This should not be an error. Spec says that we consume padding bits until the end of the sample. + if (frame_context.header_size_in_bytes == 0) return DecoderError::corrupted("Frame header is zero-sized"sv); m_probability_tables->load_probs(m_frame_context_idx); m_probability_tables->load_probs2(m_frame_context_idx); m_syntax_element_counter->clear_counts(); - TRY_READ(m_bit_stream->init_bool(m_header_size_in_bytes)); + TRY_READ(m_bit_stream->init_bool(frame_context.header_size_in_bytes)); TRY(compressed_header()); TRY_READ(m_bit_stream->exit_bool()); - TRY(m_decoder.allocate_buffers()); + TRY(m_decoder.allocate_buffers(frame_context)); - TRY(decode_tiles()); + TRY(decode_tiles(frame_context)); TRY(refresh_probs()); - return {}; + m_previous_frame_size = frame_context.size(); + m_previous_show_frame = m_show_frame; + + return frame_context; } bool Parser::trailing_bits() @@ -144,23 +148,25 @@ DecoderErrorOr<ColorRange> Parser::read_color_range() } /* (6.2) */ -DecoderErrorOr<void> Parser::uncompressed_header() +DecoderErrorOr<FrameContext> Parser::uncompressed_header() { + FrameContext frame_context; + auto frame_marker = TRY_READ(m_bit_stream->read_bits(2)); if (frame_marker != 2) return DecoderError::corrupted("uncompressed_header: Frame marker must be 2"sv); + auto profile_low_bit = TRY_READ(m_bit_stream->read_bit()); auto profile_high_bit = TRY_READ(m_bit_stream->read_bit()); - m_profile = (profile_high_bit << 1u) + profile_low_bit; - if (m_profile == 3 && TRY_READ(m_bit_stream->read_bit())) + frame_context.profile = (profile_high_bit << 1u) + profile_low_bit; + if (frame_context.profile == 3 && TRY_READ(m_bit_stream->read_bit())) return DecoderError::corrupted("uncompressed_header: Profile 3 reserved bit was non-zero"sv); - m_show_existing_frame = TRY_READ(m_bit_stream->read_bit()); - if (m_show_existing_frame) { - m_frame_to_show_map_index = TRY_READ(m_bit_stream->read_bits(3)); - m_header_size_in_bytes = 0; + + if (TRY_READ(m_bit_stream->read_bit())) { m_refresh_frame_flags = 0; m_loop_filter_level = 0; - return {}; + frame_context.set_existing_frame_to_show(TRY_READ(m_bit_stream->read_bits(3))); + return frame_context; } m_last_frame_type = m_frame_type; @@ -168,11 +174,14 @@ DecoderErrorOr<void> Parser::uncompressed_header() m_show_frame = TRY_READ(m_bit_stream->read_bit()); m_error_resilient_mode = TRY_READ(m_bit_stream->read_bit()); + Gfx::Size<u32> frame_size; + Gfx::Size<u32> render_size; + if (m_frame_type == KeyFrame) { TRY(frame_sync_code()); - TRY(color_config()); - m_frame_size = TRY(frame_size()); - m_render_size = TRY(render_size(m_frame_size)); + TRY(color_config(frame_context)); + frame_size = TRY(parse_frame_size()); + render_size = TRY(parse_render_size(frame_size)); m_refresh_frame_flags = 0xFF; m_frame_is_intra = true; } else { @@ -186,8 +195,8 @@ DecoderErrorOr<void> Parser::uncompressed_header() if (m_frame_is_intra) { TRY(frame_sync_code()); - if (m_profile > 0) { - TRY(color_config()); + if (frame_context.profile > 0) { + TRY(color_config(frame_context)); } else { m_color_space = Bt601; m_subsampling_x = true; @@ -196,22 +205,24 @@ DecoderErrorOr<void> Parser::uncompressed_header() } m_refresh_frame_flags = TRY_READ(m_bit_stream->read_f8()); - m_frame_size = TRY(frame_size()); - m_render_size = TRY(render_size(m_frame_size)); + frame_size = TRY(parse_frame_size()); + render_size = TRY(parse_render_size(frame_size)); } else { m_refresh_frame_flags = TRY_READ(m_bit_stream->read_f8()); for (auto i = 0; i < 3; i++) { m_ref_frame_idx[i] = TRY_READ(m_bit_stream->read_bits(3)); m_ref_frame_sign_bias[LastFrame + i] = TRY_READ(m_bit_stream->read_bit()); } - m_frame_size = TRY(frame_size_with_refs()); - m_render_size = TRY(render_size(m_frame_size)); + frame_size = TRY(parse_frame_size_with_refs()); + render_size = TRY(parse_render_size(frame_size)); m_allow_high_precision_mv = TRY_READ(m_bit_stream->read_bit()); TRY(read_interpolation_filter()); } } - compute_image_size(); + DECODER_TRY_ALLOC(frame_context.set_size(frame_size)); + frame_context.render_size = render_size; + TRY(compute_image_size(frame_context)); if (!m_error_resilient_mode) { m_refresh_frame_context = TRY_READ(m_bit_stream->read_bit()); @@ -237,11 +248,11 @@ DecoderErrorOr<void> Parser::uncompressed_header() TRY(loop_filter_params()); TRY(quantization_params()); TRY(segmentation_params()); - TRY(tile_info()); + TRY(tile_info(frame_context)); - m_header_size_in_bytes = TRY_READ(m_bit_stream->read_f16()); + frame_context.header_size_in_bytes = TRY_READ(m_bit_stream->read_f16()); - return {}; + return frame_context; } DecoderErrorOr<void> Parser::frame_sync_code() @@ -255,9 +266,9 @@ DecoderErrorOr<void> Parser::frame_sync_code() return {}; } -DecoderErrorOr<void> Parser::color_config() +DecoderErrorOr<void> Parser::color_config(FrameContext const& frame_context) { - if (m_profile >= 2) { + if (frame_context.profile >= 2) { m_bit_depth = TRY_READ(m_bit_stream->read_bit()) ? 12 : 10; } else { m_bit_depth = 8; @@ -269,7 +280,7 @@ DecoderErrorOr<void> Parser::color_config() if (color_space != RGB) { m_color_range = TRY(read_color_range()); - if (m_profile == 1 || m_profile == 3) { + if (frame_context.profile == 1 || frame_context.profile == 3) { m_subsampling_x = TRY_READ(m_bit_stream->read_bit()); m_subsampling_y = TRY_READ(m_bit_stream->read_bit()); if (TRY_READ(m_bit_stream->read_bit())) @@ -280,7 +291,7 @@ DecoderErrorOr<void> Parser::color_config() } } else { m_color_range = ColorRange::Full; - if (m_profile == 1 || m_profile == 3) { + if (frame_context.profile == 1 || frame_context.profile == 3) { m_subsampling_x = false; m_subsampling_y = false; if (TRY_READ(m_bit_stream->read_bit())) @@ -290,19 +301,19 @@ DecoderErrorOr<void> Parser::color_config() return {}; } -DecoderErrorOr<Gfx::Size<u32>> Parser::frame_size() +DecoderErrorOr<Gfx::Size<u32>> Parser::parse_frame_size() { return Gfx::Size<u32> { TRY_READ(m_bit_stream->read_f16()) + 1, TRY_READ(m_bit_stream->read_f16()) + 1 }; } -DecoderErrorOr<Gfx::Size<u32>> Parser::render_size(Gfx::Size<u32> frame_size) +DecoderErrorOr<Gfx::Size<u32>> Parser::parse_render_size(Gfx::Size<u32> frame_size) { if (!TRY_READ(m_bit_stream->read_bit())) return frame_size; return Gfx::Size<u32> { TRY_READ(m_bit_stream->read_f16()) + 1, TRY_READ(m_bit_stream->read_f16()) + 1 }; } -DecoderErrorOr<Gfx::Size<u32>> Parser::frame_size_with_refs() +DecoderErrorOr<Gfx::Size<u32>> Parser::parse_frame_size_with_refs() { Optional<Gfx::Size<u32>> size; for (auto frame_index : m_ref_frame_idx) { @@ -315,29 +326,23 @@ DecoderErrorOr<Gfx::Size<u32>> Parser::frame_size_with_refs() if (size.has_value()) return size.value(); - return TRY(frame_size()); + return TRY(parse_frame_size()); } -void Parser::compute_image_size() +DecoderErrorOr<void> Parser::compute_image_size(FrameContext& frame_context) { - auto new_cols = (m_frame_size.width() + 7u) >> 3u; - auto new_rows = (m_frame_size.height() + 7u) >> 3u; - // 7.2.6 Compute image size semantics // When compute_image_size is invoked, the following ordered steps occur: // 1. If this is the first time compute_image_size is invoked, or if either FrameWidth or FrameHeight have // changed in value compared to the previous time this function was invoked, then the segmentation map is // cleared to all zeros by setting SegmentId[ row ][ col ] equal to 0 for row = 0..MiRows-1 and col = // 0..MiCols-1. - bool first_invoke = !m_mi_cols && !m_mi_rows; - bool same_size = m_mi_cols == new_cols && m_mi_rows == new_rows; - if (first_invoke || !same_size) { - // FIXME: Does this ever do anything? Segment IDs are already reset every frame. It's also suspicious - // that spec refers to this as SegmentId rather than SegmentIds (plural). Is this supposed to - // refer to PrevSegmentIds? - for (size_t i = 0; i < m_frame_block_contexts.size(); i++) - m_frame_block_contexts[i].segment_id = 0; - } + // FIXME: What does this mean? SegmentIds is scoped to one frame, so it will not contain values here. It's + // also suspicious that spec refers to this as SegmentId rather than SegmentIds (plural). Is this + // supposed to refer to PrevSegmentIds? + bool first_invoke = m_is_first_compute_image_size_invoke; + m_is_first_compute_image_size_invoke = false; + bool same_size = m_previous_frame_size == frame_context.size(); // 2. The variable UsePrevFrameMvs is set equal to 1 if all of the following conditions are true: // a. This is not the first time compute_image_size is invoked. @@ -347,13 +352,8 @@ void Parser::compute_image_size() // d. error_resilient_mode is equal to 0. // e. FrameIsIntra is equal to 0. // Otherwise, UsePrevFrameMvs is set equal to 0. - m_use_prev_frame_mvs = !first_invoke && same_size && m_prev_show_frame && !m_error_resilient_mode && !m_frame_is_intra; - m_prev_show_frame = m_show_frame; - - m_mi_cols = new_cols; - m_mi_rows = new_rows; - m_sb64_cols = (m_mi_cols + 7u) >> 3u; - m_sb64_rows = (m_mi_rows + 7u) >> 3u; + m_use_prev_frame_mvs = !first_invoke && same_size && m_previous_show_frame && !m_error_resilient_mode && !m_frame_is_intra; + return {}; } DecoderErrorOr<void> Parser::read_interpolation_filter() @@ -452,10 +452,11 @@ DecoderErrorOr<u8> Parser::read_prob() return 255; } -DecoderErrorOr<void> Parser::tile_info() +DecoderErrorOr<void> Parser::tile_info(FrameContext& frame_context) { - auto min_log2_tile_cols = calc_min_log2_tile_cols(); - auto max_log2_tile_cols = calc_max_log2_tile_cols(); + auto superblock_columns = frame_context.superblock_columns(); + auto min_log2_tile_cols = calc_min_log2_tile_cols(superblock_columns); + auto max_log2_tile_cols = calc_max_log2_tile_cols(superblock_columns); m_tile_cols_log2 = min_log2_tile_cols; while (m_tile_cols_log2 < max_log2_tile_cols) { if (TRY_READ(m_bit_stream->read_bit())) @@ -470,18 +471,18 @@ DecoderErrorOr<void> Parser::tile_info() return {}; } -u16 Parser::calc_min_log2_tile_cols() +u16 Parser::calc_min_log2_tile_cols(u32 superblock_columns) { auto min_log_2 = 0u; - while ((u32)(MAX_TILE_WIDTH_B64 << min_log_2) < m_sb64_cols) + while ((u32)(MAX_TILE_WIDTH_B64 << min_log_2) < superblock_columns) min_log_2++; return min_log_2; } -u16 Parser::calc_max_log2_tile_cols() +u16 Parser::calc_max_log2_tile_cols(u32 superblock_columns) { u16 max_log_2 = 1; - while ((m_sb64_cols >> max_log_2) >= MIN_TILE_WIDTH_B64) + while ((superblock_columns >> max_log_2) >= MIN_TILE_WIDTH_B64) max_log_2++; return max_log_2 - 1; } @@ -494,7 +495,7 @@ void Parser::setup_past_independence() m_feature_enabled[i][j] = false; } } - m_previous_block_contexts.reset(); + m_previous_block_contexts.reset(); m_segmentation_abs_or_delta_update = false; m_loop_filter_delta_enabled = true; m_loop_filter_ref_deltas[IntraFrame] = 1; @@ -802,18 +803,12 @@ void Parser::setup_compound_reference_mode() } } -DecoderErrorOr<void> Parser::allocate_tile_data() -{ - DECODER_TRY_ALLOC(m_frame_block_contexts.try_resize(m_mi_rows, m_mi_cols)); - return {}; -} - -DecoderErrorOr<void> Parser::decode_tiles() +DecoderErrorOr<void> Parser::decode_tiles(FrameContext& frame_context) { auto tile_cols = 1 << m_tile_cols_log2; auto tile_rows = 1 << m_tile_rows_log2; - TRY(allocate_tile_data()); - clear_above_context(); + clear_above_context(frame_context); + for (auto tile_row = 0; tile_row < tile_rows; tile_row++) { for (auto tile_col = 0; tile_col < tile_cols; tile_col++) { auto last_tile = (tile_row == tile_rows - 1) && (tile_col == tile_cols - 1); @@ -823,12 +818,15 @@ DecoderErrorOr<void> Parser::decode_tiles() else tile_size = TRY_READ(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_end = get_tile_offset(tile_row + 1, m_mi_rows, m_tile_rows_log2); - m_mi_col_start = get_tile_offset(tile_col, m_mi_cols, m_tile_cols_log2); - m_mi_col_end = get_tile_offset(tile_col + 1, m_mi_cols, m_tile_cols_log2); + auto rows_start = get_tile_offset(tile_row, frame_context.rows(), m_tile_rows_log2); + auto rows_end = get_tile_offset(tile_row + 1, frame_context.rows(), m_tile_rows_log2); + auto columns_start = get_tile_offset(tile_col, frame_context.columns(), m_tile_cols_log2); + auto columns_end = get_tile_offset(tile_col + 1, frame_context.columns(), m_tile_cols_log2); + + auto tile_context = TileContext(frame_context, rows_start, rows_end, columns_start, columns_end); + TRY_READ(m_bit_stream->init_bool(tile_size)); - TRY(decode_tile()); + TRY(decode_tile(tile_context)); TRY_READ(m_bit_stream->exit_bool()); } } @@ -851,12 +849,12 @@ void Parser::clear_context(Vector<Vector<T>>& context, size_t outer_size, size_t clear_context(sub_vector, inner_size); } -void Parser::clear_above_context() +void Parser::clear_above_context(FrameContext& frame_context) { for (auto i = 0u; i < m_above_nonzero_context.size(); i++) - clear_context(m_above_nonzero_context[i], 2 * m_mi_cols); - clear_context(m_above_seg_pred_context, m_mi_cols); - clear_context(m_above_partition_context, m_sb64_cols * 8); + clear_context(m_above_nonzero_context[i], 2 * frame_context.columns()); + clear_context(m_above_seg_pred_context, frame_context.columns()); + clear_context(m_above_partition_context, frame_context.superblock_columns() * 8); } u32 Parser::get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2) @@ -866,51 +864,51 @@ u32 Parser::get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2) return min(offset, mis); } -DecoderErrorOr<void> Parser::decode_tile() +DecoderErrorOr<void> Parser::decode_tile(TileContext& tile_context) { - for (auto row = m_mi_row_start; row < m_mi_row_end; row += 8) { - clear_left_context(); - for (auto col = m_mi_col_start; col < m_mi_col_end; col += 8) { - TRY(decode_partition(row, col, Block_64x64)); + for (auto row = tile_context.rows_start; row < tile_context.rows_end; row += 8) { + clear_left_context(tile_context); + for (auto col = tile_context.columns_start; col < tile_context.columns_end; col += 8) { + TRY(decode_partition(tile_context, row, col, Block_64x64)); } } return {}; } -void Parser::clear_left_context() +void Parser::clear_left_context(TileContext& tile_context) { for (auto i = 0u; i < m_left_nonzero_context.size(); i++) - clear_context(m_left_nonzero_context[i], 2 * m_mi_rows); - clear_context(m_left_seg_pred_context, m_mi_rows); - clear_context(m_left_partition_context, m_sb64_rows * 8); + clear_context(m_left_nonzero_context[i], 2 * tile_context.frame_context.rows()); + clear_context(m_left_seg_pred_context, tile_context.frame_context.rows()); + clear_context(m_left_partition_context, tile_context.frame_context.superblock_rows() * 8); } -DecoderErrorOr<void> Parser::decode_partition(u32 row, u32 column, BlockSubsize subsize) +DecoderErrorOr<void> Parser::decode_partition(TileContext& tile_context, u32 row, u32 column, BlockSubsize subsize) { - if (row >= m_mi_rows || column >= m_mi_cols) + if (row >= tile_context.frame_context.rows() || column >= tile_context.frame_context.columns()) return {}; u8 num_8x8 = num_8x8_blocks_wide_lookup[subsize]; auto half_block_8x8 = num_8x8 >> 1; - bool has_rows = (row + half_block_8x8) < m_mi_rows; - bool has_cols = (column + half_block_8x8) < m_mi_cols; + bool has_rows = (row + half_block_8x8) < tile_context.frame_context.rows(); + bool has_cols = (column + half_block_8x8) < tile_context.frame_context.columns(); auto partition = TRY_READ(TreeParser::parse_partition(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, has_rows, has_cols, subsize, num_8x8, m_above_partition_context, m_left_partition_context, row, column, m_frame_is_intra)); auto child_subsize = subsize_lookup[partition][subsize]; if (child_subsize < Block_8x8 || partition == PartitionNone) { - TRY(decode_block(row, column, child_subsize)); + TRY(decode_block(tile_context, row, column, child_subsize)); } else if (partition == PartitionHorizontal) { - TRY(decode_block(row, column, child_subsize)); + TRY(decode_block(tile_context, row, column, child_subsize)); if (has_rows) - TRY(decode_block(row + half_block_8x8, column, child_subsize)); + TRY(decode_block(tile_context, row + half_block_8x8, column, child_subsize)); } else if (partition == PartitionVertical) { - TRY(decode_block(row, column, child_subsize)); + TRY(decode_block(tile_context, row, column, child_subsize)); if (has_cols) - TRY(decode_block(row, column + half_block_8x8, child_subsize)); + TRY(decode_block(tile_context, row, column + half_block_8x8, child_subsize)); } else { - TRY(decode_partition(row, column, child_subsize)); - TRY(decode_partition(row, column + half_block_8x8, child_subsize)); - TRY(decode_partition(row + half_block_8x8, column, child_subsize)); - TRY(decode_partition(row + half_block_8x8, column + half_block_8x8, child_subsize)); + TRY(decode_partition(tile_context, row, column, child_subsize)); + TRY(decode_partition(tile_context, row, column + half_block_8x8, child_subsize)); + TRY(decode_partition(tile_context, row + half_block_8x8, column, child_subsize)); + TRY(decode_partition(tile_context, row + half_block_8x8, column + half_block_8x8, child_subsize)); } if (subsize == Block_8x8 || partition != PartitionSplit) { auto above_context = 15 >> b_width_log2_lookup[child_subsize]; @@ -923,65 +921,62 @@ DecoderErrorOr<void> Parser::decode_partition(u32 row, u32 column, BlockSubsize return {}; } -size_t Parser::get_image_index(u32 row, u32 column) const +size_t Parser::get_image_index(FrameContext const& frame_context, u32 row, u32 column) const { - VERIFY(row < m_mi_rows && column < m_mi_cols); - return row * m_mi_cols + column; + VERIFY(row < frame_context.rows() && column < frame_context.columns()); + return row * frame_context.columns() + column; } -DecoderErrorOr<void> Parser::decode_block(u32 row, u32 column, BlockSubsize subsize) +DecoderErrorOr<void> Parser::decode_block(TileContext& tile_context, u32 row, u32 column, BlockSubsize subsize) { - m_mi_size = subsize; - auto above_context = row > 0 ? m_frame_block_contexts.at(row - 1, column) : FrameBlockContext(); - auto left_context = column > m_mi_col_start ? m_frame_block_contexts.at(row, column - 1) : FrameBlockContext(); - TRY(mode_info(row, column, above_context, left_context)); - auto had_residual_tokens = TRY(residual(row, column, above_context.is_available, left_context.is_available)); + auto above_context = row > 0 ? tile_context.frame_block_contexts().at(row - 1, column) : FrameBlockContext(); + auto left_context = column > tile_context.columns_start ? tile_context.frame_block_contexts().at(row, column - 1) : FrameBlockContext(); + auto block_context = BlockContext(tile_context, row, column, subsize); + + TRY(mode_info(block_context, above_context, left_context)); + auto had_residual_tokens = TRY(residual(block_context, above_context.is_available, left_context.is_available)); if (m_is_inter && subsize >= Block_8x8 && !had_residual_tokens) m_skip = true; - // Spec doesn't specify whether it might index outside the frame here, but it seems that it can. Ensure that we don't - // write out of bounds. This check seems consistent with libvpx. - // See here: - // https://github.com/webmproject/libvpx/blob/705bf9de8c96cfe5301451f1d7e5c90a41c64e5f/vp9/decoder/vp9_decodeframe.c#L917 - auto maximum_block_y = min<u32>(num_8x8_blocks_high_lookup[subsize], m_mi_rows - row); - auto maximum_block_x = min<u32>(num_8x8_blocks_wide_lookup[subsize], m_mi_cols - column); - - for (size_t y = 0; y < maximum_block_y; y++) { - for (size_t x = 0; x < maximum_block_x; x++) - m_frame_block_contexts.at(row + y, column + x) = FrameBlockContext { true, m_skip, m_tx_size, m_y_mode, m_block_sub_modes, m_interp_filter, m_ref_frame, m_block_mvs, m_segment_id }; + for (size_t y = 0; y < block_context.contexts_view.height(); y++) { + for (size_t x = 0; x < block_context.contexts_view.width(); x++) { + auto sub_block_context = FrameBlockContext { true, m_skip, m_tx_size, m_y_mode, m_block_sub_modes, m_interp_filter, m_ref_frame, m_block_mvs, m_segment_id }; + block_context.contexts_view.at(y, x) = sub_block_context; + VERIFY(block_context.frame_block_contexts().at(row + y, column + x).tx_size == sub_block_context.tx_size); + } } return {}; } -DecoderErrorOr<void> Parser::mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context) +DecoderErrorOr<void> Parser::mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context) { if (m_frame_is_intra) - TRY(intra_frame_mode_info(above_context, left_context)); + TRY(intra_frame_mode_info(block_context, above_context, left_context)); else - TRY(inter_frame_mode_info(row, column, above_context, left_context)); + TRY(inter_frame_mode_info(block_context, above_context, left_context)); return {}; } -DecoderErrorOr<void> Parser::intra_frame_mode_info(FrameBlockContext above_context, FrameBlockContext left_context) +DecoderErrorOr<void> Parser::intra_frame_mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context) { TRY(intra_segment_id()); TRY(read_skip(above_context, left_context)); - TRY(read_tx_size(above_context, left_context, true)); + TRY(read_tx_size(block_context, above_context, left_context, true)); m_ref_frame[0] = IntraFrame; m_ref_frame[1] = None; m_is_inter = false; // FIXME: This if statement is also present in parse_default_intra_mode. The selection of parameters for // the probability table lookup should be inlined here. - if (m_mi_size >= Block_8x8) { - m_y_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, m_mi_size, above_context, left_context, m_block_sub_modes, 0, 0)); + if (block_context.size >= Block_8x8) { + m_y_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, block_context.size, above_context, left_context, m_block_sub_modes, 0, 0)); for (auto& block_sub_mode : m_block_sub_modes) block_sub_mode = m_y_mode; } else { - m_num_4x4_w = num_4x4_blocks_wide_lookup[m_mi_size]; - m_num_4x4_h = num_4x4_blocks_high_lookup[m_mi_size]; + m_num_4x4_w = num_4x4_blocks_wide_lookup[block_context.size]; + m_num_4x4_h = num_4x4_blocks_high_lookup[block_context.size]; for (auto idy = 0; idy < 2; idy += m_num_4x4_h) { for (auto idx = 0; idx < 2; idx += m_num_4x4_w) { - auto sub_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, m_mi_size, above_context, left_context, m_block_sub_modes, idx, idy)); + auto sub_mode = TRY_READ(TreeParser::parse_default_intra_mode(*m_bit_stream, *m_probability_tables, block_context.size, above_context, left_context, m_block_sub_modes, idx, idy)); for (auto y = 0; y < m_num_4x4_h; y++) { for (auto x = 0; x < m_num_4x4_w; x++) { @@ -1021,37 +1016,38 @@ bool Parser::seg_feature_active(u8 feature) return m_segmentation_enabled && m_feature_enabled[m_segment_id][feature]; } -DecoderErrorOr<void> Parser::read_tx_size(FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select) +DecoderErrorOr<void> Parser::read_tx_size(BlockContext const& block_context, FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select) { - m_max_tx_size = max_txsize_lookup[m_mi_size]; - if (allow_select && m_tx_mode == TXModeSelect && m_mi_size >= Block_8x8) + // FIXME: This probably doesn't need to set max_tx_size, and can also return the TXSize it reads. + m_max_tx_size = max_txsize_lookup[block_context.size]; + if (allow_select && m_tx_mode == TXModeSelect && block_context.size >= Block_8x8) m_tx_size = TRY_READ(TreeParser::parse_tx_size(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_max_tx_size, above_context, left_context)); else m_tx_size = min(m_max_tx_size, tx_mode_to_biggest_tx_size[m_tx_mode]); return {}; } -DecoderErrorOr<void> Parser::inter_frame_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context) +DecoderErrorOr<void> Parser::inter_frame_mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context) { - TRY(inter_segment_id(row, column)); + TRY(inter_segment_id(block_context)); TRY(read_skip(above_context, left_context)); TRY(read_is_inter(above_context, left_context)); - TRY(read_tx_size(above_context, left_context, !m_skip || !m_is_inter)); + TRY(read_tx_size(block_context, above_context, left_context, !m_skip || !m_is_inter)); if (m_is_inter) { - TRY(inter_block_mode_info(row, column, above_context, left_context)); + TRY(inter_block_mode_info(block_context, above_context, left_context)); } else { - TRY(intra_block_mode_info()); + TRY(intra_block_mode_info(block_context)); } return {}; } -DecoderErrorOr<void> Parser::inter_segment_id(u32 row, u32 column) +DecoderErrorOr<void> Parser::inter_segment_id(BlockContext const& block_context) { if (!m_segmentation_enabled) { m_segment_id = 0; return {}; } - auto predicted_segment_id = get_segment_id(row, column); + auto predicted_segment_id = get_segment_id(block_context); if (!m_segmentation_update_map) { m_segment_id = predicted_segment_id; return {}; @@ -1061,37 +1057,37 @@ DecoderErrorOr<void> Parser::inter_segment_id(u32 row, u32 column) return {}; } - auto seg_id_predicted = TRY_READ(TreeParser::parse_segment_id_predicted(*m_bit_stream, m_segmentation_pred_prob, m_left_seg_pred_context[row], m_above_seg_pred_context[column])); + auto seg_id_predicted = TRY_READ(TreeParser::parse_segment_id_predicted(*m_bit_stream, m_segmentation_pred_prob, m_left_seg_pred_context[block_context.row], m_above_seg_pred_context[block_context.column])); if (seg_id_predicted) m_segment_id = predicted_segment_id; else m_segment_id = TRY_READ(TreeParser::parse_segment_id(*m_bit_stream, m_segmentation_tree_probs)); - for (size_t i = 0; i < num_8x8_blocks_wide_lookup[m_mi_size]; i++) { - auto index = column + i; + for (size_t i = 0; i < num_8x8_blocks_wide_lookup[block_context.size]; i++) { + auto index = block_context.column + i; // (7.4.1) AboveSegPredContext[ i ] only needs to be set to 0 for i = 0..MiCols-1. if (index < m_above_seg_pred_context.size()) m_above_seg_pred_context[index] = seg_id_predicted; } - for (size_t i = 0; i < num_8x8_blocks_high_lookup[m_mi_size]; i++) { - auto index = row + i; + for (size_t i = 0; i < num_8x8_blocks_high_lookup[block_context.size]; i++) { + auto index = block_context.row + i; // (7.4.1) LeftSegPredContext[ i ] only needs to be set to 0 for i = 0..MiRows-1. if (index < m_above_seg_pred_context.size()) - m_left_seg_pred_context[row + i] = seg_id_predicted; + m_left_seg_pred_context[block_context.row + i] = seg_id_predicted; } return {}; } -u8 Parser::get_segment_id(u32 row, u32 column) +u8 Parser::get_segment_id(BlockContext const& block_context) { - auto bw = num_8x8_blocks_wide_lookup[m_mi_size]; - auto bh = num_8x8_blocks_high_lookup[m_mi_size]; - auto xmis = min(m_mi_cols - column, (u32)bw); - auto ymis = min(m_mi_rows - row, (u32)bh); + auto bw = num_8x8_blocks_wide_lookup[block_context.size]; + auto bh = num_8x8_blocks_high_lookup[block_context.size]; + auto xmis = min(block_context.frame_context.columns() - block_context.column, (u32)bw); + auto ymis = min(block_context.frame_context.rows() - block_context.row, (u32)bh); u8 segment = 7; for (size_t y = 0; y < ymis; y++) { for (size_t x = 0; x < xmis; x++) { - segment = min(segment, m_previous_block_contexts.index_at(row + y, column + x)); + segment = min(segment, m_previous_block_contexts.index_at(block_context.row + y, block_context.column + x)); } } return segment; @@ -1106,17 +1102,17 @@ DecoderErrorOr<void> Parser::read_is_inter(FrameBlockContext above_context, Fram return {}; } -DecoderErrorOr<void> Parser::intra_block_mode_info() +DecoderErrorOr<void> Parser::intra_block_mode_info(BlockContext& block_context) { m_ref_frame[0] = IntraFrame; m_ref_frame[1] = None; - if (m_mi_size >= Block_8x8) { - m_y_mode = TRY_READ(TreeParser::parse_intra_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mi_size)); + if (block_context.size >= Block_8x8) { + m_y_mode = TRY_READ(TreeParser::parse_intra_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, block_context.size)); for (auto& block_sub_mode : m_block_sub_modes) block_sub_mode = m_y_mode; } else { - m_num_4x4_w = num_4x4_blocks_wide_lookup[m_mi_size]; - m_num_4x4_h = num_4x4_blocks_high_lookup[m_mi_size]; + m_num_4x4_w = num_4x4_blocks_wide_lookup[block_context.size]; + m_num_4x4_h = num_4x4_blocks_high_lookup[block_context.size]; PredictionMode sub_intra_mode; for (auto idy = 0; idy < 2; idy += m_num_4x4_h) { for (auto idx = 0; idx < 2; idx += m_num_4x4_w) { @@ -1133,34 +1129,34 @@ DecoderErrorOr<void> Parser::intra_block_mode_info() return {}; } -DecoderErrorOr<void> Parser::inter_block_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context) +DecoderErrorOr<void> Parser::inter_block_mode_info(BlockContext& block_context, FrameBlockContext above_context, FrameBlockContext left_context) { TRY(read_ref_frames(above_context, left_context)); for (auto j = 0; j < 2; j++) { if (m_ref_frame[j] > IntraFrame) { - find_mv_refs(row, column, m_ref_frame[j], -1); - find_best_ref_mvs(row, column, j); + find_mv_refs(block_context, m_ref_frame[j], -1); + find_best_ref_mvs(block_context, j); } } auto is_compound = m_ref_frame[1] > IntraFrame; if (seg_feature_active(SEG_LVL_SKIP)) { m_y_mode = PredictionMode::ZeroMv; - } else if (m_mi_size >= Block_8x8) { + } else if (block_context.size >= Block_8x8) { m_y_mode = TRY_READ(TreeParser::parse_inter_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mode_context[m_ref_frame[0]])); } if (m_interpolation_filter == Switchable) m_interp_filter = TRY_READ(TreeParser::parse_interpolation_filter(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, above_context, left_context)); else m_interp_filter = m_interpolation_filter; - if (m_mi_size < Block_8x8) { - m_num_4x4_w = num_4x4_blocks_wide_lookup[m_mi_size]; - m_num_4x4_h = num_4x4_blocks_high_lookup[m_mi_size]; + if (block_context.size < Block_8x8) { + m_num_4x4_w = num_4x4_blocks_wide_lookup[block_context.size]; + m_num_4x4_h = num_4x4_blocks_high_lookup[block_context.size]; for (auto idy = 0; idy < 2; idy += m_num_4x4_h) { for (auto idx = 0; idx < 2; idx += m_num_4x4_w) { m_y_mode = TRY_READ(TreeParser::parse_inter_mode(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, m_mode_context[m_ref_frame[0]])); if (m_y_mode == PredictionMode::NearestMv || m_y_mode == PredictionMode::NearMv) { for (auto j = 0; j < 1 + is_compound; j++) - append_sub8x8_mvs(row, column, idy * 2 + idx, j); + append_sub8x8_mvs(block_context, idy * 2 + idx, j); } TRY(assign_mv(is_compound)); for (auto y = 0; y < m_num_4x4_h; y++) { @@ -1277,46 +1273,47 @@ DecoderErrorOr<i32> Parser::read_mv_component(u8 component) return (mv_sign ? -1 : 1) * static_cast<i32>(magnitude); } -Gfx::Point<size_t> Parser::get_decoded_point_for_plane(u32 column, u32 row, u8 plane) +Gfx::Point<size_t> Parser::get_decoded_point_for_plane(FrameContext const& frame_context, u32 column, u32 row, u8 plane) { + (void)frame_context; if (plane == 0) return { column * 8, row * 8 }; return { (column * 8) >> m_subsampling_x, (row * 8) >> m_subsampling_y }; } -Gfx::Size<size_t> Parser::get_decoded_size_for_plane(u8 plane) +Gfx::Size<size_t> Parser::get_decoded_size_for_plane(FrameContext const& frame_context, u8 plane) { - auto point = get_decoded_point_for_plane(m_mi_cols, m_mi_rows, plane); + auto point = get_decoded_point_for_plane(frame_context, frame_context.columns(), frame_context.rows(), plane); return { point.x(), point.y() }; } -DecoderErrorOr<bool> Parser::residual(u32 row, u32 column, bool has_block_above, bool has_block_left) +DecoderErrorOr<bool> Parser::residual(BlockContext& block_context, bool has_block_above, bool has_block_left) { bool had_residual_tokens = false; - auto block_size = m_mi_size < Block_8x8 ? Block_8x8 : static_cast<BlockSubsize>(m_mi_size); + auto block_size = block_context.size < Block_8x8 ? Block_8x8 : block_context.size; for (u8 plane = 0; plane < 3; plane++) { - auto tx_size = (plane > 0) ? get_uv_tx_size() : m_tx_size; + auto tx_size = (plane > 0) ? get_uv_tx_size(block_context.size) : m_tx_size; auto step = 1 << tx_size; auto plane_size = get_plane_block_size(block_size, plane); auto num_4x4_w = num_4x4_blocks_wide_lookup[plane_size]; auto num_4x4_h = num_4x4_blocks_high_lookup[plane_size]; auto sub_x = (plane > 0) ? m_subsampling_x : 0; auto sub_y = (plane > 0) ? m_subsampling_y : 0; - auto base_x = (column * 8) >> sub_x; - auto base_y = (row * 8) >> sub_y; + auto base_x = (block_context.column * 8) >> sub_x; + auto base_y = (block_context.row * 8) >> sub_y; if (m_is_inter) { - if (m_mi_size < Block_8x8) { + if (block_context.size < Block_8x8) { for (auto y = 0; y < num_4x4_h; y++) { for (auto x = 0; x < num_4x4_w; x++) { - TRY(m_decoder.predict_inter(plane, row, column, base_x + (4 * x), base_y + (4 * y), 4, 4, (y * num_4x4_w) + x)); + TRY(m_decoder.predict_inter(plane, block_context, base_x + (4 * x), base_y + (4 * y), 4, 4, (y * num_4x4_w) + x)); } } } else { - TRY(m_decoder.predict_inter(plane, row, column, base_x, base_y, num_4x4_w * 4, num_4x4_h * 4, 0)); + TRY(m_decoder.predict_inter(plane, block_context, base_x, base_y, num_4x4_w * 4, num_4x4_h * 4, 0)); } } - auto max_x = (m_mi_cols * 8) >> sub_x; - auto max_y = (m_mi_rows * 8) >> sub_y; + auto max_x = (block_context.frame_context.columns() * 8) >> sub_x; + auto max_y = (block_context.frame_context.rows() * 8) >> sub_y; auto block_index = 0; for (auto y = 0; y < num_4x4_h; y += step) { for (auto x = 0; x < num_4x4_w; x += step) { @@ -1325,11 +1322,11 @@ DecoderErrorOr<bool> Parser::residual(u32 row, u32 column, bool has_block_above, auto non_zero = false; if (start_x < max_x && start_y < max_y) { if (!m_is_inter) - TRY(m_decoder.predict_intra(plane, start_x, start_y, has_block_left || x > 0, has_block_above || y > 0, (x + step) < num_4x4_w, tx_size, block_index)); + TRY(m_decoder.predict_intra(plane, block_context, start_x, start_y, has_block_left || x > 0, has_block_above || y > 0, (x + step) < num_4x4_w, tx_size, block_index)); if (!m_skip) { - non_zero = TRY(tokens(plane, start_x, start_y, tx_size, block_index)); + non_zero = TRY(tokens(block_context, plane, start_x, start_y, tx_size, block_index)); had_residual_tokens = had_residual_tokens || non_zero; - TRY(m_decoder.reconstruct(plane, start_x, start_y, tx_size)); + TRY(m_decoder.reconstruct(plane, block_context, start_x, start_y, tx_size)); } } @@ -1352,11 +1349,11 @@ DecoderErrorOr<bool> Parser::residual(u32 row, u32 column, bool has_block_above, return had_residual_tokens; } -TXSize Parser::get_uv_tx_size() +TXSize Parser::get_uv_tx_size(BlockSubsize size) { - if (m_mi_size < Block_8x8) + if (size < Block_8x8) return TX_4x4; - return min(m_tx_size, max_txsize_lookup[get_plane_block_size(m_mi_size, 1)]); + return min(m_tx_size, max_txsize_lookup[get_plane_block_size(size, 1)]); } BlockSubsize Parser::get_plane_block_size(u32 subsize, u8 plane) @@ -1366,16 +1363,16 @@ BlockSubsize Parser::get_plane_block_size(u32 subsize, u8 plane) return ss_size_lookup[subsize][sub_x][sub_y]; } -DecoderErrorOr<bool> Parser::tokens(size_t plane, u32 start_x, u32 start_y, TXSize tx_size, u32 block_index) +DecoderErrorOr<bool> Parser::tokens(BlockContext& block_context, size_t plane, u32 start_x, u32 start_y, TXSize tx_size, u32 block_index) { u32 segment_eob = 16 << (tx_size << 1); - auto const* scan = get_scan(plane, tx_size, block_index); + auto const* scan = get_scan(block_context, plane, tx_size, block_index); auto check_eob = true; u32 c = 0; for (; c < segment_eob; c++) { auto pos = scan[c]; auto band = (tx_size == TX_4x4) ? coefband_4x4[c] : coefband_8x8plus[c]; - auto tokens_context = TreeParser::get_tokens_context(m_subsampling_x, m_subsampling_y, m_mi_rows, m_mi_cols, m_above_nonzero_context, m_left_nonzero_context, m_token_cache, tx_size, m_tx_type, plane, start_x, start_y, pos, m_is_inter, band, c); + auto tokens_context = TreeParser::get_tokens_context(m_subsampling_x, m_subsampling_y, block_context.frame_context.rows(), block_context.frame_context.columns(), m_above_nonzero_context, m_left_nonzero_context, m_token_cache, tx_size, m_tx_type, plane, start_x, start_y, pos, m_is_inter, band, c); if (check_eob) { auto more_coefs = TRY_READ(TreeParser::parse_more_coefficients(*m_bit_stream, *m_probability_tables, *m_syntax_element_counter, tokens_context)); if (!more_coefs) @@ -1398,7 +1395,7 @@ DecoderErrorOr<bool> Parser::tokens(size_t plane, u32 start_x, u32 start_y, TXSi return c > 0; } -u32 const* Parser::get_scan(size_t plane, TXSize tx_size, u32 block_index) +u32 const* Parser::get_scan(BlockContext const& block_context, size_t plane, TXSize tx_size, u32 block_index) { if (plane > 0 || tx_size == TX_32x32) { m_tx_type = DCT_DCT; @@ -1406,7 +1403,7 @@ u32 const* Parser::get_scan(size_t plane, TXSize tx_size, u32 block_index) if (m_lossless || m_is_inter) m_tx_type = DCT_DCT; else - m_tx_type = mode_to_txfm_map[to_underlying(m_mi_size < Block_8x8 ? m_block_sub_modes[block_index] : m_y_mode)]; + m_tx_type = mode_to_txfm_map[to_underlying(block_context.size < Block_8x8 ? m_block_sub_modes[block_index] : m_y_mode)]; } else { m_tx_type = mode_to_txfm_map[to_underlying(m_y_mode)]; } @@ -1452,15 +1449,15 @@ DecoderErrorOr<i32> Parser::read_coef(Token token) return coef; } -bool Parser::is_inside(i32 row, i32 column) +static bool is_inside(TileContext const& tile_context, MotionVector vector) { - if (row < 0) + if (vector.row() < 0) return false; - if (column < 0) + if (vector.column() < 0) return false; - u32 row_positive = row; - u32 column_positive = column; - return row_positive < m_mi_rows && column_positive >= m_mi_col_start && column_positive < m_mi_col_end; + u32 row_positive = vector.row(); + u32 column_positive = vector.column(); + return row_positive < tile_context.frame_context.rows() && column_positive >= tile_context.columns_start && column_positive < tile_context.columns_end; } void Parser::add_mv_ref_list(u8 ref_list) @@ -1474,23 +1471,23 @@ void Parser::add_mv_ref_list(u8 ref_list) m_ref_mv_count++; } -void Parser::get_block_mv(u32 candidate_row, u32 candidate_column, u8 ref_list, bool use_prev) +void Parser::get_block_mv(BlockContext const& block_context, MotionVector candidate_vector, u8 ref_list, bool use_prev) { if (use_prev) { - auto const& prev_context = m_previous_block_contexts.at(candidate_row, candidate_column); + auto const& prev_context = m_previous_block_contexts.at(candidate_vector.row(), candidate_vector.column()); m_candidate_mv[ref_list] = prev_context.primary_motion_vector_pair[ref_list]; m_candidate_frame[ref_list] = prev_context.ref_frames[ref_list]; } else { - auto const& current_context = m_frame_block_contexts.at(candidate_row, candidate_column); + auto const& current_context = block_context.frame_block_contexts().at(candidate_vector.row(), candidate_vector.column()); m_candidate_mv[ref_list] = current_context.primary_motion_vector_pair()[ref_list]; m_candidate_frame[ref_list] = current_context.ref_frames[ref_list]; } } -void Parser::if_same_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev) +void Parser::if_same_ref_frame_add_mv(BlockContext const& block_context, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev) { for (auto ref_list = 0u; ref_list < 2; ref_list++) { - get_block_mv(candidate_row, candidate_column, ref_list, use_prev); + get_block_mv(block_context, candidate_vector, ref_list, use_prev); if (m_candidate_frame[ref_list] == ref_frame) { add_mv_ref_list(ref_list); return; @@ -1505,10 +1502,10 @@ void Parser::scale_mv(u8 ref_list, ReferenceFrameType ref_frame) m_candidate_mv[ref_list] *= -1; } -void Parser::if_diff_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev) +void Parser::if_diff_ref_frame_add_mv(BlockContext const& block_context, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev) { for (auto ref_list = 0u; ref_list < 2; ref_list++) - get_block_mv(candidate_row, candidate_column, ref_list, use_prev); + get_block_mv(block_context, candidate_vector, ref_list, use_prev); auto mvs_are_same = m_candidate_mv[0] == m_candidate_mv[1]; if (m_candidate_frame[0] > ReferenceFrameType::IntraFrame && m_candidate_frame[0] != ref_frame) { scale_mv(0, ref_frame); @@ -1520,16 +1517,16 @@ void Parser::if_diff_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, R } } -MotionVector Parser::clamp_mv(u32 row, u32 column, MotionVector vector, i32 border) +MotionVector Parser::clamp_mv(BlockContext const& block_context, MotionVector vector, i32 border) { - i32 blocks_high = num_8x8_blocks_high_lookup[m_mi_size]; + i32 blocks_high = num_8x8_blocks_high_lookup[block_context.size]; // Casts must be done here to prevent subtraction underflow from wrapping the values. - i32 mb_to_top_edge = -8 * (static_cast<i32>(row) * MI_SIZE); - i32 mb_to_bottom_edge = 8 * ((static_cast<i32>(m_mi_rows) - blocks_high - static_cast<i32>(row)) * MI_SIZE); + i32 mb_to_top_edge = -8 * (static_cast<i32>(block_context.row) * MI_SIZE); + i32 mb_to_bottom_edge = 8 * ((static_cast<i32>(block_context.frame_context.rows()) - blocks_high - static_cast<i32>(block_context.row)) * MI_SIZE); - i32 blocks_wide = num_8x8_blocks_wide_lookup[m_mi_size]; - i32 mb_to_left_edge = -8 * (static_cast<i32>(column) * MI_SIZE); - i32 mb_to_right_edge = 8 * ((static_cast<i32>(m_mi_cols) - blocks_wide - static_cast<i32>(column)) * MI_SIZE); + i32 blocks_wide = num_8x8_blocks_wide_lookup[block_context.size]; + i32 mb_to_left_edge = -8 * (static_cast<i32>(block_context.column) * MI_SIZE); + i32 mb_to_right_edge = 8 * ((static_cast<i32>(block_context.frame_context.columns()) - blocks_wide - static_cast<i32>(block_context.column)) * MI_SIZE); return { clip_3(mb_to_top_edge - border, mb_to_bottom_edge + border, vector.row()), @@ -1537,14 +1534,15 @@ MotionVector Parser::clamp_mv(u32 row, u32 column, MotionVector vector, i32 bord }; } -void Parser::clamp_mv_ref(u32 row, u32 column, u8 i) +void Parser::clamp_mv_ref(BlockContext const& block_context, u8 i) { + // FIXME: This seems silly and should probably just be written inline in the one place it's used. MotionVector& vector = m_ref_list_mv[i]; - vector = clamp_mv(row, column, vector, MV_BORDER); + vector = clamp_mv(block_context, vector, MV_BORDER); } // 6.5.1 Find MV refs syntax -void Parser::find_mv_refs(u32 row, u32 column, ReferenceFrameType reference_frame, i32 block) +void Parser::find_mv_refs(BlockContext& block_context, ReferenceFrameType reference_frame, i32 block) { m_ref_mv_count = 0; bool different_ref_found = false; @@ -1553,15 +1551,15 @@ void Parser::find_mv_refs(u32 row, u32 column, ReferenceFrameType reference_fram m_ref_list_mv[0] = {}; m_ref_list_mv[1] = {}; - MotionVector base_coordinates = MotionVector(row, column); + MotionVector base_coordinates = MotionVector(block_context.row, block_context.column); for (auto i = 0u; i < 2; i++) { - auto offset_vector = mv_ref_blocks[m_mi_size][i]; + auto offset_vector = mv_ref_blocks[block_context.size][i]; auto candidate = base_coordinates + offset_vector; - if (is_inside(candidate.row(), candidate.column())) { + if (is_inside(block_context.tile_context, candidate)) { different_ref_found = true; - auto context = m_frame_block_contexts.at(candidate.row(), candidate.column()); + auto context = block_context.frame_block_contexts().at(candidate.row(), candidate.column()); context_counter += mode_2_counter[to_underlying(context.y_mode)]; for (auto ref_list = 0u; ref_list < 2; ref_list++) { @@ -1584,28 +1582,28 @@ void Parser::find_mv_refs(u32 row, u32 column, ReferenceFrameType reference_fram } for (auto i = 2u; i < MVREF_NEIGHBOURS; i++) { - MotionVector candidate = base_coordinates + mv_ref_blocks[m_mi_size][i]; - if (is_inside(candidate.row(), candidate.column())) { + MotionVector candidate = base_coordinates + mv_ref_blocks[block_context.size][i]; + if (is_inside(block_context.tile_context, candidate)) { different_ref_found = true; - if_same_ref_frame_add_mv(candidate.row(), candidate.column(), reference_frame, false); + if_same_ref_frame_add_mv(block_context, candidate, reference_frame, false); } } if (m_use_prev_frame_mvs) - if_same_ref_frame_add_mv(row, column, reference_frame, true); + if_same_ref_frame_add_mv(block_context, base_coordinates, reference_frame, true); if (different_ref_found) { for (auto i = 0u; i < MVREF_NEIGHBOURS; i++) { - MotionVector candidate = base_coordinates + mv_ref_blocks[m_mi_size][i]; - if (is_inside(candidate.row(), candidate.column())) - if_diff_ref_frame_add_mv(candidate.row(), candidate.column(), reference_frame, false); + MotionVector candidate = base_coordinates + mv_ref_blocks[block_context.size][i]; + if (is_inside(block_context.tile_context, candidate)) + if_diff_ref_frame_add_mv(block_context, candidate, reference_frame, false); } } if (m_use_prev_frame_mvs) - if_diff_ref_frame_add_mv(row, column, reference_frame, true); + if_diff_ref_frame_add_mv(block_context, base_coordinates, reference_frame, true); m_mode_context[reference_frame] = counter_to_context[context_counter]; for (auto i = 0u; i < MAX_MV_REF_CANDIDATES; i++) - clamp_mv_ref(row, column, i); + clamp_mv_ref(block_context, i); } bool Parser::use_mv_hp(MotionVector const& vector) @@ -1613,7 +1611,7 @@ bool Parser::use_mv_hp(MotionVector const& vector) return (abs(vector.row()) >> 3) < COMPANDED_MVREF_THRESH && (abs(vector.column()) >> 3) < COMPANDED_MVREF_THRESH; } -void Parser::find_best_ref_mvs(u32 row, u32 column, u8 ref_list) +void Parser::find_best_ref_mvs(BlockContext& block_context, u8 ref_list) { for (auto i = 0u; i < MAX_MV_REF_CANDIDATES; i++) { auto delta = m_ref_list_mv[i]; @@ -1626,7 +1624,7 @@ void Parser::find_best_ref_mvs(u32 row, u32 column, u8 ref_list) delta_column += delta_column > 0 ? -1 : 1; } delta = { delta_row, delta_column }; - m_ref_list_mv[i] = clamp_mv(row, column, delta, (BORDERINPIXELS - INTERP_EXTEND) << 3); + m_ref_list_mv[i] = clamp_mv(block_context, delta, (BORDERINPIXELS - INTERP_EXTEND) << 3); } m_nearest_mv[ref_list] = m_ref_list_mv[0]; @@ -1634,10 +1632,10 @@ void Parser::find_best_ref_mvs(u32 row, u32 column, u8 ref_list) m_best_mv[ref_list] = m_ref_list_mv[0]; } -void Parser::append_sub8x8_mvs(u32 row, u32 column, i32 block, u8 ref_list) +void Parser::append_sub8x8_mvs(BlockContext& block_context, i32 block, u8 ref_list) { MotionVector sub_8x8_mvs[2]; - find_mv_refs(row, column, m_ref_frame[ref_list], block); + find_mv_refs(block_context, m_ref_frame[ref_list], block); auto destination_index = 0; if (block == 0) { for (auto i = 0u; i < 2; i++) diff --git a/Userland/Libraries/LibVideo/VP9/Parser.h b/Userland/Libraries/LibVideo/VP9/Parser.h index 5dd6e7772b..d8ff606b05 100644 --- a/Userland/Libraries/LibVideo/VP9/Parser.h +++ b/Userland/Libraries/LibVideo/VP9/Parser.h @@ -34,7 +34,7 @@ class Parser { public: explicit Parser(Decoder&); ~Parser(); - DecoderErrorOr<void> parse_frame(ReadonlyBytes); + DecoderErrorOr<FrameContext> parse_frame(ReadonlyBytes); private: /* Annex B: Superframes are a method of storing multiple coded frames into a single chunk @@ -49,30 +49,29 @@ private: void clear_context(Vector<T>& context, size_t size); template<typename T> void clear_context(Vector<Vector<T>>& context, size_t outer_size, size_t inner_size); - DecoderErrorOr<void> allocate_tile_data(); /* (6.1) Frame Syntax */ bool trailing_bits(); DecoderErrorOr<void> refresh_probs(); /* (6.2) Uncompressed Header Syntax */ - DecoderErrorOr<void> uncompressed_header(); + DecoderErrorOr<FrameContext> uncompressed_header(); DecoderErrorOr<void> frame_sync_code(); - DecoderErrorOr<void> color_config(); + DecoderErrorOr<void> color_config(FrameContext const&); DecoderErrorOr<void> set_frame_size_and_compute_image_size(); - DecoderErrorOr<Gfx::Size<u32>> frame_size(); - DecoderErrorOr<Gfx::Size<u32>> frame_size_with_refs(); - DecoderErrorOr<Gfx::Size<u32>> render_size(Gfx::Size<u32> frame_size); - void compute_image_size(); + DecoderErrorOr<Gfx::Size<u32>> parse_frame_size(); + DecoderErrorOr<Gfx::Size<u32>> parse_frame_size_with_refs(); + DecoderErrorOr<Gfx::Size<u32>> parse_render_size(Gfx::Size<u32> frame_size); + DecoderErrorOr<void> compute_image_size(FrameContext&); DecoderErrorOr<void> read_interpolation_filter(); DecoderErrorOr<void> loop_filter_params(); DecoderErrorOr<void> quantization_params(); DecoderErrorOr<i8> read_delta_q(); DecoderErrorOr<void> segmentation_params(); DecoderErrorOr<u8> read_prob(); - DecoderErrorOr<void> tile_info(); - u16 calc_min_log2_tile_cols(); - u16 calc_max_log2_tile_cols(); + DecoderErrorOr<void> tile_info(FrameContext&); + u16 calc_min_log2_tile_cols(u32 superblock_columns); + u16 calc_max_log2_tile_cols(u32 superblock_columns); void setup_past_independence(); /* (6.3) Compressed Header Syntax */ @@ -97,58 +96,53 @@ private: void setup_compound_reference_mode(); /* (6.4) Decode Tiles Syntax */ - DecoderErrorOr<void> decode_tiles(); - void clear_above_context(); + DecoderErrorOr<void> decode_tiles(FrameContext&); + void clear_above_context(FrameContext&); u32 get_tile_offset(u32 tile_num, u32 mis, u32 tile_size_log2); - DecoderErrorOr<void> decode_tile(); - void clear_left_context(); - DecoderErrorOr<void> decode_partition(u32 row, u32 column, BlockSubsize subsize); - DecoderErrorOr<void> decode_block(u32 row, u32 column, BlockSubsize subsize); - DecoderErrorOr<void> mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context); - DecoderErrorOr<void> intra_frame_mode_info(FrameBlockContext above_context, FrameBlockContext left_context); + DecoderErrorOr<void> decode_tile(TileContext&); + void clear_left_context(TileContext&); + DecoderErrorOr<void> decode_partition(TileContext&, u32 row, u32 column, BlockSubsize subsize); + DecoderErrorOr<void> decode_block(TileContext&, u32 row, u32 column, BlockSubsize subsize); + DecoderErrorOr<void> mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context); + DecoderErrorOr<void> intra_frame_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> intra_segment_id(); DecoderErrorOr<void> read_skip(FrameBlockContext above_context, FrameBlockContext left_context); bool seg_feature_active(u8 feature); - DecoderErrorOr<void> read_tx_size(FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select); - DecoderErrorOr<void> inter_frame_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context); - DecoderErrorOr<void> inter_segment_id(u32 row, u32 column); - u8 get_segment_id(u32 row, u32 column); + DecoderErrorOr<void> read_tx_size(BlockContext const&, FrameBlockContext above_context, FrameBlockContext left_context, bool allow_select); + DecoderErrorOr<void> inter_frame_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context); + DecoderErrorOr<void> inter_segment_id(BlockContext const&); + u8 get_segment_id(BlockContext const&); DecoderErrorOr<void> read_is_inter(FrameBlockContext above_context, FrameBlockContext left_context); - DecoderErrorOr<void> intra_block_mode_info(); - DecoderErrorOr<void> inter_block_mode_info(u32 row, u32 column, FrameBlockContext above_context, FrameBlockContext left_context); + DecoderErrorOr<void> intra_block_mode_info(BlockContext&); + DecoderErrorOr<void> inter_block_mode_info(BlockContext&, FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> read_ref_frames(FrameBlockContext above_context, FrameBlockContext left_context); DecoderErrorOr<void> assign_mv(bool is_compound); DecoderErrorOr<void> read_mv(u8 ref); DecoderErrorOr<i32> read_mv_component(u8 component); - DecoderErrorOr<bool> residual(u32 row, u32 column, bool has_block_above, bool has_block_left); - TXSize get_uv_tx_size(); + DecoderErrorOr<bool> residual(BlockContext&, bool has_block_above, bool has_block_left); + TXSize get_uv_tx_size(BlockSubsize size); BlockSubsize get_plane_block_size(u32 subsize, u8 plane); - DecoderErrorOr<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); + DecoderErrorOr<bool> tokens(BlockContext&, size_t plane, u32 x, u32 y, TXSize tx_size, u32 block_index); + u32 const* get_scan(BlockContext const&, size_t plane, TXSize tx_size, u32 block_index); DecoderErrorOr<i32> read_coef(Token token); /* (6.5) Motion Vector Prediction */ - void find_mv_refs(u32 row, u32 column, ReferenceFrameType, i32 block); - void find_best_ref_mvs(u32 row, u32 column, u8 ref_list); + void find_mv_refs(BlockContext&, ReferenceFrameType, i32 block); + void find_best_ref_mvs(BlockContext&, u8 ref_list); bool use_mv_hp(MotionVector const& delta_mv); - void append_sub8x8_mvs(u32 row, u32 column, i32 block, u8 ref_list); - bool is_inside(i32 row, i32 column); - void clamp_mv_ref(u32 row, u32 column, u8 i); - MotionVector clamp_mv(u32 row, u32 column, MotionVector mvec, i32 border); - size_t get_image_index(u32 row, u32 column) const; - void get_block_mv(u32 candidate_row, u32 candidate_column, u8 ref_list, bool use_prev); - void if_same_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev); - void if_diff_ref_frame_add_mv(u32 candidate_row, u32 candidate_column, ReferenceFrameType ref_frame, bool use_prev); + void append_sub8x8_mvs(BlockContext&, i32 block, u8 ref_list); + void clamp_mv_ref(BlockContext const&, u8 i); + MotionVector clamp_mv(BlockContext const&, MotionVector vector, i32 border); + size_t get_image_index(FrameContext const&, u32 row, u32 column) const; + void get_block_mv(BlockContext const&, MotionVector candidate_vector, u8 ref_list, bool use_prev); + void if_same_ref_frame_add_mv(BlockContext const&, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev); + void if_diff_ref_frame_add_mv(BlockContext const&, MotionVector candidate_vector, ReferenceFrameType ref_frame, bool use_prev); void scale_mv(u8 ref_list, ReferenceFrameType ref_frame); void add_mv_ref_list(u8 ref_list); - Gfx::Point<size_t> get_decoded_point_for_plane(u32 row, u32 column, u8 plane); - Gfx::Size<size_t> get_decoded_size_for_plane(u8 plane); + Gfx::Point<size_t> get_decoded_point_for_plane(FrameContext const&, u32 row, u32 column, u8 plane); + Gfx::Size<size_t> get_decoded_size_for_plane(FrameContext const&, u8 plane); - u8 m_profile { 0 }; - bool m_show_existing_frame { false }; - u8 m_frame_to_show_map_index { 0 }; - u16 m_header_size_in_bytes { 0 }; u8 m_refresh_frame_flags { 0 }; u8 m_loop_filter_level { 0 }; u8 m_loop_filter_sharpness { 0 }; @@ -156,7 +150,6 @@ private: FrameType m_frame_type { FrameType::KeyFrame }; FrameType m_last_frame_type { FrameType::KeyFrame }; bool m_show_frame { false }; - bool m_prev_show_frame { false }; bool m_error_resilient_mode { false }; bool m_frame_is_intra { false }; u8 m_reset_frame_context { 0 }; @@ -171,13 +164,9 @@ private: ColorRange m_color_range; bool m_subsampling_x { false }; bool m_subsampling_y { false }; - Gfx::Size<u32> m_frame_size { 0, 0 }; - Gfx::Size<u32> m_render_size { 0, 0 }; - bool m_render_and_frame_size_different { false }; - u32 m_mi_cols { 0 }; - u32 m_mi_rows { 0 }; - u32 m_sb64_cols { 0 }; - u32 m_sb64_rows { 0 }; + bool m_is_first_compute_image_size_invoke { true }; + Gfx::Size<u32> m_previous_frame_size { 0, 0 }; + bool m_previous_show_frame { false }; InterpolationFilter m_interpolation_filter { 0xf }; u8 m_base_q_idx { 0 }; i8 m_delta_q_y_dc { 0 }; @@ -205,12 +194,6 @@ private: Vector<u8> m_above_partition_context; Vector<u8> m_left_partition_context; - // FIXME: Move (some?) mi_.. to an array of struct since they are usually used together. - u32 m_mi_row_start { 0 }; - u32 m_mi_row_end { 0 }; - u32 m_mi_col_start { 0 }; - u32 m_mi_col_end { 0 }; - BlockSubsize m_mi_size { 0 }; u8 m_segment_id { 0 }; // FIXME: Should this be an enum? // skip equal to 0 indicates that there may be some transform coefficients to read for this block; skip equal to 1 @@ -219,7 +202,6 @@ private: // skip may be set to 0 even if transform blocks contain immediate end of block markers. bool m_skip { false }; TXSize m_max_tx_size { TX_4x4 }; - BlockSubsize m_block_subsize { BlockSubsize::Block_4x4 }; TXSize m_tx_size { TX_4x4 }; ReferenceFramePair m_ref_frame; bool m_is_inter { false }; @@ -253,13 +235,6 @@ private: // FIXME: Use Array<MotionVectorPair, 4> instead. Array<Array<MotionVector, 4>, 2> m_block_mvs; - // FIXME: From spec: NOTE – We are using a 2D array to store the SubModes for clarity. It is possible to reduce memory - // consumption by only storing one intra mode for each 8x8 horizontal and vertical position, i.e. to use two 1D - // arrays instead. - // I think should also apply to other fields that are only accessed relative to the current block. Worth looking - // into how much of this context needs to be stored for the whole frame vs a row or column from the current tile. - Vector2D<FrameBlockContext> m_frame_block_contexts; - MotionVectorPair m_candidate_mv; ReferenceFramePair m_candidate_frame; u8 m_ref_mv_count { 0 }; |