tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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