diff options
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Context.h | 20 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Decoder.cpp | 46 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Decoder.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Enums.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Parser.cpp | 95 | ||||
-rw-r--r-- | Userland/Libraries/LibVideo/VP9/Parser.h | 3 |
6 files changed, 104 insertions, 70 deletions
diff --git a/Userland/Libraries/LibVideo/VP9/Context.h b/Userland/Libraries/LibVideo/VP9/Context.h index 7e2477d9cc..c543fb3d4f 100644 --- a/Userland/Libraries/LibVideo/VP9/Context.h +++ b/Userland/Libraries/LibVideo/VP9/Context.h @@ -32,6 +32,14 @@ enum class FrameShowMode { DoNotShowFrame, }; +struct Quantizers { + u16 y_ac_quantizer { 0 }; + u16 uv_ac_quantizer { 0 }; + + u16 y_dc_quantizer { 0 }; + u16 uv_dc_quantizer { 0 }; +}; + struct FrameContext { public: static ErrorOr<FrameContext> create(ReadonlyBytes data, @@ -126,15 +134,9 @@ public: Array<i8, MAX_REF_FRAMES> loop_filter_reference_deltas; Array<i8, 2> loop_filter_mode_deltas; - u8 base_quantizer_index { 0 }; - i8 y_dc_quantizer_index_delta { 0 }; - i8 uv_dc_quantizer_index_delta { 0 }; - i8 uv_ac_quantizer_index_delta { 0 }; - bool is_lossless() const - { - // From quantization_params( ) in the spec. - return base_quantizer_index == 0 && y_dc_quantizer_index_delta == 0 && uv_dc_quantizer_index_delta == 0 && uv_ac_quantizer_index_delta == 0; - } + // Set based on quantization_params( ) in the spec. + bool lossless { false }; + Array<Quantizers, MAX_SEGMENTS> segment_quantizers; bool segmentation_enabled { false }; // Note: We can use Optional<Array<...>> for these tree probabilities, but unfortunately it seems to have measurable performance overhead. diff --git a/Userland/Libraries/LibVideo/VP9/Decoder.cpp b/Userland/Libraries/LibVideo/VP9/Decoder.cpp index 3756e12404..b8f3fa4ad1 100644 --- a/Userland/Libraries/LibVideo/VP9/Decoder.cpp +++ b/Userland/Libraries/LibVideo/VP9/Decoder.cpp @@ -1169,18 +1169,17 @@ inline u16 ac_q(u8 bit_depth, u8 b) return ac_qlookup[(bit_depth - 8) >> 1][clip_3<u8>(0, 255, b)]; } -u8 Decoder::get_base_quantizer_index(BlockContext const& block_context) +u8 Decoder::get_base_quantizer_index(SegmentFeatureStatus alternative_quantizer_feature, bool should_use_absolute_segment_base_quantizer, u8 base_quantizer_index) { // The function get_qindex( ) returns the quantizer index for the current block and is specified by the following: // − If seg_feature_active( SEG_LVL_ALT_Q ) is equal to 1 the following ordered steps apply: - auto alternative_quantizer_feature = block_context.get_segment_feature(SegmentFeature::UseAlternativeQuantizerBase); if (alternative_quantizer_feature.enabled) { // 1. Set the variable data equal to FeatureData[ segment_id ][ SEG_LVL_ALT_Q ]. auto data = alternative_quantizer_feature.value; // 2. If segmentation_abs_or_delta_update is equal to 0, set data equal to base_q_idx + data - if (!block_context.frame_context.should_use_absolute_segment_base_quantizer) { - data += block_context.frame_context.base_quantizer_index; + if (!should_use_absolute_segment_base_quantizer) { + data += base_quantizer_index; } // 3. Return Clip3( 0, 255, data ). @@ -1188,33 +1187,29 @@ u8 Decoder::get_base_quantizer_index(BlockContext const& block_context) } // − Otherwise, return base_q_idx. - return block_context.frame_context.base_quantizer_index; + return base_quantizer_index; } -u16 Decoder::get_dc_quantizer(BlockContext const& block_context, u8 plane) +u16 Decoder::get_dc_quantizer(u8 bit_depth, u8 base, i8 delta) { - // FIXME: The result of this function can be cached. This does not change per frame. + // NOTE: Delta is selected by the caller based on whether it is for the Y or UV planes. // The function get_dc_quant( plane ) returns the quantizer value for the dc coefficient for a particular plane and // is derived as follows: // − If plane is equal to 0, return dc_q( get_qindex( ) + delta_q_y_dc ). // − Otherwise, return dc_q( get_qindex( ) + delta_q_uv_dc ). - // Instead of if { return }, select the value to add and return. - i8 offset = plane == 0 ? block_context.frame_context.y_dc_quantizer_index_delta : block_context.frame_context.uv_dc_quantizer_index_delta; - return dc_q(block_context.frame_context.color_config.bit_depth, static_cast<u8>(get_base_quantizer_index(block_context) + offset)); + return dc_q(bit_depth, static_cast<u8>(base + delta)); } -u16 Decoder::get_ac_quantizer(BlockContext const& block_context, u8 plane) +u16 Decoder::get_ac_quantizer(u8 bit_depth, u8 base, i8 delta) { - // FIXME: The result of this function can be cached. This does not change per frame. + // NOTE: Delta is selected by the caller based on whether it is for the Y or UV planes. // The function get_ac_quant( plane ) returns the quantizer value for the ac coefficient for a particular plane and // is derived as follows: // − If plane is equal to 0, return ac_q( get_qindex( ) ). // − Otherwise, return ac_q( get_qindex( ) + delta_q_uv_ac ). - // Instead of if { return }, select the value to add and return. - i8 offset = plane == 0 ? 0 : block_context.frame_context.uv_ac_quantizer_index_delta; - return ac_q(block_context.frame_context.color_config.bit_depth, static_cast<u8>(get_base_quantizer_index(block_context) + offset)); + return ac_q(bit_depth, static_cast<u8>(base + delta)); } DecoderErrorOr<void> Decoder::reconstruct(u8 plane, BlockContext const& block_context, u32 transform_block_x, u32 transform_block_y, TransformSize transform_block_size, TransformSet transform_set) @@ -1254,18 +1249,15 @@ DecoderErrorOr<void> Decoder::reconstruct_templated(u8 plane, BlockContext const // 1. Dequant[ i ][ j ] is set equal to ( Tokens[ i * n0 + j ] * get_ac_quant( plane ) ) / dqDenom // for i = 0..(n0-1), for j = 0..(n0-1) Array<Intermediate, block_size * block_size> dequantized; - Intermediate ac_quant = get_ac_quantizer(block_context, plane); - for (auto i = 0u; i < block_size; i++) { - for (auto j = 0u; j < block_size; j++) { - auto index = i * block_size + j; - if (index == 0) - continue; - dequantized[index] = (block_context.residual_tokens[index] * ac_quant) / dq_denominator; - } + auto quantizers = block_context.frame_context.segment_quantizers[block_context.segment_id]; + Intermediate ac_quant = plane == 0 ? quantizers.y_ac_quantizer : quantizers.uv_ac_quantizer; + auto const* tokens_raw = block_context.residual_tokens.data(); + for (u32 i = 0; i < dequantized.size(); i++) { + dequantized[i] = (tokens_raw[i] * ac_quant) / dq_denominator; } // 2. Dequant[ 0 ][ 0 ] is set equal to ( Tokens[ 0 ] * get_dc_quant( plane ) ) / dqDenom - dequantized[0] = (block_context.residual_tokens[0] * get_dc_quantizer(block_context, plane)) / dq_denominator; + dequantized[0] = (block_context.residual_tokens[0] * (plane == 0 ? quantizers.y_dc_quantizer : quantizers.uv_dc_quantizer)) / dq_denominator; // It is a requirement of bitstream conformance that the values written into the Dequant array in steps 1 and 2 // are representable by a signed integer with 8 + BitDepth bits. @@ -1819,7 +1811,7 @@ DecoderErrorOr<void> Decoder::inverse_transform_2d(BlockContext const& block_con // 2. If Lossless is equal to 1, invoke the Inverse WHT process as specified in section 8.7.1.10 with shift equal // to 2. - if (block_context.frame_context.is_lossless()) { + if (block_context.frame_context.lossless) { TRY(inverse_walsh_hadamard_transform(row, log2_of_block_size, 2)); continue; } @@ -1857,7 +1849,7 @@ DecoderErrorOr<void> Decoder::inverse_transform_2d(BlockContext const& block_con // 2. If Lossless is equal to 1, invoke the Inverse WHT process as specified in section 8.7.1.10 with shift equal // to 0. - if (block_context.frame_context.is_lossless()) { + if (block_context.frame_context.lossless) { TRY(inverse_walsh_hadamard_transform(column, log2_of_block_size, 2)); continue; } @@ -1885,7 +1877,7 @@ DecoderErrorOr<void> Decoder::inverse_transform_2d(BlockContext const& block_con // 6. Otherwise (Lossless is equal to 0), set Dequant[ i ][ j ] equal to Round2( T[ i ], Min( 6, n + 2 ) ) // for i = 0..(n0-1). - if (!block_context.frame_context.is_lossless()) { + if (!block_context.frame_context.lossless) { for (auto i = 0u; i < block_size; i++) { auto index = i * block_size + j; dequantized[index] = rounded_right_shift(dequantized[index], min(6, log2_of_block_size + 2)); diff --git a/Userland/Libraries/LibVideo/VP9/Decoder.h b/Userland/Libraries/LibVideo/VP9/Decoder.h index 9246fd504f..a043257ce2 100644 --- a/Userland/Libraries/LibVideo/VP9/Decoder.h +++ b/Userland/Libraries/LibVideo/VP9/Decoder.h @@ -73,11 +73,11 @@ private: /* (8.6) Reconstruction and Dequantization */ // Returns the quantizer index for the current block - static u8 get_base_quantizer_index(BlockContext const&); + static u8 get_base_quantizer_index(SegmentFeatureStatus alternative_quantizer_feature, bool should_use_absolute_segment_base_quantizer, u8 base_quantizer_index); // Returns the quantizer value for the dc coefficient for a particular plane - static u16 get_dc_quantizer(BlockContext const&, u8 plane); + static u16 get_dc_quantizer(u8 bit_depth, u8 base, i8 delta); // Returns the quantizer value for the ac coefficient for a particular plane - static u16 get_ac_quantizer(BlockContext const&, u8 plane); + static u16 get_ac_quantizer(u8 bit_depth, u8 base, i8 delta); // (8.6.2) Reconstruct process DecoderErrorOr<void> reconstruct(u8 plane, BlockContext const&, u32 transform_block_x, u32 transform_block_y, TransformSize transform_block_size, TransformSet); diff --git a/Userland/Libraries/LibVideo/VP9/Enums.h b/Userland/Libraries/LibVideo/VP9/Enums.h index e444803db5..3f59112e65 100644 --- a/Userland/Libraries/LibVideo/VP9/Enums.h +++ b/Userland/Libraries/LibVideo/VP9/Enums.h @@ -163,9 +163,9 @@ enum Token : u8 { enum class SegmentFeature : u8 { // SEG_LVL_ALT_Q - UseAlternativeQuantizerBase, + AlternativeQuantizerBase, // SEG_LVL_ALT_L - UseAlternativeLoopFilterBase, + AlternativeLoopFilterBase, // SEG_LVL_REF_FRAME ReferenceFrameOverride, // SEG_LVL_SKIP diff --git a/Userland/Libraries/LibVideo/VP9/Parser.cpp b/Userland/Libraries/LibVideo/VP9/Parser.cpp index 25b25c14ed..a6f9bc716c 100644 --- a/Userland/Libraries/LibVideo/VP9/Parser.cpp +++ b/Userland/Libraries/LibVideo/VP9/Parser.cpp @@ -143,6 +143,39 @@ DecoderErrorOr<VideoFullRangeFlag> Parser::read_video_full_range_flag(BigEndianI return VideoFullRangeFlag::Studio; } +template<Signed T = i8> +static ErrorOr<T> read_signed(BigEndianInputBitStream& bit_stream, u8 bits) +{ + auto value_unsigned = static_cast<T>(TRY(bit_stream.read_bits(bits))); + if (TRY(bit_stream.read_bit())) + return -value_unsigned; + return value_unsigned; +} + +static DecoderErrorOr<i8> read_delta_q(BigEndianInputBitStream& bit_stream) +{ + if (TRY_READ(bit_stream.read_bit())) + return TRY_READ(read_signed(bit_stream, 4)); + return 0; +} + +struct QuantizationParameters { + u8 base_quantizer_index { 0 }; + i8 y_dc_quantizer_index_delta { 0 }; + i8 uv_dc_quantizer_index_delta { 0 }; + i8 uv_ac_quantizer_index_delta { 0 }; +}; + +static DecoderErrorOr<QuantizationParameters> quantization_params(BigEndianInputBitStream& bit_stream) +{ + QuantizationParameters result; + result.base_quantizer_index = TRY_READ(bit_stream.read_bits(8)); + result.y_dc_quantizer_index_delta = TRY(read_delta_q(bit_stream)); + result.uv_dc_quantizer_index_delta = TRY(read_delta_q(bit_stream)); + result.uv_ac_quantizer_index_delta = TRY(read_delta_q(bit_stream)); + return result; +} + /* (6.2) */ DecoderErrorOr<void> Parser::uncompressed_header(FrameContext& frame_context) { @@ -266,8 +299,10 @@ DecoderErrorOr<void> Parser::uncompressed_header(FrameContext& frame_context) frame_context.probability_context_index = probability_context_index; TRY(loop_filter_params(frame_context)); - TRY(quantization_params(frame_context)); + auto quantization_parameters = TRY(quantization_params(frame_context.bit_stream)); TRY(segmentation_params(frame_context)); + precalculate_quantizers(frame_context, quantization_parameters); + TRY(parse_tile_counts(frame_context)); frame_context.header_size_in_bytes = TRY_READ(frame_context.bit_stream.read_bits(16)); @@ -399,15 +434,6 @@ DecoderErrorOr<InterpolationFilter> Parser::read_interpolation_filter(BigEndianI return literal_to_type[TRY_READ(bit_stream.read_bits(2))]; } -template<Signed T = i8> -static ErrorOr<T> read_signed(BigEndianInputBitStream& bit_stream, u8 bits) -{ - auto value_unsigned = static_cast<T>(TRY(bit_stream.read_bits(bits))); - if (TRY(bit_stream.read_bit())) - return -value_unsigned; - return value_unsigned; -} - DecoderErrorOr<void> Parser::loop_filter_params(FrameContext& frame_context) { // FIXME: These should be moved to their own struct to return here. @@ -433,22 +459,6 @@ DecoderErrorOr<void> Parser::loop_filter_params(FrameContext& frame_context) return {}; } -DecoderErrorOr<void> Parser::quantization_params(FrameContext& frame_context) -{ - frame_context.base_quantizer_index = TRY_READ(frame_context.bit_stream.read_bits(8)); - frame_context.y_dc_quantizer_index_delta = TRY(read_delta_q(frame_context.bit_stream)); - frame_context.uv_dc_quantizer_index_delta = TRY(read_delta_q(frame_context.bit_stream)); - frame_context.uv_ac_quantizer_index_delta = TRY(read_delta_q(frame_context.bit_stream)); - return {}; -} - -DecoderErrorOr<i8> Parser::read_delta_q(BigEndianInputBitStream& bit_stream) -{ - if (TRY_READ(bit_stream.read_bit())) - return TRY_READ(read_signed(bit_stream, 4)); - return 0; -} - DecoderErrorOr<void> Parser::segmentation_params(FrameContext& frame_context) { frame_context.segmentation_enabled = TRY_READ(frame_context.bit_stream.read_bit()); @@ -501,6 +511,35 @@ DecoderErrorOr<u8> Parser::read_prob(BigEndianInputBitStream& bit_stream) return 255; } +void Parser::precalculate_quantizers(FrameContext& frame_context, QuantizationParameters quantization_parameters) +{ + frame_context.lossless = quantization_parameters.base_quantizer_index == 0 + && quantization_parameters.y_dc_quantizer_index_delta == 0 + && quantization_parameters.uv_dc_quantizer_index_delta == 0 + && quantization_parameters.uv_ac_quantizer_index_delta == 0; + + // Pre-calculate the quantizers so that the decoder doesn't have to do it repeatedly. + for (u8 segment_id = 0; segment_id < MAX_SEGMENTS; segment_id++) { + auto alternative_quantizer_feature = frame_context.get_segment_feature(segment_id, SegmentFeature::AlternativeQuantizerBase); + auto base = Decoder::get_base_quantizer_index(alternative_quantizer_feature, frame_context.should_use_absolute_segment_base_quantizer, quantization_parameters.base_quantizer_index); + + // The function get_ac_quant( plane ) returns the quantizer value for the ac coefficient for a particular plane and + // is derived as follows: + // − If plane is equal to 0, return ac_q( get_qindex( ) ). + // − Otherwise, return ac_q( get_qindex( ) + delta_q_uv_ac ). + auto& current_quantizers = frame_context.segment_quantizers[segment_id]; + current_quantizers.y_ac_quantizer = Decoder::get_ac_quantizer(frame_context.color_config.bit_depth, base, 0); + current_quantizers.uv_ac_quantizer = Decoder::get_ac_quantizer(frame_context.color_config.bit_depth, base, quantization_parameters.uv_ac_quantizer_index_delta); + + // The function get_dc_quant( plane ) returns the quantizer value for the dc coefficient for a particular plane and + // is derived as follows: + // − If plane is equal to 0, return dc_q( get_qindex( ) + delta_q_y_dc ). + // − Otherwise, return dc_q( get_qindex( ) + delta_q_uv_dc ). + current_quantizers.y_dc_quantizer = Decoder::get_dc_quantizer(frame_context.color_config.bit_depth, base, quantization_parameters.y_dc_quantizer_index_delta); + current_quantizers.uv_dc_quantizer = Decoder::get_dc_quantizer(frame_context.color_config.bit_depth, base, quantization_parameters.uv_dc_quantizer_index_delta); + } +} + static u16 calc_min_log2_of_tile_columns(u32 superblock_columns) { auto min_log_2 = 0u; @@ -577,7 +616,7 @@ DecoderErrorOr<void> Parser::compressed_header(FrameContext& frame_context) DecoderErrorOr<TransformMode> Parser::read_tx_mode(BooleanDecoder& decoder, FrameContext const& frame_context) { - if (frame_context.is_lossless()) { + if (frame_context.lossless) { return TransformMode::Only_4x4; } @@ -1373,7 +1412,7 @@ static TransformSet select_transform_type(BlockContext const& block_context, u8 if (plane > 0 || transform_size == Transform_32x32) return TransformSet { TransformType::DCT, TransformType::DCT }; if (transform_size == Transform_4x4) { - if (block_context.frame_context.is_lossless() || block_context.is_inter_predicted()) + if (block_context.frame_context.lossless || block_context.is_inter_predicted()) return TransformSet { TransformType::DCT, TransformType::DCT }; return mode_to_txfm_map[to_underlying(block_context.size < Block_8x8 ? block_context.sub_block_prediction_modes[block_index] : block_context.y_prediction_mode())]; diff --git a/Userland/Libraries/LibVideo/VP9/Parser.h b/Userland/Libraries/LibVideo/VP9/Parser.h index c29540fbe5..85b99e9075 100644 --- a/Userland/Libraries/LibVideo/VP9/Parser.h +++ b/Userland/Libraries/LibVideo/VP9/Parser.h @@ -30,6 +30,7 @@ struct FrameContext; struct TileContext; struct BlockContext; struct MotionVectorCandidate; +struct QuantizationParameters; class Parser { friend class TreeParser; @@ -61,10 +62,10 @@ private: DecoderErrorOr<void> compute_image_size(FrameContext&); DecoderErrorOr<InterpolationFilter> read_interpolation_filter(BigEndianInputBitStream&); DecoderErrorOr<void> loop_filter_params(FrameContext&); - DecoderErrorOr<void> quantization_params(FrameContext&); DecoderErrorOr<i8> read_delta_q(BigEndianInputBitStream&); DecoderErrorOr<void> segmentation_params(FrameContext&); DecoderErrorOr<u8> read_prob(BigEndianInputBitStream&); + static void precalculate_quantizers(FrameContext& frame_context, QuantizationParameters quantization_parameters); DecoderErrorOr<void> parse_tile_counts(FrameContext&); void setup_past_independence(); |