diff options
Diffstat (limited to 'src/obu_parser_test.cc')
-rw-r--r-- | src/obu_parser_test.cc | 2675 |
1 files changed, 2675 insertions, 0 deletions
diff --git a/src/obu_parser_test.cc b/src/obu_parser_test.cc new file mode 100644 index 0000000..6397ad0 --- /dev/null +++ b/src/obu_parser_test.cc @@ -0,0 +1,2675 @@ +// Copyright 2021 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/obu_parser.h" + +#include <array> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <memory> +#include <new> +#include <string> +#include <vector> + +#include "gtest/gtest.h" +#include "src/buffer_pool.h" +#include "src/decoder_impl.h" +#include "src/decoder_state.h" +#include "src/gav1/decoder_buffer.h" +#include "src/gav1/status_code.h" +#include "src/utils/common.h" +#include "src/utils/constants.h" +#include "src/utils/segmentation.h" +#include "src/utils/types.h" +#include "src/utils/vector.h" +#include "tests/third_party/libvpx/acm_random.h" + +// Note the following test classes access private functions/members of +// ObuParser. To be declared friends of ObuParser they must not have internal +// linkage (they must be outside the anonymous namespace). +namespace libgav1 { + +// Helper class to manipulate individual bits and generate a byte string. +class BytesAndBits { + public: + // Append a bit to the end. + void AppendBit(uint8_t bit) { bits_.push_back(bit != 0); } + + // Append a byte to the end. + void AppendByte(uint8_t byte) { + for (int i = 0; i < 8; ++i) { + AppendBit(GetNthBit(byte, i, 8)); + } + } + + // Append a literal of size |bits| to the end. + void AppendLiteral(int bits, int value) { + InsertLiteral(static_cast<int>(bits_.size()), bits, value); + } + + // Append an inverse signed literal to the end. |bits + 1| bits are appended. + void AppendInverseSignedLiteral(int bits, int value) { + InsertInverseSignedLiteral(static_cast<int>(bits_.size()), bits, value); + } + + // Append a sequence of bytes to the end. + void AppendBytes(const std::vector<uint8_t>& bytes) { + for (const auto& byte : bytes) { + AppendByte(byte); + } + } + + // Insert |bit| in |offset|. Moves all other bits to the right by 1. + void InsertBit(int offset, uint8_t bit) { + auto iterator = bits_.begin(); + bits_.insert(iterator + offset, bit != 0); + } + + // Insert |value| of size |bits| at offset |offset|. Moves all other bits to + // the right by |bits|. + void InsertLiteral(int offset, int bits, int value) { + for (int i = 0; i < bits; ++i) { + InsertBit(i + offset, GetNthBit(value, i, bits)); + } + } + + // Insert |value| of size |bits| at offset |offset| as an inverse signed + // literal. Move all other bits to the right by |bits + 1|. + // + // Note: This is denoted su(1+bits) in the spec. + void InsertInverseSignedLiteral(int offset, int bits, int value) { + InsertBit(offset, (value >= 0) ? 0 : 1); + InsertLiteral(offset + 1, bits, value); + } + + // Insert |value| at |offset| as an unsigned variable length code (uvlc). + // Return the number of bits inserted. + int InsertUvlc(int offset, int value) { + int leading_zeros = 1; + int shift_value = ++value; + while ((shift_value >>= 1) != 0) leading_zeros += 2; + int bits = 0; + InsertLiteral(offset, leading_zeros >> 1, 0); + bits += leading_zeros >> 1; + InsertLiteral(offset + bits, (leading_zeros + 1) >> 1, value); + bits += (leading_zeros + 1) >> 1; + return bits; + } + + // Set the bit at |offset| to |bit|. The bit should already exist. + void SetBit(int offset, uint8_t bit) { bits_[offset] = bit != 0; } + + // Set |bits| starting at |offset| to |value|. The bits should already exist. + void SetLiteral(int offset, int bits, int value) { + for (int i = 0; i < bits; ++i) { + SetBit(offset + i, GetNthBit(value, i, bits)); + } + } + + // Remove a bit in |offset|. Moves over all the following bits to the left by + // 1. + void RemoveBit(int offset) { RemoveLiteral(offset, 1); } + + // Remove a literal of size |bits| from |offset|. Moves over all the + // following bits to the left by |bits|. + void RemoveLiteral(int offset, int bits) { + bits_.erase(bits_.begin() + offset, bits_.begin() + offset + bits); + } + + // Remove all bits after offset. + void RemoveAllBitsAfter(int offset) { + RemoveLiteral(offset, static_cast<int>(bits_.size()) - offset); + } + + // Clear all the bits stored. + void Clear() { bits_.clear(); } + + // Generate the data vector from the bits. Pads 0 to the end of the last byte + // if necessary. + const std::vector<uint8_t>& GenerateData() { + data_.clear(); + for (size_t i = 0; i < bits_.size(); i += 8) { + uint8_t byte = 0; + for (int j = 0; j < 8; ++j) { + const uint8_t bit = + ((i + j) < bits_.size()) ? static_cast<uint8_t>(bits_[i + j]) : 0; + byte |= bit << (7 - j); + } + data_.push_back(byte); + } + return data_; + } + + private: + // Get the |n|th MSB from |value| with the assumption that |value| has |size| + // bits. + static uint8_t GetNthBit(int value, int n, int size) { + return (value >> (size - n - 1)) & 0x01; + } + + std::vector<uint8_t> data_; + std::vector<bool> bits_; +}; + +class ObuParserTest : public testing::Test { + protected: + // Constants for unit tests. + static constexpr int kFrameWidthBits = 9; + static constexpr int kFrameHeightBits = 8; + static constexpr int kHeight = 240; + static constexpr int kWidth = 426; + static constexpr int kRows4x4 = 60; + static constexpr int kColumns4x4 = 108; + static constexpr int kFrameToShow = 2; + static constexpr int kDisplayFrameId = 10; + static constexpr int kFrameIdLengthBits = 15; + static constexpr int kDeltaFrameIdLengthBits = 14; + + // Bit streams for testing. These may contain trailing bits and tests may have + // to remove some of the trailing bits to keep the boundary alignment. + const std::vector<uint8_t> kDefaultTemporalDelimiter = {0x12, 0x00}; + // Bits Syntax element Value + // 1 obu_forbidden_bit 0 + // 4 obu_type 2 (OBU_TEMPORAL_DELIMITER) + // 1 obu_extension_flag 1 + // 1 obu_has_size_field 1 + // 1 obu_reserved_1bit 0 + // 3 temporal_id 6 + // 2 spatial_id 2 + // 3 extension_header_reserved_3bits 0 + // 8 obu_size 0 + const std::vector<uint8_t> kDefaultTemporalDelimiterWithExtension = { + 0x16, 0xd0, 0x00}; + const std::vector<uint8_t> kDefaultHeaderWithoutSizeField = {0x10}; + // Offset Bits Syntax element Value + // 0 3 seq_profile 0 + // 3 1 still_picture 0 + // 4 1 reduced_still_picture_header 0 + // 5 1 timing_info_present_flag 0 + // 6 1 initial_display_delay_present_flag 0 + // 7 5 operating_points_cnt_minus_1 0 + // 12 12 operating_point_idc[ 0 ] 0 + // 24 5 seq_level_idx[ 0 ] 0 + // 29 4 frame_width_bits_minus_1 8 + // 33 4 frame_height_bits_minus_1 7 + // 37 9 max_frame_width_minus_1 425 + // 46 8 max_frame_height_minus_1 239 + // 54 1 frame_id_numbers_present_flag 0 + // 55 1 use_128x128_superblock 1 + // 56 1 enable_filter_intra 1 + // 57 1 enable_intra_edge_filter 1 + // 58 1 enable_interintra_compound 1 + // 59 1 enable_masked_compound 1 + // 60 1 enable_warped_motion 0 + // 61 1 enable_dual_filter 1 + // 62 1 enable_order_hint 1 + // 63 1 enable_jnt_comp 1 + // 64 1 enable_ref_frame_mvs 1 + // 65 1 seq_choose_screen_content_tools 1 + // 66 1 seq_choose_integer_mv 1 + // 67 3 order_hint_bits_minus_1 6 + // 70 1 enable_superres 0 + // 71 1 enable_cdef 1 + // 72 1 enable_restoration 1 + // ... + const std::vector<uint8_t> kDefaultSequenceHeader = { + 0x00, 0x00, 0x00, 0x04, 0x3e, 0xa7, 0xbd, 0xf7, 0xf9, 0x80, 0x40}; + const std::vector<uint8_t> kDefaultFrameHeaderKeyFrame = {0x10, 0x00}; + // Bits Syntax element Value + // 1 show_existing_frame 0 + // 2 frame_type 2 (kFrameIntraOnly) + // 1 show_frame 1 + // 1 error_resilient_mode 0 + // 1 disable_cdf_update 0 + // 1 frame_size_override_flag 0 + // 8 refresh_frame_flags 4 + // ... + const std::vector<uint8_t> kDefaultFrameHeaderIntraOnlyFrame = {0x50, 0x08, + 0x00}; + // Bits Syntax element Value + // 1 show_existing_frame 0 + // 2 frame_type 1 (kFrameInter) + // 1 show_frame 1 + // 1 error_resilient_mode 0 + // 1 disable_cdf_update 0 + // 1 frame_size_override_flag 0 + // 3 primary_ref_frame 1 + // 8 refresh_frame_flags 4 + // 3 ref_frame_idx[0] 0 + // 3 ref_frame_idx[1] 1 + // 3 ref_frame_idx[2] 2 + // 3 ref_frame_idx[3] 3 + // 3 ref_frame_idx[4] 4 + // 3 ref_frame_idx[5] 5 + // 3 ref_frame_idx[6] 6 + // ... + const std::vector<uint8_t> kDefaultFrameHeaderInterFrame = {0x30, 0x41, 0x01, + 0x4e, 0x5c, 0x60}; + const std::vector<uint8_t> kDefaultGlobalMotionParametersRotZoom = { + 0xff, 0x50, 0x77, 0x7e, 0x1f, 0xcd}; + const std::vector<uint8_t> kDefaultGlobalMotionParametersAffine = { + 0x3f, 0x50, 0x77, 0x7b, 0xbf, 0xa8, 0x3e, 0x1f, 0xcd}; + + void SetUp() override { + buffer_pool_.reset(new (std::nothrow) + BufferPool(nullptr, nullptr, nullptr, nullptr)); + ASSERT_NE(buffer_pool_, nullptr); + } + + bool Init() { + obu_.reset(new (std::nothrow) ObuParser(nullptr, 0, 0, buffer_pool_.get(), + &decoder_state_)); + if (obu_ == nullptr) return false; + obu_headers_ = &obu_->obu_headers_; + obu_frame_header_ = &obu_->frame_header_; + obu_sequence_header_ = &obu_->sequence_header_; + return true; + } + + bool Init(const std::vector<uint8_t>& data, bool init_bit_reader = true) { + obu_.reset(new (std::nothrow) ObuParser( + data.data(), data.size(), 0, buffer_pool_.get(), &decoder_state_)); + if (obu_ == nullptr) return false; + obu_headers_ = &obu_->obu_headers_; + obu_frame_header_ = &obu_->frame_header_; + obu_sequence_header_ = &obu_->sequence_header_; + return init_bit_reader ? obu_->InitBitReader(data.data(), data.size()) + : true; + } + + bool Parse(const std::string& input, + const ObuSequenceHeader* const sequence_header = nullptr) { + std::vector<uint8_t> data(input.begin(), input.end()); + return Parse(data, sequence_header); + } + + bool Parse(const std::vector<uint8_t>& data, + const ObuSequenceHeader* const sequence_header = nullptr) { + EXPECT_TRUE(Init(data, false)); + if (sequence_header != nullptr) obu_->set_sequence_header(*sequence_header); + return obu_->ParseOneFrame(¤t_frame_) == kStatusOk; + } + + bool ParseSequenceHeader(const std::vector<uint8_t>& data) { + EXPECT_TRUE(Init(data)); + return obu_->ParseSequenceHeader(/*seen_frame_header=*/false); + } + + bool ParseFrameParameters(const std::vector<uint8_t>& data, + bool id_bits_present = false, + int force_screen_content_tools = 0, + int force_integer_mv = 0, + bool enable_superres = false) { + EXPECT_TRUE(Init(data)); + if (id_bits_present) { + obu_->sequence_header_.frame_id_numbers_present = true; + obu_->sequence_header_.frame_id_length_bits = kFrameIdLengthBits; + obu_->sequence_header_.delta_frame_id_length_bits = + kDeltaFrameIdLengthBits; + } + obu_->sequence_header_.force_screen_content_tools = + force_screen_content_tools; + obu_->sequence_header_.force_integer_mv = force_integer_mv; + obu_->sequence_header_.enable_superres = enable_superres; + obu_->sequence_header_.frame_width_bits = kFrameWidthBits; + obu_->sequence_header_.frame_height_bits = kFrameHeightBits; + obu_->sequence_header_.max_frame_width = kWidth; + obu_->sequence_header_.max_frame_height = kHeight; + return obu_->ParseFrameParameters(); + } + + bool ParseSegmentationParameters(const std::vector<uint8_t>& data, + int primary_reference_frame, + int prev_frame_index) { + EXPECT_TRUE(Init(data)); + obu_->frame_header_.primary_reference_frame = primary_reference_frame; + if (primary_reference_frame != kPrimaryReferenceNone) { + obu_->frame_header_.reference_frame_index[primary_reference_frame] = + prev_frame_index; + } + return obu_->ParseSegmentationParameters(); + } + + bool ParseFrameReferenceModeSyntax(const std::vector<uint8_t>& data, + FrameType frame_type) { + EXPECT_TRUE(Init(data)); + obu_->frame_header_.frame_type = frame_type; + return obu_->ParseFrameReferenceModeSyntax(); + } + + bool ParseGlobalMotionParameters(const std::vector<uint8_t>& data, + FrameType frame_type) { + EXPECT_TRUE(Init(data)); + obu_->frame_header_.frame_type = frame_type; + obu_->frame_header_.primary_reference_frame = kPrimaryReferenceNone; + return obu_->ParseGlobalMotionParameters(); + } + + bool ParseFilmGrainParameters(const std::vector<uint8_t>& data, + const ObuSequenceHeader& sequence_header, + const ObuFrameHeader& frame_header) { + EXPECT_TRUE(Init(data)); + obu_->set_sequence_header(sequence_header); + obu_->frame_header_ = frame_header; + return obu_->ParseFilmGrainParameters(); + } + + bool ParseTileInfoSyntax(const std::vector<uint8_t>& data, int columns4x4, + int rows4x4, bool use_128x128_superblock) { + EXPECT_TRUE(Init(data)); + obu_->frame_header_.columns4x4 = columns4x4; + obu_->frame_header_.rows4x4 = rows4x4; + obu_->sequence_header_.use_128x128_superblock = use_128x128_superblock; + return obu_->ParseTileInfoSyntax(); + } + + bool ParseMetadata(const std::vector<uint8_t>& data) { + EXPECT_TRUE(Init(data)); + return obu_->ParseMetadata(data.data(), data.size()); + } + + void DefaultSequenceHeader(ObuSequenceHeader* const gold) { + memset(gold, 0, sizeof(*gold)); + gold->profile = kProfile0; + gold->level[0].major = kMinimumMajorBitstreamLevel; + gold->operating_points = 1; + gold->max_frame_width = kWidth; + gold->max_frame_height = kHeight; + gold->frame_width_bits = kFrameWidthBits; + gold->frame_height_bits = kFrameHeightBits; + gold->use_128x128_superblock = true; + gold->enable_filter_intra = true; + gold->enable_intra_edge_filter = true; + gold->enable_interintra_compound = true; + gold->enable_masked_compound = true; + gold->enable_dual_filter = true; + gold->enable_order_hint = true; + gold->enable_jnt_comp = true; + gold->enable_ref_frame_mvs = true; + gold->choose_screen_content_tools = true; + gold->force_screen_content_tools = 2; + gold->choose_integer_mv = true; + gold->force_integer_mv = 2; + gold->order_hint_bits = 7; + gold->enable_cdef = true; + gold->enable_restoration = true; + gold->color_config.bitdepth = 8; + gold->color_config.color_primary = kColorPrimaryUnspecified; + gold->color_config.transfer_characteristics = + kTransferCharacteristicsUnspecified; + gold->color_config.matrix_coefficients = kMatrixCoefficientsUnspecified; + gold->color_config.subsampling_x = 1; + gold->color_config.subsampling_y = 1; + } + + void DefaultFrameHeader(ObuFrameHeader* const gold, FrameType frame_type) { + memset(gold, 0, sizeof(*gold)); + gold->frame_type = frame_type; + gold->show_frame = true; + gold->showable_frame = (frame_type != kFrameKey); + gold->enable_cdf_update = true; + gold->width = kWidth; + gold->height = kHeight; + gold->render_width = kWidth; + gold->render_height = kHeight; + gold->upscaled_width = kWidth; + gold->primary_reference_frame = kPrimaryReferenceNone; + gold->enable_frame_end_update_cdf = true; + gold->rows4x4 = kRows4x4; + gold->columns4x4 = kColumns4x4; + if (frame_type == kFrameKey) { + gold->refresh_frame_flags = 0xff; + gold->error_resilient_mode = true; + gold->force_integer_mv = 1; + } else if (frame_type == kFrameIntraOnly) { + gold->refresh_frame_flags = 4; + gold->force_integer_mv = 1; + } else if (frame_type == kFrameInter) { + gold->refresh_frame_flags = 4; + gold->primary_reference_frame = 1; + for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) { + gold->reference_frame_index[i] = i; + } + gold->is_motion_mode_switchable = true; + } + } + + void OverrideFrameSize(BytesAndBits* const data, ObuFrameHeader* const gold, + int flag_offset, int size_offset) { + data->SetBit(flag_offset, 1); // frame_size_override_flag. + data->InsertLiteral(size_offset, kFrameWidthBits, + kWidth - 2); // frame_width_minus_1. + data->InsertLiteral(size_offset + kFrameWidthBits, kFrameHeightBits, + kHeight - 2); // frame_height_minus_1. + gold->frame_size_override_flag = true; + gold->width = kWidth - 1; + gold->height = kHeight - 1; + gold->render_width = gold->width; + gold->render_height = gold->height; + gold->upscaled_width = gold->width; + } + + void OverrideRenderSize(BytesAndBits* const data, ObuFrameHeader* const gold, + int flag_offset) { + data->SetBit(flag_offset, 1); // render_and_frame_size_different. + data->InsertLiteral(flag_offset + 1, 16, + kWidth - 10); // render_width_minus_1. + data->InsertLiteral(flag_offset + 17, 16, + kHeight - 10); // render_height_minus_1. + gold->render_width = kWidth - 9; + gold->render_height = kHeight - 9; + gold->render_and_frame_size_different = true; + } + + void OverrideSegmentation(BytesAndBits* const data, Segmentation* const gold, + int offset) { + gold->update_data = true; + data->SetBit(offset++, static_cast<uint8_t>(gold->update_data)); + libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed()); + gold->segment_id_pre_skip = false; + gold->last_active_segment_id = 0; + for (int i = 0; i < kMaxSegments; ++i) { + for (int j = 0; j < kSegmentFeatureMax; ++j) { + gold->feature_enabled[i][j] = static_cast<bool>(rnd.Rand8() & 1); + data->InsertBit(offset++, + static_cast<uint8_t>(gold->feature_enabled[i][j])); + if (gold->feature_enabled[i][j]) { + gold->feature_data[i][j] = rnd(1 << kSegmentationFeatureBits[j]); + if (Segmentation::FeatureSigned(static_cast<SegmentFeature>(j))) { + if (static_cast<bool>(rnd.Rand8() & 1)) { + gold->feature_data[i][j] *= -1; + } + data->InsertInverseSignedLiteral( + offset, kSegmentationFeatureBits[j], gold->feature_data[i][j]); + offset += kSegmentationFeatureBits[j] + 1; + } else { + data->InsertLiteral(offset, kSegmentationFeatureBits[j], + gold->feature_data[i][j]); + offset += kSegmentationFeatureBits[j]; + } + gold->last_active_segment_id = i; + if (j >= kSegmentFeatureReferenceFrame) { + gold->segment_id_pre_skip = true; + } + } + } + } + } + + void VerifyObuHeader(bool extension) { + EXPECT_EQ(obu_->obu_headers().back().temporal_id, extension ? 6 : 0); + EXPECT_EQ(obu_->obu_headers().back().spatial_id, extension ? 2 : 0); + } + +#define OBU_TEST_COMPARE(x) EXPECT_EQ(expected.x, actual.x) + void VerifyFrameParameters(const ObuFrameHeader& expected, + bool id_bits_present = false) { + const ObuFrameHeader& actual = obu_->frame_header(); + OBU_TEST_COMPARE(show_existing_frame); + if (actual.show_existing_frame) { + OBU_TEST_COMPARE(frame_to_show); + OBU_TEST_COMPARE(frame_presentation_time); + if (id_bits_present) { + OBU_TEST_COMPARE(display_frame_id); + } + return; + } + OBU_TEST_COMPARE(frame_type); + OBU_TEST_COMPARE(show_frame); + OBU_TEST_COMPARE(frame_presentation_time); + OBU_TEST_COMPARE(showable_frame); + OBU_TEST_COMPARE(error_resilient_mode); + OBU_TEST_COMPARE(enable_cdf_update); + OBU_TEST_COMPARE(current_frame_id); + OBU_TEST_COMPARE(frame_size_override_flag); + OBU_TEST_COMPARE(order_hint); + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + OBU_TEST_COMPARE(reference_order_hint[i]); + } + OBU_TEST_COMPARE(primary_reference_frame); + OBU_TEST_COMPARE(width); + OBU_TEST_COMPARE(height); + OBU_TEST_COMPARE(render_and_frame_size_different); + OBU_TEST_COMPARE(render_width); + OBU_TEST_COMPARE(render_height); + OBU_TEST_COMPARE(upscaled_width); + OBU_TEST_COMPARE(coded_lossless); + OBU_TEST_COMPARE(upscaled_lossless); + OBU_TEST_COMPARE(allow_screen_content_tools); + OBU_TEST_COMPARE(is_motion_mode_switchable); + OBU_TEST_COMPARE(refresh_frame_flags); + OBU_TEST_COMPARE(enable_frame_end_update_cdf); + OBU_TEST_COMPARE(force_integer_mv); + if (actual.frame_type == kFrameInter) { + for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) { + OBU_TEST_COMPARE(reference_frame_index[i]); + } + } + OBU_TEST_COMPARE(use_superres); + OBU_TEST_COMPARE(rows4x4); + OBU_TEST_COMPARE(columns4x4); + } + + void VerifyLoopFilterParameters(const LoopFilter& expected) { + const LoopFilter& actual = obu_->frame_header().loop_filter; + for (int i = 0; i < 4; ++i) { + OBU_TEST_COMPARE(level[i]); + } + OBU_TEST_COMPARE(sharpness); + OBU_TEST_COMPARE(delta_enabled); + OBU_TEST_COMPARE(delta_update); + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + OBU_TEST_COMPARE(ref_deltas[i]); + } + for (int i = 0; i < kLoopFilterMaxModeDeltas; ++i) { + OBU_TEST_COMPARE(mode_deltas[i]); + } + } + + void VerifyQuantizerParameters(const QuantizerParameters& expected) { + const QuantizerParameters& actual = obu_->frame_header().quantizer; + OBU_TEST_COMPARE(base_index); + OBU_TEST_COMPARE(delta_dc[kPlaneY]); + OBU_TEST_COMPARE(delta_dc[kPlaneU]); + OBU_TEST_COMPARE(delta_dc[kPlaneV]); + EXPECT_EQ(0, actual.delta_ac[kPlaneY]); + OBU_TEST_COMPARE(delta_ac[kPlaneY]); + OBU_TEST_COMPARE(delta_ac[kPlaneU]); + OBU_TEST_COMPARE(delta_ac[kPlaneV]); + OBU_TEST_COMPARE(use_matrix); + OBU_TEST_COMPARE(matrix_level[kPlaneY]); + OBU_TEST_COMPARE(matrix_level[kPlaneU]); + OBU_TEST_COMPARE(matrix_level[kPlaneV]); + } + + void VerifySegmentationParameters(const Segmentation& expected) { + const Segmentation& actual = obu_->frame_header().segmentation; + OBU_TEST_COMPARE(enabled); + OBU_TEST_COMPARE(update_map); + OBU_TEST_COMPARE(update_data); + OBU_TEST_COMPARE(temporal_update); + OBU_TEST_COMPARE(segment_id_pre_skip); + OBU_TEST_COMPARE(last_active_segment_id); + for (int i = 0; i < kMaxSegments; ++i) { + for (int j = 0; j < kSegmentFeatureMax; ++j) { + OBU_TEST_COMPARE(feature_enabled[i][j]); + OBU_TEST_COMPARE(feature_data[i][j]); + } + } + } + + void VerifyDeltaParameters(const Delta& expected, const Delta& actual) { + OBU_TEST_COMPARE(present); + OBU_TEST_COMPARE(scale); + OBU_TEST_COMPARE(multi); + } + + void VerifyCdefParameters(const Cdef& expected) { + const Cdef& actual = obu_->frame_header().cdef; + OBU_TEST_COMPARE(damping); + OBU_TEST_COMPARE(bits); + for (int i = 0; i < (1 << actual.bits); ++i) { + OBU_TEST_COMPARE(y_primary_strength[i]); + OBU_TEST_COMPARE(y_secondary_strength[i]); + OBU_TEST_COMPARE(uv_primary_strength[i]); + OBU_TEST_COMPARE(uv_secondary_strength[i]); + } + } + + void VerifyLoopRestorationParameters(const LoopRestoration& expected) { + const LoopRestoration& actual = obu_->frame_header().loop_restoration; + for (int i = 0; i < kMaxPlanes; ++i) { + OBU_TEST_COMPARE(type[i]); + OBU_TEST_COMPARE(unit_size_log2[i]); + } + } + + void VerifyGlobalMotionParameters( + const std::array<GlobalMotion, kNumReferenceFrameTypes>& gold) { + for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) { + const GlobalMotion& expected = gold[i]; + const GlobalMotion& actual = obu_->frame_header().global_motion[i]; + OBU_TEST_COMPARE(type) << " i: " << i; + for (int j = 0; j < 6; ++j) { + OBU_TEST_COMPARE(params[j]) << " i: " << i << " j: " << j; + } + } + } + + void VerifyFilmGrainParameters(const FilmGrainParams& expected) { + const FilmGrainParams& actual = obu_->frame_header().film_grain_params; + OBU_TEST_COMPARE(apply_grain); + OBU_TEST_COMPARE(update_grain); + OBU_TEST_COMPARE(chroma_scaling_from_luma); + OBU_TEST_COMPARE(overlap_flag); + OBU_TEST_COMPARE(clip_to_restricted_range); + OBU_TEST_COMPARE(num_y_points); + OBU_TEST_COMPARE(num_u_points); + OBU_TEST_COMPARE(num_v_points); + for (int i = 0; i < 14; ++i) { + OBU_TEST_COMPARE(point_y_value[i]); + OBU_TEST_COMPARE(point_y_scaling[i]); + } + for (int i = 0; i < 10; ++i) { + OBU_TEST_COMPARE(point_u_value[i]); + OBU_TEST_COMPARE(point_u_scaling[i]); + } + for (int i = 0; i < 10; ++i) { + OBU_TEST_COMPARE(point_v_value[i]); + OBU_TEST_COMPARE(point_v_scaling[i]); + } + OBU_TEST_COMPARE(chroma_scaling); + OBU_TEST_COMPARE(auto_regression_coeff_lag); + for (int i = 0; i < 24; ++i) { + OBU_TEST_COMPARE(auto_regression_coeff_y[i]); + } + for (int i = 0; i < 25; ++i) { + OBU_TEST_COMPARE(auto_regression_coeff_u[i]); + } + for (int i = 0; i < 25; ++i) { + OBU_TEST_COMPARE(auto_regression_coeff_v[i]); + } + OBU_TEST_COMPARE(auto_regression_shift); + OBU_TEST_COMPARE(grain_seed); + OBU_TEST_COMPARE(reference_index); + OBU_TEST_COMPARE(grain_scale_shift); + OBU_TEST_COMPARE(u_multiplier); + OBU_TEST_COMPARE(u_luma_multiplier); + OBU_TEST_COMPARE(u_offset); + OBU_TEST_COMPARE(v_multiplier); + OBU_TEST_COMPARE(v_luma_multiplier); + OBU_TEST_COMPARE(v_offset); + } + + void VerifyTileInfoParameters(const TileInfo& expected) { + const TileInfo& actual = obu_->frame_header().tile_info; + OBU_TEST_COMPARE(uniform_spacing); + OBU_TEST_COMPARE(tile_columns_log2); + OBU_TEST_COMPARE(tile_columns); + for (int i = 0; i < kMaxTileColumns + 1; ++i) { + OBU_TEST_COMPARE(tile_column_start[i]) << "tile_column: " << i; + OBU_TEST_COMPARE(tile_column_width_in_superblocks[i]) + << "tile_column: " << i; + } + OBU_TEST_COMPARE(tile_rows_log2); + OBU_TEST_COMPARE(tile_rows); + for (int i = 0; i < kMaxTileRows + 1; ++i) { + OBU_TEST_COMPARE(tile_row_start[i]) << "tile_row: " << i; + OBU_TEST_COMPARE(tile_row_height_in_superblocks[i]) << "tile_rows: " << i; + } + OBU_TEST_COMPARE(tile_count); + OBU_TEST_COMPARE(context_update_id); + OBU_TEST_COMPARE(tile_size_bytes); + } + + void VerifySequenceHeader(const ObuSequenceHeader& expected) { + EXPECT_TRUE(obu_->sequence_header_changed()); + const ObuSequenceHeader& actual = obu_->sequence_header(); + OBU_TEST_COMPARE(profile); + OBU_TEST_COMPARE(still_picture); + OBU_TEST_COMPARE(reduced_still_picture_header); + OBU_TEST_COMPARE(operating_points); + for (int i = 0; i < actual.operating_points; ++i) { + OBU_TEST_COMPARE(operating_point_idc[i]) << "i: " << i; + OBU_TEST_COMPARE(level[i].major) << "i: " << i; + OBU_TEST_COMPARE(level[i].minor) << "i: " << i; + OBU_TEST_COMPARE(tier[i]) << "i: " << i; + } + OBU_TEST_COMPARE(frame_width_bits); + OBU_TEST_COMPARE(frame_height_bits); + OBU_TEST_COMPARE(max_frame_width); + OBU_TEST_COMPARE(max_frame_height); + OBU_TEST_COMPARE(frame_id_numbers_present); + if (actual.frame_id_numbers_present) { + OBU_TEST_COMPARE(frame_id_length_bits); + OBU_TEST_COMPARE(delta_frame_id_length_bits); + } + OBU_TEST_COMPARE(use_128x128_superblock); + OBU_TEST_COMPARE(enable_filter_intra); + OBU_TEST_COMPARE(enable_intra_edge_filter); + OBU_TEST_COMPARE(enable_interintra_compound); + OBU_TEST_COMPARE(enable_masked_compound); + OBU_TEST_COMPARE(enable_warped_motion); + OBU_TEST_COMPARE(enable_dual_filter); + OBU_TEST_COMPARE(enable_order_hint); + OBU_TEST_COMPARE(enable_jnt_comp); + OBU_TEST_COMPARE(enable_ref_frame_mvs); + OBU_TEST_COMPARE(choose_screen_content_tools); + OBU_TEST_COMPARE(force_screen_content_tools); + OBU_TEST_COMPARE(choose_integer_mv); + OBU_TEST_COMPARE(force_integer_mv); + OBU_TEST_COMPARE(order_hint_bits); + OBU_TEST_COMPARE(enable_superres); + OBU_TEST_COMPARE(enable_cdef); + OBU_TEST_COMPARE(enable_restoration); + OBU_TEST_COMPARE(color_config.bitdepth); + OBU_TEST_COMPARE(color_config.is_monochrome); + OBU_TEST_COMPARE(color_config.color_range); + OBU_TEST_COMPARE(color_config.subsampling_x); + OBU_TEST_COMPARE(color_config.subsampling_y); + OBU_TEST_COMPARE(color_config.chroma_sample_position); + OBU_TEST_COMPARE(timing_info_present_flag); + OBU_TEST_COMPARE(timing_info.num_units_in_tick); + OBU_TEST_COMPARE(timing_info.time_scale); + OBU_TEST_COMPARE(timing_info.equal_picture_interval); + OBU_TEST_COMPARE(timing_info.num_ticks_per_picture); + OBU_TEST_COMPARE(decoder_model_info_present_flag); + OBU_TEST_COMPARE(decoder_model_info.encoder_decoder_buffer_delay_length); + OBU_TEST_COMPARE(decoder_model_info.num_units_in_decoding_tick); + OBU_TEST_COMPARE(decoder_model_info.buffer_removal_time_length); + OBU_TEST_COMPARE(decoder_model_info.frame_presentation_time_length); + for (int i = 0; i < actual.operating_points; ++i) { + SCOPED_TRACE("i: " + std::to_string(i)); + OBU_TEST_COMPARE(operating_parameters.decoder_buffer_delay[i]); + OBU_TEST_COMPARE(operating_parameters.encoder_buffer_delay[i]); + OBU_TEST_COMPARE(operating_parameters.low_delay_mode_flag[i]); + OBU_TEST_COMPARE(initial_display_delay[i]); + } + OBU_TEST_COMPARE(film_grain_params_present); + } + + void VerifyMetadata(MetadataType type, const ObuMetadata& expected) { + const ObuMetadata& actual = obu_->metadata(); + switch (type) { + case kMetadataTypeHdrContentLightLevel: + OBU_TEST_COMPARE(max_cll); + OBU_TEST_COMPARE(max_fall); + break; + case kMetadataTypeHdrMasteringDisplayColorVolume: + for (int i = 0; i < 3; ++i) { + OBU_TEST_COMPARE(primary_chromaticity_x[i]); + OBU_TEST_COMPARE(primary_chromaticity_y[i]); + } + OBU_TEST_COMPARE(white_point_chromaticity_x); + OBU_TEST_COMPARE(white_point_chromaticity_y); + OBU_TEST_COMPARE(luminance_max); + OBU_TEST_COMPARE(luminance_min); + break; + case kMetadataTypeScalability: + break; + case kMetadataTypeItutT35: + OBU_TEST_COMPARE(itu_t_t35_country_code); + OBU_TEST_COMPARE(itu_t_t35_country_code_extension_byte); + ASSERT_EQ(expected.itu_t_t35_payload_size, + actual.itu_t_t35_payload_size); + if (actual.itu_t_t35_payload_size != 0) { + EXPECT_EQ(memcmp(expected.itu_t_t35_payload_bytes.get(), + actual.itu_t_t35_payload_bytes.get(), + actual.itu_t_t35_payload_size), + 0); + } + break; + case kMetadataTypeTimecode: + break; + } + } + +#undef OBU_TEST_COMPARE + + // Accessors to private members of ObuParser. This avoids the need for a + // dependency on a googletest header in the main library for FRIEND_TEST() + // (or the need to duplicate the implementation). + bool ObuParseFrameParameters() { return obu_->ParseFrameParameters(); } + bool ObuParseLoopFilterParameters() { + return obu_->ParseLoopFilterParameters(); + } + bool ObuParseLoopFilterDeltaParameters() { + return obu_->ParseLoopFilterDeltaParameters(); + } + bool ObuParseQuantizerParameters() { + return obu_->ParseQuantizerParameters(); + } + bool ObuParseQuantizerIndexDeltaParameters() { + return obu_->ParseQuantizerIndexDeltaParameters(); + } + void ObuComputeSegmentLosslessAndQIndex() { + obu_->ComputeSegmentLosslessAndQIndex(); + } + bool ObuParseCdefParameters() { return obu_->ParseCdefParameters(); } + bool ObuParseLoopRestorationParameters() { + return obu_->ParseLoopRestorationParameters(); + } + bool ObuParseTxModeSyntax() { return obu_->ParseTxModeSyntax(); } + bool ObuIsSkipModeAllowed() { return obu_->IsSkipModeAllowed(); } + bool ObuParseSkipModeParameters() { return obu_->ParseSkipModeParameters(); } + bool ObuReadAllowWarpedMotion() { return obu_->ReadAllowWarpedMotion(); } + bool ObuSetFrameReferences(int8_t last_frame_idx, int8_t gold_frame_idx) { + return obu_->SetFrameReferences(last_frame_idx, gold_frame_idx); + } + + std::unique_ptr<BufferPool> buffer_pool_; + DecoderState decoder_state_; + std::unique_ptr<ObuParser> obu_; + // The following members are reset with each Init(). + Vector<ObuHeader>* obu_headers_; + ObuFrameHeader* obu_frame_header_; + ObuSequenceHeader* obu_sequence_header_; + RefCountedBufferPtr current_frame_; +}; + +TEST_F(ObuParserTest, InvalidInputs) { + obu_.reset(new (std::nothrow) + ObuParser(nullptr, 0, 0, buffer_pool_.get(), &decoder_state_)); + EXPECT_EQ(obu_->ParseOneFrame(¤t_frame_), kStatusInvalidArgument); + obu_.reset(new (std::nothrow) ObuParser(nullptr, 10, 0, buffer_pool_.get(), + &decoder_state_)); + EXPECT_EQ(obu_->ParseOneFrame(¤t_frame_), kStatusInvalidArgument); + obu_.reset(new (std::nothrow) + ObuParser(kDefaultTemporalDelimiter.data(), 0, 0, + buffer_pool_.get(), &decoder_state_)); + EXPECT_EQ(obu_->ParseOneFrame(¤t_frame_), kStatusInvalidArgument); +} + +TEST_F(ObuParserTest, TemporalDelimiter) { + BytesAndBits data; + data.AppendBytes(kDefaultTemporalDelimiter); + + ASSERT_TRUE(Parse(data.GenerateData())); + EXPECT_EQ(obu_->obu_headers().size(), 1); + EXPECT_EQ(obu_->obu_headers().back().type, kObuTemporalDelimiter); + VerifyObuHeader(false); + + // forbidden_bit is not zero. + data.SetBit(0, 1); + EXPECT_FALSE(Parse(data.GenerateData())); +} + +TEST_F(ObuParserTest, HeaderExtensions) { + BytesAndBits data; + data.AppendBytes(kDefaultTemporalDelimiterWithExtension); + + ASSERT_TRUE(Parse(data.GenerateData())); + EXPECT_EQ(obu_->obu_headers().size(), 1); + EXPECT_EQ(obu_->obu_headers().back().type, kObuTemporalDelimiter); + VerifyObuHeader(true); + + // extension flag is set but no extensions found. + data.Clear(); + data.AppendByte(kDefaultTemporalDelimiterWithExtension[0]); + EXPECT_FALSE(Parse(data.GenerateData())); +} + +TEST_F(ObuParserTest, HeaderHasSizeFieldNotSet) { + BytesAndBits data; + data.AppendBytes(kDefaultHeaderWithoutSizeField); + + EXPECT_FALSE(Parse(data.GenerateData())); +} + +TEST_F(ObuParserTest, SequenceHeader) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +TEST_F(ObuParserTest, SequenceHeaderLevel) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + // Set level to 1. + gold.level[0].major = 2; + gold.level[0].minor = 1; + data.SetLiteral(24, 5, 1); // level. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + // Set operating_point_idc of operating point 1 to 0x101 (temporal layer 0 + // and spatial layer 0 should be decoded). Set level of operating point 1 to + // 8 (4.0) and tier to 1. + gold.operating_points = 2; + gold.operating_point_idc[1] = (1 << 0) | (1 << (0 + 8)); + gold.level[1].major = 4; + gold.level[1].minor = 0; + gold.tier[1] = 1; + data.SetLiteral(7, 5, gold.operating_points - 1); + data.InsertLiteral(29, 12, 0x101); // operating_point_idc. + data.InsertLiteral(41, 5, 8); // level. + data.InsertBit(46, gold.tier[1]); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +TEST_F(ObuParserTest, SequenceHeaderProfile) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.still_picture = true; + data.SetBit(3, static_cast<uint8_t>(gold.still_picture)); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + // profile 2; bitdepth 8; + gold.profile = kProfile2; + gold.color_config.bitdepth = 8; + gold.color_config.subsampling_x = 1; + gold.color_config.subsampling_y = 0; + data.SetLiteral(0, 3, gold.profile); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + // profile 2; bitdepth 10; + gold.color_config.bitdepth = 10; + data.SetBit(73, 1); // high_bitdepth. + data.InsertBit(74, 0); // twelve_bit. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + // profile 2; bitdepth 12; + gold.color_config.bitdepth = 12; + gold.color_config.subsampling_y = 1; + data.SetBit(74, 1); // twelve_bit. + data.InsertBit(78, 1); // subsampling_x. + data.InsertBit(79, 1); // subsampling_y. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +TEST_F(ObuParserTest, SequenceHeaderIdLength) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.frame_id_numbers_present = true; + gold.delta_frame_id_length_bits = kDeltaFrameIdLengthBits; + gold.frame_id_length_bits = kFrameIdLengthBits; + data.SetBit(54, 1); // frame_id_numbers_present. + data.InsertLiteral(55, 4, kDeltaFrameIdLengthBits - 2); + data.InsertLiteral(59, 3, kFrameIdLengthBits - kDeltaFrameIdLengthBits - 1); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +// An idLen greater than 16 is invalid. +TEST_F(ObuParserTest, SequenceHeaderIdLengthInvalid) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + + data.SetBit(54, 1); // frame_id_numbers_present. + data.InsertLiteral(55, 4, kDeltaFrameIdLengthBits - 2); + data.InsertLiteral(59, 3, 17 - kDeltaFrameIdLengthBits - 1); // idLen = 17. + + ASSERT_FALSE(ParseSequenceHeader(data.GenerateData())); +} + +TEST_F(ObuParserTest, SequenceHeaderFlags) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.enable_warped_motion = true; + gold.enable_superres = true; + data.SetBit(60, 1); // enable_warped_motion. + data.SetBit(70, 1); // enable_superres. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +TEST_F(ObuParserTest, SequenceHeaderForceScreenContentToolsEqualTo0) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.choose_screen_content_tools = false; + gold.force_screen_content_tools = 0; + gold.choose_integer_mv = false; + gold.force_integer_mv = 2; + data.SetBit(65, 0); // choose_screen_content_tools. + data.SetBit(66, 0); // force_screen_content_tools. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +TEST_F(ObuParserTest, SequenceHeaderMonochrome) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.color_config.is_monochrome = true; + gold.color_config.color_range = kColorRangeFull; + data.SetBit(74, 1); // monochrome. + data.InsertBit(76, 1); // color_range. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +// This tests TimingInfo, DecoderModelInfo and OperatingParameters. The test is +// kind of long but it is the simplest way to test all three since they are +// dependent on one another. +TEST_F(ObuParserTest, SequenceHeaderTimingInfo) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.timing_info_present_flag = true; + gold.timing_info.num_units_in_tick = 100; + gold.timing_info.time_scale = 1000; + gold.timing_info.equal_picture_interval = false; + gold.decoder_model_info_present_flag = false; + data.SetBit(5, static_cast<uint8_t>(gold.timing_info_present_flag)); + data.InsertLiteral(6, 32, gold.timing_info.num_units_in_tick); + data.InsertLiteral(38, 32, gold.timing_info.time_scale); + data.InsertBit(70, + static_cast<uint8_t>(gold.timing_info.equal_picture_interval)); + data.InsertBit(71, + static_cast<uint8_t>(gold.decoder_model_info_present_flag)); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + gold.timing_info.equal_picture_interval = true; + gold.timing_info.num_ticks_per_picture = 7; + data.SetBit(70, + static_cast<uint8_t>(gold.timing_info.equal_picture_interval)); + EXPECT_EQ(data.InsertUvlc(71, gold.timing_info.num_ticks_per_picture - 1), 5); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + gold.decoder_model_info_present_flag = true; + gold.decoder_model_info.encoder_decoder_buffer_delay_length = 5; + gold.decoder_model_info.num_units_in_decoding_tick = 1000; + gold.decoder_model_info.buffer_removal_time_length = 18; + gold.decoder_model_info.frame_presentation_time_length = 20; + + data.SetBit(76, static_cast<uint8_t>(gold.decoder_model_info_present_flag)); + data.InsertLiteral( + 77, 5, gold.decoder_model_info.encoder_decoder_buffer_delay_length - 1); + data.InsertLiteral(82, 32, + gold.decoder_model_info.num_units_in_decoding_tick); + data.InsertLiteral(114, 5, + gold.decoder_model_info.buffer_removal_time_length - 1); + data.InsertLiteral( + 119, 5, gold.decoder_model_info.frame_presentation_time_length - 1); + data.InsertBit(147, 0); // decoder_model_present_for_this_op. + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); + + gold.operating_parameters.decoder_buffer_delay[0] = 10; + gold.operating_parameters.encoder_buffer_delay[0] = 20; + gold.operating_parameters.low_delay_mode_flag[0] = true; + + data.SetBit(147, 1); // decoder_model_present_for_this_op. + data.InsertLiteral( + 148, gold.decoder_model_info.encoder_decoder_buffer_delay_length, + gold.operating_parameters.decoder_buffer_delay[0]); + data.InsertLiteral( + 153, gold.decoder_model_info.encoder_decoder_buffer_delay_length, + gold.operating_parameters.encoder_buffer_delay[0]); + data.InsertBit(158, static_cast<uint8_t>( + gold.operating_parameters.low_delay_mode_flag[0])); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +TEST_F(ObuParserTest, SequenceHeaderInitialDisplayDelay) { + BytesAndBits data; + data.AppendBytes(kDefaultSequenceHeader); + ObuSequenceHeader gold; + DefaultSequenceHeader(&gold); + + gold.initial_display_delay[0] = 8; + + data.SetBit(6, 1); // initial_display_delay_present_flag. + data.InsertBit(29, 1); // initial_display_delay_present_for_this_op. + data.InsertLiteral(30, 4, gold.initial_display_delay[0] - 1); + + ASSERT_TRUE(ParseSequenceHeader(data.GenerateData())); + VerifySequenceHeader(gold); +} + +// Parsing of a frame header should fail if no sequence header has been +// received. +TEST_F(ObuParserTest, FrameHeaderWithoutSequenceHeader) { + // The aom-test-data test vector av1-1-b8-01-size-16x16.ivf has two temporal + // units. The first temporal unit has a presentation timestamp of 0 and + // consists of three OBUs: a temporal delimiter OBU, a sequence header OBU, + // and a frame OBU. + const std::vector<uint8_t> kTemporalDelimiter = {0x12, 0x00}; + const std::vector<uint8_t> kSequenceHeader = { + 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80}; + const std::vector<uint8_t> kFrame = { + 0x32, 0xa6, 0x01, 0x10, 0x00, 0x87, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x9e, 0x86, 0x5b, 0xb2, 0x22, 0xb5, 0x58, 0x4d, 0x68, 0xe6, + 0x37, 0x54, 0x42, 0x7b, 0x84, 0xce, 0xdf, 0x9f, 0xec, 0xab, 0x07, 0x4d, + 0xf6, 0xe1, 0x5e, 0x9e, 0x27, 0xbf, 0x93, 0x2f, 0x47, 0x0d, 0x7b, 0x7c, + 0x45, 0x8d, 0xcf, 0x26, 0xf7, 0x6c, 0x06, 0xd7, 0x8c, 0x2e, 0xf5, 0x2c, + 0xb0, 0x8a, 0x31, 0xac, 0x69, 0xf5, 0xcd, 0xd8, 0x71, 0x5d, 0xaf, 0xf8, + 0x96, 0x43, 0x8c, 0x9c, 0x23, 0x6f, 0xab, 0xd0, 0x35, 0x43, 0xdf, 0x81, + 0x12, 0xe3, 0x7d, 0xec, 0x22, 0xb0, 0x30, 0x54, 0x32, 0x9f, 0x90, 0xc0, + 0x5d, 0x64, 0x9b, 0x0f, 0x75, 0x31, 0x84, 0x3a, 0x57, 0xd7, 0x5f, 0x03, + 0x6e, 0x7f, 0x43, 0x17, 0x6d, 0x08, 0xc3, 0x81, 0x8a, 0xae, 0x73, 0x1c, + 0xa8, 0xa7, 0xe4, 0x9c, 0xa9, 0x5b, 0x3f, 0xd1, 0xeb, 0x75, 0x3a, 0x7f, + 0x22, 0x77, 0x38, 0x64, 0x1c, 0x77, 0xdb, 0xcd, 0xef, 0xb7, 0x08, 0x45, + 0x8e, 0x7f, 0xea, 0xa3, 0xd0, 0x81, 0xc9, 0xc1, 0xbc, 0x93, 0x9b, 0x41, + 0xb1, 0xa1, 0x42, 0x17, 0x98, 0x3f, 0x1e, 0x95, 0xdf, 0x68, 0x7c, 0xb7, + 0x98}; + + BytesAndBits data; + data.AppendBytes(kTemporalDelimiter); + // Skip the sequence header OBU. + data.AppendBytes(kFrame); + ASSERT_FALSE(Parse(data.GenerateData())); + + // Now verify that all three OBUs are correct, by adding them to |data| + // successively. + data.Clear(); + data.AppendBytes(kTemporalDelimiter); + ASSERT_TRUE(Parse(data.GenerateData())); + data.Clear(); + data.AppendBytes(kTemporalDelimiter); + data.AppendBytes(kSequenceHeader); + ASSERT_TRUE(Parse(data.GenerateData())); + data.Clear(); + data.AppendBytes(kTemporalDelimiter); + data.AppendBytes(kSequenceHeader); + data.AppendBytes(kFrame); + ASSERT_TRUE(Parse(data.GenerateData())); +} + +TEST_F(ObuParserTest, FrameParameterShowExistingFrame) { + BytesAndBits data; + data.AppendBit(1); // show_existing_frame. + data.AppendLiteral(3, kFrameToShow); // frame_to_show. + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + gold.show_existing_frame = true; + gold.frame_to_show = kFrameToShow; + + // kFrameToShow'th frame is not yet decoded. + ASSERT_FALSE(ParseFrameParameters(data.GenerateData())); + + decoder_state_.reference_frame[kFrameToShow] = buffer_pool_->GetFreeBuffer(); + // kFrameToShow'th frame is not a showable frame. + ASSERT_FALSE(ParseFrameParameters(data.GenerateData())); + + decoder_state_.reference_frame[kFrameToShow]->set_showable_frame(true); + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParametersShowExistingFrameWithDisplayFrameId) { + BytesAndBits data; + data.AppendBit(1); // show_existing_frame. + data.AppendLiteral(3, kFrameToShow); // frame_to_show. + data.AppendLiteral(15, kDisplayFrameId); // display_frame_id. + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + gold.show_existing_frame = true; + gold.frame_to_show = kFrameToShow; + gold.display_frame_id = kDisplayFrameId; + + // kFrameToShow'th frame is not yet decoded. + ASSERT_FALSE(ParseFrameParameters(data.GenerateData(), true)); + + decoder_state_.reference_frame_id[kFrameToShow] = kDisplayFrameId; + decoder_state_.reference_frame[kFrameToShow] = buffer_pool_->GetFreeBuffer(); + // kFrameToShow'th frame is not a showable frame. + ASSERT_FALSE(ParseFrameParameters(data.GenerateData(), true)); + + decoder_state_.reference_frame[kFrameToShow]->set_showable_frame(true); + ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), true)); + VerifyFrameParameters(gold, true); +} + +TEST_F(ObuParserTest, FrameParameterShowExistingFrameTemporalPointInfo) { + BytesAndBits data; + data.AppendBit(1); // show_existing_frame. + data.AppendLiteral(3, kFrameToShow); // frame_to_show. + data.AppendLiteral(20, 38); // frame_presentation_time. + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + gold.show_existing_frame = true; + gold.frame_to_show = kFrameToShow; + gold.frame_presentation_time = 38; + + EXPECT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->frame_width_bits = kFrameWidthBits; + obu_sequence_header_->frame_height_bits = kFrameHeightBits; + obu_sequence_header_->max_frame_width = kWidth; + obu_sequence_header_->max_frame_height = kHeight; + + obu_sequence_header_->decoder_model_info_present_flag = true; + obu_sequence_header_->decoder_model_info.frame_presentation_time_length = 20; + + decoder_state_.reference_frame[kFrameToShow] = buffer_pool_->GetFreeBuffer(); + decoder_state_.reference_frame[kFrameToShow]->set_showable_frame(true); + + ASSERT_TRUE(ObuParseFrameParameters()); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterErrorResilientMode) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameIntraOnly); + + gold.error_resilient_mode = true; + data.SetBit(4, static_cast<uint8_t>(gold.error_resilient_mode)); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterKeyFrame) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderKeyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterKeyFrameTemporalPointInfo) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderKeyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + + data.InsertLiteral(4, 20, 38); // frame_presentation_time. + gold.frame_presentation_time = 38; + + EXPECT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->frame_width_bits = kFrameWidthBits; + obu_sequence_header_->frame_height_bits = kFrameHeightBits; + obu_sequence_header_->max_frame_width = kWidth; + obu_sequence_header_->max_frame_height = kHeight; + + obu_sequence_header_->decoder_model_info_present_flag = true; + obu_sequence_header_->decoder_model_info.frame_presentation_time_length = 20; + + ASSERT_TRUE(ObuParseFrameParameters()); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterKeyFrameOverrideSize) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderKeyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + + OverrideFrameSize(&data, &gold, 5, 6); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); + + OverrideRenderSize(&data, &gold, 23); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterKeyFrameSuperRes) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderKeyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + gold.use_superres = true; + gold.superres_scale_denominator = 15; + gold.width = kWidth * 8 / 15; + gold.columns4x4 = 58; + + data.SetBit(6, static_cast<int>(gold.use_superres)); + data.SetLiteral(7, 3, gold.superres_scale_denominator - 9); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 0, 0, true)); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterKeyFrameAllowScreenContentTools) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderKeyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameKey); + + data.InsertBit(5, 1); // allow_screen_content_tools. + data.InsertBit(8, 1); // allow_intrabc. + gold.allow_screen_content_tools = true; + gold.allow_intrabc = true; + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 2)); + VerifyFrameParameters(gold); + + data.InsertBit(6, 1); // force_integer_mv. + gold.force_integer_mv = 1; + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 2, 2)); + VerifyFrameParameters(gold); + + data.SetBit(6, 0); // force_integer_mv. + + // Gold need not be updated, because force_integer_mv is always 1 for + // keyframes. + ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 2, 2)); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterIntraOnlyFrame) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameIntraOnly); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterIntraOnlyFrameOverrideSize) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameIntraOnly); + + OverrideFrameSize(&data, &gold, 6, 15); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); + + OverrideRenderSize(&data, &gold, 32); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +// An INTRA_ONLY_FRAME cannot set refresh_frame_flags to 0xff. +TEST_F(ObuParserTest, FrameParameterIntraOnlyFrameRefreshAllFrames) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame); + data.SetLiteral(7, 8, 0xFF); // refresh_frame_flags. + + ASSERT_FALSE(ParseFrameParameters(data.GenerateData())); +} + +TEST_F(ObuParserTest, FrameParameterInterFrame) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderInterFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameInter); + ObuFrameHeader reference_frame_header; + reference_frame_header.width = kWidth; + reference_frame_header.height = kHeight; + reference_frame_header.render_width = kWidth; + reference_frame_header.render_height = kHeight; + reference_frame_header.upscaled_width = kWidth; + reference_frame_header.rows4x4 = kRows4x4; + reference_frame_header.columns4x4 = kColumns4x4; + reference_frame_header.refresh_frame_flags = 0; + for (auto& reference_frame : decoder_state_.reference_frame) { + reference_frame = buffer_pool_->GetFreeBuffer(); + EXPECT_TRUE(reference_frame->SetFrameDimensions(reference_frame_header)); + } + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +TEST_F(ObuParserTest, FrameParameterInterFrameOverrideSize) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderInterFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameInter); + ObuFrameHeader reference_frame_header; + reference_frame_header.width = kWidth; + reference_frame_header.height = kHeight; + reference_frame_header.render_width = kWidth; + reference_frame_header.render_height = kHeight; + reference_frame_header.upscaled_width = kWidth; + reference_frame_header.rows4x4 = kRows4x4; + reference_frame_header.columns4x4 = kColumns4x4; + reference_frame_header.refresh_frame_flags = 0; + for (auto& reference_frame : decoder_state_.reference_frame) { + reference_frame = buffer_pool_->GetFreeBuffer(); + EXPECT_TRUE(reference_frame->SetFrameDimensions(reference_frame_header)); + } + + data.InsertLiteral(39, kNumInterReferenceFrameTypes, 0); // found_ref. + OverrideFrameSize(&data, &gold, 6, 46); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); + + OverrideRenderSize(&data, &gold, 63); + + ASSERT_TRUE(ParseFrameParameters(data.GenerateData())); + VerifyFrameParameters(gold); +} + +// This test verifies we check the following requirement at the end of Section +// 6.8.4: +// If FrameIsIntra is equal to 0 (indicating that this frame may use inter +// prediction), the requirements described in the frame size with refs +// semantics of section 6.8.6 must also be satisfied. +TEST_F(ObuParserTest, FrameParameterInterFrameInvalidSize) { + BytesAndBits data; + data.AppendBytes(kDefaultFrameHeaderInterFrame); + ObuFrameHeader gold; + DefaultFrameHeader(&gold, kFrameInter); + ObuFrameHeader reference_frame_header; + reference_frame_header.width = kWidth; + reference_frame_header.height = 2 * kHeight + 8; + reference_frame_header.render_width = kWidth; + reference_frame_header.render_height = 2 * kHeight + 8; + reference_frame_header.upscaled_width = kWidth; + reference_frame_header.rows4x4 = 2 * kRows4x4 + 2; + reference_frame_header.columns4x4 = kColumns4x4; + reference_frame_header.refresh_frame_flags = 0; + for (auto& reference_frame : decoder_state_.reference_frame) { + reference_frame = buffer_pool_->GetFreeBuffer(); + EXPECT_TRUE(reference_frame->SetFrameDimensions(reference_frame_header)); + } + + EXPECT_FALSE(ParseFrameParameters(data.GenerateData())); +} + +// Tests the ObuParser::SetFrameReferences() method. +// +// This method uses the following data members as input: +// decoder_state_.reference_order_hint +// sequence_header_.enable_order_hint +// sequence_header_.order_hint_bits +// frame_header_.order_hint +// So we need to set up these data members before calling +// ObuParser::SetFrameReferences(). +// +// The output is in frame_header_.reference_frame_index. +TEST_F(ObuParserTest, SetFrameReferences) { + // All reference frames are forward references (because 9 < 17). + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + decoder_state_.reference_order_hint[i] = 9; + } + + ASSERT_TRUE(Init()); + obu_sequence_header_->enable_order_hint = true; + obu_sequence_header_->order_hint_bits = 5; + obu_sequence_header_->order_hint_shift_bits = + Mod32(32 - obu_sequence_header_->order_hint_bits); + obu_frame_header_->order_hint = 17; + + const int8_t last_frame_idx = 0; + const int8_t gold_frame_idx = 1; + + // Since all reference frames are forward references, we set the remaining + // five references in reverse chronological order. So Last2, Last3, Backward, + // Alternate2, and Alternate are set to 7, 6, 5, 4, and 3, respectively. + + EXPECT_TRUE(ObuSetFrameReferences(last_frame_idx, gold_frame_idx)); + + EXPECT_EQ( + obu_frame_header_ + ->reference_frame_index[kReferenceFrameLast - kReferenceFrameLast], + 0); + EXPECT_EQ( + obu_frame_header_ + ->reference_frame_index[kReferenceFrameLast2 - kReferenceFrameLast], + 7); + EXPECT_EQ( + obu_frame_header_ + ->reference_frame_index[kReferenceFrameLast3 - kReferenceFrameLast], + 6); + EXPECT_EQ( + obu_frame_header_ + ->reference_frame_index[kReferenceFrameGolden - kReferenceFrameLast], + 1); + EXPECT_EQ(obu_frame_header_->reference_frame_index[kReferenceFrameBackward - + kReferenceFrameLast], + 5); + EXPECT_EQ(obu_frame_header_->reference_frame_index[kReferenceFrameAlternate2 - + kReferenceFrameLast], + 4); + EXPECT_EQ(obu_frame_header_->reference_frame_index[kReferenceFrameAlternate - + kReferenceFrameLast], + 3); +} + +TEST_F(ObuParserTest, LoopFilterParameters) { + LoopFilter gold; + memset(&gold, 0, sizeof(gold)); + + BytesAndBits data; + data.AppendBit(0); // dummy. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone; + obu_frame_header_->coded_lossless = true; + gold.ref_deltas[kReferenceFrameIntra] = 1; + gold.ref_deltas[kReferenceFrameGolden] = -1; + gold.ref_deltas[kReferenceFrameAlternate] = -1; + gold.ref_deltas[kReferenceFrameAlternate2] = -1; + ASSERT_TRUE(ObuParseLoopFilterParameters()); + VerifyLoopFilterParameters(gold); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone; + obu_frame_header_->allow_intrabc = true; + ASSERT_TRUE(ObuParseLoopFilterParameters()); + VerifyLoopFilterParameters(gold); + + gold.level[0] = 32; + gold.level[3] = 48; + gold.sharpness = 4; + data.Clear(); + for (const auto& level : gold.level) { + data.AppendLiteral(6, level); + } + data.AppendLiteral(3, gold.sharpness); + data.AppendBit(0); // delta_enabled. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone; + ASSERT_TRUE(ObuParseLoopFilterParameters()); + VerifyLoopFilterParameters(gold); + + gold.delta_enabled = true; + gold.delta_update = true; + gold.ref_deltas[0] = 20; + gold.mode_deltas[0] = -20; + data.SetBit(27, 1); // delta_enabled. + data.AppendBit(1); // delta_update. + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + if (i == 0) { + data.AppendBit(1); // update_ref_delta. + data.AppendInverseSignedLiteral(6, gold.ref_deltas[0]); // ref_delta. + } else { + data.AppendBit(0); // update_ref_delta. + } + } + for (int i = 0; i < kLoopFilterMaxModeDeltas; ++i) { + if (i == 0) { + data.AppendBit(1); // update_mode_delta. + data.AppendInverseSignedLiteral(6, gold.mode_deltas[0]); // mode_delta. + } else { + data.AppendBit(0); // update_mode_delta. + } + } + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone; + ASSERT_TRUE(ObuParseLoopFilterParameters()); + VerifyLoopFilterParameters(gold); +} + +TEST_F(ObuParserTest, QuantizerParameters) { + QuantizerParameters gold = {}; + gold.base_index = 48; + + BytesAndBits data; + data.AppendLiteral(8, gold.base_index); + data.AppendLiteral(3, 0); // delta_coded. + data.AppendBit(0); // use_matrix. + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); +} + +TEST_F(ObuParserTest, QuantizerParametersMonochrome) { + QuantizerParameters gold = {}; + gold.base_index = 48; + + BytesAndBits data; + data.AppendLiteral(8, gold.base_index); + data.AppendBit(0); // delta_coded. + data.AppendBit(0); // use_matrix. + // The quantizer parameters end here. Add a 1 bit. It should not be parsed. + data.AppendBit(1); // Would be segmentation_enabled in a bitstream. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->color_config.is_monochrome = true; + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); +} + +TEST_F(ObuParserTest, QuantizerParametersDeltaCoded) { + QuantizerParameters gold = {}; + gold.base_index = 48; + gold.delta_dc[kPlaneY] = -30; + + BytesAndBits data; + data.AppendLiteral(8, gold.base_index); + data.AppendBit(1); // delta_coded. + data.AppendInverseSignedLiteral(6, gold.delta_dc[kPlaneY]); + data.AppendLiteral(2, 0); // delta_coded u dc/ac. + data.AppendBit(0); // use_matrix. + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); + + gold.delta_dc[kPlaneU] = -40; + gold.delta_dc[kPlaneV] = gold.delta_dc[kPlaneU]; + data.SetBit(16, 1); // delta_coded. + data.InsertInverseSignedLiteral(17, 6, gold.delta_dc[kPlaneU]); + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); + + gold.delta_ac[kPlaneU] = 50; + gold.delta_ac[kPlaneV] = gold.delta_ac[kPlaneU]; + data.SetBit(24, 1); // delta_coded. + data.InsertInverseSignedLiteral(25, 6, gold.delta_ac[kPlaneU]); + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); + + gold.delta_dc[kPlaneV] = 60; + gold.delta_ac[kPlaneV] = 0; + data.InsertBit(16, 1); // diff_uv_delta. + data.InsertBit(33, 1); // delta_coded. + data.InsertInverseSignedLiteral(34, 6, gold.delta_dc[kPlaneV]); + data.InsertBit(41, 0); // delta_coded. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->color_config.separate_uv_delta_q = true; + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); + + gold.delta_ac[kPlaneV] = -20; + data.SetBit(41, 1); // delta_coded. + data.InsertInverseSignedLiteral(42, 6, gold.delta_ac[kPlaneV]); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->color_config.separate_uv_delta_q = true; + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); +} + +TEST_F(ObuParserTest, QuantizerParametersUseQmatrix) { + QuantizerParameters gold = {}; + gold.base_index = 48; + gold.use_matrix = true; + gold.matrix_level[kPlaneY] = 3; + gold.matrix_level[kPlaneU] = 6; + gold.matrix_level[kPlaneV] = gold.matrix_level[kPlaneU]; + + // Test three cases. + // 1. separate_uv_delta_q = false (which implies diff_uv_delta = false). + BytesAndBits data; + data.AppendLiteral(8, gold.base_index); + data.AppendLiteral(3, 0); // delta_coded. + data.AppendBit(static_cast<uint8_t>(gold.use_matrix)); + data.AppendLiteral(4, gold.matrix_level[kPlaneY]); + data.AppendLiteral(4, gold.matrix_level[kPlaneU]); + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); + + // 2. separate_uv_delta_q = true and diff_uv_delta = false. + gold.matrix_level[kPlaneV] = 5; + data.InsertBit(9, 0); // diff_uv_delta. + data.AppendLiteral(4, gold.matrix_level[kPlaneV]); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->color_config.separate_uv_delta_q = true; + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); + + // 3. separate_uv_delta_q = true and diff_uv_delta = true. + data.SetBit(9, 1); // diff_uv_delta. + data.InsertLiteral(12, 2, 0); // delta_coded. + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->color_config.separate_uv_delta_q = true; + ASSERT_TRUE(ObuParseQuantizerParameters()); + VerifyQuantizerParameters(gold); +} + +TEST_F(ObuParserTest, SegmentationParameters) { + const int kPrimaryReferenceNotNone = 1; + const int kPrevFrameIndexNotNone = 2; + + // Set up decoder_state_ with a previous frame containing saved segmentation + // parameters. + decoder_state_.reference_frame[kPrevFrameIndexNotNone] = + buffer_pool_->GetFreeBuffer(); + ASSERT_NE(decoder_state_.reference_frame[kPrevFrameIndexNotNone], nullptr); + Segmentation prev_segmentation = {}; + prev_segmentation.feature_enabled[2][0] = true; + prev_segmentation.feature_enabled[5][0] = true; + prev_segmentation.last_active_segment_id = 5; + decoder_state_.reference_frame[kPrevFrameIndexNotNone] + ->SetSegmentationParameters(prev_segmentation); + + Segmentation gold; + memset(&gold, 0, sizeof(gold)); + + BytesAndBits data; + data.AppendBit(0); // segmentation_enabled. + + // Since segmentation_enabled is false, we expect the parameters to be all + // zero/false. + ASSERT_TRUE(ParseSegmentationParameters( + data.GenerateData(), kPrimaryReferenceNotNone, kPrevFrameIndexNotNone)); + VerifySegmentationParameters(gold); + + gold.enabled = true; + gold.update_map = true; + gold.temporal_update = true; + data.SetBit(0, static_cast<uint8_t>(gold.enabled)); + data.AppendBit(static_cast<uint8_t>(gold.update_map)); + data.AppendBit(static_cast<uint8_t>(gold.temporal_update)); + data.AppendBit(static_cast<uint8_t>(gold.update_data)); + + // Since update_data is false, we expect the parameters to be loaded from the + // previous frame in |decoder_state_|. So change |gold| accordingly. + gold.feature_enabled[2][0] = true; + gold.feature_enabled[5][0] = true; + gold.last_active_segment_id = 5; + + ASSERT_TRUE(ParseSegmentationParameters( + data.GenerateData(), kPrimaryReferenceNotNone, kPrevFrameIndexNotNone)); + VerifySegmentationParameters(gold); + + OverrideSegmentation(&data, &gold, 3); + + ASSERT_TRUE(ParseSegmentationParameters( + data.GenerateData(), kPrimaryReferenceNotNone, kPrevFrameIndexNotNone)); + VerifySegmentationParameters(gold); + + // If primary_ref_frame is kPrimaryReferenceNone, these three fields are + // implied. + data.RemoveBit(1); // segmentation_update_map. + data.RemoveBit(1); // segmentation_temporal_update. + data.RemoveBit(1); // segmentation_update_data. + gold.update_map = true; + gold.temporal_update = false; + gold.update_data = true; + + // Since update_data is true, we expect the parameters to be read from + // |data|. + ASSERT_TRUE(ParseSegmentationParameters(data.GenerateData(), + kPrimaryReferenceNone, 0)); + VerifySegmentationParameters(gold); +} + +TEST_F(ObuParserTest, QuantizerIndexDeltaParameters) { + BytesAndBits data; + data.AppendBit(1); // delta_q_present. + data.AppendLiteral(2, 2); // delta_q_res. + + Delta gold; + memset(&gold, 0, sizeof(gold)); + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseQuantizerIndexDeltaParameters()); + VerifyDeltaParameters(gold, obu_->frame_header().delta_q); + + gold.present = true; + gold.scale = 2; + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->quantizer.base_index = 40; + ASSERT_TRUE(ObuParseQuantizerIndexDeltaParameters()); + VerifyDeltaParameters(gold, obu_->frame_header().delta_q); +} + +TEST_F(ObuParserTest, LoopFilterDeltaParameters) { + BytesAndBits data; + data.AppendBit(1); // delta_lf_present. + data.AppendLiteral(2, 2); // delta_lf_res. + data.AppendBit(1); // delta_lf_multi. + + Delta gold; + memset(&gold, 0, sizeof(gold)); + + // delta_q_present is false, so loop filter delta will not be read. + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseLoopFilterDeltaParameters()); + VerifyDeltaParameters(gold, obu_->frame_header().delta_lf); + + // allow_intrabc is true, so loop filter delta will not be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->delta_q.present = true; + obu_frame_header_->allow_intrabc = true; + ASSERT_TRUE(ObuParseLoopFilterDeltaParameters()); + VerifyDeltaParameters(gold, obu_->frame_header().delta_lf); + + gold.present = true; + gold.scale = 2; + gold.multi = true; + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->delta_q.present = true; + ASSERT_TRUE(ObuParseLoopFilterDeltaParameters()); + VerifyDeltaParameters(gold, obu_->frame_header().delta_lf); +} + +TEST_F(ObuParserTest, ComputeSegmentLosslessAndQIndex) { + BytesAndBits data; + data.AppendBit(0); // dummy. + + ASSERT_TRUE(Init(data.GenerateData())); + + // Segmentation is disabled. All quantizers are 0. + ObuComputeSegmentLosslessAndQIndex(); + EXPECT_TRUE(obu_->frame_header().coded_lossless); + EXPECT_TRUE(obu_->frame_header().upscaled_lossless); + for (const auto& qindex : obu_->frame_header().segmentation.qindex) { + EXPECT_EQ(qindex, 0); + } + + // Segmentation is enabled. All quantizers are zero. + obu_frame_header_->segmentation.enabled = true; + ObuComputeSegmentLosslessAndQIndex(); + EXPECT_TRUE(obu_->frame_header().coded_lossless); + EXPECT_TRUE(obu_->frame_header().upscaled_lossless); + for (const auto& qindex : obu_->frame_header().segmentation.qindex) { + EXPECT_EQ(qindex, 0); + } + + // Segmentation is enabled. All quantizers are zero. upscaled_width != width. + obu_frame_header_->segmentation.enabled = true; + obu_frame_header_->upscaled_width = 100; + ObuComputeSegmentLosslessAndQIndex(); + EXPECT_TRUE(obu_->frame_header().coded_lossless); + EXPECT_FALSE(obu_->frame_header().upscaled_lossless); + for (const auto& qindex : obu_->frame_header().segmentation.qindex) { + EXPECT_EQ(qindex, 0); + } + + // Segmentation in disabled. Some quantizer deltas are non zero. + obu_frame_header_->segmentation.enabled = false; + obu_frame_header_->quantizer.delta_dc[kPlaneY] = 40; + ObuComputeSegmentLosslessAndQIndex(); + EXPECT_FALSE(obu_->frame_header().coded_lossless); + EXPECT_FALSE(obu_->frame_header().upscaled_lossless); + for (const auto& qindex : obu_->frame_header().segmentation.qindex) { + EXPECT_EQ(qindex, 0); + } + + // Segmentation is disabled. Quantizer base index is non zero. + obu_frame_header_->segmentation.enabled = true; + obu_frame_header_->quantizer.delta_dc[kPlaneY] = 0; + obu_frame_header_->quantizer.base_index = 40; + ObuComputeSegmentLosslessAndQIndex(); + EXPECT_FALSE(obu_->frame_header().coded_lossless); + EXPECT_FALSE(obu_->frame_header().upscaled_lossless); + for (const auto& qindex : obu_->frame_header().segmentation.qindex) { + EXPECT_EQ(qindex, 40); + } +} + +TEST_F(ObuParserTest, CdefParameters) { + Cdef gold; + memset(&gold, 0, sizeof(gold)); + const int coeff_shift = 2; // bitdepth - 8. + gold.damping = 3 + coeff_shift; + + BytesAndBits data; + data.AppendBit(0); // dummy. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->color_config.bitdepth = 10; + ASSERT_TRUE(ObuParseCdefParameters()); + // Cdef will be {0} except for damping because enable_cdef is false. + VerifyCdefParameters(gold); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_cdef = true; + obu_sequence_header_->color_config.bitdepth = 10; + obu_frame_header_->coded_lossless = true; + ASSERT_TRUE(ObuParseCdefParameters()); + // Cdef will be {0} except for damping because coded_lossless is true. + VerifyCdefParameters(gold); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_cdef = true; + obu_sequence_header_->color_config.bitdepth = 10; + obu_frame_header_->allow_intrabc = true; + ASSERT_TRUE(ObuParseCdefParameters()); + // Cdef will be {0} except for damping because allow_intrabc is true. + VerifyCdefParameters(gold); + + gold.damping = 5; + gold.bits = 1; + data.Clear(); + data.AppendLiteral(2, gold.damping - 3); // cdef_damping_minus3. + gold.damping += coeff_shift; + data.AppendLiteral(2, gold.bits); // cdef_bits. + for (int i = 0; i < 2; ++i) { + gold.y_primary_strength[i] = 10; + gold.y_secondary_strength[i] = (i == 0) ? 2 : 3; + gold.uv_primary_strength[i] = 12; + gold.uv_secondary_strength[i] = (i == 1) ? 2 : 3; + data.AppendLiteral(4, gold.y_primary_strength[i]); + data.AppendLiteral(2, gold.y_secondary_strength[i]); + data.AppendLiteral(4, gold.uv_primary_strength[i]); + data.AppendLiteral(2, gold.uv_secondary_strength[i]); + if (gold.y_secondary_strength[i] == 3) ++gold.y_secondary_strength[i]; + if (gold.uv_secondary_strength[i] == 3) ++gold.uv_secondary_strength[i]; + gold.y_primary_strength[i] <<= coeff_shift; + gold.uv_primary_strength[i] <<= coeff_shift; + gold.y_secondary_strength[i] <<= coeff_shift; + gold.uv_secondary_strength[i] <<= coeff_shift; + } + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_cdef = true; + obu_sequence_header_->color_config.bitdepth = 10; + ASSERT_TRUE(ObuParseCdefParameters()); + VerifyCdefParameters(gold); +} + +TEST_F(ObuParserTest, LoopRestorationParameters) { + for (bool use_128x128_superblock : testing::Bool()) { + SCOPED_TRACE("use_128x128_superblock: " + + std::to_string(use_128x128_superblock)); + LoopRestoration gold; + memset(&gold, 0, sizeof(gold)); + + BytesAndBits data; + data.AppendBit(0); // dummy. + + // enable_restoration is false. nothing will be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->allow_intrabc = true; + obu_frame_header_->coded_lossless = true; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + + // allow_intrabc is true. nothing will be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->allow_intrabc = true; + obu_sequence_header_->enable_restoration = true; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + + // coded_lossless is true. nothing will be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->coded_lossless = true; + obu_sequence_header_->enable_restoration = true; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + + data.Clear(); + for (int i = 0; i < kMaxPlanes; ++i) { + data.AppendLiteral(2, kLoopRestorationTypeNone); // lr_type. + } + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_restoration = true; + obu_sequence_header_->use_128x128_superblock = use_128x128_superblock; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + + gold.type[0] = gold.type[1] = kLoopRestorationTypeWiener; + gold.unit_size_log2[0] = gold.unit_size_log2[1] = gold.unit_size_log2[2] = + use_128x128_superblock ? 8 : 7; + data.SetLiteral(0, 2, gold.type[0]); // lr_type. + data.SetLiteral(2, 2, gold.type[0]); // lr_type. + data.AppendBit(1); // lr_unit_shift. + if (!use_128x128_superblock) { + data.AppendBit(0); // lr_unit_extra_shift. + } + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_restoration = true; + obu_sequence_header_->use_128x128_superblock = use_128x128_superblock; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + + if (!use_128x128_superblock) { + gold.unit_size_log2[0] = gold.unit_size_log2[1] = gold.unit_size_log2[2] = + 8; + data.SetBit(7, 1); // lr_unit_extra_shift. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_restoration = true; + obu_sequence_header_->use_128x128_superblock = use_128x128_superblock; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + } + + gold.unit_size_log2[1] = gold.unit_size_log2[2] = 7; + data.AppendBit(1); // lr_uv_shift. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_sequence_header_->enable_restoration = true; + obu_sequence_header_->use_128x128_superblock = use_128x128_superblock; + obu_sequence_header_->color_config.subsampling_x = 1; + obu_sequence_header_->color_config.subsampling_y = 1; + ASSERT_TRUE(ObuParseLoopRestorationParameters()); + VerifyLoopRestorationParameters(gold); + } +} + +TEST_F(ObuParserTest, TxModeSyntax) { + BytesAndBits data; + data.AppendBit(1); // tx_mode_select. + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseTxModeSyntax()); + EXPECT_EQ(kTxModeSelect, obu_->frame_header().tx_mode); + + data.SetBit(0, 0); // tx_mode_select. + + ASSERT_TRUE(Init(data.GenerateData())); + ASSERT_TRUE(ObuParseTxModeSyntax()); + EXPECT_EQ(kTxModeLargest, obu_->frame_header().tx_mode); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->coded_lossless = true; + ASSERT_TRUE(ObuParseTxModeSyntax()); + EXPECT_EQ(kTxModeOnly4x4, obu_->frame_header().tx_mode); +} + +TEST_F(ObuParserTest, FrameReferenceModeSyntax) { + BytesAndBits data; + data.AppendBit(0); // dummy. + + ASSERT_TRUE(ParseFrameReferenceModeSyntax(data.GenerateData(), kFrameKey)); + EXPECT_FALSE(obu_->frame_header().reference_mode_select); + + data.SetBit(0, 1); // reference_mode_select. + + ASSERT_TRUE(ParseFrameReferenceModeSyntax(data.GenerateData(), kFrameInter)); + EXPECT_TRUE(obu_->frame_header().reference_mode_select); +} + +TEST_F(ObuParserTest, SkipModeParameters) { + BytesAndBits data; + data.AppendBit(1); // skip_mode_present. + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameKey; + ASSERT_FALSE(ObuIsSkipModeAllowed()); + ASSERT_TRUE(ObuParseSkipModeParameters()); + EXPECT_FALSE(obu_->frame_header().skip_mode_present); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->reference_mode_select = true; + ASSERT_FALSE(ObuIsSkipModeAllowed()); + ASSERT_TRUE(ObuParseSkipModeParameters()); + EXPECT_FALSE(obu_->frame_header().skip_mode_present); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->reference_mode_select = true; + obu_sequence_header_->enable_order_hint = true; + obu_sequence_header_->order_hint_bits = 7; + obu_sequence_header_->order_hint_shift_bits = + Mod32(32 - obu_sequence_header_->order_hint_bits); + ASSERT_FALSE(ObuIsSkipModeAllowed()); + ASSERT_TRUE(ObuParseSkipModeParameters()); + EXPECT_FALSE(obu_->frame_header().skip_mode_present); + + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->reference_mode_select = true; + obu_frame_header_->order_hint = 1; + decoder_state_.order_hint = 1; + obu_sequence_header_->enable_order_hint = true; + obu_sequence_header_->order_hint_bits = 7; + obu_sequence_header_->order_hint_shift_bits = + Mod32(32 - obu_sequence_header_->order_hint_bits); + ASSERT_FALSE(ObuIsSkipModeAllowed()); + ASSERT_TRUE(ObuParseSkipModeParameters()); + EXPECT_FALSE(obu_->frame_header().skip_mode_present); + + ASSERT_TRUE(Init(data.GenerateData())); + for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) { + obu_frame_header_->reference_frame_index[i] = i; + decoder_state_.reference_order_hint[i] = i; + } + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->reference_mode_select = true; + obu_frame_header_->order_hint = 1; + decoder_state_.order_hint = 1; + obu_sequence_header_->enable_order_hint = true; + obu_sequence_header_->order_hint_bits = 7; + obu_sequence_header_->order_hint_shift_bits = + Mod32(32 - obu_sequence_header_->order_hint_bits); + ASSERT_TRUE(ObuIsSkipModeAllowed()); + ASSERT_TRUE(ObuParseSkipModeParameters()); + EXPECT_TRUE(obu_->frame_header().skip_mode_present); +} + +TEST_F(ObuParserTest, AllowWarpedMotion) { + BytesAndBits data; + data.AppendBit(0xff); // dummy. + + // IsIntraFrame is true, so nothing will be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameKey; + obu_frame_header_->error_resilient_mode = false; + obu_sequence_header_->enable_warped_motion = true; + ASSERT_TRUE(ObuReadAllowWarpedMotion()); + EXPECT_FALSE(obu_->frame_header().allow_warped_motion); + + // error_resilient_mode is true, so nothing will be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->error_resilient_mode = true; + obu_sequence_header_->enable_warped_motion = true; + ASSERT_TRUE(ObuReadAllowWarpedMotion()); + EXPECT_FALSE(obu_->frame_header().allow_warped_motion); + + // enable_warped_motion is false, so nothing will be read. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->error_resilient_mode = false; + obu_sequence_header_->enable_warped_motion = false; + ASSERT_TRUE(ObuReadAllowWarpedMotion()); + EXPECT_FALSE(obu_->frame_header().allow_warped_motion); + + // allow_warped_motion will be read and equal to true. + ASSERT_TRUE(Init(data.GenerateData())); + obu_frame_header_->frame_type = kFrameInter; + obu_frame_header_->error_resilient_mode = false; + obu_sequence_header_->enable_warped_motion = true; + ASSERT_TRUE(ObuReadAllowWarpedMotion()); + EXPECT_TRUE(obu_->frame_header().allow_warped_motion); +} + +TEST_F(ObuParserTest, GlobalMotionParameters) { + BytesAndBits data; + data.AppendBit(0); // dummy. + std::array<GlobalMotion, kNumReferenceFrameTypes> gold; + for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) { + gold[i].type = kGlobalMotionTransformationTypeIdentity; + for (int j = 0; j < 6; ++j) { + gold[i].params[j] = (j % 3 == 2) ? 1 << kWarpedModelPrecisionBits : 0; + } + } + + ASSERT_TRUE(ParseGlobalMotionParameters(data.GenerateData(), kFrameKey)); + VerifyGlobalMotionParameters(gold); + + data.Clear(); + for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) { + // is_global=1; is_rot_zoom=1; parameter_values; + data.AppendBytes(kDefaultGlobalMotionParametersRotZoom); + + // Magic numbers based on kDefaultGlobalMotionParametersRotZoom. + gold[i].type = kGlobalMotionTransformationTypeRotZoom; + gold[i].params[0] = -73728; + gold[i].params[1] = -23552; + gold[i].params[2] = 65952; + gold[i].params[3] = -62; + gold[i].params[4] = 62; + gold[i].params[5] = 65952; + } + + ASSERT_TRUE(ParseGlobalMotionParameters(data.GenerateData(), kFrameInter)); + VerifyGlobalMotionParameters(gold); + + data.Clear(); + for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) { + // This bit is not part of the hex string because it would make the whole + // string not align to 8 bits. Appending this separately so that we can keep + // the rest of them a magic hex string. + data.AppendBit(1); // is_global. + // is_rot_zoom=0; is_translation=0; parameter_values; + data.AppendBytes(kDefaultGlobalMotionParametersAffine); + + // Magic numbers based on kDefaultGlobalMotionParametersAffine. + gold[i].type = kGlobalMotionTransformationTypeAffine; + gold[i].params[4] = -62; + } + + ASSERT_TRUE(ParseGlobalMotionParameters(data.GenerateData(), kFrameInter)); + VerifyGlobalMotionParameters(gold); +} + +TEST_F(ObuParserTest, FilmGrainParameters) { + BytesAndBits data; + data.AppendBit(0); // dummy. + + // Test film grain not present. + FilmGrainParams gold = {}; + ObuSequenceHeader sequence_header = {}; + sequence_header.film_grain_params_present = false; + ObuFrameHeader frame_header = {}; + ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header, + frame_header)); + VerifyFilmGrainParameters(gold); + + // Test if show_frame = false and showable_frame = false. + data.Clear(); + gold = {}; + sequence_header.film_grain_params_present = true; + frame_header.show_frame = false; + frame_header.showable_frame = false; + ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header, + frame_header)); + VerifyFilmGrainParameters(gold); + + // Test if apply_grain = false. + data.Clear(); + gold = {}; + sequence_header.film_grain_params_present = true; + frame_header.show_frame = true; + frame_header.showable_frame = true; + data.AppendBit(0); + ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header, + frame_header)); + VerifyFilmGrainParameters(gold); + + // Test if update_grain = false. + data.Clear(); + gold = {}; + sequence_header.film_grain_params_present = true; + frame_header.show_frame = true; + frame_header.showable_frame = true; + frame_header.frame_type = kFrameInter; + for (auto& index : frame_header.reference_frame_index) { + index = 1; + } + data.AppendBit(1); + gold.apply_grain = true; + data.AppendLiteral(16, 8); + gold.grain_seed = 8; + data.AppendBit(0); + gold.update_grain = false; + data.AppendLiteral(3, 1); + gold.reference_index = 1; + // Set up decoder_state_ with a previous frame containing saved film grain + // parameters. + decoder_state_.reference_frame[1] = buffer_pool_->GetFreeBuffer(); + EXPECT_NE(decoder_state_.reference_frame[1], nullptr); + FilmGrainParams prev_grain_params = {}; + prev_grain_params.apply_grain = true; + prev_grain_params.grain_seed = 11; + prev_grain_params.update_grain = true; + decoder_state_.reference_frame[1]->set_film_grain_params(prev_grain_params); + ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header, + frame_header)); + VerifyFilmGrainParameters(gold); + + // Test if update_grain = true, is_monochrome = true; + data.Clear(); + gold = {}; + frame_header.frame_type = kFrameKey; + for (auto& index : frame_header.reference_frame_index) { + index = 0; + } + data.AppendBit(1); + gold.apply_grain = true; + data.AppendLiteral(16, 8); + gold.grain_seed = 8; + gold.update_grain = true; + data.AppendLiteral(4, 10); + gold.num_y_points = 10; + for (int i = 0; i < gold.num_y_points; ++i) { + data.AppendLiteral(8, 2 * i); + gold.point_y_value[i] = 2 * i; + data.AppendLiteral(8, i); + gold.point_y_scaling[i] = i; + } + sequence_header.color_config.is_monochrome = true; + gold.chroma_scaling_from_luma = false; + gold.num_u_points = 0; + gold.num_v_points = 0; + data.AppendLiteral(2, 3); + gold.chroma_scaling = 11; + data.AppendLiteral(2, 1); + gold.auto_regression_coeff_lag = 1; + const int num_pos_luma = + 2 * gold.auto_regression_coeff_lag * (gold.auto_regression_coeff_lag + 1); + for (int i = 0; i < num_pos_luma; ++i) { + data.AppendLiteral(8, i + 128); + gold.auto_regression_coeff_y[i] = i; + } + data.AppendLiteral(2, 0); + gold.auto_regression_shift = 6; + data.AppendLiteral(2, 1); + gold.grain_scale_shift = 1; + data.AppendBit(1); + gold.overlap_flag = true; + data.AppendBit(0); + gold.clip_to_restricted_range = false; + ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header, + frame_header)); + ASSERT_TRUE( + obu_->frame_header().frame_type == kFrameInter || + obu_->frame_header().film_grain_params.update_grain); // a implies b. + VerifyFilmGrainParameters(gold); + + // Test if update_grain = true, is_monochrome = false; + data.Clear(); + gold = {}; + frame_header.frame_type = kFrameKey; + data.AppendBit(1); + gold.apply_grain = true; + data.AppendLiteral(16, 8); + gold.grain_seed = 8; + gold.update_grain = true; + data.AppendLiteral(4, 10); + gold.num_y_points = 10; + for (int i = 0; i < gold.num_y_points; ++i) { + data.AppendLiteral(8, 2 * i); + gold.point_y_value[i] = 2 * i; + data.AppendLiteral(8, i); + gold.point_y_scaling[i] = i; + } + sequence_header.color_config.is_monochrome = false; + data.AppendBit(0); + gold.chroma_scaling_from_luma = false; + data.AppendLiteral(4, 5); + gold.num_u_points = 5; + for (int i = 0; i < gold.num_u_points; ++i) { + data.AppendLiteral(8, 2 * i + 1); + gold.point_u_value[i] = 2 * i + 1; + data.AppendLiteral(8, i); + gold.point_u_scaling[i] = i; + } + data.AppendLiteral(4, 3); + gold.num_v_points = 3; + for (int i = 0; i < gold.num_v_points; ++i) { + data.AppendLiteral(8, i); + gold.point_v_value[i] = i; + data.AppendLiteral(8, i + 1); + gold.point_v_scaling[i] = i + 1; + } + data.AppendLiteral(2, 3); + gold.chroma_scaling = 11; + data.AppendLiteral(2, 1); + gold.auto_regression_coeff_lag = 1; + const int num_pos_luma2 = + 2 * gold.auto_regression_coeff_lag * (gold.auto_regression_coeff_lag + 1); + for (int i = 0; i < num_pos_luma2; ++i) { + data.AppendLiteral(8, i + 128); + gold.auto_regression_coeff_y[i] = i; + } + for (int i = 0; i < num_pos_luma2 + 1; ++i) { + data.AppendLiteral(8, i); + gold.auto_regression_coeff_u[i] = i - 128; + } + for (int i = 0; i < num_pos_luma2 + 1; ++i) { + data.AppendLiteral(8, i); + gold.auto_regression_coeff_v[i] = i - 128; + } + data.AppendLiteral(2, 0); + gold.auto_regression_shift = 6; + data.AppendLiteral(2, 1); + gold.grain_scale_shift = 1; + data.AppendLiteral(8, 2); + gold.u_multiplier = -126; + data.AppendLiteral(8, 1); + gold.u_luma_multiplier = -127; + data.AppendLiteral(9, 3); + gold.u_offset = -253; + data.AppendLiteral(8, 3); + gold.v_multiplier = -125; + data.AppendLiteral(8, 2); + gold.v_luma_multiplier = -126; + data.AppendLiteral(9, 1); + gold.v_offset = -255; + data.AppendBit(1); + gold.overlap_flag = true; + data.AppendBit(0); + gold.clip_to_restricted_range = false; + ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header, + frame_header)); + ASSERT_TRUE( + obu_->frame_header().frame_type == kFrameInter || + obu_->frame_header().film_grain_params.update_grain); // a implies b. + VerifyFilmGrainParameters(gold); +} + +TEST_F(ObuParserTest, TileInfoSyntax) { + BytesAndBits data; + TileInfo gold; + memset(&gold, 0, sizeof(gold)); + + gold.uniform_spacing = true; + gold.tile_columns_log2 = 1; + gold.tile_columns = 2; + gold.tile_rows_log2 = 1; + gold.tile_rows = 2; + gold.tile_count = 4; + gold.tile_column_start[1] = 64; + gold.tile_column_start[2] = 88; + gold.tile_row_start[1] = 64; + gold.tile_row_start[2] = 72; + gold.context_update_id = 3; + gold.tile_size_bytes = 4; + data.AppendBit(static_cast<uint8_t>(gold.uniform_spacing)); + data.AppendBit(1); // increment_tile_cols_log2. + data.AppendBit(0); // increment_tile_cols_log2. + data.AppendBit(1); // increment_tile_rows_log2. + data.AppendBit(0); // increment_tile_rows_log2. + data.AppendBit(1); // context update id, columns_log2+rows_log2 bits + data.AppendBit(1); + data.AppendLiteral(2, gold.tile_size_bytes - 1); + + ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 88, 72, true)); + VerifyTileInfoParameters(gold); + + gold.uniform_spacing = false; + gold.tile_column_width_in_superblocks[0] = 2; + gold.tile_column_width_in_superblocks[1] = 1; + gold.tile_row_height_in_superblocks[0] = 2; + gold.tile_row_height_in_superblocks[1] = 1; + + data.SetBit(0, static_cast<uint8_t>(gold.uniform_spacing)); + // The next 4 bits remain the same except now they represent f(w - 1) and + // extra_bit in DecodeUniform. All the subsequent bits are unchanged the + // represent the same thing as above. + + ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 88, 72, true)); + VerifyTileInfoParameters(gold); + + // No tiles. + memset(&gold, 0, sizeof(gold)); + gold.uniform_spacing = true; + gold.tile_columns = 1; + gold.tile_rows = 1; + gold.tile_count = 1; + gold.tile_column_start[1] = 88; + gold.tile_row_start[1] = 72; + data.Clear(); + data.AppendBit(static_cast<uint8_t>(gold.uniform_spacing)); + data.AppendBit(0); // tile_cols_log2. + data.AppendBit(0); // tile_rows_log2. + + ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 88, 72, true)); + VerifyTileInfoParameters(gold); + + // 64x64 superblocks. No tiles. + gold.tile_column_start[1] = 640; + gold.tile_row_start[1] = 360; + + ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 640, 360, false)); + VerifyTileInfoParameters(gold); +} + +TEST_F(ObuParserTest, MetadataUnknownType) { + BytesAndBits data; + // The metadata_type 10 is a user private value (6-31). + data.AppendLiteral(8, 10); // metadata_type. + // The Note in Section 5.8.1 says "Decoders should ignore the entire OBU if + // they do not understand the metadata_type." + ASSERT_TRUE(ParseMetadata(data.GenerateData())); +} + +TEST_F(ObuParserTest, MetadataCll) { + BytesAndBits data; + ObuMetadata gold; + gold.max_cll = 25; + gold.max_fall = 100; + + data.AppendLiteral(8, kMetadataTypeHdrContentLightLevel); + data.AppendLiteral(16, gold.max_cll); + data.AppendLiteral(16, gold.max_fall); + + ASSERT_TRUE(ParseMetadata(data.GenerateData())); + VerifyMetadata(kMetadataTypeHdrContentLightLevel, gold); +} + +TEST_F(ObuParserTest, MetadataMdcv) { + BytesAndBits data; + ObuMetadata gold; + for (int i = 0; i < 3; ++i) { + gold.primary_chromaticity_x[i] = 0; + gold.primary_chromaticity_y[i] = 0; + } + gold.white_point_chromaticity_x = 250; + gold.white_point_chromaticity_y = 2500; + gold.luminance_max = 6000; + gold.luminance_min = 3000; + + data.AppendLiteral(8, kMetadataTypeHdrMasteringDisplayColorVolume); + for (int i = 0; i < 3; ++i) { + data.AppendLiteral(16, gold.primary_chromaticity_x[i]); + data.AppendLiteral(16, gold.primary_chromaticity_y[i]); + } + data.AppendLiteral(16, gold.white_point_chromaticity_x); + data.AppendLiteral(16, gold.white_point_chromaticity_y); + data.AppendLiteral(32, gold.luminance_max); + data.AppendLiteral(32, gold.luminance_min); + + ASSERT_TRUE(ParseMetadata(data.GenerateData())); + VerifyMetadata(kMetadataTypeHdrMasteringDisplayColorVolume, gold); +} + +TEST_F(ObuParserTest, MetadataScalability) { + BytesAndBits data; + ObuMetadata gold; + + data.AppendLiteral(8, kMetadataTypeScalability); + data.AppendLiteral(8, 0); // scalability_mode_idc + + ASSERT_TRUE(ParseMetadata(data.GenerateData())); + VerifyMetadata(kMetadataTypeScalability, gold); +} + +TEST_F(ObuParserTest, MetadataItutT35) { + BytesAndBits data; + ObuMetadata gold; + gold.itu_t_t35_country_code = 0xA6; // 1 0 1 0 0 1 1 0 Switzerland + gold.itu_t_t35_country_code_extension_byte = 0; + gold.itu_t_t35_payload_bytes.reset(new (std::nothrow) uint8_t[10]); + ASSERT_NE(gold.itu_t_t35_payload_bytes, nullptr); + for (int i = 0; i < 10; ++i) { + gold.itu_t_t35_payload_bytes[i] = 9 - i; + } + gold.itu_t_t35_payload_size = 10; + + data.AppendLiteral(8, kMetadataTypeItutT35); + data.AppendLiteral(8, gold.itu_t_t35_country_code); + for (int i = 0; i < 10; ++i) { + data.AppendLiteral(8, 9 - i); + } + // For the kMetadataTypeItutT35 metadata type, we must include the trailing + // bit so that the end of the itu_t_t35_payload_bytes can be identified. + data.AppendLiteral(8, 0x80); + data.AppendLiteral(8, 0x00); + data.AppendLiteral(8, 0x00); + + ASSERT_TRUE(ParseMetadata(data.GenerateData())); + VerifyMetadata(kMetadataTypeItutT35, gold); +} + +TEST_F(ObuParserTest, MetadataTimecode) { + BytesAndBits data; + ObuMetadata gold; + + data.AppendLiteral(8, kMetadataTypeTimecode); + data.AppendLiteral(5, 0); // counting_type + data.AppendBit(1); // full_timestamp_flag + data.AppendBit(0); // discontinuity_flag + data.AppendBit(0); // cnt_dropped_flag + data.AppendLiteral(9, 8); // n_frames + data.AppendLiteral(6, 59); // seconds_value + data.AppendLiteral(6, 59); // minutes_value + data.AppendLiteral(5, 23); // hours_value + data.AppendLiteral(5, 0); // time_offset_length + + ASSERT_TRUE(ParseMetadata(data.GenerateData())); + VerifyMetadata(kMetadataTypeTimecode, gold); +} + +TEST_F(ObuParserTest, MetadataTimecodeInvalidSecondsValue) { + BytesAndBits data; + ObuMetadata gold; + + data.AppendLiteral(8, kMetadataTypeTimecode); + data.AppendLiteral(5, 0); // counting_type + data.AppendBit(1); // full_timestamp_flag + data.AppendBit(0); // discontinuity_flag + data.AppendBit(0); // cnt_dropped_flag + data.AppendLiteral(9, 8); // n_frames + data.AppendLiteral(6, 60); // seconds_value + data.AppendLiteral(6, 59); // minutes_value + data.AppendLiteral(5, 23); // hours_value + data.AppendLiteral(5, 0); // time_offset_length + + EXPECT_FALSE(ParseMetadata(data.GenerateData())); +} + +TEST_F(ObuParserTest, MetadataTimecodeInvalidMinutesValue) { + BytesAndBits data; + ObuMetadata gold; + + data.AppendLiteral(8, kMetadataTypeTimecode); + data.AppendLiteral(5, 0); // counting_type + data.AppendBit(1); // full_timestamp_flag + data.AppendBit(0); // discontinuity_flag + data.AppendBit(0); // cnt_dropped_flag + data.AppendLiteral(9, 8); // n_frames + data.AppendLiteral(6, 59); // seconds_value + data.AppendLiteral(6, 60); // minutes_value + data.AppendLiteral(5, 23); // hours_value + data.AppendLiteral(5, 0); // time_offset_length + + EXPECT_FALSE(ParseMetadata(data.GenerateData())); +} + +TEST_F(ObuParserTest, MetadataTimecodeInvalidHoursValue) { + BytesAndBits data; + ObuMetadata gold; + + data.AppendLiteral(8, kMetadataTypeTimecode); + data.AppendLiteral(5, 0); // counting_type + data.AppendBit(1); // full_timestamp_flag + data.AppendBit(0); // discontinuity_flag + data.AppendBit(0); // cnt_dropped_flag + data.AppendLiteral(9, 8); // n_frames + data.AppendLiteral(6, 59); // seconds_value + data.AppendLiteral(6, 59); // minutes_value + data.AppendLiteral(5, 24); // hours_value + data.AppendLiteral(5, 0); // time_offset_length + + EXPECT_FALSE(ParseMetadata(data.GenerateData())); +} + +} // namespace libgav1 |