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