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