h264_profile_level_id.cc (11645B)
1 /* 2 * Copyright (c) 2021 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/h264_profile_level_id.h" 12 13 #include <cstdint> 14 #include <cstdio> 15 #include <cstdlib> 16 #include <cstring> 17 #include <optional> 18 #include <string> 19 20 #include "api/rtp_parameters.h" 21 22 namespace webrtc { 23 24 namespace { 25 26 const char kProfileLevelId[] = "profile-level-id"; 27 28 // For level_idc=11 and profile_idc=0x42, 0x4D, or 0x58, the constraint set3 29 // flag specifies if level 1b or level 1.1 is used. 30 const uint8_t kConstraintSet3Flag = 0x10; 31 32 // Convert a string of 8 characters into a byte where the positions containing 33 // character c will have their bit set. For example, c = 'x', str = "x1xx0000" 34 // will return 0b10110000. constexpr is used so that the pattern table in 35 // kProfilePatterns is statically initialized. 36 constexpr uint8_t ByteMaskString(char c, const char (&str)[9]) { 37 return (str[0] == c) << 7 | (str[1] == c) << 6 | (str[2] == c) << 5 | 38 (str[3] == c) << 4 | (str[4] == c) << 3 | (str[5] == c) << 2 | 39 (str[6] == c) << 1 | (str[7] == c) << 0; 40 } 41 42 // Class for matching bit patterns such as "x1xx0000" where 'x' is allowed to be 43 // either 0 or 1. 44 class BitPattern { 45 public: 46 explicit constexpr BitPattern(const char (&str)[9]) 47 : mask_(~ByteMaskString('x', str)), 48 masked_value_(ByteMaskString('1', str)) {} 49 50 bool IsMatch(uint8_t value) const { return masked_value_ == (value & mask_); } 51 52 private: 53 const uint8_t mask_; 54 const uint8_t masked_value_; 55 }; 56 57 // Table for converting between profile_idc/profile_iop to H264Profile. 58 struct ProfilePattern { 59 const uint8_t profile_idc; 60 const BitPattern profile_iop; 61 const H264Profile profile; 62 }; 63 64 // This is from https://tools.ietf.org/html/rfc6184#section-8.1. 65 constexpr ProfilePattern kProfilePatterns[] = { 66 {.profile_idc = 0x42, 67 .profile_iop = BitPattern("x1xx0000"), 68 .profile = H264Profile::kProfileConstrainedBaseline}, 69 {.profile_idc = 0x4D, 70 .profile_iop = BitPattern("1xxx0000"), 71 .profile = H264Profile::kProfileConstrainedBaseline}, 72 {.profile_idc = 0x58, 73 .profile_iop = BitPattern("11xx0000"), 74 .profile = H264Profile::kProfileConstrainedBaseline}, 75 {.profile_idc = 0x42, 76 .profile_iop = BitPattern("x0xx0000"), 77 .profile = H264Profile::kProfileBaseline}, 78 {.profile_idc = 0x58, 79 .profile_iop = BitPattern("10xx0000"), 80 .profile = H264Profile::kProfileBaseline}, 81 {.profile_idc = 0x4D, 82 .profile_iop = BitPattern("0x0x0000"), 83 .profile = H264Profile::kProfileMain}, 84 {.profile_idc = 0x64, 85 .profile_iop = BitPattern("00000000"), 86 .profile = H264Profile::kProfileHigh}, 87 {.profile_idc = 0x64, 88 .profile_iop = BitPattern("00001100"), 89 .profile = H264Profile::kProfileConstrainedHigh}, 90 {.profile_idc = 0xF4, 91 .profile_iop = BitPattern("00000000"), 92 .profile = H264Profile::kProfilePredictiveHigh444}}; 93 94 struct LevelConstraint { 95 const int max_macroblocks_per_second; 96 const int max_macroblock_frame_size; 97 const H264Level level; 98 }; 99 100 // This is from ITU-T H.264 (02/2016) Table A-1 – Level limits. 101 constexpr LevelConstraint kLevelConstraints[] = { 102 {.max_macroblocks_per_second = 1485, 103 .max_macroblock_frame_size = 99, 104 .level = H264Level::kLevel1}, 105 {.max_macroblocks_per_second = 1485, 106 .max_macroblock_frame_size = 99, 107 .level = H264Level::kLevel1_b}, 108 {.max_macroblocks_per_second = 3000, 109 .max_macroblock_frame_size = 396, 110 .level = H264Level::kLevel1_1}, 111 {.max_macroblocks_per_second = 6000, 112 .max_macroblock_frame_size = 396, 113 .level = H264Level::kLevel1_2}, 114 {.max_macroblocks_per_second = 11880, 115 .max_macroblock_frame_size = 396, 116 .level = H264Level::kLevel1_3}, 117 {.max_macroblocks_per_second = 11880, 118 .max_macroblock_frame_size = 396, 119 .level = H264Level::kLevel2}, 120 {.max_macroblocks_per_second = 19800, 121 .max_macroblock_frame_size = 792, 122 .level = H264Level::kLevel2_1}, 123 {.max_macroblocks_per_second = 20250, 124 .max_macroblock_frame_size = 1620, 125 .level = H264Level::kLevel2_2}, 126 {.max_macroblocks_per_second = 40500, 127 .max_macroblock_frame_size = 1620, 128 .level = H264Level::kLevel3}, 129 {.max_macroblocks_per_second = 108000, 130 .max_macroblock_frame_size = 3600, 131 .level = H264Level::kLevel3_1}, 132 {.max_macroblocks_per_second = 216000, 133 .max_macroblock_frame_size = 5120, 134 .level = H264Level::kLevel3_2}, 135 {.max_macroblocks_per_second = 245760, 136 .max_macroblock_frame_size = 8192, 137 .level = H264Level::kLevel4}, 138 {.max_macroblocks_per_second = 245760, 139 .max_macroblock_frame_size = 8192, 140 .level = H264Level::kLevel4_1}, 141 {.max_macroblocks_per_second = 522240, 142 .max_macroblock_frame_size = 8704, 143 .level = H264Level::kLevel4_2}, 144 {.max_macroblocks_per_second = 589824, 145 .max_macroblock_frame_size = 22080, 146 .level = H264Level::kLevel5}, 147 {.max_macroblocks_per_second = 983040, 148 .max_macroblock_frame_size = 36864, 149 .level = H264Level::kLevel5_1}, 150 {.max_macroblocks_per_second = 2073600, 151 .max_macroblock_frame_size = 36864, 152 .level = H264Level::kLevel5_2}, 153 }; 154 155 } // anonymous namespace 156 157 std::optional<H264ProfileLevelId> ParseH264ProfileLevelId(const char* str) { 158 // The string should consist of 3 bytes in hexadecimal format. 159 if (strlen(str) != 6u) 160 return std::nullopt; 161 const uint32_t profile_level_id_numeric = strtol(str, nullptr, 16); 162 if (profile_level_id_numeric == 0) 163 return std::nullopt; 164 165 // Separate into three bytes. 166 const uint8_t level_idc = 167 static_cast<uint8_t>(profile_level_id_numeric & 0xFF); 168 const uint8_t profile_iop = 169 static_cast<uint8_t>((profile_level_id_numeric >> 8) & 0xFF); 170 const uint8_t profile_idc = 171 static_cast<uint8_t>((profile_level_id_numeric >> 16) & 0xFF); 172 173 // Parse level based on level_idc and constraint set 3 flag. 174 H264Level level_casted = static_cast<H264Level>(level_idc); 175 H264Level level; 176 177 switch (level_casted) { 178 case H264Level::kLevel1_1: 179 level = (profile_iop & kConstraintSet3Flag) != 0 ? H264Level::kLevel1_b 180 : H264Level::kLevel1_1; 181 break; 182 case H264Level::kLevel1: 183 case H264Level::kLevel1_2: 184 case H264Level::kLevel1_3: 185 case H264Level::kLevel2: 186 case H264Level::kLevel2_1: 187 case H264Level::kLevel2_2: 188 case H264Level::kLevel3: 189 case H264Level::kLevel3_1: 190 case H264Level::kLevel3_2: 191 case H264Level::kLevel4: 192 case H264Level::kLevel4_1: 193 case H264Level::kLevel4_2: 194 case H264Level::kLevel5: 195 case H264Level::kLevel5_1: 196 case H264Level::kLevel5_2: 197 level = level_casted; 198 break; 199 default: 200 // Unrecognized level_idc. 201 return std::nullopt; 202 } 203 204 // Parse profile_idc/profile_iop into a Profile enum. 205 for (const ProfilePattern& pattern : kProfilePatterns) { 206 if (profile_idc == pattern.profile_idc && 207 pattern.profile_iop.IsMatch(profile_iop)) { 208 return H264ProfileLevelId(pattern.profile, level); 209 } 210 } 211 212 // Unrecognized profile_idc/profile_iop combination. 213 return std::nullopt; 214 } 215 216 std::optional<H264Level> H264SupportedLevel(int max_frame_pixel_count, 217 float max_fps) { 218 static const int kPixelsPerMacroblock = 16 * 16; 219 220 for (int i = std::ssize(kLevelConstraints) - 1; i >= 0; --i) { 221 const LevelConstraint& level_constraint = kLevelConstraints[i]; 222 if (level_constraint.max_macroblock_frame_size * kPixelsPerMacroblock <= 223 max_frame_pixel_count && 224 level_constraint.max_macroblocks_per_second <= 225 max_fps * level_constraint.max_macroblock_frame_size) { 226 return level_constraint.level; 227 } 228 } 229 230 // No level supported. 231 return std::nullopt; 232 } 233 234 std::optional<H264ProfileLevelId> ParseSdpForH264ProfileLevelId( 235 const CodecParameterMap& params) { 236 // TODO(magjed): The default should really be kProfileBaseline and kLevel1 237 // according to the spec: https://tools.ietf.org/html/rfc6184#section-8.1. In 238 // order to not break backwards compatibility with older versions of WebRTC 239 // where external codecs don't have any parameters, use 240 // kProfileConstrainedBaseline kLevel3_1 instead. This workaround will only be 241 // done in an interim period to allow external clients to update their code. 242 // http://crbug/webrtc/6337. 243 static const H264ProfileLevelId kDefaultProfileLevelId( 244 H264Profile::kProfileConstrainedBaseline, H264Level::kLevel3_1); 245 246 const auto profile_level_id_it = params.find(kProfileLevelId); 247 return (profile_level_id_it == params.end()) 248 ? kDefaultProfileLevelId 249 : ParseH264ProfileLevelId(profile_level_id_it->second.c_str()); 250 } 251 252 std::optional<std::string> H264ProfileLevelIdToString( 253 const H264ProfileLevelId& profile_level_id) { 254 // Handle special case level == 1b. 255 if (profile_level_id.level == H264Level::kLevel1_b) { 256 switch (profile_level_id.profile) { 257 case H264Profile::kProfileConstrainedBaseline: 258 return {"42f00b"}; 259 case H264Profile::kProfileBaseline: 260 return {"42100b"}; 261 case H264Profile::kProfileMain: 262 return {"4d100b"}; 263 // Level 1b is not allowed for other profiles. 264 default: 265 return std::nullopt; 266 } 267 } 268 269 const char* profile_idc_iop_string; 270 switch (profile_level_id.profile) { 271 case H264Profile::kProfileConstrainedBaseline: 272 profile_idc_iop_string = "42e0"; 273 break; 274 case H264Profile::kProfileBaseline: 275 profile_idc_iop_string = "4200"; 276 break; 277 case H264Profile::kProfileMain: 278 profile_idc_iop_string = "4d00"; 279 break; 280 case H264Profile::kProfileConstrainedHigh: 281 profile_idc_iop_string = "640c"; 282 break; 283 case H264Profile::kProfileHigh: 284 profile_idc_iop_string = "6400"; 285 break; 286 case H264Profile::kProfilePredictiveHigh444: 287 profile_idc_iop_string = "f400"; 288 break; 289 // Unrecognized profile. 290 default: 291 return std::nullopt; 292 } 293 294 char str[7]; 295 snprintf(str, 7u, "%s%02x", profile_idc_iop_string, 296 static_cast<unsigned>(profile_level_id.level)); 297 return {str}; 298 } 299 300 bool H264IsSameProfile(const CodecParameterMap& params1, 301 const CodecParameterMap& params2) { 302 const std::optional<H264ProfileLevelId> profile_level_id = 303 ParseSdpForH264ProfileLevelId(params1); 304 const std::optional<H264ProfileLevelId> other_profile_level_id = 305 ParseSdpForH264ProfileLevelId(params2); 306 // Compare H264 profiles, but not levels. 307 return profile_level_id && other_profile_level_id && 308 profile_level_id->profile == other_profile_level_id->profile; 309 } 310 311 bool H264IsSameProfileAndLevel(const CodecParameterMap& params1, 312 const CodecParameterMap& params2) { 313 const std::optional<H264ProfileLevelId> profile_level_id = 314 ParseSdpForH264ProfileLevelId(params1); 315 const std::optional<H264ProfileLevelId> other_profile_level_id = 316 ParseSdpForH264ProfileLevelId(params2); 317 // Compare H264 profiles, but not levels. 318 return profile_level_id && other_profile_level_id && 319 profile_level_id->profile == other_profile_level_id->profile && 320 profile_level_id->level == other_profile_level_id->level; 321 } 322 323 } // namespace webrtc