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