aboutsummaryrefslogtreecommitdiff
path: root/src/obu_parser_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/obu_parser_test.cc')
-rw-r--r--src/obu_parser_test.cc2675
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(&current_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(&current_frame_), kStatusInvalidArgument);
+ obu_.reset(new (std::nothrow) ObuParser(nullptr, 10, 0, buffer_pool_.get(),
+ &decoder_state_));
+ EXPECT_EQ(obu_->ParseOneFrame(&current_frame_), kStatusInvalidArgument);
+ obu_.reset(new (std::nothrow)
+ ObuParser(kDefaultTemporalDelimiter.data(), 0, 0,
+ buffer_pool_.get(), &decoder_state_));
+ EXPECT_EQ(obu_->ParseOneFrame(&current_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