1 /*
2 * Copyright 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 "pc/media_session.h"
12
13 #include <stddef.h>
14
15 #include <algorithm>
16 #include <map>
17 #include <string>
18 #include <unordered_map>
19 #include <utility>
20
21 #include "absl/algorithm/container.h"
22 #include "absl/strings/match.h"
23 #include "absl/strings/string_view.h"
24 #include "absl/types/optional.h"
25 #include "api/crypto_params.h"
26 #include "media/base/codec.h"
27 #include "media/base/media_constants.h"
28 #include "media/base/media_engine.h"
29 #include "media/base/sdp_video_format_utils.h"
30 #include "media/sctp/sctp_transport_internal.h"
31 #include "p2p/base/p2p_constants.h"
32 #include "pc/media_protocol_names.h"
33 #include "pc/rtp_media_utils.h"
34 #include "pc/used_ids.h"
35 #include "rtc_base/checks.h"
36 #include "rtc_base/helpers.h"
37 #include "rtc_base/logging.h"
38 #include "rtc_base/ssl_stream_adapter.h"
39 #include "rtc_base/string_encode.h"
40 #include "rtc_base/third_party/base64/base64.h"
41 #include "rtc_base/unique_id_generator.h"
42
43 namespace {
44
45 using rtc::UniqueRandomIdGenerator;
46 using webrtc::RtpTransceiverDirection;
47
48 const char kInline[] = "inline:";
49
GetSupportedSdesCryptoSuiteNames(void (* func)(const webrtc::CryptoOptions &,std::vector<int> *),const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * names)50 void GetSupportedSdesCryptoSuiteNames(
51 void (*func)(const webrtc::CryptoOptions&, std::vector<int>*),
52 const webrtc::CryptoOptions& crypto_options,
53 std::vector<std::string>* names) {
54 std::vector<int> crypto_suites;
55 func(crypto_options, &crypto_suites);
56 for (const auto crypto : crypto_suites) {
57 names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
58 }
59 }
60
RtpExtensionFromCapability(const webrtc::RtpHeaderExtensionCapability & capability)61 webrtc::RtpExtension RtpExtensionFromCapability(
62 const webrtc::RtpHeaderExtensionCapability& capability) {
63 return webrtc::RtpExtension(capability.uri,
64 capability.preferred_id.value_or(1));
65 }
66
RtpHeaderExtensionsFromCapabilities(const std::vector<webrtc::RtpHeaderExtensionCapability> & capabilities)67 cricket::RtpHeaderExtensions RtpHeaderExtensionsFromCapabilities(
68 const std::vector<webrtc::RtpHeaderExtensionCapability>& capabilities) {
69 cricket::RtpHeaderExtensions exts;
70 for (const auto& capability : capabilities) {
71 exts.push_back(RtpExtensionFromCapability(capability));
72 }
73 return exts;
74 }
75
76 std::vector<webrtc::RtpHeaderExtensionCapability>
UnstoppedRtpHeaderExtensionCapabilities(std::vector<webrtc::RtpHeaderExtensionCapability> capabilities)77 UnstoppedRtpHeaderExtensionCapabilities(
78 std::vector<webrtc::RtpHeaderExtensionCapability> capabilities) {
79 capabilities.erase(
80 std::remove_if(
81 capabilities.begin(), capabilities.end(),
82 [](const webrtc::RtpHeaderExtensionCapability& capability) {
83 return capability.direction == RtpTransceiverDirection::kStopped;
84 }),
85 capabilities.end());
86 return capabilities;
87 }
88
IsCapabilityPresent(const webrtc::RtpHeaderExtensionCapability & capability,const cricket::RtpHeaderExtensions & extensions)89 bool IsCapabilityPresent(const webrtc::RtpHeaderExtensionCapability& capability,
90 const cricket::RtpHeaderExtensions& extensions) {
91 return std::find_if(extensions.begin(), extensions.end(),
92 [&capability](const webrtc::RtpExtension& extension) {
93 return capability.uri == extension.uri;
94 }) != extensions.end();
95 }
96
UnstoppedOrPresentRtpHeaderExtensions(const std::vector<webrtc::RtpHeaderExtensionCapability> & capabilities,const cricket::RtpHeaderExtensions & unencrypted,const cricket::RtpHeaderExtensions & encrypted)97 cricket::RtpHeaderExtensions UnstoppedOrPresentRtpHeaderExtensions(
98 const std::vector<webrtc::RtpHeaderExtensionCapability>& capabilities,
99 const cricket::RtpHeaderExtensions& unencrypted,
100 const cricket::RtpHeaderExtensions& encrypted) {
101 cricket::RtpHeaderExtensions extensions;
102 for (const auto& capability : capabilities) {
103 if (capability.direction != RtpTransceiverDirection::kStopped ||
104 IsCapabilityPresent(capability, unencrypted) ||
105 IsCapabilityPresent(capability, encrypted)) {
106 extensions.push_back(RtpExtensionFromCapability(capability));
107 }
108 }
109 return extensions;
110 }
111
112 } // namespace
113
114 namespace cricket {
115
NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,RtpTransceiverDirection wants)116 static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
117 RtpTransceiverDirection offer,
118 RtpTransceiverDirection wants) {
119 bool offer_send = webrtc::RtpTransceiverDirectionHasSend(offer);
120 bool offer_recv = webrtc::RtpTransceiverDirectionHasRecv(offer);
121 bool wants_send = webrtc::RtpTransceiverDirectionHasSend(wants);
122 bool wants_recv = webrtc::RtpTransceiverDirectionHasRecv(wants);
123 return webrtc::RtpTransceiverDirectionFromSendRecv(offer_recv && wants_send,
124 offer_send && wants_recv);
125 }
126
IsMediaContentOfType(const ContentInfo * content,MediaType media_type)127 static bool IsMediaContentOfType(const ContentInfo* content,
128 MediaType media_type) {
129 if (!content || !content->media_description()) {
130 return false;
131 }
132 return content->media_description()->type() == media_type;
133 }
134
CreateCryptoParams(int tag,const std::string & cipher,CryptoParams * crypto_out)135 static bool CreateCryptoParams(int tag,
136 const std::string& cipher,
137 CryptoParams* crypto_out) {
138 int key_len;
139 int salt_len;
140 if (!rtc::GetSrtpKeyAndSaltLengths(rtc::SrtpCryptoSuiteFromName(cipher),
141 &key_len, &salt_len)) {
142 return false;
143 }
144
145 int master_key_len = key_len + salt_len;
146 std::string master_key;
147 if (!rtc::CreateRandomData(master_key_len, &master_key)) {
148 return false;
149 }
150
151 RTC_CHECK_EQ(master_key_len, master_key.size());
152 std::string key = rtc::Base64::Encode(master_key);
153
154 crypto_out->tag = tag;
155 crypto_out->cipher_suite = cipher;
156 crypto_out->key_params = kInline;
157 crypto_out->key_params += key;
158 return true;
159 }
160
AddCryptoParams(const std::string & cipher_suite,CryptoParamsVec * cryptos_out)161 static bool AddCryptoParams(const std::string& cipher_suite,
162 CryptoParamsVec* cryptos_out) {
163 int size = static_cast<int>(cryptos_out->size());
164
165 cryptos_out->resize(size + 1);
166 return CreateCryptoParams(size, cipher_suite, &cryptos_out->at(size));
167 }
168
AddMediaCryptos(const CryptoParamsVec & cryptos,MediaContentDescription * media)169 void AddMediaCryptos(const CryptoParamsVec& cryptos,
170 MediaContentDescription* media) {
171 for (const CryptoParams& crypto : cryptos) {
172 media->AddCrypto(crypto);
173 }
174 }
175
CreateMediaCryptos(const std::vector<std::string> & crypto_suites,MediaContentDescription * media)176 bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
177 MediaContentDescription* media) {
178 CryptoParamsVec cryptos;
179 for (const std::string& crypto_suite : crypto_suites) {
180 if (!AddCryptoParams(crypto_suite, &cryptos)) {
181 return false;
182 }
183 }
184 AddMediaCryptos(cryptos, media);
185 return true;
186 }
187
GetCryptos(const ContentInfo * content)188 const CryptoParamsVec* GetCryptos(const ContentInfo* content) {
189 if (!content || !content->media_description()) {
190 return nullptr;
191 }
192 return &content->media_description()->cryptos();
193 }
194
FindMatchingCrypto(const CryptoParamsVec & cryptos,const CryptoParams & crypto,CryptoParams * crypto_out)195 bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
196 const CryptoParams& crypto,
197 CryptoParams* crypto_out) {
198 auto it = absl::c_find_if(
199 cryptos, [&crypto](const CryptoParams& c) { return crypto.Matches(c); });
200 if (it == cryptos.end()) {
201 return false;
202 }
203 *crypto_out = *it;
204 return true;
205 }
206
207 // For audio, HMAC 32 (if enabled) is prefered over HMAC 80 because of the
208 // low overhead.
GetSupportedAudioSdesCryptoSuites(const webrtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)209 void GetSupportedAudioSdesCryptoSuites(
210 const webrtc::CryptoOptions& crypto_options,
211 std::vector<int>* crypto_suites) {
212 if (crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher) {
213 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_32);
214 }
215 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
216 if (crypto_options.srtp.enable_gcm_crypto_suites) {
217 crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
218 crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
219 }
220 }
221
GetSupportedAudioSdesCryptoSuiteNames(const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)222 void GetSupportedAudioSdesCryptoSuiteNames(
223 const webrtc::CryptoOptions& crypto_options,
224 std::vector<std::string>* crypto_suite_names) {
225 GetSupportedSdesCryptoSuiteNames(GetSupportedAudioSdesCryptoSuites,
226 crypto_options, crypto_suite_names);
227 }
228
GetSupportedVideoSdesCryptoSuites(const webrtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)229 void GetSupportedVideoSdesCryptoSuites(
230 const webrtc::CryptoOptions& crypto_options,
231 std::vector<int>* crypto_suites) {
232 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
233 if (crypto_options.srtp.enable_gcm_crypto_suites) {
234 crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
235 crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
236 }
237 }
238
GetSupportedVideoSdesCryptoSuiteNames(const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)239 void GetSupportedVideoSdesCryptoSuiteNames(
240 const webrtc::CryptoOptions& crypto_options,
241 std::vector<std::string>* crypto_suite_names) {
242 GetSupportedSdesCryptoSuiteNames(GetSupportedVideoSdesCryptoSuites,
243 crypto_options, crypto_suite_names);
244 }
245
GetSupportedDataSdesCryptoSuites(const webrtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)246 void GetSupportedDataSdesCryptoSuites(
247 const webrtc::CryptoOptions& crypto_options,
248 std::vector<int>* crypto_suites) {
249 crypto_suites->push_back(rtc::kSrtpAes128CmSha1_80);
250 if (crypto_options.srtp.enable_gcm_crypto_suites) {
251 crypto_suites->push_back(rtc::kSrtpAeadAes256Gcm);
252 crypto_suites->push_back(rtc::kSrtpAeadAes128Gcm);
253 }
254 }
255
GetSupportedDataSdesCryptoSuiteNames(const webrtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)256 void GetSupportedDataSdesCryptoSuiteNames(
257 const webrtc::CryptoOptions& crypto_options,
258 std::vector<std::string>* crypto_suite_names) {
259 GetSupportedSdesCryptoSuiteNames(GetSupportedDataSdesCryptoSuites,
260 crypto_options, crypto_suite_names);
261 }
262
263 // Support any GCM cipher (if enabled through options). For video support only
264 // 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated (if enabled) unless
265 // bundle is enabled because it is low overhead.
266 // Pick the crypto in the list that is supported.
SelectCrypto(const MediaContentDescription * offer,bool bundle,const webrtc::CryptoOptions & crypto_options,CryptoParams * crypto_out)267 static bool SelectCrypto(const MediaContentDescription* offer,
268 bool bundle,
269 const webrtc::CryptoOptions& crypto_options,
270 CryptoParams* crypto_out) {
271 bool audio = offer->type() == MEDIA_TYPE_AUDIO;
272 const CryptoParamsVec& cryptos = offer->cryptos();
273
274 for (const CryptoParams& crypto : cryptos) {
275 if ((crypto_options.srtp.enable_gcm_crypto_suites &&
276 rtc::IsGcmCryptoSuiteName(crypto.cipher_suite)) ||
277 rtc::kCsAesCm128HmacSha1_80 == crypto.cipher_suite ||
278 (rtc::kCsAesCm128HmacSha1_32 == crypto.cipher_suite && audio &&
279 !bundle && crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher)) {
280 return CreateCryptoParams(crypto.tag, crypto.cipher_suite, crypto_out);
281 }
282 }
283 return false;
284 }
285
286 // Finds all StreamParams of all media types and attach them to stream_params.
GetCurrentStreamParams(const std::vector<const ContentInfo * > & active_local_contents)287 static StreamParamsVec GetCurrentStreamParams(
288 const std::vector<const ContentInfo*>& active_local_contents) {
289 StreamParamsVec stream_params;
290 for (const ContentInfo* content : active_local_contents) {
291 for (const StreamParams& params : content->media_description()->streams()) {
292 stream_params.push_back(params);
293 }
294 }
295 return stream_params;
296 }
297
CreateStreamParamsForNewSenderWithSsrcs(const SenderOptions & sender,const std::string & rtcp_cname,bool include_rtx_streams,bool include_flexfec_stream,UniqueRandomIdGenerator * ssrc_generator,const webrtc::FieldTrialsView & field_trials)298 static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
299 const SenderOptions& sender,
300 const std::string& rtcp_cname,
301 bool include_rtx_streams,
302 bool include_flexfec_stream,
303 UniqueRandomIdGenerator* ssrc_generator,
304 const webrtc::FieldTrialsView& field_trials) {
305 StreamParams result;
306 result.id = sender.track_id;
307
308 // TODO(brandtr): Update when we support multistream protection.
309 if (include_flexfec_stream && sender.num_sim_layers > 1) {
310 include_flexfec_stream = false;
311 RTC_LOG(LS_WARNING)
312 << "Our FlexFEC implementation only supports protecting "
313 "a single media streams. This session has multiple "
314 "media streams however, so no FlexFEC SSRC will be generated.";
315 }
316 if (include_flexfec_stream && !field_trials.IsEnabled("WebRTC-FlexFEC-03")) {
317 include_flexfec_stream = false;
318 RTC_LOG(LS_WARNING)
319 << "WebRTC-FlexFEC trial is not enabled, not sending FlexFEC";
320 }
321
322 result.GenerateSsrcs(sender.num_sim_layers, include_rtx_streams,
323 include_flexfec_stream, ssrc_generator);
324
325 result.cname = rtcp_cname;
326 result.set_stream_ids(sender.stream_ids);
327
328 return result;
329 }
330
ValidateSimulcastLayers(const std::vector<RidDescription> & rids,const SimulcastLayerList & simulcast_layers)331 static bool ValidateSimulcastLayers(
332 const std::vector<RidDescription>& rids,
333 const SimulcastLayerList& simulcast_layers) {
334 return absl::c_all_of(
335 simulcast_layers.GetAllLayers(), [&rids](const SimulcastLayer& layer) {
336 return absl::c_any_of(rids, [&layer](const RidDescription& rid) {
337 return rid.rid == layer.rid;
338 });
339 });
340 }
341
CreateStreamParamsForNewSenderWithRids(const SenderOptions & sender,const std::string & rtcp_cname)342 static StreamParams CreateStreamParamsForNewSenderWithRids(
343 const SenderOptions& sender,
344 const std::string& rtcp_cname) {
345 RTC_DCHECK(!sender.rids.empty());
346 RTC_DCHECK_EQ(sender.num_sim_layers, 0)
347 << "RIDs are the compliant way to indicate simulcast.";
348 RTC_DCHECK(ValidateSimulcastLayers(sender.rids, sender.simulcast_layers));
349 StreamParams result;
350 result.id = sender.track_id;
351 result.cname = rtcp_cname;
352 result.set_stream_ids(sender.stream_ids);
353
354 // More than one rid should be signaled.
355 if (sender.rids.size() > 1) {
356 result.set_rids(sender.rids);
357 }
358
359 return result;
360 }
361
362 // Adds SimulcastDescription if indicated by the media description options.
363 // MediaContentDescription should already be set up with the send rids.
AddSimulcastToMediaDescription(const MediaDescriptionOptions & media_description_options,MediaContentDescription * description)364 static void AddSimulcastToMediaDescription(
365 const MediaDescriptionOptions& media_description_options,
366 MediaContentDescription* description) {
367 RTC_DCHECK(description);
368
369 // Check if we are using RIDs in this scenario.
370 if (absl::c_all_of(description->streams(), [](const StreamParams& params) {
371 return !params.has_rids();
372 })) {
373 return;
374 }
375
376 RTC_DCHECK_EQ(1, description->streams().size())
377 << "RIDs are only supported in Unified Plan semantics.";
378 RTC_DCHECK_EQ(1, media_description_options.sender_options.size());
379 RTC_DCHECK(description->type() == MediaType::MEDIA_TYPE_AUDIO ||
380 description->type() == MediaType::MEDIA_TYPE_VIDEO);
381
382 // One RID or less indicates that simulcast is not needed.
383 if (description->streams()[0].rids().size() <= 1) {
384 return;
385 }
386
387 // Only negotiate the send layers.
388 SimulcastDescription simulcast;
389 simulcast.send_layers() =
390 media_description_options.sender_options[0].simulcast_layers;
391 description->set_simulcast_description(simulcast);
392 }
393
394 // Adds a StreamParams for each SenderOptions in `sender_options` to
395 // content_description.
396 // `current_params` - All currently known StreamParams of any media type.
397 template <class C>
AddStreamParams(const std::vector<SenderOptions> & sender_options,const std::string & rtcp_cname,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * content_description,const webrtc::FieldTrialsView & field_trials)398 static bool AddStreamParams(const std::vector<SenderOptions>& sender_options,
399 const std::string& rtcp_cname,
400 UniqueRandomIdGenerator* ssrc_generator,
401 StreamParamsVec* current_streams,
402 MediaContentDescriptionImpl<C>* content_description,
403 const webrtc::FieldTrialsView& field_trials) {
404 // SCTP streams are not negotiated using SDP/ContentDescriptions.
405 if (IsSctpProtocol(content_description->protocol())) {
406 return true;
407 }
408
409 const bool include_rtx_streams =
410 ContainsRtxCodec(content_description->codecs());
411
412 const bool include_flexfec_stream =
413 ContainsFlexfecCodec(content_description->codecs());
414
415 for (const SenderOptions& sender : sender_options) {
416 StreamParams* param = GetStreamByIds(*current_streams, sender.track_id);
417 if (!param) {
418 // This is a new sender.
419 StreamParams stream_param =
420 sender.rids.empty()
421 ?
422 // Signal SSRCs and legacy simulcast (if requested).
423 CreateStreamParamsForNewSenderWithSsrcs(
424 sender, rtcp_cname, include_rtx_streams,
425 include_flexfec_stream, ssrc_generator, field_trials)
426 :
427 // Signal RIDs and spec-compliant simulcast (if requested).
428 CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
429
430 content_description->AddStream(stream_param);
431
432 // Store the new StreamParams in current_streams.
433 // This is necessary so that we can use the CNAME for other media types.
434 current_streams->push_back(stream_param);
435 } else {
436 // Use existing generated SSRCs/groups, but update the sync_label if
437 // necessary. This may be needed if a MediaStreamTrack was moved from one
438 // MediaStream to another.
439 param->set_stream_ids(sender.stream_ids);
440 content_description->AddStream(*param);
441 }
442 }
443 return true;
444 }
445
446 // Updates the transport infos of the `sdesc` according to the given
447 // `bundle_group`. The transport infos of the content names within the
448 // `bundle_group` should be updated to use the ufrag, pwd and DTLS role of the
449 // first content within the `bundle_group`.
UpdateTransportInfoForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)450 static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
451 SessionDescription* sdesc) {
452 // The bundle should not be empty.
453 if (!sdesc || !bundle_group.FirstContentName()) {
454 return false;
455 }
456
457 // We should definitely have a transport for the first content.
458 const std::string& selected_content_name = *bundle_group.FirstContentName();
459 const TransportInfo* selected_transport_info =
460 sdesc->GetTransportInfoByName(selected_content_name);
461 if (!selected_transport_info) {
462 return false;
463 }
464
465 // Set the other contents to use the same ICE credentials.
466 const std::string& selected_ufrag =
467 selected_transport_info->description.ice_ufrag;
468 const std::string& selected_pwd =
469 selected_transport_info->description.ice_pwd;
470 ConnectionRole selected_connection_role =
471 selected_transport_info->description.connection_role;
472 for (TransportInfo& transport_info : sdesc->transport_infos()) {
473 if (bundle_group.HasContentName(transport_info.content_name) &&
474 transport_info.content_name != selected_content_name) {
475 transport_info.description.ice_ufrag = selected_ufrag;
476 transport_info.description.ice_pwd = selected_pwd;
477 transport_info.description.connection_role = selected_connection_role;
478 }
479 }
480 return true;
481 }
482
483 // Gets the CryptoParamsVec of the given `content_name` from `sdesc`, and
484 // sets it to `cryptos`.
GetCryptosByName(const SessionDescription * sdesc,const std::string & content_name,CryptoParamsVec * cryptos)485 static bool GetCryptosByName(const SessionDescription* sdesc,
486 const std::string& content_name,
487 CryptoParamsVec* cryptos) {
488 if (!sdesc || !cryptos) {
489 return false;
490 }
491 const ContentInfo* content = sdesc->GetContentByName(content_name);
492 if (!content || !content->media_description()) {
493 return false;
494 }
495 *cryptos = content->media_description()->cryptos();
496 return true;
497 }
498
499 // Prunes the `target_cryptos` by removing the crypto params (cipher_suite)
500 // which are not available in `filter`.
PruneCryptos(const CryptoParamsVec & filter,CryptoParamsVec * target_cryptos)501 static void PruneCryptos(const CryptoParamsVec& filter,
502 CryptoParamsVec* target_cryptos) {
503 if (!target_cryptos) {
504 return;
505 }
506
507 target_cryptos->erase(
508 std::remove_if(target_cryptos->begin(), target_cryptos->end(),
509 // Returns true if the `crypto`'s cipher_suite is not
510 // found in `filter`.
511 [&filter](const CryptoParams& crypto) {
512 for (const CryptoParams& entry : filter) {
513 if (entry.cipher_suite == crypto.cipher_suite)
514 return false;
515 }
516 return true;
517 }),
518 target_cryptos->end());
519 }
520
IsRtpContent(SessionDescription * sdesc,const std::string & content_name)521 static bool IsRtpContent(SessionDescription* sdesc,
522 const std::string& content_name) {
523 bool is_rtp = false;
524 ContentInfo* content = sdesc->GetContentByName(content_name);
525 if (content && content->media_description()) {
526 is_rtp = IsRtpProtocol(content->media_description()->protocol());
527 }
528 return is_rtp;
529 }
530
531 // Updates the crypto parameters of the `sdesc` according to the given
532 // `bundle_group`. The crypto parameters of all the contents within the
533 // `bundle_group` should be updated to use the common subset of the
534 // available cryptos.
UpdateCryptoParamsForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)535 static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
536 SessionDescription* sdesc) {
537 // The bundle should not be empty.
538 if (!sdesc || !bundle_group.FirstContentName()) {
539 return false;
540 }
541
542 bool common_cryptos_needed = false;
543 // Get the common cryptos.
544 const ContentNames& content_names = bundle_group.content_names();
545 CryptoParamsVec common_cryptos;
546 bool first = true;
547 for (const std::string& content_name : content_names) {
548 if (!IsRtpContent(sdesc, content_name)) {
549 continue;
550 }
551 // The common cryptos are needed if any of the content does not have DTLS
552 // enabled.
553 if (!sdesc->GetTransportInfoByName(content_name)->description.secure()) {
554 common_cryptos_needed = true;
555 }
556 if (first) {
557 first = false;
558 // Initial the common_cryptos with the first content in the bundle group.
559 if (!GetCryptosByName(sdesc, content_name, &common_cryptos)) {
560 return false;
561 }
562 if (common_cryptos.empty()) {
563 // If there's no crypto params, we should just return.
564 return true;
565 }
566 } else {
567 CryptoParamsVec cryptos;
568 if (!GetCryptosByName(sdesc, content_name, &cryptos)) {
569 return false;
570 }
571 PruneCryptos(cryptos, &common_cryptos);
572 }
573 }
574
575 if (common_cryptos.empty() && common_cryptos_needed) {
576 return false;
577 }
578
579 // Update to use the common cryptos.
580 for (const std::string& content_name : content_names) {
581 if (!IsRtpContent(sdesc, content_name)) {
582 continue;
583 }
584 ContentInfo* content = sdesc->GetContentByName(content_name);
585 if (IsMediaContent(content)) {
586 MediaContentDescription* media_desc = content->media_description();
587 if (!media_desc) {
588 return false;
589 }
590 media_desc->set_cryptos(common_cryptos);
591 }
592 }
593 return true;
594 }
595
GetActiveContents(const SessionDescription & description,const MediaSessionOptions & session_options)596 static std::vector<const ContentInfo*> GetActiveContents(
597 const SessionDescription& description,
598 const MediaSessionOptions& session_options) {
599 std::vector<const ContentInfo*> active_contents;
600 for (size_t i = 0; i < description.contents().size(); ++i) {
601 RTC_DCHECK_LT(i, session_options.media_description_options.size());
602 const ContentInfo& content = description.contents()[i];
603 const MediaDescriptionOptions& media_options =
604 session_options.media_description_options[i];
605 if (!content.rejected && !media_options.stopped &&
606 content.name == media_options.mid) {
607 active_contents.push_back(&content);
608 }
609 }
610 return active_contents;
611 }
612
613 template <class C>
ContainsRtxCodec(const std::vector<C> & codecs)614 static bool ContainsRtxCodec(const std::vector<C>& codecs) {
615 for (const auto& codec : codecs) {
616 if (IsRtxCodec(codec)) {
617 return true;
618 }
619 }
620 return false;
621 }
622
623 template <class C>
IsRedCodec(const C & codec)624 static bool IsRedCodec(const C& codec) {
625 return absl::EqualsIgnoreCase(codec.name, kRedCodecName);
626 }
627
628 template <class C>
IsRtxCodec(const C & codec)629 static bool IsRtxCodec(const C& codec) {
630 return absl::EqualsIgnoreCase(codec.name, kRtxCodecName);
631 }
632
633 template <class C>
ContainsFlexfecCodec(const std::vector<C> & codecs)634 static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
635 for (const auto& codec : codecs) {
636 if (IsFlexfecCodec(codec)) {
637 return true;
638 }
639 }
640 return false;
641 }
642
643 template <class C>
IsFlexfecCodec(const C & codec)644 static bool IsFlexfecCodec(const C& codec) {
645 return absl::EqualsIgnoreCase(codec.name, kFlexfecCodecName);
646 }
647
648 template <class C>
IsUlpfecCodec(const C & codec)649 static bool IsUlpfecCodec(const C& codec) {
650 return absl::EqualsIgnoreCase(codec.name, kUlpfecCodecName);
651 }
652
653 template <class C>
IsComfortNoiseCodec(const C & codec)654 static bool IsComfortNoiseCodec(const C& codec) {
655 return absl::EqualsIgnoreCase(codec.name, kComfortNoiseCodecName);
656 }
657
658 // Create a media content to be offered for the given `sender_options`,
659 // according to the given options.rtcp_mux, session_options.is_muc, codecs,
660 // secure_transport, crypto, and current_streams. If we don't currently have
661 // crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
662 // created (according to crypto_suites). The created content is added to the
663 // offer.
CreateContentOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const SecurePolicy & secure_policy,const CryptoParamsVec * current_cryptos,const std::vector<std::string> & crypto_suites,const RtpHeaderExtensions & rtp_extensions,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescription * offer)664 static bool CreateContentOffer(
665 const MediaDescriptionOptions& media_description_options,
666 const MediaSessionOptions& session_options,
667 const SecurePolicy& secure_policy,
668 const CryptoParamsVec* current_cryptos,
669 const std::vector<std::string>& crypto_suites,
670 const RtpHeaderExtensions& rtp_extensions,
671 UniqueRandomIdGenerator* ssrc_generator,
672 StreamParamsVec* current_streams,
673 MediaContentDescription* offer) {
674 offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
675 if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
676 offer->set_rtcp_reduced_size(true);
677 }
678
679 // Build the vector of header extensions with directions for this
680 // media_description's options.
681 RtpHeaderExtensions extensions;
682 for (auto extension_with_id : rtp_extensions) {
683 for (const auto& extension : media_description_options.header_extensions) {
684 if (extension_with_id.uri == extension.uri) {
685 // TODO(crbug.com/1051821): Configure the extension direction from
686 // the information in the media_description_options extension
687 // capability.
688 extensions.push_back(extension_with_id);
689 }
690 }
691 }
692 offer->set_rtp_header_extensions(extensions);
693
694 AddSimulcastToMediaDescription(media_description_options, offer);
695
696 if (secure_policy != SEC_DISABLED) {
697 if (current_cryptos) {
698 AddMediaCryptos(*current_cryptos, offer);
699 }
700 if (offer->cryptos().empty()) {
701 if (!CreateMediaCryptos(crypto_suites, offer)) {
702 return false;
703 }
704 }
705 }
706
707 if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
708 return false;
709 }
710 return true;
711 }
712 template <class C>
CreateMediaContentOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const std::vector<C> & codecs,const SecurePolicy & secure_policy,const CryptoParamsVec * current_cryptos,const std::vector<std::string> & crypto_suites,const RtpHeaderExtensions & rtp_extensions,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * offer,const webrtc::FieldTrialsView & field_trials)713 static bool CreateMediaContentOffer(
714 const MediaDescriptionOptions& media_description_options,
715 const MediaSessionOptions& session_options,
716 const std::vector<C>& codecs,
717 const SecurePolicy& secure_policy,
718 const CryptoParamsVec* current_cryptos,
719 const std::vector<std::string>& crypto_suites,
720 const RtpHeaderExtensions& rtp_extensions,
721 UniqueRandomIdGenerator* ssrc_generator,
722 StreamParamsVec* current_streams,
723 MediaContentDescriptionImpl<C>* offer,
724 const webrtc::FieldTrialsView& field_trials) {
725 offer->AddCodecs(codecs);
726 if (!AddStreamParams(media_description_options.sender_options,
727 session_options.rtcp_cname, ssrc_generator,
728 current_streams, offer, field_trials)) {
729 return false;
730 }
731
732 return CreateContentOffer(media_description_options, session_options,
733 secure_policy, current_cryptos, crypto_suites,
734 rtp_extensions, ssrc_generator, current_streams,
735 offer);
736 }
737
738 template <class C>
ReferencedCodecsMatch(const std::vector<C> & codecs1,const int codec1_id,const std::vector<C> & codecs2,const int codec2_id,const webrtc::FieldTrialsView * field_trials)739 static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
740 const int codec1_id,
741 const std::vector<C>& codecs2,
742 const int codec2_id,
743 const webrtc::FieldTrialsView* field_trials) {
744 const C* codec1 = FindCodecById(codecs1, codec1_id);
745 const C* codec2 = FindCodecById(codecs2, codec2_id);
746 return codec1 != nullptr && codec2 != nullptr &&
747 codec1->Matches(*codec2, field_trials);
748 }
749
750 template <class C>
NegotiatePacketization(const C & local_codec,const C & remote_codec,C * negotiated_codec)751 static void NegotiatePacketization(const C& local_codec,
752 const C& remote_codec,
753 C* negotiated_codec) {}
754
755 template <>
NegotiatePacketization(const VideoCodec & local_codec,const VideoCodec & remote_codec,VideoCodec * negotiated_codec)756 void NegotiatePacketization(const VideoCodec& local_codec,
757 const VideoCodec& remote_codec,
758 VideoCodec* negotiated_codec) {
759 negotiated_codec->packetization =
760 VideoCodec::IntersectPacketization(local_codec, remote_codec);
761 }
762
763 template <class C>
NegotiateCodecs(const std::vector<C> & local_codecs,const std::vector<C> & offered_codecs,std::vector<C> * negotiated_codecs,bool keep_offer_order,const webrtc::FieldTrialsView * field_trials)764 static void NegotiateCodecs(const std::vector<C>& local_codecs,
765 const std::vector<C>& offered_codecs,
766 std::vector<C>* negotiated_codecs,
767 bool keep_offer_order,
768 const webrtc::FieldTrialsView* field_trials) {
769 for (const C& ours : local_codecs) {
770 C theirs;
771 // Note that we intentionally only find one matching codec for each of our
772 // local codecs, in case the remote offer contains duplicate codecs.
773 if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs,
774 field_trials)) {
775 C negotiated = ours;
776 NegotiatePacketization(ours, theirs, &negotiated);
777 negotiated.IntersectFeedbackParams(theirs);
778 if (IsRtxCodec(negotiated)) {
779 const auto apt_it =
780 theirs.params.find(kCodecParamAssociatedPayloadType);
781 // FindMatchingCodec shouldn't return something with no apt value.
782 RTC_DCHECK(apt_it != theirs.params.end());
783 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
784
785 // We support parsing the declarative rtx-time parameter.
786 const auto rtx_time_it = theirs.params.find(kCodecParamRtxTime);
787 if (rtx_time_it != theirs.params.end()) {
788 negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second);
789 }
790 } else if (IsRedCodec(negotiated)) {
791 const auto red_it = theirs.params.find(kCodecParamNotInNameValueFormat);
792 if (red_it != theirs.params.end()) {
793 negotiated.SetParam(kCodecParamNotInNameValueFormat, red_it->second);
794 }
795 }
796 if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) {
797 webrtc::H264GenerateProfileLevelIdForAnswer(ours.params, theirs.params,
798 &negotiated.params);
799 }
800 negotiated.id = theirs.id;
801 negotiated.name = theirs.name;
802 negotiated_codecs->push_back(std::move(negotiated));
803 }
804 }
805 if (keep_offer_order) {
806 // RFC3264: Although the answerer MAY list the formats in their desired
807 // order of preference, it is RECOMMENDED that unless there is a
808 // specific reason, the answerer list formats in the same relative order
809 // they were present in the offer.
810 // This can be skipped when the transceiver has any codec preferences.
811 std::unordered_map<int, int> payload_type_preferences;
812 int preference = static_cast<int>(offered_codecs.size() + 1);
813 for (const C& codec : offered_codecs) {
814 payload_type_preferences[codec.id] = preference--;
815 }
816 absl::c_sort(*negotiated_codecs, [&payload_type_preferences](const C& a,
817 const C& b) {
818 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
819 });
820 }
821 }
822
823 // Finds a codec in `codecs2` that matches `codec_to_match`, which is
824 // a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
825 // the codecs themselves and their associated codecs must match.
826 template <class C>
FindMatchingCodec(const std::vector<C> & codecs1,const std::vector<C> & codecs2,const C & codec_to_match,C * found_codec,const webrtc::FieldTrialsView * field_trials)827 static bool FindMatchingCodec(const std::vector<C>& codecs1,
828 const std::vector<C>& codecs2,
829 const C& codec_to_match,
830 C* found_codec,
831 const webrtc::FieldTrialsView* field_trials) {
832 // `codec_to_match` should be a member of `codecs1`, in order to look up
833 // RED/RTX codecs' associated codecs correctly. If not, that's a programming
834 // error.
835 RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) {
836 return &codec == &codec_to_match;
837 }));
838 for (const C& potential_match : codecs2) {
839 if (potential_match.Matches(codec_to_match, field_trials)) {
840 if (IsRtxCodec(codec_to_match)) {
841 int apt_value_1 = 0;
842 int apt_value_2 = 0;
843 if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
844 &apt_value_1) ||
845 !potential_match.GetParam(kCodecParamAssociatedPayloadType,
846 &apt_value_2)) {
847 RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
848 continue;
849 }
850 if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, apt_value_2,
851 field_trials)) {
852 continue;
853 }
854 } else if (IsRedCodec(codec_to_match)) {
855 auto red_parameters_1 =
856 codec_to_match.params.find(kCodecParamNotInNameValueFormat);
857 auto red_parameters_2 =
858 potential_match.params.find(kCodecParamNotInNameValueFormat);
859 bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end();
860 bool has_parameters_2 =
861 red_parameters_2 != potential_match.params.end();
862 if (has_parameters_1 && has_parameters_2) {
863 // Mixed reference codecs (i.e. 111/112) are not supported.
864 // Different levels of redundancy between offer and answer are
865 // since RED is considered to be declarative.
866 std::vector<absl::string_view> redundant_payloads_1 =
867 rtc::split(red_parameters_1->second, '/');
868 std::vector<absl::string_view> redundant_payloads_2 =
869 rtc::split(red_parameters_2->second, '/');
870 if (redundant_payloads_1.size() > 0 &&
871 redundant_payloads_2.size() > 0) {
872 bool consistent = true;
873 for (size_t i = 1; i < redundant_payloads_1.size(); i++) {
874 if (redundant_payloads_1[i] != redundant_payloads_1[0]) {
875 consistent = false;
876 break;
877 }
878 }
879 for (size_t i = 1; i < redundant_payloads_2.size(); i++) {
880 if (redundant_payloads_2[i] != redundant_payloads_2[0]) {
881 consistent = false;
882 break;
883 }
884 }
885 if (!consistent) {
886 continue;
887 }
888
889 int red_value_1;
890 int red_value_2;
891 if (rtc::FromString(redundant_payloads_1[0], &red_value_1) &&
892 rtc::FromString(redundant_payloads_2[0], &red_value_2)) {
893 if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2,
894 red_value_2, field_trials)) {
895 continue;
896 }
897 }
898 }
899 } else if (has_parameters_1 != has_parameters_2) {
900 continue;
901 }
902 }
903 if (found_codec) {
904 *found_codec = potential_match;
905 }
906 return true;
907 }
908 }
909 return false;
910 }
911
912 // Find the codec in `codec_list` that `rtx_codec` is associated with.
913 template <class C>
GetAssociatedCodecForRtx(const std::vector<C> & codec_list,const C & rtx_codec)914 static const C* GetAssociatedCodecForRtx(const std::vector<C>& codec_list,
915 const C& rtx_codec) {
916 std::string associated_pt_str;
917 if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
918 &associated_pt_str)) {
919 RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
920 << " is missing an associated payload type.";
921 return nullptr;
922 }
923
924 int associated_pt;
925 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
926 RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
927 << " of RTX codec " << rtx_codec.name
928 << " to an integer.";
929 return nullptr;
930 }
931
932 // Find the associated codec for the RTX codec.
933 const C* associated_codec = FindCodecById(codec_list, associated_pt);
934 if (!associated_codec) {
935 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
936 << associated_pt << " for RTX codec " << rtx_codec.name
937 << ".";
938 }
939 return associated_codec;
940 }
941
942 // Find the codec in `codec_list` that `red_codec` is associated with.
943 template <class C>
GetAssociatedCodecForRed(const std::vector<C> & codec_list,const C & red_codec)944 static const C* GetAssociatedCodecForRed(const std::vector<C>& codec_list,
945 const C& red_codec) {
946 std::string fmtp;
947 if (!red_codec.GetParam(kCodecParamNotInNameValueFormat, &fmtp)) {
948 // Normal for video/RED.
949 RTC_LOG(LS_WARNING) << "RED codec " << red_codec.name
950 << " is missing an associated payload type.";
951 return nullptr;
952 }
953
954 std::vector<absl::string_view> redundant_payloads = rtc::split(fmtp, '/');
955 if (redundant_payloads.size() < 2) {
956 return nullptr;
957 }
958
959 absl::string_view associated_pt_str = redundant_payloads[0];
960 int associated_pt;
961 if (!rtc::FromString(associated_pt_str, &associated_pt)) {
962 RTC_LOG(LS_WARNING) << "Couldn't convert first payload type "
963 << associated_pt_str << " of RED codec "
964 << red_codec.name << " to an integer.";
965 return nullptr;
966 }
967
968 // Find the associated codec for the RED codec.
969 const C* associated_codec = FindCodecById(codec_list, associated_pt);
970 if (!associated_codec) {
971 RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
972 << associated_pt << " for RED codec " << red_codec.name
973 << ".";
974 }
975 return associated_codec;
976 }
977
978 // Adds all codecs from `reference_codecs` to `offered_codecs` that don't
979 // already exist in `offered_codecs` and ensure the payload types don't
980 // collide.
981 template <class C>
MergeCodecs(const std::vector<C> & reference_codecs,std::vector<C> * offered_codecs,UsedPayloadTypes * used_pltypes,const webrtc::FieldTrialsView * field_trials)982 static void MergeCodecs(const std::vector<C>& reference_codecs,
983 std::vector<C>* offered_codecs,
984 UsedPayloadTypes* used_pltypes,
985 const webrtc::FieldTrialsView* field_trials) {
986 // Add all new codecs that are not RTX/RED codecs.
987 // The two-pass splitting of the loops means preferring payload types
988 // of actual codecs with respect to collisions.
989 for (const C& reference_codec : reference_codecs) {
990 if (!IsRtxCodec(reference_codec) && !IsRedCodec(reference_codec) &&
991 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
992 reference_codec, nullptr, field_trials)) {
993 C codec = reference_codec;
994 used_pltypes->FindAndSetIdUsed(&codec);
995 offered_codecs->push_back(codec);
996 }
997 }
998
999 // Add all new RTX or RED codecs.
1000 for (const C& reference_codec : reference_codecs) {
1001 if (IsRtxCodec(reference_codec) &&
1002 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
1003 reference_codec, nullptr, field_trials)) {
1004 C rtx_codec = reference_codec;
1005 const C* associated_codec =
1006 GetAssociatedCodecForRtx(reference_codecs, rtx_codec);
1007 if (!associated_codec) {
1008 continue;
1009 }
1010 // Find a codec in the offered list that matches the reference codec.
1011 // Its payload type may be different than the reference codec.
1012 C matching_codec;
1013 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
1014 *associated_codec, &matching_codec,
1015 field_trials)) {
1016 RTC_LOG(LS_WARNING)
1017 << "Couldn't find matching " << associated_codec->name << " codec.";
1018 continue;
1019 }
1020
1021 rtx_codec.params[kCodecParamAssociatedPayloadType] =
1022 rtc::ToString(matching_codec.id);
1023 used_pltypes->FindAndSetIdUsed(&rtx_codec);
1024 offered_codecs->push_back(rtx_codec);
1025 } else if (IsRedCodec(reference_codec) &&
1026 !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
1027 reference_codec, nullptr, field_trials)) {
1028 C red_codec = reference_codec;
1029 const C* associated_codec =
1030 GetAssociatedCodecForRed(reference_codecs, red_codec);
1031 if (associated_codec) {
1032 C matching_codec;
1033 if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
1034 *associated_codec, &matching_codec,
1035 field_trials)) {
1036 RTC_LOG(LS_WARNING) << "Couldn't find matching "
1037 << associated_codec->name << " codec.";
1038 continue;
1039 }
1040
1041 red_codec.params[kCodecParamNotInNameValueFormat] =
1042 rtc::ToString(matching_codec.id) + "/" +
1043 rtc::ToString(matching_codec.id);
1044 }
1045 used_pltypes->FindAndSetIdUsed(&red_codec);
1046 offered_codecs->push_back(red_codec);
1047 }
1048 }
1049 }
1050
1051 // `codecs` is a full list of codecs with correct payload type mappings, which
1052 // don't conflict with mappings of the other media type; `supported_codecs` is
1053 // a list filtered for the media section`s direction but with default payload
1054 // types.
1055 template <typename Codecs>
MatchCodecPreference(const std::vector<webrtc::RtpCodecCapability> & codec_preferences,const Codecs & codecs,const Codecs & supported_codecs,const webrtc::FieldTrialsView * field_trials)1056 static Codecs MatchCodecPreference(
1057 const std::vector<webrtc::RtpCodecCapability>& codec_preferences,
1058 const Codecs& codecs,
1059 const Codecs& supported_codecs,
1060 const webrtc::FieldTrialsView* field_trials) {
1061 Codecs filtered_codecs;
1062 bool want_rtx = false;
1063 bool want_red = false;
1064
1065 for (const auto& codec_preference : codec_preferences) {
1066 if (IsRtxCodec(codec_preference)) {
1067 want_rtx = true;
1068 } else if (IsRedCodec(codec_preference)) {
1069 want_red = true;
1070 }
1071 }
1072 for (const auto& codec_preference : codec_preferences) {
1073 auto found_codec = absl::c_find_if(
1074 supported_codecs,
1075 [&codec_preference](const typename Codecs::value_type& codec) {
1076 webrtc::RtpCodecParameters codec_parameters =
1077 codec.ToCodecParameters();
1078 return codec_parameters.name == codec_preference.name &&
1079 codec_parameters.kind == codec_preference.kind &&
1080 codec_parameters.num_channels ==
1081 codec_preference.num_channels &&
1082 codec_parameters.clock_rate == codec_preference.clock_rate &&
1083 codec_parameters.parameters == codec_preference.parameters;
1084 });
1085
1086 if (found_codec != supported_codecs.end()) {
1087 typename Codecs::value_type found_codec_with_correct_pt;
1088 if (FindMatchingCodec(supported_codecs, codecs, *found_codec,
1089 &found_codec_with_correct_pt, field_trials)) {
1090 filtered_codecs.push_back(found_codec_with_correct_pt);
1091 std::string id = rtc::ToString(found_codec_with_correct_pt.id);
1092 // Search for the matching rtx or red codec.
1093 if (want_red || want_rtx) {
1094 for (const auto& codec : codecs) {
1095 if (IsRtxCodec(codec)) {
1096 const auto apt =
1097 codec.params.find(cricket::kCodecParamAssociatedPayloadType);
1098 if (apt != codec.params.end() && apt->second == id) {
1099 filtered_codecs.push_back(codec);
1100 break;
1101 }
1102 } else if (IsRedCodec(codec)) {
1103 // For RED, do not insert the codec again if it was already
1104 // inserted. audio/red for opus gets enabled by having RED before
1105 // the primary codec.
1106 const auto fmtp =
1107 codec.params.find(cricket::kCodecParamNotInNameValueFormat);
1108 if (fmtp != codec.params.end()) {
1109 std::vector<absl::string_view> redundant_payloads =
1110 rtc::split(fmtp->second, '/');
1111 if (redundant_payloads.size() > 0 &&
1112 redundant_payloads[0] == id) {
1113 if (std::find(filtered_codecs.begin(), filtered_codecs.end(),
1114 codec) == filtered_codecs.end()) {
1115 filtered_codecs.push_back(codec);
1116 }
1117 break;
1118 }
1119 }
1120 }
1121 }
1122 }
1123 }
1124 }
1125 }
1126
1127 return filtered_codecs;
1128 }
1129
1130 // Compute the union of `codecs1` and `codecs2`.
1131 template <class C>
ComputeCodecsUnion(const std::vector<C> & codecs1,const std::vector<C> & codecs2,const webrtc::FieldTrialsView * field_trials)1132 std::vector<C> ComputeCodecsUnion(const std::vector<C>& codecs1,
1133 const std::vector<C>& codecs2,
1134 const webrtc::FieldTrialsView* field_trials) {
1135 std::vector<C> all_codecs;
1136 UsedPayloadTypes used_payload_types;
1137 for (const C& codec : codecs1) {
1138 C codec_mutable = codec;
1139 used_payload_types.FindAndSetIdUsed(&codec_mutable);
1140 all_codecs.push_back(codec_mutable);
1141 }
1142
1143 // Use MergeCodecs to merge the second half of our list as it already checks
1144 // and fixes problems with duplicate payload types.
1145 MergeCodecs<C>(codecs2, &all_codecs, &used_payload_types, field_trials);
1146
1147 return all_codecs;
1148 }
1149
1150 // Adds all extensions from `reference_extensions` to `offered_extensions` that
1151 // don't already exist in `offered_extensions` and ensure the IDs don't
1152 // collide. If an extension is added, it's also added to `regular_extensions` or
1153 // `encrypted_extensions`, and if the extension is in `regular_extensions` or
1154 // `encrypted_extensions`, its ID is marked as used in `used_ids`.
1155 // `offered_extensions` is for either audio or video while `regular_extensions`
1156 // and `encrypted_extensions` are used for both audio and video. There could be
1157 // overlap between audio extensions and video extensions.
MergeRtpHdrExts(const RtpHeaderExtensions & reference_extensions,RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * regular_extensions,RtpHeaderExtensions * encrypted_extensions,UsedRtpHeaderExtensionIds * used_ids)1158 static void MergeRtpHdrExts(const RtpHeaderExtensions& reference_extensions,
1159 RtpHeaderExtensions* offered_extensions,
1160 RtpHeaderExtensions* regular_extensions,
1161 RtpHeaderExtensions* encrypted_extensions,
1162 UsedRtpHeaderExtensionIds* used_ids) {
1163 for (auto reference_extension : reference_extensions) {
1164 if (!webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1165 *offered_extensions, reference_extension.uri,
1166 reference_extension.encrypt)) {
1167 if (reference_extension.encrypt) {
1168 const webrtc::RtpExtension* existing =
1169 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1170 *encrypted_extensions, reference_extension.uri,
1171 reference_extension.encrypt);
1172 if (existing) {
1173 offered_extensions->push_back(*existing);
1174 } else {
1175 used_ids->FindAndSetIdUsed(&reference_extension);
1176 encrypted_extensions->push_back(reference_extension);
1177 offered_extensions->push_back(reference_extension);
1178 }
1179 } else {
1180 const webrtc::RtpExtension* existing =
1181 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1182 *regular_extensions, reference_extension.uri,
1183 reference_extension.encrypt);
1184 if (existing) {
1185 offered_extensions->push_back(*existing);
1186 } else {
1187 used_ids->FindAndSetIdUsed(&reference_extension);
1188 regular_extensions->push_back(reference_extension);
1189 offered_extensions->push_back(reference_extension);
1190 }
1191 }
1192 }
1193 }
1194 }
1195
AddEncryptedVersionsOfHdrExts(RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * encrypted_extensions,UsedRtpHeaderExtensionIds * used_ids)1196 static void AddEncryptedVersionsOfHdrExts(
1197 RtpHeaderExtensions* offered_extensions,
1198 RtpHeaderExtensions* encrypted_extensions,
1199 UsedRtpHeaderExtensionIds* used_ids) {
1200 RtpHeaderExtensions encrypted_extensions_to_add;
1201 for (const auto& extension : *offered_extensions) {
1202 // Skip existing encrypted offered extension
1203 if (extension.encrypt) {
1204 continue;
1205 }
1206
1207 // Skip if we cannot encrypt the extension
1208 if (!webrtc::RtpExtension::IsEncryptionSupported(extension.uri)) {
1209 continue;
1210 }
1211
1212 // Skip if an encrypted extension with that URI already exists in the
1213 // offered extensions.
1214 const bool have_encrypted_extension =
1215 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1216 *offered_extensions, extension.uri, true);
1217 if (have_encrypted_extension) {
1218 continue;
1219 }
1220
1221 // Determine if a shared encrypted extension with that URI already exists.
1222 const webrtc::RtpExtension* shared_encrypted_extension =
1223 webrtc::RtpExtension::FindHeaderExtensionByUriAndEncryption(
1224 *encrypted_extensions, extension.uri, true);
1225 if (shared_encrypted_extension) {
1226 // Re-use the shared encrypted extension
1227 encrypted_extensions_to_add.push_back(*shared_encrypted_extension);
1228 continue;
1229 }
1230
1231 // None exists. Create a new shared encrypted extension from the
1232 // non-encrypted one.
1233 webrtc::RtpExtension new_encrypted_extension(extension);
1234 new_encrypted_extension.encrypt = true;
1235 used_ids->FindAndSetIdUsed(&new_encrypted_extension);
1236 encrypted_extensions->push_back(new_encrypted_extension);
1237 encrypted_extensions_to_add.push_back(new_encrypted_extension);
1238 }
1239
1240 // Append the additional encrypted extensions to be offered
1241 offered_extensions->insert(offered_extensions->end(),
1242 encrypted_extensions_to_add.begin(),
1243 encrypted_extensions_to_add.end());
1244 }
1245
1246 // Mostly identical to RtpExtension::FindHeaderExtensionByUri but discards any
1247 // encrypted extensions that this implementation cannot encrypt.
FindHeaderExtensionByUriDiscardUnsupported(const std::vector<webrtc::RtpExtension> & extensions,absl::string_view uri,webrtc::RtpExtension::Filter filter)1248 static const webrtc::RtpExtension* FindHeaderExtensionByUriDiscardUnsupported(
1249 const std::vector<webrtc::RtpExtension>& extensions,
1250 absl::string_view uri,
1251 webrtc::RtpExtension::Filter filter) {
1252 // Note: While it's technically possible to decrypt extensions that we don't
1253 // encrypt, the symmetric API of libsrtp does not allow us to supply
1254 // different IDs for encryption/decryption of header extensions depending on
1255 // whether the packet is inbound or outbound. Thereby, we are limited to
1256 // what we can send in encrypted form.
1257 if (!webrtc::RtpExtension::IsEncryptionSupported(uri)) {
1258 // If there's no encryption support and we only want encrypted extensions,
1259 // there's no point in continuing the search here.
1260 if (filter == webrtc::RtpExtension::kRequireEncryptedExtension) {
1261 return nullptr;
1262 }
1263
1264 // Instruct to only return non-encrypted extensions
1265 filter = webrtc::RtpExtension::Filter::kDiscardEncryptedExtension;
1266 }
1267
1268 return webrtc::RtpExtension::FindHeaderExtensionByUri(extensions, uri,
1269 filter);
1270 }
1271
NegotiateRtpHeaderExtensions(const RtpHeaderExtensions & local_extensions,const RtpHeaderExtensions & offered_extensions,webrtc::RtpExtension::Filter filter,RtpHeaderExtensions * negotiated_extensions)1272 static void NegotiateRtpHeaderExtensions(
1273 const RtpHeaderExtensions& local_extensions,
1274 const RtpHeaderExtensions& offered_extensions,
1275 webrtc::RtpExtension::Filter filter,
1276 RtpHeaderExtensions* negotiated_extensions) {
1277 // TransportSequenceNumberV2 is not offered by default. The special logic for
1278 // the TransportSequenceNumber extensions works as follows:
1279 // Offer Answer
1280 // V1 V1 if in local_extensions.
1281 // V1 and V2 V2 regardless of local_extensions.
1282 // V2 V2 regardless of local_extensions.
1283 const webrtc::RtpExtension* transport_sequence_number_v2_offer =
1284 FindHeaderExtensionByUriDiscardUnsupported(
1285 offered_extensions,
1286 webrtc::RtpExtension::kTransportSequenceNumberV2Uri, filter);
1287
1288 bool frame_descriptor_in_local = false;
1289 bool dependency_descriptor_in_local = false;
1290 bool abs_capture_time_in_local = false;
1291
1292 for (const webrtc::RtpExtension& ours : local_extensions) {
1293 if (ours.uri == webrtc::RtpExtension::kGenericFrameDescriptorUri00)
1294 frame_descriptor_in_local = true;
1295 else if (ours.uri == webrtc::RtpExtension::kDependencyDescriptorUri)
1296 dependency_descriptor_in_local = true;
1297 else if (ours.uri == webrtc::RtpExtension::kAbsoluteCaptureTimeUri)
1298 abs_capture_time_in_local = true;
1299 const webrtc::RtpExtension* theirs =
1300 FindHeaderExtensionByUriDiscardUnsupported(offered_extensions, ours.uri,
1301 filter);
1302 if (theirs) {
1303 if (transport_sequence_number_v2_offer &&
1304 ours.uri == webrtc::RtpExtension::kTransportSequenceNumberUri) {
1305 // Don't respond to
1306 // http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
1307 // if we get an offer including
1308 // http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02
1309 continue;
1310 } else {
1311 // We respond with their RTP header extension id.
1312 negotiated_extensions->push_back(*theirs);
1313 }
1314 }
1315 }
1316
1317 if (transport_sequence_number_v2_offer) {
1318 // Respond that we support kTransportSequenceNumberV2Uri.
1319 negotiated_extensions->push_back(*transport_sequence_number_v2_offer);
1320 }
1321
1322 // Frame descriptors support. If the extension is not present locally, but is
1323 // in the offer, we add it to the list.
1324 if (!dependency_descriptor_in_local) {
1325 const webrtc::RtpExtension* theirs =
1326 FindHeaderExtensionByUriDiscardUnsupported(
1327 offered_extensions, webrtc::RtpExtension::kDependencyDescriptorUri,
1328 filter);
1329 if (theirs) {
1330 negotiated_extensions->push_back(*theirs);
1331 }
1332 }
1333 if (!frame_descriptor_in_local) {
1334 const webrtc::RtpExtension* theirs =
1335 FindHeaderExtensionByUriDiscardUnsupported(
1336 offered_extensions,
1337 webrtc::RtpExtension::kGenericFrameDescriptorUri00, filter);
1338 if (theirs) {
1339 negotiated_extensions->push_back(*theirs);
1340 }
1341 }
1342
1343 // Absolute capture time support. If the extension is not present locally, but
1344 // is in the offer, we add it to the list.
1345 if (!abs_capture_time_in_local) {
1346 const webrtc::RtpExtension* theirs =
1347 FindHeaderExtensionByUriDiscardUnsupported(
1348 offered_extensions, webrtc::RtpExtension::kAbsoluteCaptureTimeUri,
1349 filter);
1350 if (theirs) {
1351 negotiated_extensions->push_back(*theirs);
1352 }
1353 }
1354 }
1355
StripCNCodecs(AudioCodecs * audio_codecs)1356 static void StripCNCodecs(AudioCodecs* audio_codecs) {
1357 audio_codecs->erase(std::remove_if(audio_codecs->begin(), audio_codecs->end(),
1358 [](const AudioCodec& codec) {
1359 return IsComfortNoiseCodec(codec);
1360 }),
1361 audio_codecs->end());
1362 }
1363
1364 template <class C>
SetCodecsInAnswer(const MediaContentDescriptionImpl<C> * offer,const std::vector<C> & local_codecs,const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,UniqueRandomIdGenerator * ssrc_generator,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * answer,const webrtc::FieldTrialsView & field_trials)1365 static bool SetCodecsInAnswer(
1366 const MediaContentDescriptionImpl<C>* offer,
1367 const std::vector<C>& local_codecs,
1368 const MediaDescriptionOptions& media_description_options,
1369 const MediaSessionOptions& session_options,
1370 UniqueRandomIdGenerator* ssrc_generator,
1371 StreamParamsVec* current_streams,
1372 MediaContentDescriptionImpl<C>* answer,
1373 const webrtc::FieldTrialsView& field_trials) {
1374 std::vector<C> negotiated_codecs;
1375 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
1376 media_description_options.codec_preferences.empty(),
1377 &field_trials);
1378 answer->AddCodecs(negotiated_codecs);
1379 answer->set_protocol(offer->protocol());
1380 if (!AddStreamParams(media_description_options.sender_options,
1381 session_options.rtcp_cname, ssrc_generator,
1382 current_streams, answer, field_trials)) {
1383 return false; // Something went seriously wrong.
1384 }
1385 return true;
1386 }
1387
1388 // Create a media content to be answered for the given `sender_options`
1389 // according to the given session_options.rtcp_mux, session_options.streams,
1390 // codecs, crypto, and current_streams. If we don't currently have crypto (in
1391 // current_cryptos) and it is enabled (in secure_policy), crypto is created
1392 // (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
1393 // negotiated with the offer. If the negotiation fails, this method returns
1394 // false. The created content is added to the offer.
CreateMediaContentAnswer(const MediaContentDescription * offer,const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const SecurePolicy & sdes_policy,const CryptoParamsVec * current_cryptos,const RtpHeaderExtensions & local_rtp_extensions,UniqueRandomIdGenerator * ssrc_generator,bool enable_encrypted_rtp_header_extensions,StreamParamsVec * current_streams,bool bundle_enabled,MediaContentDescription * answer)1395 static bool CreateMediaContentAnswer(
1396 const MediaContentDescription* offer,
1397 const MediaDescriptionOptions& media_description_options,
1398 const MediaSessionOptions& session_options,
1399 const SecurePolicy& sdes_policy,
1400 const CryptoParamsVec* current_cryptos,
1401 const RtpHeaderExtensions& local_rtp_extensions,
1402 UniqueRandomIdGenerator* ssrc_generator,
1403 bool enable_encrypted_rtp_header_extensions,
1404 StreamParamsVec* current_streams,
1405 bool bundle_enabled,
1406 MediaContentDescription* answer) {
1407 answer->set_extmap_allow_mixed_enum(offer->extmap_allow_mixed_enum());
1408 const webrtc::RtpExtension::Filter extensions_filter =
1409 enable_encrypted_rtp_header_extensions
1410 ? webrtc::RtpExtension::Filter::kPreferEncryptedExtension
1411 : webrtc::RtpExtension::Filter::kDiscardEncryptedExtension;
1412 RtpHeaderExtensions negotiated_rtp_extensions;
1413 NegotiateRtpHeaderExtensions(local_rtp_extensions,
1414 offer->rtp_header_extensions(),
1415 extensions_filter, &negotiated_rtp_extensions);
1416 answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1417
1418 answer->set_rtcp_mux(session_options.rtcp_mux_enabled && offer->rtcp_mux());
1419 if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1420 answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1421 }
1422
1423 answer->set_remote_estimate(offer->remote_estimate());
1424
1425 if (sdes_policy != SEC_DISABLED) {
1426 CryptoParams crypto;
1427 if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
1428 &crypto)) {
1429 if (current_cryptos) {
1430 FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1431 }
1432 answer->AddCrypto(crypto);
1433 }
1434 }
1435
1436 if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
1437 return false;
1438 }
1439
1440 AddSimulcastToMediaDescription(media_description_options, answer);
1441
1442 answer->set_direction(NegotiateRtpTransceiverDirection(
1443 offer->direction(), media_description_options.direction));
1444
1445 return true;
1446 }
1447
IsMediaProtocolSupported(MediaType type,const std::string & protocol,bool secure_transport)1448 static bool IsMediaProtocolSupported(MediaType type,
1449 const std::string& protocol,
1450 bool secure_transport) {
1451 // Since not all applications serialize and deserialize the media protocol,
1452 // we will have to accept `protocol` to be empty.
1453 if (protocol.empty()) {
1454 return true;
1455 }
1456
1457 if (type == MEDIA_TYPE_DATA) {
1458 // Check for SCTP
1459 if (secure_transport) {
1460 // Most likely scenarios first.
1461 return IsDtlsSctp(protocol);
1462 } else {
1463 return IsPlainSctp(protocol);
1464 }
1465 }
1466
1467 // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1468 // JSEP specifies.
1469 if (secure_transport) {
1470 // Most likely scenarios first.
1471 return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1472 } else {
1473 return IsPlainRtp(protocol);
1474 }
1475 }
1476
SetMediaProtocol(bool secure_transport,MediaContentDescription * desc)1477 static void SetMediaProtocol(bool secure_transport,
1478 MediaContentDescription* desc) {
1479 if (!desc->cryptos().empty())
1480 desc->set_protocol(kMediaProtocolSavpf);
1481 else if (secure_transport)
1482 desc->set_protocol(kMediaProtocolDtlsSavpf);
1483 else
1484 desc->set_protocol(kMediaProtocolAvpf);
1485 }
1486
1487 // Gets the TransportInfo of the given `content_name` from the
1488 // `current_description`. If doesn't exist, returns a new one.
GetTransportDescription(const std::string & content_name,const SessionDescription * current_description)1489 static const TransportDescription* GetTransportDescription(
1490 const std::string& content_name,
1491 const SessionDescription* current_description) {
1492 const TransportDescription* desc = NULL;
1493 if (current_description) {
1494 const TransportInfo* info =
1495 current_description->GetTransportInfoByName(content_name);
1496 if (info) {
1497 desc = &info->description;
1498 }
1499 }
1500 return desc;
1501 }
1502
1503 // Gets the current DTLS state from the transport description.
IsDtlsActive(const ContentInfo * content,const SessionDescription * current_description)1504 static bool IsDtlsActive(const ContentInfo* content,
1505 const SessionDescription* current_description) {
1506 if (!content) {
1507 return false;
1508 }
1509
1510 size_t msection_index = content - ¤t_description->contents()[0];
1511
1512 if (current_description->transport_infos().size() <= msection_index) {
1513 return false;
1514 }
1515
1516 return current_description->transport_infos()[msection_index]
1517 .description.secure();
1518 }
1519
AddAudioSender(const std::string & track_id,const std::vector<std::string> & stream_ids)1520 void MediaDescriptionOptions::AddAudioSender(
1521 const std::string& track_id,
1522 const std::vector<std::string>& stream_ids) {
1523 RTC_DCHECK(type == MEDIA_TYPE_AUDIO);
1524 AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1);
1525 }
1526
AddVideoSender(const std::string & track_id,const std::vector<std::string> & stream_ids,const std::vector<RidDescription> & rids,const SimulcastLayerList & simulcast_layers,int num_sim_layers)1527 void MediaDescriptionOptions::AddVideoSender(
1528 const std::string& track_id,
1529 const std::vector<std::string>& stream_ids,
1530 const std::vector<RidDescription>& rids,
1531 const SimulcastLayerList& simulcast_layers,
1532 int num_sim_layers) {
1533 RTC_DCHECK(type == MEDIA_TYPE_VIDEO);
1534 RTC_DCHECK(rids.empty() || num_sim_layers == 0)
1535 << "RIDs are the compliant way to indicate simulcast.";
1536 RTC_DCHECK(ValidateSimulcastLayers(rids, simulcast_layers));
1537 AddSenderInternal(track_id, stream_ids, rids, simulcast_layers,
1538 num_sim_layers);
1539 }
1540
AddSenderInternal(const std::string & track_id,const std::vector<std::string> & stream_ids,const std::vector<RidDescription> & rids,const SimulcastLayerList & simulcast_layers,int num_sim_layers)1541 void MediaDescriptionOptions::AddSenderInternal(
1542 const std::string& track_id,
1543 const std::vector<std::string>& stream_ids,
1544 const std::vector<RidDescription>& rids,
1545 const SimulcastLayerList& simulcast_layers,
1546 int num_sim_layers) {
1547 // TODO(steveanton): Support any number of stream ids.
1548 RTC_CHECK(stream_ids.size() == 1U);
1549 SenderOptions options;
1550 options.track_id = track_id;
1551 options.stream_ids = stream_ids;
1552 options.simulcast_layers = simulcast_layers;
1553 options.rids = rids;
1554 options.num_sim_layers = num_sim_layers;
1555 sender_options.push_back(options);
1556 }
1557
HasMediaDescription(MediaType type) const1558 bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
1559 return absl::c_any_of(
1560 media_description_options,
1561 [type](const MediaDescriptionOptions& t) { return t.type == type; });
1562 }
1563
MediaSessionDescriptionFactory(const TransportDescriptionFactory * transport_desc_factory,rtc::UniqueRandomIdGenerator * ssrc_generator)1564 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1565 const TransportDescriptionFactory* transport_desc_factory,
1566 rtc::UniqueRandomIdGenerator* ssrc_generator)
1567 : ssrc_generator_(ssrc_generator),
1568 transport_desc_factory_(transport_desc_factory) {}
1569
MediaSessionDescriptionFactory(cricket::MediaEngineInterface * media_engine,bool rtx_enabled,rtc::UniqueRandomIdGenerator * ssrc_generator,const TransportDescriptionFactory * transport_desc_factory)1570 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1571 cricket::MediaEngineInterface* media_engine,
1572 bool rtx_enabled,
1573 rtc::UniqueRandomIdGenerator* ssrc_generator,
1574 const TransportDescriptionFactory* transport_desc_factory)
1575 : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
1576 if (media_engine) {
1577 audio_send_codecs_ = media_engine->voice().send_codecs();
1578 audio_recv_codecs_ = media_engine->voice().recv_codecs();
1579 video_send_codecs_ = media_engine->video().send_codecs(rtx_enabled);
1580 video_recv_codecs_ = media_engine->video().recv_codecs(rtx_enabled);
1581 }
1582 ComputeAudioCodecsIntersectionAndUnion();
1583 ComputeVideoCodecsIntersectionAndUnion();
1584 }
1585
audio_sendrecv_codecs() const1586 const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1587 const {
1588 return audio_sendrecv_codecs_;
1589 }
1590
audio_send_codecs() const1591 const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1592 return audio_send_codecs_;
1593 }
1594
audio_recv_codecs() const1595 const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1596 return audio_recv_codecs_;
1597 }
1598
set_audio_codecs(const AudioCodecs & send_codecs,const AudioCodecs & recv_codecs)1599 void MediaSessionDescriptionFactory::set_audio_codecs(
1600 const AudioCodecs& send_codecs,
1601 const AudioCodecs& recv_codecs) {
1602 audio_send_codecs_ = send_codecs;
1603 audio_recv_codecs_ = recv_codecs;
1604 ComputeAudioCodecsIntersectionAndUnion();
1605 }
1606
video_sendrecv_codecs() const1607 const VideoCodecs& MediaSessionDescriptionFactory::video_sendrecv_codecs()
1608 const {
1609 return video_sendrecv_codecs_;
1610 }
1611
video_send_codecs() const1612 const VideoCodecs& MediaSessionDescriptionFactory::video_send_codecs() const {
1613 return video_send_codecs_;
1614 }
1615
video_recv_codecs() const1616 const VideoCodecs& MediaSessionDescriptionFactory::video_recv_codecs() const {
1617 return video_recv_codecs_;
1618 }
1619
set_video_codecs(const VideoCodecs & send_codecs,const VideoCodecs & recv_codecs)1620 void MediaSessionDescriptionFactory::set_video_codecs(
1621 const VideoCodecs& send_codecs,
1622 const VideoCodecs& recv_codecs) {
1623 video_send_codecs_ = send_codecs;
1624 video_recv_codecs_ = recv_codecs;
1625 ComputeVideoCodecsIntersectionAndUnion();
1626 }
1627
RemoveUnifiedPlanExtensions(RtpHeaderExtensions * extensions)1628 static void RemoveUnifiedPlanExtensions(RtpHeaderExtensions* extensions) {
1629 RTC_DCHECK(extensions);
1630
1631 extensions->erase(
1632 std::remove_if(extensions->begin(), extensions->end(),
1633 [](auto extension) {
1634 return extension.uri == webrtc::RtpExtension::kMidUri ||
1635 extension.uri == webrtc::RtpExtension::kRidUri ||
1636 extension.uri ==
1637 webrtc::RtpExtension::kRepairedRidUri;
1638 }),
1639 extensions->end());
1640 }
1641
1642 RtpHeaderExtensions
filtered_rtp_header_extensions(RtpHeaderExtensions extensions) const1643 MediaSessionDescriptionFactory::filtered_rtp_header_extensions(
1644 RtpHeaderExtensions extensions) const {
1645 if (!is_unified_plan_) {
1646 RemoveUnifiedPlanExtensions(&extensions);
1647 }
1648 return extensions;
1649 }
1650
CreateOffer(const MediaSessionOptions & session_options,const SessionDescription * current_description) const1651 std::unique_ptr<SessionDescription> MediaSessionDescriptionFactory::CreateOffer(
1652 const MediaSessionOptions& session_options,
1653 const SessionDescription* current_description) const {
1654 // Must have options for each existing section.
1655 if (current_description) {
1656 RTC_DCHECK_LE(current_description->contents().size(),
1657 session_options.media_description_options.size());
1658 }
1659
1660 IceCredentialsIterator ice_credentials(
1661 session_options.pooled_ice_credentials);
1662
1663 std::vector<const ContentInfo*> current_active_contents;
1664 if (current_description) {
1665 current_active_contents =
1666 GetActiveContents(*current_description, session_options);
1667 }
1668
1669 StreamParamsVec current_streams =
1670 GetCurrentStreamParams(current_active_contents);
1671
1672 AudioCodecs offer_audio_codecs;
1673 VideoCodecs offer_video_codecs;
1674 GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
1675 &offer_video_codecs);
1676 AudioVideoRtpHeaderExtensions extensions_with_ids =
1677 GetOfferedRtpHeaderExtensionsWithIds(
1678 current_active_contents, session_options.offer_extmap_allow_mixed,
1679 session_options.media_description_options);
1680
1681 auto offer = std::make_unique<SessionDescription>();
1682
1683 // Iterate through the media description options, matching with existing media
1684 // descriptions in `current_description`.
1685 size_t msection_index = 0;
1686 for (const MediaDescriptionOptions& media_description_options :
1687 session_options.media_description_options) {
1688 const ContentInfo* current_content = nullptr;
1689 if (current_description &&
1690 msection_index < current_description->contents().size()) {
1691 current_content = ¤t_description->contents()[msection_index];
1692 // Media type must match unless this media section is being recycled.
1693 RTC_DCHECK(current_content->name != media_description_options.mid ||
1694 IsMediaContentOfType(current_content,
1695 media_description_options.type));
1696 }
1697 switch (media_description_options.type) {
1698 case MEDIA_TYPE_AUDIO:
1699 if (!AddAudioContentForOffer(media_description_options, session_options,
1700 current_content, current_description,
1701 extensions_with_ids.audio,
1702 offer_audio_codecs, ¤t_streams,
1703 offer.get(), &ice_credentials)) {
1704 return nullptr;
1705 }
1706 break;
1707 case MEDIA_TYPE_VIDEO:
1708 if (!AddVideoContentForOffer(media_description_options, session_options,
1709 current_content, current_description,
1710 extensions_with_ids.video,
1711 offer_video_codecs, ¤t_streams,
1712 offer.get(), &ice_credentials)) {
1713 return nullptr;
1714 }
1715 break;
1716 case MEDIA_TYPE_DATA:
1717 if (!AddDataContentForOffer(media_description_options, session_options,
1718 current_content, current_description,
1719 ¤t_streams, offer.get(),
1720 &ice_credentials)) {
1721 return nullptr;
1722 }
1723 break;
1724 case MEDIA_TYPE_UNSUPPORTED:
1725 if (!AddUnsupportedContentForOffer(
1726 media_description_options, session_options, current_content,
1727 current_description, offer.get(), &ice_credentials)) {
1728 return nullptr;
1729 }
1730 break;
1731 default:
1732 RTC_DCHECK_NOTREACHED();
1733 }
1734 ++msection_index;
1735 }
1736
1737 // Bundle the contents together, if we've been asked to do so, and update any
1738 // parameters that need to be tweaked for BUNDLE.
1739 if (session_options.bundle_enabled) {
1740 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1741 for (const ContentInfo& content : offer->contents()) {
1742 if (content.rejected) {
1743 continue;
1744 }
1745 // TODO(deadbeef): There are conditions that make bundling two media
1746 // descriptions together illegal. For example, they use the same payload
1747 // type to represent different codecs, or same IDs for different header
1748 // extensions. We need to detect this and not try to bundle those media
1749 // descriptions together.
1750 offer_bundle.AddContentName(content.name);
1751 }
1752 if (!offer_bundle.content_names().empty()) {
1753 offer->AddGroup(offer_bundle);
1754 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1755 RTC_LOG(LS_ERROR)
1756 << "CreateOffer failed to UpdateTransportInfoForBundle.";
1757 return nullptr;
1758 }
1759 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1760 RTC_LOG(LS_ERROR)
1761 << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1762 return nullptr;
1763 }
1764 }
1765 }
1766
1767 // The following determines how to signal MSIDs to ensure compatibility with
1768 // older endpoints (in particular, older Plan B endpoints).
1769 if (is_unified_plan_) {
1770 // Be conservative and signal using both a=msid and a=ssrc lines. Unified
1771 // Plan answerers will look at a=msid and Plan B answerers will look at the
1772 // a=ssrc MSID line.
1773 offer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1774 cricket::kMsidSignalingSsrcAttribute);
1775 } else {
1776 // Plan B always signals MSID using a=ssrc lines.
1777 offer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1778 }
1779
1780 offer->set_extmap_allow_mixed(session_options.offer_extmap_allow_mixed);
1781
1782 return offer;
1783 }
1784
1785 std::unique_ptr<SessionDescription>
CreateAnswer(const SessionDescription * offer,const MediaSessionOptions & session_options,const SessionDescription * current_description) const1786 MediaSessionDescriptionFactory::CreateAnswer(
1787 const SessionDescription* offer,
1788 const MediaSessionOptions& session_options,
1789 const SessionDescription* current_description) const {
1790 if (!offer) {
1791 return nullptr;
1792 }
1793
1794 // Must have options for exactly as many sections as in the offer.
1795 RTC_DCHECK_EQ(offer->contents().size(),
1796 session_options.media_description_options.size());
1797
1798 IceCredentialsIterator ice_credentials(
1799 session_options.pooled_ice_credentials);
1800
1801 std::vector<const ContentInfo*> current_active_contents;
1802 if (current_description) {
1803 current_active_contents =
1804 GetActiveContents(*current_description, session_options);
1805 }
1806
1807 StreamParamsVec current_streams =
1808 GetCurrentStreamParams(current_active_contents);
1809
1810 // Get list of all possible codecs that respects existing payload type
1811 // mappings and uses a single payload type space.
1812 //
1813 // Note that these lists may be further filtered for each m= section; this
1814 // step is done just to establish the payload type mappings shared by all
1815 // sections.
1816 AudioCodecs answer_audio_codecs;
1817 VideoCodecs answer_video_codecs;
1818 GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs,
1819 &answer_video_codecs);
1820
1821 auto answer = std::make_unique<SessionDescription>();
1822
1823 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1824 // group in the answer with the appropriate content names.
1825 std::vector<const ContentGroup*> offer_bundles =
1826 offer->GetGroupsByName(GROUP_TYPE_BUNDLE);
1827 // There are as many answer BUNDLE groups as offer BUNDLE groups (even if
1828 // rejected, we respond with an empty group). `offer_bundles`,
1829 // `answer_bundles` and `bundle_transports` share the same size and indices.
1830 std::vector<ContentGroup> answer_bundles;
1831 std::vector<std::unique_ptr<TransportInfo>> bundle_transports;
1832 answer_bundles.reserve(offer_bundles.size());
1833 bundle_transports.reserve(offer_bundles.size());
1834 for (size_t i = 0; i < offer_bundles.size(); ++i) {
1835 answer_bundles.emplace_back(GROUP_TYPE_BUNDLE);
1836 bundle_transports.emplace_back(nullptr);
1837 }
1838
1839 answer->set_extmap_allow_mixed(offer->extmap_allow_mixed());
1840
1841 // Iterate through the media description options, matching with existing
1842 // media descriptions in `current_description`.
1843 size_t msection_index = 0;
1844 for (const MediaDescriptionOptions& media_description_options :
1845 session_options.media_description_options) {
1846 const ContentInfo* offer_content = &offer->contents()[msection_index];
1847 // Media types and MIDs must match between the remote offer and the
1848 // MediaDescriptionOptions.
1849 RTC_DCHECK(
1850 IsMediaContentOfType(offer_content, media_description_options.type));
1851 RTC_DCHECK(media_description_options.mid == offer_content->name);
1852 // Get the index of the BUNDLE group that this MID belongs to, if any.
1853 absl::optional<size_t> bundle_index;
1854 for (size_t i = 0; i < offer_bundles.size(); ++i) {
1855 if (offer_bundles[i]->HasContentName(media_description_options.mid)) {
1856 bundle_index = i;
1857 break;
1858 }
1859 }
1860 TransportInfo* bundle_transport =
1861 bundle_index.has_value() ? bundle_transports[bundle_index.value()].get()
1862 : nullptr;
1863
1864 const ContentInfo* current_content = nullptr;
1865 if (current_description &&
1866 msection_index < current_description->contents().size()) {
1867 current_content = ¤t_description->contents()[msection_index];
1868 }
1869 RtpHeaderExtensions header_extensions = RtpHeaderExtensionsFromCapabilities(
1870 UnstoppedRtpHeaderExtensionCapabilities(
1871 media_description_options.header_extensions));
1872 switch (media_description_options.type) {
1873 case MEDIA_TYPE_AUDIO:
1874 if (!AddAudioContentForAnswer(
1875 media_description_options, session_options, offer_content,
1876 offer, current_content, current_description, bundle_transport,
1877 answer_audio_codecs, header_extensions, ¤t_streams,
1878 answer.get(), &ice_credentials)) {
1879 return nullptr;
1880 }
1881 break;
1882 case MEDIA_TYPE_VIDEO:
1883 if (!AddVideoContentForAnswer(
1884 media_description_options, session_options, offer_content,
1885 offer, current_content, current_description, bundle_transport,
1886 answer_video_codecs, header_extensions, ¤t_streams,
1887 answer.get(), &ice_credentials)) {
1888 return nullptr;
1889 }
1890 break;
1891 case MEDIA_TYPE_DATA:
1892 if (!AddDataContentForAnswer(
1893 media_description_options, session_options, offer_content,
1894 offer, current_content, current_description, bundle_transport,
1895 ¤t_streams, answer.get(), &ice_credentials)) {
1896 return nullptr;
1897 }
1898 break;
1899 case MEDIA_TYPE_UNSUPPORTED:
1900 if (!AddUnsupportedContentForAnswer(
1901 media_description_options, session_options, offer_content,
1902 offer, current_content, current_description, bundle_transport,
1903 answer.get(), &ice_credentials)) {
1904 return nullptr;
1905 }
1906 break;
1907 default:
1908 RTC_DCHECK_NOTREACHED();
1909 }
1910 ++msection_index;
1911 // See if we can add the newly generated m= section to the BUNDLE group in
1912 // the answer.
1913 ContentInfo& added = answer->contents().back();
1914 if (!added.rejected && session_options.bundle_enabled &&
1915 bundle_index.has_value()) {
1916 // The `bundle_index` is for `media_description_options.mid`.
1917 RTC_DCHECK_EQ(media_description_options.mid, added.name);
1918 answer_bundles[bundle_index.value()].AddContentName(added.name);
1919 bundle_transports[bundle_index.value()].reset(
1920 new TransportInfo(*answer->GetTransportInfoByName(added.name)));
1921 }
1922 }
1923
1924 // If BUNDLE group(s) were offered, put the same number of BUNDLE groups in
1925 // the answer even if they're empty. RFC5888 says:
1926 //
1927 // A SIP entity that receives an offer that contains an "a=group" line
1928 // with semantics that are understood MUST return an answer that
1929 // contains an "a=group" line with the same semantics.
1930 if (!offer_bundles.empty()) {
1931 for (const ContentGroup& answer_bundle : answer_bundles) {
1932 answer->AddGroup(answer_bundle);
1933
1934 if (answer_bundle.FirstContentName()) {
1935 // Share the same ICE credentials and crypto params across all contents,
1936 // as BUNDLE requires.
1937 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1938 RTC_LOG(LS_ERROR)
1939 << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1940 return NULL;
1941 }
1942
1943 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1944 RTC_LOG(LS_ERROR)
1945 << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1946 return NULL;
1947 }
1948 }
1949 }
1950 }
1951
1952 // The following determines how to signal MSIDs to ensure compatibility with
1953 // older endpoints (in particular, older Plan B endpoints).
1954 if (is_unified_plan_) {
1955 // Unified Plan needs to look at what the offer included to find the most
1956 // compatible answer.
1957 if (offer->msid_signaling() == 0) {
1958 // We end up here in one of three cases:
1959 // 1. An empty offer. We'll reply with an empty answer so it doesn't
1960 // matter what we pick here.
1961 // 2. A data channel only offer. We won't add any MSIDs to the answer so
1962 // it also doesn't matter what we pick here.
1963 // 3. Media that's either sendonly or inactive from the remote endpoint.
1964 // We don't have any information to say whether the endpoint is Plan B
1965 // or Unified Plan, so be conservative and send both.
1966 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection |
1967 cricket::kMsidSignalingSsrcAttribute);
1968 } else if (offer->msid_signaling() ==
1969 (cricket::kMsidSignalingMediaSection |
1970 cricket::kMsidSignalingSsrcAttribute)) {
1971 // If both a=msid and a=ssrc MSID signaling methods were used, we're
1972 // probably talking to a Unified Plan endpoint so respond with just
1973 // a=msid.
1974 answer->set_msid_signaling(cricket::kMsidSignalingMediaSection);
1975 } else {
1976 // Otherwise, it's clear which method the offerer is using so repeat that
1977 // back to them.
1978 answer->set_msid_signaling(offer->msid_signaling());
1979 }
1980 } else {
1981 // Plan B always signals MSID using a=ssrc lines.
1982 answer->set_msid_signaling(cricket::kMsidSignalingSsrcAttribute);
1983 }
1984
1985 return answer;
1986 }
1987
GetAudioCodecsForOffer(const RtpTransceiverDirection & direction) const1988 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1989 const RtpTransceiverDirection& direction) const {
1990 switch (direction) {
1991 // If stream is inactive - generate list as if sendrecv.
1992 case RtpTransceiverDirection::kSendRecv:
1993 case RtpTransceiverDirection::kStopped:
1994 case RtpTransceiverDirection::kInactive:
1995 return audio_sendrecv_codecs_;
1996 case RtpTransceiverDirection::kSendOnly:
1997 return audio_send_codecs_;
1998 case RtpTransceiverDirection::kRecvOnly:
1999 return audio_recv_codecs_;
2000 }
2001 RTC_CHECK_NOTREACHED();
2002 }
2003
GetAudioCodecsForAnswer(const RtpTransceiverDirection & offer,const RtpTransceiverDirection & answer) const2004 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
2005 const RtpTransceiverDirection& offer,
2006 const RtpTransceiverDirection& answer) const {
2007 switch (answer) {
2008 // For inactive and sendrecv answers, generate lists as if we were to accept
2009 // the offer's direction. See RFC 3264 Section 6.1.
2010 case RtpTransceiverDirection::kSendRecv:
2011 case RtpTransceiverDirection::kStopped:
2012 case RtpTransceiverDirection::kInactive:
2013 return GetAudioCodecsForOffer(
2014 webrtc::RtpTransceiverDirectionReversed(offer));
2015 case RtpTransceiverDirection::kSendOnly:
2016 return audio_send_codecs_;
2017 case RtpTransceiverDirection::kRecvOnly:
2018 return audio_recv_codecs_;
2019 }
2020 RTC_CHECK_NOTREACHED();
2021 }
2022
GetVideoCodecsForOffer(const RtpTransceiverDirection & direction) const2023 const VideoCodecs& MediaSessionDescriptionFactory::GetVideoCodecsForOffer(
2024 const RtpTransceiverDirection& direction) const {
2025 switch (direction) {
2026 // If stream is inactive - generate list as if sendrecv.
2027 case RtpTransceiverDirection::kSendRecv:
2028 case RtpTransceiverDirection::kStopped:
2029 case RtpTransceiverDirection::kInactive:
2030 return video_sendrecv_codecs_;
2031 case RtpTransceiverDirection::kSendOnly:
2032 return video_send_codecs_;
2033 case RtpTransceiverDirection::kRecvOnly:
2034 return video_recv_codecs_;
2035 }
2036 RTC_CHECK_NOTREACHED();
2037 }
2038
GetVideoCodecsForAnswer(const RtpTransceiverDirection & offer,const RtpTransceiverDirection & answer) const2039 const VideoCodecs& MediaSessionDescriptionFactory::GetVideoCodecsForAnswer(
2040 const RtpTransceiverDirection& offer,
2041 const RtpTransceiverDirection& answer) const {
2042 switch (answer) {
2043 // For inactive and sendrecv answers, generate lists as if we were to accept
2044 // the offer's direction. See RFC 3264 Section 6.1.
2045 case RtpTransceiverDirection::kSendRecv:
2046 case RtpTransceiverDirection::kStopped:
2047 case RtpTransceiverDirection::kInactive:
2048 return GetVideoCodecsForOffer(
2049 webrtc::RtpTransceiverDirectionReversed(offer));
2050 case RtpTransceiverDirection::kSendOnly:
2051 return video_send_codecs_;
2052 case RtpTransceiverDirection::kRecvOnly:
2053 return video_recv_codecs_;
2054 }
2055 RTC_CHECK_NOTREACHED();
2056 }
2057
MergeCodecsFromDescription(const std::vector<const ContentInfo * > & current_active_contents,AudioCodecs * audio_codecs,VideoCodecs * video_codecs,UsedPayloadTypes * used_pltypes,const webrtc::FieldTrialsView * field_trials)2058 void MergeCodecsFromDescription(
2059 const std::vector<const ContentInfo*>& current_active_contents,
2060 AudioCodecs* audio_codecs,
2061 VideoCodecs* video_codecs,
2062 UsedPayloadTypes* used_pltypes,
2063 const webrtc::FieldTrialsView* field_trials) {
2064 for (const ContentInfo* content : current_active_contents) {
2065 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
2066 const AudioContentDescription* audio =
2067 content->media_description()->as_audio();
2068 MergeCodecs<AudioCodec>(audio->codecs(), audio_codecs, used_pltypes,
2069 field_trials);
2070 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
2071 const VideoContentDescription* video =
2072 content->media_description()->as_video();
2073 MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes,
2074 field_trials);
2075 }
2076 }
2077 }
2078
2079 // Getting codecs for an offer involves these steps:
2080 //
2081 // 1. Construct payload type -> codec mappings for current description.
2082 // 2. Add any reference codecs that weren't already present
2083 // 3. For each individual media description (m= section), filter codecs based
2084 // on the directional attribute (happens in another method).
GetCodecsForOffer(const std::vector<const ContentInfo * > & current_active_contents,AudioCodecs * audio_codecs,VideoCodecs * video_codecs) const2085 void MediaSessionDescriptionFactory::GetCodecsForOffer(
2086 const std::vector<const ContentInfo*>& current_active_contents,
2087 AudioCodecs* audio_codecs,
2088 VideoCodecs* video_codecs) const {
2089 const webrtc::FieldTrialsView* field_trials =
2090 &transport_desc_factory_->trials();
2091 // First - get all codecs from the current description if the media type
2092 // is used. Add them to `used_pltypes` so the payload type is not reused if a
2093 // new media type is added.
2094 UsedPayloadTypes used_pltypes;
2095 MergeCodecsFromDescription(current_active_contents, audio_codecs,
2096 video_codecs, &used_pltypes, field_trials);
2097
2098 // Add our codecs that are not in the current description.
2099 MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes,
2100 field_trials);
2101 MergeCodecs<VideoCodec>(all_video_codecs_, video_codecs, &used_pltypes,
2102 field_trials);
2103 }
2104
2105 // Getting codecs for an answer involves these steps:
2106 //
2107 // 1. Construct payload type -> codec mappings for current description.
2108 // 2. Add any codecs from the offer that weren't already present.
2109 // 3. Add any remaining codecs that weren't already present.
2110 // 4. For each individual media description (m= section), filter codecs based
2111 // on the directional attribute (happens in another method).
GetCodecsForAnswer(const std::vector<const ContentInfo * > & current_active_contents,const SessionDescription & remote_offer,AudioCodecs * audio_codecs,VideoCodecs * video_codecs) const2112 void MediaSessionDescriptionFactory::GetCodecsForAnswer(
2113 const std::vector<const ContentInfo*>& current_active_contents,
2114 const SessionDescription& remote_offer,
2115 AudioCodecs* audio_codecs,
2116 VideoCodecs* video_codecs) const {
2117 const webrtc::FieldTrialsView* field_trials =
2118 &transport_desc_factory_->trials();
2119 // First - get all codecs from the current description if the media type
2120 // is used. Add them to `used_pltypes` so the payload type is not reused if a
2121 // new media type is added.
2122 UsedPayloadTypes used_pltypes;
2123 MergeCodecsFromDescription(current_active_contents, audio_codecs,
2124 video_codecs, &used_pltypes, field_trials);
2125
2126 // Second - filter out codecs that we don't support at all and should ignore.
2127 AudioCodecs filtered_offered_audio_codecs;
2128 VideoCodecs filtered_offered_video_codecs;
2129 for (const ContentInfo& content : remote_offer.contents()) {
2130 if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
2131 const AudioContentDescription* audio =
2132 content.media_description()->as_audio();
2133 for (const AudioCodec& offered_audio_codec : audio->codecs()) {
2134 if (!FindMatchingCodec<AudioCodec>(
2135 audio->codecs(), filtered_offered_audio_codecs,
2136 offered_audio_codec, nullptr, field_trials) &&
2137 FindMatchingCodec<AudioCodec>(audio->codecs(), all_audio_codecs_,
2138 offered_audio_codec, nullptr,
2139 field_trials)) {
2140 filtered_offered_audio_codecs.push_back(offered_audio_codec);
2141 }
2142 }
2143 } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
2144 const VideoContentDescription* video =
2145 content.media_description()->as_video();
2146 for (const VideoCodec& offered_video_codec : video->codecs()) {
2147 if (!FindMatchingCodec<VideoCodec>(
2148 video->codecs(), filtered_offered_video_codecs,
2149 offered_video_codec, nullptr, field_trials) &&
2150 FindMatchingCodec<VideoCodec>(video->codecs(), all_video_codecs_,
2151 offered_video_codec, nullptr,
2152 field_trials)) {
2153 filtered_offered_video_codecs.push_back(offered_video_codec);
2154 }
2155 }
2156 }
2157 }
2158
2159 // Add codecs that are not in the current description but were in
2160 // `remote_offer`.
2161 MergeCodecs<AudioCodec>(filtered_offered_audio_codecs, audio_codecs,
2162 &used_pltypes, field_trials);
2163 MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
2164 &used_pltypes, field_trials);
2165 }
2166
2167 MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions
GetOfferedRtpHeaderExtensionsWithIds(const std::vector<const ContentInfo * > & current_active_contents,bool extmap_allow_mixed,const std::vector<MediaDescriptionOptions> & media_description_options) const2168 MediaSessionDescriptionFactory::GetOfferedRtpHeaderExtensionsWithIds(
2169 const std::vector<const ContentInfo*>& current_active_contents,
2170 bool extmap_allow_mixed,
2171 const std::vector<MediaDescriptionOptions>& media_description_options)
2172 const {
2173 // All header extensions allocated from the same range to avoid potential
2174 // issues when using BUNDLE.
2175
2176 // Strictly speaking the SDP attribute extmap_allow_mixed signals that the
2177 // receiver supports an RTP stream where one- and two-byte RTP header
2178 // extensions are mixed. For backwards compatibility reasons it's used in
2179 // WebRTC to signal that two-byte RTP header extensions are supported.
2180 UsedRtpHeaderExtensionIds used_ids(
2181 extmap_allow_mixed ? UsedRtpHeaderExtensionIds::IdDomain::kTwoByteAllowed
2182 : UsedRtpHeaderExtensionIds::IdDomain::kOneByteOnly);
2183 RtpHeaderExtensions all_regular_extensions;
2184 RtpHeaderExtensions all_encrypted_extensions;
2185
2186 AudioVideoRtpHeaderExtensions offered_extensions;
2187 // First - get all extensions from the current description if the media type
2188 // is used.
2189 // Add them to `used_ids` so the local ids are not reused if a new media
2190 // type is added.
2191 for (const ContentInfo* content : current_active_contents) {
2192 if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
2193 const AudioContentDescription* audio =
2194 content->media_description()->as_audio();
2195 MergeRtpHdrExts(audio->rtp_header_extensions(), &offered_extensions.audio,
2196 &all_regular_extensions, &all_encrypted_extensions,
2197 &used_ids);
2198 } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) {
2199 const VideoContentDescription* video =
2200 content->media_description()->as_video();
2201 MergeRtpHdrExts(video->rtp_header_extensions(), &offered_extensions.video,
2202 &all_regular_extensions, &all_encrypted_extensions,
2203 &used_ids);
2204 }
2205 }
2206
2207 // Add all encountered header extensions in the media description options that
2208 // are not in the current description.
2209
2210 for (const auto& entry : media_description_options) {
2211 RtpHeaderExtensions filtered_extensions =
2212 filtered_rtp_header_extensions(UnstoppedOrPresentRtpHeaderExtensions(
2213 entry.header_extensions, all_regular_extensions,
2214 all_encrypted_extensions));
2215 if (entry.type == MEDIA_TYPE_AUDIO)
2216 MergeRtpHdrExts(filtered_extensions, &offered_extensions.audio,
2217 &all_regular_extensions, &all_encrypted_extensions,
2218 &used_ids);
2219 else if (entry.type == MEDIA_TYPE_VIDEO)
2220 MergeRtpHdrExts(filtered_extensions, &offered_extensions.video,
2221 &all_regular_extensions, &all_encrypted_extensions,
2222 &used_ids);
2223 }
2224 // TODO(jbauch): Support adding encrypted header extensions to existing
2225 // sessions.
2226 if (enable_encrypted_rtp_header_extensions_ &&
2227 current_active_contents.empty()) {
2228 AddEncryptedVersionsOfHdrExts(&offered_extensions.audio,
2229 &all_encrypted_extensions, &used_ids);
2230 AddEncryptedVersionsOfHdrExts(&offered_extensions.video,
2231 &all_encrypted_extensions, &used_ids);
2232 }
2233 return offered_extensions;
2234 }
2235
AddTransportOffer(const std::string & content_name,const TransportOptions & transport_options,const SessionDescription * current_desc,SessionDescription * offer_desc,IceCredentialsIterator * ice_credentials) const2236 bool MediaSessionDescriptionFactory::AddTransportOffer(
2237 const std::string& content_name,
2238 const TransportOptions& transport_options,
2239 const SessionDescription* current_desc,
2240 SessionDescription* offer_desc,
2241 IceCredentialsIterator* ice_credentials) const {
2242 if (!transport_desc_factory_)
2243 return false;
2244 const TransportDescription* current_tdesc =
2245 GetTransportDescription(content_name, current_desc);
2246 std::unique_ptr<TransportDescription> new_tdesc(
2247 transport_desc_factory_->CreateOffer(transport_options, current_tdesc,
2248 ice_credentials));
2249 if (!new_tdesc) {
2250 RTC_LOG(LS_ERROR) << "Failed to AddTransportOffer, content name="
2251 << content_name;
2252 }
2253 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc));
2254 return true;
2255 }
2256
2257 std::unique_ptr<TransportDescription>
CreateTransportAnswer(const std::string & content_name,const SessionDescription * offer_desc,const TransportOptions & transport_options,const SessionDescription * current_desc,bool require_transport_attributes,IceCredentialsIterator * ice_credentials) const2258 MediaSessionDescriptionFactory::CreateTransportAnswer(
2259 const std::string& content_name,
2260 const SessionDescription* offer_desc,
2261 const TransportOptions& transport_options,
2262 const SessionDescription* current_desc,
2263 bool require_transport_attributes,
2264 IceCredentialsIterator* ice_credentials) const {
2265 if (!transport_desc_factory_)
2266 return NULL;
2267 const TransportDescription* offer_tdesc =
2268 GetTransportDescription(content_name, offer_desc);
2269 const TransportDescription* current_tdesc =
2270 GetTransportDescription(content_name, current_desc);
2271 return transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
2272 require_transport_attributes,
2273 current_tdesc, ice_credentials);
2274 }
2275
AddTransportAnswer(const std::string & content_name,const TransportDescription & transport_desc,SessionDescription * answer_desc) const2276 bool MediaSessionDescriptionFactory::AddTransportAnswer(
2277 const std::string& content_name,
2278 const TransportDescription& transport_desc,
2279 SessionDescription* answer_desc) const {
2280 answer_desc->AddTransportInfo(TransportInfo(content_name, transport_desc));
2281 return true;
2282 }
2283
2284 // `audio_codecs` = set of all possible codecs that can be used, with correct
2285 // payload type mappings
2286 //
2287 // `supported_audio_codecs` = set of codecs that are supported for the direction
2288 // of this m= section
2289 //
2290 // acd->codecs() = set of previously negotiated codecs for this m= section
2291 //
2292 // The payload types should come from audio_codecs, but the order should come
2293 // from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2294 // change existing codec priority, and that new codecs are added with the right
2295 // priority.
AddAudioContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,const RtpHeaderExtensions & audio_rtp_extensions,const AudioCodecs & audio_codecs,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2296 bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
2297 const MediaDescriptionOptions& media_description_options,
2298 const MediaSessionOptions& session_options,
2299 const ContentInfo* current_content,
2300 const SessionDescription* current_description,
2301 const RtpHeaderExtensions& audio_rtp_extensions,
2302 const AudioCodecs& audio_codecs,
2303 StreamParamsVec* current_streams,
2304 SessionDescription* desc,
2305 IceCredentialsIterator* ice_credentials) const {
2306 const webrtc::FieldTrialsView* field_trials =
2307 &transport_desc_factory_->trials();
2308 // Filter audio_codecs (which includes all codecs, with correctly remapped
2309 // payload types) based on transceiver direction.
2310 const AudioCodecs& supported_audio_codecs =
2311 GetAudioCodecsForOffer(media_description_options.direction);
2312
2313 AudioCodecs filtered_codecs;
2314
2315 if (!media_description_options.codec_preferences.empty()) {
2316 // Add the codecs from the current transceiver's codec preferences.
2317 // They override any existing codecs from previous negotiations.
2318 filtered_codecs = MatchCodecPreference(
2319 media_description_options.codec_preferences, audio_codecs,
2320 supported_audio_codecs, field_trials);
2321 } else {
2322 // Add the codecs from current content if it exists and is not rejected nor
2323 // recycled.
2324 if (current_content && !current_content->rejected &&
2325 current_content->name == media_description_options.mid) {
2326 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2327 const AudioContentDescription* acd =
2328 current_content->media_description()->as_audio();
2329 for (const AudioCodec& codec : acd->codecs()) {
2330 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2331 nullptr, field_trials)) {
2332 filtered_codecs.push_back(codec);
2333 }
2334 }
2335 }
2336 // Add other supported audio codecs.
2337 AudioCodec found_codec;
2338 for (const AudioCodec& codec : supported_audio_codecs) {
2339 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2340 codec, &found_codec, field_trials) &&
2341 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
2342 filtered_codecs, codec, nullptr,
2343 field_trials)) {
2344 // Use the `found_codec` from `audio_codecs` because it has the
2345 // correctly mapped payload type.
2346 filtered_codecs.push_back(found_codec);
2347 }
2348 }
2349 }
2350 if (!session_options.vad_enabled) {
2351 // If application doesn't want CN codecs in offer.
2352 StripCNCodecs(&filtered_codecs);
2353 }
2354
2355 cricket::SecurePolicy sdes_policy =
2356 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2357 : secure();
2358
2359 auto audio = std::make_unique<AudioContentDescription>();
2360 std::vector<std::string> crypto_suites;
2361 GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
2362 &crypto_suites);
2363 if (!CreateMediaContentOffer(
2364 media_description_options, session_options, filtered_codecs,
2365 sdes_policy, GetCryptos(current_content), crypto_suites,
2366 audio_rtp_extensions, ssrc_generator(), current_streams, audio.get(),
2367 transport_desc_factory_->trials())) {
2368 return false;
2369 }
2370
2371 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2372 SetMediaProtocol(secure_transport, audio.get());
2373
2374 audio->set_direction(media_description_options.direction);
2375
2376 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
2377 media_description_options.stopped, std::move(audio));
2378 if (!AddTransportOffer(media_description_options.mid,
2379 media_description_options.transport_options,
2380 current_description, desc, ice_credentials)) {
2381 return false;
2382 }
2383
2384 return true;
2385 }
2386
2387 // TODO(kron): This function is very similar to AddAudioContentForOffer.
2388 // Refactor to reuse shared code.
AddVideoContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,const RtpHeaderExtensions & video_rtp_extensions,const VideoCodecs & video_codecs,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2389 bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
2390 const MediaDescriptionOptions& media_description_options,
2391 const MediaSessionOptions& session_options,
2392 const ContentInfo* current_content,
2393 const SessionDescription* current_description,
2394 const RtpHeaderExtensions& video_rtp_extensions,
2395 const VideoCodecs& video_codecs,
2396 StreamParamsVec* current_streams,
2397 SessionDescription* desc,
2398 IceCredentialsIterator* ice_credentials) const {
2399 const webrtc::FieldTrialsView* field_trials =
2400 &transport_desc_factory_->trials();
2401 // Filter video_codecs (which includes all codecs, with correctly remapped
2402 // payload types) based on transceiver direction.
2403 const VideoCodecs& supported_video_codecs =
2404 GetVideoCodecsForOffer(media_description_options.direction);
2405
2406 VideoCodecs filtered_codecs;
2407
2408 if (!media_description_options.codec_preferences.empty()) {
2409 // Add the codecs from the current transceiver's codec preferences.
2410 // They override any existing codecs from previous negotiations.
2411 filtered_codecs = MatchCodecPreference(
2412 media_description_options.codec_preferences, video_codecs,
2413 supported_video_codecs, field_trials);
2414 } else {
2415 // Add the codecs from current content if it exists and is not rejected nor
2416 // recycled.
2417 if (current_content && !current_content->rejected &&
2418 current_content->name == media_description_options.mid) {
2419 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2420 const VideoContentDescription* vcd =
2421 current_content->media_description()->as_video();
2422 for (const VideoCodec& codec : vcd->codecs()) {
2423 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
2424 nullptr, field_trials)) {
2425 filtered_codecs.push_back(codec);
2426 }
2427 }
2428 }
2429 // Add other supported video codecs.
2430 VideoCodec found_codec;
2431 for (const VideoCodec& codec : supported_video_codecs) {
2432 if (FindMatchingCodec<VideoCodec>(supported_video_codecs, video_codecs,
2433 codec, &found_codec, field_trials) &&
2434 !FindMatchingCodec<VideoCodec>(supported_video_codecs,
2435 filtered_codecs, codec, nullptr,
2436 field_trials)) {
2437 // Use the `found_codec` from `video_codecs` because it has the
2438 // correctly mapped payload type.
2439 if (IsRtxCodec(codec)) {
2440 // For RTX we might need to adjust the apt parameter if we got a
2441 // remote offer without RTX for a codec for which we support RTX.
2442 auto referenced_codec =
2443 GetAssociatedCodecForRtx(supported_video_codecs, codec);
2444 RTC_DCHECK(referenced_codec);
2445
2446 // Find the codec we should be referencing and point to it.
2447 VideoCodec changed_referenced_codec;
2448 if (FindMatchingCodec<VideoCodec>(
2449 supported_video_codecs, filtered_codecs, *referenced_codec,
2450 &changed_referenced_codec, field_trials)) {
2451 found_codec.SetParam(kCodecParamAssociatedPayloadType,
2452 changed_referenced_codec.id);
2453 }
2454 }
2455 filtered_codecs.push_back(found_codec);
2456 }
2457 }
2458 }
2459
2460 if (session_options.raw_packetization_for_video) {
2461 for (VideoCodec& codec : filtered_codecs) {
2462 if (codec.GetCodecType() == VideoCodec::CODEC_VIDEO) {
2463 codec.packetization = kPacketizationParamRaw;
2464 }
2465 }
2466 }
2467
2468 cricket::SecurePolicy sdes_policy =
2469 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2470 : secure();
2471 auto video = std::make_unique<VideoContentDescription>();
2472 std::vector<std::string> crypto_suites;
2473 GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
2474 &crypto_suites);
2475 if (!CreateMediaContentOffer(
2476 media_description_options, session_options, filtered_codecs,
2477 sdes_policy, GetCryptos(current_content), crypto_suites,
2478 video_rtp_extensions, ssrc_generator(), current_streams, video.get(),
2479 transport_desc_factory_->trials())) {
2480 return false;
2481 }
2482
2483 video->set_bandwidth(kAutoBandwidth);
2484
2485 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2486 SetMediaProtocol(secure_transport, video.get());
2487
2488 video->set_direction(media_description_options.direction);
2489
2490 desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
2491 media_description_options.stopped, std::move(video));
2492 if (!AddTransportOffer(media_description_options.mid,
2493 media_description_options.transport_options,
2494 current_description, desc, ice_credentials)) {
2495 return false;
2496 }
2497
2498 return true;
2499 }
2500
AddDataContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2501 bool MediaSessionDescriptionFactory::AddDataContentForOffer(
2502 const MediaDescriptionOptions& media_description_options,
2503 const MediaSessionOptions& session_options,
2504 const ContentInfo* current_content,
2505 const SessionDescription* current_description,
2506 StreamParamsVec* current_streams,
2507 SessionDescription* desc,
2508 IceCredentialsIterator* ice_credentials) const {
2509 auto data = std::make_unique<SctpDataContentDescription>();
2510
2511 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
2512
2513 cricket::SecurePolicy sdes_policy =
2514 IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
2515 : secure();
2516 std::vector<std::string> crypto_suites;
2517 // SDES doesn't make sense for SCTP, so we disable it, and we only
2518 // get SDES crypto suites for RTP-based data channels.
2519 sdes_policy = cricket::SEC_DISABLED;
2520 // Unlike SetMediaProtocol below, we need to set the protocol
2521 // before we call CreateMediaContentOffer. Otherwise,
2522 // CreateMediaContentOffer won't know this is SCTP and will
2523 // generate SSRCs rather than SIDs.
2524 data->set_protocol(secure_transport ? kMediaProtocolUdpDtlsSctp
2525 : kMediaProtocolSctp);
2526 data->set_use_sctpmap(session_options.use_obsolete_sctp_sdp);
2527 data->set_max_message_size(kSctpSendBufferSize);
2528
2529 if (!CreateContentOffer(media_description_options, session_options,
2530 sdes_policy, GetCryptos(current_content),
2531 crypto_suites, RtpHeaderExtensions(),
2532 ssrc_generator(), current_streams, data.get())) {
2533 return false;
2534 }
2535
2536 desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
2537 media_description_options.stopped, std::move(data));
2538 if (!AddTransportOffer(media_description_options.mid,
2539 media_description_options.transport_options,
2540 current_description, desc, ice_credentials)) {
2541 return false;
2542 }
2543 return true;
2544 }
2545
AddUnsupportedContentForOffer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * current_content,const SessionDescription * current_description,SessionDescription * desc,IceCredentialsIterator * ice_credentials) const2546 bool MediaSessionDescriptionFactory::AddUnsupportedContentForOffer(
2547 const MediaDescriptionOptions& media_description_options,
2548 const MediaSessionOptions& session_options,
2549 const ContentInfo* current_content,
2550 const SessionDescription* current_description,
2551 SessionDescription* desc,
2552 IceCredentialsIterator* ice_credentials) const {
2553 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_UNSUPPORTED));
2554
2555 const UnsupportedContentDescription* current_unsupported_description =
2556 current_content->media_description()->as_unsupported();
2557 auto unsupported = std::make_unique<UnsupportedContentDescription>(
2558 current_unsupported_description->media_type());
2559 unsupported->set_protocol(current_content->media_description()->protocol());
2560 desc->AddContent(media_description_options.mid, MediaProtocolType::kOther,
2561 /*rejected=*/true, std::move(unsupported));
2562
2563 if (!AddTransportOffer(media_description_options.mid,
2564 media_description_options.transport_options,
2565 current_description, desc, ice_credentials)) {
2566 return false;
2567 }
2568 return true;
2569 }
2570
2571 // `audio_codecs` = set of all possible codecs that can be used, with correct
2572 // payload type mappings
2573 //
2574 // `supported_audio_codecs` = set of codecs that are supported for the direction
2575 // of this m= section
2576 //
2577 // acd->codecs() = set of previously negotiated codecs for this m= section
2578 //
2579 // The payload types should come from audio_codecs, but the order should come
2580 // from acd->codecs() and then supported_codecs, to ensure that re-offers don't
2581 // change existing codec priority, and that new codecs are added with the right
2582 // priority.
AddAudioContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,const AudioCodecs & audio_codecs,const RtpHeaderExtensions & default_audio_rtp_header_extensions,StreamParamsVec * current_streams,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2583 bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
2584 const MediaDescriptionOptions& media_description_options,
2585 const MediaSessionOptions& session_options,
2586 const ContentInfo* offer_content,
2587 const SessionDescription* offer_description,
2588 const ContentInfo* current_content,
2589 const SessionDescription* current_description,
2590 const TransportInfo* bundle_transport,
2591 const AudioCodecs& audio_codecs,
2592 const RtpHeaderExtensions& default_audio_rtp_header_extensions,
2593 StreamParamsVec* current_streams,
2594 SessionDescription* answer,
2595 IceCredentialsIterator* ice_credentials) const {
2596 const webrtc::FieldTrialsView* field_trials =
2597 &transport_desc_factory_->trials();
2598 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO));
2599 const AudioContentDescription* offer_audio_description =
2600 offer_content->media_description()->as_audio();
2601
2602 std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer(
2603 media_description_options.mid, offer_description,
2604 media_description_options.transport_options, current_description,
2605 bundle_transport != nullptr, ice_credentials);
2606 if (!audio_transport) {
2607 return false;
2608 }
2609
2610 // Pick codecs based on the requested communications direction in the offer
2611 // and the selected direction in the answer.
2612 // Note these will be filtered one final time in CreateMediaContentAnswer.
2613 auto wants_rtd = media_description_options.direction;
2614 auto offer_rtd = offer_audio_description->direction();
2615 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
2616 AudioCodecs supported_audio_codecs =
2617 GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
2618
2619 AudioCodecs filtered_codecs;
2620
2621 if (!media_description_options.codec_preferences.empty()) {
2622 filtered_codecs = MatchCodecPreference(
2623 media_description_options.codec_preferences, audio_codecs,
2624 supported_audio_codecs, field_trials);
2625 } else {
2626 // Add the codecs from current content if it exists and is not rejected nor
2627 // recycled.
2628 if (current_content && !current_content->rejected &&
2629 current_content->name == media_description_options.mid) {
2630 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
2631 const AudioContentDescription* acd =
2632 current_content->media_description()->as_audio();
2633 for (const AudioCodec& codec : acd->codecs()) {
2634 if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
2635 nullptr, field_trials)) {
2636 filtered_codecs.push_back(codec);
2637 }
2638 }
2639 }
2640 // Add other supported audio codecs.
2641 for (const AudioCodec& codec : supported_audio_codecs) {
2642 if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
2643 codec, nullptr, field_trials) &&
2644 !FindMatchingCodec<AudioCodec>(supported_audio_codecs,
2645 filtered_codecs, codec, nullptr,
2646 field_trials)) {
2647 // We should use the local codec with local parameters and the codec id
2648 // would be correctly mapped in `NegotiateCodecs`.
2649 filtered_codecs.push_back(codec);
2650 }
2651 }
2652 }
2653 if (!session_options.vad_enabled) {
2654 // If application doesn't want CN codecs in answer.
2655 StripCNCodecs(&filtered_codecs);
2656 }
2657
2658 // Determine if we have media codecs in common.
2659 bool has_common_media_codecs =
2660 std::find_if(filtered_codecs.begin(), filtered_codecs.end(),
2661 [](const AudioCodec& c) {
2662 return !(IsRedCodec(c) || IsComfortNoiseCodec(c));
2663 }) != filtered_codecs.end();
2664
2665 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2666 session_options.bundle_enabled;
2667 auto audio_answer = std::make_unique<AudioContentDescription>();
2668 // Do not require or create SDES cryptos if DTLS is used.
2669 cricket::SecurePolicy sdes_policy =
2670 audio_transport->secure() ? cricket::SEC_DISABLED : secure();
2671 if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,
2672 media_description_options, session_options,
2673 ssrc_generator(), current_streams, audio_answer.get(),
2674 transport_desc_factory_->trials())) {
2675 return false;
2676 }
2677 if (!CreateMediaContentAnswer(
2678 offer_audio_description, media_description_options, session_options,
2679 sdes_policy, GetCryptos(current_content),
2680 filtered_rtp_header_extensions(default_audio_rtp_header_extensions),
2681 ssrc_generator(), enable_encrypted_rtp_header_extensions_,
2682 current_streams, bundle_enabled, audio_answer.get())) {
2683 return false; // Fails the session setup.
2684 }
2685
2686 bool secure = bundle_transport ? bundle_transport->description.secure()
2687 : audio_transport->secure();
2688 bool rejected = media_description_options.stopped ||
2689 offer_content->rejected || !has_common_media_codecs ||
2690 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
2691 audio_answer->protocol(), secure);
2692 if (!AddTransportAnswer(media_description_options.mid,
2693 *(audio_transport.get()), answer)) {
2694 return false;
2695 }
2696
2697 if (rejected) {
2698 RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid
2699 << "' being rejected in answer.";
2700 }
2701
2702 answer->AddContent(media_description_options.mid, offer_content->type,
2703 rejected, std::move(audio_answer));
2704 return true;
2705 }
2706
2707 // TODO(kron): This function is very similar to AddAudioContentForAnswer.
2708 // Refactor to reuse shared code.
AddVideoContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,const VideoCodecs & video_codecs,const RtpHeaderExtensions & default_video_rtp_header_extensions,StreamParamsVec * current_streams,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2709 bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
2710 const MediaDescriptionOptions& media_description_options,
2711 const MediaSessionOptions& session_options,
2712 const ContentInfo* offer_content,
2713 const SessionDescription* offer_description,
2714 const ContentInfo* current_content,
2715 const SessionDescription* current_description,
2716 const TransportInfo* bundle_transport,
2717 const VideoCodecs& video_codecs,
2718 const RtpHeaderExtensions& default_video_rtp_header_extensions,
2719 StreamParamsVec* current_streams,
2720 SessionDescription* answer,
2721 IceCredentialsIterator* ice_credentials) const {
2722 const webrtc::FieldTrialsView* field_trials =
2723 &transport_desc_factory_->trials();
2724 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
2725 const VideoContentDescription* offer_video_description =
2726 offer_content->media_description()->as_video();
2727
2728 std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
2729 media_description_options.mid, offer_description,
2730 media_description_options.transport_options, current_description,
2731 bundle_transport != nullptr, ice_credentials);
2732 if (!video_transport) {
2733 return false;
2734 }
2735
2736 // Pick codecs based on the requested communications direction in the offer
2737 // and the selected direction in the answer.
2738 // Note these will be filtered one final time in CreateMediaContentAnswer.
2739 auto wants_rtd = media_description_options.direction;
2740 auto offer_rtd = offer_video_description->direction();
2741 auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
2742 VideoCodecs supported_video_codecs =
2743 GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
2744
2745 VideoCodecs filtered_codecs;
2746
2747 if (!media_description_options.codec_preferences.empty()) {
2748 filtered_codecs = MatchCodecPreference(
2749 media_description_options.codec_preferences, video_codecs,
2750 supported_video_codecs, field_trials);
2751 } else {
2752 // Add the codecs from current content if it exists and is not rejected nor
2753 // recycled.
2754 if (current_content && !current_content->rejected &&
2755 current_content->name == media_description_options.mid) {
2756 RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
2757 const VideoContentDescription* vcd =
2758 current_content->media_description()->as_video();
2759 for (const VideoCodec& codec : vcd->codecs()) {
2760 if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
2761 nullptr, field_trials)) {
2762 filtered_codecs.push_back(codec);
2763 }
2764 }
2765 }
2766
2767 // Add other supported video codecs.
2768 VideoCodecs other_video_codecs;
2769 for (const VideoCodec& codec : supported_video_codecs) {
2770 if (FindMatchingCodec<VideoCodec>(supported_video_codecs, video_codecs,
2771 codec, nullptr, field_trials) &&
2772 !FindMatchingCodec<VideoCodec>(supported_video_codecs,
2773 filtered_codecs, codec, nullptr,
2774 field_trials)) {
2775 // We should use the local codec with local parameters and the codec id
2776 // would be correctly mapped in `NegotiateCodecs`.
2777 other_video_codecs.push_back(codec);
2778 }
2779 }
2780
2781 // Use ComputeCodecsUnion to avoid having duplicate payload IDs
2782 filtered_codecs = ComputeCodecsUnion<VideoCodec>(
2783 filtered_codecs, other_video_codecs, field_trials);
2784 }
2785 // Determine if we have media codecs in common.
2786 bool has_common_media_codecs =
2787 std::find_if(
2788 filtered_codecs.begin(), filtered_codecs.end(),
2789 [](const VideoCodec& c) {
2790 return !(IsRedCodec(c) || IsUlpfecCodec(c) || IsFlexfecCodec(c));
2791 }) != filtered_codecs.end();
2792
2793 if (session_options.raw_packetization_for_video) {
2794 for (VideoCodec& codec : filtered_codecs) {
2795 if (codec.GetCodecType() == VideoCodec::CODEC_VIDEO) {
2796 codec.packetization = kPacketizationParamRaw;
2797 }
2798 }
2799 }
2800
2801 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2802 session_options.bundle_enabled;
2803 auto video_answer = std::make_unique<VideoContentDescription>();
2804 // Do not require or create SDES cryptos if DTLS is used.
2805 cricket::SecurePolicy sdes_policy =
2806 video_transport->secure() ? cricket::SEC_DISABLED : secure();
2807 if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
2808 media_description_options, session_options,
2809 ssrc_generator(), current_streams, video_answer.get(),
2810 transport_desc_factory_->trials())) {
2811 return false;
2812 }
2813 if (!CreateMediaContentAnswer(
2814 offer_video_description, media_description_options, session_options,
2815 sdes_policy, GetCryptos(current_content),
2816 filtered_rtp_header_extensions(default_video_rtp_header_extensions),
2817 ssrc_generator(), enable_encrypted_rtp_header_extensions_,
2818 current_streams, bundle_enabled, video_answer.get())) {
2819 return false; // Failed the session setup.
2820 }
2821 bool secure = bundle_transport ? bundle_transport->description.secure()
2822 : video_transport->secure();
2823 bool rejected = media_description_options.stopped ||
2824 offer_content->rejected || !has_common_media_codecs ||
2825 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
2826 video_answer->protocol(), secure);
2827 if (!AddTransportAnswer(media_description_options.mid,
2828 *(video_transport.get()), answer)) {
2829 return false;
2830 }
2831
2832 if (!rejected) {
2833 video_answer->set_bandwidth(kAutoBandwidth);
2834 } else {
2835 RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
2836 << "' being rejected in answer.";
2837 }
2838 answer->AddContent(media_description_options.mid, offer_content->type,
2839 rejected, std::move(video_answer));
2840 return true;
2841 }
2842
AddDataContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,StreamParamsVec * current_streams,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2843 bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
2844 const MediaDescriptionOptions& media_description_options,
2845 const MediaSessionOptions& session_options,
2846 const ContentInfo* offer_content,
2847 const SessionDescription* offer_description,
2848 const ContentInfo* current_content,
2849 const SessionDescription* current_description,
2850 const TransportInfo* bundle_transport,
2851 StreamParamsVec* current_streams,
2852 SessionDescription* answer,
2853 IceCredentialsIterator* ice_credentials) const {
2854 std::unique_ptr<TransportDescription> data_transport = CreateTransportAnswer(
2855 media_description_options.mid, offer_description,
2856 media_description_options.transport_options, current_description,
2857 bundle_transport != nullptr, ice_credentials);
2858 if (!data_transport) {
2859 return false;
2860 }
2861
2862 // Do not require or create SDES cryptos if DTLS is used.
2863 cricket::SecurePolicy sdes_policy =
2864 data_transport->secure() ? cricket::SEC_DISABLED : secure();
2865 bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
2866 session_options.bundle_enabled;
2867 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
2868 std::unique_ptr<MediaContentDescription> data_answer;
2869 if (offer_content->media_description()->as_sctp()) {
2870 // SCTP data content
2871 data_answer = std::make_unique<SctpDataContentDescription>();
2872 const SctpDataContentDescription* offer_data_description =
2873 offer_content->media_description()->as_sctp();
2874 // Respond with the offerer's proto, whatever it is.
2875 data_answer->as_sctp()->set_protocol(offer_data_description->protocol());
2876 // Respond with our max message size or the remote max messsage size,
2877 // whichever is smaller.
2878 // 0 is treated specially - it means "I can accept any size". Since
2879 // we do not implement infinite size messages, reply with
2880 // kSctpSendBufferSize.
2881 if (offer_data_description->max_message_size() == 0) {
2882 data_answer->as_sctp()->set_max_message_size(kSctpSendBufferSize);
2883 } else {
2884 data_answer->as_sctp()->set_max_message_size(std::min(
2885 offer_data_description->max_message_size(), kSctpSendBufferSize));
2886 }
2887 if (!CreateMediaContentAnswer(
2888 offer_data_description, media_description_options, session_options,
2889 sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
2890 ssrc_generator(), enable_encrypted_rtp_header_extensions_,
2891 current_streams, bundle_enabled, data_answer.get())) {
2892 return false; // Fails the session setup.
2893 }
2894 // Respond with sctpmap if the offer uses sctpmap.
2895 bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
2896 data_answer->as_sctp()->set_use_sctpmap(offer_uses_sctpmap);
2897 } else {
2898 RTC_DCHECK_NOTREACHED() << "Non-SCTP data content found";
2899 }
2900
2901 bool secure = bundle_transport ? bundle_transport->description.secure()
2902 : data_transport->secure();
2903
2904 bool rejected = media_description_options.stopped ||
2905 offer_content->rejected ||
2906 !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2907 data_answer->protocol(), secure);
2908 if (!AddTransportAnswer(media_description_options.mid,
2909 *(data_transport.get()), answer)) {
2910 return false;
2911 }
2912
2913 answer->AddContent(media_description_options.mid, offer_content->type,
2914 rejected, std::move(data_answer));
2915 return true;
2916 }
2917
AddUnsupportedContentForAnswer(const MediaDescriptionOptions & media_description_options,const MediaSessionOptions & session_options,const ContentInfo * offer_content,const SessionDescription * offer_description,const ContentInfo * current_content,const SessionDescription * current_description,const TransportInfo * bundle_transport,SessionDescription * answer,IceCredentialsIterator * ice_credentials) const2918 bool MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer(
2919 const MediaDescriptionOptions& media_description_options,
2920 const MediaSessionOptions& session_options,
2921 const ContentInfo* offer_content,
2922 const SessionDescription* offer_description,
2923 const ContentInfo* current_content,
2924 const SessionDescription* current_description,
2925 const TransportInfo* bundle_transport,
2926 SessionDescription* answer,
2927 IceCredentialsIterator* ice_credentials) const {
2928 std::unique_ptr<TransportDescription> unsupported_transport =
2929 CreateTransportAnswer(media_description_options.mid, offer_description,
2930 media_description_options.transport_options,
2931 current_description, bundle_transport != nullptr,
2932 ice_credentials);
2933 if (!unsupported_transport) {
2934 return false;
2935 }
2936 RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_UNSUPPORTED));
2937
2938 const UnsupportedContentDescription* offer_unsupported_description =
2939 offer_content->media_description()->as_unsupported();
2940 std::unique_ptr<MediaContentDescription> unsupported_answer =
2941 std::make_unique<UnsupportedContentDescription>(
2942 offer_unsupported_description->media_type());
2943 unsupported_answer->set_protocol(offer_unsupported_description->protocol());
2944
2945 if (!AddTransportAnswer(media_description_options.mid,
2946 *(unsupported_transport.get()), answer)) {
2947 return false;
2948 }
2949 answer->AddContent(media_description_options.mid, offer_content->type,
2950 /*rejected=*/true, std::move(unsupported_answer));
2951 return true;
2952 }
2953
ComputeAudioCodecsIntersectionAndUnion()2954 void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
2955 const webrtc::FieldTrialsView* field_trials =
2956 &transport_desc_factory_->trials();
2957 audio_sendrecv_codecs_.clear();
2958 all_audio_codecs_.clear();
2959 // Compute the audio codecs union.
2960 for (const AudioCodec& send : audio_send_codecs_) {
2961 all_audio_codecs_.push_back(send);
2962 if (!FindMatchingCodec<AudioCodec>(audio_send_codecs_, audio_recv_codecs_,
2963 send, nullptr, field_trials)) {
2964 // It doesn't make sense to have an RTX codec we support sending but not
2965 // receiving.
2966 RTC_DCHECK(!IsRtxCodec(send));
2967 }
2968 }
2969 for (const AudioCodec& recv : audio_recv_codecs_) {
2970 if (!FindMatchingCodec<AudioCodec>(audio_recv_codecs_, audio_send_codecs_,
2971 recv, nullptr, field_trials)) {
2972 all_audio_codecs_.push_back(recv);
2973 }
2974 }
2975 // Use NegotiateCodecs to merge our codec lists, since the operation is
2976 // essentially the same. Put send_codecs as the offered_codecs, which is the
2977 // order we'd like to follow. The reasoning is that encoding is usually more
2978 // expensive than decoding, and prioritizing a codec in the send list probably
2979 // means it's a codec we can handle efficiently.
2980 NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
2981 &audio_sendrecv_codecs_, true, field_trials);
2982 }
2983
ComputeVideoCodecsIntersectionAndUnion()2984 void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() {
2985 const webrtc::FieldTrialsView* field_trials =
2986 &transport_desc_factory_->trials();
2987 video_sendrecv_codecs_.clear();
2988
2989 // Use ComputeCodecsUnion to avoid having duplicate payload IDs
2990 all_video_codecs_ =
2991 ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_, field_trials);
2992
2993 // Use NegotiateCodecs to merge our codec lists, since the operation is
2994 // essentially the same. Put send_codecs as the offered_codecs, which is the
2995 // order we'd like to follow. The reasoning is that encoding is usually more
2996 // expensive than decoding, and prioritizing a codec in the send list probably
2997 // means it's a codec we can handle efficiently.
2998 NegotiateCodecs(video_recv_codecs_, video_send_codecs_,
2999 &video_sendrecv_codecs_, true, field_trials);
3000 }
3001
IsMediaContent(const ContentInfo * content)3002 bool IsMediaContent(const ContentInfo* content) {
3003 return (content && (content->type == MediaProtocolType::kRtp ||
3004 content->type == MediaProtocolType::kSctp));
3005 }
3006
IsAudioContent(const ContentInfo * content)3007 bool IsAudioContent(const ContentInfo* content) {
3008 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
3009 }
3010
IsVideoContent(const ContentInfo * content)3011 bool IsVideoContent(const ContentInfo* content) {
3012 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
3013 }
3014
IsDataContent(const ContentInfo * content)3015 bool IsDataContent(const ContentInfo* content) {
3016 return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
3017 }
3018
IsUnsupportedContent(const ContentInfo * content)3019 bool IsUnsupportedContent(const ContentInfo* content) {
3020 return IsMediaContentOfType(content, MEDIA_TYPE_UNSUPPORTED);
3021 }
3022
GetFirstMediaContent(const ContentInfos & contents,MediaType media_type)3023 const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
3024 MediaType media_type) {
3025 for (const ContentInfo& content : contents) {
3026 if (IsMediaContentOfType(&content, media_type)) {
3027 return &content;
3028 }
3029 }
3030 return nullptr;
3031 }
3032
GetFirstAudioContent(const ContentInfos & contents)3033 const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
3034 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
3035 }
3036
GetFirstVideoContent(const ContentInfos & contents)3037 const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
3038 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
3039 }
3040
GetFirstDataContent(const ContentInfos & contents)3041 const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
3042 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
3043 }
3044
GetFirstMediaContent(const SessionDescription * sdesc,MediaType media_type)3045 const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
3046 MediaType media_type) {
3047 if (sdesc == nullptr) {
3048 return nullptr;
3049 }
3050
3051 return GetFirstMediaContent(sdesc->contents(), media_type);
3052 }
3053
GetFirstAudioContent(const SessionDescription * sdesc)3054 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
3055 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
3056 }
3057
GetFirstVideoContent(const SessionDescription * sdesc)3058 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
3059 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
3060 }
3061
GetFirstDataContent(const SessionDescription * sdesc)3062 const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
3063 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
3064 }
3065
GetFirstMediaContentDescription(const SessionDescription * sdesc,MediaType media_type)3066 const MediaContentDescription* GetFirstMediaContentDescription(
3067 const SessionDescription* sdesc,
3068 MediaType media_type) {
3069 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
3070 return (content ? content->media_description() : nullptr);
3071 }
3072
GetFirstAudioContentDescription(const SessionDescription * sdesc)3073 const AudioContentDescription* GetFirstAudioContentDescription(
3074 const SessionDescription* sdesc) {
3075 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
3076 return desc ? desc->as_audio() : nullptr;
3077 }
3078
GetFirstVideoContentDescription(const SessionDescription * sdesc)3079 const VideoContentDescription* GetFirstVideoContentDescription(
3080 const SessionDescription* sdesc) {
3081 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
3082 return desc ? desc->as_video() : nullptr;
3083 }
3084
GetFirstSctpDataContentDescription(const SessionDescription * sdesc)3085 const SctpDataContentDescription* GetFirstSctpDataContentDescription(
3086 const SessionDescription* sdesc) {
3087 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
3088 return desc ? desc->as_sctp() : nullptr;
3089 }
3090
3091 //
3092 // Non-const versions of the above functions.
3093 //
3094
GetFirstMediaContent(ContentInfos * contents,MediaType media_type)3095 ContentInfo* GetFirstMediaContent(ContentInfos* contents,
3096 MediaType media_type) {
3097 for (ContentInfo& content : *contents) {
3098 if (IsMediaContentOfType(&content, media_type)) {
3099 return &content;
3100 }
3101 }
3102 return nullptr;
3103 }
3104
GetFirstAudioContent(ContentInfos * contents)3105 ContentInfo* GetFirstAudioContent(ContentInfos* contents) {
3106 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
3107 }
3108
GetFirstVideoContent(ContentInfos * contents)3109 ContentInfo* GetFirstVideoContent(ContentInfos* contents) {
3110 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
3111 }
3112
GetFirstDataContent(ContentInfos * contents)3113 ContentInfo* GetFirstDataContent(ContentInfos* contents) {
3114 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
3115 }
3116
GetFirstMediaContent(SessionDescription * sdesc,MediaType media_type)3117 ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
3118 MediaType media_type) {
3119 if (sdesc == nullptr) {
3120 return nullptr;
3121 }
3122
3123 return GetFirstMediaContent(&sdesc->contents(), media_type);
3124 }
3125
GetFirstAudioContent(SessionDescription * sdesc)3126 ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
3127 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
3128 }
3129
GetFirstVideoContent(SessionDescription * sdesc)3130 ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
3131 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
3132 }
3133
GetFirstDataContent(SessionDescription * sdesc)3134 ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
3135 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
3136 }
3137
GetFirstMediaContentDescription(SessionDescription * sdesc,MediaType media_type)3138 MediaContentDescription* GetFirstMediaContentDescription(
3139 SessionDescription* sdesc,
3140 MediaType media_type) {
3141 ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
3142 return (content ? content->media_description() : nullptr);
3143 }
3144
GetFirstAudioContentDescription(SessionDescription * sdesc)3145 AudioContentDescription* GetFirstAudioContentDescription(
3146 SessionDescription* sdesc) {
3147 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
3148 return desc ? desc->as_audio() : nullptr;
3149 }
3150
GetFirstVideoContentDescription(SessionDescription * sdesc)3151 VideoContentDescription* GetFirstVideoContentDescription(
3152 SessionDescription* sdesc) {
3153 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
3154 return desc ? desc->as_video() : nullptr;
3155 }
3156
GetFirstSctpDataContentDescription(SessionDescription * sdesc)3157 SctpDataContentDescription* GetFirstSctpDataContentDescription(
3158 SessionDescription* sdesc) {
3159 auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
3160 return desc ? desc->as_sctp() : nullptr;
3161 }
3162
3163 } // namespace cricket
3164