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