| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/gpu/av1_builder.h" |
| |
| #include <new> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/libgav1/src/src/buffer_pool.h" |
| #include "third_party/libgav1/src/src/decoder_state.h" |
| #include "third_party/libgav1/src/src/obu_parser.h" |
| |
| namespace media { |
| |
| using ::testing::ElementsAreArray; |
| |
| class AV1BuilderTest : public ::testing::Test { |
| public: |
| AV1BuilderTest() { |
| buffer_pool_ = std::make_unique<libgav1::BufferPool>( |
| /*on_frame_buffer_size_changed=*/nullptr, |
| /*get_frame_buffer=*/nullptr, |
| /*release_frame_buffer=*/nullptr, |
| /*callback_private_data=*/nullptr); |
| av1_decoder_state_ = std::make_unique<libgav1::DecoderState>(); |
| } |
| |
| ~AV1BuilderTest() override = default; |
| |
| AV1BitstreamBuilder::SequenceHeader MakeSequenceHeader() { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr{}; |
| seq_hdr.profile = 1; |
| seq_hdr.operating_points_cnt_minus_1 = 0; |
| seq_hdr.level.at(0) = 12; |
| seq_hdr.tier.at(0) = 0; |
| seq_hdr.frame_width_bits_minus_1 = 15; |
| seq_hdr.frame_height_bits_minus_1 = 15; |
| seq_hdr.width = 1280; |
| seq_hdr.height = 720; |
| seq_hdr.use_128x128_superblock = true; |
| seq_hdr.enable_filter_intra = true; |
| seq_hdr.enable_intra_edge_filter = true; |
| seq_hdr.enable_interintra_compound = true; |
| seq_hdr.enable_masked_compound = true; |
| seq_hdr.enable_warped_motion = true; |
| seq_hdr.enable_dual_filter = true; |
| seq_hdr.enable_order_hint = true; |
| seq_hdr.enable_jnt_comp = true; |
| seq_hdr.enable_ref_frame_mvs = true; |
| seq_hdr.order_hint_bits_minus_1 = 7; |
| seq_hdr.enable_superres = true; |
| seq_hdr.enable_cdef = true; |
| seq_hdr.enable_restoration = true; |
| return seq_hdr; |
| } |
| |
| // A version that mimics the default settings used by AV1 delegate. |
| AV1BitstreamBuilder::SequenceHeader MakeDefaultSequenceHeader() { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr{}; |
| |
| seq_hdr.profile = 0; |
| seq_hdr.level[0] = 12; |
| seq_hdr.tier[0] = 0; |
| seq_hdr.operating_points_cnt_minus_1 = 0; |
| seq_hdr.frame_width_bits_minus_1 = 15; |
| seq_hdr.frame_height_bits_minus_1 = 15; |
| seq_hdr.width = 1280; |
| seq_hdr.height = 720; |
| seq_hdr.order_hint_bits_minus_1 = 7; |
| |
| seq_hdr.use_128x128_superblock = false; |
| seq_hdr.enable_filter_intra = false; |
| seq_hdr.enable_intra_edge_filter = false; |
| seq_hdr.enable_interintra_compound = false; |
| seq_hdr.enable_masked_compound = false; |
| seq_hdr.enable_warped_motion = false; |
| seq_hdr.enable_dual_filter = false; |
| seq_hdr.enable_order_hint = true; |
| seq_hdr.enable_jnt_comp = false; |
| seq_hdr.enable_ref_frame_mvs = false; |
| seq_hdr.enable_superres = false; |
| seq_hdr.enable_cdef = true; |
| seq_hdr.enable_restoration = false; |
| |
| return seq_hdr; |
| } |
| |
| AV1BitstreamBuilder::FrameHeader MakeFrameHeader(uint32_t frame_id) { |
| AV1BitstreamBuilder::FrameHeader pic_hdr{}; |
| pic_hdr.frame_type = frame_id == 0 ? libgav1::FrameType::kFrameKey |
| : libgav1::FrameType::kFrameInter; |
| pic_hdr.error_resilient_mode = false; |
| pic_hdr.disable_cdf_update = false; |
| pic_hdr.disable_frame_end_update_cdf = false; |
| pic_hdr.base_qindex = 100; |
| pic_hdr.order_hint = frame_id; |
| pic_hdr.filter_level.at(0) = 1; |
| pic_hdr.filter_level.at(1) = 1; |
| pic_hdr.filter_level_u = 1; |
| pic_hdr.filter_level_v = 1; |
| pic_hdr.sharpness_level = 1; |
| pic_hdr.loop_filter_delta_enabled = false; |
| pic_hdr.primary_ref_frame = 0; |
| for (uint8_t& ref_idx : pic_hdr.ref_frame_idx) { |
| ref_idx = 0; |
| } |
| pic_hdr.refresh_frame_flags = 1; |
| for (uint32_t& ref_order_hint : pic_hdr.ref_order_hint) { |
| ref_order_hint = 0; |
| } |
| pic_hdr.cdef_bits = 3; |
| pic_hdr.cdef_damping_minus_3 = 2; |
| for (int i = 0; i < 8; i++) { |
| pic_hdr.cdef_y_pri_strength.at(i) = 0; |
| pic_hdr.cdef_y_sec_strength.at(i) = 0; |
| pic_hdr.cdef_uv_pri_strength.at(i) = 0; |
| pic_hdr.cdef_uv_sec_strength.at(i) = 0; |
| } |
| pic_hdr.reduced_tx_set = true; |
| pic_hdr.tx_mode = libgav1::TxMode::kTxModeSelect; |
| pic_hdr.segmentation_enabled = false; |
| pic_hdr.allow_screen_content_tools = false; |
| pic_hdr.allow_intrabc = false; |
| |
| return pic_hdr; |
| } |
| |
| std::unique_ptr<libgav1::BufferPool> buffer_pool_; |
| std::unique_ptr<libgav1::DecoderState> av1_decoder_state_; |
| }; |
| |
| TEST_F(AV1BuilderTest, AV1BitstreamBuilderOutstandingBits) { |
| const size_t kExpectedDataBits = 8; |
| AV1BitstreamBuilder packed_data; |
| packed_data.Write(0, 3); |
| packed_data.WriteBool(false); |
| packed_data.PutAlignBits(); |
| EXPECT_EQ(packed_data.OutstandingBits(), kExpectedDataBits); |
| } |
| |
| TEST_F(AV1BuilderTest, AV1BitstreamBuilderWriteOBUHeader) { |
| const std::vector<uint8_t> expected_packed_data = {0b00010010, 0b00000011, |
| 0b10100000}; |
| AV1BitstreamBuilder packed_obu_header; |
| packed_obu_header.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_obu_header.WriteValueInLeb128(3); |
| packed_obu_header.WriteBool(true); |
| packed_obu_header.Write(1, 2); |
| EXPECT_EQ(std::move(packed_obu_header).Flush(), expected_packed_data); |
| } |
| |
| TEST_F(AV1BuilderTest, AV1BitstreamBuilderWriteTemporalOBUHeader) { |
| const std::vector<uint8_t> expected_packed_data = {0b00010110, 0b01000000, |
| 0b00000011, 0b10100000}; |
| AV1BitstreamBuilder packed_obu_header; |
| packed_obu_header.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true, |
| /*extension_flag=*/true, |
| /*temporal_id=*/2); |
| packed_obu_header.WriteValueInLeb128(3); |
| packed_obu_header.WriteBool(true); |
| packed_obu_header.Write(1, 2); |
| EXPECT_EQ(std::move(packed_obu_header).Flush(), expected_packed_data); |
| } |
| |
| TEST_F(AV1BuilderTest, AV1BitstreamBuilderAppendBitstreamBuffer) { |
| const std::vector<uint8_t> expected_packed_data = {0b11010100, 0b11000000}; |
| AV1BitstreamBuilder append_data; |
| append_data.Write(9, 5); |
| append_data.PutTrailingBits(); |
| |
| AV1BitstreamBuilder packed_data; |
| packed_data.Write(6, 3); |
| packed_data.WriteBool(true); |
| packed_data.AppendBitstreamBuffer(std::move(append_data)); |
| packed_data.PutAlignBits(); |
| EXPECT_EQ(std::move(packed_data).Flush(), expected_packed_data); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildSequenceHeaderOBU) { |
| const std::vector<uint8_t> expected_packed_data = { |
| 0b00100000, 0b00000000, 0b00000000, 0b01100011, 0b11111100, |
| 0b00010011, 0b11111100, 0b00001011, 0b00111101, 0b11111111, |
| 0b11001111, 0b11000000, 0b10100000}; |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(MakeSequenceHeader()); |
| |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| EXPECT_EQ(std::move(seq_header_obu).Flush(), expected_packed_data); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildTemporalSequenceHeaderOBU) { |
| const std::vector<uint8_t> expected_packed_data = { |
| 0b00100000, 0b00100001, 0b00000111, 0b01100000, 0b01000000, 0b11011000, |
| 0b00010000, 0b00010110, 0b00111111, 0b11000001, 0b00111111, 0b11000000, |
| 0b10110011, 0b11011111, 0b11111100, 0b11111100, 0b00001010}; |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeSequenceHeader(); |
| seq_hdr.operating_points_cnt_minus_1 = 2; // Set scalability mode to L1T3. |
| for (uint32_t i = 0; i <= seq_hdr.operating_points_cnt_minus_1; i++) { |
| seq_hdr.level.at(i) = 12; |
| seq_hdr.tier.at(i) = 0; |
| } |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| EXPECT_EQ(std::move(seq_header_obu).Flush(), expected_packed_data); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBU) { |
| const std::vector<uint8_t> expected_packed_keyframe = { |
| 0b00010000, 0b00000000, 0b01000110, 0b01000000, 0b00000000, 0b10000010, |
| 0b00001000, 0b00100101, 0b01100000, 0b00000000, 0b00000000, 0b00000000, |
| 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, |
| 0b00000000, 0b00000000, 0b00000000, 0b01100000}; |
| const std::vector<uint8_t> expected_packed_interframe = { |
| 0b00110000, 0b00000001, 0b00000000, 0b00100000, 0b00000000, 0b00000000, |
| 0b00000000, 0b01000110, 0b01000000, 0b00000000, 0b10000010, 0b00001000, |
| 0b00100101, 0b01100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, |
| 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, |
| 0b00000000, 0b00000000, 0b01010000, 0b00000000}; |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeSequenceHeader(); |
| AV1BitstreamBuilder frame_obu_key = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, MakeFrameHeader(0)); |
| AV1BitstreamBuilder frame_obu_inter = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, MakeFrameHeader(1)); |
| |
| EXPECT_EQ(frame_obu_key.OutstandingBits() % 8, 0ull); |
| EXPECT_EQ(frame_obu_inter.OutstandingBits() % 8, 0ull); |
| EXPECT_EQ(std::move(frame_obu_key).Flush(), expected_packed_keyframe); |
| EXPECT_EQ(std::move(frame_obu_inter).Flush(), expected_packed_interframe); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithSegmentation) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| pic_hdr.primary_ref_frame = 7; |
| pic_hdr.segmentation_enabled = true; |
| pic_hdr.segmentation_update_map = true; |
| pic_hdr.segmentation_temporal_update = false; |
| pic_hdr.segmentation_update_data = true; |
| // Enable SEG_LVEL_ALT_Q, SEG_LVL_LF_Y_V for segment 0, |
| // and SEG_LVL_ATT_LF_Y_H for segment 1. |
| pic_hdr.feature_enabled[0][0] = true; |
| pic_hdr.feature_enabled[0][1] = true; |
| pic_hdr.feature_enabled[0][2] = false; |
| pic_hdr.feature_data[0][0] = -67; |
| pic_hdr.feature_data[0][1] = 5; |
| pic_hdr.feature_enabled[1][2] = true; |
| pic_hdr.feature_data[1][2] = -12; |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data: |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| EXPECT_TRUE(frame_header.segmentation.enabled); |
| EXPECT_TRUE(frame_header.segmentation.update_map); |
| EXPECT_FALSE(frame_header.segmentation.temporal_update); |
| EXPECT_TRUE(frame_header.segmentation.update_data); |
| EXPECT_TRUE(frame_header.segmentation.feature_enabled[0][0]); |
| EXPECT_TRUE(frame_header.segmentation.feature_enabled[0][1]); |
| EXPECT_FALSE(frame_header.segmentation.feature_enabled[0][2]); |
| EXPECT_FALSE(frame_header.segmentation.feature_enabled[1][0]); |
| EXPECT_FALSE(frame_header.segmentation.feature_enabled[1][1]); |
| EXPECT_TRUE(frame_header.segmentation.feature_enabled[1][2]); |
| EXPECT_EQ(frame_header.segmentation.feature_data[0][0], |
| pic_hdr.feature_data[0][0]); |
| EXPECT_EQ(frame_header.segmentation.feature_data[0][1], |
| pic_hdr.feature_data[0][1]); |
| EXPECT_EQ(frame_header.segmentation.feature_data[1][2], |
| pic_hdr.feature_data[1][2]); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildSeqHeaderWithColorConfig) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| seq_hdr.color_description_present_flag = true; |
| seq_hdr.color_primaries = kLibgav1ColorPrimaryBt709; |
| seq_hdr.transfer_characteristics = kLibgav1TransferCharacteristicsBt709; |
| seq_hdr.matrix_coefficients = kLibgav1MatrixCoefficientsBt709; |
| seq_hdr.color_range = kLibgav1ColorRangeStudio; |
| seq_hdr.chroma_sample_position = kLibgav1ChromaSamplePositionUnknown; |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto sequence_header = parser->sequence_header(); |
| EXPECT_EQ(sequence_header.color_config.color_primary, |
| kLibgav1ColorPrimaryBt709); |
| EXPECT_EQ(sequence_header.color_config.transfer_characteristics, |
| kLibgav1TransferCharacteristicsBt709); |
| EXPECT_EQ(sequence_header.color_config.matrix_coefficients, |
| kLibgav1MatrixCoefficientsBt709); |
| EXPECT_EQ(sequence_header.color_config.color_range, kLibgav1ColorRangeStudio); |
| EXPECT_EQ(sequence_header.color_config.chroma_sample_position, |
| kLibgav1ChromaSamplePositionUnknown); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithQuantizationParams) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| pic_hdr.delta_q_present = false; |
| pic_hdr.delta_q_y_dc = -2; |
| pic_hdr.delta_q_u_dc = -1; |
| pic_hdr.delta_q_u_ac = 0; |
| pic_hdr.delta_q_v_dc = 4; |
| pic_hdr.delta_q_v_ac = -3; |
| pic_hdr.using_qmatrix = true; |
| pic_hdr.qm_y = 2; |
| pic_hdr.qm_u = 3; |
| pic_hdr.qm_v = 4; |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| EXPECT_EQ(frame_header.quantizer.base_index, 100); |
| EXPECT_EQ(frame_header.quantizer.delta_dc[0], pic_hdr.delta_q_y_dc); |
| EXPECT_EQ(frame_header.quantizer.delta_dc[1], pic_hdr.delta_q_u_dc); |
| EXPECT_EQ(frame_header.quantizer.delta_ac[1], pic_hdr.delta_q_u_ac); |
| EXPECT_EQ(frame_header.quantizer.delta_dc[2], pic_hdr.delta_q_v_dc); |
| EXPECT_EQ(frame_header.quantizer.delta_ac[2], pic_hdr.delta_q_v_ac); |
| EXPECT_TRUE(frame_header.quantizer.use_matrix); |
| EXPECT_EQ(frame_header.quantizer.matrix_level[0], pic_hdr.qm_y); |
| EXPECT_EQ(frame_header.quantizer.matrix_level[1], pic_hdr.qm_u); |
| EXPECT_EQ(frame_header.quantizer.matrix_level[2], pic_hdr.qm_v); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithLoopFilter) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| pic_hdr.delta_q_present = true; |
| pic_hdr.loop_filter_delta_enabled = true; |
| pic_hdr.loop_filter_delta_update = true; |
| pic_hdr.update_ref_delta = true; |
| pic_hdr.loop_filter_ref_deltas = {1, -1, 2, -2, 3, -3, 4, -4}; |
| pic_hdr.update_mode_delta = true; |
| pic_hdr.loop_filter_mode_deltas = {1, -1}; |
| pic_hdr.delta_lf_present = true; |
| pic_hdr.delta_lf_res = 2; |
| pic_hdr.delta_lf_multi = true; |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Create a fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| // Write the tile_group_obu into packed_frame. |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| EXPECT_TRUE(frame_header.loop_filter.delta_enabled); |
| EXPECT_TRUE(frame_header.loop_filter.delta_update); |
| EXPECT_EQ(frame_header.loop_filter.ref_deltas, |
| pic_hdr.loop_filter_ref_deltas); |
| EXPECT_TRUE(frame_header.delta_lf.present); |
| EXPECT_EQ(frame_header.delta_lf.scale, pic_hdr.delta_lf_res); |
| EXPECT_TRUE(frame_header.delta_lf.multi); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithCDEF) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| seq_hdr.enable_restoration = false; |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| pic_hdr.cdef_damping_minus_3 = 2; |
| pic_hdr.cdef_bits = 3; |
| for (int i = 0; i < (1 << pic_hdr.cdef_bits); i++) { |
| pic_hdr.cdef_y_pri_strength[i] = i; |
| pic_hdr.cdef_y_sec_strength[i] = i; |
| pic_hdr.cdef_uv_pri_strength[i] = i; |
| pic_hdr.cdef_uv_sec_strength[i] = i; |
| } |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| EXPECT_EQ(frame_header.cdef.damping, pic_hdr.cdef_damping_minus_3 + 3); |
| EXPECT_EQ(frame_header.cdef.bits, pic_hdr.cdef_bits); |
| EXPECT_THAT(pic_hdr.cdef_y_pri_strength, |
| ElementsAreArray(frame_header.cdef.y_primary_strength)); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithTxMode) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| seq_hdr.enable_restoration = false; |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| pic_hdr.tx_mode = libgav1::TxMode::kTxModeLargest; |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| EXPECT_EQ(frame_header.tx_mode, libgav1::TxMode::kTxModeLargest); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithLoopRestoration) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| seq_hdr.enable_restoration = true; |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| pic_hdr.restoration_type = { |
| libgav1::LoopRestorationType::kLoopRestorationTypeWiener, |
| libgav1::LoopRestorationType::kLoopRestorationTypeSgrProj, |
| libgav1::LoopRestorationType::kLoopRestorationTypeSwitchable}; |
| pic_hdr.lr_unit_shift = 1; // Use 128x128 restoration units for Y-plane. |
| pic_hdr.lr_uv_shift = 1; // Use 64x64 restoration units for UV-plane. |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| EXPECT_EQ(frame_header.loop_restoration.type[0], |
| libgav1::LoopRestorationType::kLoopRestorationTypeWiener); |
| EXPECT_EQ(frame_header.loop_restoration.unit_size_log2[0], 7); |
| EXPECT_EQ(frame_header.loop_restoration.type[1], |
| libgav1::LoopRestorationType::kLoopRestorationTypeSgrProj); |
| EXPECT_EQ(frame_header.loop_restoration.unit_size_log2[1], 6); |
| EXPECT_EQ(frame_header.loop_restoration.type[2], |
| libgav1::LoopRestorationType::kLoopRestorationTypeSwitchable); |
| EXPECT_EQ(frame_header.loop_restoration.unit_size_log2[2], 6); |
| } |
| |
| TEST_F(AV1BuilderTest, BuildFrameOBUWithReferenceSelect) { |
| AV1BitstreamBuilder::SequenceHeader seq_hdr = MakeDefaultSequenceHeader(); |
| seq_hdr.enable_restoration = false; |
| seq_hdr.enable_ref_frame_mvs = true; |
| AV1BitstreamBuilder seq_header_obu = |
| AV1BitstreamBuilder::BuildSequenceHeaderOBU(seq_hdr); |
| |
| AV1BitstreamBuilder packed_frame; |
| packed_frame.WriteOBUHeader(/*type=*/libgav1::kObuTemporalDelimiter, |
| /*has_size=*/true); |
| packed_frame.WriteValueInLeb128(0); |
| |
| packed_frame.WriteOBUHeader(libgav1::kObuSequenceHeader, /*has_size=*/true); |
| EXPECT_EQ(seq_header_obu.OutstandingBits() % 8, 0ull); |
| packed_frame.WriteValueInLeb128(seq_header_obu.OutstandingBits() / 8); |
| packed_frame.AppendBitstreamBuffer(std::move(seq_header_obu)); |
| |
| AV1BitstreamBuilder::FrameHeader pic_hdr = MakeFrameHeader(0); |
| packed_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr); |
| EXPECT_EQ(frame_obu.OutstandingBits() % 8, 0ull); |
| // Fake tile_group_obu with only dummy data. |
| static const uint8_t tile_group_obu[] = {0x00, 0x80}; |
| packed_frame.WriteValueInLeb128(frame_obu.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_frame.AppendBitstreamBuffer(std::move(frame_obu)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_frame.Write(byte, 8); |
| } |
| std::vector<uint8_t> chunk = std::move(packed_frame).Flush(); |
| auto parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| |
| libgav1::RefCountedBufferPtr current_frame; |
| libgav1::StatusCode status = parser->ParseOneFrame(¤t_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header = parser->frame_header(); |
| auto sequence_header = parser->sequence_header(); |
| av1_decoder_state_->UpdateReferenceFrames( |
| current_frame, base::strict_cast<int>(frame_header.refresh_frame_flags)); |
| EXPECT_FALSE(frame_header.reference_mode_select); |
| |
| AV1BitstreamBuilder packed_delta_frame; |
| AV1BitstreamBuilder::FrameHeader pic_hdr_delta = MakeFrameHeader(1); |
| pic_hdr_delta.reference_select = true; |
| pic_hdr_delta.refresh_frame_flags = 0b00000010; |
| packed_delta_frame.WriteOBUHeader(libgav1::kObuFrame, /*has_size=*/true); |
| AV1BitstreamBuilder frame_obu_delta = |
| AV1BitstreamBuilder::BuildFrameHeaderOBU(seq_hdr, pic_hdr_delta); |
| EXPECT_EQ(frame_obu_delta.OutstandingBits() % 8, 0ull); |
| |
| packed_delta_frame.WriteValueInLeb128(frame_obu_delta.OutstandingBits() / 8 + |
| std::size(tile_group_obu)); |
| packed_delta_frame.AppendBitstreamBuffer(std::move(frame_obu_delta)); |
| for (const uint8_t byte : tile_group_obu) { |
| packed_delta_frame.Write(byte, 8); |
| } |
| chunk = std::move(packed_delta_frame).Flush(); |
| auto delta_parser = base::WrapUnique(new (std::nothrow) libgav1::ObuParser( |
| chunk.data(), chunk.size(), 0, buffer_pool_.get(), |
| av1_decoder_state_.get())); |
| delta_parser->set_sequence_header(sequence_header); |
| |
| libgav1::RefCountedBufferPtr current_delta_frame; |
| status = delta_parser->ParseOneFrame(¤t_delta_frame); |
| |
| EXPECT_EQ(status, libgav1::kStatusOk); |
| auto frame_header_delta = delta_parser->frame_header(); |
| EXPECT_TRUE(frame_header_delta.reference_mode_select); |
| } |
| |
| } // namespace media |