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 "absl/strings/match.h"
14 #include "absl/types/optional.h"
15 #include "api/array_view.h"
16 #include "api/video_codecs/av1_profile.h"
17 #include "api/video_codecs/h264_profile_level_id.h"
18 #include "api/video_codecs/video_codec.h"
19 #include "api/video_codecs/vp9_profile.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/logging.h"
22 #include "rtc_base/strings/string_builder.h"
23
24 namespace webrtc {
25
26 namespace {
27
H264GetPacketizationModeOrDefault(const SdpVideoFormat::Parameters & params)28 std::string H264GetPacketizationModeOrDefault(
29 const SdpVideoFormat::Parameters& params) {
30 constexpr char kH264FmtpPacketizationMode[] = "packetization-mode";
31 const auto it = params.find(kH264FmtpPacketizationMode);
32 if (it != params.end()) {
33 return it->second;
34 }
35 // If packetization-mode is not present, default to "0".
36 // https://tools.ietf.org/html/rfc6184#section-6.2
37 return "0";
38 }
39
H264IsSamePacketizationMode(const SdpVideoFormat::Parameters & left,const SdpVideoFormat::Parameters & right)40 bool H264IsSamePacketizationMode(const SdpVideoFormat::Parameters& left,
41 const SdpVideoFormat::Parameters& right) {
42 return H264GetPacketizationModeOrDefault(left) ==
43 H264GetPacketizationModeOrDefault(right);
44 }
45
46 // Some (video) codecs are actually families of codecs and rely on parameters
47 // to distinguish different incompatible family members.
IsSameCodecSpecific(const SdpVideoFormat & format1,const SdpVideoFormat & format2)48 bool IsSameCodecSpecific(const SdpVideoFormat& format1,
49 const SdpVideoFormat& format2) {
50 // The assumption when calling this function is that the two formats have the
51 // same name.
52 RTC_DCHECK(absl::EqualsIgnoreCase(format1.name, format2.name));
53
54 VideoCodecType codec_type = PayloadStringToCodecType(format1.name);
55 switch (codec_type) {
56 case kVideoCodecH264:
57 return H264IsSameProfile(format1.parameters, format2.parameters) &&
58 H264IsSamePacketizationMode(format1.parameters,
59 format2.parameters);
60 case kVideoCodecVP9:
61 return VP9IsSameProfile(format1.parameters, format2.parameters);
62 case kVideoCodecAV1:
63 return AV1IsSameProfile(format1.parameters, format2.parameters);
64 default:
65 return true;
66 }
67 }
68 } // namespace
69
SdpVideoFormat(const std::string & name)70 SdpVideoFormat::SdpVideoFormat(const std::string& name) : name(name) {}
71
SdpVideoFormat(const std::string & name,const Parameters & parameters)72 SdpVideoFormat::SdpVideoFormat(const std::string& name,
73 const Parameters& parameters)
74 : name(name), parameters(parameters) {}
75
SdpVideoFormat(const std::string & name,const Parameters & parameters,const absl::InlinedVector<ScalabilityMode,kScalabilityModeCount> & scalability_modes)76 SdpVideoFormat::SdpVideoFormat(
77 const std::string& name,
78 const Parameters& parameters,
79 const absl::InlinedVector<ScalabilityMode, kScalabilityModeCount>&
80 scalability_modes)
81 : name(name),
82 parameters(parameters),
83 scalability_modes(scalability_modes) {}
84
85 SdpVideoFormat::SdpVideoFormat(const SdpVideoFormat&) = default;
86 SdpVideoFormat::SdpVideoFormat(SdpVideoFormat&&) = default;
87 SdpVideoFormat& SdpVideoFormat::operator=(const SdpVideoFormat&) = default;
88 SdpVideoFormat& SdpVideoFormat::operator=(SdpVideoFormat&&) = default;
89
90 SdpVideoFormat::~SdpVideoFormat() = default;
91
ToString() const92 std::string SdpVideoFormat::ToString() const {
93 rtc::StringBuilder builder;
94 builder << "Codec name: " << name << ", parameters: {";
95 for (const auto& kv : parameters) {
96 builder << " " << kv.first << "=" << kv.second;
97 }
98
99 builder << " }";
100 if (!scalability_modes.empty()) {
101 builder << ", scalability_modes: [";
102 bool first = true;
103 for (const auto scalability_mode : scalability_modes) {
104 if (first) {
105 first = false;
106 } else {
107 builder << ", ";
108 }
109 builder << ScalabilityModeToString(scalability_mode);
110 }
111 builder << "]";
112 }
113
114 return builder.str();
115 }
116
IsSameCodec(const SdpVideoFormat & other) const117 bool SdpVideoFormat::IsSameCodec(const SdpVideoFormat& other) const {
118 // Two codecs are considered the same if the name matches (case insensitive)
119 // and certain codec-specific parameters match.
120 return absl::EqualsIgnoreCase(name, other.name) &&
121 IsSameCodecSpecific(*this, other);
122 }
123
IsCodecInList(rtc::ArrayView<const webrtc::SdpVideoFormat> formats) const124 bool SdpVideoFormat::IsCodecInList(
125 rtc::ArrayView<const webrtc::SdpVideoFormat> formats) const {
126 for (const auto& format : formats) {
127 if (IsSameCodec(format)) {
128 return true;
129 }
130 }
131 return false;
132 }
133
operator ==(const SdpVideoFormat & a,const SdpVideoFormat & b)134 bool operator==(const SdpVideoFormat& a, const SdpVideoFormat& b) {
135 return a.name == b.name && a.parameters == b.parameters &&
136 a.scalability_modes == b.scalability_modes;
137 }
138
FuzzyMatchSdpVideoFormat(rtc::ArrayView<const SdpVideoFormat> supported_formats,const SdpVideoFormat & format)139 absl::optional<SdpVideoFormat> FuzzyMatchSdpVideoFormat(
140 rtc::ArrayView<const SdpVideoFormat> supported_formats,
141 const SdpVideoFormat& format) {
142 absl::optional<SdpVideoFormat> res;
143 int best_parameter_match = 0;
144 for (const auto& supported_format : supported_formats) {
145 if (absl::EqualsIgnoreCase(supported_format.name, format.name)) {
146 int matching_parameters = 0;
147 for (const auto& kv : supported_format.parameters) {
148 auto it = format.parameters.find(kv.first);
149 if (it != format.parameters.end() && it->second == kv.second) {
150 matching_parameters += 1;
151 }
152 }
153
154 if (!res || matching_parameters > best_parameter_match) {
155 res = supported_format;
156 best_parameter_match = matching_parameters;
157 }
158 }
159 }
160
161 if (!res) {
162 RTC_LOG(LS_INFO) << "Failed to match SdpVideoFormat " << format.ToString();
163 } else if (*res != format) {
164 RTC_LOG(LS_INFO) << "Matched SdpVideoFormat " << format.ToString()
165 << " with " << res->ToString();
166 }
167
168 return res;
169 }
170
171 } // namespace webrtc
172