• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
MediaSessionClient(const buzz::Jid & jid,SessionManager * manager)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 
MediaSessionClient(const buzz::Jid & jid,SessionManager * manager,MediaEngineInterface * media_engine,DataEngineInterface * data_media_engine,DeviceManagerInterface * device_manager)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 
Construct()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 
~MediaSessionClient()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 
CreateCall()107 Call *MediaSessionClient::CreateCall() {
108   Call *call = new Call(this);
109   calls_[call->id()] = call;
110   SignalCallCreate(call);
111   return call;
112 }
113 
OnSessionCreate(Session * session,bool received_initiate)114 void MediaSessionClient::OnSessionCreate(Session *session,
115                                          bool received_initiate) {
116   if (received_initiate) {
117     session->SignalState.connect(this, &MediaSessionClient::OnSessionState);
118   }
119 }
120 
OnSessionState(BaseSession * base_session,BaseSession::State state)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 
DestroyCall(Call * call)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 
OnSessionDestroy(Session * session)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 
GetFocus()197 Call *MediaSessionClient::GetFocus() {
198   return focus_call_;
199 }
200 
SetFocus(Call * call)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 
JoinCalls(Call * call_to_join,Call * call)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 
CreateSession(Call * call)223 Session *MediaSessionClient::CreateSession(Call *call) {
224   std::string id;
225   return CreateSession(id, call);
226 }
227 
CreateSession(const std::string & id,Call * call)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 
FindCallByRemoteName(const std::string & remote_name)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.
ParseGingleAudioCodec(const buzz::XmlElement * element,AudioCodec * out)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 
ParseGingleVideoCodec(const buzz::XmlElement * element,VideoCodec * out)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.
ParseSsrcAsLegacyStream(const std::string & ssrc_str,std::vector<StreamParams> * streams,ParseError * error)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 
ParseGingleSsrc(const buzz::XmlElement * parent_elem,const buzz::QName & name,MediaContentDescription * media)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 
ParseCryptoParams(const buzz::XmlElement * element,CryptoParams * out,ParseError * error)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.
ParseGingleEncryption(const buzz::XmlElement * desc,const buzz::QName & usage,MediaContentDescription * media,ParseError * error)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 
ParseBandwidth(const buzz::XmlElement * parent_elem,MediaContentDescription * media)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 
ParseGingleAudioContent(const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
ParseGingleVideoContent(const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
ParsePayloadTypeParameters(const buzz::XmlElement * element,std::map<std::string,std::string> * paramap)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 
ParseFeedbackParams(const buzz::XmlElement * element,FeedbackParams * params)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 
AddFeedbackParams(const FeedbackParams & additional_params,FeedbackParams * params)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 
FindWithDefault(const std::map<std::string,std::string> & map,const std::string & key,const int def)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.
ParseJingleEncryption(const buzz::XmlElement * content_elem,MediaContentDescription * media,ParseError * error)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 
ParseJingleAudioCodec(const buzz::XmlElement * elem,AudioCodec * codec)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 
ParseJingleVideoCodec(const buzz::XmlElement * elem,VideoCodec * codec)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 
ParseJingleDataCodec(const buzz::XmlElement * elem,DataCodec * codec)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 
ParseJingleStreamsOrLegacySsrc(const buzz::XmlElement * desc_elem,MediaContentDescription * media,ParseError * error)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 
ParseJingleAudioContent(const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
ParseJingleVideoContent(const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
ParseJingleSctpDataContent(const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
ParseJingleRtpDataContent(const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
ParseContent(SignalingProtocol protocol,const buzz::XmlElement * content_elem,ContentDescription ** content,ParseError * error)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 
CreateGingleAudioCodecElem(const AudioCodec & codec)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 
CreateGingleVideoCodecElem(const VideoCodec & codec)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 
CreateGingleSsrcElem(const buzz::QName & name,uint32 ssrc)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 
CreateBandwidthElem(const buzz::QName & name,int bps)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.
CreateJingleEncryptionElem(const CryptoParamsVec & cryptos,bool required)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 
CreateGingleEncryptionElem(const CryptoParamsVec & cryptos,const buzz::QName & usage_qname,bool required)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 
CreateGingleAudioContentElem(const AudioContentDescription * audio,bool crypto_required)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 
CreateGingleVideoContentElem(const VideoContentDescription * video,bool crypto_required)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>
CreatePayloadTypeParameterElem(const std::string & name,T value)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 
AddRtcpFeedbackElem(buzz::XmlElement * elem,const FeedbackParams & feedback_params)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 
CreateJingleAudioCodecElem(const AudioCodec & codec)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 
CreateJingleVideoCodecElem(const VideoCodec & codec)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 
CreateJingleDataCodecElem(const DataCodec & codec)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 
WriteLegacyJingleSsrc(const MediaContentDescription * media,buzz::XmlElement * elem)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 
WriteJingleStreamsOrLegacySsrc(const MediaContentDescription * media,buzz::XmlElement * desc_elem)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 
CreateJingleAudioContentElem(const AudioContentDescription * audio,bool crypto_required)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 
CreateJingleVideoContentElem(const VideoContentDescription * video,bool crypto_required)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 
CreateJingleSctpDataContentElem(const DataContentDescription * data)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 
CreateJingleRtpDataContentElem(const DataContentDescription * data,bool crypto_required)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 
IsSctp(const DataContentDescription * data)1096 bool IsSctp(const DataContentDescription* data) {
1097   return (data->protocol() == kMediaProtocolSctp ||
1098     data->protocol() == kMediaProtocolDtlsSctp);
1099 }
1100 
CreateJingleDataContentElem(const DataContentDescription * data,bool crypto_required)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 
IsWritable(SignalingProtocol protocol,const ContentDescription * content)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 
WriteContent(SignalingProtocol protocol,const ContentDescription * content,buzz::XmlElement ** elem,WriteError * error)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