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, ¶ms, 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, ¶ms, 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, ¶map);
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, ¶map);
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