You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
9.0 KiB
270 lines
9.0 KiB
// 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, 512},
|
|
{LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8, 768},
|
|
{LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8, 960},
|
|
{LEVEL_2_1, 9216000, 245760, 3600, 2800, 2, 2, 4, 8, 1344},
|
|
{LEVEL_3, 20736000, 552960, 7200, 6000, 2, 4, 4, 8, 2048},
|
|
{LEVEL_3_1, 36864000, 983040, 12000, 10000, 2, 4, 4, 8, 2752},
|
|
{LEVEL_4, 83558400, 2228224, 18000, 16000, 4, 4, 4, 8, 4160},
|
|
{LEVEL_4_1, 160432128, 2228224, 30000, 18000, 4, 4, 5, 6, 4160},
|
|
{LEVEL_5, 311951360, 8912896, 60000, 36000, 6, 8, 6, 4, 8384},
|
|
{LEVEL_5_1, 588251136, 8912896, 120000, 46000, 8, 8, 10, 4, 8384},
|
|
// CPB Size = 0 for levels 5_2 to 6_2
|
|
{LEVEL_5_2, 1176502272, 8912896, 180000, 0, 8, 8, 10, 4, 8384},
|
|
{LEVEL_6, 1176502272, 35651584, 180000, 0, 8, 16, 10, 4, 16832},
|
|
{LEVEL_6_1, 2353004544, 35651584, 240000, 0, 8, 16, 10, 4, 16832},
|
|
{LEVEL_6_2, 4706009088, 35651584, 480000, 0, 8, 16, 10, 4, 16832}};
|
|
|
|
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;
|
|
const int64_t luma_picture_breadth = (width > height) ? width : height;
|
|
if (luma_picture_size > max_luma_picture_size_)
|
|
max_luma_picture_size_ = luma_picture_size;
|
|
if (luma_picture_breadth > max_luma_picture_breadth_)
|
|
max_luma_picture_breadth_ = luma_picture_breadth;
|
|
|
|
total_compressed_size_ += parser.frame_size();
|
|
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
// TODO(fgalligan): Add support for other color formats. Currently assuming
|
|
// 420.
|
|
total_uncompressed_bits_ +=
|
|
(luma_picture_size * parser.bit_depth() * 3) / 2;
|
|
}
|
|
|
|
// 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 int64_t max_luma_picture_breadth = GetMaxLumaPictureBreadth();
|
|
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;
|
|
const double grace_multiplier =
|
|
max_luma_sample_rate_grace_percent_ / 100.0 + 1.0;
|
|
for (int i = 0; i < kNumVp9Levels; ++i) {
|
|
if (max_luma_sample_rate <=
|
|
Vp9LevelTable[i].max_luma_sample_rate * grace_multiplier) {
|
|
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 (max_luma_picture_breadth <= Vp9LevelTable[i].max_luma_picture_breadth) {
|
|
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_size_; }
|
|
|
|
int64_t Vp9LevelStats::GetMaxLumaPictureSize() const {
|
|
return max_luma_picture_size_;
|
|
}
|
|
|
|
int64_t Vp9LevelStats::GetMaxLumaPictureBreadth() const {
|
|
return max_luma_picture_breadth_;
|
|
}
|
|
|
|
double Vp9LevelStats::GetAverageBitRate() const {
|
|
const int64_t frame_duration_ns = end_ns_ - start_ns_;
|
|
double duration_seconds =
|
|
((duration_ns_ == -1) ? frame_duration_ns : duration_ns_) /
|
|
libwebm::kNanosecondsPerSecond;
|
|
if (estimate_last_frame_duration_ &&
|
|
(duration_ns_ == -1 || duration_ns_ <= frame_duration_ns)) {
|
|
const double sec_per_frame = frame_duration_ns /
|
|
libwebm::kNanosecondsPerSecond /
|
|
(displayed_frames - 1);
|
|
duration_seconds += sec_per_frame;
|
|
}
|
|
|
|
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
|