|  | /* | 
|  | *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "api/video_codecs/sdp_video_format.h" | 
|  |  | 
|  | #include "absl/strings/match.h" | 
|  | #include "absl/types/optional.h" | 
|  | #include "api/array_view.h" | 
|  | #include "api/video_codecs/av1_profile.h" | 
|  | #include "api/video_codecs/h264_profile_level_id.h" | 
|  | #include "api/video_codecs/video_codec.h" | 
|  | #include "api/video_codecs/vp9_profile.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "rtc_base/strings/string_builder.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | std::string H264GetPacketizationModeOrDefault( | 
|  | const SdpVideoFormat::Parameters& params) { | 
|  | constexpr char kH264FmtpPacketizationMode[] = "packetization-mode"; | 
|  | const auto it = params.find(kH264FmtpPacketizationMode); | 
|  | if (it != params.end()) { | 
|  | return it->second; | 
|  | } | 
|  | // If packetization-mode is not present, default to "0". | 
|  | // https://tools.ietf.org/html/rfc6184#section-6.2 | 
|  | return "0"; | 
|  | } | 
|  |  | 
|  | bool H264IsSamePacketizationMode(const SdpVideoFormat::Parameters& left, | 
|  | const SdpVideoFormat::Parameters& right) { | 
|  | return H264GetPacketizationModeOrDefault(left) == | 
|  | H264GetPacketizationModeOrDefault(right); | 
|  | } | 
|  |  | 
|  | // Some (video) codecs are actually families of codecs and rely on parameters | 
|  | // to distinguish different incompatible family members. | 
|  | bool IsSameCodecSpecific(const SdpVideoFormat& format1, | 
|  | const SdpVideoFormat& format2) { | 
|  | // The assumption when calling this function is that the two formats have the | 
|  | // same name. | 
|  | RTC_DCHECK(absl::EqualsIgnoreCase(format1.name, format2.name)); | 
|  |  | 
|  | VideoCodecType codec_type = PayloadStringToCodecType(format1.name); | 
|  | switch (codec_type) { | 
|  | case kVideoCodecH264: | 
|  | return H264IsSameProfile(format1.parameters, format2.parameters) && | 
|  | H264IsSamePacketizationMode(format1.parameters, | 
|  | format2.parameters); | 
|  | case kVideoCodecVP9: | 
|  | return VP9IsSameProfile(format1.parameters, format2.parameters); | 
|  | case kVideoCodecAV1: | 
|  | return AV1IsSameProfile(format1.parameters, format2.parameters); | 
|  | default: | 
|  | return true; | 
|  | } | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | SdpVideoFormat::SdpVideoFormat(const std::string& name) : name(name) {} | 
|  |  | 
|  | SdpVideoFormat::SdpVideoFormat(const std::string& name, | 
|  | const Parameters& parameters) | 
|  | : name(name), parameters(parameters) {} | 
|  |  | 
|  | SdpVideoFormat::SdpVideoFormat( | 
|  | const std::string& name, | 
|  | const Parameters& parameters, | 
|  | const absl::InlinedVector<ScalabilityMode, kScalabilityModeCount>& | 
|  | scalability_modes) | 
|  | : name(name), | 
|  | parameters(parameters), | 
|  | scalability_modes(scalability_modes) {} | 
|  |  | 
|  | SdpVideoFormat::SdpVideoFormat(const SdpVideoFormat&) = default; | 
|  | SdpVideoFormat::SdpVideoFormat(SdpVideoFormat&&) = default; | 
|  | SdpVideoFormat& SdpVideoFormat::operator=(const SdpVideoFormat&) = default; | 
|  | SdpVideoFormat& SdpVideoFormat::operator=(SdpVideoFormat&&) = default; | 
|  |  | 
|  | SdpVideoFormat::~SdpVideoFormat() = default; | 
|  |  | 
|  | std::string SdpVideoFormat::ToString() const { | 
|  | rtc::StringBuilder builder; | 
|  | builder << "Codec name: " << name << ", parameters: {"; | 
|  | for (const auto& kv : parameters) { | 
|  | builder << " " << kv.first << "=" << kv.second; | 
|  | } | 
|  |  | 
|  | builder << " }"; | 
|  | if (!scalability_modes.empty()) { | 
|  | builder << ", scalability_modes: ["; | 
|  | bool first = true; | 
|  | for (const auto scalability_mode : scalability_modes) { | 
|  | if (first) { | 
|  | first = false; | 
|  | } else { | 
|  | builder << ", "; | 
|  | } | 
|  | builder << ScalabilityModeToString(scalability_mode); | 
|  | } | 
|  | builder << "]"; | 
|  | } | 
|  |  | 
|  | return builder.str(); | 
|  | } | 
|  |  | 
|  | bool SdpVideoFormat::IsSameCodec(const SdpVideoFormat& other) const { | 
|  | // Two codecs are considered the same if the name matches (case insensitive) | 
|  | // and certain codec-specific parameters match. | 
|  | return absl::EqualsIgnoreCase(name, other.name) && | 
|  | IsSameCodecSpecific(*this, other); | 
|  | } | 
|  |  | 
|  | bool SdpVideoFormat::IsCodecInList( | 
|  | rtc::ArrayView<const webrtc::SdpVideoFormat> formats) const { | 
|  | for (const auto& format : formats) { | 
|  | if (IsSameCodec(format)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool operator==(const SdpVideoFormat& a, const SdpVideoFormat& b) { | 
|  | return a.name == b.name && a.parameters == b.parameters && | 
|  | a.scalability_modes == b.scalability_modes; | 
|  | } | 
|  |  | 
|  | absl::optional<SdpVideoFormat> FuzzyMatchSdpVideoFormat( | 
|  | rtc::ArrayView<const SdpVideoFormat> supported_formats, | 
|  | const SdpVideoFormat& format) { | 
|  | absl::optional<SdpVideoFormat> res; | 
|  | int best_parameter_match = 0; | 
|  | for (const auto& supported_format : supported_formats) { | 
|  | if (absl::EqualsIgnoreCase(supported_format.name, format.name)) { | 
|  | int matching_parameters = 0; | 
|  | for (const auto& kv : supported_format.parameters) { | 
|  | auto it = format.parameters.find(kv.first); | 
|  | if (it != format.parameters.end() && it->second == kv.second) { | 
|  | matching_parameters += 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!res || matching_parameters > best_parameter_match) { | 
|  | res = supported_format; | 
|  | best_parameter_match = matching_parameters; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!res) { | 
|  | RTC_LOG(LS_INFO) << "Failed to match SdpVideoFormat " << format.ToString(); | 
|  | } else if (*res != format) { | 
|  | RTC_LOG(LS_INFO) << "Matched SdpVideoFormat " << format.ToString() | 
|  | << " with " << res->ToString(); | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |