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 <string>
     29 
     30 #include "talk/session/media/mediasessionclient.h"
     31 
     32 #include "talk/media/base/capturemanager.h"
     33 #include "talk/media/base/cryptoparams.h"
     34 #include "talk/media/sctp/sctpdataengine.h"
     35 #include "talk/p2p/base/constants.h"
     36 #include "talk/p2p/base/parsing.h"
     37 #include "talk/session/media/mediamessages.h"
     38 #include "talk/session/media/srtpfilter.h"
     39 #include "webrtc/libjingle/xmllite/qname.h"
     40 #include "webrtc/libjingle/xmllite/xmlconstants.h"
     41 #include "talk/xmpp/constants.h"
     42 #include "webrtc/base/helpers.h"
     43 #include "webrtc/base/logging.h"
     44 #include "webrtc/base/stringencode.h"
     45 #include "webrtc/base/stringutils.h"
     46 
     47 namespace cricket {
     48 
     49 #if !defined(DISABLE_MEDIA_ENGINE_FACTORY)
     50 MediaSessionClient::MediaSessionClient(
     51     const buzz::Jid& jid, SessionManager *manager)
     52     : jid_(jid),
     53       session_manager_(manager),
     54       focus_call_(NULL),
     55       channel_manager_(new ChannelManager(session_manager_->worker_thread())),
     56       desc_factory_(channel_manager_,
     57           session_manager_->transport_desc_factory()),
     58       multisession_enabled_(false) {
     59   Construct();
     60 }
     61 #endif
     62 
     63 MediaSessionClient::MediaSessionClient(
     64     const buzz::Jid& jid, SessionManager *manager,
     65     MediaEngineInterface* media_engine,
     66     DataEngineInterface* data_media_engine,
     67     DeviceManagerInterface* device_manager)
     68     : jid_(jid),
     69       session_manager_(manager),
     70       focus_call_(NULL),
     71       channel_manager_(new ChannelManager(
     72           media_engine, data_media_engine,
     73           device_manager, new CaptureManager(),
     74           session_manager_->worker_thread())),
     75       desc_factory_(channel_manager_,
     76                     session_manager_->transport_desc_factory()),
     77       multisession_enabled_(false) {
     78   Construct();
     79 }
     80 
     81 void MediaSessionClient::Construct() {
     82   // Register ourselves as the handler of audio and video sessions.
     83   session_manager_->AddClient(NS_JINGLE_RTP, this);
     84   // Forward device notifications.
     85   SignalDevicesChange.repeat(channel_manager_->SignalDevicesChange);
     86   // Bring up the channel manager.
     87   // In previous versions of ChannelManager, this was done automatically
     88   // in the constructor.
     89   channel_manager_->Init();
     90 }
     91 
     92 MediaSessionClient::~MediaSessionClient() {
     93   // Destroy all calls
     94   std::map<uint32, Call *>::iterator it;
     95   while (calls_.begin() != calls_.end()) {
     96     std::map<uint32, Call *>::iterator it = calls_.begin();
     97     DestroyCall((*it).second);
     98   }
     99 
    100   // Delete channel manager. This will wait for the channels to exit
    101   delete channel_manager_;
    102 
    103   // Remove ourselves from the client map.
    104   session_manager_->RemoveClient(NS_JINGLE_RTP);
    105 }
    106 
    107 Call *MediaSessionClient::CreateCall() {
    108   Call *call = new Call(this);
    109   calls_[call->id()] = call;
    110   SignalCallCreate(call);
    111   return call;
    112 }
    113 
    114 void MediaSessionClient::OnSessionCreate(Session *session,
    115                                          bool received_initiate) {
    116   if (received_initiate) {
    117     session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
    118   }
    119 }
    120 
    121 void MediaSessionClient::OnSessionState(BaseSession* base_session,
    122                                         BaseSession::State state) {
    123   // MediaSessionClient can only be used with a Session*, so it's
    124   // safe to cast here.
    125   Session* session = static_cast<Session*>(base_session);
    126 
    127   if (state == Session::STATE_RECEIVEDINITIATE) {
    128     // The creation of the call must happen after the session has
    129     // processed the initiate message because we need the
    130     // remote_description to know what content names to use in the
    131     // call.
    132 
    133     // If our accept would have no codecs, then we must reject this call.
    134     const SessionDescription* offer = session->remote_description();
    135     const SessionDescription* accept = CreateAnswer(offer, CallOptions());
    136     const ContentInfo* audio_content = GetFirstAudioContent(accept);
    137     bool audio_rejected = (!audio_content) ? true : audio_content->rejected;
    138     const AudioContentDescription* audio_desc = (!audio_content) ? NULL :
    139         static_cast<const AudioContentDescription*>(audio_content->description);
    140 
    141     // For some reason, we need a call even if we reject. So, either find a
    142     // matching call or create a new one.
    143     // The matching of existing calls is used to support the multi-session mode
    144     // required for p2p handoffs: ie. once a MUC call is established, a new
    145     // session may be established for the same call but is direct between the
    146     // clients. To indicate that this is the case, the initiator of the incoming
    147     // session is set to be the same as the remote name of the MUC for the
    148     // existing session, thus the client can know that this is a new session for
    149     // the existing call, rather than a whole new call.
    150     Call* call = NULL;
    151     if (multisession_enabled_) {
    152       call = FindCallByRemoteName(session->initiator_name());
    153     }
    154 
    155     if (call == NULL) {
    156       // Could not find a matching call, so create a new one.
    157       call = CreateCall();
    158     }
    159 
    160     session_map_[session->id()] = call;
    161     call->IncomingSession(session, offer);
    162 
    163     if (audio_rejected || !audio_desc || audio_desc->codecs().size() == 0) {
    164       session->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
    165     }
    166     delete accept;
    167   }
    168 }
    169 
    170 void MediaSessionClient::DestroyCall(Call *call) {
    171   // Change focus away, signal destruction
    172 
    173   if (call == focus_call_)
    174     SetFocus(NULL);
    175   SignalCallDestroy(call);
    176 
    177   // Remove it from calls_ map and delete
    178 
    179   std::map<uint32, Call *>::iterator it = calls_.find(call->id());
    180   if (it != calls_.end())
    181     calls_.erase(it);
    182 
    183   delete call;
    184 }
    185 
    186 void MediaSessionClient::OnSessionDestroy(Session *session) {
    187   // Find the call this session is in, remove it
    188   SessionMap::iterator it = session_map_.find(session->id());
    189   ASSERT(it != session_map_.end());
    190   if (it != session_map_.end()) {
    191     Call *call = (*it).second;
    192     session_map_.erase(it);
    193     call->RemoveSession(session);
    194   }
    195 }
    196 
    197 Call *MediaSessionClient::GetFocus() {
    198   return focus_call_;
    199 }
    200 
    201 void MediaSessionClient::SetFocus(Call *call) {
    202   Call *old_focus_call = focus_call_;
    203   if (focus_call_ != call) {
    204     if (focus_call_ != NULL)
    205       focus_call_->EnableChannels(false);
    206     focus_call_ = call;
    207     if (focus_call_ != NULL)
    208       focus_call_->EnableChannels(true);
    209     SignalFocus(focus_call_, old_focus_call);
    210   }
    211 }
    212 
    213 void MediaSessionClient::JoinCalls(Call *call_to_join, Call *call) {
    214   // Move all sessions from call to call_to_join, delete call.
    215   // If call_to_join has focus, added sessions should have enabled channels.
    216 
    217   if (focus_call_ == call)
    218     SetFocus(NULL);
    219   call_to_join->Join(call, focus_call_ == call_to_join);
    220   DestroyCall(call);
    221 }
    222 
    223 Session *MediaSessionClient::CreateSession(Call *call) {
    224   std::string id;
    225   return CreateSession(id, call);
    226 }
    227 
    228 Session *MediaSessionClient::CreateSession(const std::string& id, Call* call) {
    229   const std::string& type = NS_JINGLE_RTP;
    230   Session *session = session_manager_->CreateSession(id, jid().Str(), type);
    231   session_map_[session->id()] = call;
    232   return session;
    233 }
    234 
    235 Call *MediaSessionClient::FindCallByRemoteName(const std::string &remote_name) {
    236   SessionMap::const_iterator call;
    237   for (call = session_map_.begin(); call != session_map_.end(); ++call) {
    238     std::vector<Session *> sessions = call->second->sessions();
    239     std::vector<Session *>::const_iterator session;
    240     for (session = sessions.begin(); session != sessions.end(); ++session) {
    241       if (remote_name == (*session)->remote_name()) {
    242         return call->second;
    243       }
    244     }
    245   }
    246 
    247   return NULL;
    248 }
    249 
    250 // TODO(pthatcher): Move all of the parsing and writing functions into
    251 // mediamessages.cc, with unit tests.
    252 bool ParseGingleAudioCodec(const buzz::XmlElement* element, AudioCodec* out) {
    253   int id = GetXmlAttr(element, QN_ID, -1);
    254   if (id < 0)
    255     return false;
    256 
    257   std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
    258   int clockrate = GetXmlAttr(element, QN_CLOCKRATE, 0);
    259   int bitrate = GetXmlAttr(element, QN_BITRATE, 0);
    260   int channels = GetXmlAttr(element, QN_CHANNELS, 1);
    261   *out = AudioCodec(id, name, clockrate, bitrate, channels, 0);
    262   return true;
    263 }
    264 
    265 bool ParseGingleVideoCodec(const buzz::XmlElement* element, VideoCodec* out) {
    266   int id = GetXmlAttr(element, QN_ID, -1);
    267   if (id < 0)
    268     return false;
    269 
    270   std::string name = GetXmlAttr(element, QN_NAME, buzz::STR_EMPTY);
    271   int width = GetXmlAttr(element, QN_WIDTH, 0);
    272   int height = GetXmlAttr(element, QN_HEIGHT, 0);
    273   int framerate = GetXmlAttr(element, QN_FRAMERATE, 0);
    274 
    275   *out = VideoCodec(id, name, width, height, framerate, 0);
    276   return true;
    277 }
    278 
    279 // Parses an ssrc string as a legacy stream.  If it fails, returns
    280 // false and fills an error message.
    281 bool ParseSsrcAsLegacyStream(const std::string& ssrc_str,
    282                              std::vector<StreamParams>* streams,
    283                              ParseError* error) {
    284   if (!ssrc_str.empty()) {
    285     uint32 ssrc;
    286     if (!rtc::FromString(ssrc_str, &ssrc)) {
    287       return BadParse("Missing or invalid ssrc.", error);
    288     }
    289 
    290     streams->push_back(StreamParams::CreateLegacy(ssrc));
    291   }
    292   return true;
    293 }
    294 
    295 void ParseGingleSsrc(const buzz::XmlElement* parent_elem,
    296                      const buzz::QName& name,
    297                      MediaContentDescription* media) {
    298   const buzz::XmlElement* ssrc_elem = parent_elem->FirstNamed(name);
    299   if (ssrc_elem) {
    300     ParseError error;
    301     ParseSsrcAsLegacyStream(
    302         ssrc_elem->BodyText(), &(media->mutable_streams()), &error);
    303   }
    304 }
    305 
    306 bool ParseCryptoParams(const buzz::XmlElement* element,
    307                        CryptoParams* out,
    308                        ParseError* error) {
    309   if (!element->HasAttr(QN_CRYPTO_SUITE)) {
    310     return BadParse("crypto: crypto-suite attribute missing ", error);
    311   } else if (!element->HasAttr(QN_CRYPTO_KEY_PARAMS)) {
    312     return BadParse("crypto: key-params attribute missing ", error);
    313   } else if (!element->HasAttr(QN_CRYPTO_TAG)) {
    314     return BadParse("crypto: tag attribute missing ", error);
    315   }
    316 
    317   const std::string& crypto_suite = element->Attr(QN_CRYPTO_SUITE);
    318   const std::string& key_params = element->Attr(QN_CRYPTO_KEY_PARAMS);
    319   const int tag = GetXmlAttr(element, QN_CRYPTO_TAG, 0);
    320   const std::string& session_params =
    321       element->Attr(QN_CRYPTO_SESSION_PARAMS);  // Optional.
    322 
    323   *out = CryptoParams(tag, crypto_suite, key_params, session_params);
    324   return true;
    325 }
    326 
    327 
    328 // Parse the first encryption element found with a matching 'usage'
    329 // element.
    330 // <usage/> is specific to Gingle. In Jingle, <crypto/> is already
    331 // scoped to a content.
    332 // Return false if there was an encryption element and it could not be
    333 // parsed.
    334 bool ParseGingleEncryption(const buzz::XmlElement* desc,
    335                            const buzz::QName& usage,
    336                            MediaContentDescription* media,
    337                            ParseError* error) {
    338   for (const buzz::XmlElement* encryption = desc->FirstNamed(QN_ENCRYPTION);
    339        encryption != NULL;
    340        encryption = encryption->NextNamed(QN_ENCRYPTION)) {
    341     if (encryption->FirstNamed(usage) != NULL) {
    342       if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) {
    343         media->set_crypto_required(CT_SDES);
    344       }
    345       for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
    346            crypto != NULL;
    347            crypto = crypto->NextNamed(QN_CRYPTO)) {
    348         CryptoParams params;
    349         if (!ParseCryptoParams(crypto, &params, error)) {
    350           return false;
    351         }
    352         media->AddCrypto(params);
    353       }
    354       break;
    355     }
    356   }
    357   return true;
    358 }
    359 
    360 void ParseBandwidth(const buzz::XmlElement* parent_elem,
    361                     MediaContentDescription* media) {
    362   const buzz::XmlElement* bw_elem = GetXmlChild(parent_elem, LN_BANDWIDTH);
    363   int bandwidth_kbps = -1;
    364   if (bw_elem && rtc::FromString(bw_elem->BodyText(), &bandwidth_kbps)) {
    365     if (bandwidth_kbps >= 0) {
    366       media->set_bandwidth(bandwidth_kbps * 1000);
    367     }
    368   }
    369 }
    370 
    371 bool ParseGingleAudioContent(const buzz::XmlElement* content_elem,
    372                              ContentDescription** content,
    373                              ParseError* error) {
    374   AudioContentDescription* audio = new AudioContentDescription();
    375 
    376   int preference = kMaxPayloadId;
    377   if (content_elem->FirstElement()) {
    378     for (const buzz::XmlElement* codec_elem =
    379              content_elem->FirstNamed(QN_GINGLE_AUDIO_PAYLOADTYPE);
    380          codec_elem != NULL;
    381          codec_elem = codec_elem->NextNamed(QN_GINGLE_AUDIO_PAYLOADTYPE)) {
    382       AudioCodec codec;
    383       if (ParseGingleAudioCodec(codec_elem, &codec)) {
    384         codec.preference = preference--;
    385         audio->AddCodec(codec);
    386       }
    387     }
    388   } else {
    389     // For backward compatibility, we can assume the other client is
    390     // an old version of Talk if it has no audio payload types at all.
    391     audio->AddCodec(AudioCodec(103, "ISAC", 16000, -1, 1, 1));
    392     audio->AddCodec(AudioCodec(0, "PCMU", 8000, 64000, 1, 0));
    393   }
    394 
    395   ParseGingleSsrc(content_elem, QN_GINGLE_AUDIO_SRCID, audio);
    396 
    397   if (!ParseGingleEncryption(content_elem, QN_GINGLE_AUDIO_CRYPTO_USAGE,
    398                              audio, error)) {
    399     return false;
    400   }
    401 
    402   *content = audio;
    403   return true;
    404 }
    405 
    406 bool ParseGingleVideoContent(const buzz::XmlElement* content_elem,
    407                              ContentDescription** content,
    408                              ParseError* error) {
    409   VideoContentDescription* video = new VideoContentDescription();
    410 
    411   int preference = kMaxPayloadId;
    412   for (const buzz::XmlElement* codec_elem =
    413            content_elem->FirstNamed(QN_GINGLE_VIDEO_PAYLOADTYPE);
    414        codec_elem != NULL;
    415        codec_elem = codec_elem->NextNamed(QN_GINGLE_VIDEO_PAYLOADTYPE)) {
    416     VideoCodec codec;
    417     if (ParseGingleVideoCodec(codec_elem, &codec)) {
    418       codec.preference = preference--;
    419       video->AddCodec(codec);
    420     }
    421   }
    422 
    423   ParseGingleSsrc(content_elem, QN_GINGLE_VIDEO_SRCID, video);
    424   ParseBandwidth(content_elem, video);
    425 
    426   if (!ParseGingleEncryption(content_elem, QN_GINGLE_VIDEO_CRYPTO_USAGE,
    427                              video, error)) {
    428     return false;
    429   }
    430 
    431   *content = video;
    432   return true;
    433 }
    434 
    435 void ParsePayloadTypeParameters(const buzz::XmlElement* element,
    436                                 std::map<std::string, std::string>* paramap) {
    437   for (const buzz::XmlElement* param = element->FirstNamed(QN_PARAMETER);
    438        param != NULL; param = param->NextNamed(QN_PARAMETER)) {
    439     std::string name  = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_NAME,
    440                                    buzz::STR_EMPTY);
    441     std::string value = GetXmlAttr(param, QN_PAYLOADTYPE_PARAMETER_VALUE,
    442                                    buzz::STR_EMPTY);
    443     if (!name.empty() && !value.empty()) {
    444       paramap->insert(make_pair(name, value));
    445     }
    446   }
    447 }
    448 
    449 void ParseFeedbackParams(const buzz::XmlElement* element,
    450                          FeedbackParams* params) {
    451   for (const buzz::XmlElement* param = element->FirstNamed(QN_JINGLE_RTCP_FB);
    452        param != NULL; param = param->NextNamed(QN_JINGLE_RTCP_FB)) {
    453     std::string type = GetXmlAttr(param, QN_TYPE, buzz::STR_EMPTY);
    454     std::string subtype = GetXmlAttr(param, QN_SUBTYPE, buzz::STR_EMPTY);
    455     if (!type.empty()) {
    456       params->Add(FeedbackParam(type, subtype));
    457     }
    458   }
    459 }
    460 
    461 void AddFeedbackParams(const FeedbackParams& additional_params,
    462                        FeedbackParams* params) {
    463   for (size_t i = 0; i < additional_params.params().size(); ++i) {
    464     params->Add(additional_params.params()[i]);
    465   }
    466 }
    467 
    468 int FindWithDefault(const std::map<std::string, std::string>& map,
    469                     const std::string& key, const int def) {
    470   std::map<std::string, std::string>::const_iterator iter = map.find(key);
    471   return (iter == map.end()) ? def : atoi(iter->second.c_str());
    472 }
    473 
    474 
    475 // Parse the first encryption element found.
    476 // Return false if there was an encryption element and it could not be
    477 // parsed.
    478 bool ParseJingleEncryption(const buzz::XmlElement* content_elem,
    479                            MediaContentDescription* media,
    480                            ParseError* error) {
    481   const buzz::XmlElement* encryption =
    482           content_elem->FirstNamed(QN_ENCRYPTION);
    483   if (encryption == NULL) {
    484       return true;
    485   }
    486 
    487   if (GetXmlAttr(encryption, QN_ENCRYPTION_REQUIRED, false)) {
    488     media->set_crypto_required(CT_SDES);
    489   }
    490 
    491   for (const buzz::XmlElement* crypto = encryption->FirstNamed(QN_CRYPTO);
    492        crypto != NULL;
    493        crypto = crypto->NextNamed(QN_CRYPTO)) {
    494     CryptoParams params;
    495     if (!ParseCryptoParams(crypto, &params, error)) {
    496       return false;
    497     }
    498     media->AddCrypto(params);
    499   }
    500   return true;
    501 }
    502 
    503 bool ParseJingleAudioCodec(const buzz::XmlElement* elem, AudioCodec* codec) {
    504   int id = GetXmlAttr(elem, QN_ID, -1);
    505   if (id < 0)
    506     return false;
    507 
    508   std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
    509   int clockrate = GetXmlAttr(elem, QN_CLOCKRATE, 0);
    510   int channels = GetXmlAttr(elem, QN_CHANNELS, 1);
    511 
    512   std::map<std::string, std::string> paramap;
    513   ParsePayloadTypeParameters(elem, &paramap);
    514   int bitrate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_BITRATE, 0);
    515 
    516   *codec = AudioCodec(id, name, clockrate, bitrate, channels, 0);
    517   ParseFeedbackParams(elem, &codec->feedback_params);
    518   return true;
    519 }
    520 
    521 bool ParseJingleVideoCodec(const buzz::XmlElement* elem, VideoCodec* codec) {
    522   int id = GetXmlAttr(elem, QN_ID, -1);
    523   if (id < 0)
    524     return false;
    525 
    526   std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
    527 
    528   std::map<std::string, std::string> paramap;
    529   ParsePayloadTypeParameters(elem, &paramap);
    530   int width = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_WIDTH, 0);
    531   int height = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_HEIGHT, 0);
    532   int framerate = FindWithDefault(paramap, PAYLOADTYPE_PARAMETER_FRAMERATE, 0);
    533 
    534   *codec = VideoCodec(id, name, width, height, framerate, 0);
    535   codec->params = paramap;
    536   ParseFeedbackParams(elem, &codec->feedback_params);
    537   return true;
    538 }
    539 
    540 bool ParseJingleDataCodec(const buzz::XmlElement* elem, DataCodec* codec) {
    541   int id = GetXmlAttr(elem, QN_ID, -1);
    542   if (id < 0)
    543     return false;
    544 
    545   std::string name = GetXmlAttr(elem, QN_NAME, buzz::STR_EMPTY);
    546 
    547   *codec = DataCodec(id, name, 0);
    548   ParseFeedbackParams(elem, &codec->feedback_params);
    549   return true;
    550 }
    551 
    552 bool ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement* desc_elem,
    553                                     MediaContentDescription* media,
    554                                     ParseError* error) {
    555   if (HasJingleStreams(desc_elem)) {
    556     if (!ParseJingleStreams(desc_elem, &(media->mutable_streams()), error)) {
    557       return false;
    558     }
    559   } else {
    560     const std::string ssrc_str = desc_elem->Attr(QN_SSRC);
    561     if (!ParseSsrcAsLegacyStream(
    562             ssrc_str, &(media->mutable_streams()), error)) {
    563       return false;
    564     }
    565   }
    566   return true;
    567 }
    568 
    569 bool ParseJingleAudioContent(const buzz::XmlElement* content_elem,
    570                              ContentDescription** content,
    571                              ParseError* error) {
    572   rtc::scoped_ptr<AudioContentDescription> audio(
    573       new AudioContentDescription());
    574 
    575   FeedbackParams content_feedback_params;
    576   ParseFeedbackParams(content_elem, &content_feedback_params);
    577 
    578   int preference = kMaxPayloadId;
    579   for (const buzz::XmlElement* payload_elem =
    580            content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
    581       payload_elem != NULL;
    582       payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
    583     AudioCodec codec;
    584     if (ParseJingleAudioCodec(payload_elem, &codec)) {
    585       AddFeedbackParams(content_feedback_params, &codec.feedback_params);
    586       codec.preference = preference--;
    587       audio->AddCodec(codec);
    588     }
    589   }
    590 
    591   if (!ParseJingleStreamsOrLegacySsrc(content_elem, audio.get(), error)) {
    592     return false;
    593   }
    594 
    595   if (!ParseJingleEncryption(content_elem, audio.get(), error)) {
    596     return false;
    597   }
    598 
    599   audio->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
    600 
    601   RtpHeaderExtensions hdrexts;
    602   if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
    603     return false;
    604   }
    605   audio->set_rtp_header_extensions(hdrexts);
    606 
    607   *content = audio.release();
    608   return true;
    609 }
    610 
    611 bool ParseJingleVideoContent(const buzz::XmlElement* content_elem,
    612                              ContentDescription** content,
    613                              ParseError* error) {
    614   rtc::scoped_ptr<VideoContentDescription> video(
    615       new VideoContentDescription());
    616 
    617   FeedbackParams content_feedback_params;
    618   ParseFeedbackParams(content_elem, &content_feedback_params);
    619 
    620   int preference = kMaxPayloadId;
    621   for (const buzz::XmlElement* payload_elem =
    622            content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
    623       payload_elem != NULL;
    624       payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
    625     VideoCodec codec;
    626     if (ParseJingleVideoCodec(payload_elem, &codec)) {
    627       AddFeedbackParams(content_feedback_params, &codec.feedback_params);
    628       codec.preference = preference--;
    629       video->AddCodec(codec);
    630     }
    631   }
    632 
    633   if (!ParseJingleStreamsOrLegacySsrc(content_elem, video.get(), error)) {
    634     return false;
    635   }
    636   ParseBandwidth(content_elem, video.get());
    637 
    638   if (!ParseJingleEncryption(content_elem, video.get(), error)) {
    639     return false;
    640   }
    641 
    642   video->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
    643 
    644   RtpHeaderExtensions hdrexts;
    645   if (!ParseJingleRtpHeaderExtensions(content_elem, &hdrexts, error)) {
    646     return false;
    647   }
    648   video->set_rtp_header_extensions(hdrexts);
    649 
    650   *content = video.release();
    651   return true;
    652 }
    653 
    654 bool ParseJingleSctpDataContent(const buzz::XmlElement* content_elem,
    655                                 ContentDescription** content,
    656                                 ParseError* error) {
    657   rtc::scoped_ptr<DataContentDescription> data(
    658       new DataContentDescription());
    659   data->set_protocol(kMediaProtocolSctp);
    660 
    661   for (const buzz::XmlElement* stream_elem =
    662            content_elem->FirstNamed(QN_JINGLE_DRAFT_SCTP_STREAM);
    663        stream_elem != NULL;
    664        stream_elem = stream_elem->NextNamed(QN_JINGLE_DRAFT_SCTP_STREAM)) {
    665     StreamParams stream;
    666     stream.groupid = stream_elem->Attr(QN_NICK);
    667     stream.id = stream_elem->Attr(QN_NAME);
    668     uint32 sid;
    669     if (!rtc::FromString(stream_elem->Attr(QN_SID), &sid)) {
    670       return BadParse("Missing or invalid sid.", error);
    671     }
    672     if (sid > kMaxSctpSid) {
    673       return BadParse("SID is greater than max value.", error);
    674     }
    675 
    676     stream.ssrcs.push_back(sid);
    677     data->mutable_streams().push_back(stream);
    678   }
    679 
    680   *content = data.release();
    681   return true;
    682 }
    683 
    684 bool ParseJingleRtpDataContent(const buzz::XmlElement* content_elem,
    685                                ContentDescription** content,
    686                                ParseError* error) {
    687   DataContentDescription* data = new DataContentDescription();
    688 
    689   FeedbackParams content_feedback_params;
    690   ParseFeedbackParams(content_elem, &content_feedback_params);
    691 
    692   int preference = kMaxPayloadId;
    693   for (const buzz::XmlElement* payload_elem =
    694            content_elem->FirstNamed(QN_JINGLE_RTP_PAYLOADTYPE);
    695       payload_elem != NULL;
    696       payload_elem = payload_elem->NextNamed(QN_JINGLE_RTP_PAYLOADTYPE)) {
    697     DataCodec codec;
    698     if (ParseJingleDataCodec(payload_elem, &codec)) {
    699       AddFeedbackParams(content_feedback_params, &codec.feedback_params);
    700       codec.preference = preference--;
    701       data->AddCodec(codec);
    702     }
    703   }
    704 
    705   if (!ParseJingleStreamsOrLegacySsrc(content_elem, data, error)) {
    706     return false;
    707   }
    708   ParseBandwidth(content_elem, data);
    709 
    710   if (!ParseJingleEncryption(content_elem, data, error)) {
    711     return false;
    712   }
    713 
    714   data->set_rtcp_mux(content_elem->FirstNamed(QN_JINGLE_RTCP_MUX) != NULL);
    715 
    716   *content = data;
    717   return true;
    718 }
    719 
    720 bool MediaSessionClient::ParseContent(SignalingProtocol protocol,
    721                                       const buzz::XmlElement* content_elem,
    722                                       ContentDescription** content,
    723                                       ParseError* error) {
    724   if (protocol == PROTOCOL_GINGLE) {
    725     const std::string& content_type = content_elem->Name().Namespace();
    726     if (NS_GINGLE_AUDIO == content_type) {
    727       return ParseGingleAudioContent(content_elem, content, error);
    728     } else if (NS_GINGLE_VIDEO == content_type) {
    729       return ParseGingleVideoContent(content_elem, content, error);
    730     } else {
    731       return BadParse("Unknown content type: " + content_type, error);
    732     }
    733   } else {
    734     const std::string& content_type = content_elem->Name().Namespace();
    735     // We use the XMLNS of the <description> element to determine if
    736     // it's RTP or SCTP.
    737     if (content_type == NS_JINGLE_DRAFT_SCTP) {
    738       return ParseJingleSctpDataContent(content_elem, content, error);
    739     }
    740 
    741     std::string media;
    742     if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
    743       return false;
    744 
    745     if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
    746       return ParseJingleAudioContent(content_elem, content, error);
    747     } else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
    748       return ParseJingleVideoContent(content_elem, content, error);
    749     } else if (media == JINGLE_CONTENT_MEDIA_DATA) {
    750       return ParseJingleRtpDataContent(content_elem, content, error);
    751     } else {
    752       return BadParse("Unknown media: " + media, error);
    753     }
    754   }
    755 }
    756 
    757 buzz::XmlElement* CreateGingleAudioCodecElem(const AudioCodec& codec) {
    758   buzz::XmlElement* payload_type =
    759       new buzz::XmlElement(QN_GINGLE_AUDIO_PAYLOADTYPE, true);
    760   AddXmlAttr(payload_type, QN_ID, codec.id);
    761   payload_type->AddAttr(QN_NAME, codec.name);
    762   if (codec.clockrate > 0)
    763     AddXmlAttr(payload_type, QN_CLOCKRATE, codec.clockrate);
    764   if (codec.bitrate > 0)
    765     AddXmlAttr(payload_type, QN_BITRATE, codec.bitrate);
    766   if (codec.channels > 1)
    767     AddXmlAttr(payload_type, QN_CHANNELS, codec.channels);
    768   return payload_type;
    769 }
    770 
    771 buzz::XmlElement* CreateGingleVideoCodecElem(const VideoCodec& codec) {
    772   buzz::XmlElement* payload_type =
    773       new buzz::XmlElement(QN_GINGLE_VIDEO_PAYLOADTYPE, true);
    774   AddXmlAttr(payload_type, QN_ID, codec.id);
    775   payload_type->AddAttr(QN_NAME, codec.name);
    776   AddXmlAttr(payload_type, QN_WIDTH, codec.width);
    777   AddXmlAttr(payload_type, QN_HEIGHT, codec.height);
    778   AddXmlAttr(payload_type, QN_FRAMERATE, codec.framerate);
    779   return payload_type;
    780 }
    781 
    782 buzz::XmlElement* CreateGingleSsrcElem(const buzz::QName& name, uint32 ssrc) {
    783   buzz::XmlElement* elem = new buzz::XmlElement(name, true);
    784   if (ssrc) {
    785     SetXmlBody(elem, ssrc);
    786   }
    787   return elem;
    788 }
    789 
    790 buzz::XmlElement* CreateBandwidthElem(const buzz::QName& name, int bps) {
    791   int kbps = bps / 1000;
    792   buzz::XmlElement* elem = new buzz::XmlElement(name);
    793   elem->AddAttr(buzz::QN_TYPE, "AS");
    794   SetXmlBody(elem, kbps);
    795   return elem;
    796 }
    797 
    798 // For Jingle, usage_qname is empty.
    799 buzz::XmlElement* CreateJingleEncryptionElem(const CryptoParamsVec& cryptos,
    800                                              bool required) {
    801   buzz::XmlElement* encryption_elem = new buzz::XmlElement(QN_ENCRYPTION);
    802 
    803   if (required) {
    804     encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
    805   }
    806 
    807   for (CryptoParamsVec::const_iterator i = cryptos.begin();
    808        i != cryptos.end();
    809        ++i) {
    810     buzz::XmlElement* crypto_elem = new buzz::XmlElement(QN_CRYPTO);
    811 
    812     AddXmlAttr(crypto_elem, QN_CRYPTO_TAG, i->tag);
    813     crypto_elem->AddAttr(QN_CRYPTO_SUITE, i->cipher_suite);
    814     crypto_elem->AddAttr(QN_CRYPTO_KEY_PARAMS, i->key_params);
    815     if (!i->session_params.empty()) {
    816       crypto_elem->AddAttr(QN_CRYPTO_SESSION_PARAMS, i->session_params);
    817     }
    818     encryption_elem->AddElement(crypto_elem);
    819   }
    820   return encryption_elem;
    821 }
    822 
    823 buzz::XmlElement* CreateGingleEncryptionElem(const CryptoParamsVec& cryptos,
    824                                              const buzz::QName& usage_qname,
    825                                              bool required) {
    826   buzz::XmlElement* encryption_elem =
    827       CreateJingleEncryptionElem(cryptos, required);
    828 
    829   if (required) {
    830     encryption_elem->SetAttr(QN_ENCRYPTION_REQUIRED, "true");
    831   }
    832 
    833   buzz::XmlElement* usage_elem = new buzz::XmlElement(usage_qname);
    834   encryption_elem->AddElement(usage_elem);
    835 
    836   return encryption_elem;
    837 }
    838 
    839 buzz::XmlElement* CreateGingleAudioContentElem(
    840     const AudioContentDescription* audio,
    841     bool crypto_required) {
    842   buzz::XmlElement* elem =
    843       new buzz::XmlElement(QN_GINGLE_AUDIO_CONTENT, true);
    844 
    845   for (AudioCodecs::const_iterator codec = audio->codecs().begin();
    846        codec != audio->codecs().end(); ++codec) {
    847     elem->AddElement(CreateGingleAudioCodecElem(*codec));
    848   }
    849   if (audio->has_ssrcs()) {
    850     elem->AddElement(CreateGingleSsrcElem(
    851         QN_GINGLE_AUDIO_SRCID, audio->first_ssrc()));
    852   }
    853 
    854   const CryptoParamsVec& cryptos = audio->cryptos();
    855   if (!cryptos.empty()) {
    856     elem->AddElement(CreateGingleEncryptionElem(cryptos,
    857                                                 QN_GINGLE_AUDIO_CRYPTO_USAGE,
    858                                                 crypto_required));
    859   }
    860   return elem;
    861 }
    862 
    863 buzz::XmlElement* CreateGingleVideoContentElem(
    864     const VideoContentDescription* video,
    865     bool crypto_required) {
    866   buzz::XmlElement* elem =
    867       new buzz::XmlElement(QN_GINGLE_VIDEO_CONTENT, true);
    868 
    869   for (VideoCodecs::const_iterator codec = video->codecs().begin();
    870        codec != video->codecs().end(); ++codec) {
    871     elem->AddElement(CreateGingleVideoCodecElem(*codec));
    872   }
    873   if (video->has_ssrcs()) {
    874     elem->AddElement(CreateGingleSsrcElem(
    875         QN_GINGLE_VIDEO_SRCID, video->first_ssrc()));
    876   }
    877   if (video->bandwidth() != kAutoBandwidth) {
    878     elem->AddElement(CreateBandwidthElem(QN_GINGLE_VIDEO_BANDWIDTH,
    879                                          video->bandwidth()));
    880   }
    881 
    882   const CryptoParamsVec& cryptos = video->cryptos();
    883   if (!cryptos.empty()) {
    884     elem->AddElement(CreateGingleEncryptionElem(cryptos,
    885                                                 QN_GINGLE_VIDEO_CRYPTO_USAGE,
    886                                                 crypto_required));
    887   }
    888 
    889   return elem;
    890 }
    891 
    892 template <class T>
    893 buzz::XmlElement* CreatePayloadTypeParameterElem(
    894     const std::string& name, T value) {
    895   buzz::XmlElement* elem = new buzz::XmlElement(QN_PARAMETER);
    896 
    897   elem->AddAttr(QN_PAYLOADTYPE_PARAMETER_NAME, name);
    898   AddXmlAttr(elem, QN_PAYLOADTYPE_PARAMETER_VALUE, value);
    899 
    900   return elem;
    901 }
    902 
    903 void AddRtcpFeedbackElem(buzz::XmlElement* elem,
    904                       const FeedbackParams& feedback_params) {
    905   std::vector<FeedbackParam>::const_iterator it;
    906   for (it = feedback_params.params().begin();
    907        it != feedback_params.params().end(); ++it) {
    908     buzz::XmlElement* fb_elem = new buzz::XmlElement(QN_JINGLE_RTCP_FB);
    909     fb_elem->AddAttr(QN_TYPE, it->id());
    910     fb_elem->AddAttr(QN_SUBTYPE, it->param());
    911     elem->AddElement(fb_elem);
    912   }
    913 }
    914 
    915 buzz::XmlElement* CreateJingleAudioCodecElem(const AudioCodec& codec) {
    916   buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
    917 
    918   AddXmlAttr(elem, QN_ID, codec.id);
    919   elem->AddAttr(QN_NAME, codec.name);
    920   if (codec.clockrate > 0) {
    921     AddXmlAttr(elem, QN_CLOCKRATE, codec.clockrate);
    922   }
    923   if (codec.bitrate > 0) {
    924     elem->AddElement(CreatePayloadTypeParameterElem(
    925         PAYLOADTYPE_PARAMETER_BITRATE, codec.bitrate));
    926   }
    927   if (codec.channels > 1) {
    928     AddXmlAttr(elem, QN_CHANNELS, codec.channels);
    929   }
    930 
    931   AddRtcpFeedbackElem(elem, codec.feedback_params);
    932 
    933   return elem;
    934 }
    935 
    936 buzz::XmlElement* CreateJingleVideoCodecElem(const VideoCodec& codec) {
    937   buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
    938 
    939   AddXmlAttr(elem, QN_ID, codec.id);
    940   elem->AddAttr(QN_NAME, codec.name);
    941   elem->AddElement(CreatePayloadTypeParameterElem(
    942       PAYLOADTYPE_PARAMETER_WIDTH, codec.width));
    943   elem->AddElement(CreatePayloadTypeParameterElem(
    944       PAYLOADTYPE_PARAMETER_HEIGHT, codec.height));
    945   elem->AddElement(CreatePayloadTypeParameterElem(
    946       PAYLOADTYPE_PARAMETER_FRAMERATE, codec.framerate));
    947 
    948   AddRtcpFeedbackElem(elem, codec.feedback_params);
    949 
    950   CodecParameterMap::const_iterator param_iter;
    951   for (param_iter = codec.params.begin(); param_iter != codec.params.end();
    952        ++param_iter) {
    953     elem->AddElement(CreatePayloadTypeParameterElem(param_iter->first,
    954                                                     param_iter->second));
    955   }
    956 
    957   return elem;
    958 }
    959 
    960 buzz::XmlElement* CreateJingleDataCodecElem(const DataCodec& codec) {
    961   buzz::XmlElement* elem = new buzz::XmlElement(QN_JINGLE_RTP_PAYLOADTYPE);
    962 
    963   AddXmlAttr(elem, QN_ID, codec.id);
    964   elem->AddAttr(QN_NAME, codec.name);
    965 
    966   AddRtcpFeedbackElem(elem, codec.feedback_params);
    967 
    968   return elem;
    969 }
    970 
    971 void WriteLegacyJingleSsrc(const MediaContentDescription* media,
    972                            buzz::XmlElement* elem) {
    973   if (media->has_ssrcs()) {
    974     AddXmlAttr(elem, QN_SSRC, media->first_ssrc());
    975   }
    976 }
    977 
    978 void WriteJingleStreamsOrLegacySsrc(const MediaContentDescription* media,
    979                                     buzz::XmlElement* desc_elem) {
    980   if (!media->multistream()) {
    981     WriteLegacyJingleSsrc(media, desc_elem);
    982   } else {
    983     WriteJingleStreams(media->streams(), desc_elem);
    984   }
    985 }
    986 
    987 buzz::XmlElement* CreateJingleAudioContentElem(
    988     const AudioContentDescription* audio, bool crypto_required) {
    989   buzz::XmlElement* elem =
    990       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
    991 
    992   elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_AUDIO);
    993   WriteJingleStreamsOrLegacySsrc(audio, elem);
    994 
    995   for (AudioCodecs::const_iterator codec = audio->codecs().begin();
    996        codec != audio->codecs().end(); ++codec) {
    997     elem->AddElement(CreateJingleAudioCodecElem(*codec));
    998   }
    999 
   1000   const CryptoParamsVec& cryptos = audio->cryptos();
   1001   if (!cryptos.empty()) {
   1002     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
   1003   }
   1004 
   1005   if (audio->rtcp_mux()) {
   1006     elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
   1007   }
   1008 
   1009   WriteJingleRtpHeaderExtensions(audio->rtp_header_extensions(), elem);
   1010 
   1011   return elem;
   1012 }
   1013 
   1014 buzz::XmlElement* CreateJingleVideoContentElem(
   1015     const VideoContentDescription* video, bool crypto_required) {
   1016   buzz::XmlElement* elem =
   1017       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
   1018 
   1019   elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_VIDEO);
   1020   WriteJingleStreamsOrLegacySsrc(video, elem);
   1021 
   1022   for (VideoCodecs::const_iterator codec = video->codecs().begin();
   1023        codec != video->codecs().end(); ++codec) {
   1024     elem->AddElement(CreateJingleVideoCodecElem(*codec));
   1025   }
   1026 
   1027   const CryptoParamsVec& cryptos = video->cryptos();
   1028   if (!cryptos.empty()) {
   1029     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
   1030   }
   1031 
   1032   if (video->rtcp_mux()) {
   1033     elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
   1034   }
   1035 
   1036   if (video->bandwidth() != kAutoBandwidth) {
   1037     elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
   1038                                          video->bandwidth()));
   1039   }
   1040 
   1041   WriteJingleRtpHeaderExtensions(video->rtp_header_extensions(), elem);
   1042 
   1043   return elem;
   1044 }
   1045 
   1046 buzz::XmlElement* CreateJingleSctpDataContentElem(
   1047     const DataContentDescription* data) {
   1048   buzz::XmlElement* content_elem =
   1049       new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_CONTENT, true);
   1050   for (std::vector<StreamParams>::const_iterator
   1051            stream = data->streams().begin();
   1052        stream != data->streams().end(); ++stream) {
   1053     buzz::XmlElement* stream_elem =
   1054       new buzz::XmlElement(QN_JINGLE_DRAFT_SCTP_STREAM, false);
   1055     AddXmlAttrIfNonEmpty(stream_elem, QN_NICK, stream->groupid);
   1056     AddXmlAttrIfNonEmpty(stream_elem, QN_NAME, stream->id);
   1057     if (!stream->ssrcs.empty()) {
   1058       AddXmlAttr(stream_elem, QN_SID, stream->ssrcs[0]);
   1059     }
   1060     content_elem->AddElement(stream_elem);
   1061   }
   1062   return content_elem;;
   1063 }
   1064 
   1065 buzz::XmlElement* CreateJingleRtpDataContentElem(
   1066     const DataContentDescription* data, bool crypto_required) {
   1067 
   1068   buzz::XmlElement* elem =
   1069       new buzz::XmlElement(QN_JINGLE_RTP_CONTENT, true);
   1070 
   1071   elem->SetAttr(QN_JINGLE_CONTENT_MEDIA, JINGLE_CONTENT_MEDIA_DATA);
   1072   WriteJingleStreamsOrLegacySsrc(data, elem);
   1073 
   1074   for (DataCodecs::const_iterator codec = data->codecs().begin();
   1075        codec != data->codecs().end(); ++codec) {
   1076     elem->AddElement(CreateJingleDataCodecElem(*codec));
   1077   }
   1078 
   1079   const CryptoParamsVec& cryptos = data->cryptos();
   1080   if (!cryptos.empty()) {
   1081     elem->AddElement(CreateJingleEncryptionElem(cryptos, crypto_required));
   1082   }
   1083 
   1084   if (data->rtcp_mux()) {
   1085     elem->AddElement(new buzz::XmlElement(QN_JINGLE_RTCP_MUX));
   1086   }
   1087 
   1088   if (data->bandwidth() != kAutoBandwidth) {
   1089     elem->AddElement(CreateBandwidthElem(QN_JINGLE_RTP_BANDWIDTH,
   1090                                          data->bandwidth()));
   1091   }
   1092 
   1093   return elem;
   1094 }
   1095 
   1096 bool IsSctp(const DataContentDescription* data) {
   1097   return (data->protocol() == kMediaProtocolSctp ||
   1098     data->protocol() == kMediaProtocolDtlsSctp);
   1099 }
   1100 
   1101 buzz::XmlElement* CreateJingleDataContentElem(
   1102     const DataContentDescription* data, bool crypto_required) {
   1103   if (IsSctp(data)) {
   1104     return CreateJingleSctpDataContentElem(data);
   1105   } else {
   1106     return CreateJingleRtpDataContentElem(data, crypto_required);
   1107   }
   1108 }
   1109 
   1110 bool MediaSessionClient::IsWritable(SignalingProtocol protocol,
   1111                                     const ContentDescription* content) {
   1112   const MediaContentDescription* media =
   1113       static_cast<const MediaContentDescription*>(content);
   1114   if (protocol == PROTOCOL_GINGLE &&
   1115       media->type() == MEDIA_TYPE_DATA) {
   1116     return false;
   1117   }
   1118   return true;
   1119 }
   1120 
   1121 bool MediaSessionClient::WriteContent(SignalingProtocol protocol,
   1122                                       const ContentDescription* content,
   1123                                       buzz::XmlElement** elem,
   1124                                       WriteError* error) {
   1125   const MediaContentDescription* media =
   1126       static_cast<const MediaContentDescription*>(content);
   1127   bool crypto_required = secure() == SEC_REQUIRED;
   1128 
   1129   if (media->type() == MEDIA_TYPE_AUDIO) {
   1130     const AudioContentDescription* audio =
   1131         static_cast<const AudioContentDescription*>(media);
   1132     if (protocol == PROTOCOL_GINGLE) {
   1133       *elem = CreateGingleAudioContentElem(audio, crypto_required);
   1134     } else {
   1135       *elem = CreateJingleAudioContentElem(audio, crypto_required);
   1136     }
   1137   } else if (media->type() == MEDIA_TYPE_VIDEO) {
   1138     const VideoContentDescription* video =
   1139         static_cast<const VideoContentDescription*>(media);
   1140     if (protocol == PROTOCOL_GINGLE) {
   1141       *elem = CreateGingleVideoContentElem(video, crypto_required);
   1142     } else {
   1143       *elem = CreateJingleVideoContentElem(video, crypto_required);
   1144     }
   1145   } else if (media->type() == MEDIA_TYPE_DATA) {
   1146     const DataContentDescription* data =
   1147         static_cast<const DataContentDescription*>(media);
   1148     if (protocol == PROTOCOL_GINGLE) {
   1149       return BadWrite("Data channel not supported with Gingle.", error);
   1150     } else {
   1151       *elem = CreateJingleDataContentElem(data, crypto_required);
   1152     }
   1153   } else {
   1154     return BadWrite("Unknown content type: " +
   1155                     rtc::ToString<int>(media->type()), error);
   1156   }
   1157 
   1158   return true;
   1159 }
   1160 
   1161 }  // namespace cricket
   1162