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