1 #include "CodecSelectHelper.h"
2 
3 #include "platform/PlatformInterface.h"
4 
5 #include "media/base/media_constants.h"
6 #include "media/base/codec.h"
7 #include "absl/strings/match.h"
8 #include "rtc_base/logging.h"
9 #include "system_wrappers/include/field_trial.h"
10 
11 namespace tgcalls {
12 namespace {
13 
14 using VideoFormat = webrtc::SdpVideoFormat;
15 
CompareFormats(const VideoFormat & a,const VideoFormat & b)16 bool CompareFormats(const VideoFormat &a, const VideoFormat &b) {
17 	if (a.name < b.name) {
18 		return true;
19 	} else if (b.name < a.name) {
20 		return false;
21 	} else {
22 		return a.parameters < b.parameters;
23 	}
24 }
25 
FormatPriority(const VideoFormat & format,const std::vector<std::string> & preferredCodecs)26 int FormatPriority(const VideoFormat &format, const std::vector<std::string> &preferredCodecs) {
27 	static const auto kCodecs = {
28 		std::string(cricket::kAv1CodecName),
29 #ifndef WEBRTC_DISABLE_H265
30 		std::string(cricket::kH265CodecName),
31 #endif
32 		std::string(cricket::kH264CodecName),
33 		std::string(cricket::kVp8CodecName),
34         std::string(cricket::kVp9CodecName),
35 	};
36 	static const auto kSupported = [] {
37 		const auto platform = PlatformInterface::SharedInstance();
38 
39 		auto result = std::vector<std::string>();
40 		result.reserve(kCodecs.size());
41 		for (const auto &codec : kCodecs) {
42 			if (platform->supportsEncoding(codec)) {
43 				result.push_back(codec);
44 			}
45 		}
46 		return result;
47 	}();
48 
49     for (int i = 0; i < preferredCodecs.size(); i++) {
50         for (const auto &name : kSupported) {
51             if (absl::EqualsIgnoreCase(format.name, preferredCodecs[i]) && absl::EqualsIgnoreCase(format.name, name)) {
52                 return i;
53             }
54         }
55     }
56 
57     auto result = (int)preferredCodecs.size();
58 	for (const auto &name : kSupported) {
59 		if (absl::EqualsIgnoreCase(format.name, name)) {
60 			return result;
61 		}
62 		++result;
63 	}
64 	return -1;
65 }
66 
ComparePriorities(const VideoFormat & a,const VideoFormat & b,const std::vector<std::string> & preferredCodecs)67 bool ComparePriorities(const VideoFormat &a, const VideoFormat &b, const std::vector<std::string> &preferredCodecs) {
68 	return FormatPriority(a, preferredCodecs) < FormatPriority(b, preferredCodecs);
69 }
70 
FilterAndSortEncoders(std::vector<VideoFormat> list,const std::vector<std::string> & preferredCodecs)71 std::vector<VideoFormat> FilterAndSortEncoders(std::vector<VideoFormat> list, const std::vector<std::string> &preferredCodecs) {
72 	const auto listBegin = begin(list);
73 	const auto listEnd = end(list);
74     std::sort(listBegin, listEnd, [&preferredCodecs](const VideoFormat &lhs, const VideoFormat &rhs) {
75         return ComparePriorities(lhs, rhs, preferredCodecs);
76     });
77 	auto eraseFrom = listBegin;
78 	auto eraseTill = eraseFrom;
79 	while (eraseTill != listEnd && FormatPriority(*eraseTill, preferredCodecs) == -1) {
80 		++eraseTill;
81 	}
82 	if (eraseTill != eraseFrom) {
83 		list.erase(eraseFrom, eraseTill);
84 	}
85 	return list;
86 }
87 
AppendUnique(std::vector<VideoFormat> list,std::vector<VideoFormat> other)88 std::vector<VideoFormat> AppendUnique(
89 		std::vector<VideoFormat> list,
90 		std::vector<VideoFormat> other) {
91 	if (list.empty()) {
92 		return other;
93 	}
94 	list.reserve(list.size() + other.size());
95 	const auto oldBegin = &list[0];
96 	const auto oldEnd = oldBegin + list.size();
97 	for (auto &format : other) {
98 		if (std::find(oldBegin, oldEnd, format) == oldEnd) {
99 			list.push_back(std::move(format));
100 		}
101 	}
102 	return list;
103 }
104 
FindEqualFormat(const std::vector<VideoFormat> & list,const VideoFormat & format)105 std::vector<VideoFormat>::const_iterator FindEqualFormat(
106 		const std::vector<VideoFormat> &list,
107 		const VideoFormat &format) {
108 	return std::find_if(list.begin(), list.end(), [&](const VideoFormat &other) {
109 		return cricket::IsSameCodec(
110 			format.name,
111 			format.parameters,
112 			other.name,
113 			other.parameters);
114 	});
115 }
116 
AddDefaultFeedbackParams(cricket::VideoCodec * codec)117 void AddDefaultFeedbackParams(cricket::VideoCodec *codec) {
118 	// Don't add any feedback params for RED and ULPFEC.
119 	if (codec->name == cricket::kRedCodecName || codec->name == cricket::kUlpfecCodecName)
120 		return;
121 	codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty));
122 	codec->AddFeedbackParam(
123 		cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc, cricket::kParamValueEmpty));
124 	// Don't add any more feedback params for FLEXFEC.
125 	if (codec->name == cricket::kFlexfecCodecName)
126 		return;
127 	codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir));
128 	codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
129 	codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli));
130 	if (codec->name == cricket::kVp8CodecName &&
131 		webrtc::field_trial::IsEnabled("WebRTC-RtcpLossNotification")) {
132 		codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamLntf, cricket::kParamValueEmpty));
133 	}
134 }
135 
136 } // namespace
137 
ComposeSupportedFormats(std::vector<VideoFormat> encoders,std::vector<VideoFormat> decoders,const std::vector<std::string> & preferredCodecs)138 VideoFormatsMessage ComposeSupportedFormats(
139 		std::vector<VideoFormat> encoders,
140 		std::vector<VideoFormat> decoders,
141         const std::vector<std::string> &preferredCodecs) {
142 	encoders = FilterAndSortEncoders(std::move(encoders), preferredCodecs);
143 
144 	auto result = VideoFormatsMessage();
145 	result.encodersCount = (int)encoders.size();
146 	result.formats = AppendUnique(std::move(encoders), std::move(decoders));
147 	for (const auto &format : result.formats) {
148 		RTC_LOG(LS_INFO) << "Format: " << format.ToString();
149 	}
150 	RTC_LOG(LS_INFO) << "First " << result.encodersCount << " formats are supported encoders.";
151 	return result;
152 }
153 
ComputeCommonFormats(const VideoFormatsMessage & my,VideoFormatsMessage their)154 CommonFormats ComputeCommonFormats(
155 		const VideoFormatsMessage &my,
156 		VideoFormatsMessage their) {
157 	assert(my.encodersCount <= my.formats.size());
158 	assert(their.encodersCount <= their.formats.size());
159 
160 	for (const auto &format : their.formats) {
161 		RTC_LOG(LS_INFO) << "Their format: " << format.ToString();
162 	}
163 	RTC_LOG(LS_INFO) << "Their first " << their.encodersCount << " formats are supported encoders.";
164 
165 	const auto myEncodersBegin = begin(my.formats);
166 	const auto myEncodersEnd = myEncodersBegin + my.encodersCount;
167 	const auto theirEncodersBegin = begin(their.formats);
168 	const auto theirEncodersEnd = theirEncodersBegin + their.encodersCount;
169 
170 	auto result = CommonFormats();
171 	const auto addUnique = [&](const VideoFormat &format) {
172 		const auto already = std::find(
173 			result.list.begin(),
174 			result.list.end(),
175 			format);
176 		if (already == result.list.end()) {
177 			result.list.push_back(format);
178 		}
179 	};
180 	const auto addCommonAndFindFirst = [&](
181 			std::vector<VideoFormat>::const_iterator begin,
182 			std::vector<VideoFormat>::const_iterator end,
183 			const std::vector<VideoFormat> &decoders) {
184 		auto first = VideoFormat(std::string());
185 		for (auto i = begin; i != end; ++i) {
186 			const auto &format = *i;
187 			const auto j = FindEqualFormat(decoders, format);
188 			if (j != decoders.end()) {
189 				if (first.name.empty()) {
190 					first = format;
191 				}
192 				addUnique(format);
193 				addUnique(*j);
194 			};
195 		}
196 		return first;
197 	};
198 
199 	result.list.reserve(my.formats.size() + their.formats.size());
200 	auto myEncoderFormat = addCommonAndFindFirst(
201 		myEncodersBegin,
202 		myEncodersEnd,
203 		their.formats);
204 	auto theirEncoderFormat = addCommonAndFindFirst(
205 		theirEncodersBegin,
206 		theirEncodersEnd,
207 		my.formats);
208 	std::sort(begin(result.list), end(result.list), CompareFormats);
209 	if (!myEncoderFormat.name.empty()) {
210 		const auto i = std::find(begin(result.list), end(result.list), myEncoderFormat);
211 		assert(i != end(result.list));
212 		result.myEncoderIndex = (i - begin(result.list));
213 	}
214 
215 	for (const auto &format : result.list) {
216 		RTC_LOG(LS_INFO) << "Common format: " << format.ToString();
217 	}
218 	RTC_LOG(LS_INFO) << "My encoder: " << (result.myEncoderIndex >= 0 ? result.list[result.myEncoderIndex].ToString() : "(null)");
219 	RTC_LOG(LS_INFO) << "Their encoder: " << (!theirEncoderFormat.name.empty() ? theirEncoderFormat.ToString() : "(null)");
220 
221 	return result;
222 }
223 
AssignPayloadTypesAndDefaultCodecs(CommonFormats && formats)224 CommonCodecs AssignPayloadTypesAndDefaultCodecs(CommonFormats &&formats) {
225 	if (formats.list.empty()) {
226 		return CommonCodecs();
227 	}
228 
229 	constexpr int kFirstDynamicPayloadType = 96;
230 	constexpr int kLastDynamicPayloadType = 127;
231 
232 	int payload_type = kFirstDynamicPayloadType;
233 
234 	formats.list.push_back(webrtc::SdpVideoFormat(cricket::kRedCodecName));
235 	formats.list.push_back(webrtc::SdpVideoFormat(cricket::kUlpfecCodecName));
236 
237 	if (true) {
238 		webrtc::SdpVideoFormat flexfec_format(cricket::kFlexfecCodecName);
239 		// This value is currently arbitrarily set to 10 seconds. (The unit
240 		// is microseconds.) This parameter MUST be present in the SDP, but
241 		// we never use the actual value anywhere in our code however.
242 		// TODO(brandtr): Consider honouring this value in the sender and receiver.
243 		flexfec_format.parameters = { {cricket::kFlexfecFmtpRepairWindow, "10000000"} };
244 		formats.list.push_back(flexfec_format);
245 	}
246 
247 	auto inputIndex = 0;
248 	auto result = CommonCodecs();
249 	result.list.reserve(2 * formats.list.size() - 2);
250 	for (const auto &format : formats.list) {
251 		cricket::VideoCodec codec(format);
252 		codec.id = payload_type;
253 		AddDefaultFeedbackParams(&codec);
254 
255 		if (inputIndex++ == formats.myEncoderIndex) {
256 			result.myEncoderIndex = result.list.size();
257 		}
258 		result.list.push_back(codec);
259 
260 		// Increment payload type.
261 		++payload_type;
262 		if (payload_type > kLastDynamicPayloadType) {
263 			RTC_LOG(LS_ERROR) << "Out of dynamic payload types, skipping the rest.";
264 			break;
265 		}
266 
267 		// Add associated RTX codec for non-FEC codecs.
268 		if (!absl::EqualsIgnoreCase(codec.name, cricket::kUlpfecCodecName) &&
269 			!absl::EqualsIgnoreCase(codec.name, cricket::kFlexfecCodecName)) {
270 			result.list.push_back(cricket::VideoCodec::CreateRtxCodec(payload_type, codec.id));
271 
272 			// Increment payload type.
273 			++payload_type;
274 			if (payload_type > kLastDynamicPayloadType) {
275 				RTC_LOG(LS_ERROR) << "Out of dynamic payload types, skipping the rest.";
276 				break;
277 			}
278 		}
279 	}
280 	return result;
281 }
282 
283 } // namespace tgcalls
284