Home | History | Annotate | Download | only in phone
      1 /*
      2  * libjingle
      3  * Copyright 2004--2005, 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 <string>
     29 
     30 #include "talk/session/phone/mediasessionclient.h"
     31 
     32 #include "talk/base/helpers.h"
     33 #include "talk/base/logging.h"
     34 #include "talk/base/stringutils.h"
     35 #include "talk/base/stringencode.h"
     36 #include "talk/p2p/base/constants.h"
     37 #include "talk/p2p/base/parsing.h"
     38 #include "talk/session/phone/cryptoparams.h"
     39 #include "talk/session/phone/srtpfilter.h"
     40 #include "talk/xmpp/constants.h"
     41 #include "talk/xmllite/qname.h"
     42 #include "talk/xmllite/xmlconstants.h"
     43 
     44 using namespace talk_base;
     45 
     46 namespace {
     47 const std::string kInline = "inline:";
     48 }
     49 
     50 namespace cricket {
     51 
     52 typedef std::vector<CryptoParams> CryptoParamsVec;
     53 
     54 MediaSessionClient::MediaSessionClient(
     55     const buzz::Jid& jid, SessionManager *manager)
     56     : jid_(jid), session_manager_(manager), focus_call_(NULL),
     57       channel_manager_(new ChannelManager(session_manager_->worker_thread())),
     58       secure_(SEC_DISABLED) {
     59   Construct();
     60 }
     61 
     62 MediaSessionClient::MediaSessionClient(
     63     const buzz::Jid& jid, SessionManager *manager,
     64     MediaEngine* media_engine, DeviceManager* device_manager)
     65     : jid_(jid), session_manager_(manager), focus_call_(NULL),
     66       channel_manager_(new ChannelManager(
     67           media_engine, device_manager, session_manager_->worker_thread())),
     68       secure_(SEC_DISABLED) {
     69   Construct();
     70 }
     71 
     72 
     73 void MediaSessionClient::Construct() {
     74   // Register ourselves as the handler of phone and video sessions.
     75   session_manager_->AddClient(NS_JINGLE_RTP, this);
     76   // Forward device notifications.
     77   SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange);
     78   // Bring up the channel manager.
     79   // In previous versions of ChannelManager, this was done automatically
     80   // in the constructor.
     81   channel_manager_->Init();
     82 }
     83 
     84 MediaSessionClient::~MediaSessionClient() {
     85   // Destroy all calls
     86   std::map<uint32, Call *>::iterator it;
     87   while (calls_.begin() != calls_.end()) {
     88     std::map<uint32, Call *>::iterator it = calls_.begin();
     89     DestroyCall((*it).second);
     90   }
     91 
     92   // Delete channel manager. This will wait for the channels to exit
     93   delete channel_manager_;
     94 
     95   // Remove ourselves from the client map.
     96   session_manager_->RemoveClient(NS_JINGLE_RTP);
     97 }
     98 
     99 bool CreateCryptoParams(int tag, const std::string& cipher, CryptoParams *out) {
    100   std::string key;
    101   key.reserve(SRTP_MASTER_KEY_BASE64_LEN);
    102 
    103   if (!CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) {
    104     return false;
    105   }
    106   out->tag = tag;
    107   out->cipher_suite = cipher;
    108   out->key_params = kInline + key;
    109   return true;
    110 }
    111 
    112 bool AddCryptoParams(const std::string& cipher_suite, CryptoParamsVec *out) {
    113   int size = out->size();
    114 
    115   out->resize(size + 1);
    116   return CreateCryptoParams(size, cipher_suite, &out->at(size));
    117 }
    118 
    119 // For audio, HMAC 32 is prefered because of the low overhead.
    120 bool GetSupportedAudioCryptos(CryptoParamsVec* cryptos) {
    121 #ifdef HAVE_SRTP
    122   return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_32, cryptos) &&
    123       AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
    124 #else
    125   return false;
    126 #endif
    127 }
    128 
    129 bool GetSupportedVideoCryptos(CryptoParamsVec* cryptos) {
    130 #ifdef HAVE_SRTP
    131   return AddCryptoParams(CS_AES_CM_128_HMAC_SHA1_80, cryptos);
    132 #else
    133   return false;
    134 #endif
    135 }
    136 
    137 SessionDescription* MediaSessionClient::CreateOffer(
    138     const CallOptions& options) {
    139   SessionDescription* offer = new SessionDescription();
    140   AudioContentDescription* audio = new AudioContentDescription();
    141 
    142 
    143   AudioCodecs audio_codecs;
    144   channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
    145   for (AudioCodecs::const_iterator codec = audio_codecs.begin();
    146        codec != audio_codecs.end(); ++codec) {
    147     audio->AddCodec(*codec);
    148   }
    149   if (options.is_muc) {
    150     audio->set_ssrc(0);
    151   }
    152   audio->SortCodecs();
    153 
    154   if (secure() != SEC_DISABLED) {
    155     CryptoParamsVec audio_cryptos;
    156     if (GetSupportedAudioCryptos(&audio_cryptos)) {
    157       for (CryptoParamsVec::const_iterator crypto = audio_cryptos.begin();
    158            crypto != audio_cryptos.end(); ++crypto) {
    159         audio->AddCrypto(*crypto);
    160       }
    161     }
    162     if (secure() == SEC_REQUIRED) {
    163       if (audio->cryptos().empty()) {
    164         return NULL;  // Abort, crypto required but none found.
    165       }
    166       audio->set_crypto_required(true);
    167     }
    168   }
    169 
    170   offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio);
    171 
    172   // add video codecs, if this is a video call
    173   if (options.is_video) {
    174     VideoContentDescription* video = new VideoContentDescription();
    175     VideoCodecs video_codecs;
    176     channel_manager_->GetSupportedVideoCodecs(&video_codecs);
    177     for (VideoCodecs::const_iterator codec = video_codecs.begin();
    178          codec != video_codecs.end(); ++codec) {
    179       video->AddCodec(*codec);
    180     }
    181     if (options.is_muc) {
    182       video->set_ssrc(0);
    183     }
    184     video->set_bandwidth(options.video_bandwidth);
    185     video->SortCodecs();
    186 
    187     if (secure() != SEC_DISABLED) {
    188       CryptoParamsVec video_cryptos;
    189       if (GetSupportedVideoCryptos(&video_cryptos)) {
    190         for (CryptoParamsVec::const_iterator crypto = video_cryptos.begin();
    191              crypto != video_cryptos.end(); ++crypto) {
    192           video->AddCrypto(*crypto);
    193         }
    194       }
    195       if (secure() == SEC_REQUIRED) {
    196         if (video->cryptos().empty()) {
    197           return NULL;  // Abort, crypto required but none found.
    198         }
    199         video->set_crypto_required(true);
    200       }
    201     }
    202 
    203     offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video);
    204   }
    205 
    206   return offer;
    207 }
    208 
    209 const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc,
    210                                         MediaType media_type) {
    211   if (sdesc == NULL)
    212     return NULL;
    213 
    214   const ContentInfos& contents = sdesc->contents();
    215   for (ContentInfos::const_iterator content = contents.begin();
    216        content != contents.end(); content++) {
    217     if (content->type == NS_JINGLE_RTP) {
    218       const MediaContentDescription* media =
    219           static_cast<const MediaContentDescription*>(content->description);
    220       if (media->type() == media_type) {
    221         return &*content;
    222       }
    223     }
    224   }
    225   return NULL;
    226 }
    227 
    228 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) {
    229   return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO);
    230 }
    231 
    232 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) {
    233   return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO);
    234 }
    235 
    236 // For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is
    237 // tolerated because it is low overhead. Pick the crypto in the list
    238 // that is supported.
    239 bool SelectCrypto(const MediaContentDescription* offer, CryptoParams *crypto) {
    240   bool audio = offer->type() == MEDIA_TYPE_AUDIO;
    241   const CryptoParamsVec& cryptos = offer->cryptos();
    242 
    243   for (CryptoParamsVec::const_iterator i = cryptos.begin();
    244        i != cryptos.end(); ++i) {
    245     if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite ||
    246         (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio)) {
    247       return CreateCryptoParams(i->tag, i->cipher_suite, crypto);
    248     }
    249   }
    250   return false;
    251 }
    252 
    253 SessionDescription* MediaSessionClient::CreateAnswer(
    254     const SessionDescription* offer, const CallOptions& options) {
    255   // The answer contains the intersection of the codecs in the offer with the
    256   // codecs we support, ordered by our local preference. As indicated by
    257   // XEP-0167, we retain the same payload ids from the offer in the answer.
    258   SessionDescription* accept = new SessionDescription();
    259 
    260   const ContentInfo* audio_content = GetFirstAudioContent(offer);
    261   if (audio_content) {
    262     const AudioContentDescription* audio_offer =
    263         static_cast<const AudioContentDescription*>(audio_content->description);
    264     AudioContentDescription* audio_accept = new AudioContentDescription();
    265     AudioCodecs audio_codecs;
    266     channel_manager_->GetSupportedAudioCodecs(&audio_codecs);
    267     for (AudioCodecs::const_iterator ours = audio_codecs.begin();
    268         ours != audio_codecs.end(); ++ours) {
    269       for (AudioCodecs::const_iterator theirs = audio_offer->codecs().begin();
    270           theirs != audio_offer->codecs().end(); ++theirs) {
    271         if (ours->Matches(*theirs)) {
    272           AudioCodec negotiated(*ours);
    273           negotiated.id = theirs->id;
    274           audio_accept->AddCodec(negotiated);
    275         }
    276       }
    277     }
    278 
    279     audio_accept->SortCodecs();
    280 
    281     if (secure() != SEC_DISABLED) {
    282       CryptoParams crypto;
    283 
    284       if (SelectCrypto(audio_offer, &crypto)) {
    285         audio_accept->AddCrypto(crypto);
    286       }
    287     }
    288 
    289     if (audio_accept->cryptos().empty() &&
    290         (audio_offer->crypto_required() || secure() == SEC_REQUIRED)) {
    291       return NULL;  // Fails the session setup.
    292     }
    293     accept->AddContent(audio_content->name, audio_content->type, audio_accept);
    294   }
    295 
    296   const ContentInfo* video_content = GetFirstVideoContent(offer);
    297   if (video_content) {
    298     const VideoContentDescription* video_offer =
    299         static_cast<const VideoContentDescription*>(video_content->description);
    300     VideoContentDescription* video_accept = new VideoContentDescription();
    301     VideoCodecs video_codecs;
    302     channel_manager_->GetSupportedVideoCodecs(&video_codecs);
    303     for (VideoCodecs::const_iterator ours = video_codecs.begin();
    304         ours != video_codecs.end(); ++ours) {
    305       for (VideoCodecs::const_iterator theirs = video_offer->codecs().begin();
    306           theirs != video_offer->codecs().end(); ++theirs) {
    307         if (ours->Matches(*theirs)) {
    308           VideoCodec negotiated(*ours);
    309           negotiated.id = theirs->id;
    310           video_accept->AddCodec(negotiated);
    311         }
    312       }
    313     }
    314 
    315     video_accept->set_bandwidth(options.video_bandwidth);
    316     video_accept->SortCodecs();
    317 
    318     if (secure() != SEC_DISABLED) {
    319       CryptoParams crypto;
    320 
    321       if (SelectCrypto(video_offer, &crypto)) {
    322         video_accept->AddCrypto(crypto);
    323       }
    324     }
    325 
    326     if (video_accept->cryptos().empty() &&
    327         (video_offer->crypto_required() || secure() == SEC_REQUIRED)) {
    328       return NULL;  // Fails the session setup.
    329     }
    330     accept->AddContent(video_content->name, video_content->type, video_accept);
    331   }
    332 
    333   return accept;
    334 }
    335 
    336 Call *MediaSessionClient::CreateCall() {
    337   Call *call = new Call(this);
    338   calls_[call->id()] = call;
    339   SignalCallCreate(call);
    340   return call;
    341 }
    342 
    343 void MediaSessionClient::OnSessionCreate(Session *session,
    344                                          bool received_initiate) {
    345   if (received_initiate) {
    346     session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
    347   }
    348 }
    349 
    350 void MediaSessionClient::OnSessionState(BaseSession* base_session,
    351                                         BaseSession::State state) {
    352   // MediaSessionClient can only be used with a Session*, so it's
    353   // safe to cast here.
    354   Session* session = static_cast<Session*>(base_session);
    355 
    356   if (state == Session::STATE_RECEIVEDINITIATE) {
    357     // The creation of the call must happen after the session has
    358     // processed the initiate message because we need the
    359     // remote_description to know what content names to use in the
    360     // call.
    361 
    362     // If our accept would have no codecs, then we must reject this call.
    363     const SessionDescription* offer = session->remote_description();
    364     const SessionDescription* accept = CreateAnswer(offer, CallOptions());
    365     const ContentInfo* audio_content = GetFirstAudioContent(accept);
    366     const AudioContentDescription* audio_accept = (!audio_content) ? NULL :
    367         static_cast<const AudioContentDescription*>(audio_content->description);
    368 
    369     // For some reason, we need to create the call even when we
    370     // reject.
    371     Call *call = CreateCall();
    372     session_map_[session->id()] = call;
    373     call->IncomingSession(session, offer);
    374 
    375     if (!audio_accept || audio_accept->codecs().size() == 0) {
    376       session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
    377     }
    378     delete accept;
    379   }
    380 }
    381 
    382 void MediaSessionClient::DestroyCall(Call *call) {
    383   // Change focus away, signal destruction
    384 
    385   if (call == focus_call_)
    386     SetFocus(NULL);
    387   SignalCallDestroy(call);
    388 
    389   // Remove it from calls_ map and delete
    390 
    391   std::map<uint32, Call *>::iterator it = calls_.find(call->id());
    392   if (it != calls_.end())
    393     calls_.erase(it);
    394 
    395   delete call;
    396 }
    397 
    398 void MediaSessionClient::OnSessionDestroy(Session *session) {
    399   // Find the call this session is in, remove it
    400 
    401   std::map<std::string, Call *>::iterator it = session_map_.find(session->id());
    402   ASSERT(it != session_map_.end());
    403   if (it != session_map_.end()) {
    404     Call *call = (*it).second;
    405     session_map_.erase(it);
    406     call->RemoveSession(session);
    407   }
    408 }
    409 
    410 Call *MediaSessionClient::GetFocus() {
    411   return focus_call_;
    412 }
    413 
    414 void MediaSessionClient::SetFocus(Call *call) {
    415   Call *old_focus_call = focus_call_;
    416   if (focus_call_ != call) {
    417     if (focus_call_ != NULL)
    418       focus_call_->EnableChannels(false);
    419     focus_call_ = call;
    420     if (focus_call_ != NULL)
    421       focus_call_->EnableChannels(true);
    422     SignalFocus(focus_call_, old_focus_call);
    423   }
    424 }
    425 
    426 void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) {
    427   // Move all sessions from call to call_to_join, delete call.
    428   // If call_to_join has focus, added sessions should have enabled channels.
    429 
    430   if (focus_call_ == call)
    431     SetFocus(NULL);
    432   call_to_join->Join(call, focus_call_ == call_to_join);
    433   DestroyCall(call);
    434 }
    435 
    436 Session *MediaSessionClient::CreateSession(Call *call) {
    437   const std::string& type = NS_JINGLE_RTP;
    438   Session *session = session_manager_->CreateSession(jid().Str(), type);
    439   session_map_[session->id()] = call;
    440   return session;
    441 }
    442 
    443 bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) {
    444   int id = GetXmlAttr(element, QN_ID, -1);
    445   if (id < 0)
    446     return false;
    447 
    448   std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
    449   int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
    450   int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
    451   int channels = GetXmlAttr(element, QN_CHANNELS, 1);
    452   *out = AudioCodec(id, name, clockrate, bitrate, channels, 0);
    453   return true;
    454 }
    455 
    456 bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) {
    457   int id = GetXmlAttr(element, QN_ID, -1);
    458   if (id < 0)
    459     return false;
    460 
    461   std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
    462   int width = GetXmlAttr(element, QN_WIDTH, 0);
    463   int height = GetXmlAttr(element, QN_HEIGHT, 0);
    464   int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
    465 
    466   *out = VideoCodec(id, name, width, height, framerate, 0);
    467   return true;
    468 }
    469 
    470 void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
    471                      const buzz::QName& name,
    472                      MediaContentDescription* content) {
    473   const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name);
    474   if (ssrc_elem) {
    475     content->set_ssrc(strtoul(ssrc_elem->BodyText().c_str(), NULL, 10));
    476   }
    477 }
    478 
    479 bool ParseCryptoParams(const buzz::XmlElement* element,
    480                        CryptoParams* out,
    481                        ParseError* error) {
    482   if (!element->HasAttr(QN_CRYPTO_SUITE)) {
    483     return BadParse("crypto: crypto-suite attribute missing ", error);
    484   } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
    485     return BadParse("crypto: key-params attribute missing ", error);
    486   } else if (!element->HasAttr(QN_CRYPTO_TAG)) {
    487     return BadParse("crypto: tag attribute missing ", error);
    488   }
    489 
    490   const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
    491   const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
    492   const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
    493   const std::string& session_params =
    494       element->Attr(QN_CRYPTO_SESSION_PARAMS);  // Optional.
    495 
    496   *out = CryptoParams(tag, crypto_suite, key_params, session_params);
    497   return true;
    498 }
    499 
    500 
    501 // Parse the first encryption element found with a matching 'usage'
    502 // element.
    503 // <usage/> is specific to Gingle. In Jingle, <crypto/> is already
    504 // scoped to a content.
    505 // Return false if there was an encryption element and it could not be
    506 // parsed.
    507 bool ParseGingleEncryption(const buzz::XmlElement* desc,
    508                            const buzz::QName& usage,
    509                            MediaContentDescription* media,
    510                            ParseError* error) {
    511   for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
    512        encryption != NULL;
    513        encryption = encryption->NextNamed(QN_ENCRYPTION)) {
    514     if (encryption->FirstNamed(usage) != NULL) {
    515       media->set_crypto_required(
    516           GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
    517       for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
    518            crypto != NULL;
    519            crypto = crypto->NextNamed(QN_CRYPTO)) {
    520         CryptoParams params;
    521         if (!ParseCryptoParams(crypto, &params, error)) {
    522           return false;
    523         }
    524         media->AddCrypto(params);
    525       }
    526       break;
    527     }
    528   }
    529   return true;
    530 }
    531 
    532 void ParseBandwidth(const buzz::XmlElement* parent_elem,
    533                     MediaContentDescription* media) {
    534   const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
    535   int bandwidth_kbps;
    536   if (bw_elem && FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
    537     if (bandwidth_kbps >= 0) {
    538       media->set_bandwidth(bandwidth_kbps * 1000);
    539     }
    540   }
    541 }
    542 
    543 bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
    544                              const ContentDescription** content,
    545                              ParseError* error) {
    546   AudioContentDescription* audio = new AudioContentDescription();
    547 
    548   if (content_elem->FirstElement()) {
    549     for (const buzz::XmlElement* codec_elem =
    550              content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
    551          codec_elem != NULL;
    552          codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
    553       AudioCodec codec;
    554       if (ParseGingleAudioCodec(codec_elem, &codec)) {
    555         audio->AddCodec(codec);
    556       }
    557     }
    558   } else {
    559     // For backward compatibility, we can assume the other client is
    560     // an old version of Talk if it has no audio payload types at all.
    561     audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
    562     audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
    563   }
    564 
    565   ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
    566 
    567   if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
    568                              audio, error)) {
    569     return false;
    570   }
    571 
    572   *content = audio;
    573   return true;
    574 }
    575 
    576 bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
    577                              const ContentDescription** content,
    578                              ParseError* error) {
    579   VideoContentDescription* video = new VideoContentDescription();
    580 
    581   for (const buzz::XmlElement* codec_elem =
    582            content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
    583        codec_elem != NULL;
    584        codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
    585     VideoCodec codec;
    586     if (ParseGingleVideoCodec(codec_elem, &codec)) {
    587       video->AddCodec(codec);
    588     }
    589   }
    590 
    591   ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
    592   ParseBandwidth(content_elem, video);
    593 
    594   if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
    595                              video, error)) {
    596     return false;
    597   }
    598 
    599   *content = video;
    600   return true;
    601 }
    602 
    603 void ParsePayloadTypeParameters(const buzz::XmlElement* element,
    604                                 std::map<std::string, std::string>* paramap) {
    605   for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
    606        param != NULL; param = param->NextNamed(QN_PARAMETER)) {
    607     std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
    608                                    buzz::STR_EMPTY);
    609     std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
    610                                    buzz::STR_EMPTY);
    611     if (!name.empty() && !value.empty()) {
    612       paramap->insert(make_pair(name, value));
    613     }
    614   }
    615 }
    616 
    617 int FindWithDefault(const std::map<std::string, std::string>& map,
    618                     const std::string& key, const int def) {
    619   std::map<std::string, std::string>::const_iterator iter = map.find(key);
    620   return (iter == map.end()) ? def : atoi(iter->second.c_str());
    621 }
    622 
    623 
    624 // Parse the first encryption element found.
    625 // Return false if there was an encryption element and it could not be
    626 // parsed.
    627 bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
    628                            MediaContentDescription* media,
    629                            ParseError* error) {
    630   const buzz::XmlElement* encryption =
    631           content_elem->FirstNamed(QN_ENCRYPTION);
    632   if (encryption == NULL) {
    633       return true;
    634   }
    635 
    636   media->set_crypto_required(
    637       GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false));
    638 
    639   for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
    640        crypto != NULL;
    641        crypto = crypto->NextNamed(QN_CRYPTO)) {
    642     CryptoParams params;
    643     if (!ParseCryptoParams(crypto, &params, error)) {
    644       return false;
    645     }
    646     media->AddCrypto(params);
    647   }
    648   return true;
    649 }
    650 
    651 bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
    652   int id = GetXmlAttr(elem, QN_ID, -1);
    653   if (id < 0)
    654     return false;
    655 
    656   std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
    657   int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
    658   int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
    659 
    660   std::map<std::string, std::string> paramap;
    661   ParsePayloadTypeParameters(elem, &paramap);
    662   int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
    663 
    664   *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
    665   return true;
    666 }
    667 
    668 bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
    669   int id = GetXmlAttr(elem, QN_ID, -1);
    670   if (id < 0)
    671     return false;
    672 
    673   std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
    674 
    675   std::map<std::string, std::string> paramap;
    676   ParsePayloadTypeParameters(elem, &paramap);
    677   int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
    678   int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
    679   int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
    680 
    681   *codec = VideoCodec(id, name, width, height, framerate, 0);
    682   return true;
    683 }
    684 
    685 bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
    686                              const ContentDescription** content,
    687                              ParseError* error) {
    688   AudioContentDescription* audio = new AudioContentDescription();
    689 
    690   for (const buzz::XmlElement* payload_elem =
    691            content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
    692       payload_elem != NULL;
    693       payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
    694     AudioCodec codec;
    695     if (ParseJingleAudioCodec(payload_elem, &codec)) {
    696       audio->AddCodec(codec);
    697     }
    698   }
    699 
    700   if (!ParseJingleEncryption(content_elem, audio, error)) {
    701     return false;
    702   }
    703   // TODO: Figure out how to integrate SSRC into Jingle.
    704   *content = audio;
    705   return true;
    706 }
    707 
    708 bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
    709                              const ContentDescription** content,
    710                              ParseError* error) {
    711   VideoContentDescription* video = new VideoContentDescription();
    712 
    713   for (const buzz::XmlElement* payload_elem =
    714            content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
    715       payload_elem != NULL;
    716       payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
    717     VideoCodec codec;
    718     if (ParseJingleVideoCodec(payload_elem, &codec)) {
    719       video->AddCodec(codec);
    720     }
    721   }
    722 
    723   ParseBandwidth(content_elem, video);
    724 
    725   if (!ParseJingleEncryption(content_elem, video, error)) {
    726     return false;
    727   }
    728   // TODO: Figure out how to integrate SSRC into Jingle.
    729   *content = video;
    730   return true;
    731 }
    732 
    733 bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
    734                                      const buzz::XmlElement* content_elem,
    735                                      const ContentDescription** content,
    736                                      ParseError* error) {
    737   if (protocol == PROTOCOL_GINGLE) {
    738     const std::string& content_type = content_elem->Name().Namespace();
    739     if (NS_GINGLE_AUDIO == content_type) {
    740       return ParseGingleAudioContent(content_elem, content, error);
    741     } else if (NS_GINGLE_VIDEO == content_type) {
    742       return ParseGingleVideoContent(content_elem, content, error);
    743     } else {
    744       return BadParse("Unknown content type: " + content_type, error);
    745     }
    746   } else {
    747     std::string media;
    748     if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
    749       return false;
    750 
    751     if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
    752       return ParseJingleAudioContent(content_elem, content, error);
    753     } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
    754       return ParseJingleVideoContent(content_elem, content, error);
    755     } else {
    756       return BadParse("Unknown media: " + media, error);
    757     }
    758   }
    759 }
    760 
    761 buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
    762   buzz::XmlElement* payload_type =
    763       new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
    764   AddXmlAttr(payload_type, QN_ID, codec.id);
    765   payload_type->AddAttr(QN_NAME, codec.name);
    766   if (codec.clockrate > 0)
    767     AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
    768   if (codec.bitrate > 0)
    769     AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
    770   if (codec.channels > 1)
    771     AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
    772   return payload_type;
    773 }
    774 
    775 buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
    776   buzz::XmlElement* payload_type =
    777       new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
    778   AddXmlAttr(payload_type, QN_ID, codec.id);
    779   payload_type->AddAttr(QN_NAME, codec.name);
    780   AddXmlAttr(payload_type, QN_WIDTH, codec.width);
    781   AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
    782   AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
    783   return payload_type;
    784 }
    785 
    786 buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
    787   buzz::XmlElement* elem = new buzz::XmlElement(name, true);
    788   if (ssrc) {
    789     SetXmlBody(elem, ssrc);
    790   }
    791   return elem;
    792 }
    793 
    794 buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
    795   int kbps = bps / 1000;
    796   buzz::XmlElement* elem = new buzz::XmlElement(name);
    797   elem->AddAttr(buzz::QN_TYPE, "AS");
    798   SetXmlBody(elem, kbps);
    799   return elem;
    800 }
    801 
    802 // For Jingle, usage_qname is empty.
    803 buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
    804                                              bool required) {
    805   buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
    806 
    807   if (required) {
    808     encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
    809   }
    810 
    811   for (CryptoParamsVec::const_iterator i = cryptos.begin();
    812        i != cryptos.end();
    813        ++i) {
    814     buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
    815 
    816     AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
    817     crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
    818     crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
    819     if (!i->session_params.empty()) {
    820       crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
    821     }
    822     encryption_elem->AddElement(crypto_elem);
    823   }
    824   return encryption_elem;
    825 }
    826 
    827 buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
    828                                              const buzz::QName& usage_qname,
    829                                              bool required) {
    830   buzz::XmlElement* encryption_elem =
    831       CreateJingleEncryptionElem(cryptos, required);
    832 
    833   if (required) {
    834     encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
    835   }
    836 
    837   buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
    838   encryption_elem->AddElement(usage_elem);
    839 
    840   return encryption_elem;
    841 }
    842 
    843 buzz::XmlElement* CreateGingleAudioContentElem(
    844     const AudioContentDescription* audio,
    845     bool crypto_required) {
    846   buzz::XmlElement* elem =
    847       new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
    848 
    849   for (AudioCodecs::const_iterator codec = audio->codecs().begin();
    850        codec != audio->codecs().end(); ++codec) {
    851     elem->AddElement(CreateGingleAudioCodecElem(*codec));
    852   }
    853   if (audio->ssrc_set()) {
    854     elem->AddElement(CreateGingleSsrcElem(
    855         QN_GINGLE_AUDIO_SRCID, audio->ssrc()));
    856   }
    857 
    858   const CryptoParamsVec& cryptos = audio->cryptos();
    859   if (!cryptos.empty()) {
    860     elem->AddElement(CreateGingleEncryptionElem(cryptos,
    861                                                 QN_GINGLE_AUDIO_CRYPTO_USAGE,
    862                                                 crypto_required));
    863   }
    864 
    865 
    866   return elem;
    867 }
    868 
    869 buzz::XmlElement* CreateGingleVideoContentElem(
    870     const VideoContentDescription* video,
    871     bool crypto_required) {
    872   buzz::XmlElement* elem =
    873       new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
    874 
    875   for (VideoCodecs::const_iterator codec = video->codecs().begin();
    876        codec != video->codecs().end(); ++codec) {
    877     elem->AddElement(CreateGingleVideoCodecElem(*codec));
    878   }
    879   if (video->ssrc_set()) {
    880     elem->AddElement(CreateGingleSsrcElem(
    881         QN_GINGLE_VIDEO_SRCID, video->ssrc()));
    882   }
    883   if (video->bandwidth() != kAutoBandwidth) {
    884     elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
    885                                          video->bandwidth()));
    886   }
    887 
    888   const CryptoParamsVec& cryptos = video->cryptos();
    889   if (!cryptos.empty()) {
    890     elem->AddElement(CreateGingleEncryptionElem(cryptos,
    891                                                 QN_GINGLE_VIDEO_CRYPTO_USAGE,
    892                                                 crypto_required));
    893   }
    894 
    895   return elem;
    896 }
    897 
    898 buzz::XmlElement* CreatePayloadTypeParameterElem(
    899     const std::string& name, int value) {
    900   buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
    901 
    902   elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
    903   AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
    904 
    905   return elem;
    906 }
    907 
    908 buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
    909   buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
    910 
    911   AddXmlAttr(elem, QN_ID, codec.id);
    912   elem->AddAttr(QN_NAME, codec.name);
    913   if (codec.clockrate > 0) {
    914     AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
    915   }
    916   if (codec.bitrate > 0) {
    917     elem->AddElement(CreatePayloadTypeParameterElem(
    918         PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
    919   }
    920   if (codec.channels > 1) {
    921     AddXmlAttr(elem, QN_CHANNELS, codec.channels);
    922   }
    923 
    924   return elem;
    925 }
    926 
    927 buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
    928   buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
    929 
    930   AddXmlAttr(elem, QN_ID, codec.id);
    931   elem->AddAttr(QN_NAME, codec.name);
    932   elem->AddElement(CreatePayloadTypeParameterElem(
    933       PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
    934   elem->AddElement(CreatePayloadTypeParameterElem(
    935       PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
    936   elem->AddElement(CreatePayloadTypeParameterElem(
    937       PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
    938 
    939   return elem;
    940 }
    941 
    942 buzz::XmlElement* CreateJingleAudioContentElem(
    943     const AudioContentDescription* audio, bool crypto_required) {
    944   buzz::XmlElement* elem =
    945       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
    946 
    947   elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
    948 
    949   for (AudioCodecs::const_iterator codec = audio->codecs().begin();
    950        codec != audio->codecs().end(); ++codec) {
    951     elem->AddElement(CreateJingleAudioCodecElem(*codec));
    952   }
    953 
    954   const CryptoParamsVec& cryptos = audio->cryptos();
    955   if (!cryptos.empty()) {
    956     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
    957   }
    958 
    959   // TODO: Figure out how to integrate SSRC into Jingle.
    960   return elem;
    961 }
    962 
    963 buzz::XmlElement* CreateJingleVideoContentElem(
    964     const VideoContentDescription* video, bool crypto_required) {
    965   buzz::XmlElement* elem =
    966       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
    967 
    968   elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
    969 
    970   for (VideoCodecs::const_iterator codec = video->codecs().begin();
    971        codec != video->codecs().end(); ++codec) {
    972     elem->AddElement(CreateJingleVideoCodecElem(*codec));
    973   }
    974 
    975   const CryptoParamsVec& cryptos = video->cryptos();
    976   if (!cryptos.empty()) {
    977     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
    978   }
    979 
    980   if (video->bandwidth() != kAutoBandwidth) {
    981     elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
    982                                          video->bandwidth()));
    983   }
    984 
    985   // TODO: Figure out how to integrate SSRC into Jingle.
    986   return elem;
    987 }
    988 
    989 bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
    990                                       const ContentDescription* content,
    991                                       buzz::XmlElement** elem,
    992                                       WriteError* error) {
    993   const MediaContentDescription* media =
    994       static_cast<const MediaContentDescription*>(content);
    995   bool crypto_required = secure() == SEC_REQUIRED;
    996 
    997   if (media->type() == MEDIA_TYPE_AUDIO) {
    998     const AudioContentDescription* audio =
    999         static_cast<const AudioContentDescription*>(media);
   1000     if (protocol == PROTOCOL_GINGLE) {
   1001       *elem = CreateGingleAudioContentElem(audio, crypto_required);
   1002     } else {
   1003       *elem = CreateJingleAudioContentElem(audio, crypto_required);
   1004     }
   1005   } else if (media->type() == MEDIA_TYPE_VIDEO) {
   1006     const VideoContentDescription* video =
   1007         static_cast<const VideoContentDescription*>(media);
   1008     if (protocol == PROTOCOL_GINGLE) {
   1009       *elem = CreateGingleVideoContentElem(video, crypto_required);
   1010     } else {
   1011       *elem = CreateJingleVideoContentElem(video, crypto_required);
   1012     }
   1013   } else {
   1014     return BadWrite("Unknown content type: " + media->type(), error);
   1015   }
   1016 
   1017   return true;
   1018 }
   1019 
   1020 }  // namespace cricket
   1021