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