sdp_video_format.cc (9441B)
1 /* 2 * Copyright (c) 2018 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/sdp_video_format.h" 12 13 #include <optional> 14 #include <string> 15 16 #include "absl/container/inlined_vector.h" 17 #include "absl/strings/match.h" 18 #include "api/array_view.h" 19 #include "api/rtp_parameters.h" 20 #include "api/video/video_codec_type.h" 21 #include "api/video_codecs/av1_profile.h" 22 #include "api/video_codecs/h264_profile_level_id.h" 23 #include "api/video_codecs/scalability_mode.h" 24 #ifdef RTC_ENABLE_H265 25 #include "api/video_codecs/h265_profile_tier_level.h" 26 #endif 27 #include "api/video_codecs/video_codec.h" 28 #include "api/video_codecs/vp9_profile.h" 29 #include "media/base/media_constants.h" 30 #include "rtc_base/checks.h" 31 #include "rtc_base/logging.h" 32 #include "rtc_base/strings/string_builder.h" 33 34 namespace webrtc { 35 36 namespace { 37 38 // TODO(bugs.webrtc.org/15847): remove code duplication of IsSameCodecSpecific 39 // in media/base/codec.cc 40 std::string GetFmtpParameterOrDefault(const CodecParameterMap& params, 41 const std::string& name, 42 const std::string& default_value) { 43 const auto it = params.find(name); 44 if (it != params.end()) { 45 return it->second; 46 } 47 return default_value; 48 } 49 50 std::string H264GetPacketizationModeOrDefault(const CodecParameterMap& params) { 51 // If packetization-mode is not present, default to "0". 52 // https://tools.ietf.org/html/rfc6184#section-6.2 53 return GetFmtpParameterOrDefault(params, kH264FmtpPacketizationMode, "0"); 54 } 55 56 bool H264IsSamePacketizationMode(const CodecParameterMap& left, 57 const CodecParameterMap& right) { 58 return H264GetPacketizationModeOrDefault(left) == 59 H264GetPacketizationModeOrDefault(right); 60 } 61 62 #ifdef RTC_ENABLE_H265 63 std::string GetH265TxModeOrDefault(const CodecParameterMap& params) { 64 // If TxMode is not present, a value of "SRST" must be inferred. 65 // https://tools.ietf.org/html/rfc7798@section-7.1 66 return GetFmtpParameterOrDefault(params, kH265FmtpTxMode, "SRST"); 67 } 68 69 bool IsSameH265TxMode(const CodecParameterMap& left, 70 const CodecParameterMap& right) { 71 return absl::EqualsIgnoreCase(GetH265TxModeOrDefault(left), 72 GetH265TxModeOrDefault(right)); 73 } 74 #endif 75 76 // Some (video) codecs are actually families of codecs and rely on parameters 77 // to distinguish different incompatible family members. 78 bool IsSameCodecSpecific(const std::string& name1, 79 const CodecParameterMap& params1, 80 const std::string& name2, 81 const CodecParameterMap& params2) { 82 // The assumption when calling this function is that the two formats have the 83 // same name. 84 RTC_DCHECK(absl::EqualsIgnoreCase(name1, name2)); 85 86 VideoCodecType codec_type = PayloadStringToCodecType(name1); 87 switch (codec_type) { 88 case kVideoCodecH264: 89 return H264IsSameProfile(params1, params2) && 90 H264IsSamePacketizationMode(params1, params2); 91 case kVideoCodecVP9: 92 return VP9IsSameProfile(params1, params2); 93 case kVideoCodecAV1: 94 // https://aomediacodec.github.io/av1-rtp-spec/#723-usage-with-the-sdp-offeranswer-model 95 // These media configuration parameters are asymmetrical and the 96 // answerer MAY declare its own media configuration 97 // TODO(bugs.webrtc.org/396434695): for backward compability we currently 98 // compare profile. 99 return AV1IsSameProfile(params1, params2); 100 #ifdef RTC_ENABLE_H265 101 case kVideoCodecH265: 102 return H265IsSameProfile(params1, params2) && 103 H265IsSameTier(params1, params2) && 104 IsSameH265TxMode(params1, params2); 105 #endif 106 default: 107 return true; 108 } 109 } 110 111 } // namespace 112 113 SdpVideoFormat::SdpVideoFormat(const std::string& name) : name(name) {} 114 115 SdpVideoFormat::SdpVideoFormat(const std::string& name, 116 const CodecParameterMap& parameters) 117 : name(name), parameters(parameters) {} 118 119 SdpVideoFormat::SdpVideoFormat( 120 const std::string& name, 121 const CodecParameterMap& parameters, 122 const absl::InlinedVector<ScalabilityMode, kScalabilityModeCount>& 123 scalability_modes) 124 : name(name), 125 parameters(parameters), 126 scalability_modes(scalability_modes) {} 127 128 SdpVideoFormat::SdpVideoFormat( 129 const SdpVideoFormat& format, 130 const absl::InlinedVector<ScalabilityMode, kScalabilityModeCount>& modes) 131 : SdpVideoFormat(format) { 132 scalability_modes = modes; 133 } 134 135 SdpVideoFormat::SdpVideoFormat(const SdpVideoFormat&) = default; 136 SdpVideoFormat::SdpVideoFormat(SdpVideoFormat&&) = default; 137 SdpVideoFormat& SdpVideoFormat::operator=(const SdpVideoFormat&) = default; 138 SdpVideoFormat& SdpVideoFormat::operator=(SdpVideoFormat&&) = default; 139 140 SdpVideoFormat::~SdpVideoFormat() = default; 141 142 std::string SdpVideoFormat::ToString() const { 143 StringBuilder builder; 144 builder << "Codec name: " << name << ", parameters: {"; 145 for (const auto& kv : parameters) { 146 builder << " " << kv.first << "=" << kv.second; 147 } 148 149 builder << " }"; 150 if (!scalability_modes.empty()) { 151 builder << ", scalability_modes: ["; 152 bool first = true; 153 for (const auto scalability_mode : scalability_modes) { 154 if (first) { 155 first = false; 156 } else { 157 builder << ", "; 158 } 159 builder << ScalabilityModeToString(scalability_mode); 160 } 161 builder << "]"; 162 } 163 164 return builder.Release(); 165 } 166 167 bool SdpVideoFormat::IsSameCodec(const SdpVideoFormat& other) const { 168 // Two codecs are considered the same if the name matches (case insensitive) 169 // and certain codec-specific parameters match. 170 return absl::EqualsIgnoreCase(name, other.name) && 171 IsSameCodecSpecific(name, parameters, other.name, other.parameters); 172 } 173 174 bool SdpVideoFormat::IsCodecInList( 175 ArrayView<const SdpVideoFormat> formats) const { 176 for (const auto& format : formats) { 177 if (IsSameCodec(format)) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 bool operator==(const SdpVideoFormat& a, const SdpVideoFormat& b) { 185 return a.name == b.name && a.parameters == b.parameters && 186 a.scalability_modes == b.scalability_modes; 187 } 188 189 const SdpVideoFormat SdpVideoFormat::VP8() { 190 return SdpVideoFormat(kVp8CodecName, {}); 191 } 192 193 const SdpVideoFormat SdpVideoFormat::H264() { 194 // H264 will typically require more tweaking like setting 195 // * packetization-mode (which defaults to 0 but 1 is more common) 196 // * level-asymmetry-allowed (which defaults to 0 but 1 is more common) 197 // * profile-level-id of which there are many. 198 return SdpVideoFormat(kH264CodecName, {}); 199 } 200 201 const SdpVideoFormat SdpVideoFormat::H265() { 202 return SdpVideoFormat(kH265CodecName, {}); 203 } 204 205 const SdpVideoFormat SdpVideoFormat::VP9Profile0() { 206 return SdpVideoFormat( 207 kVp9CodecName, 208 {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile0)}}); 209 } 210 211 const SdpVideoFormat SdpVideoFormat::VP9Profile1() { 212 return SdpVideoFormat( 213 kVp9CodecName, 214 {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile1)}}); 215 } 216 217 const SdpVideoFormat SdpVideoFormat::VP9Profile2() { 218 return SdpVideoFormat( 219 kVp9CodecName, 220 {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile2)}}); 221 } 222 223 const SdpVideoFormat SdpVideoFormat::VP9Profile3() { 224 return SdpVideoFormat( 225 kVp9CodecName, 226 {{kVP9FmtpProfileId, VP9ProfileToString(VP9Profile::kProfile3)}}); 227 } 228 229 const SdpVideoFormat SdpVideoFormat::AV1Profile0() { 230 // https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters 231 return SdpVideoFormat( 232 kAv1CodecName, 233 {{kAv1FmtpProfile, AV1ProfileToString(AV1Profile::kProfile0).data()}, 234 {kAv1FmtpLevelIdx, "5"}, 235 {kAv1FmtpTier, "0"}}); 236 } 237 238 const SdpVideoFormat SdpVideoFormat::AV1Profile1() { 239 // https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters 240 return SdpVideoFormat( 241 kAv1CodecName, 242 {{kAv1FmtpProfile, AV1ProfileToString(AV1Profile::kProfile1).data()}, 243 {kAv1FmtpLevelIdx, "5"}, 244 {kAv1FmtpTier, "0"}}); 245 } 246 247 std::optional<SdpVideoFormat> FuzzyMatchSdpVideoFormat( 248 ArrayView<const SdpVideoFormat> supported_formats, 249 const SdpVideoFormat& format) { 250 std::optional<SdpVideoFormat> res; 251 int best_parameter_match = 0; 252 for (const auto& supported_format : supported_formats) { 253 if (absl::EqualsIgnoreCase(supported_format.name, format.name)) { 254 int matching_parameters = 0; 255 for (const auto& kv : supported_format.parameters) { 256 auto it = format.parameters.find(kv.first); 257 if (it != format.parameters.end() && it->second == kv.second) { 258 matching_parameters += 1; 259 } 260 } 261 262 if (!res || matching_parameters > best_parameter_match) { 263 res = supported_format; 264 best_parameter_match = matching_parameters; 265 } 266 } 267 } 268 269 if (!res) { 270 RTC_LOG(LS_INFO) << "Failed to match SdpVideoFormat " << format.ToString(); 271 } else if (*res != format) { 272 RTC_LOG(LS_INFO) << "Matched SdpVideoFormat " << format.ToString() 273 << " with " << res->ToString(); 274 } 275 276 return res; 277 } 278 279 } // namespace webrtc