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 "webrtc/pc/mediasession.h"
12 
13 #include <algorithm>  // For std::find_if, std::sort.
14 #include <functional>
15 #include <map>
16 #include <memory>
17 #include <set>
18 #include <unordered_map>
19 #include <utility>
20 
21 #include "webrtc/base/base64.h"
22 #include "webrtc/base/checks.h"
23 #include "webrtc/base/helpers.h"
24 #include "webrtc/base/logging.h"
25 #include "webrtc/base/stringutils.h"
26 #include "webrtc/common_video/h264/profile_level_id.h"
27 #include "webrtc/media/base/cryptoparams.h"
28 #include "webrtc/media/base/mediaconstants.h"
29 #include "webrtc/p2p/base/p2pconstants.h"
30 #include "webrtc/pc/channelmanager.h"
31 #include "webrtc/pc/srtpfilter.h"
32 
33 namespace {
34 const char kInline[] = "inline:";
35 
GetSupportedCryptoSuiteNames(void (* func)(const rtc::CryptoOptions &,std::vector<int> *),const rtc::CryptoOptions & crypto_options,std::vector<std::string> * names)36 void GetSupportedCryptoSuiteNames(void (*func)(const rtc::CryptoOptions&,
37                                       std::vector<int>*),
38                                   const rtc::CryptoOptions& crypto_options,
39                                   std::vector<std::string>* names) {
40 #ifdef HAVE_SRTP
41   std::vector<int> crypto_suites;
42   func(crypto_options, &crypto_suites);
43   for (const auto crypto : crypto_suites) {
44     names->push_back(rtc::SrtpCryptoSuiteToName(crypto));
45   }
46 #endif
47 }
48 }  // namespace
49 
50 namespace cricket {
51 
52 // RTP Profile names
53 // http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
54 // RFC4585
55 const char kMediaProtocolAvpf[] = "RTP/AVPF";
56 // RFC5124
57 const char kMediaProtocolDtlsSavpf[] = "UDP/TLS/RTP/SAVPF";
58 
59 // We always generate offers with "UDP/TLS/RTP/SAVPF" when using DTLS-SRTP,
60 // but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
61 const char kMediaProtocolSavpf[] = "RTP/SAVPF";
62 
63 const char kMediaProtocolRtpPrefix[] = "RTP/";
64 
65 const char kMediaProtocolSctp[] = "SCTP";
66 const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
67 const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
68 const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
69 
FromMediaContentDirection(MediaContentDirection md)70 RtpTransceiverDirection RtpTransceiverDirection::FromMediaContentDirection(
71     MediaContentDirection md) {
72   const bool send = (md == MD_SENDRECV || md == MD_SENDONLY);
73   const bool recv = (md == MD_SENDRECV || md == MD_RECVONLY);
74   return RtpTransceiverDirection(send, recv);
75 }
76 
ToMediaContentDirection() const77 MediaContentDirection RtpTransceiverDirection::ToMediaContentDirection() const {
78   if (send && recv) {
79     return MD_SENDRECV;
80   } else if (send) {
81     return MD_SENDONLY;
82   } else if (recv) {
83     return MD_RECVONLY;
84   }
85 
86   return MD_INACTIVE;
87 }
88 
89 RtpTransceiverDirection
NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,RtpTransceiverDirection wants)90 NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
91                                  RtpTransceiverDirection wants) {
92   return RtpTransceiverDirection(offer.recv && wants.send,
93                                  offer.send && wants.recv);
94 }
95 
IsMediaContentOfType(const ContentInfo * content,MediaType media_type)96 static bool IsMediaContentOfType(const ContentInfo* content,
97                                  MediaType media_type) {
98   if (!IsMediaContent(content)) {
99     return false;
100   }
101 
102   const MediaContentDescription* mdesc =
103       static_cast<const MediaContentDescription*>(content->description);
104   return mdesc && mdesc->type() == media_type;
105 }
106 
CreateCryptoParams(int tag,const std::string & cipher,CryptoParams * out)107 static bool CreateCryptoParams(int tag, const std::string& cipher,
108                                CryptoParams *out) {
109   int key_len;
110   int salt_len;
111   if (!rtc::GetSrtpKeyAndSaltLengths(
112       rtc::SrtpCryptoSuiteFromName(cipher), &key_len, &salt_len)) {
113     return false;
114   }
115 
116   int master_key_len = key_len + salt_len;
117   std::string master_key;
118   if (!rtc::CreateRandomData(master_key_len, &master_key)) {
119     return false;
120   }
121 
122   RTC_CHECK_EQ(master_key_len, master_key.size());
123   std::string key = rtc::Base64::Encode(master_key);
124 
125   out->tag = tag;
126   out->cipher_suite = cipher;
127   out->key_params = kInline;
128   out->key_params += key;
129   return true;
130 }
131 
132 #ifdef HAVE_SRTP
AddCryptoParams(const std::string & cipher_suite,CryptoParamsVec * out)133 static bool AddCryptoParams(const std::string& cipher_suite,
134                             CryptoParamsVec *out) {
135   int size = static_cast<int>(out->size());
136 
137   out->resize(size + 1);
138   return CreateCryptoParams(size, cipher_suite, &out->at(size));
139 }
140 
AddMediaCryptos(const CryptoParamsVec & cryptos,MediaContentDescription * media)141 void AddMediaCryptos(const CryptoParamsVec& cryptos,
142                      MediaContentDescription* media) {
143   for (CryptoParamsVec::const_iterator crypto = cryptos.begin();
144        crypto != cryptos.end(); ++crypto) {
145     media->AddCrypto(*crypto);
146   }
147 }
148 
CreateMediaCryptos(const std::vector<std::string> & crypto_suites,MediaContentDescription * media)149 bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites,
150                         MediaContentDescription* media) {
151   CryptoParamsVec cryptos;
152   for (std::vector<std::string>::const_iterator it = crypto_suites.begin();
153        it != crypto_suites.end(); ++it) {
154     if (!AddCryptoParams(*it, &cryptos)) {
155       return false;
156     }
157   }
158   AddMediaCryptos(cryptos, media);
159   return true;
160 }
161 #endif
162 
GetCryptos(const MediaContentDescription * media)163 const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) {
164   if (!media) {
165     return NULL;
166   }
167   return &media->cryptos();
168 }
169 
FindMatchingCrypto(const CryptoParamsVec & cryptos,const CryptoParams & crypto,CryptoParams * out)170 bool FindMatchingCrypto(const CryptoParamsVec& cryptos,
171                         const CryptoParams& crypto,
172                         CryptoParams* out) {
173   for (CryptoParamsVec::const_iterator it = cryptos.begin();
174        it != cryptos.end(); ++it) {
175     if (crypto.Matches(*it)) {
176       *out = *it;
177       return true;
178     }
179   }
180   return false;
181 }
182 
183 // For audio, HMAC 32 is prefered over HMAC 80 because of the low overhead.
GetSupportedAudioCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)184 void GetSupportedAudioCryptoSuites(const rtc::CryptoOptions& crypto_options,
185     std::vector<int>* crypto_suites) {
186 #ifdef HAVE_SRTP
187   if (crypto_options.enable_gcm_crypto_suites) {
188     crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
189     crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
190   }
191   crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_32);
192   crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
193 #endif
194 }
195 
GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)196 void GetSupportedAudioCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
197     std::vector<std::string>* crypto_suite_names) {
198   GetSupportedCryptoSuiteNames(GetSupportedAudioCryptoSuites,
199                                crypto_options, crypto_suite_names);
200 }
201 
GetSupportedVideoCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)202 void GetSupportedVideoCryptoSuites(const rtc::CryptoOptions& crypto_options,
203     std::vector<int>* crypto_suites) {
204   GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
205 }
206 
GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)207 void GetSupportedVideoCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
208     std::vector<std::string>* crypto_suite_names) {
209   GetSupportedCryptoSuiteNames(GetSupportedVideoCryptoSuites,
210                                crypto_options, crypto_suite_names);
211 }
212 
GetSupportedDataCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)213 void GetSupportedDataCryptoSuites(const rtc::CryptoOptions& crypto_options,
214     std::vector<int>* crypto_suites) {
215   GetDefaultSrtpCryptoSuites(crypto_options, crypto_suites);
216 }
217 
GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)218 void GetSupportedDataCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
219     std::vector<std::string>* crypto_suite_names) {
220   GetSupportedCryptoSuiteNames(GetSupportedDataCryptoSuites,
221                                crypto_options, crypto_suite_names);
222 }
223 
GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions & crypto_options,std::vector<int> * crypto_suites)224 void GetDefaultSrtpCryptoSuites(const rtc::CryptoOptions& crypto_options,
225     std::vector<int>* crypto_suites) {
226 #ifdef HAVE_SRTP
227   if (crypto_options.enable_gcm_crypto_suites) {
228     crypto_suites->push_back(rtc::SRTP_AEAD_AES_256_GCM);
229     crypto_suites->push_back(rtc::SRTP_AEAD_AES_128_GCM);
230   }
231   crypto_suites->push_back(rtc::SRTP_AES128_CM_SHA1_80);
232 #endif
233 }
234 
GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions & crypto_options,std::vector<std::string> * crypto_suite_names)235 void GetDefaultSrtpCryptoSuiteNames(const rtc::CryptoOptions& crypto_options,
236     std::vector<std::string>* crypto_suite_names) {
237   GetSupportedCryptoSuiteNames(GetDefaultSrtpCryptoSuites,
238                                crypto_options, crypto_suite_names);
239 }
240 
241 // Support any GCM cipher (if enabled through options). For video support only
242 // 80-bit SHA1 HMAC. For audio 32-bit HMAC is tolerated unless bundle is enabled
243 // because it is low overhead.
244 // Pick the crypto in the list that is supported.
SelectCrypto(const MediaContentDescription * offer,bool bundle,const rtc::CryptoOptions & crypto_options,CryptoParams * crypto)245 static bool SelectCrypto(const MediaContentDescription* offer,
246                          bool bundle,
247                          const rtc::CryptoOptions& crypto_options,
248                          CryptoParams *crypto) {
249   bool audio = offer->type() == MEDIA_TYPE_AUDIO;
250   const CryptoParamsVec& cryptos = offer->cryptos();
251 
252   for (CryptoParamsVec::const_iterator i = cryptos.begin();
253        i != cryptos.end(); ++i) {
254     if ((crypto_options.enable_gcm_crypto_suites &&
255          rtc::IsGcmCryptoSuiteName(i->cipher_suite)) ||
256         rtc::CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
257         (rtc::CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio &&
258          !bundle)) {
259       return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
260     }
261   }
262   return false;
263 }
264 
265 // Generate random SSRC values that are not already present in |params_vec|.
266 // The generated values are added to |ssrcs|.
267 // |num_ssrcs| is the number of the SSRC will be generated.
GenerateSsrcs(const StreamParamsVec & params_vec,int num_ssrcs,std::vector<uint32_t> * ssrcs)268 static void GenerateSsrcs(const StreamParamsVec& params_vec,
269                           int num_ssrcs,
270                           std::vector<uint32_t>* ssrcs) {
271   for (int i = 0; i < num_ssrcs; i++) {
272     uint32_t candidate;
273     do {
274       candidate = rtc::CreateRandomNonZeroId();
275     } while (GetStreamBySsrc(params_vec, candidate) ||
276              std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0);
277     ssrcs->push_back(candidate);
278   }
279 }
280 
281 // Finds all StreamParams of all media types and attach them to stream_params.
GetCurrentStreamParams(const SessionDescription * sdesc,StreamParamsVec * stream_params)282 static void GetCurrentStreamParams(const SessionDescription* sdesc,
283                                    StreamParamsVec* stream_params) {
284   if (!sdesc)
285     return;
286 
287   const ContentInfos& contents = sdesc->contents();
288   for (ContentInfos::const_iterator content = contents.begin();
289        content != contents.end(); ++content) {
290     if (!IsMediaContent(&*content)) {
291       continue;
292     }
293     const MediaContentDescription* media =
294         static_cast<const MediaContentDescription*>(
295             content->description);
296     const StreamParamsVec& streams = media->streams();
297     for (StreamParamsVec::const_iterator it = streams.begin();
298          it != streams.end(); ++it) {
299       stream_params->push_back(*it);
300     }
301   }
302 }
303 
304 // Filters the data codecs for the data channel type.
FilterDataCodecs(std::vector<DataCodec> * codecs,bool sctp)305 void FilterDataCodecs(std::vector<DataCodec>* codecs, bool sctp) {
306   // Filter RTP codec for SCTP and vice versa.
307   const char* codec_name =
308       sctp ? kGoogleRtpDataCodecName : kGoogleSctpDataCodecName;
309   for (std::vector<DataCodec>::iterator iter = codecs->begin();
310        iter != codecs->end();) {
311     if (CodecNamesEq(iter->name, codec_name)) {
312       iter = codecs->erase(iter);
313     } else {
314       ++iter;
315     }
316   }
317 }
318 
319 template <typename IdStruct>
320 class UsedIds {
321  public:
UsedIds(int min_allowed_id,int max_allowed_id)322   UsedIds(int min_allowed_id, int max_allowed_id)
323       : min_allowed_id_(min_allowed_id),
324         max_allowed_id_(max_allowed_id),
325         next_id_(max_allowed_id) {
326   }
327 
328   // Loops through all Id in |ids| and changes its id if it is
329   // already in use by another IdStruct. Call this methods with all Id
330   // in a session description to make sure no duplicate ids exists.
331   // Note that typename Id must be a type of IdStruct.
332   template <typename Id>
FindAndSetIdUsed(std::vector<Id> * ids)333   void FindAndSetIdUsed(std::vector<Id>* ids) {
334     for (typename std::vector<Id>::iterator it = ids->begin();
335          it != ids->end(); ++it) {
336       FindAndSetIdUsed(&*it);
337     }
338   }
339 
340   // Finds and sets an unused id if the |idstruct| id is already in use.
FindAndSetIdUsed(IdStruct * idstruct)341   void FindAndSetIdUsed(IdStruct* idstruct) {
342     const int original_id = idstruct->id;
343     int new_id = idstruct->id;
344 
345     if (original_id > max_allowed_id_ || original_id < min_allowed_id_) {
346       // If the original id is not in range - this is an id that can't be
347       // dynamically changed.
348       return;
349     }
350 
351     if (IsIdUsed(original_id)) {
352       new_id = FindUnusedId();
353       LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id
354           << " to " << new_id;
355       idstruct->id = new_id;
356     }
357     SetIdUsed(new_id);
358   }
359 
360  private:
361   // Returns the first unused id in reverse order.
362   // This hopefully reduce the risk of more collisions. We want to change the
363   // default ids as little as possible.
FindUnusedId()364   int FindUnusedId() {
365     while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) {
366       --next_id_;
367     }
368     RTC_DCHECK(next_id_ >= min_allowed_id_);
369     return next_id_;
370   }
371 
IsIdUsed(int new_id)372   bool IsIdUsed(int new_id) {
373     return id_set_.find(new_id) != id_set_.end();
374   }
375 
SetIdUsed(int new_id)376   void SetIdUsed(int new_id) {
377     id_set_.insert(new_id);
378   }
379 
380   const int min_allowed_id_;
381   const int max_allowed_id_;
382   int next_id_;
383   std::set<int> id_set_;
384 };
385 
386 // Helper class used for finding duplicate RTP payload types among audio, video
387 // and data codecs. When bundle is used the payload types may not collide.
388 class UsedPayloadTypes : public UsedIds<Codec> {
389  public:
UsedPayloadTypes()390   UsedPayloadTypes()
391       : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) {
392   }
393 
394 
395  private:
396   static const int kDynamicPayloadTypeMin = 96;
397   static const int kDynamicPayloadTypeMax = 127;
398 };
399 
400 // Helper class used for finding duplicate RTP Header extension ids among
401 // audio and video extensions.
402 class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
403  public:
UsedRtpHeaderExtensionIds()404   UsedRtpHeaderExtensionIds()
405       : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
406 
407  private:
408   // Min and Max local identifier for one-byte header extensions, per RFC5285.
409   static const int kLocalIdMin = 1;
410   static const int kLocalIdMax = 14;
411 };
412 
IsSctp(const MediaContentDescription * desc)413 static bool IsSctp(const MediaContentDescription* desc) {
414   return ((desc->protocol() == kMediaProtocolSctp) ||
415           (desc->protocol() == kMediaProtocolDtlsSctp));
416 }
417 
418 // Adds a StreamParams for each Stream in Streams with media type
419 // media_type to content_description.
420 // |current_params| - All currently known StreamParams of any media type.
421 template <class C>
AddStreamParams(MediaType media_type,const MediaSessionOptions & options,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * content_description,const bool add_legacy_stream)422 static bool AddStreamParams(MediaType media_type,
423                             const MediaSessionOptions& options,
424                             StreamParamsVec* current_streams,
425                             MediaContentDescriptionImpl<C>* content_description,
426                             const bool add_legacy_stream) {
427   // SCTP streams are not negotiated using SDP/ContentDescriptions.
428   if (IsSctp(content_description)) {
429     return true;
430   }
431 
432   const bool include_rtx_streams =
433       ContainsRtxCodec(content_description->codecs());
434 
435   const MediaSessionOptions::Streams& streams = options.streams;
436   if (streams.empty() && add_legacy_stream) {
437     // TODO(perkj): Remove this legacy stream when all apps use StreamParams.
438     std::vector<uint32_t> ssrcs;
439     int num_ssrcs = include_rtx_streams ? 2 : 1;
440     GenerateSsrcs(*current_streams, num_ssrcs, &ssrcs);
441     if (include_rtx_streams) {
442       content_description->AddLegacyStream(ssrcs[0], ssrcs[1]);
443       content_description->set_multistream(true);
444     } else {
445       content_description->AddLegacyStream(ssrcs[0]);
446     }
447     return true;
448   }
449 
450   const bool include_flexfec_stream =
451       ContainsFlexfecCodec(content_description->codecs());
452 
453   MediaSessionOptions::Streams::const_iterator stream_it;
454   for (stream_it = streams.begin();
455        stream_it != streams.end(); ++stream_it) {
456     if (stream_it->type != media_type)
457       continue;  // Wrong media type.
458 
459     const StreamParams* param =
460         GetStreamByIds(*current_streams, "", stream_it->id);
461     // groupid is empty for StreamParams generated using
462     // MediaSessionDescriptionFactory.
463     if (!param) {
464       // This is a new stream.
465       std::vector<uint32_t> ssrcs;
466       GenerateSsrcs(*current_streams, stream_it->num_sim_layers, &ssrcs);
467       StreamParams stream_param;
468       stream_param.id = stream_it->id;
469       // Add the generated ssrc.
470       for (size_t i = 0; i < ssrcs.size(); ++i) {
471         stream_param.ssrcs.push_back(ssrcs[i]);
472       }
473       if (stream_it->num_sim_layers > 1) {
474         SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs);
475         stream_param.ssrc_groups.push_back(group);
476       }
477       // Generate extra ssrcs for include_rtx_streams case.
478       if (include_rtx_streams) {
479         // Generate an RTX ssrc for every ssrc in the group.
480         std::vector<uint32_t> rtx_ssrcs;
481         GenerateSsrcs(*current_streams, static_cast<int>(ssrcs.size()),
482                       &rtx_ssrcs);
483         for (size_t i = 0; i < ssrcs.size(); ++i) {
484           stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]);
485         }
486         content_description->set_multistream(true);
487       }
488       // Generate extra ssrc for include_flexfec_stream case.
489       if (include_flexfec_stream) {
490         // TODO(brandtr): Update when we support multistream protection.
491         if (ssrcs.size() == 1) {
492           std::vector<uint32_t> flexfec_ssrcs;
493           GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs);
494           stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]);
495           content_description->set_multistream(true);
496         } else if (!ssrcs.empty()) {
497           LOG(LS_WARNING)
498               << "Our FlexFEC implementation only supports protecting "
499               << "a single media streams. This session has multiple "
500               << "media streams however, so no FlexFEC SSRC will be generated.";
501         }
502       }
503       stream_param.cname = options.rtcp_cname;
504       stream_param.sync_label = stream_it->sync_label;
505       content_description->AddStream(stream_param);
506 
507       // Store the new StreamParams in current_streams.
508       // This is necessary so that we can use the CNAME for other media types.
509       current_streams->push_back(stream_param);
510     } else {
511       content_description->AddStream(*param);
512     }
513   }
514   return true;
515 }
516 
517 // Updates the transport infos of the |sdesc| according to the given
518 // |bundle_group|. The transport infos of the content names within the
519 // |bundle_group| should be updated to use the ufrag, pwd and DTLS role of the
520 // first content within the |bundle_group|.
UpdateTransportInfoForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)521 static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group,
522                                          SessionDescription* sdesc) {
523   // The bundle should not be empty.
524   if (!sdesc || !bundle_group.FirstContentName()) {
525     return false;
526   }
527 
528   // We should definitely have a transport for the first content.
529   const std::string& selected_content_name = *bundle_group.FirstContentName();
530   const TransportInfo* selected_transport_info =
531       sdesc->GetTransportInfoByName(selected_content_name);
532   if (!selected_transport_info) {
533     return false;
534   }
535 
536   // Set the other contents to use the same ICE credentials.
537   const std::string& selected_ufrag =
538       selected_transport_info->description.ice_ufrag;
539   const std::string& selected_pwd =
540       selected_transport_info->description.ice_pwd;
541   ConnectionRole selected_connection_role =
542       selected_transport_info->description.connection_role;
543   for (TransportInfos::iterator it =
544            sdesc->transport_infos().begin();
545        it != sdesc->transport_infos().end(); ++it) {
546     if (bundle_group.HasContentName(it->content_name) &&
547         it->content_name != selected_content_name) {
548       it->description.ice_ufrag = selected_ufrag;
549       it->description.ice_pwd = selected_pwd;
550       it->description.connection_role = selected_connection_role;
551     }
552   }
553   return true;
554 }
555 
556 // Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and
557 // sets it to |cryptos|.
GetCryptosByName(const SessionDescription * sdesc,const std::string & content_name,CryptoParamsVec * cryptos)558 static bool GetCryptosByName(const SessionDescription* sdesc,
559                              const std::string& content_name,
560                              CryptoParamsVec* cryptos) {
561   if (!sdesc || !cryptos) {
562     return false;
563   }
564 
565   const ContentInfo* content = sdesc->GetContentByName(content_name);
566   if (!IsMediaContent(content) || !content->description) {
567     return false;
568   }
569 
570   const MediaContentDescription* media_desc =
571       static_cast<const MediaContentDescription*>(content->description);
572   *cryptos = media_desc->cryptos();
573   return true;
574 }
575 
576 // Predicate function used by the remove_if.
577 // Returns true if the |crypto|'s cipher_suite is not found in |filter|.
CryptoNotFound(const CryptoParams crypto,const CryptoParamsVec * filter)578 static bool CryptoNotFound(const CryptoParams crypto,
579                            const CryptoParamsVec* filter) {
580   if (filter == NULL) {
581     return true;
582   }
583   for (CryptoParamsVec::const_iterator it = filter->begin();
584        it != filter->end(); ++it) {
585     if (it->cipher_suite == crypto.cipher_suite) {
586       return false;
587     }
588   }
589   return true;
590 }
591 
592 // Prunes the |target_cryptos| by removing the crypto params (cipher_suite)
593 // which are not available in |filter|.
PruneCryptos(const CryptoParamsVec & filter,CryptoParamsVec * target_cryptos)594 static void PruneCryptos(const CryptoParamsVec& filter,
595                          CryptoParamsVec* target_cryptos) {
596   if (!target_cryptos) {
597     return;
598   }
599   target_cryptos->erase(std::remove_if(target_cryptos->begin(),
600                                        target_cryptos->end(),
601                                        bind2nd(ptr_fun(CryptoNotFound),
602                                                &filter)),
603                         target_cryptos->end());
604 }
605 
IsRtpProtocol(const std::string & protocol)606 static bool IsRtpProtocol(const std::string& protocol) {
607   return protocol.empty() ||
608          (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
609 }
610 
IsRtpContent(SessionDescription * sdesc,const std::string & content_name)611 static bool IsRtpContent(SessionDescription* sdesc,
612                          const std::string& content_name) {
613   bool is_rtp = false;
614   ContentInfo* content = sdesc->GetContentByName(content_name);
615   if (IsMediaContent(content)) {
616     MediaContentDescription* media_desc =
617         static_cast<MediaContentDescription*>(content->description);
618     if (!media_desc) {
619       return false;
620     }
621     is_rtp = IsRtpProtocol(media_desc->protocol());
622   }
623   return is_rtp;
624 }
625 
626 // Updates the crypto parameters of the |sdesc| according to the given
627 // |bundle_group|. The crypto parameters of all the contents within the
628 // |bundle_group| should be updated to use the common subset of the
629 // available cryptos.
UpdateCryptoParamsForBundle(const ContentGroup & bundle_group,SessionDescription * sdesc)630 static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group,
631                                         SessionDescription* sdesc) {
632   // The bundle should not be empty.
633   if (!sdesc || !bundle_group.FirstContentName()) {
634     return false;
635   }
636 
637   bool common_cryptos_needed = false;
638   // Get the common cryptos.
639   const ContentNames& content_names = bundle_group.content_names();
640   CryptoParamsVec common_cryptos;
641   for (ContentNames::const_iterator it = content_names.begin();
642        it != content_names.end(); ++it) {
643     if (!IsRtpContent(sdesc, *it)) {
644       continue;
645     }
646     // The common cryptos are needed if any of the content does not have DTLS
647     // enabled.
648     if (!sdesc->GetTransportInfoByName(*it)->description.secure()) {
649       common_cryptos_needed = true;
650     }
651     if (it == content_names.begin()) {
652       // Initial the common_cryptos with the first content in the bundle group.
653       if (!GetCryptosByName(sdesc, *it, &common_cryptos)) {
654         return false;
655       }
656       if (common_cryptos.empty()) {
657         // If there's no crypto params, we should just return.
658         return true;
659       }
660     } else {
661       CryptoParamsVec cryptos;
662       if (!GetCryptosByName(sdesc, *it, &cryptos)) {
663         return false;
664       }
665       PruneCryptos(cryptos, &common_cryptos);
666     }
667   }
668 
669   if (common_cryptos.empty() && common_cryptos_needed) {
670     return false;
671   }
672 
673   // Update to use the common cryptos.
674   for (ContentNames::const_iterator it = content_names.begin();
675        it != content_names.end(); ++it) {
676     if (!IsRtpContent(sdesc, *it)) {
677       continue;
678     }
679     ContentInfo* content = sdesc->GetContentByName(*it);
680     if (IsMediaContent(content)) {
681       MediaContentDescription* media_desc =
682           static_cast<MediaContentDescription*>(content->description);
683       if (!media_desc) {
684         return false;
685       }
686       media_desc->set_cryptos(common_cryptos);
687     }
688   }
689   return true;
690 }
691 
692 template <class C>
ContainsRtxCodec(const std::vector<C> & codecs)693 static bool ContainsRtxCodec(const std::vector<C>& codecs) {
694   for (const auto& codec : codecs) {
695     if (IsRtxCodec(codec)) {
696       return true;
697     }
698   }
699   return false;
700 }
701 
702 template <class C>
IsRtxCodec(const C & codec)703 static bool IsRtxCodec(const C& codec) {
704   return stricmp(codec.name.c_str(), kRtxCodecName) == 0;
705 }
706 
707 template <class C>
ContainsFlexfecCodec(const std::vector<C> & codecs)708 static bool ContainsFlexfecCodec(const std::vector<C>& codecs) {
709   for (const auto& codec : codecs) {
710     if (IsFlexfecCodec(codec)) {
711       return true;
712     }
713   }
714   return false;
715 }
716 
717 template <class C>
IsFlexfecCodec(const C & codec)718 static bool IsFlexfecCodec(const C& codec) {
719   return stricmp(codec.name.c_str(), kFlexfecCodecName) == 0;
720 }
721 
GetTransportOptions(const MediaSessionOptions & options,const std::string & content_name)722 static TransportOptions GetTransportOptions(const MediaSessionOptions& options,
723                                             const std::string& content_name) {
724   TransportOptions transport_options;
725   auto it = options.transport_options.find(content_name);
726   if (it != options.transport_options.end()) {
727     transport_options = it->second;
728   }
729   transport_options.enable_ice_renomination = options.enable_ice_renomination;
730   return transport_options;
731 }
732 
733 // Create a media content to be offered in a session-initiate,
734 // according to the given options.rtcp_mux, options.is_muc,
735 // options.streams, codecs, secure_transport, crypto, and streams.  If we don't
736 // currently have crypto (in current_cryptos) and it is enabled (in
737 // secure_policy), crypto is created (according to crypto_suites).  If
738 // add_legacy_stream is true, and current_streams is empty, a legacy
739 // stream is created.  The created content is added to the offer.
740 template <class C>
CreateMediaContentOffer(const MediaSessionOptions & 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,bool add_legacy_stream,StreamParamsVec * current_streams,MediaContentDescriptionImpl<C> * offer)741 static bool CreateMediaContentOffer(
742     const MediaSessionOptions& options,
743     const std::vector<C>& codecs,
744     const SecurePolicy& secure_policy,
745     const CryptoParamsVec* current_cryptos,
746     const std::vector<std::string>& crypto_suites,
747     const RtpHeaderExtensions& rtp_extensions,
748     bool add_legacy_stream,
749     StreamParamsVec* current_streams,
750     MediaContentDescriptionImpl<C>* offer) {
751   offer->AddCodecs(codecs);
752 
753   offer->set_rtcp_mux(options.rtcp_mux_enabled);
754   if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
755     offer->set_rtcp_reduced_size(true);
756   }
757   offer->set_multistream(options.is_muc);
758   offer->set_rtp_header_extensions(rtp_extensions);
759 
760   if (!AddStreamParams(offer->type(), options, current_streams, offer,
761                        add_legacy_stream)) {
762     return false;
763   }
764 
765 #ifdef HAVE_SRTP
766   if (secure_policy != SEC_DISABLED) {
767     if (current_cryptos) {
768       AddMediaCryptos(*current_cryptos, offer);
769     }
770     if (offer->cryptos().empty()) {
771       if (!CreateMediaCryptos(crypto_suites, offer)) {
772         return false;
773       }
774     }
775   }
776 #endif
777 
778   if (secure_policy == SEC_REQUIRED && offer->cryptos().empty()) {
779     return false;
780   }
781   return true;
782 }
783 
784 template <class C>
ReferencedCodecsMatch(const std::vector<C> & codecs1,const int codec1_id,const std::vector<C> & codecs2,const int codec2_id)785 static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
786                                   const int codec1_id,
787                                   const std::vector<C>& codecs2,
788                                   const int codec2_id) {
789   const C* codec1 = FindCodecById(codecs1, codec1_id);
790   const C* codec2 = FindCodecById(codecs2, codec2_id);
791   return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
792 }
793 
794 template <class C>
NegotiateCodecs(const std::vector<C> & local_codecs,const std::vector<C> & offered_codecs,std::vector<C> * negotiated_codecs)795 static void NegotiateCodecs(const std::vector<C>& local_codecs,
796                             const std::vector<C>& offered_codecs,
797                             std::vector<C>* negotiated_codecs) {
798   for (const C& ours : local_codecs) {
799     C theirs;
800     // Note that we intentionally only find one matching codec for each of our
801     // local codecs, in case the remote offer contains duplicate codecs.
802     if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
803       C negotiated = ours;
804       negotiated.IntersectFeedbackParams(theirs);
805       if (IsRtxCodec(negotiated)) {
806         const auto apt_it =
807             theirs.params.find(kCodecParamAssociatedPayloadType);
808         // FindMatchingCodec shouldn't return something with no apt value.
809         RTC_DCHECK(apt_it != theirs.params.end());
810         negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
811       }
812       if (CodecNamesEq(ours.name.c_str(), kH264CodecName)) {
813         webrtc::H264::GenerateProfileLevelIdForAnswer(
814             ours.params, theirs.params, &negotiated.params);
815       }
816       negotiated.id = theirs.id;
817       negotiated.name = theirs.name;
818       negotiated_codecs->push_back(std::move(negotiated));
819     }
820   }
821   // RFC3264: Although the answerer MAY list the formats in their desired
822   // order of preference, it is RECOMMENDED that unless there is a
823   // specific reason, the answerer list formats in the same relative order
824   // they were present in the offer.
825   std::unordered_map<int, int> payload_type_preferences;
826   int preference = static_cast<int>(offered_codecs.size() + 1);
827   for (const C& codec : offered_codecs) {
828     payload_type_preferences[codec.id] = preference--;
829   }
830   std::sort(negotiated_codecs->begin(), negotiated_codecs->end(),
831             [&payload_type_preferences](const C& a, const C& b) {
832               return payload_type_preferences[a.id] >
833                      payload_type_preferences[b.id];
834             });
835 }
836 
837 // Finds a codec in |codecs2| that matches |codec_to_match|, which is
838 // a member of |codecs1|. If |codec_to_match| is an RTX codec, both
839 // the codecs themselves and their associated codecs must match.
840 template <class C>
FindMatchingCodec(const std::vector<C> & codecs1,const std::vector<C> & codecs2,const C & codec_to_match,C * found_codec)841 static bool FindMatchingCodec(const std::vector<C>& codecs1,
842                               const std::vector<C>& codecs2,
843                               const C& codec_to_match,
844                               C* found_codec) {
845   for (const C& potential_match : codecs2) {
846     if (potential_match.Matches(codec_to_match)) {
847       if (IsRtxCodec(codec_to_match)) {
848         int apt_value_1 = 0;
849         int apt_value_2 = 0;
850         if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
851                                      &apt_value_1) ||
852             !potential_match.GetParam(kCodecParamAssociatedPayloadType,
853                                       &apt_value_2)) {
854           LOG(LS_WARNING) << "RTX missing associated payload type.";
855           continue;
856         }
857         if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
858                                    apt_value_2)) {
859           continue;
860         }
861       }
862       if (found_codec) {
863         *found_codec = potential_match;
864       }
865       return true;
866     }
867   }
868   return false;
869 }
870 
871 // Adds all codecs from |reference_codecs| to |offered_codecs| that dont'
872 // already exist in |offered_codecs| and ensure the payload types don't
873 // collide.
874 template <class C>
FindCodecsToOffer(const std::vector<C> & reference_codecs,std::vector<C> * offered_codecs,UsedPayloadTypes * used_pltypes)875 static void FindCodecsToOffer(
876     const std::vector<C>& reference_codecs,
877     std::vector<C>* offered_codecs,
878     UsedPayloadTypes* used_pltypes) {
879 
880   // Add all new codecs that are not RTX codecs.
881   for (const C& reference_codec : reference_codecs) {
882     if (!IsRtxCodec(reference_codec) &&
883         !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
884                               reference_codec, nullptr)) {
885       C codec = reference_codec;
886       used_pltypes->FindAndSetIdUsed(&codec);
887       offered_codecs->push_back(codec);
888     }
889   }
890 
891   // Add all new RTX codecs.
892   for (const C& reference_codec : reference_codecs) {
893     if (IsRtxCodec(reference_codec) &&
894         !FindMatchingCodec<C>(reference_codecs, *offered_codecs,
895                               reference_codec, nullptr)) {
896       C rtx_codec = reference_codec;
897 
898       std::string associated_pt_str;
899       if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType,
900                               &associated_pt_str)) {
901         LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
902                         << " is missing an associated payload type.";
903         continue;
904       }
905 
906       int associated_pt;
907       if (!rtc::FromString(associated_pt_str, &associated_pt)) {
908         LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
909                         << " of RTX codec " << rtx_codec.name
910                         << " to an integer.";
911         continue;
912       }
913 
914       // Find the associated reference codec for the reference RTX codec.
915       const C* associated_codec =
916           FindCodecById(reference_codecs, associated_pt);
917       if (!associated_codec) {
918         LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
919                         << associated_pt << " for RTX codec " << rtx_codec.name
920                         << ".";
921         continue;
922       }
923 
924       // Find a codec in the offered list that matches the reference codec.
925       // Its payload type may be different than the reference codec.
926       C matching_codec;
927       if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
928                                 *associated_codec, &matching_codec)) {
929         LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name
930                         << " codec.";
931         continue;
932       }
933 
934       rtx_codec.params[kCodecParamAssociatedPayloadType] =
935           rtc::ToString(matching_codec.id);
936       used_pltypes->FindAndSetIdUsed(&rtx_codec);
937       offered_codecs->push_back(rtx_codec);
938     }
939   }
940 }
941 
FindByUri(const RtpHeaderExtensions & extensions,const webrtc::RtpExtension & ext_to_match,webrtc::RtpExtension * found_extension)942 static bool FindByUri(const RtpHeaderExtensions& extensions,
943                       const webrtc::RtpExtension& ext_to_match,
944                       webrtc::RtpExtension* found_extension) {
945   for (RtpHeaderExtensions::const_iterator it = extensions.begin();
946        it  != extensions.end(); ++it) {
947     // We assume that all URIs are given in a canonical format.
948     if (it->uri == ext_to_match.uri) {
949       if (found_extension != NULL) {
950         *found_extension = *it;
951       }
952       return true;
953     }
954   }
955   return false;
956 }
957 
958 // Iterates through |offered_extensions|, adding each one to |all_extensions|
959 // and |used_ids|, and resolving ID conflicts. If an offered extension has the
960 // same URI as one in |all_extensions|, it will re-use the same ID and won't be
961 // treated as a conflict.
FindAndSetRtpHdrExtUsed(RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * all_extensions,UsedRtpHeaderExtensionIds * used_ids)962 static void FindAndSetRtpHdrExtUsed(RtpHeaderExtensions* offered_extensions,
963                                     RtpHeaderExtensions* all_extensions,
964                                     UsedRtpHeaderExtensionIds* used_ids) {
965   for (auto& extension : *offered_extensions) {
966     webrtc::RtpExtension existing;
967     if (FindByUri(*all_extensions, extension, &existing)) {
968       extension.id = existing.id;
969     } else {
970       used_ids->FindAndSetIdUsed(&extension);
971       all_extensions->push_back(extension);
972     }
973   }
974 }
975 
976 // Adds |reference_extensions| to |offered_extensions|, while updating
977 // |all_extensions| and |used_ids|.
FindRtpHdrExtsToOffer(const RtpHeaderExtensions & reference_extensions,RtpHeaderExtensions * offered_extensions,RtpHeaderExtensions * all_extensions,UsedRtpHeaderExtensionIds * used_ids)978 static void FindRtpHdrExtsToOffer(
979     const RtpHeaderExtensions& reference_extensions,
980     RtpHeaderExtensions* offered_extensions,
981     RtpHeaderExtensions* all_extensions,
982     UsedRtpHeaderExtensionIds* used_ids) {
983   for (auto reference_extension : reference_extensions) {
984     if (!FindByUri(*offered_extensions, reference_extension, NULL)) {
985       webrtc::RtpExtension existing;
986       if (FindByUri(*all_extensions, reference_extension, &existing)) {
987         offered_extensions->push_back(existing);
988       } else {
989         used_ids->FindAndSetIdUsed(&reference_extension);
990         all_extensions->push_back(reference_extension);
991         offered_extensions->push_back(reference_extension);
992       }
993     }
994   }
995 }
996 
NegotiateRtpHeaderExtensions(const RtpHeaderExtensions & local_extensions,const RtpHeaderExtensions & offered_extensions,RtpHeaderExtensions * negotiated_extenstions)997 static void NegotiateRtpHeaderExtensions(
998     const RtpHeaderExtensions& local_extensions,
999     const RtpHeaderExtensions& offered_extensions,
1000     RtpHeaderExtensions* negotiated_extenstions) {
1001   RtpHeaderExtensions::const_iterator ours;
1002   for (ours = local_extensions.begin();
1003        ours != local_extensions.end(); ++ours) {
1004     webrtc::RtpExtension theirs;
1005     if (FindByUri(offered_extensions, *ours, &theirs)) {
1006       // We respond with their RTP header extension id.
1007       negotiated_extenstions->push_back(theirs);
1008     }
1009   }
1010 }
1011 
StripCNCodecs(AudioCodecs * audio_codecs)1012 static void StripCNCodecs(AudioCodecs* audio_codecs) {
1013   AudioCodecs::iterator iter = audio_codecs->begin();
1014   while (iter != audio_codecs->end()) {
1015     if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) {
1016       iter = audio_codecs->erase(iter);
1017     } else {
1018       ++iter;
1019     }
1020   }
1021 }
1022 
1023 // Create a media content to be answered in a session-accept,
1024 // according to the given options.rtcp_mux, options.streams, codecs,
1025 // crypto, and streams.  If we don't currently have crypto (in
1026 // current_cryptos) and it is enabled (in secure_policy), crypto is
1027 // created (according to crypto_suites).  If add_legacy_stream is
1028 // true, and current_streams is empty, a legacy stream is created.
1029 // The codecs, rtcp_mux, and crypto are all negotiated with the offer
1030 // from the incoming session-initiate.  If the negotiation fails, this
1031 // method returns false.  The created content is added to the offer.
1032 template <class C>
CreateMediaContentAnswer(const MediaContentDescriptionImpl<C> * offer,const MediaSessionOptions & options,const std::vector<C> & local_codecs,const SecurePolicy & sdes_policy,const CryptoParamsVec * current_cryptos,const RtpHeaderExtensions & local_rtp_extenstions,StreamParamsVec * current_streams,bool add_legacy_stream,bool bundle_enabled,MediaContentDescriptionImpl<C> * answer)1033 static bool CreateMediaContentAnswer(
1034     const MediaContentDescriptionImpl<C>* offer,
1035     const MediaSessionOptions& options,
1036     const std::vector<C>& local_codecs,
1037     const SecurePolicy& sdes_policy,
1038     const CryptoParamsVec* current_cryptos,
1039     const RtpHeaderExtensions& local_rtp_extenstions,
1040     StreamParamsVec* current_streams,
1041     bool add_legacy_stream,
1042     bool bundle_enabled,
1043     MediaContentDescriptionImpl<C>* answer) {
1044   std::vector<C> negotiated_codecs;
1045   NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
1046   answer->AddCodecs(negotiated_codecs);
1047   answer->set_protocol(offer->protocol());
1048   RtpHeaderExtensions negotiated_rtp_extensions;
1049   NegotiateRtpHeaderExtensions(local_rtp_extenstions,
1050                                offer->rtp_header_extensions(),
1051                                &negotiated_rtp_extensions);
1052   answer->set_rtp_header_extensions(negotiated_rtp_extensions);
1053 
1054   answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux());
1055   if (answer->type() == cricket::MEDIA_TYPE_VIDEO) {
1056     answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
1057   }
1058 
1059   if (sdes_policy != SEC_DISABLED) {
1060     CryptoParams crypto;
1061     if (SelectCrypto(offer, bundle_enabled, options.crypto_options, &crypto)) {
1062       if (current_cryptos) {
1063         FindMatchingCrypto(*current_cryptos, crypto, &crypto);
1064       }
1065       answer->AddCrypto(crypto);
1066     }
1067   }
1068 
1069   if (answer->cryptos().empty() && sdes_policy == SEC_REQUIRED) {
1070     return false;
1071   }
1072 
1073   if (!AddStreamParams(answer->type(), options, current_streams, answer,
1074                        add_legacy_stream)) {
1075     return false;  // Something went seriously wrong.
1076   }
1077 
1078   // Make sure the answer media content direction is per default set as
1079   // described in RFC3264 section 6.1.
1080   const bool is_data = !IsRtpProtocol(answer->protocol());
1081   const bool has_send_streams = !answer->streams().empty();
1082   const bool wants_send = has_send_streams || is_data;
1083   const bool recv_audio =
1084       answer->type() == cricket::MEDIA_TYPE_AUDIO && options.recv_audio;
1085   const bool recv_video =
1086       answer->type() == cricket::MEDIA_TYPE_VIDEO && options.recv_video;
1087   const bool recv_data =
1088       answer->type() == cricket::MEDIA_TYPE_DATA;
1089   const bool wants_receive = recv_audio || recv_video || recv_data;
1090 
1091   auto offer_rtd =
1092       RtpTransceiverDirection::FromMediaContentDirection(offer->direction());
1093   auto wants_rtd = RtpTransceiverDirection(wants_send, wants_receive);
1094   answer->set_direction(NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd)
1095                         .ToMediaContentDirection());
1096   return true;
1097 }
1098 
IsDtlsRtp(const std::string & protocol)1099 static bool IsDtlsRtp(const std::string& protocol) {
1100   // Most-likely values first.
1101   return protocol == "UDP/TLS/RTP/SAVPF" || protocol == "TCP/TLS/RTP/SAVPF" ||
1102          protocol == "UDP/TLS/RTP/SAVP" || protocol == "TCP/TLS/RTP/SAVP";
1103 }
1104 
IsPlainRtp(const std::string & protocol)1105 static bool IsPlainRtp(const std::string& protocol) {
1106   // Most-likely values first.
1107   return protocol == "RTP/SAVPF" || protocol == "RTP/AVPF" ||
1108          protocol == "RTP/SAVP" || protocol == "RTP/AVP";
1109 }
1110 
IsDtlsSctp(const std::string & protocol)1111 static bool IsDtlsSctp(const std::string& protocol) {
1112   return protocol == "DTLS/SCTP";
1113 }
1114 
IsPlainSctp(const std::string & protocol)1115 static bool IsPlainSctp(const std::string& protocol) {
1116   return protocol == "SCTP";
1117 }
1118 
IsMediaProtocolSupported(MediaType type,const std::string & protocol,bool secure_transport)1119 static bool IsMediaProtocolSupported(MediaType type,
1120                                      const std::string& protocol,
1121                                      bool secure_transport) {
1122   // Since not all applications serialize and deserialize the media protocol,
1123   // we will have to accept |protocol| to be empty.
1124   if (protocol.empty()) {
1125     return true;
1126   }
1127 
1128   if (type == MEDIA_TYPE_DATA) {
1129     // Check for SCTP, but also for RTP for RTP-based data channels.
1130     // TODO(pthatcher): Remove RTP once RTP-based data channels are gone.
1131     if (secure_transport) {
1132       // Most likely scenarios first.
1133       return IsDtlsSctp(protocol) || IsDtlsRtp(protocol) ||
1134              IsPlainRtp(protocol);
1135     } else {
1136       return IsPlainSctp(protocol) || IsPlainRtp(protocol);
1137     }
1138   }
1139 
1140   // Allow for non-DTLS RTP protocol even when using DTLS because that's what
1141   // JSEP specifies.
1142   if (secure_transport) {
1143     // Most likely scenarios first.
1144     return IsDtlsRtp(protocol) || IsPlainRtp(protocol);
1145   } else {
1146     return IsPlainRtp(protocol);
1147   }
1148 }
1149 
SetMediaProtocol(bool secure_transport,MediaContentDescription * desc)1150 static void SetMediaProtocol(bool secure_transport,
1151                              MediaContentDescription* desc) {
1152   if (!desc->cryptos().empty())
1153     desc->set_protocol(kMediaProtocolSavpf);
1154   else if (secure_transport)
1155     desc->set_protocol(kMediaProtocolDtlsSavpf);
1156   else
1157     desc->set_protocol(kMediaProtocolAvpf);
1158 }
1159 
1160 // Gets the TransportInfo of the given |content_name| from the
1161 // |current_description|. If doesn't exist, returns a new one.
GetTransportDescription(const std::string & content_name,const SessionDescription * current_description)1162 static const TransportDescription* GetTransportDescription(
1163     const std::string& content_name,
1164     const SessionDescription* current_description) {
1165   const TransportDescription* desc = NULL;
1166   if (current_description) {
1167     const TransportInfo* info =
1168         current_description->GetTransportInfoByName(content_name);
1169     if (info) {
1170       desc = &info->description;
1171     }
1172   }
1173   return desc;
1174 }
1175 
1176 // Gets the current DTLS state from the transport description.
IsDtlsActive(const std::string & content_name,const SessionDescription * current_description)1177 static bool IsDtlsActive(
1178     const std::string& content_name,
1179     const SessionDescription* current_description) {
1180   if (!current_description)
1181     return false;
1182 
1183   const ContentInfo* content =
1184       current_description->GetContentByName(content_name);
1185   if (!content)
1186     return false;
1187 
1188   const TransportDescription* current_tdesc =
1189       GetTransportDescription(content_name, current_description);
1190   if (!current_tdesc)
1191     return false;
1192 
1193   return current_tdesc->secure();
1194 }
1195 
MediaTypeToString(MediaType type)1196 std::string MediaTypeToString(MediaType type) {
1197   std::string type_str;
1198   switch (type) {
1199     case MEDIA_TYPE_AUDIO:
1200       type_str = "audio";
1201       break;
1202     case MEDIA_TYPE_VIDEO:
1203       type_str = "video";
1204       break;
1205     case MEDIA_TYPE_DATA:
1206       type_str = "data";
1207       break;
1208     default:
1209       RTC_NOTREACHED();
1210       break;
1211   }
1212   return type_str;
1213 }
1214 
MediaContentDirectionToString(MediaContentDirection direction)1215 std::string MediaContentDirectionToString(MediaContentDirection direction) {
1216   std::string dir_str;
1217   switch (direction) {
1218     case MD_INACTIVE:
1219       dir_str = "inactive";
1220       break;
1221     case MD_SENDONLY:
1222       dir_str = "sendonly";
1223       break;
1224     case MD_RECVONLY:
1225       dir_str = "recvonly";
1226       break;
1227     case MD_SENDRECV:
1228       dir_str = "sendrecv";
1229       break;
1230     default:
1231       RTC_NOTREACHED();
1232       break;
1233   }
1234 
1235   return dir_str;
1236 }
1237 
AddSendStream(MediaType type,const std::string & id,const std::string & sync_label)1238 void MediaSessionOptions::AddSendStream(MediaType type,
1239                                     const std::string& id,
1240                                     const std::string& sync_label) {
1241   AddSendStreamInternal(type, id, sync_label, 1);
1242 }
1243 
AddSendVideoStream(const std::string & id,const std::string & sync_label,int num_sim_layers)1244 void MediaSessionOptions::AddSendVideoStream(
1245     const std::string& id,
1246     const std::string& sync_label,
1247     int num_sim_layers) {
1248   AddSendStreamInternal(MEDIA_TYPE_VIDEO, id, sync_label, num_sim_layers);
1249 }
1250 
AddSendStreamInternal(MediaType type,const std::string & id,const std::string & sync_label,int num_sim_layers)1251 void MediaSessionOptions::AddSendStreamInternal(
1252     MediaType type,
1253     const std::string& id,
1254     const std::string& sync_label,
1255     int num_sim_layers) {
1256   streams.push_back(Stream(type, id, sync_label, num_sim_layers));
1257 
1258   // If we haven't already set the data_channel_type, and we add a
1259   // stream, we assume it's an RTP data stream.
1260   if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE)
1261     data_channel_type = DCT_RTP;
1262 }
1263 
RemoveSendStream(MediaType type,const std::string & id)1264 void MediaSessionOptions::RemoveSendStream(MediaType type,
1265                                        const std::string& id) {
1266   Streams::iterator stream_it = streams.begin();
1267   for (; stream_it != streams.end(); ++stream_it) {
1268     if (stream_it->type == type && stream_it->id == id) {
1269       streams.erase(stream_it);
1270       return;
1271     }
1272   }
1273   RTC_NOTREACHED();
1274 }
1275 
HasSendMediaStream(MediaType type) const1276 bool MediaSessionOptions::HasSendMediaStream(MediaType type) const {
1277   Streams::const_iterator stream_it = streams.begin();
1278   for (; stream_it != streams.end(); ++stream_it) {
1279     if (stream_it->type == type) {
1280       return true;
1281     }
1282   }
1283   return false;
1284 }
1285 
MediaSessionDescriptionFactory(const TransportDescriptionFactory * transport_desc_factory)1286 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1287     const TransportDescriptionFactory* transport_desc_factory)
1288     : secure_(SEC_DISABLED),
1289       add_legacy_(true),
1290       transport_desc_factory_(transport_desc_factory) {
1291 }
1292 
MediaSessionDescriptionFactory(ChannelManager * channel_manager,const TransportDescriptionFactory * transport_desc_factory)1293 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
1294     ChannelManager* channel_manager,
1295     const TransportDescriptionFactory* transport_desc_factory)
1296     : secure_(SEC_DISABLED),
1297       add_legacy_(true),
1298       transport_desc_factory_(transport_desc_factory) {
1299   channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1300   channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
1301   channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
1302   channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
1303   channel_manager->GetSupportedVideoCodecs(&video_codecs_);
1304   channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
1305   channel_manager->GetSupportedDataCodecs(&data_codecs_);
1306   NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
1307                   &audio_sendrecv_codecs_);
1308 }
1309 
audio_sendrecv_codecs() const1310 const AudioCodecs& MediaSessionDescriptionFactory::audio_sendrecv_codecs()
1311     const {
1312   return audio_sendrecv_codecs_;
1313 }
1314 
audio_send_codecs() const1315 const AudioCodecs& MediaSessionDescriptionFactory::audio_send_codecs() const {
1316   return audio_send_codecs_;
1317 }
1318 
audio_recv_codecs() const1319 const AudioCodecs& MediaSessionDescriptionFactory::audio_recv_codecs() const {
1320   return audio_recv_codecs_;
1321 }
1322 
set_audio_codecs(const AudioCodecs & send_codecs,const AudioCodecs & recv_codecs)1323 void MediaSessionDescriptionFactory::set_audio_codecs(
1324     const AudioCodecs& send_codecs, const AudioCodecs& recv_codecs) {
1325   audio_send_codecs_ = send_codecs;
1326   audio_recv_codecs_ = recv_codecs;
1327   audio_sendrecv_codecs_.clear();
1328   // Use NegotiateCodecs to merge our codec lists, since the operation is
1329   // essentially the same. Put send_codecs as the offered_codecs, which is the
1330   // order we'd like to follow. The reasoning is that encoding is usually more
1331   // expensive than decoding, and prioritizing a codec in the send list probably
1332   // means it's a codec we can handle efficiently.
1333   NegotiateCodecs(recv_codecs, send_codecs, &audio_sendrecv_codecs_);
1334 }
1335 
CreateOffer(const MediaSessionOptions & options,const SessionDescription * current_description) const1336 SessionDescription* MediaSessionDescriptionFactory::CreateOffer(
1337     const MediaSessionOptions& options,
1338     const SessionDescription* current_description) const {
1339   std::unique_ptr<SessionDescription> offer(new SessionDescription());
1340 
1341   StreamParamsVec current_streams;
1342   GetCurrentStreamParams(current_description, &current_streams);
1343 
1344   const bool wants_send =
1345       options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1346   const AudioCodecs& supported_audio_codecs =
1347       GetAudioCodecsForOffer({wants_send, options.recv_audio});
1348 
1349   AudioCodecs audio_codecs;
1350   VideoCodecs video_codecs;
1351   DataCodecs data_codecs;
1352   GetCodecsToOffer(current_description, supported_audio_codecs,
1353                    video_codecs_, data_codecs_,
1354                    &audio_codecs, &video_codecs, &data_codecs);
1355 
1356   if (!options.vad_enabled) {
1357     // If application doesn't want CN codecs in offer.
1358     StripCNCodecs(&audio_codecs);
1359   }
1360 
1361   RtpHeaderExtensions audio_rtp_extensions;
1362   RtpHeaderExtensions video_rtp_extensions;
1363   GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions,
1364                        &video_rtp_extensions);
1365 
1366   bool audio_added = false;
1367   bool video_added = false;
1368   bool data_added = false;
1369 
1370   // Iterate through the contents of |current_description| to maintain the order
1371   // of the m-lines in the new offer.
1372   if (current_description) {
1373     ContentInfos::const_iterator it = current_description->contents().begin();
1374     for (; it != current_description->contents().end(); ++it) {
1375       if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1376         if (!AddAudioContentForOffer(options, current_description,
1377                                      audio_rtp_extensions, audio_codecs,
1378                                      &current_streams, offer.get())) {
1379           return NULL;
1380         }
1381         audio_added = true;
1382       } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1383         if (!AddVideoContentForOffer(options, current_description,
1384                                      video_rtp_extensions, video_codecs,
1385                                      &current_streams, offer.get())) {
1386           return NULL;
1387         }
1388         video_added = true;
1389       } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_DATA)) {
1390         MediaSessionOptions options_copy(options);
1391         if (IsSctp(static_cast<const MediaContentDescription*>(
1392                 it->description))) {
1393           options_copy.data_channel_type = DCT_SCTP;
1394         }
1395         if (!AddDataContentForOffer(options_copy, current_description,
1396                                     &data_codecs, &current_streams,
1397                                     offer.get())) {
1398           return NULL;
1399         }
1400         data_added = true;
1401       } else {
1402         RTC_NOTREACHED();
1403       }
1404     }
1405   }
1406 
1407   // Append contents that are not in |current_description|.
1408   if (!audio_added && options.has_audio() &&
1409       !AddAudioContentForOffer(options, current_description,
1410                                audio_rtp_extensions, audio_codecs,
1411                                &current_streams, offer.get())) {
1412     return NULL;
1413   }
1414   if (!video_added && options.has_video() &&
1415       !AddVideoContentForOffer(options, current_description,
1416                                video_rtp_extensions, video_codecs,
1417                                &current_streams, offer.get())) {
1418     return NULL;
1419   }
1420   if (!data_added && options.has_data() &&
1421       !AddDataContentForOffer(options, current_description, &data_codecs,
1422                               &current_streams, offer.get())) {
1423     return NULL;
1424   }
1425 
1426   // Bundle the contents together, if we've been asked to do so, and update any
1427   // parameters that need to be tweaked for BUNDLE.
1428   if (options.bundle_enabled) {
1429     ContentGroup offer_bundle(GROUP_TYPE_BUNDLE);
1430     for (ContentInfos::const_iterator content = offer->contents().begin();
1431        content != offer->contents().end(); ++content) {
1432       offer_bundle.AddContentName(content->name);
1433     }
1434     offer->AddGroup(offer_bundle);
1435     if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) {
1436       LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle.";
1437       return NULL;
1438     }
1439     if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) {
1440       LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle.";
1441       return NULL;
1442     }
1443   }
1444 
1445   return offer.release();
1446 }
1447 
CreateAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description) const1448 SessionDescription* MediaSessionDescriptionFactory::CreateAnswer(
1449     const SessionDescription* offer, const MediaSessionOptions& options,
1450     const SessionDescription* current_description) const {
1451   // The answer contains the intersection of the codecs in the offer with the
1452   // codecs we support. As indicated by XEP-0167, we retain the same payload ids
1453   // from the offer in the answer.
1454   std::unique_ptr<SessionDescription> answer(new SessionDescription());
1455 
1456   StreamParamsVec current_streams;
1457   GetCurrentStreamParams(current_description, &current_streams);
1458 
1459   if (offer) {
1460     ContentInfos::const_iterator it = offer->contents().begin();
1461     for (; it != offer->contents().end(); ++it) {
1462       if (IsMediaContentOfType(&*it, MEDIA_TYPE_AUDIO)) {
1463         if (!AddAudioContentForAnswer(offer, options, current_description,
1464                                   &current_streams, answer.get())) {
1465           return NULL;
1466         }
1467       } else if (IsMediaContentOfType(&*it, MEDIA_TYPE_VIDEO)) {
1468         if (!AddVideoContentForAnswer(offer, options, current_description,
1469                                       &current_streams, answer.get())) {
1470           return NULL;
1471         }
1472       } else {
1473         RTC_DCHECK(IsMediaContentOfType(&*it, MEDIA_TYPE_DATA));
1474         if (!AddDataContentForAnswer(offer, options, current_description,
1475                                      &current_streams, answer.get())) {
1476           return NULL;
1477         }
1478       }
1479     }
1480   }
1481 
1482   // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE
1483   // group in the answer with the appropriate content names.
1484   if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) {
1485     const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE);
1486     ContentGroup answer_bundle(GROUP_TYPE_BUNDLE);
1487     for (ContentInfos::const_iterator content = answer->contents().begin();
1488        content != answer->contents().end(); ++content) {
1489       if (!content->rejected && offer_bundle->HasContentName(content->name)) {
1490         answer_bundle.AddContentName(content->name);
1491       }
1492     }
1493     if (answer_bundle.FirstContentName()) {
1494       answer->AddGroup(answer_bundle);
1495 
1496       // Share the same ICE credentials and crypto params across all contents,
1497       // as BUNDLE requires.
1498       if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) {
1499         LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle.";
1500         return NULL;
1501       }
1502 
1503       if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) {
1504         LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle.";
1505         return NULL;
1506       }
1507     }
1508   }
1509 
1510   return answer.release();
1511 }
1512 
GetAudioCodecsForOffer(const RtpTransceiverDirection & direction) const1513 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForOffer(
1514     const RtpTransceiverDirection& direction) const {
1515   // If stream is inactive - generate list as if sendrecv.
1516   if (direction.send == direction.recv) {
1517     return audio_sendrecv_codecs_;
1518   } else if (direction.send) {
1519     return audio_send_codecs_;
1520   } else {
1521     return audio_recv_codecs_;
1522   }
1523 }
1524 
GetAudioCodecsForAnswer(const RtpTransceiverDirection & offer,const RtpTransceiverDirection & answer) const1525 const AudioCodecs& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer(
1526     const RtpTransceiverDirection& offer,
1527     const RtpTransceiverDirection& answer) const {
1528   // For inactive and sendrecv answers, generate lists as if we were to accept
1529   // the offer's direction. See RFC 3264 Section 6.1.
1530   if (answer.send == answer.recv) {
1531     if (offer.send == offer.recv) {
1532       return audio_sendrecv_codecs_;
1533     } else if (offer.send) {
1534       return audio_recv_codecs_;
1535     } else {
1536       return audio_send_codecs_;
1537     }
1538   } else if (answer.send) {
1539     return audio_send_codecs_;
1540   } else {
1541     return audio_recv_codecs_;
1542   }
1543 }
1544 
GetCodecsToOffer(const SessionDescription * current_description,const AudioCodecs & supported_audio_codecs,const VideoCodecs & supported_video_codecs,const DataCodecs & supported_data_codecs,AudioCodecs * audio_codecs,VideoCodecs * video_codecs,DataCodecs * data_codecs) const1545 void MediaSessionDescriptionFactory::GetCodecsToOffer(
1546     const SessionDescription* current_description,
1547     const AudioCodecs& supported_audio_codecs,
1548     const VideoCodecs& supported_video_codecs,
1549     const DataCodecs& supported_data_codecs,
1550     AudioCodecs* audio_codecs,
1551     VideoCodecs* video_codecs,
1552     DataCodecs* data_codecs) const {
1553   UsedPayloadTypes used_pltypes;
1554   audio_codecs->clear();
1555   video_codecs->clear();
1556   data_codecs->clear();
1557 
1558 
1559   // First - get all codecs from the current description if the media type
1560   // is used.
1561   // Add them to |used_pltypes| so the payloadtype is not reused if a new media
1562   // type is added.
1563   if (current_description) {
1564     const AudioContentDescription* audio =
1565         GetFirstAudioContentDescription(current_description);
1566     if (audio) {
1567       *audio_codecs = audio->codecs();
1568       used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs);
1569     }
1570     const VideoContentDescription* video =
1571         GetFirstVideoContentDescription(current_description);
1572     if (video) {
1573       *video_codecs = video->codecs();
1574       used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs);
1575     }
1576     const DataContentDescription* data =
1577         GetFirstDataContentDescription(current_description);
1578     if (data) {
1579       *data_codecs = data->codecs();
1580       used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs);
1581     }
1582   }
1583 
1584   // Add our codecs that are not in |current_description|.
1585   FindCodecsToOffer<AudioCodec>(supported_audio_codecs, audio_codecs,
1586                                 &used_pltypes);
1587   FindCodecsToOffer<VideoCodec>(supported_video_codecs, video_codecs,
1588                                 &used_pltypes);
1589   FindCodecsToOffer<DataCodec>(supported_data_codecs, data_codecs,
1590                                &used_pltypes);
1591 }
1592 
GetRtpHdrExtsToOffer(const SessionDescription * current_description,RtpHeaderExtensions * audio_extensions,RtpHeaderExtensions * video_extensions) const1593 void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer(
1594     const SessionDescription* current_description,
1595     RtpHeaderExtensions* audio_extensions,
1596     RtpHeaderExtensions* video_extensions) const {
1597   // All header extensions allocated from the same range to avoid potential
1598   // issues when using BUNDLE.
1599   UsedRtpHeaderExtensionIds used_ids;
1600   RtpHeaderExtensions all_extensions;
1601   audio_extensions->clear();
1602   video_extensions->clear();
1603 
1604   // First - get all extensions from the current description if the media type
1605   // is used.
1606   // Add them to |used_ids| so the local ids are not reused if a new media
1607   // type is added.
1608   if (current_description) {
1609     const AudioContentDescription* audio =
1610         GetFirstAudioContentDescription(current_description);
1611     if (audio) {
1612       *audio_extensions = audio->rtp_header_extensions();
1613       FindAndSetRtpHdrExtUsed(audio_extensions, &all_extensions, &used_ids);
1614     }
1615     const VideoContentDescription* video =
1616         GetFirstVideoContentDescription(current_description);
1617     if (video) {
1618       *video_extensions = video->rtp_header_extensions();
1619       FindAndSetRtpHdrExtUsed(video_extensions, &all_extensions, &used_ids);
1620     }
1621   }
1622 
1623   // Add our default RTP header extensions that are not in
1624   // |current_description|.
1625   FindRtpHdrExtsToOffer(audio_rtp_header_extensions(), audio_extensions,
1626                         &all_extensions, &used_ids);
1627   FindRtpHdrExtsToOffer(video_rtp_header_extensions(), video_extensions,
1628                         &all_extensions, &used_ids);
1629 }
1630 
AddTransportOffer(const std::string & content_name,const TransportOptions & transport_options,const SessionDescription * current_desc,SessionDescription * offer_desc) const1631 bool MediaSessionDescriptionFactory::AddTransportOffer(
1632   const std::string& content_name,
1633   const TransportOptions& transport_options,
1634   const SessionDescription* current_desc,
1635   SessionDescription* offer_desc) const {
1636   if (!transport_desc_factory_)
1637      return false;
1638   const TransportDescription* current_tdesc =
1639       GetTransportDescription(content_name, current_desc);
1640   std::unique_ptr<TransportDescription> new_tdesc(
1641       transport_desc_factory_->CreateOffer(transport_options, current_tdesc));
1642   bool ret = (new_tdesc.get() != NULL &&
1643       offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc)));
1644   if (!ret) {
1645     LOG(LS_ERROR)
1646         << "Failed to AddTransportOffer, content name=" << content_name;
1647   }
1648   return ret;
1649 }
1650 
CreateTransportAnswer(const std::string & content_name,const SessionDescription * offer_desc,const TransportOptions & transport_options,const SessionDescription * current_desc) const1651 TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer(
1652     const std::string& content_name,
1653     const SessionDescription* offer_desc,
1654     const TransportOptions& transport_options,
1655     const SessionDescription* current_desc) const {
1656   if (!transport_desc_factory_)
1657     return NULL;
1658   const TransportDescription* offer_tdesc =
1659       GetTransportDescription(content_name, offer_desc);
1660   const TransportDescription* current_tdesc =
1661       GetTransportDescription(content_name, current_desc);
1662   return
1663       transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options,
1664                                             current_tdesc);
1665 }
1666 
AddTransportAnswer(const std::string & content_name,const TransportDescription & transport_desc,SessionDescription * answer_desc) const1667 bool MediaSessionDescriptionFactory::AddTransportAnswer(
1668     const std::string& content_name,
1669     const TransportDescription& transport_desc,
1670     SessionDescription* answer_desc) const {
1671   if (!answer_desc->AddTransportInfo(TransportInfo(content_name,
1672                                                    transport_desc))) {
1673     LOG(LS_ERROR)
1674         << "Failed to AddTransportAnswer, content name=" << content_name;
1675     return false;
1676   }
1677   return true;
1678 }
1679 
AddAudioContentForOffer(const MediaSessionOptions & options,const SessionDescription * current_description,const RtpHeaderExtensions & audio_rtp_extensions,const AudioCodecs & audio_codecs,StreamParamsVec * current_streams,SessionDescription * desc) const1680 bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
1681     const MediaSessionOptions& options,
1682     const SessionDescription* current_description,
1683     const RtpHeaderExtensions& audio_rtp_extensions,
1684     const AudioCodecs& audio_codecs,
1685     StreamParamsVec* current_streams,
1686     SessionDescription* desc) const {
1687   const ContentInfo* current_audio_content =
1688       GetFirstAudioContent(current_description);
1689   std::string content_name =
1690       current_audio_content ? current_audio_content->name : CN_AUDIO;
1691 
1692   cricket::SecurePolicy sdes_policy =
1693       IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1694                                                       : secure();
1695 
1696   std::unique_ptr<AudioContentDescription> audio(new AudioContentDescription());
1697   std::vector<std::string> crypto_suites;
1698   GetSupportedAudioCryptoSuiteNames(options.crypto_options, &crypto_suites);
1699   if (!CreateMediaContentOffer(
1700           options,
1701           audio_codecs,
1702           sdes_policy,
1703           GetCryptos(GetFirstAudioContentDescription(current_description)),
1704           crypto_suites,
1705           audio_rtp_extensions,
1706           add_legacy_,
1707           current_streams,
1708           audio.get())) {
1709     return false;
1710   }
1711   audio->set_lang(lang_);
1712 
1713   bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1714   SetMediaProtocol(secure_transport, audio.get());
1715 
1716   auto offer_rtd =
1717       RtpTransceiverDirection(!audio->streams().empty(), options.recv_audio);
1718   audio->set_direction(offer_rtd.ToMediaContentDirection());
1719 
1720   desc->AddContent(content_name, NS_JINGLE_RTP, audio.release());
1721   if (!AddTransportOffer(content_name,
1722                          GetTransportOptions(options, content_name),
1723                          current_description, desc)) {
1724     return false;
1725   }
1726 
1727   return true;
1728 }
1729 
AddVideoContentForOffer(const MediaSessionOptions & options,const SessionDescription * current_description,const RtpHeaderExtensions & video_rtp_extensions,const VideoCodecs & video_codecs,StreamParamsVec * current_streams,SessionDescription * desc) const1730 bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
1731     const MediaSessionOptions& options,
1732     const SessionDescription* current_description,
1733     const RtpHeaderExtensions& video_rtp_extensions,
1734     const VideoCodecs& video_codecs,
1735     StreamParamsVec* current_streams,
1736     SessionDescription* desc) const {
1737   const ContentInfo* current_video_content =
1738       GetFirstVideoContent(current_description);
1739   std::string content_name =
1740       current_video_content ? current_video_content->name : CN_VIDEO;
1741 
1742   cricket::SecurePolicy sdes_policy =
1743       IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1744                                                       : secure();
1745 
1746   std::unique_ptr<VideoContentDescription> video(new VideoContentDescription());
1747   std::vector<std::string> crypto_suites;
1748   GetSupportedVideoCryptoSuiteNames(options.crypto_options, &crypto_suites);
1749   if (!CreateMediaContentOffer(
1750           options,
1751           video_codecs,
1752           sdes_policy,
1753           GetCryptos(GetFirstVideoContentDescription(current_description)),
1754           crypto_suites,
1755           video_rtp_extensions,
1756           add_legacy_,
1757           current_streams,
1758           video.get())) {
1759     return false;
1760   }
1761 
1762   video->set_bandwidth(options.video_bandwidth);
1763 
1764   bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1765   SetMediaProtocol(secure_transport, video.get());
1766 
1767   if (!video->streams().empty()) {
1768     if (options.recv_video) {
1769       video->set_direction(MD_SENDRECV);
1770     } else {
1771       video->set_direction(MD_SENDONLY);
1772     }
1773   } else {
1774     if (options.recv_video) {
1775       video->set_direction(MD_RECVONLY);
1776     } else {
1777       video->set_direction(MD_INACTIVE);
1778     }
1779   }
1780 
1781   desc->AddContent(content_name, NS_JINGLE_RTP, video.release());
1782   if (!AddTransportOffer(content_name,
1783                          GetTransportOptions(options, content_name),
1784                          current_description, desc)) {
1785     return false;
1786   }
1787 
1788   return true;
1789 }
1790 
AddDataContentForOffer(const MediaSessionOptions & options,const SessionDescription * current_description,DataCodecs * data_codecs,StreamParamsVec * current_streams,SessionDescription * desc) const1791 bool MediaSessionDescriptionFactory::AddDataContentForOffer(
1792     const MediaSessionOptions& options,
1793     const SessionDescription* current_description,
1794     DataCodecs* data_codecs,
1795     StreamParamsVec* current_streams,
1796     SessionDescription* desc) const {
1797   bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
1798 
1799   std::unique_ptr<DataContentDescription> data(new DataContentDescription());
1800   bool is_sctp = (options.data_channel_type == DCT_SCTP);
1801 
1802   FilterDataCodecs(data_codecs, is_sctp);
1803 
1804   const ContentInfo* current_data_content =
1805       GetFirstDataContent(current_description);
1806   std::string content_name =
1807       current_data_content ? current_data_content->name : CN_DATA;
1808 
1809   cricket::SecurePolicy sdes_policy =
1810       IsDtlsActive(content_name, current_description) ? cricket::SEC_DISABLED
1811                                                       : secure();
1812   std::vector<std::string> crypto_suites;
1813   if (is_sctp) {
1814     // SDES doesn't make sense for SCTP, so we disable it, and we only
1815     // get SDES crypto suites for RTP-based data channels.
1816     sdes_policy = cricket::SEC_DISABLED;
1817     // Unlike SetMediaProtocol below, we need to set the protocol
1818     // before we call CreateMediaContentOffer.  Otherwise,
1819     // CreateMediaContentOffer won't know this is SCTP and will
1820     // generate SSRCs rather than SIDs.
1821     data->set_protocol(
1822         secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp);
1823   } else {
1824     GetSupportedDataCryptoSuiteNames(options.crypto_options, &crypto_suites);
1825   }
1826 
1827   if (!CreateMediaContentOffer(
1828           options,
1829           *data_codecs,
1830           sdes_policy,
1831           GetCryptos(GetFirstDataContentDescription(current_description)),
1832           crypto_suites,
1833           RtpHeaderExtensions(),
1834           add_legacy_,
1835           current_streams,
1836           data.get())) {
1837     return false;
1838   }
1839 
1840   if (is_sctp) {
1841     desc->AddContent(content_name, NS_JINGLE_DRAFT_SCTP, data.release());
1842   } else {
1843     data->set_bandwidth(options.data_bandwidth);
1844     SetMediaProtocol(secure_transport, data.get());
1845     desc->AddContent(content_name, NS_JINGLE_RTP, data.release());
1846   }
1847   if (!AddTransportOffer(content_name,
1848                          GetTransportOptions(options, content_name),
1849                          current_description, desc)) {
1850     return false;
1851   }
1852   return true;
1853 }
1854 
AddAudioContentForAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * answer) const1855 bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
1856     const SessionDescription* offer,
1857     const MediaSessionOptions& options,
1858     const SessionDescription* current_description,
1859     StreamParamsVec* current_streams,
1860     SessionDescription* answer) const {
1861   const ContentInfo* audio_content = GetFirstAudioContent(offer);
1862   const AudioContentDescription* offer_audio =
1863       static_cast<const AudioContentDescription*>(audio_content->description);
1864 
1865   std::unique_ptr<TransportDescription> audio_transport(CreateTransportAnswer(
1866       audio_content->name, offer,
1867       GetTransportOptions(options, audio_content->name), current_description));
1868   if (!audio_transport) {
1869     return false;
1870   }
1871 
1872   // Pick codecs based on the requested communications direction in the offer.
1873   const bool wants_send =
1874       options.HasSendMediaStream(MEDIA_TYPE_AUDIO) || add_legacy_;
1875   auto wants_rtd = RtpTransceiverDirection(wants_send, options.recv_audio);
1876   auto offer_rtd =
1877       RtpTransceiverDirection::FromMediaContentDirection(
1878           offer_audio->direction());
1879   auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
1880   AudioCodecs audio_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
1881   if (!options.vad_enabled) {
1882     StripCNCodecs(&audio_codecs);
1883   }
1884 
1885   bool bundle_enabled =
1886       offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1887   std::unique_ptr<AudioContentDescription> audio_answer(
1888       new AudioContentDescription());
1889   // Do not require or create SDES cryptos if DTLS is used.
1890   cricket::SecurePolicy sdes_policy =
1891       audio_transport->secure() ? cricket::SEC_DISABLED : secure();
1892   if (!CreateMediaContentAnswer(
1893           offer_audio,
1894           options,
1895           audio_codecs,
1896           sdes_policy,
1897           GetCryptos(GetFirstAudioContentDescription(current_description)),
1898           audio_rtp_extensions_,
1899           current_streams,
1900           add_legacy_,
1901           bundle_enabled,
1902           audio_answer.get())) {
1903     return false;  // Fails the session setup.
1904   }
1905 
1906   bool rejected = !options.has_audio() || audio_content->rejected ||
1907       !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
1908                                 audio_answer->protocol(),
1909                                 audio_transport->secure());
1910   if (!rejected) {
1911     AddTransportAnswer(audio_content->name, *(audio_transport.get()), answer);
1912   } else {
1913     // RFC 3264
1914     // The answer MUST contain the same number of m-lines as the offer.
1915     LOG(LS_INFO) << "Audio is not supported in the answer.";
1916   }
1917 
1918   answer->AddContent(audio_content->name, audio_content->type, rejected,
1919                      audio_answer.release());
1920   return true;
1921 }
1922 
AddVideoContentForAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * answer) const1923 bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
1924     const SessionDescription* offer,
1925     const MediaSessionOptions& options,
1926     const SessionDescription* current_description,
1927     StreamParamsVec* current_streams,
1928     SessionDescription* answer) const {
1929   const ContentInfo* video_content = GetFirstVideoContent(offer);
1930   std::unique_ptr<TransportDescription> video_transport(CreateTransportAnswer(
1931       video_content->name, offer,
1932       GetTransportOptions(options, video_content->name), current_description));
1933   if (!video_transport) {
1934     return false;
1935   }
1936 
1937   std::unique_ptr<VideoContentDescription> video_answer(
1938       new VideoContentDescription());
1939   // Do not require or create SDES cryptos if DTLS is used.
1940   cricket::SecurePolicy sdes_policy =
1941       video_transport->secure() ? cricket::SEC_DISABLED : secure();
1942   bool bundle_enabled =
1943       offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
1944   if (!CreateMediaContentAnswer(
1945           static_cast<const VideoContentDescription*>(
1946               video_content->description),
1947           options,
1948           video_codecs_,
1949           sdes_policy,
1950           GetCryptos(GetFirstVideoContentDescription(current_description)),
1951           video_rtp_extensions_,
1952           current_streams,
1953           add_legacy_,
1954           bundle_enabled,
1955           video_answer.get())) {
1956     return false;
1957   }
1958   bool rejected = !options.has_video() || video_content->rejected ||
1959       !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
1960                                 video_answer->protocol(),
1961                                 video_transport->secure());
1962   if (!rejected) {
1963     if (!AddTransportAnswer(video_content->name, *(video_transport.get()),
1964                             answer)) {
1965       return false;
1966     }
1967     video_answer->set_bandwidth(options.video_bandwidth);
1968   } else {
1969     // RFC 3264
1970     // The answer MUST contain the same number of m-lines as the offer.
1971     LOG(LS_INFO) << "Video is not supported in the answer.";
1972   }
1973   answer->AddContent(video_content->name, video_content->type, rejected,
1974                      video_answer.release());
1975   return true;
1976 }
1977 
AddDataContentForAnswer(const SessionDescription * offer,const MediaSessionOptions & options,const SessionDescription * current_description,StreamParamsVec * current_streams,SessionDescription * answer) const1978 bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
1979     const SessionDescription* offer,
1980     const MediaSessionOptions& options,
1981     const SessionDescription* current_description,
1982     StreamParamsVec* current_streams,
1983     SessionDescription* answer) const {
1984   const ContentInfo* data_content = GetFirstDataContent(offer);
1985   std::unique_ptr<TransportDescription> data_transport(CreateTransportAnswer(
1986       data_content->name, offer,
1987       GetTransportOptions(options, data_content->name), current_description));
1988   if (!data_transport) {
1989     return false;
1990   }
1991   bool is_sctp = (options.data_channel_type == DCT_SCTP);
1992   std::vector<DataCodec> data_codecs(data_codecs_);
1993   FilterDataCodecs(&data_codecs, is_sctp);
1994 
1995   std::unique_ptr<DataContentDescription> data_answer(
1996       new DataContentDescription());
1997   // Do not require or create SDES cryptos if DTLS is used.
1998   cricket::SecurePolicy sdes_policy =
1999       data_transport->secure() ? cricket::SEC_DISABLED : secure();
2000   bool bundle_enabled =
2001       offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled;
2002   if (!CreateMediaContentAnswer(
2003           static_cast<const DataContentDescription*>(
2004               data_content->description),
2005           options,
2006           data_codecs_,
2007           sdes_policy,
2008           GetCryptos(GetFirstDataContentDescription(current_description)),
2009           RtpHeaderExtensions(),
2010           current_streams,
2011           add_legacy_,
2012           bundle_enabled,
2013           data_answer.get())) {
2014     return false;  // Fails the session setup.
2015   }
2016 
2017   bool rejected = !options.has_data() || data_content->rejected ||
2018       !IsMediaProtocolSupported(MEDIA_TYPE_DATA,
2019                                 data_answer->protocol(),
2020                                 data_transport->secure());
2021   if (!rejected) {
2022     data_answer->set_bandwidth(options.data_bandwidth);
2023     if (!AddTransportAnswer(data_content->name, *(data_transport.get()),
2024                             answer)) {
2025       return false;
2026     }
2027   } else {
2028     // RFC 3264
2029     // The answer MUST contain the same number of m-lines as the offer.
2030     LOG(LS_INFO) << "Data is not supported in the answer.";
2031   }
2032   answer->AddContent(data_content->name, data_content->type, rejected,
2033                      data_answer.release());
2034   return true;
2035 }
2036 
IsMediaContent(const ContentInfo * content)2037 bool IsMediaContent(const ContentInfo* content) {
2038   return (content &&
2039           (content->type == NS_JINGLE_RTP ||
2040            content->type == NS_JINGLE_DRAFT_SCTP));
2041 }
2042 
IsAudioContent(const ContentInfo * content)2043 bool IsAudioContent(const ContentInfo* content) {
2044   return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO);
2045 }
2046 
IsVideoContent(const ContentInfo * content)2047 bool IsVideoContent(const ContentInfo* content) {
2048   return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO);
2049 }
2050 
IsDataContent(const ContentInfo * content)2051 bool IsDataContent(const ContentInfo* content) {
2052   return IsMediaContentOfType(content, MEDIA_TYPE_DATA);
2053 }
2054 
GetFirstMediaContent(const ContentInfos & contents,MediaType media_type)2055 const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
2056                                         MediaType media_type) {
2057   for (const ContentInfo& content : contents) {
2058     if (IsMediaContentOfType(&content, media_type)) {
2059       return &content;
2060     }
2061   }
2062   return nullptr;
2063 }
2064 
GetFirstAudioContent(const ContentInfos & contents)2065 const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) {
2066   return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2067 }
2068 
GetFirstVideoContent(const ContentInfos & contents)2069 const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) {
2070   return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2071 }
2072 
GetFirstDataContent(const ContentInfos & contents)2073 const ContentInfo* GetFirstDataContent(const ContentInfos& contents) {
2074   return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2075 }
2076 
GetFirstMediaContent(const SessionDescription * sdesc,MediaType media_type)2077 static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
2078                                                MediaType media_type) {
2079   if (sdesc == nullptr) {
2080     return nullptr;
2081   }
2082 
2083   return GetFirstMediaContent(sdesc->contents(), media_type);
2084 }
2085 
GetFirstAudioContent(const SessionDescription * sdesc)2086 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
2087   return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2088 }
2089 
GetFirstVideoContent(const SessionDescription * sdesc)2090 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
2091   return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2092 }
2093 
GetFirstDataContent(const SessionDescription * sdesc)2094 const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) {
2095   return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2096 }
2097 
GetFirstMediaContentDescription(const SessionDescription * sdesc,MediaType media_type)2098 const MediaContentDescription* GetFirstMediaContentDescription(
2099     const SessionDescription* sdesc, MediaType media_type) {
2100   const ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2101   const ContentDescription* description = content ? content->description : NULL;
2102   return static_cast<const MediaContentDescription*>(description);
2103 }
2104 
GetFirstAudioContentDescription(const SessionDescription * sdesc)2105 const AudioContentDescription* GetFirstAudioContentDescription(
2106     const SessionDescription* sdesc) {
2107   return static_cast<const AudioContentDescription*>(
2108       GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2109 }
2110 
GetFirstVideoContentDescription(const SessionDescription * sdesc)2111 const VideoContentDescription* GetFirstVideoContentDescription(
2112     const SessionDescription* sdesc) {
2113   return static_cast<const VideoContentDescription*>(
2114       GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2115 }
2116 
GetFirstDataContentDescription(const SessionDescription * sdesc)2117 const DataContentDescription* GetFirstDataContentDescription(
2118     const SessionDescription* sdesc) {
2119   return static_cast<const DataContentDescription*>(
2120       GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2121 }
2122 
2123 //
2124 // Non-const versions of the above functions.
2125 //
2126 
GetFirstMediaContent(ContentInfos & contents,MediaType media_type)2127 ContentInfo* GetFirstMediaContent(ContentInfos& contents,
2128                                   MediaType media_type) {
2129   for (ContentInfo& content : contents) {
2130     if (IsMediaContentOfType(&content, media_type)) {
2131       return &content;
2132     }
2133   }
2134   return nullptr;
2135 }
2136 
GetFirstAudioContent(ContentInfos & contents)2137 ContentInfo* GetFirstAudioContent(ContentInfos& contents) {
2138   return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO);
2139 }
2140 
GetFirstVideoContent(ContentInfos & contents)2141 ContentInfo* GetFirstVideoContent(ContentInfos& contents) {
2142   return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO);
2143 }
2144 
GetFirstDataContent(ContentInfos & contents)2145 ContentInfo* GetFirstDataContent(ContentInfos& contents) {
2146   return GetFirstMediaContent(contents, MEDIA_TYPE_DATA);
2147 }
2148 
GetFirstMediaContent(SessionDescription * sdesc,MediaType media_type)2149 static ContentInfo* GetFirstMediaContent(SessionDescription* sdesc,
2150                                          MediaType media_type) {
2151   if (sdesc == nullptr) {
2152     return nullptr;
2153   }
2154 
2155   return GetFirstMediaContent(sdesc->contents(), media_type);
2156 }
2157 
GetFirstAudioContent(SessionDescription * sdesc)2158 ContentInfo* GetFirstAudioContent(SessionDescription* sdesc) {
2159   return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
2160 }
2161 
GetFirstVideoContent(SessionDescription * sdesc)2162 ContentInfo* GetFirstVideoContent(SessionDescription* sdesc) {
2163   return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
2164 }
2165 
GetFirstDataContent(SessionDescription * sdesc)2166 ContentInfo* GetFirstDataContent(SessionDescription* sdesc) {
2167   return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA);
2168 }
2169 
GetFirstMediaContentDescription(SessionDescription * sdesc,MediaType media_type)2170 MediaContentDescription* GetFirstMediaContentDescription(
2171     SessionDescription* sdesc,
2172     MediaType media_type) {
2173   ContentInfo* content = GetFirstMediaContent(sdesc, media_type);
2174   ContentDescription* description = content ? content->description : NULL;
2175   return static_cast<MediaContentDescription*>(description);
2176 }
2177 
GetFirstAudioContentDescription(SessionDescription * sdesc)2178 AudioContentDescription* GetFirstAudioContentDescription(
2179     SessionDescription* sdesc) {
2180   return static_cast<AudioContentDescription*>(
2181       GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
2182 }
2183 
GetFirstVideoContentDescription(SessionDescription * sdesc)2184 VideoContentDescription* GetFirstVideoContentDescription(
2185     SessionDescription* sdesc) {
2186   return static_cast<VideoContentDescription*>(
2187       GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
2188 }
2189 
GetFirstDataContentDescription(SessionDescription * sdesc)2190 DataContentDescription* GetFirstDataContentDescription(
2191     SessionDescription* sdesc) {
2192   return static_cast<DataContentDescription*>(
2193       GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
2194 }
2195 
2196 }  // namespace cricket
2197