h265_profile_tier_level.cc (11740B)
1 /* 2 * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "api/video_codecs/h265_profile_tier_level.h" 12 13 #include <optional> 14 #include <string> 15 16 #include "api/rtp_parameters.h" 17 #include "api/video/resolution.h" 18 #include "rtc_base/string_to_number.h" 19 20 namespace webrtc { 21 22 namespace { 23 24 const char kH265FmtpProfile[] = "profile-id"; 25 const char kH265FmtpTier[] = "tier-flag"; 26 const char kH265FmtpLevel[] = "level-id"; 27 28 // Used to align frame width and height for luma picture size calculation. 29 // Use the maximum value allowed by spec to get upper bound of luma picture 30 // size for given resolution. 31 constexpr int kMinCbSizeYMax = 64; 32 33 struct LevelConstraint { 34 const int max_luma_picture_size; 35 const double max_luma_sample_rate; 36 const int max_pic_width_or_height_in_pixels; 37 const H265Level level; 38 }; 39 40 // This is from ITU-T H.265 (09/2023) Table A.8, A.9 & A.11 – Level limits. 41 // The max_pic_width_or_height_in_luma_samples is pre-calculated following 42 // ITU-T H.265 section A.4.1, that is, find the largest integer value that 43 // is multiple of minimal MinCbSizeY(8 according to equation 7-10 and 7-12), is 44 // less than sqrt(max_luma_picture_size * 8). For example, at level 1, 45 // max_luma_picture_size is 36864, so pic_width_in_luma_samples <= sqrt(36864 * 46 // 8) = 543.06. The largest integer that is multiple of 8 and less than 543.06 47 // is 536. 48 constexpr LevelConstraint kLevelConstraints[] = { 49 {.max_luma_picture_size = 36864, 50 .max_luma_sample_rate = 552960, 51 .max_pic_width_or_height_in_pixels = 536, 52 .level = H265Level::kLevel1}, 53 {.max_luma_picture_size = 122880, 54 .max_luma_sample_rate = 3686400, 55 .max_pic_width_or_height_in_pixels = 984, 56 .level = H265Level::kLevel2}, 57 {.max_luma_picture_size = 245760, 58 .max_luma_sample_rate = 7372800, 59 .max_pic_width_or_height_in_pixels = 1400, 60 .level = H265Level::kLevel2_1}, 61 {.max_luma_picture_size = 552960, 62 .max_luma_sample_rate = 16588800, 63 .max_pic_width_or_height_in_pixels = 2096, 64 .level = H265Level::kLevel3}, 65 {.max_luma_picture_size = 983040, 66 .max_luma_sample_rate = 33177600, 67 .max_pic_width_or_height_in_pixels = 2800, 68 .level = H265Level::kLevel3_1}, 69 {.max_luma_picture_size = 2228224, 70 .max_luma_sample_rate = 66846720, 71 .max_pic_width_or_height_in_pixels = 4216, 72 .level = H265Level::kLevel4}, 73 {.max_luma_picture_size = 2228224, 74 .max_luma_sample_rate = 133693400, 75 .max_pic_width_or_height_in_pixels = 4216, 76 .level = H265Level::kLevel4_1}, 77 {.max_luma_picture_size = 8912896, 78 .max_luma_sample_rate = 267386880, 79 .max_pic_width_or_height_in_pixels = 8440, 80 .level = H265Level::kLevel5}, 81 {.max_luma_picture_size = 8912896, 82 .max_luma_sample_rate = 534773760, 83 .max_pic_width_or_height_in_pixels = 8440, 84 .level = H265Level::kLevel5_1}, 85 {.max_luma_picture_size = 8912896, 86 .max_luma_sample_rate = 1069547520, 87 .max_pic_width_or_height_in_pixels = 8440, 88 .level = H265Level::kLevel5_2}, 89 {.max_luma_picture_size = 35651584, 90 .max_luma_sample_rate = 1069547520, 91 .max_pic_width_or_height_in_pixels = 16888, 92 .level = H265Level::kLevel6}, 93 {.max_luma_picture_size = 35651584, 94 .max_luma_sample_rate = 2139095040, 95 .max_pic_width_or_height_in_pixels = 16888, 96 .level = H265Level::kLevel6_1}, 97 {.max_luma_picture_size = 35651584, 98 .max_luma_sample_rate = 4278190080, 99 .max_pic_width_or_height_in_pixels = 16888, 100 .level = H265Level::kLevel6_2}, 101 }; 102 103 } // anonymous namespace 104 105 // Annex A of https://www.itu.int/rec/T-REC-H.265 (08/21), section A.3. 106 std::optional<H265Profile> StringToH265Profile(const std::string& profile) { 107 std::optional<int> i = StringToNumber<int>(profile); 108 if (!i.has_value()) { 109 return std::nullopt; 110 } 111 112 switch (i.value()) { 113 case 1: 114 return H265Profile::kProfileMain; 115 case 2: 116 return H265Profile::kProfileMain10; 117 case 3: 118 return H265Profile::kProfileMainStill; 119 case 4: 120 return H265Profile::kProfileRangeExtensions; 121 case 5: 122 return H265Profile::kProfileHighThroughput; 123 case 6: 124 return H265Profile::kProfileMultiviewMain; 125 case 7: 126 return H265Profile::kProfileScalableMain; 127 case 8: 128 return H265Profile::kProfile3dMain; 129 case 9: 130 return H265Profile::kProfileScreenContentCoding; 131 case 10: 132 return H265Profile::kProfileScalableRangeExtensions; 133 case 11: 134 return H265Profile::kProfileHighThroughputScreenContentCoding; 135 default: 136 return std::nullopt; 137 } 138 } 139 140 // Annex A of https://www.itu.int/rec/T-REC-H.265 (08/21), section A.4, 141 // tiers and levels. 142 std::optional<H265Tier> StringToH265Tier(const std::string& tier) { 143 std::optional<int> i = StringToNumber<int>(tier); 144 if (!i.has_value()) { 145 return std::nullopt; 146 } 147 148 switch (i.value()) { 149 case 0: 150 return H265Tier::kTier0; 151 case 1: 152 return H265Tier::kTier1; 153 default: 154 return std::nullopt; 155 } 156 } 157 158 std::optional<H265Level> StringToH265Level(const std::string& level) { 159 const std::optional<int> i = StringToNumber<int>(level); 160 if (!i.has_value()) 161 return std::nullopt; 162 163 switch (i.value()) { 164 case 30: 165 return H265Level::kLevel1; 166 case 60: 167 return H265Level::kLevel2; 168 case 63: 169 return H265Level::kLevel2_1; 170 case 90: 171 return H265Level::kLevel3; 172 case 93: 173 return H265Level::kLevel3_1; 174 case 120: 175 return H265Level::kLevel4; 176 case 123: 177 return H265Level::kLevel4_1; 178 case 150: 179 return H265Level::kLevel5; 180 case 153: 181 return H265Level::kLevel5_1; 182 case 156: 183 return H265Level::kLevel5_2; 184 case 180: 185 return H265Level::kLevel6; 186 case 183: 187 return H265Level::kLevel6_1; 188 case 186: 189 return H265Level::kLevel6_2; 190 default: 191 return std::nullopt; 192 } 193 } 194 195 std::string H265ProfileToString(H265Profile profile) { 196 switch (profile) { 197 case H265Profile::kProfileMain: 198 return "1"; 199 case H265Profile::kProfileMain10: 200 return "2"; 201 case H265Profile::kProfileMainStill: 202 return "3"; 203 case H265Profile::kProfileRangeExtensions: 204 return "4"; 205 case H265Profile::kProfileHighThroughput: 206 return "5"; 207 case H265Profile::kProfileMultiviewMain: 208 return "6"; 209 case H265Profile::kProfileScalableMain: 210 return "7"; 211 case H265Profile::kProfile3dMain: 212 return "8"; 213 case H265Profile::kProfileScreenContentCoding: 214 return "9"; 215 case H265Profile::kProfileScalableRangeExtensions: 216 return "10"; 217 case H265Profile::kProfileHighThroughputScreenContentCoding: 218 return "11"; 219 } 220 } 221 222 std::string H265TierToString(H265Tier tier) { 223 switch (tier) { 224 case H265Tier::kTier0: 225 return "0"; 226 case H265Tier::kTier1: 227 return "1"; 228 } 229 } 230 231 std::string H265LevelToString(H265Level level) { 232 switch (level) { 233 case H265Level::kLevel1: 234 return "30"; 235 case H265Level::kLevel2: 236 return "60"; 237 case H265Level::kLevel2_1: 238 return "63"; 239 case H265Level::kLevel3: 240 return "90"; 241 case H265Level::kLevel3_1: 242 return "93"; 243 case H265Level::kLevel4: 244 return "120"; 245 case H265Level::kLevel4_1: 246 return "123"; 247 case H265Level::kLevel5: 248 return "150"; 249 case H265Level::kLevel5_1: 250 return "153"; 251 case H265Level::kLevel5_2: 252 return "156"; 253 case H265Level::kLevel6: 254 return "180"; 255 case H265Level::kLevel6_1: 256 return "183"; 257 case H265Level::kLevel6_2: 258 return "186"; 259 } 260 } 261 262 std::optional<H265ProfileTierLevel> ParseSdpForH265ProfileTierLevel( 263 const CodecParameterMap& params) { 264 static const H265ProfileTierLevel kDefaultProfileTierLevel( 265 H265Profile::kProfileMain, H265Tier::kTier0, H265Level::kLevel3_1); 266 bool profile_tier_level_specified = false; 267 268 std::optional<H265Profile> profile; 269 const auto profile_it = params.find(kH265FmtpProfile); 270 if (profile_it != params.end()) { 271 profile_tier_level_specified = true; 272 const std::string& profile_str = profile_it->second; 273 profile = StringToH265Profile(profile_str); 274 if (!profile) { 275 return std::nullopt; 276 } 277 } else { 278 profile = H265Profile::kProfileMain; 279 } 280 std::optional<H265Tier> tier; 281 const auto tier_it = params.find(kH265FmtpTier); 282 if (tier_it != params.end()) { 283 profile_tier_level_specified = true; 284 const std::string& tier_str = tier_it->second; 285 tier = StringToH265Tier(tier_str); 286 if (!tier) { 287 return std::nullopt; 288 } 289 } else { 290 tier = H265Tier::kTier0; 291 } 292 std::optional<H265Level> level; 293 const auto level_it = params.find(kH265FmtpLevel); 294 if (level_it != params.end()) { 295 profile_tier_level_specified = true; 296 const std::string& level_str = level_it->second; 297 level = StringToH265Level(level_str); 298 if (!level) { 299 return std::nullopt; 300 } 301 } else { 302 level = H265Level::kLevel3_1; 303 } 304 305 // Spec Table A.9, level 1 to level 3.1 does not allow high tiers. 306 if (level <= H265Level::kLevel3_1 && tier == H265Tier::kTier1) { 307 return std::nullopt; 308 } 309 310 return !profile_tier_level_specified 311 ? kDefaultProfileTierLevel 312 : H265ProfileTierLevel(profile.value(), tier.value(), 313 level.value()); 314 } 315 316 bool H265IsSameProfileTierLevel(const CodecParameterMap& params1, 317 const CodecParameterMap& params2) { 318 const std::optional<H265ProfileTierLevel> ptl1 = 319 ParseSdpForH265ProfileTierLevel(params1); 320 const std::optional<H265ProfileTierLevel> ptl2 = 321 ParseSdpForH265ProfileTierLevel(params2); 322 return ptl1 && ptl2 && ptl1->profile == ptl2->profile && 323 ptl1->tier == ptl2->tier && ptl1->level == ptl2->level; 324 } 325 326 bool H265IsSameProfile(const CodecParameterMap& params1, 327 const CodecParameterMap& params2) { 328 const std::optional<H265ProfileTierLevel> ptl1 = 329 ParseSdpForH265ProfileTierLevel(params1); 330 const std::optional<H265ProfileTierLevel> ptl2 = 331 ParseSdpForH265ProfileTierLevel(params2); 332 return ptl1 && ptl2 && ptl1->profile == ptl2->profile; 333 } 334 335 bool H265IsSameTier(const CodecParameterMap& params1, 336 const CodecParameterMap& params2) { 337 const std::optional<H265ProfileTierLevel> ptl1 = 338 ParseSdpForH265ProfileTierLevel(params1); 339 const std::optional<H265ProfileTierLevel> ptl2 = 340 ParseSdpForH265ProfileTierLevel(params2); 341 return ptl1 && ptl2 && ptl1->tier == ptl2->tier; 342 } 343 344 std::optional<H265Level> GetSupportedH265Level(const Resolution& resolution, 345 float max_fps) { 346 int aligned_width = 347 (resolution.width + kMinCbSizeYMax - 1) & ~(kMinCbSizeYMax - 1); 348 int aligned_height = 349 (resolution.height + kMinCbSizeYMax - 1) & ~(kMinCbSizeYMax - 1); 350 351 for (int i = std::ssize(kLevelConstraints) - 1; i >= 0; --i) { 352 const LevelConstraint& level_constraint = kLevelConstraints[i]; 353 if (level_constraint.max_luma_picture_size <= 354 aligned_width * aligned_height && 355 level_constraint.max_luma_sample_rate <= 356 aligned_width * aligned_height * max_fps && 357 level_constraint.max_pic_width_or_height_in_pixels >= aligned_width && 358 level_constraint.max_pic_width_or_height_in_pixels >= aligned_height) { 359 return level_constraint.level; 360 } 361 } 362 return std::nullopt; 363 } 364 365 } // namespace webrtc