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 44using namespace talk_base; 45 46namespace { 47const std::string kInline = "inline:"; 48} 49 50namespace cricket { 51 52typedef std::vector<CryptoParams> CryptoParamsVec; 53 54MediaSessionClient::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 62MediaSessionClient::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 73void 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 84MediaSessionClient::~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 99bool 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 112bool 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. 120bool 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 129bool 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 137SessionDescription* 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 209const 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 228const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) { 229 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO); 230} 231 232const 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. 239bool 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 253SessionDescription* 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 336Call *MediaSessionClient::CreateCall() { 337 Call *call = new Call(this); 338 calls_[call->id()] = call; 339 SignalCallCreate(call); 340 return call; 341} 342 343void MediaSessionClient::OnSessionCreate(Session *session, 344 bool received_initiate) { 345 if (received_initiate) { 346 session->SignalState.connect(this, &MediaSessionClient::OnSessionState); 347 } 348} 349 350void 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 382void 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 398void 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 410Call *MediaSessionClient::GetFocus() { 411 return focus_call_; 412} 413 414void 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 426void 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 436Session *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 443bool 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 456bool 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 470void 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 479bool 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. 507bool 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 532void 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 543bool 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 576bool 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 603void 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 617int 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. 627bool 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 651bool 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 668bool 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 685bool 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 708bool 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 733bool 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 761buzz::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 775buzz::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 786buzz::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 794buzz::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. 803buzz::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 827buzz::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 843buzz::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 869buzz::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 898buzz::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 908buzz::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 927buzz::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 942buzz::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 963buzz::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 989bool 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