xref: /aosp_15_r20/external/libwebm/common/vp9_level_stats.cc (revision 103e46e4cd4b6efcf6001f23fa8665fb110abf8d)
1 // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS.  All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 #include "common/vp9_level_stats.h"
9 
10 #include <inttypes.h>
11 
12 #include <limits>
13 #include <utility>
14 
15 #include "common/webm_constants.h"
16 
17 namespace vp9_parser {
18 
19 const Vp9LevelRow Vp9LevelStats::Vp9LevelTable[kNumVp9Levels] = {
20     {LEVEL_1, 829440, 36864, 512, 200, 400, 2, 1, 4, 8},
21     {LEVEL_1_1, 2764800, 73728, 768, 800, 1000, 2, 1, 4, 8},
22     {LEVEL_2, 4608000, 122880, 960, 1800, 1500, 2, 1, 4, 8},
23     {LEVEL_2_1, 9216000, 245760, 1344, 3600, 2800, 2, 2, 4, 8},
24     {LEVEL_3, 20736000, 552960, 2048, 7200, 6000, 2, 4, 4, 8},
25     {LEVEL_3_1, 36864000, 983040, 2752, 12000, 10000, 2, 4, 4, 8},
26     {LEVEL_4, 83558400, 2228224, 4160, 18000, 16000, 4, 4, 4, 8},
27     {LEVEL_4_1, 160432128, 2228224, 4160, 30000, 18000, 4, 4, 5, 6},
28     {LEVEL_5, 311951360, 8912896, 8384, 60000, 36000, 6, 8, 6, 4},
29     {LEVEL_5_1, 588251136, 8912896, 8384, 120000, 46000, 8, 8, 10, 4},
30     // CPB Size = 0 for levels 5_2 to 6_2
31     {LEVEL_5_2, 1176502272, 8912896, 8384, 180000, 0, 8, 8, 10, 4},
32     {LEVEL_6, 1176502272, 35651584, 16832, 180000, 0, 8, 16, 10, 4},
33     {LEVEL_6_1, 2353004544, 35651584, 16832, 240000, 0, 8, 16, 10, 4},
34     {LEVEL_6_2, 4706009088, 35651584, 16832, 480000, 0, 8, 16, 10, 4}};
35 
AddFrame(const Vp9HeaderParser & parser,int64_t time_ns)36 void Vp9LevelStats::AddFrame(const Vp9HeaderParser& parser, int64_t time_ns) {
37   ++frames;
38   if (start_ns_ == -1)
39     start_ns_ = time_ns;
40   end_ns_ = time_ns;
41 
42   const int width = parser.width();
43   const int height = parser.height();
44   const int64_t luma_picture_size = width * height;
45   const int64_t luma_picture_breadth = (width > height) ? width : height;
46   if (luma_picture_size > max_luma_picture_size_)
47     max_luma_picture_size_ = luma_picture_size;
48   if (luma_picture_breadth > max_luma_picture_breadth_)
49     max_luma_picture_breadth_ = luma_picture_breadth;
50 
51   total_compressed_size_ += parser.frame_size();
52 
53   while (!luma_window_.empty() &&
54          luma_window_.front().first <
55              (time_ns - (libwebm::kNanosecondsPerSecondi - 1))) {
56     current_luma_size_ -= luma_window_.front().second;
57     luma_window_.pop();
58   }
59   current_luma_size_ += luma_picture_size;
60   luma_window_.push(std::make_pair(time_ns, luma_picture_size));
61   if (current_luma_size_ > max_luma_size_) {
62     max_luma_size_ = current_luma_size_;
63     max_luma_end_ns_ = luma_window_.back().first;
64   }
65 
66   // Record CPB stats.
67   // Remove all frames that are less than window size.
68   while (cpb_window_.size() > 3) {
69     current_cpb_size_ -= cpb_window_.front().second;
70     cpb_window_.pop();
71   }
72   cpb_window_.push(std::make_pair(time_ns, parser.frame_size()));
73 
74   current_cpb_size_ += parser.frame_size();
75   if (current_cpb_size_ > max_cpb_size_) {
76     max_cpb_size_ = current_cpb_size_;
77     max_cpb_start_ns_ = cpb_window_.front().first;
78     max_cpb_end_ns_ = cpb_window_.back().first;
79   }
80 
81   if (max_cpb_window_size_ < static_cast<int64_t>(cpb_window_.size())) {
82     max_cpb_window_size_ = cpb_window_.size();
83     max_cpb_window_end_ns_ = time_ns;
84   }
85 
86   // Record altref stats.
87   if (parser.altref()) {
88     const int delta_altref = frames_since_last_altref;
89     if (first_altref) {
90       first_altref = false;
91     } else if (delta_altref < minimum_altref_distance) {
92       minimum_altref_distance = delta_altref;
93       min_altref_end_ns = time_ns;
94     }
95     frames_since_last_altref = 0;
96   } else {
97     ++frames_since_last_altref;
98     ++displayed_frames;
99     // TODO(fgalligan): Add support for other color formats. Currently assuming
100     // 420.
101     total_uncompressed_bits_ +=
102         (luma_picture_size * parser.bit_depth() * 3) / 2;
103   }
104 
105   // Count max reference frames.
106   if (parser.key() == 1) {
107     frames_refreshed_ = 0;
108   } else {
109     frames_refreshed_ |= parser.refresh_frame_flags();
110 
111     int ref_frame_count = frames_refreshed_ & 1;
112     for (int i = 1; i < kMaxVp9RefFrames; ++i) {
113       ref_frame_count += (frames_refreshed_ >> i) & 1;
114     }
115 
116     if (ref_frame_count > max_frames_refreshed_)
117       max_frames_refreshed_ = ref_frame_count;
118   }
119 
120   // Count max tiles.
121   const int tiles = parser.column_tiles();
122   if (tiles > max_column_tiles_)
123     max_column_tiles_ = tiles;
124 }
125 
GetLevel() const126 Vp9Level Vp9LevelStats::GetLevel() const {
127   const int64_t max_luma_sample_rate = GetMaxLumaSampleRate();
128   const int64_t max_luma_picture_size = GetMaxLumaPictureSize();
129   const int64_t max_luma_picture_breadth = GetMaxLumaPictureBreadth();
130   const double average_bitrate = GetAverageBitRate();
131   const double max_cpb_size = GetMaxCpbSize();
132   const double compression_ratio = GetCompressionRatio();
133   const int max_column_tiles = GetMaxColumnTiles();
134   const int min_altref_distance = GetMinimumAltrefDistance();
135   const int max_ref_frames = GetMaxReferenceFrames();
136 
137   int level_index = 0;
138   Vp9Level max_level = LEVEL_UNKNOWN;
139   const double grace_multiplier =
140       max_luma_sample_rate_grace_percent_ / 100.0 + 1.0;
141   for (int i = 0; i < kNumVp9Levels; ++i) {
142     if (max_luma_sample_rate <=
143         Vp9LevelTable[i].max_luma_sample_rate * grace_multiplier) {
144       if (max_level < Vp9LevelTable[i].level) {
145         max_level = Vp9LevelTable[i].level;
146         level_index = i;
147       }
148       break;
149     }
150   }
151   for (int i = 0; i < kNumVp9Levels; ++i) {
152     if (max_luma_picture_size <= Vp9LevelTable[i].max_luma_picture_size) {
153       if (max_level < Vp9LevelTable[i].level) {
154         max_level = Vp9LevelTable[i].level;
155         level_index = i;
156       }
157       break;
158     }
159   }
160   for (int i = 0; i < kNumVp9Levels; ++i) {
161     if (max_luma_picture_breadth <= Vp9LevelTable[i].max_luma_picture_breadth) {
162       if (max_level < Vp9LevelTable[i].level) {
163         max_level = Vp9LevelTable[i].level;
164         level_index = i;
165       }
166       break;
167     }
168   }
169   for (int i = 0; i < kNumVp9Levels; ++i) {
170     if (average_bitrate <= Vp9LevelTable[i].average_bitrate) {
171       if (max_level < Vp9LevelTable[i].level) {
172         max_level = Vp9LevelTable[i].level;
173         level_index = i;
174       }
175       break;
176     }
177   }
178   for (int i = 0; i < kNumVp9Levels; ++i) {
179     // Only check CPB size for levels that are defined.
180     if (Vp9LevelTable[i].max_cpb_size > 0 &&
181         max_cpb_size <= Vp9LevelTable[i].max_cpb_size) {
182       if (max_level < Vp9LevelTable[i].level) {
183         max_level = Vp9LevelTable[i].level;
184         level_index = i;
185       }
186       break;
187     }
188   }
189   for (int i = 0; i < kNumVp9Levels; ++i) {
190     if (max_column_tiles <= Vp9LevelTable[i].max_tiles) {
191       if (max_level < Vp9LevelTable[i].level) {
192         max_level = Vp9LevelTable[i].level;
193         level_index = i;
194       }
195       break;
196     }
197   }
198 
199   for (int i = 0; i < kNumVp9Levels; ++i) {
200     if (max_ref_frames <= Vp9LevelTable[i].max_ref_frames) {
201       if (max_level < Vp9LevelTable[i].level) {
202         max_level = Vp9LevelTable[i].level;
203         level_index = i;
204       }
205       break;
206     }
207   }
208 
209   // Check if the current level meets the minimum altref distance requirement.
210   // If not, set to unknown level as we can't move up a level as the minimum
211   // altref distance get farther apart and we can't move down a level as we are
212   // already at the minimum level for all the other requirements.
213   if (min_altref_distance < Vp9LevelTable[level_index].min_altref_distance)
214     max_level = LEVEL_UNKNOWN;
215 
216   // The minimum compression ratio has the same behavior as minimum altref
217   // distance.
218   if (compression_ratio < Vp9LevelTable[level_index].compression_ratio)
219     max_level = LEVEL_UNKNOWN;
220   return max_level;
221 }
222 
GetMaxLumaSampleRate() const223 int64_t Vp9LevelStats::GetMaxLumaSampleRate() const { return max_luma_size_; }
224 
GetMaxLumaPictureSize() const225 int64_t Vp9LevelStats::GetMaxLumaPictureSize() const {
226   return max_luma_picture_size_;
227 }
228 
GetMaxLumaPictureBreadth() const229 int64_t Vp9LevelStats::GetMaxLumaPictureBreadth() const {
230   return max_luma_picture_breadth_;
231 }
232 
GetAverageBitRate() const233 double Vp9LevelStats::GetAverageBitRate() const {
234   const int64_t frame_duration_ns = end_ns_ - start_ns_;
235   double duration_seconds =
236       ((duration_ns_ == -1) ? frame_duration_ns : duration_ns_) /
237       libwebm::kNanosecondsPerSecond;
238   if (estimate_last_frame_duration_ &&
239       (duration_ns_ == -1 || duration_ns_ <= frame_duration_ns)) {
240     const double sec_per_frame = frame_duration_ns /
241                                  libwebm::kNanosecondsPerSecond /
242                                  (displayed_frames - 1);
243     duration_seconds += sec_per_frame;
244   }
245 
246   return total_compressed_size_ / duration_seconds / 125.0;
247 }
248 
GetMaxCpbSize() const249 double Vp9LevelStats::GetMaxCpbSize() const { return max_cpb_size_ / 125.0; }
250 
GetCompressionRatio() const251 double Vp9LevelStats::GetCompressionRatio() const {
252   return total_uncompressed_bits_ /
253          static_cast<double>(total_compressed_size_ * 8);
254 }
255 
GetMaxColumnTiles() const256 int Vp9LevelStats::GetMaxColumnTiles() const { return max_column_tiles_; }
257 
GetMinimumAltrefDistance() const258 int Vp9LevelStats::GetMinimumAltrefDistance() const {
259   if (minimum_altref_distance != std::numeric_limits<int>::max())
260     return minimum_altref_distance;
261   else
262     return -1;
263 }
264 
GetMaxReferenceFrames() const265 int Vp9LevelStats::GetMaxReferenceFrames() const {
266   return max_frames_refreshed_;
267 }
268 
269 }  // namespace vp9_parser
270