sdp_video_format_utils.cc (7296B)
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 <optional> 16 #include <string> 17 #include <utility> 18 19 #include "api/rtp_parameters.h" 20 #include "api/video_codecs/h264_profile_level_id.h" 21 #ifdef RTC_ENABLE_H265 22 #include "api/video_codecs/h265_profile_tier_level.h" 23 #endif 24 #include "absl/algorithm/container.h" 25 #include "media/base/media_constants.h" 26 #include "rtc_base/checks.h" 27 #include "rtc_base/string_to_number.h" 28 29 namespace webrtc { 30 namespace { 31 const char kProfileLevelId[] = "profile-level-id"; 32 const char kH264LevelAsymmetryAllowed[] = "level-asymmetry-allowed"; 33 // Max frame rate for VP8 and VP9 video. 34 const char kVPxFmtpMaxFrameRate[] = "max-fr"; 35 // Max frame size for VP8 and VP9 video. 36 const char kVPxFmtpMaxFrameSize[] = "max-fs"; 37 const int kVPxFmtpFrameSizeSubBlockPixels = 256; 38 #ifdef RTC_ENABLE_H265 39 constexpr char kH265ProfileId[] = "profile-id"; 40 constexpr char kH265TierFlag[] = "tier-flag"; 41 constexpr char kH265LevelId[] = "level-id"; 42 #endif 43 44 bool IsH264LevelAsymmetryAllowed(const CodecParameterMap& params) { 45 const auto it = params.find(kH264LevelAsymmetryAllowed); 46 return it != params.end() && strcmp(it->second.c_str(), "1") == 0; 47 } 48 49 // Compare H264 levels and handle the level 1b case. 50 bool H264LevelIsLess(H264Level a, H264Level b) { 51 if (a == H264Level::kLevel1_b) 52 return b != H264Level::kLevel1 && b != H264Level::kLevel1_b; 53 if (b == H264Level::kLevel1_b) 54 return a == H264Level::kLevel1; 55 return a < b; 56 } 57 58 H264Level H264LevelMin(H264Level a, H264Level b) { 59 return H264LevelIsLess(a, b) ? a : b; 60 } 61 62 std::optional<int> ParsePositiveNumberFromParams( 63 const CodecParameterMap& params, 64 const char* parameter_name) { 65 const auto max_frame_rate_it = params.find(parameter_name); 66 if (max_frame_rate_it == params.end()) 67 return std::nullopt; 68 69 const std::optional<int> i = StringToNumber<int>(max_frame_rate_it->second); 70 if (!i.has_value() || i.value() <= 0) 71 return std::nullopt; 72 return i; 73 } 74 75 #ifdef RTC_ENABLE_H265 76 // Compares two H265Level and return the smaller. 77 H265Level H265LevelMin(H265Level a, H265Level b) { 78 return a <= b ? a : b; 79 } 80 81 // Returns true if none of profile-id/tier-flag/level-id is specified 82 // explicitly in the param. 83 bool IsDefaultH265PTL(const CodecParameterMap& params) { 84 return !params.count(kH265ProfileId) && !params.count(kH265TierFlag) && 85 !params.count(kH265LevelId); 86 } 87 #endif 88 89 } // namespace 90 91 #ifdef RTC_ENABLE_H265 92 // Set level according to https://tools.ietf.org/html/rfc7798#section-7.1 93 void H265GenerateProfileTierLevelForAnswer( 94 const CodecParameterMap& local_supported_params, 95 const CodecParameterMap& remote_offered_params, 96 CodecParameterMap* answer_params) { 97 // If local and remote haven't set profile-id/tier-flag/level-id, they 98 // are both using the default PTL In this case, don't set PTL in answer 99 // either. 100 if (IsDefaultH265PTL(local_supported_params) && 101 IsDefaultH265PTL(remote_offered_params)) { 102 return; 103 } 104 105 // Parse profile-tier-level. 106 const std::optional<H265ProfileTierLevel> local_profile_tier_level = 107 ParseSdpForH265ProfileTierLevel(local_supported_params); 108 const std::optional<H265ProfileTierLevel> remote_profile_tier_level = 109 ParseSdpForH265ProfileTierLevel(remote_offered_params); 110 // Profile and tier for local and remote codec must be valid and equal. 111 RTC_DCHECK(local_profile_tier_level); 112 RTC_DCHECK(remote_profile_tier_level); 113 RTC_DCHECK_EQ(local_profile_tier_level->profile, 114 remote_profile_tier_level->profile); 115 RTC_DCHECK_EQ(local_profile_tier_level->tier, 116 remote_profile_tier_level->tier); 117 118 const H265Level answer_level = H265LevelMin(local_profile_tier_level->level, 119 remote_profile_tier_level->level); 120 121 // Level-id in answer is changable as long as the highest level indicated by 122 // the answer is not higher than that indicated by the offer. See 123 // https://tools.ietf.org/html/rfc7798#section-7.2.2, sub-clause 2. 124 (*answer_params)[kH265LevelId] = H265LevelToString(answer_level); 125 } 126 #endif 127 128 // Set level according to https://tools.ietf.org/html/rfc6184#section-8.2.2. 129 void H264GenerateProfileLevelIdForAnswer( 130 const CodecParameterMap& local_supported_params, 131 const CodecParameterMap& remote_offered_params, 132 CodecParameterMap* answer_params) { 133 // If both local and remote haven't set profile-level-id, they are both using 134 // the default profile. In this case, don't set profile-level-id in answer 135 // either. 136 if (!local_supported_params.count(kProfileLevelId) && 137 !remote_offered_params.count(kProfileLevelId)) { 138 return; 139 } 140 141 // Parse profile-level-ids. 142 const std::optional<H264ProfileLevelId> local_profile_level_id = 143 ParseSdpForH264ProfileLevelId(local_supported_params); 144 const std::optional<H264ProfileLevelId> remote_profile_level_id = 145 ParseSdpForH264ProfileLevelId(remote_offered_params); 146 // The local and remote codec must have valid and equal H264 Profiles. 147 RTC_DCHECK(local_profile_level_id); 148 RTC_DCHECK(remote_profile_level_id); 149 RTC_DCHECK_EQ(local_profile_level_id->profile, 150 remote_profile_level_id->profile); 151 152 // Parse level information. 153 const bool level_asymmetry_allowed = 154 IsH264LevelAsymmetryAllowed(local_supported_params) && 155 IsH264LevelAsymmetryAllowed(remote_offered_params); 156 const H264Level local_level = local_profile_level_id->level; 157 const H264Level remote_level = remote_profile_level_id->level; 158 const H264Level min_level = H264LevelMin(local_level, remote_level); 159 160 // Determine answer level. When level asymmetry is not allowed, level upgrade 161 // is not allowed, i.e., the level in the answer must be equal to or lower 162 // than the level in the offer. 163 const H264Level answer_level = 164 level_asymmetry_allowed ? local_level : min_level; 165 166 // Set the resulting profile-level-id in the answer parameters. 167 (*answer_params)[kProfileLevelId] = *H264ProfileLevelIdToString( 168 H264ProfileLevelId(local_profile_level_id->profile, answer_level)); 169 } 170 171 std::optional<int> ParseSdpForVPxMaxFrameRate(const CodecParameterMap& params) { 172 return ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameRate); 173 } 174 175 std::optional<int> ParseSdpForVPxMaxFrameSize(const CodecParameterMap& params) { 176 const std::optional<int> i = 177 ParsePositiveNumberFromParams(params, kVPxFmtpMaxFrameSize); 178 return i ? std::make_optional(i.value() * kVPxFmtpFrameSizeSubBlockPixels) 179 : std::nullopt; 180 } 181 182 bool SupportsPerLayerPictureLossIndication(const CodecParameterMap& params) { 183 return absl::c_find_if( 184 params, [](const std::pair<std::string, std::string>& kv) { 185 return kv.first == kCodecParamPerLayerPictureLossIndication && 186 kv.second == "1"; 187 }) != params.end(); 188 } 189 190 } // namespace webrtc