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
This commit is contained in:
parent
8bb68c2b3e
commit
bbaaf2da8d
@ -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"
|
||||
|
248
common/vp9_level_stats.cc
Normal file
248
common/vp9_level_stats.cc
Normal file
@ -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 <inttypes.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#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<int64_t>(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<double>(total_compressed_size_ * 8);
|
||||
}
|
||||
|
||||
int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; }
|
||||
|
||||
int Vp9LevelStats::GetMinimumAltrefDistance() const {
|
||||
if (minimum_altref_distance != std::numeric_limits<int>::max())
|
||||
return minimum_altref_distance;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Vp9LevelStats::GetMaxReferenceFrames() const {
|
||||
return max_frames_refreshed_;
|
||||
}
|
||||
|
||||
} // namespace vp9_parser
|
184
common/vp9_level_stats.h
Normal file
184
common/vp9_level_stats.h
Normal file
@ -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 <limits>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
#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<int>::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<std::pair<int64_t, int64_t>> 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<std::pair<int64_t, int64_t>> 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_
|
185
common/vp9_level_stats_tests.cc
Normal file
185
common/vp9_level_stats_tests.cc
Normal file
@ -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 <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<uint8_t> 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<unsigned long>(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<size_t>(frame.len) > data.size()) {
|
||||
data.resize(frame.len);
|
||||
data_len = static_cast<size_t>(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<mkvparser::Segment> 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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user