1 /*
2 * Copyright (c) 2019 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/sdp_video_format_utils.h"
12
13 #include <cstring>
14 #include <map>
15 #include <utility>
16
17 #include "api/video_codecs/h264_profile_level_id.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/string_to_number.h"
20
21 namespace webrtc {
22 namespace {
23 const char kProfileLevelId[] = "profile-level-id";
24 const char kH264LevelAsymmetryAllowed[] = "level-asymmetry-allowed";
25 // Max frame rate for VP8 and VP9 video.
26 const char kVPxFmtpMaxFrameRate[] = "max-fr";
27 // Max frame size for VP8 and VP9 video.
28 const char kVPxFmtpMaxFrameSize[] = "max-fs";
29 const int kVPxFmtpFrameSizeSubBlockPixels = 256;
30
IsH264LevelAsymmetryAllowed(const SdpVideoFormat::Parameters & params)31 bool IsH264LevelAsymmetryAllowed(const SdpVideoFormat::Parameters& params) {
32 const auto it = params.find(kH264LevelAsymmetryAllowed);
33 return it != params.end() && strcmp(it->second.c_str(), "1") == 0;
34 }
35
36 // Compare H264 levels and handle the level 1b case.
H264LevelIsLess(H264Level a,H264Level b)37 bool H264LevelIsLess(H264Level a, H264Level b) {
38 if (a == H264Level::kLevel1_b)
39 return b != H264Level::kLevel1 && b != H264Level::kLevel1_b;
40 if (b == H264Level::kLevel1_b)
41 return a == H264Level::kLevel1;
42 return a < b;
43 }
44
H264LevelMin(H264Level a,H264Level b)45 H264Level H264LevelMin(H264Level a, H264Level b) {
46 return H264LevelIsLess(a, b) ? a : b;
47 }
48
ParsePositiveNumberFromParams(const SdpVideoFormat::Parameters & params,const char * parameter_name)49 absl::optional<int> ParsePositiveNumberFromParams(
50 const SdpVideoFormat::Parameters& params,
51 const char* parameter_name) {
52 const auto max_frame_rate_it = params.find(parameter_name);
53 if (max_frame_rate_it == params.end())
54 return absl::nullopt;
55
56 const absl::optional<int> i =
57 rtc::StringToNumber<int>(max_frame_rate_it->second);
58 if (!i.has_value() || i.value() <= 0)
59 return absl::nullopt;
60 return i;
61 }
62
63 } // namespace
64
65 // Set level according to https://tools.ietf.org/html/rfc6184#section-8.2.2.
H264GenerateProfileLevelIdForAnswer(const SdpVideoFormat::Parameters & local_supported_params,const SdpVideoFormat::Parameters & remote_offered_params,SdpVideoFormat::Parameters * answer_params)66 void H264GenerateProfileLevelIdForAnswer(
67 const SdpVideoFormat::Parameters& local_supported_params,
68 const SdpVideoFormat::Parameters& remote_offered_params,
69 SdpVideoFormat::Parameters* answer_params) {
70 // If both local and remote haven't set profile-level-id, they are both using
71 // the default profile. In this case, don't set profile-level-id in answer
72 // either.
73 if (!local_supported_params.count(kProfileLevelId) &&
74 !remote_offered_params.count(kProfileLevelId)) {
75 return;
76 }
77
78 // Parse profile-level-ids.
79 const absl::optional<H264ProfileLevelId> local_profile_level_id =
80 ParseSdpForH264ProfileLevelId(local_supported_params);
81 const absl::optional<H264ProfileLevelId> remote_profile_level_id =
82 ParseSdpForH264ProfileLevelId(remote_offered_params);
83 // The local and remote codec must have valid and equal H264 Profiles.
84 RTC_DCHECK(local_profile_level_id);
85 RTC_DCHECK(remote_profile_level_id);
86 RTC_DCHECK_EQ(local_profile_level_id->profile,
87 remote_profile_level_id->profile);
88
89 // Parse level information.
90 const bool level_asymmetry_allowed =
91 IsH264LevelAsymmetryAllowed(local_supported_params) &&
92 IsH264LevelAsymmetryAllowed(remote_offered_params);
93 const H264Level local_level = local_profile_level_id->level;
94 const H264Level remote_level = remote_profile_level_id->level;
95 const H264Level min_level = H264LevelMin(local_level, remote_level);
96
97 // Determine answer level. When level asymmetry is not allowed, level upgrade
98 // is not allowed, i.e., the level in the answer must be equal to or lower
99 // than the level in the offer.
100 const H264Level answer_level =
101 level_asymmetry_allowed ? local_level : min_level;
102
103 // Set the resulting profile-level-id in the answer parameters.
104 (*answer_params)[kProfileLevelId] = *H264ProfileLevelIdToString(
105 H264ProfileLevelId(local_profile_level_id->profile, answer_level));
106 }
107
ParseSdpForVPxMaxFrameRate(const SdpVideoFormat::Parameters & params)108 absl::optional<int> ParseSdpForVPxMaxFrameRate(
109 const SdpVideoFormat::Parameters& params) {
110 return ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameRate);
111 }
112
ParseSdpForVPxMaxFrameSize(const SdpVideoFormat::Parameters & params)113 absl::optional<int> ParseSdpForVPxMaxFrameSize(
114 const SdpVideoFormat::Parameters& params) {
115 const absl::optional<int> i =
116 ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameSize);
117 return i ? absl::make_optional(i.value() * kVPxFmtpFrameSizeSubBlockPixels)
118 : absl::nullopt;
119 }
120
121 } // namespace webrtc
122