1 /*
2 * Copyright (c) 2017 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 "media/base/h264_profile_level_id.h"
12
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16
17 #include "rtc_base/arraysize.h"
18 #include "rtc_base/checks.h"
19
20 namespace webrtc {
21 namespace H264 {
22
23 namespace {
24
25 const char kProfileLevelId[] = "profile-level-id";
26 const char kLevelAsymmetryAllowed[] = "level-asymmetry-allowed";
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.
ByteMaskString(char c,const char (& str)[9])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:
BitPattern(const char (& str)[9])46 explicit constexpr BitPattern(const char (&str)[9])
47 : mask_(~ByteMaskString('x', str)),
48 masked_value_(ByteMaskString('1', str)) {}
49
IsMatch(uint8_t value) const50 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 H264::Profile.
58 struct ProfilePattern {
59 const uint8_t profile_idc;
60 const BitPattern profile_iop;
61 const Profile profile;
62 };
63
64 // This is from https://tools.ietf.org/html/rfc6184#section-8.1.
65 constexpr ProfilePattern kProfilePatterns[] = {
66 {0x42, BitPattern("x1xx0000"), kProfileConstrainedBaseline},
67 {0x4D, BitPattern("1xxx0000"), kProfileConstrainedBaseline},
68 {0x58, BitPattern("11xx0000"), kProfileConstrainedBaseline},
69 {0x42, BitPattern("x0xx0000"), kProfileBaseline},
70 {0x58, BitPattern("10xx0000"), kProfileBaseline},
71 {0x4D, BitPattern("0x0x0000"), kProfileMain},
72 {0x64, BitPattern("00000000"), kProfileHigh},
73 {0x64, BitPattern("00001100"), kProfileConstrainedHigh}};
74
75 // Compare H264 levels and handle the level 1b case.
IsLess(Level a,Level b)76 bool IsLess(Level a, Level b) {
77 if (a == kLevel1_b)
78 return b != kLevel1 && b != kLevel1_b;
79 if (b == kLevel1_b)
80 return a == kLevel1;
81 return a < b;
82 }
83
Min(Level a,Level b)84 Level Min(Level a, Level b) {
85 return IsLess(a, b) ? a : b;
86 }
87
IsLevelAsymmetryAllowed(const CodecParameterMap & params)88 bool IsLevelAsymmetryAllowed(const CodecParameterMap& params) {
89 const auto it = params.find(kLevelAsymmetryAllowed);
90 return it != params.end() && strcmp(it->second.c_str(), "1") == 0;
91 }
92
93 struct LevelConstraint {
94 const int max_macroblocks_per_second;
95 const int max_macroblock_frame_size;
96 const webrtc::H264::Level level;
97 };
98
99 // This is from ITU-T H.264 (02/2016) Table A-1 – Level limits.
100 static constexpr LevelConstraint kLevelConstraints[] = {
101 {1485, 99, webrtc::H264::kLevel1},
102 {1485, 99, webrtc::H264::kLevel1_b},
103 {3000, 396, webrtc::H264::kLevel1_1},
104 {6000, 396, webrtc::H264::kLevel1_2},
105 {11880, 396, webrtc::H264::kLevel1_3},
106 {11880, 396, webrtc::H264::kLevel2},
107 {19800, 792, webrtc::H264::kLevel2_1},
108 {20250, 1620, webrtc::H264::kLevel2_2},
109 {40500, 1620, webrtc::H264::kLevel3},
110 {108000, 3600, webrtc::H264::kLevel3_1},
111 {216000, 5120, webrtc::H264::kLevel3_2},
112 {245760, 8192, webrtc::H264::kLevel4},
113 {245760, 8192, webrtc::H264::kLevel4_1},
114 {522240, 8704, webrtc::H264::kLevel4_2},
115 {589824, 22080, webrtc::H264::kLevel5},
116 {983040, 36864, webrtc::H264::kLevel5_1},
117 {2073600, 36864, webrtc::H264::kLevel5_2},
118 };
119
120 } // anonymous namespace
121
ParseProfileLevelId(const char * str)122 absl::optional<ProfileLevelId> ParseProfileLevelId(const char* str) {
123 // The string should consist of 3 bytes in hexadecimal format.
124 if (strlen(str) != 6u)
125 return absl::nullopt;
126 const uint32_t profile_level_id_numeric = strtol(str, nullptr, 16);
127 if (profile_level_id_numeric == 0)
128 return absl::nullopt;
129
130 // Separate into three bytes.
131 const uint8_t level_idc =
132 static_cast<uint8_t>(profile_level_id_numeric & 0xFF);
133 const uint8_t profile_iop =
134 static_cast<uint8_t>((profile_level_id_numeric >> 8) & 0xFF);
135 const uint8_t profile_idc =
136 static_cast<uint8_t>((profile_level_id_numeric >> 16) & 0xFF);
137
138 // Parse level based on level_idc and constraint set 3 flag.
139 Level level;
140 switch (level_idc) {
141 case kLevel1_1:
142 level = (profile_iop & kConstraintSet3Flag) != 0 ? kLevel1_b : kLevel1_1;
143 break;
144 case kLevel1:
145 case kLevel1_2:
146 case kLevel1_3:
147 case kLevel2:
148 case kLevel2_1:
149 case kLevel2_2:
150 case kLevel3:
151 case kLevel3_1:
152 case kLevel3_2:
153 case kLevel4:
154 case kLevel4_1:
155 case kLevel4_2:
156 case kLevel5:
157 case kLevel5_1:
158 case kLevel5_2:
159 level = static_cast<Level>(level_idc);
160 break;
161 default:
162 // Unrecognized level_idc.
163 return absl::nullopt;
164 }
165
166 // Parse profile_idc/profile_iop into a Profile enum.
167 for (const ProfilePattern& pattern : kProfilePatterns) {
168 if (profile_idc == pattern.profile_idc &&
169 pattern.profile_iop.IsMatch(profile_iop)) {
170 return ProfileLevelId(pattern.profile, level);
171 }
172 }
173
174 // Unrecognized profile_idc/profile_iop combination.
175 return absl::nullopt;
176 }
177
SupportedLevel(int max_frame_pixel_count,float max_fps)178 absl::optional<Level> SupportedLevel(int max_frame_pixel_count, float max_fps) {
179 static const int kPixelsPerMacroblock = 16 * 16;
180
181 for (int i = arraysize(kLevelConstraints) - 1; i >= 0; --i) {
182 const LevelConstraint& level_constraint = kLevelConstraints[i];
183 if (level_constraint.max_macroblock_frame_size * kPixelsPerMacroblock <=
184 max_frame_pixel_count &&
185 level_constraint.max_macroblocks_per_second <=
186 max_fps * level_constraint.max_macroblock_frame_size) {
187 return level_constraint.level;
188 }
189 }
190
191 // No level supported.
192 return absl::nullopt;
193 }
194
ParseSdpProfileLevelId(const CodecParameterMap & params)195 absl::optional<ProfileLevelId> ParseSdpProfileLevelId(
196 const CodecParameterMap& params) {
197 // TODO(magjed): The default should really be kProfileBaseline and kLevel1
198 // according to the spec: https://tools.ietf.org/html/rfc6184#section-8.1. In
199 // order to not break backwards compatibility with older versions of WebRTC
200 // where external codecs don't have any parameters, use
201 // kProfileConstrainedBaseline kLevel3_1 instead. This workaround will only be
202 // done in an interim period to allow external clients to update their code.
203 // http://crbug/webrtc/6337.
204 static const ProfileLevelId kDefaultProfileLevelId(
205 kProfileConstrainedBaseline, kLevel3_1);
206
207 const auto profile_level_id_it = params.find(kProfileLevelId);
208 return (profile_level_id_it == params.end())
209 ? kDefaultProfileLevelId
210 : ParseProfileLevelId(profile_level_id_it->second.c_str());
211 }
212
ProfileLevelIdToString(const ProfileLevelId & profile_level_id)213 absl::optional<std::string> ProfileLevelIdToString(
214 const ProfileLevelId& profile_level_id) {
215 // Handle special case level == 1b.
216 if (profile_level_id.level == kLevel1_b) {
217 switch (profile_level_id.profile) {
218 case kProfileConstrainedBaseline:
219 return {"42f00b"};
220 case kProfileBaseline:
221 return {"42100b"};
222 case kProfileMain:
223 return {"4d100b"};
224 // Level 1b is not allowed for other profiles.
225 default:
226 return absl::nullopt;
227 }
228 }
229
230 const char* profile_idc_iop_string;
231 switch (profile_level_id.profile) {
232 case kProfileConstrainedBaseline:
233 profile_idc_iop_string = "42e0";
234 break;
235 case kProfileBaseline:
236 profile_idc_iop_string = "4200";
237 break;
238 case kProfileMain:
239 profile_idc_iop_string = "4d00";
240 break;
241 case kProfileConstrainedHigh:
242 profile_idc_iop_string = "640c";
243 break;
244 case kProfileHigh:
245 profile_idc_iop_string = "6400";
246 break;
247 // Unrecognized profile.
248 default:
249 return absl::nullopt;
250 }
251
252 char str[7];
253 snprintf(str, 7u, "%s%02x", profile_idc_iop_string, profile_level_id.level);
254 return {str};
255 }
256
257 // Set level according to https://tools.ietf.org/html/rfc6184#section-8.2.2.
GenerateProfileLevelIdForAnswer(const CodecParameterMap & local_supported_params,const CodecParameterMap & remote_offered_params,CodecParameterMap * answer_params)258 void GenerateProfileLevelIdForAnswer(
259 const CodecParameterMap& local_supported_params,
260 const CodecParameterMap& remote_offered_params,
261 CodecParameterMap* answer_params) {
262 // If both local and remote haven't set profile-level-id, they are both using
263 // the default profile. In this case, don't set profile-level-id in answer
264 // either.
265 if (!local_supported_params.count(kProfileLevelId) &&
266 !remote_offered_params.count(kProfileLevelId)) {
267 return;
268 }
269
270 // Parse profile-level-ids.
271 const absl::optional<ProfileLevelId> local_profile_level_id =
272 ParseSdpProfileLevelId(local_supported_params);
273 const absl::optional<ProfileLevelId> remote_profile_level_id =
274 ParseSdpProfileLevelId(remote_offered_params);
275 // The local and remote codec must have valid and equal H264 Profiles.
276 RTC_DCHECK(local_profile_level_id);
277 RTC_DCHECK(remote_profile_level_id);
278 RTC_DCHECK_EQ(local_profile_level_id->profile,
279 remote_profile_level_id->profile);
280
281 // Parse level information.
282 const bool level_asymmetry_allowed =
283 IsLevelAsymmetryAllowed(local_supported_params) &&
284 IsLevelAsymmetryAllowed(remote_offered_params);
285 const Level local_level = local_profile_level_id->level;
286 const Level remote_level = remote_profile_level_id->level;
287 const Level min_level = Min(local_level, remote_level);
288
289 // Determine answer level. When level asymmetry is not allowed, level upgrade
290 // is not allowed, i.e., the level in the answer must be equal to or lower
291 // than the level in the offer.
292 const Level answer_level = level_asymmetry_allowed ? local_level : min_level;
293
294 // Set the resulting profile-level-id in the answer parameters.
295 (*answer_params)[kProfileLevelId] = *ProfileLevelIdToString(
296 ProfileLevelId(local_profile_level_id->profile, answer_level));
297 }
298
IsSameH264Profile(const CodecParameterMap & params1,const CodecParameterMap & params2)299 bool IsSameH264Profile(const CodecParameterMap& params1,
300 const CodecParameterMap& params2) {
301 const absl::optional<webrtc::H264::ProfileLevelId> profile_level_id =
302 webrtc::H264::ParseSdpProfileLevelId(params1);
303 const absl::optional<webrtc::H264::ProfileLevelId> other_profile_level_id =
304 webrtc::H264::ParseSdpProfileLevelId(params2);
305 // Compare H264 profiles, but not levels.
306 return profile_level_id && other_profile_level_id &&
307 profile_level_id->profile == other_profile_level_id->profile;
308 }
309
310 } // namespace H264
311 } // namespace webrtc
312