codec.cc (13179B)
1 /* 2 * Copyright (c) 2004 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/codec.h" 12 13 #include <algorithm> 14 #include <cstddef> 15 #include <iterator> 16 #include <optional> 17 #include <string> 18 #include <vector> 19 20 #include "absl/algorithm/container.h" 21 #include "absl/strings/match.h" 22 #include "absl/strings/str_cat.h" 23 #include "api/audio_codecs/audio_format.h" 24 #include "api/media_types.h" 25 #include "api/rtp_parameters.h" 26 #include "api/video_codecs/h264_profile_level_id.h" 27 #include "api/video_codecs/sdp_video_format.h" 28 #ifdef RTC_ENABLE_H265 29 #include "api/video_codecs/h265_profile_tier_level.h" // IWYU pragma: keep 30 #endif 31 #include "media/base/codec_comparators.h" 32 #include "media/base/media_constants.h" 33 #include "rtc_base/checks.h" 34 #include "rtc_base/logging.h" 35 #include "rtc_base/string_encode.h" 36 #include "rtc_base/strings/string_builder.h" 37 38 namespace webrtc { 39 40 FeedbackParams::FeedbackParams() = default; 41 FeedbackParams::~FeedbackParams() = default; 42 43 bool FeedbackParam::operator==(const FeedbackParam& other) const { 44 return absl::EqualsIgnoreCase(other.id(), id()) && 45 absl::EqualsIgnoreCase(other.param(), param()); 46 } 47 48 bool FeedbackParams::operator==(const FeedbackParams& other) const { 49 return params_ == other.params_; 50 } 51 52 bool FeedbackParams::Has(const FeedbackParam& param) const { 53 return absl::c_linear_search(params_, param); 54 } 55 56 void FeedbackParams::Add(const FeedbackParam& param) { 57 if (param.id().empty()) { 58 return; 59 } 60 if (Has(param)) { 61 // Param already in `this`. 62 return; 63 } 64 params_.push_back(param); 65 RTC_CHECK(!HasDuplicateEntries()); 66 } 67 68 bool FeedbackParams::Remove(const FeedbackParam& param) { 69 if (!Has(param)) { 70 return false; 71 } 72 std::erase(params_, param); 73 return true; 74 } 75 76 void FeedbackParams::Intersect(const FeedbackParams& from) { 77 std::vector<FeedbackParam>::iterator iter_to = params_.begin(); 78 while (iter_to != params_.end()) { 79 if (!from.Has(*iter_to)) { 80 iter_to = params_.erase(iter_to); 81 } else { 82 ++iter_to; 83 } 84 } 85 } 86 87 bool FeedbackParams::HasDuplicateEntries() const { 88 for (std::vector<FeedbackParam>::const_iterator iter = params_.begin(); 89 iter != params_.end(); ++iter) { 90 for (std::vector<FeedbackParam>::const_iterator found = iter + 1; 91 found != params_.end(); ++found) { 92 if (*found == *iter) { 93 return true; 94 } 95 } 96 } 97 return false; 98 } 99 100 Codec::Codec(Type type, int id, const std::string& name, int clockrate) 101 : Codec(type, id, name, clockrate, 0) {} 102 Codec::Codec(Type type, 103 int id, 104 const std::string& name, 105 int clockrate, 106 size_t channels) 107 : type(type), 108 id(id), 109 name(name), 110 clockrate(clockrate), 111 bitrate(0), 112 channels(channels) { 113 RTC_DCHECK_GT(clockrate, 0); 114 } 115 116 Codec::Codec(Type type) 117 : Codec(type, 118 kIdNotSet, 119 "", 120 type == Type::kVideo ? kDefaultVideoClockRateHz 121 : kDefaultAudioClockRateHz) {} 122 123 Codec::Codec(const SdpAudioFormat& c) 124 : Codec(Type::kAudio, kIdNotSet, c.name, c.clockrate_hz, c.num_channels) { 125 params = c.parameters; 126 } 127 128 Codec::Codec(const SdpVideoFormat& c) 129 : Codec(Type::kVideo, kIdNotSet, c.name, kVideoCodecClockrate) { 130 params = c.parameters; 131 scalability_modes = c.scalability_modes; 132 } 133 134 Codec::Codec(const Codec& c) = default; 135 Codec::Codec(Codec&& c) = default; 136 Codec::~Codec() = default; 137 Codec& Codec::operator=(const Codec& c) = default; 138 Codec& Codec::operator=(Codec&& c) = default; 139 140 bool Codec::operator==(const Codec& c) const { 141 return type == c.type && this->id == c.id && // id is reserved in objective-c 142 name == c.name && clockrate == c.clockrate && params == c.params && 143 feedback_params == c.feedback_params && 144 (type == Type::kAudio 145 ? (bitrate == c.bitrate && channels == c.channels) 146 : (packetization == c.packetization)); 147 } 148 149 bool Codec::Matches(const Codec& codec) const { 150 return MatchesWithCodecRules(*this, codec); 151 } 152 153 bool Codec::MatchesRtpCodec(const RtpCodec& codec_capability) const { 154 RtpCodecParameters codec_parameters = ToCodecParameters(); 155 156 return codec_parameters.name == codec_capability.name && 157 codec_parameters.kind == codec_capability.kind && 158 codec_parameters.num_channels == codec_capability.num_channels && 159 codec_parameters.clock_rate == codec_capability.clock_rate && 160 (codec_parameters.name == kRtxCodecName || 161 codec_parameters.parameters == codec_capability.parameters); 162 } 163 164 bool Codec::GetParam(const std::string& key, std::string* out) const { 165 CodecParameterMap::const_iterator iter = params.find(key); 166 if (iter == params.end()) 167 return false; 168 *out = iter->second; 169 return true; 170 } 171 172 bool Codec::GetParam(const std::string& key, int* out) const { 173 CodecParameterMap::const_iterator iter = params.find(key); 174 if (iter == params.end()) 175 return false; 176 return FromString(iter->second, out); 177 } 178 179 void Codec::SetParam(const std::string& key, const std::string& value) { 180 params[key] = value; 181 } 182 183 void Codec::SetParam(const std::string& key, int value) { 184 params[key] = absl::StrCat(value); 185 } 186 187 bool Codec::RemoveParam(const std::string& key) { 188 return params.erase(key) == 1; 189 } 190 191 void Codec::AddFeedbackParam(const FeedbackParam& param) { 192 feedback_params.Add(param); 193 } 194 195 bool Codec::HasFeedbackParam(const FeedbackParam& param) const { 196 return feedback_params.Has(param); 197 } 198 199 void Codec::IntersectFeedbackParams(const Codec& other) { 200 feedback_params.Intersect(other.feedback_params); 201 } 202 203 webrtc::RtpCodecParameters Codec::ToCodecParameters() const { 204 RtpCodecParameters codec_params; 205 codec_params.payload_type = id; 206 codec_params.name = name; 207 codec_params.clock_rate = clockrate; 208 codec_params.parameters.insert(params.begin(), params.end()); 209 210 switch (type) { 211 case Type::kAudio: { 212 codec_params.num_channels = static_cast<int>(channels); 213 codec_params.kind = MediaType::AUDIO; 214 break; 215 } 216 case Type::kVideo: { 217 codec_params.kind = MediaType::VIDEO; 218 break; 219 } 220 } 221 222 return codec_params; 223 } 224 225 bool Codec::IsMediaCodec() const { 226 return !IsResiliencyCodec() && 227 !absl::EqualsIgnoreCase(name, kComfortNoiseCodecName); 228 } 229 230 bool Codec::IsResiliencyCodec() const { 231 return GetResiliencyType() != ResiliencyType::kNone; 232 } 233 234 Codec::ResiliencyType Codec::GetResiliencyType() const { 235 if (absl::EqualsIgnoreCase(name, kRedCodecName)) { 236 return ResiliencyType::kRed; 237 } 238 if (absl::EqualsIgnoreCase(name, kUlpfecCodecName)) { 239 return ResiliencyType::kUlpfec; 240 } 241 if (absl::EqualsIgnoreCase(name, kFlexfecCodecName)) { 242 return ResiliencyType::kFlexfec; 243 } 244 if (absl::EqualsIgnoreCase(name, kRtxCodecName)) { 245 return ResiliencyType::kRtx; 246 } 247 return ResiliencyType::kNone; 248 } 249 250 bool Codec::ValidateCodecFormat() const { 251 if (id < 0 || id > 127) { 252 RTC_LOG(LS_ERROR) << "Codec with invalid payload type: " << ToString(); 253 return false; 254 } 255 if (IsResiliencyCodec()) { 256 return true; 257 } 258 259 int min_bitrate = -1; 260 int max_bitrate = -1; 261 if (GetParam(kCodecParamMinBitrate, &min_bitrate) && 262 GetParam(kCodecParamMaxBitrate, &max_bitrate)) { 263 if (max_bitrate < min_bitrate) { 264 RTC_LOG(LS_ERROR) << "Codec with max < min bitrate: " << ToString(); 265 return false; 266 } 267 } 268 return true; 269 } 270 271 std::string Codec::ToString() const { 272 char buf[256]; 273 274 SimpleStringBuilder sb(buf); 275 switch (type) { 276 case Type::kAudio: { 277 sb << "AudioCodec[" << id << ":" << name << ":" << clockrate << ":" 278 << bitrate << ":" << channels << "]"; 279 break; 280 } 281 case Type::kVideo: { 282 sb << "VideoCodec[" << id << ":" << name; 283 if (packetization.has_value()) { 284 sb << ":" << *packetization; 285 } 286 sb << "]"; 287 break; 288 } 289 } 290 return sb.str(); 291 } 292 293 Codec CreateAudioRtxCodec(int rtx_payload_type, int associated_payload_type) { 294 Codec rtx_codec = CreateAudioCodec(rtx_payload_type, kRtxCodecName, 295 kDefaultAudioClockRateHz, 1); 296 rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type); 297 return rtx_codec; 298 } 299 300 Codec CreateVideoRtxCodec(int rtx_payload_type, int associated_payload_type) { 301 Codec rtx_codec = CreateVideoCodec(rtx_payload_type, kRtxCodecName); 302 rtx_codec.SetParam(kCodecParamAssociatedPayloadType, associated_payload_type); 303 return rtx_codec; 304 } 305 306 const Codec* FindCodecById(const std::vector<Codec>& codecs, int payload_type) { 307 for (const auto& codec : codecs) { 308 if (codec.id == payload_type) 309 return &codec; 310 } 311 return nullptr; 312 } 313 314 bool HasLntf(const Codec& codec) { 315 return codec.HasFeedbackParam( 316 FeedbackParam(kRtcpFbParamLntf, kParamValueEmpty)); 317 } 318 319 bool HasNack(const Codec& codec) { 320 return codec.HasFeedbackParam( 321 FeedbackParam(kRtcpFbParamNack, kParamValueEmpty)); 322 } 323 324 bool HasRemb(const Codec& codec) { 325 return codec.HasFeedbackParam( 326 FeedbackParam(kRtcpFbParamRemb, kParamValueEmpty)); 327 } 328 329 bool HasRrtr(const Codec& codec) { 330 return codec.HasFeedbackParam( 331 FeedbackParam(kRtcpFbParamRrtr, kParamValueEmpty)); 332 } 333 334 const Codec* FindMatchingVideoCodec(const std::vector<Codec>& supported_codecs, 335 const Codec& codec) { 336 SdpVideoFormat sdp_video_format{codec.name, codec.params}; 337 for (const Codec& supported_codec : supported_codecs) { 338 if (sdp_video_format.IsSameCodec( 339 {supported_codec.name, supported_codec.params})) { 340 return &supported_codec; 341 } 342 } 343 return nullptr; 344 } 345 346 std::vector<const Codec*> FindAllMatchingCodecs( 347 const std::vector<Codec>& supported_codecs, 348 const Codec& codec) { 349 std::vector<const Codec*> result; 350 SdpVideoFormat sdp(codec.name, codec.params); 351 for (const Codec& supported_codec : supported_codecs) { 352 if (sdp.IsSameCodec({supported_codec.name, supported_codec.params})) { 353 result.push_back(&supported_codec); 354 } 355 } 356 return result; 357 } 358 359 // If a decoder supports any H264 profile, it is implicitly assumed to also 360 // support constrained base line even though it's not explicitly listed. 361 void AddH264ConstrainedBaselineProfileToSupportedFormats( 362 std::vector<SdpVideoFormat>* supported_formats) { 363 std::vector<SdpVideoFormat> cbr_supported_formats; 364 365 // For any H264 supported profile, add the corresponding constrained baseline 366 // profile. 367 for (auto it = supported_formats->cbegin(); it != supported_formats->cend(); 368 ++it) { 369 if (it->name == kH264CodecName) { 370 const std::optional<H264ProfileLevelId> profile_level_id = 371 ParseSdpForH264ProfileLevelId(it->parameters); 372 if (profile_level_id && profile_level_id->profile != 373 H264Profile::kProfileConstrainedBaseline) { 374 SdpVideoFormat cbp_format = *it; 375 H264ProfileLevelId cbp_profile = *profile_level_id; 376 cbp_profile.profile = H264Profile::kProfileConstrainedBaseline; 377 cbp_format.parameters[kH264FmtpProfileLevelId] = 378 *H264ProfileLevelIdToString(cbp_profile); 379 cbr_supported_formats.push_back(cbp_format); 380 } 381 } 382 } 383 384 size_t original_size = supported_formats->size(); 385 // ...if it's not already in the list. 386 std::copy_if(cbr_supported_formats.begin(), cbr_supported_formats.end(), 387 std::back_inserter(*supported_formats), 388 [supported_formats](const SdpVideoFormat& format) { 389 return !format.IsCodecInList(*supported_formats); 390 }); 391 392 if (supported_formats->size() > original_size) { 393 RTC_LOG(LS_INFO) << "Explicitly added H264 constrained baseline to list " 394 "of supported formats."; 395 } 396 } 397 398 Codec CreateAudioCodec(int id, 399 const std::string& name, 400 int clockrate, 401 size_t channels) { 402 return Codec(Codec::Type::kAudio, id, name, clockrate, channels); 403 } 404 405 Codec CreateAudioCodec(const SdpAudioFormat& c) { 406 return Codec(c); 407 } 408 409 Codec CreateVideoCodec(const std::string& name) { 410 return CreateVideoCodec(Codec::kIdNotSet, name); 411 } 412 413 Codec CreateVideoCodec(int id, const std::string& name) { 414 Codec c(Codec::Type::kVideo, id, name, kVideoCodecClockrate); 415 if (absl::EqualsIgnoreCase(kH264CodecName, name)) { 416 // This default is set for all H.264 codecs created because 417 // that was the default before packetization mode support was added. 418 // TODO(hta): Move this to the places that create VideoCodecs from 419 // SDP or from knowledge of implementation capabilities. 420 c.SetParam(kH264FmtpPacketizationMode, "1"); 421 } 422 return c; 423 } 424 425 Codec CreateVideoCodec(const SdpVideoFormat& c) { 426 return Codec(c); 427 } 428 429 Codec CreateVideoCodec(int id, const SdpVideoFormat& sdp) { 430 Codec c = CreateVideoCodec(sdp); 431 c.id = id; 432 return c; 433 } 434 435 } // namespace webrtc