xref: /aosp_15_r20/external/webrtc/pc/media_session.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 - &current_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 = &current_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, &current_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, &current_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                                     &current_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 = &current_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, &current_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, &current_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                 &current_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