From bbaaf2da8d8b7de0d70388f2e251472eead3d431 Mon Sep 17 00:00:00 2001 From: Frank Galligan Date: Fri, 15 Apr 2016 18:15:09 -0700 Subject: [PATCH] Add class to gather VP9 level stats. See http://www.webmproject.org/vp9/profiles/ for more information on VP9 levels. BUG=https://bugs.chromium.org/p/webm/issues/detail?id=1188 Change-Id: I91448069bbd4740106a159014db6935365af75ca --- CMakeLists.txt | 10 ++ common/vp9_level_stats.cc | 248 ++++++++++++++++++++++++++++++++ common/vp9_level_stats.h | 184 ++++++++++++++++++++++++ common/vp9_level_stats_tests.cc | 185 ++++++++++++++++++++++++ 4 files changed, 627 insertions(+) create mode 100644 common/vp9_level_stats.cc create mode 100644 common/vp9_level_stats.h create mode 100644 common/vp9_level_stats_tests.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index d5d5c33..f31e0ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,16 @@ if (ENABLE_TESTS) "${LIBWEBM_SRC_DIR}/testing/test_util.h") target_link_libraries(vp9_header_parser_tests LINK_PUBLIC gtest webm) + add_executable(vp9_level_stats_tests + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_header_parser.h" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats_tests.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.cc" + "${LIBWEBM_SRC_DIR}/common/vp9_level_stats.h" + "${LIBWEBM_SRC_DIR}/testing/test_util.cc" + "${LIBWEBM_SRC_DIR}/testing/test_util.h") + target_link_libraries(vp9_level_stats_tests LINK_PUBLIC gtest webm) + if (ENABLE_WEBMTS) add_executable(webm2pes_tests "${LIBWEBM_SRC_DIR}/testing/test_util.cc" diff --git a/common/vp9_level_stats.cc b/common/vp9_level_stats.cc new file mode 100644 index 0000000..e331a71 --- /dev/null +++ b/common/vp9_level_stats.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/vp9_level_stats.h" + +#include + +#include +#include + +#include "common/webm_constants.h" + +namespace vp9_parser { + +const Vp9LevelRow Vp9LevelStats::Vp9LevelTable[kNumVp9Levels] = { + {LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8}, + {LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8}, + {LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8}, + {LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8}, + {LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8}, + {LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8}, + {LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8}, + {LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6}, + {LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4}, + {LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4}, + {LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4}, // CPB Size = 0 + {LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4}, // CPB Size = 0 + {LEVEL_6_1, 2353004544, 35651584, 240000, 0, 8, 16, 10, 4}, // CPB Size = 0 + {LEVEL_6_2, 4706009088, 35651584, 480000, 0, 8, 16, 10, 4} // CPB Size = 0 +}; + +void Vp9LevelStats::AddFrame(const Vp9HeaderParser& parser, int64_t time_ns) { + ++frames; + if (start_ns_ == -1) + start_ns_ = time_ns; + end_ns_ = time_ns; + + const int width = parser.width(); + const int height = parser.height(); + const int64_t luma_picture_size = width * height; + if (luma_picture_size > max_luma_picture_size_) + max_luma_picture_size_ = luma_picture_size; + + total_compressed_size_ += parser.frame_size(); + + // TODO(fgalligan): Add support for other color formats. Currently assuming + // 420. + total_uncompressed_bits_ += (luma_picture_size * parser.bit_depth() * 3) / 2; + + while (!luma_window_.empty() && + luma_window_.front().first < + (time_ns - (libwebm::kNanosecondsPerSecondi - 1))) { + current_luma_size_ -= luma_window_.front().second; + luma_window_.pop(); + } + current_luma_size_ += luma_picture_size; + luma_window_.push(std::make_pair(time_ns, luma_picture_size)); + if (current_luma_size_ > max_luma_size_) { + max_luma_size_ = current_luma_size_; + max_luma_end_ns_ = luma_window_.back().first; + } + + // Max luma sample rate does not take frame resizing into account. So + // I'm doing max number of frames in one second times max width times max + // height to generate Max luma sample rate. + if (luma_window_.size() > max_frames_in_one_second_) + max_frames_in_one_second_ = luma_window_.size(); + + // Record CPB stats. + // Remove all frames that are less than window size. + while (cpb_window_.size() > 3) { + current_cpb_size_ -= cpb_window_.front().second; + cpb_window_.pop(); + } + cpb_window_.push(std::make_pair(time_ns, parser.frame_size())); + + current_cpb_size_ += parser.frame_size(); + if (current_cpb_size_ > max_cpb_size_) { + max_cpb_size_ = current_cpb_size_; + max_cpb_start_ns_ = cpb_window_.front().first; + max_cpb_end_ns_ = cpb_window_.back().first; + } + + if (max_cpb_window_size_ < static_cast(cpb_window_.size())) { + max_cpb_window_size_ = cpb_window_.size(); + max_cpb_window_end_ns_ = time_ns; + } + + // Record altref stats. + if (parser.altref()) { + const int delta_altref = frames_since_last_altref; + if (first_altref) { + first_altref = false; + } else if (delta_altref < minimum_altref_distance) { + minimum_altref_distance = delta_altref; + min_altref_end_ns = time_ns; + } + frames_since_last_altref = 0; + } else { + ++frames_since_last_altref; + ++displayed_frames; + } + + // Count max reference frames. + if (parser.key() == 1) { + frames_refreshed_ = 0; + } else { + frames_refreshed_ |= parser.refresh_frame_flags(); + + int ref_frame_count = frames_refreshed_ & 1; + for (int i = 1; i < kMaxVp9RefFrames; ++i) { + ref_frame_count += (frames_refreshed_ >> i) & 1; + } + + if (ref_frame_count > max_frames_refreshed_) + max_frames_refreshed_ = ref_frame_count; + } + + // Count max tiles. + const int tiles = parser.column_tiles(); + if (tiles > max_column_tiles_) + max_column_tiles_ = tiles; +} + +Vp9Level Vp9LevelStats::GetLevel() const { + const int64_t max_luma_sample_rate = GetMaxLumaSampleRate(); + const int64_t max_luma_picture_size = GetMaxLumaPictureSize(); + const double average_bitrate = GetAverageBitRate(); + const double max_cpb_size = GetMaxCpbSize(); + const double compresion_ratio = GetCompressionRatio(); + const int max_column_tiles = GetMaxColumnTiles(); + const int min_altref_distance = GetMinimumAltrefDistance(); + const int max_ref_frames = GetMaxReferenceFrames(); + + int level_index = 0; + Vp9Level max_level = LEVEL_UNKNOWN; + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_luma_sample_rate <= Vp9LevelTable[i].max_luma_sample_rate) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_luma_picture_size <= Vp9LevelTable[i].max_luma_picture_size) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (average_bitrate <= Vp9LevelTable[i].average_bitrate) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + // Only check CPB size for levels that are defined. + if (Vp9LevelTable[i].max_cpb_size > 0 && + max_cpb_size <= Vp9LevelTable[i].max_cpb_size) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_column_tiles <= Vp9LevelTable[i].max_tiles) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + + for (int i = 0; i < kNumVp9Levels; ++i) { + if (max_ref_frames <= Vp9LevelTable[i].max_ref_frames) { + if (max_level < Vp9LevelTable[i].level) { + max_level = Vp9LevelTable[i].level; + level_index = i; + } + break; + } + } + + // Check if the current level meets the minimum altref distance requirement. + // If not, set to unknown level as we can't move up a level as the minimum + // altref distance get farther apart and we can't move down a level as we are + // already at the minimum level for all the other requirements. + if (min_altref_distance < Vp9LevelTable[level_index].min_altref_distance) + max_level = LEVEL_UNKNOWN; + + // The minimum compression ratio has the same behavior as minimum altref + // distance. + if (compresion_ratio < Vp9LevelTable[level_index].compresion_ratio) + max_level = LEVEL_UNKNOWN; + return max_level; +} + +int64_t Vp9LevelStats::GetMaxLumaSampleRate() const { + return max_luma_picture_size_ * max_frames_in_one_second_; +} + +int64_t Vp9LevelStats::GetMaxLumaPictureSize() const { + return max_luma_picture_size_; +} + +double Vp9LevelStats::GetAverageBitRate() const { + const double duration_seconds = + ((duration_ns_ == -1) ? end_ns_ - start_ns_ : duration_ns_) / + libwebm::kNanosecondsPerSecond; + return total_compressed_size_ / duration_seconds / 125.0; +} + +double Vp9LevelStats::GetMaxCpbSize() const { return max_cpb_size_ / 125.0; } + +double Vp9LevelStats::GetCompressionRatio() const { + return total_uncompressed_bits_ / + static_cast(total_compressed_size_ * 8); +} + +int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; } + +int Vp9LevelStats::GetMinimumAltrefDistance() const { + if (minimum_altref_distance != std::numeric_limits::max()) + return minimum_altref_distance; + else + return -1; +} + +int Vp9LevelStats::GetMaxReferenceFrames() const { + return max_frames_refreshed_; +} + +} // namespace vp9_parser diff --git a/common/vp9_level_stats.h b/common/vp9_level_stats.h new file mode 100644 index 0000000..2f843a6 --- /dev/null +++ b/common/vp9_level_stats.h @@ -0,0 +1,184 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#ifndef LIBWEBM_COMMON_VP9_LEVEL_STATS_H_ +#define LIBWEBM_COMMON_VP9_LEVEL_STATS_H_ + +#include +#include +#include + +#include "common/vp9_header_parser.h" + +namespace vp9_parser { + +const int kMaxVp9RefFrames = 8; + +// Defined VP9 levels. See http://www.webmproject.org/vp9/profiles/ for +// detailed information on VP9 levels. +const int kNumVp9Levels = 14; +enum Vp9Level { + LEVEL_UNKNOWN = 0, + LEVEL_1 = 10, + LEVEL_1_1 = 11, + LEVEL_2 = 20, + LEVEL_2_1 = 21, + LEVEL_3 = 30, + LEVEL_3_1 = 31, + LEVEL_4 = 40, + LEVEL_4_1 = 41, + LEVEL_5 = 50, + LEVEL_5_1 = 51, + LEVEL_5_2 = 52, + LEVEL_6 = 60, + LEVEL_6_1 = 61, + LEVEL_6_2 = 62 +}; + +struct Vp9LevelRow { + Vp9LevelRow() = default; + ~Vp9LevelRow() = default; + Vp9LevelRow(Vp9LevelRow&& other) = default; + Vp9LevelRow(const Vp9LevelRow& other) = default; + Vp9LevelRow& operator=(Vp9LevelRow&& other) = delete; + Vp9LevelRow& operator=(const Vp9LevelRow& other) = delete; + + Vp9Level level; + int64_t max_luma_sample_rate; + int64_t max_luma_picture_size; + double average_bitrate; + double max_cpb_size; + double compresion_ratio; + int max_tiles; + int min_altref_distance; + int max_ref_frames; +}; + +// Class to determine the VP9 level of a VP9 bitstream. +class Vp9LevelStats { + public: + static const Vp9LevelRow Vp9LevelTable[kNumVp9Levels]; + + Vp9LevelStats() + : frames(0), + displayed_frames(0), + start_ns_(-1), + end_ns_(-1), + duration_ns_(-1), + max_luma_picture_size_(0), + current_luma_size_(0), + max_luma_size_(0), + max_luma_end_ns_(0), + max_frames_in_one_second_(0), + first_altref(true), + frames_since_last_altref(0), + minimum_altref_distance(std::numeric_limits::max()), + min_altref_end_ns(0), + max_cpb_window_size_(0), + max_cpb_window_end_ns_(0), + current_cpb_size_(0), + max_cpb_size_(0), + max_cpb_start_ns_(0), + max_cpb_end_ns_(0), + total_compressed_size_(0), + total_uncompressed_bits_(0), + frames_refreshed_(0), + max_frames_refreshed_(0), + max_column_tiles_(0) {} + + ~Vp9LevelStats() = default; + Vp9LevelStats(Vp9LevelStats&& other) = delete; + Vp9LevelStats(const Vp9LevelStats& other) = delete; + Vp9LevelStats& operator=(Vp9LevelStats&& other) = delete; + Vp9LevelStats& operator=(const Vp9LevelStats& other) = delete; + + // Collects stats on a VP9 frame. The frame must already be parsed by + // |parser|. |time_ns| is the start time of the frame in nanoseconds. + void AddFrame(const Vp9HeaderParser& parser, int64_t time_ns); + + // Returns the current VP9 level. All of the video frames should have been + // processed with AddFrame before calling this function. + Vp9Level GetLevel() const; + + // Returns the maximum luma samples (pixels) per second. The Alt-Ref frames + // are taken into account, therefore this number may be larger than the + // display luma samples per second + int64_t GetMaxLumaSampleRate() const; + + // The maximum frame size (width * height) in samples. + int64_t GetMaxLumaPictureSize() const; + + // The average bitrate of the video in kbps. + double GetAverageBitRate() const; + + // The largest data size for any 4 consecutive frames in kilobits. + double GetMaxCpbSize() const; + + // The ratio of total bytes decompressed over total bytes compressed. + double GetCompressionRatio() const; + + // The maximum number of VP9 column tiles. + int GetMaxColumnTiles() const; + + // The minimum distance in frames between two consecutive alternate reference + // frames. + int GetMinimumAltrefDistance() const; + + // The maximum number of reference frames that had to be stored. + int GetMaxReferenceFrames() const; + + // Sets the duration of the video stream in nanoseconds. If the duration is + // not explictly set by this function then this class will use end - start + // as the duration. + void set_duration(int64_t time_ns) { duration_ns_ = time_ns; } + + private: + int frames; + int displayed_frames; + + int64_t start_ns_; + int64_t end_ns_; + int64_t duration_ns_; + + int64_t max_luma_picture_size_; + + // This is used to calculate the maximum number of luma samples per second. + // The first value is the luma picture size and the second value is the time + // in nanoseconds of one frame. + std::queue> luma_window_; + int64_t current_luma_size_; + int64_t max_luma_size_; + int64_t max_luma_end_ns_; + size_t max_frames_in_one_second_; + + bool first_altref; + int frames_since_last_altref; + int minimum_altref_distance; + int64_t min_altref_end_ns; + + // This is used to calculate the maximum number of compressed bytes for four + // consecutive frames. The first value is the compressed frame size and the + // second value is the time in nanoseconds of one frame. + std::queue> cpb_window_; + int64_t max_cpb_window_size_; + int64_t max_cpb_window_end_ns_; + int64_t current_cpb_size_; + int64_t max_cpb_size_; + int64_t max_cpb_start_ns_; + int64_t max_cpb_end_ns_; + + int64_t total_compressed_size_; + int64_t total_uncompressed_bits_; + int frames_refreshed_; + int max_frames_refreshed_; + + int max_column_tiles_; +}; + +} // namespace vp9_parser + +#endif // LIBWEBM_COMMON_VP9_LEVEL_STATS_H_ diff --git a/common/vp9_level_stats_tests.cc b/common/vp9_level_stats_tests.cc new file mode 100644 index 0000000..9f916e2 --- /dev/null +++ b/common/vp9_level_stats_tests.cc @@ -0,0 +1,185 @@ +// Copyright (c) 2016 The WebM project authors. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +#include "common/vp9_level_stats.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "common/hdr_util.h" +#include "common/vp9_header_parser.h" +#include "mkvparser/mkvparser.h" +#include "mkvparser/mkvreader.h" +#include "testing/test_util.h" + +namespace { + +// TODO(fgalligan): Refactor this test with other test files in this directory. +class Vp9LevelStatsTests : public ::testing::Test { + public: + Vp9LevelStatsTests() : is_reader_open_(false) {} + + ~Vp9LevelStatsTests() override { CloseReader(); } + + void CloseReader() { + if (is_reader_open_) { + reader_.Close(); + } + is_reader_open_ = false; + } + + void CreateAndLoadSegment(const std::string& filename, + int expected_doc_type_ver) { + ASSERT_NE(0u, filename.length()); + filename_ = test::GetTestFilePath(filename); + ASSERT_EQ(0, reader_.Open(filename_.c_str())); + is_reader_open_ = true; + pos_ = 0; + mkvparser::EBMLHeader ebml_header; + ebml_header.Parse(&reader_, pos_); + ASSERT_EQ(1, ebml_header.m_version); + ASSERT_EQ(1, ebml_header.m_readVersion); + ASSERT_STREQ("webm", ebml_header.m_docType); + ASSERT_EQ(expected_doc_type_ver, ebml_header.m_docTypeVersion); + ASSERT_EQ(2, ebml_header.m_docTypeReadVersion); + mkvparser::Segment* temp; + ASSERT_EQ(0, mkvparser::Segment::CreateInstance(&reader_, pos_, temp)); + segment_.reset(temp); + ASSERT_FALSE(HasFailure()); + ASSERT_GE(0, segment_->Load()); + } + + void CreateAndLoadSegment(const std::string& filename) { + CreateAndLoadSegment(filename, 2); + } + + void ProcessTheFrames() { + std::vector data; + size_t data_len = 0; + const mkvparser::Tracks* const parser_tracks = segment_->GetTracks(); + ASSERT_TRUE(parser_tracks != NULL); + const mkvparser::Cluster* cluster = segment_->GetFirst(); + ASSERT_TRUE(cluster); + + while ((cluster != NULL) && !cluster->EOS()) { + const mkvparser::BlockEntry* block_entry; + long status = cluster->GetFirst(block_entry); // NOLINT + ASSERT_EQ(0, status); + + while ((block_entry != NULL) && !block_entry->EOS()) { + const mkvparser::Block* const block = block_entry->GetBlock(); + ASSERT_TRUE(block != NULL); + const long long trackNum = block->GetTrackNumber(); // NOLINT + const mkvparser::Track* const parser_track = + parser_tracks->GetTrackByNumber( + static_cast(trackNum)); // NOLINT + ASSERT_TRUE(parser_track != NULL); + const long long track_type = parser_track->GetType(); // NOLINT + + if (track_type == mkvparser::Track::kVideo) { + const int frame_count = block->GetFrameCount(); + const long long time_ns = block->GetTime(cluster); // NOLINT + + for (int i = 0; i < frame_count; ++i) { + const mkvparser::Block::Frame& frame = block->GetFrame(i); + if (static_cast(frame.len) > data.size()) { + data.resize(frame.len); + data_len = static_cast(frame.len); + } + ASSERT_FALSE(frame.Read(&reader_, &data[0])); + parser_.SetFrame(&data[0], data_len); + parser_.ParseUncompressedHeader(); + stats_.AddFrame(parser_, time_ns); + } + } + + status = cluster->GetNext(block_entry, block_entry); + ASSERT_EQ(0, status); + } + + cluster = segment_->GetNext(cluster); + } + } + + protected: + mkvparser::MkvReader reader_; + bool is_reader_open_; + std::unique_ptr segment_; + std::string filename_; + long long pos_; // NOLINT + vp9_parser::Vp9HeaderParser parser_; + vp9_parser::Vp9LevelStats stats_; +}; + +TEST_F(Vp9LevelStatsTests, VideoOnlyFile) { + CreateAndLoadSegment("test_stereo_left_right.webm"); + ProcessTheFrames(); + EXPECT_EQ(256, parser_.width()); + EXPECT_EQ(144, parser_.height()); + EXPECT_EQ(1, parser_.column_tiles()); + EXPECT_EQ(0, parser_.frame_parallel_mode()); + + EXPECT_EQ(11, stats_.GetLevel()); + EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate()); + EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize()); + EXPECT_DOUBLE_EQ(275.512, stats_.GetAverageBitRate()); + EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize()); + EXPECT_DOUBLE_EQ(20.873079938441883, stats_.GetCompressionRatio()); + EXPECT_EQ(1, stats_.GetMaxColumnTiles()); + EXPECT_EQ(11, stats_.GetMinimumAltrefDistance()); + EXPECT_EQ(3, stats_.GetMaxReferenceFrames()); +} + +TEST_F(Vp9LevelStatsTests, Muxed) { + CreateAndLoadSegment("bbb_480p_vp9_opus_1second.webm", 4); + ProcessTheFrames(); + EXPECT_EQ(854, parser_.width()); + EXPECT_EQ(480, parser_.height()); + EXPECT_EQ(2, parser_.column_tiles()); + EXPECT_EQ(1, parser_.frame_parallel_mode()); + + EXPECT_EQ(30, stats_.GetLevel()); + EXPECT_EQ(9838080, stats_.GetMaxLumaSampleRate()); + EXPECT_EQ(409920, stats_.GetMaxLumaPictureSize()); + EXPECT_DOUBLE_EQ(468.38413361169108, stats_.GetAverageBitRate()); + EXPECT_DOUBLE_EQ(118.464, stats_.GetMaxCpbSize()); + EXPECT_DOUBLE_EQ(263.10185597889068, stats_.GetCompressionRatio()); + EXPECT_EQ(2, stats_.GetMaxColumnTiles()); + EXPECT_EQ(9, stats_.GetMinimumAltrefDistance()); + EXPECT_EQ(3, stats_.GetMaxReferenceFrames()); +} + +TEST_F(Vp9LevelStatsTests, SetDuration) { + CreateAndLoadSegment("test_stereo_left_right.webm"); + ProcessTheFrames(); + const int64_t kDurationNano = 2080000000; // 2.08 seconds + stats_.set_duration(kDurationNano); + EXPECT_EQ(256, parser_.width()); + EXPECT_EQ(144, parser_.height()); + EXPECT_EQ(1, parser_.column_tiles()); + EXPECT_EQ(0, parser_.frame_parallel_mode()); + + EXPECT_EQ(11, stats_.GetLevel()); + EXPECT_EQ(479232, stats_.GetMaxLumaSampleRate()); + EXPECT_EQ(36864, stats_.GetMaxLumaPictureSize()); + EXPECT_DOUBLE_EQ(264.9153846153846, stats_.GetAverageBitRate()); + EXPECT_DOUBLE_EQ(147.136, stats_.GetMaxCpbSize()); + EXPECT_DOUBLE_EQ(20.873079938441883, stats_.GetCompressionRatio()); + EXPECT_EQ(1, stats_.GetMaxColumnTiles()); + EXPECT_EQ(11, stats_.GetMinimumAltrefDistance()); + EXPECT_EQ(3, stats_.GetMaxReferenceFrames()); +} + +} // namespace + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}