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 "talk/session/media/mediasession.h" 29 30 #include <functional> 31 #include <map> 32 #include <set> 33 #include <utility> 34 35 #include "talk/base/helpers.h" 36 #include "talk/base/logging.h" 37 #include "talk/base/scoped_ptr.h" 38 #include "talk/base/stringutils.h" 39 #include "talk/media/base/constants.h" 40 #include "talk/media/base/cryptoparams.h" 41 #include "talk/p2p/base/constants.h" 42 #include "talk/session/media/channelmanager.h" 43 #include "talk/session/media/srtpfilter.h" 44 #include "talk/xmpp/constants.h" 45 46 namespace { 47 const char kInline[] = "inline:"; 48 } 49 50 namespace cricket { 51 52 using talk_base::scoped_ptr; 53 54 // RTP Profile names 55 // http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml 56 // RFC4585 57 const char kMediaProtocolAvpf[] = "RTP/AVPF"; 58 // RFC5124 59 const char kMediaProtocolSavpf[] = "RTP/SAVPF"; 60 61 const char kMediaProtocolRtpPrefix[] = "RTP/"; 62 63 const char kMediaProtocolSctp[] = "SCTP"; 64 const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP"; 65 66 static bool IsMediaContentOfType(const ContentInfo* content, 67 MediaType media_type) { 68 if (!IsMediaContent(content)) { 69 return false; 70 } 71 72 const MediaContentDescription* mdesc = 73 static_cast<const MediaContentDescription*>(content->description); 74 return mdesc && mdesc->type() == media_type; 75 } 76 77 static bool CreateCryptoParams(int tag, const std::string& cipher, 78 CryptoParams *out) { 79 std::string key; 80 key.reserve(SRTP_MASTER_KEY_BASE64_LEN); 81 82 if (!talk_base::CreateRandomString(SRTP_MASTER_KEY_BASE64_LEN, &key)) { 83 return false; 84 } 85 out->tag = tag; 86 out->cipher_suite = cipher; 87 out->key_params = kInline; 88 out->key_params += key; 89 return true; 90 } 91 92 #ifdef HAVE_SRTP 93 static bool AddCryptoParams(const std::string& cipher_suite, 94 CryptoParamsVec *out) { 95 int size = static_cast<int>(out->size()); 96 97 out->resize(size + 1); 98 return CreateCryptoParams(size, cipher_suite, &out->at(size)); 99 } 100 101 void AddMediaCryptos(const CryptoParamsVec& cryptos, 102 MediaContentDescription* media) { 103 for (CryptoParamsVec::const_iterator crypto = cryptos.begin(); 104 crypto != cryptos.end(); ++crypto) { 105 media->AddCrypto(*crypto); 106 } 107 } 108 109 bool CreateMediaCryptos(const std::vector<std::string>& crypto_suites, 110 MediaContentDescription* media) { 111 CryptoParamsVec cryptos; 112 for (std::vector<std::string>::const_iterator it = crypto_suites.begin(); 113 it != crypto_suites.end(); ++it) { 114 if (!AddCryptoParams(*it, &cryptos)) { 115 return false; 116 } 117 } 118 AddMediaCryptos(cryptos, media); 119 return true; 120 } 121 #endif 122 123 const CryptoParamsVec* GetCryptos(const MediaContentDescription* media) { 124 if (!media) { 125 return NULL; 126 } 127 return &media->cryptos(); 128 } 129 130 bool FindMatchingCrypto(const CryptoParamsVec& cryptos, 131 const CryptoParams& crypto, 132 CryptoParams* out) { 133 for (CryptoParamsVec::const_iterator it = cryptos.begin(); 134 it != cryptos.end(); ++it) { 135 if (crypto.Matches(*it)) { 136 *out = *it; 137 return true; 138 } 139 } 140 return false; 141 } 142 143 // For audio, HMAC 32 is prefered because of the low overhead. 144 void GetSupportedAudioCryptoSuites( 145 std::vector<std::string>* crypto_suites) { 146 #ifdef HAVE_SRTP 147 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_32); 148 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80); 149 #endif 150 } 151 152 void GetSupportedVideoCryptoSuites( 153 std::vector<std::string>* crypto_suites) { 154 GetSupportedDefaultCryptoSuites(crypto_suites); 155 } 156 157 void GetSupportedDataCryptoSuites( 158 std::vector<std::string>* crypto_suites) { 159 GetSupportedDefaultCryptoSuites(crypto_suites); 160 } 161 162 void GetSupportedDefaultCryptoSuites( 163 std::vector<std::string>* crypto_suites) { 164 #ifdef HAVE_SRTP 165 crypto_suites->push_back(CS_AES_CM_128_HMAC_SHA1_80); 166 #endif 167 } 168 169 // For video support only 80-bit SHA1 HMAC. For audio 32-bit HMAC is 170 // tolerated unless bundle is enabled because it is low overhead. Pick the 171 // crypto in the list that is supported. 172 static bool SelectCrypto(const MediaContentDescription* offer, 173 bool bundle, 174 CryptoParams *crypto) { 175 bool audio = offer->type() == MEDIA_TYPE_AUDIO; 176 const CryptoParamsVec& cryptos = offer->cryptos(); 177 178 for (CryptoParamsVec::const_iterator i = cryptos.begin(); 179 i != cryptos.end(); ++i) { 180 if (CS_AES_CM_128_HMAC_SHA1_80 == i->cipher_suite || 181 (CS_AES_CM_128_HMAC_SHA1_32 == i->cipher_suite && audio && !bundle)) { 182 return CreateCryptoParams(i->tag, i->cipher_suite, crypto); 183 } 184 } 185 return false; 186 } 187 188 static const StreamParams* FindFirstStreamParamsByCname( 189 const StreamParamsVec& params_vec, 190 const std::string& cname) { 191 for (StreamParamsVec::const_iterator it = params_vec.begin(); 192 it != params_vec.end(); ++it) { 193 if (cname == it->cname) 194 return &*it; 195 } 196 return NULL; 197 } 198 199 // Generates a new CNAME or the CNAME of an already existing StreamParams 200 // if a StreamParams exist for another Stream in streams with sync_label 201 // sync_label. 202 static bool GenerateCname(const StreamParamsVec& params_vec, 203 const MediaSessionOptions::Streams& streams, 204 const std::string& synch_label, 205 std::string* cname) { 206 ASSERT(cname != NULL); 207 if (!cname) 208 return false; 209 210 // Check if a CNAME exist for any of the other synched streams. 211 for (MediaSessionOptions::Streams::const_iterator stream_it = streams.begin(); 212 stream_it != streams.end() ; ++stream_it) { 213 if (synch_label != stream_it->sync_label) 214 continue; 215 216 StreamParams param; 217 // groupid is empty for StreamParams generated using 218 // MediaSessionDescriptionFactory. 219 if (GetStreamByIds(params_vec, "", stream_it->id, 220 ¶m)) { 221 *cname = param.cname; 222 return true; 223 } 224 } 225 // No other stream seems to exist that we should sync with. 226 // Generate a random string for the RTCP CNAME, as stated in RFC 6222. 227 // This string is only used for synchronization, and therefore is opaque. 228 do { 229 if (!talk_base::CreateRandomString(16, cname)) { 230 ASSERT(false); 231 return false; 232 } 233 } while (FindFirstStreamParamsByCname(params_vec, *cname)); 234 235 return true; 236 } 237 238 // Generate random SSRC values that are not already present in |params_vec|. 239 // Either 2 or 1 ssrcs will be generated based on |include_rtx_stream| being 240 // true or false. The generated values are added to |ssrcs|. 241 static void GenerateSsrcs(const StreamParamsVec& params_vec, 242 bool include_rtx_stream, 243 std::vector<uint32>* ssrcs) { 244 unsigned int num_ssrcs = include_rtx_stream ? 2 : 1; 245 for (unsigned int i = 0; i < num_ssrcs; i++) { 246 uint32 candidate; 247 do { 248 candidate = talk_base::CreateRandomNonZeroId(); 249 } while (GetStreamBySsrc(params_vec, candidate, NULL) || 250 std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0); 251 ssrcs->push_back(candidate); 252 } 253 } 254 255 // Returns false if we exhaust the range of SIDs. 256 static bool GenerateSctpSid(const StreamParamsVec& params_vec, 257 uint32* sid) { 258 if (params_vec.size() > kMaxSctpSid) { 259 LOG(LS_WARNING) << 260 "Could not generate an SCTP SID: too many SCTP streams."; 261 return false; 262 } 263 while (true) { 264 uint32 candidate = talk_base::CreateRandomNonZeroId() % kMaxSctpSid; 265 if (!GetStreamBySsrc(params_vec, candidate, NULL)) { 266 *sid = candidate; 267 return true; 268 } 269 } 270 } 271 272 static bool GenerateSctpSids(const StreamParamsVec& params_vec, 273 std::vector<uint32>* sids) { 274 uint32 sid; 275 if (!GenerateSctpSid(params_vec, &sid)) { 276 LOG(LS_WARNING) << "Could not generated an SCTP SID."; 277 return false; 278 } 279 sids->push_back(sid); 280 return true; 281 } 282 283 // Finds all StreamParams of all media types and attach them to stream_params. 284 static void GetCurrentStreamParams(const SessionDescription* sdesc, 285 StreamParamsVec* stream_params) { 286 if (!sdesc) 287 return; 288 289 const ContentInfos& contents = sdesc->contents(); 290 for (ContentInfos::const_iterator content = contents.begin(); 291 content != contents.end(); ++content) { 292 if (!IsMediaContent(&*content)) { 293 continue; 294 } 295 const MediaContentDescription* media = 296 static_cast<const MediaContentDescription*>( 297 content->description); 298 const StreamParamsVec& streams = media->streams(); 299 for (StreamParamsVec::const_iterator it = streams.begin(); 300 it != streams.end(); ++it) { 301 stream_params->push_back(*it); 302 } 303 } 304 } 305 306 template <typename IdStruct> 307 class UsedIds { 308 public: 309 UsedIds(int min_allowed_id, int max_allowed_id) 310 : min_allowed_id_(min_allowed_id), 311 max_allowed_id_(max_allowed_id), 312 next_id_(max_allowed_id) { 313 } 314 315 // Loops through all Id in |ids| and changes its id if it is 316 // already in use by another IdStruct. Call this methods with all Id 317 // in a session description to make sure no duplicate ids exists. 318 // Note that typename Id must be a type of IdStruct. 319 template <typename Id> 320 void FindAndSetIdUsed(std::vector<Id>* ids) { 321 for (typename std::vector<Id>::iterator it = ids->begin(); 322 it != ids->end(); ++it) { 323 FindAndSetIdUsed(&*it); 324 } 325 } 326 327 // Finds and sets an unused id if the |idstruct| id is already in use. 328 void FindAndSetIdUsed(IdStruct* idstruct) { 329 const int original_id = idstruct->id; 330 int new_id = idstruct->id; 331 332 if (original_id > max_allowed_id_ || original_id < min_allowed_id_) { 333 // If the original id is not in range - this is an id that can't be 334 // dynamically changed. 335 return; 336 } 337 338 if (IsIdUsed(original_id)) { 339 new_id = FindUnusedId(); 340 LOG(LS_WARNING) << "Duplicate id found. Reassigning from " << original_id 341 << " to " << new_id; 342 idstruct->id = new_id; 343 } 344 SetIdUsed(new_id); 345 } 346 347 private: 348 // Returns the first unused id in reverse order. 349 // This hopefully reduce the risk of more collisions. We want to change the 350 // default ids as little as possible. 351 int FindUnusedId() { 352 while (IsIdUsed(next_id_) && next_id_ >= min_allowed_id_) { 353 --next_id_; 354 } 355 ASSERT(next_id_ >= min_allowed_id_); 356 return next_id_; 357 } 358 359 bool IsIdUsed(int new_id) { 360 return id_set_.find(new_id) != id_set_.end(); 361 } 362 363 void SetIdUsed(int new_id) { 364 id_set_.insert(new_id); 365 } 366 367 const int min_allowed_id_; 368 const int max_allowed_id_; 369 int next_id_; 370 std::set<int> id_set_; 371 }; 372 373 // Helper class used for finding duplicate RTP payload types among audio, video 374 // and data codecs. When bundle is used the payload types may not collide. 375 class UsedPayloadTypes : public UsedIds<Codec> { 376 public: 377 UsedPayloadTypes() 378 : UsedIds<Codec>(kDynamicPayloadTypeMin, kDynamicPayloadTypeMax) { 379 } 380 381 382 private: 383 static const int kDynamicPayloadTypeMin = 96; 384 static const int kDynamicPayloadTypeMax = 127; 385 }; 386 387 // Helper class used for finding duplicate RTP Header extension ids among 388 // audio and video extensions. 389 class UsedRtpHeaderExtensionIds : public UsedIds<RtpHeaderExtension> { 390 public: 391 UsedRtpHeaderExtensionIds() 392 : UsedIds<RtpHeaderExtension>(kLocalIdMin, kLocalIdMax) { 393 } 394 395 private: 396 // Min and Max local identifier as specified by RFC5285. 397 static const int kLocalIdMin = 1; 398 static const int kLocalIdMax = 255; 399 }; 400 401 static bool IsSctp(const MediaContentDescription* desc) { 402 return ((desc->protocol() == kMediaProtocolSctp) || 403 (desc->protocol() == kMediaProtocolDtlsSctp)); 404 } 405 406 // Adds a StreamParams for each Stream in Streams with media type 407 // media_type to content_description. 408 // |current_params| - All currently known StreamParams of any media type. 409 template <class C> 410 static bool AddStreamParams( 411 MediaType media_type, 412 const MediaSessionOptions::Streams& streams, 413 StreamParamsVec* current_streams, 414 MediaContentDescriptionImpl<C>* content_description, 415 const bool add_legacy_stream) { 416 const bool include_rtx_stream = 417 ContainsRtxCodec(content_description->codecs()); 418 419 if (streams.empty() && add_legacy_stream) { 420 // TODO(perkj): Remove this legacy stream when all apps use StreamParams. 421 std::vector<uint32> ssrcs; 422 if (IsSctp(content_description)) { 423 GenerateSctpSids(*current_streams, &ssrcs); 424 } else { 425 GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs); 426 } 427 if (include_rtx_stream) { 428 content_description->AddLegacyStream(ssrcs[0], ssrcs[1]); 429 content_description->set_multistream(true); 430 } else { 431 content_description->AddLegacyStream(ssrcs[0]); 432 } 433 return true; 434 } 435 436 MediaSessionOptions::Streams::const_iterator stream_it; 437 for (stream_it = streams.begin(); 438 stream_it != streams.end(); ++stream_it) { 439 if (stream_it->type != media_type) 440 continue; // Wrong media type. 441 442 StreamParams param; 443 // groupid is empty for StreamParams generated using 444 // MediaSessionDescriptionFactory. 445 if (!GetStreamByIds(*current_streams, "", stream_it->id, 446 ¶m)) { 447 // This is a new stream. 448 // Get a CNAME. Either new or same as one of the other synched streams. 449 std::string cname; 450 if (!GenerateCname(*current_streams, streams, stream_it->sync_label, 451 &cname)) { 452 return false; 453 } 454 455 std::vector<uint32> ssrcs; 456 if (IsSctp(content_description)) { 457 GenerateSctpSids(*current_streams, &ssrcs); 458 } else { 459 GenerateSsrcs(*current_streams, include_rtx_stream, &ssrcs); 460 } 461 StreamParams stream_param; 462 stream_param.id = stream_it->id; 463 stream_param.ssrcs.push_back(ssrcs[0]); 464 if (include_rtx_stream) { 465 stream_param.AddFidSsrc(ssrcs[0], ssrcs[1]); 466 content_description->set_multistream(true); 467 } 468 stream_param.cname = cname; 469 stream_param.sync_label = stream_it->sync_label; 470 content_description->AddStream(stream_param); 471 472 // Store the new StreamParams in current_streams. 473 // This is necessary so that we can use the CNAME for other media types. 474 current_streams->push_back(stream_param); 475 } else { 476 content_description->AddStream(param); 477 } 478 } 479 return true; 480 } 481 482 // Updates the transport infos of the |sdesc| according to the given 483 // |bundle_group|. The transport infos of the content names within the 484 // |bundle_group| should be updated to use the ufrag and pwd of the first 485 // content within the |bundle_group|. 486 static bool UpdateTransportInfoForBundle(const ContentGroup& bundle_group, 487 SessionDescription* sdesc) { 488 // The bundle should not be empty. 489 if (!sdesc || !bundle_group.FirstContentName()) { 490 return false; 491 } 492 493 // We should definitely have a transport for the first content. 494 std::string selected_content_name = *bundle_group.FirstContentName(); 495 const TransportInfo* selected_transport_info = 496 sdesc->GetTransportInfoByName(selected_content_name); 497 if (!selected_transport_info) { 498 return false; 499 } 500 501 // Set the other contents to use the same ICE credentials. 502 const std::string selected_ufrag = 503 selected_transport_info->description.ice_ufrag; 504 const std::string selected_pwd = 505 selected_transport_info->description.ice_pwd; 506 for (TransportInfos::iterator it = 507 sdesc->transport_infos().begin(); 508 it != sdesc->transport_infos().end(); ++it) { 509 if (bundle_group.HasContentName(it->content_name) && 510 it->content_name != selected_content_name) { 511 it->description.ice_ufrag = selected_ufrag; 512 it->description.ice_pwd = selected_pwd; 513 } 514 } 515 return true; 516 } 517 518 // Gets the CryptoParamsVec of the given |content_name| from |sdesc|, and 519 // sets it to |cryptos|. 520 static bool GetCryptosByName(const SessionDescription* sdesc, 521 const std::string& content_name, 522 CryptoParamsVec* cryptos) { 523 if (!sdesc || !cryptos) { 524 return false; 525 } 526 527 const ContentInfo* content = sdesc->GetContentByName(content_name); 528 if (!IsMediaContent(content) || !content->description) { 529 return false; 530 } 531 532 const MediaContentDescription* media_desc = 533 static_cast<const MediaContentDescription*>(content->description); 534 *cryptos = media_desc->cryptos(); 535 return true; 536 } 537 538 // Predicate function used by the remove_if. 539 // Returns true if the |crypto|'s cipher_suite is not found in |filter|. 540 static bool CryptoNotFound(const CryptoParams crypto, 541 const CryptoParamsVec* filter) { 542 if (filter == NULL) { 543 return true; 544 } 545 for (CryptoParamsVec::const_iterator it = filter->begin(); 546 it != filter->end(); ++it) { 547 if (it->cipher_suite == crypto.cipher_suite) { 548 return false; 549 } 550 } 551 return true; 552 } 553 554 // Prunes the |target_cryptos| by removing the crypto params (cipher_suite) 555 // which are not available in |filter|. 556 static void PruneCryptos(const CryptoParamsVec& filter, 557 CryptoParamsVec* target_cryptos) { 558 if (!target_cryptos) { 559 return; 560 } 561 target_cryptos->erase(std::remove_if(target_cryptos->begin(), 562 target_cryptos->end(), 563 bind2nd(ptr_fun(CryptoNotFound), 564 &filter)), 565 target_cryptos->end()); 566 } 567 568 static bool IsRtpContent(SessionDescription* sdesc, 569 const std::string& content_name) { 570 bool is_rtp = false; 571 ContentInfo* content = sdesc->GetContentByName(content_name); 572 if (IsMediaContent(content)) { 573 MediaContentDescription* media_desc = 574 static_cast<MediaContentDescription*>(content->description); 575 if (!media_desc) { 576 return false; 577 } 578 is_rtp = media_desc->protocol().empty() || 579 talk_base::starts_with(media_desc->protocol().data(), 580 kMediaProtocolRtpPrefix); 581 } 582 return is_rtp; 583 } 584 585 // Updates the crypto parameters of the |sdesc| according to the given 586 // |bundle_group|. The crypto parameters of all the contents within the 587 // |bundle_group| should be updated to use the common subset of the 588 // available cryptos. 589 static bool UpdateCryptoParamsForBundle(const ContentGroup& bundle_group, 590 SessionDescription* sdesc) { 591 // The bundle should not be empty. 592 if (!sdesc || !bundle_group.FirstContentName()) { 593 return false; 594 } 595 596 // Get the common cryptos. 597 const ContentNames& content_names = bundle_group.content_names(); 598 CryptoParamsVec common_cryptos; 599 for (ContentNames::const_iterator it = content_names.begin(); 600 it != content_names.end(); ++it) { 601 if (!IsRtpContent(sdesc, *it)) { 602 continue; 603 } 604 if (it == content_names.begin()) { 605 // Initial the common_cryptos with the first content in the bundle group. 606 if (!GetCryptosByName(sdesc, *it, &common_cryptos)) { 607 return false; 608 } 609 if (common_cryptos.empty()) { 610 // If there's no crypto params, we should just return. 611 return true; 612 } 613 } else { 614 CryptoParamsVec cryptos; 615 if (!GetCryptosByName(sdesc, *it, &cryptos)) { 616 return false; 617 } 618 PruneCryptos(cryptos, &common_cryptos); 619 } 620 } 621 622 if (common_cryptos.empty()) { 623 return false; 624 } 625 626 // Update to use the common cryptos. 627 for (ContentNames::const_iterator it = content_names.begin(); 628 it != content_names.end(); ++it) { 629 if (!IsRtpContent(sdesc, *it)) { 630 continue; 631 } 632 ContentInfo* content = sdesc->GetContentByName(*it); 633 if (IsMediaContent(content)) { 634 MediaContentDescription* media_desc = 635 static_cast<MediaContentDescription*>(content->description); 636 if (!media_desc) { 637 return false; 638 } 639 media_desc->set_cryptos(common_cryptos); 640 } 641 } 642 return true; 643 } 644 645 template <class C> 646 static bool ContainsRtxCodec(const std::vector<C>& codecs) { 647 typename std::vector<C>::const_iterator it; 648 for (it = codecs.begin(); it != codecs.end(); ++it) { 649 if (IsRtxCodec(*it)) { 650 return true; 651 } 652 } 653 return false; 654 } 655 656 template <class C> 657 static bool IsRtxCodec(const C& codec) { 658 return stricmp(codec.name.c_str(), kRtxCodecName) == 0; 659 } 660 661 // Create a media content to be offered in a session-initiate, 662 // according to the given options.rtcp_mux, options.is_muc, 663 // options.streams, codecs, secure_transport, crypto, and streams. If we don't 664 // currently have crypto (in current_cryptos) and it is enabled (in 665 // secure_policy), crypto is created (according to crypto_suites). If 666 // add_legacy_stream is true, and current_streams is empty, a legacy 667 // stream is created. The created content is added to the offer. 668 template <class C> 669 static bool CreateMediaContentOffer( 670 const MediaSessionOptions& options, 671 const std::vector<C>& codecs, 672 const SecureMediaPolicy& secure_policy, 673 const CryptoParamsVec* current_cryptos, 674 const std::vector<std::string>& crypto_suites, 675 const RtpHeaderExtensions& rtp_extensions, 676 bool add_legacy_stream, 677 StreamParamsVec* current_streams, 678 MediaContentDescriptionImpl<C>* offer) { 679 offer->AddCodecs(codecs); 680 offer->SortCodecs(); 681 682 offer->set_crypto_required(secure_policy == SEC_REQUIRED); 683 offer->set_rtcp_mux(options.rtcp_mux_enabled); 684 offer->set_multistream(options.is_muc); 685 offer->set_rtp_header_extensions(rtp_extensions); 686 687 if (!AddStreamParams( 688 offer->type(), options.streams, current_streams, 689 offer, add_legacy_stream)) { 690 return false; 691 } 692 693 #ifdef HAVE_SRTP 694 if (secure_policy != SEC_DISABLED) { 695 if (current_cryptos) { 696 AddMediaCryptos(*current_cryptos, offer); 697 } 698 if (offer->cryptos().empty()) { 699 if (!CreateMediaCryptos(crypto_suites, offer)) { 700 return false; 701 } 702 } 703 } 704 #endif 705 706 if (offer->crypto_required() && offer->cryptos().empty()) { 707 return false; 708 } 709 return true; 710 } 711 712 template <class C> 713 static void NegotiateCodecs(const std::vector<C>& local_codecs, 714 const std::vector<C>& offered_codecs, 715 std::vector<C>* negotiated_codecs) { 716 typename std::vector<C>::const_iterator ours; 717 for (ours = local_codecs.begin(); 718 ours != local_codecs.end(); ++ours) { 719 typename std::vector<C>::const_iterator theirs; 720 for (theirs = offered_codecs.begin(); 721 theirs != offered_codecs.end(); ++theirs) { 722 if (ours->Matches(*theirs)) { 723 C negotiated = *ours; 724 negotiated.IntersectFeedbackParams(*theirs); 725 if (IsRtxCodec(negotiated)) { 726 // Only negotiate RTX if kCodecParamAssociatedPayloadType has been 727 // set. 728 std::string apt_value; 729 if (!theirs->GetParam(kCodecParamAssociatedPayloadType, &apt_value)) { 730 LOG(LS_WARNING) << "RTX missing associated payload type."; 731 continue; 732 } 733 negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_value); 734 } 735 negotiated.id = theirs->id; 736 negotiated_codecs->push_back(negotiated); 737 } 738 } 739 } 740 } 741 742 template <class C> 743 static bool FindMatchingCodec(const std::vector<C>& codecs, 744 const C& codec_to_match, 745 C* found_codec) { 746 for (typename std::vector<C>::const_iterator it = codecs.begin(); 747 it != codecs.end(); ++it) { 748 if (it->Matches(codec_to_match)) { 749 if (found_codec != NULL) { 750 *found_codec= *it; 751 } 752 return true; 753 } 754 } 755 return false; 756 } 757 758 // Adds all codecs from |reference_codecs| to |offered_codecs| that dont' 759 // already exist in |offered_codecs| and ensure the payload types don't 760 // collide. 761 template <class C> 762 static void FindCodecsToOffer( 763 const std::vector<C>& reference_codecs, 764 std::vector<C>* offered_codecs, 765 UsedPayloadTypes* used_pltypes) { 766 767 typedef std::map<int, C> RtxCodecReferences; 768 RtxCodecReferences new_rtx_codecs; 769 770 // Find all new RTX codecs. 771 for (typename std::vector<C>::const_iterator it = reference_codecs.begin(); 772 it != reference_codecs.end(); ++it) { 773 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && IsRtxCodec(*it)) { 774 C rtx_codec = *it; 775 int referenced_pl_type = 776 talk_base::FromString<int>(0, 777 rtx_codec.params[kCodecParamAssociatedPayloadType]); 778 new_rtx_codecs.insert(std::pair<int, C>(referenced_pl_type, 779 rtx_codec)); 780 } 781 } 782 783 // Add all new codecs that are not RTX codecs. 784 for (typename std::vector<C>::const_iterator it = reference_codecs.begin(); 785 it != reference_codecs.end(); ++it) { 786 if (!FindMatchingCodec<C>(*offered_codecs, *it, NULL) && !IsRtxCodec(*it)) { 787 C codec = *it; 788 int original_payload_id = codec.id; 789 used_pltypes->FindAndSetIdUsed(&codec); 790 offered_codecs->push_back(codec); 791 792 // If this codec is referenced by a new RTX codec, update the reference 793 // in the RTX codec with the new payload type. 794 typename RtxCodecReferences::iterator rtx_it = 795 new_rtx_codecs.find(original_payload_id); 796 if (rtx_it != new_rtx_codecs.end()) { 797 C& rtx_codec = rtx_it->second; 798 rtx_codec.params[kCodecParamAssociatedPayloadType] = 799 talk_base::ToString(codec.id); 800 } 801 } 802 } 803 804 // Add all new RTX codecs. 805 for (typename RtxCodecReferences::iterator it = new_rtx_codecs.begin(); 806 it != new_rtx_codecs.end(); ++it) { 807 C& rtx_codec = it->second; 808 used_pltypes->FindAndSetIdUsed(&rtx_codec); 809 offered_codecs->push_back(rtx_codec); 810 } 811 } 812 813 814 static bool FindByUri(const RtpHeaderExtensions& extensions, 815 const RtpHeaderExtension& ext_to_match, 816 RtpHeaderExtension* found_extension) { 817 for (RtpHeaderExtensions::const_iterator it = extensions.begin(); 818 it != extensions.end(); ++it) { 819 // We assume that all URIs are given in a canonical format. 820 if (it->uri == ext_to_match.uri) { 821 if (found_extension != NULL) { 822 *found_extension= *it; 823 } 824 return true; 825 } 826 } 827 return false; 828 } 829 830 static void FindAndSetRtpHdrExtUsed( 831 const RtpHeaderExtensions& reference_extensions, 832 RtpHeaderExtensions* offered_extensions, 833 UsedRtpHeaderExtensionIds* used_extensions) { 834 for (RtpHeaderExtensions::const_iterator it = reference_extensions.begin(); 835 it != reference_extensions.end(); ++it) { 836 if (!FindByUri(*offered_extensions, *it, NULL)) { 837 RtpHeaderExtension ext = *it; 838 used_extensions->FindAndSetIdUsed(&ext); 839 offered_extensions->push_back(ext); 840 } 841 } 842 } 843 844 static void NegotiateRtpHeaderExtensions( 845 const RtpHeaderExtensions& local_extensions, 846 const RtpHeaderExtensions& offered_extensions, 847 RtpHeaderExtensions* negotiated_extenstions) { 848 RtpHeaderExtensions::const_iterator ours; 849 for (ours = local_extensions.begin(); 850 ours != local_extensions.end(); ++ours) { 851 RtpHeaderExtension theirs; 852 if (FindByUri(offered_extensions, *ours, &theirs)) { 853 // We respond with their RTP header extension id. 854 negotiated_extenstions->push_back(theirs); 855 } 856 } 857 } 858 859 static void StripCNCodecs(AudioCodecs* audio_codecs) { 860 AudioCodecs::iterator iter = audio_codecs->begin(); 861 while (iter != audio_codecs->end()) { 862 if (stricmp(iter->name.c_str(), kComfortNoiseCodecName) == 0) { 863 iter = audio_codecs->erase(iter); 864 } else { 865 ++iter; 866 } 867 } 868 } 869 870 // Create a media content to be answered in a session-accept, 871 // according to the given options.rtcp_mux, options.streams, codecs, 872 // crypto, and streams. If we don't currently have crypto (in 873 // current_cryptos) and it is enabled (in secure_policy), crypto is 874 // created (according to crypto_suites). If add_legacy_stream is 875 // true, and current_streams is empty, a legacy stream is created. 876 // The codecs, rtcp_mux, and crypto are all negotiated with the offer 877 // from the incoming session-initiate. If the negotiation fails, this 878 // method returns false. The created content is added to the offer. 879 template <class C> 880 static bool CreateMediaContentAnswer( 881 const MediaContentDescriptionImpl<C>* offer, 882 const MediaSessionOptions& options, 883 const std::vector<C>& local_codecs, 884 const SecureMediaPolicy& sdes_policy, 885 const CryptoParamsVec* current_cryptos, 886 const RtpHeaderExtensions& local_rtp_extenstions, 887 StreamParamsVec* current_streams, 888 bool add_legacy_stream, 889 bool bundle_enabled, 890 MediaContentDescriptionImpl<C>* answer) { 891 std::vector<C> negotiated_codecs; 892 NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs); 893 answer->AddCodecs(negotiated_codecs); 894 answer->SortCodecs(); 895 answer->set_protocol(offer->protocol()); 896 RtpHeaderExtensions negotiated_rtp_extensions; 897 NegotiateRtpHeaderExtensions(local_rtp_extenstions, 898 offer->rtp_header_extensions(), 899 &negotiated_rtp_extensions); 900 answer->set_rtp_header_extensions(negotiated_rtp_extensions); 901 902 answer->set_rtcp_mux(options.rtcp_mux_enabled && offer->rtcp_mux()); 903 904 if (sdes_policy != SEC_DISABLED) { 905 CryptoParams crypto; 906 if (SelectCrypto(offer, bundle_enabled, &crypto)) { 907 if (current_cryptos) { 908 FindMatchingCrypto(*current_cryptos, crypto, &crypto); 909 } 910 answer->AddCrypto(crypto); 911 } 912 } 913 914 if (answer->cryptos().empty() && 915 (offer->crypto_required() || sdes_policy == SEC_REQUIRED)) { 916 return false; 917 } 918 919 if (!AddStreamParams( 920 answer->type(), options.streams, current_streams, 921 answer, add_legacy_stream)) { 922 return false; // Something went seriously wrong. 923 } 924 925 // Make sure the answer media content direction is per default set as 926 // described in RFC3264 section 6.1. 927 switch (offer->direction()) { 928 case MD_INACTIVE: 929 answer->set_direction(MD_INACTIVE); 930 break; 931 case MD_SENDONLY: 932 answer->set_direction(MD_RECVONLY); 933 break; 934 case MD_RECVONLY: 935 answer->set_direction(MD_SENDONLY); 936 break; 937 case MD_SENDRECV: 938 answer->set_direction(MD_SENDRECV); 939 break; 940 default: 941 break; 942 } 943 944 return true; 945 } 946 947 static bool IsMediaProtocolSupported(MediaType type, 948 const std::string& protocol) { 949 // Data channels can have a protocol of SCTP or SCTP/DTLS. 950 if (type == MEDIA_TYPE_DATA && 951 (protocol == kMediaProtocolSctp || 952 protocol == kMediaProtocolDtlsSctp)) { 953 return true; 954 } 955 // Since not all applications serialize and deserialize the media protocol, 956 // we will have to accept |protocol| to be empty. 957 return protocol == kMediaProtocolAvpf || protocol == kMediaProtocolSavpf || 958 protocol.empty(); 959 } 960 961 static void SetMediaProtocol(bool secure_transport, 962 MediaContentDescription* desc) { 963 if (!desc->cryptos().empty() || secure_transport) 964 desc->set_protocol(kMediaProtocolSavpf); 965 else 966 desc->set_protocol(kMediaProtocolAvpf); 967 } 968 969 void MediaSessionOptions::AddStream(MediaType type, 970 const std::string& id, 971 const std::string& sync_label) { 972 streams.push_back(Stream(type, id, sync_label)); 973 974 if (type == MEDIA_TYPE_VIDEO) 975 has_video = true; 976 else if (type == MEDIA_TYPE_AUDIO) 977 has_audio = true; 978 // If we haven't already set the data_channel_type, and we add a 979 // stream, we assume it's an RTP data stream. 980 else if (type == MEDIA_TYPE_DATA && data_channel_type == DCT_NONE) 981 data_channel_type = DCT_RTP; 982 } 983 984 void MediaSessionOptions::RemoveStream(MediaType type, 985 const std::string& id) { 986 Streams::iterator stream_it = streams.begin(); 987 for (; stream_it != streams.end(); ++stream_it) { 988 if (stream_it->type == type && stream_it->id == id) { 989 streams.erase(stream_it); 990 return; 991 } 992 } 993 ASSERT(false); 994 } 995 996 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( 997 const TransportDescriptionFactory* transport_desc_factory) 998 : secure_(SEC_DISABLED), 999 add_legacy_(true), 1000 transport_desc_factory_(transport_desc_factory) { 1001 } 1002 1003 MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( 1004 ChannelManager* channel_manager, 1005 const TransportDescriptionFactory* transport_desc_factory) 1006 : secure_(SEC_DISABLED), 1007 add_legacy_(true), 1008 transport_desc_factory_(transport_desc_factory) { 1009 channel_manager->GetSupportedAudioCodecs(&audio_codecs_); 1010 channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_); 1011 channel_manager->GetSupportedVideoCodecs(&video_codecs_); 1012 channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_); 1013 channel_manager->GetSupportedDataCodecs(&data_codecs_); 1014 } 1015 1016 SessionDescription* MediaSessionDescriptionFactory::CreateOffer( 1017 const MediaSessionOptions& options, 1018 const SessionDescription* current_description) const { 1019 bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED); 1020 1021 scoped_ptr<SessionDescription> offer(new SessionDescription()); 1022 1023 StreamParamsVec current_streams; 1024 GetCurrentStreamParams(current_description, ¤t_streams); 1025 1026 AudioCodecs audio_codecs; 1027 VideoCodecs video_codecs; 1028 DataCodecs data_codecs; 1029 GetCodecsToOffer(current_description, &audio_codecs, &video_codecs, 1030 &data_codecs); 1031 1032 if (!options.vad_enabled) { 1033 // If application doesn't want CN codecs in offer. 1034 StripCNCodecs(&audio_codecs); 1035 } 1036 1037 RtpHeaderExtensions audio_rtp_extensions; 1038 RtpHeaderExtensions video_rtp_extensions; 1039 GetRtpHdrExtsToOffer(current_description, &audio_rtp_extensions, 1040 &video_rtp_extensions); 1041 1042 // Handle m=audio. 1043 if (options.has_audio) { 1044 scoped_ptr<AudioContentDescription> audio(new AudioContentDescription()); 1045 std::vector<std::string> crypto_suites; 1046 GetSupportedAudioCryptoSuites(&crypto_suites); 1047 if (!CreateMediaContentOffer( 1048 options, 1049 audio_codecs, 1050 secure(), 1051 GetCryptos(GetFirstAudioContentDescription(current_description)), 1052 crypto_suites, 1053 audio_rtp_extensions, 1054 add_legacy_, 1055 ¤t_streams, 1056 audio.get())) { 1057 return NULL; 1058 } 1059 1060 audio->set_lang(lang_); 1061 SetMediaProtocol(secure_transport, audio.get()); 1062 offer->AddContent(CN_AUDIO, NS_JINGLE_RTP, audio.release()); 1063 if (!AddTransportOffer(CN_AUDIO, options.transport_options, 1064 current_description, offer.get())) { 1065 return NULL; 1066 } 1067 } 1068 1069 // Handle m=video. 1070 if (options.has_video) { 1071 scoped_ptr<VideoContentDescription> video(new VideoContentDescription()); 1072 std::vector<std::string> crypto_suites; 1073 GetSupportedVideoCryptoSuites(&crypto_suites); 1074 if (!CreateMediaContentOffer( 1075 options, 1076 video_codecs, 1077 secure(), 1078 GetCryptos(GetFirstVideoContentDescription(current_description)), 1079 crypto_suites, 1080 video_rtp_extensions, 1081 add_legacy_, 1082 ¤t_streams, 1083 video.get())) { 1084 return NULL; 1085 } 1086 1087 video->set_bandwidth(options.video_bandwidth); 1088 SetMediaProtocol(secure_transport, video.get()); 1089 offer->AddContent(CN_VIDEO, NS_JINGLE_RTP, video.release()); 1090 if (!AddTransportOffer(CN_VIDEO, options.transport_options, 1091 current_description, offer.get())) { 1092 return NULL; 1093 } 1094 } 1095 1096 // Handle m=data. 1097 if (options.has_data()) { 1098 scoped_ptr<DataContentDescription> data(new DataContentDescription()); 1099 bool is_sctp = (options.data_channel_type == DCT_SCTP); 1100 1101 std::vector<std::string> crypto_suites; 1102 cricket::SecurePolicy sdes_policy = secure(); 1103 if (is_sctp) { 1104 // SDES doesn't make sense for SCTP, so we disable it, and we only 1105 // get SDES crypto suites for RTP-based data channels. 1106 sdes_policy = cricket::SEC_DISABLED; 1107 // Unlike SetMediaProtocol below, we need to set the protocol 1108 // before we call CreateMediaContentOffer. Otherwise, 1109 // CreateMediaContentOffer won't know this is SCTP and will 1110 // generate SSRCs rather than SIDs. 1111 data->set_protocol( 1112 secure_transport ? kMediaProtocolDtlsSctp : kMediaProtocolSctp); 1113 } else { 1114 GetSupportedDataCryptoSuites(&crypto_suites); 1115 } 1116 1117 if (!CreateMediaContentOffer( 1118 options, 1119 data_codecs, 1120 sdes_policy, 1121 GetCryptos(GetFirstDataContentDescription(current_description)), 1122 crypto_suites, 1123 RtpHeaderExtensions(), 1124 add_legacy_, 1125 ¤t_streams, 1126 data.get())) { 1127 return NULL; 1128 } 1129 1130 if (is_sctp) { 1131 offer->AddContent(CN_DATA, NS_JINGLE_DRAFT_SCTP, data.release()); 1132 } else { 1133 data->set_bandwidth(options.data_bandwidth); 1134 SetMediaProtocol(secure_transport, data.get()); 1135 offer->AddContent(CN_DATA, NS_JINGLE_RTP, data.release()); 1136 } 1137 if (!AddTransportOffer(CN_DATA, options.transport_options, 1138 current_description, offer.get())) { 1139 return NULL; 1140 } 1141 } 1142 1143 // Bundle the contents together, if we've been asked to do so, and update any 1144 // parameters that need to be tweaked for BUNDLE. 1145 if (options.bundle_enabled) { 1146 ContentGroup offer_bundle(GROUP_TYPE_BUNDLE); 1147 for (ContentInfos::const_iterator content = offer->contents().begin(); 1148 content != offer->contents().end(); ++content) { 1149 offer_bundle.AddContentName(content->name); 1150 } 1151 offer->AddGroup(offer_bundle); 1152 if (!UpdateTransportInfoForBundle(offer_bundle, offer.get())) { 1153 LOG(LS_ERROR) << "CreateOffer failed to UpdateTransportInfoForBundle."; 1154 return NULL; 1155 } 1156 if (!UpdateCryptoParamsForBundle(offer_bundle, offer.get())) { 1157 LOG(LS_ERROR) << "CreateOffer failed to UpdateCryptoParamsForBundle."; 1158 return NULL; 1159 } 1160 } 1161 1162 return offer.release(); 1163 } 1164 1165 SessionDescription* MediaSessionDescriptionFactory::CreateAnswer( 1166 const SessionDescription* offer, const MediaSessionOptions& options, 1167 const SessionDescription* current_description) const { 1168 // The answer contains the intersection of the codecs in the offer with the 1169 // codecs we support, ordered by our local preference. As indicated by 1170 // XEP-0167, we retain the same payload ids from the offer in the answer. 1171 scoped_ptr<SessionDescription> answer(new SessionDescription()); 1172 1173 StreamParamsVec current_streams; 1174 GetCurrentStreamParams(current_description, ¤t_streams); 1175 1176 bool bundle_enabled = 1177 offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled; 1178 1179 // Handle m=audio. 1180 const ContentInfo* audio_content = GetFirstAudioContent(offer); 1181 if (audio_content) { 1182 scoped_ptr<TransportDescription> audio_transport( 1183 CreateTransportAnswer(audio_content->name, offer, 1184 options.transport_options, 1185 current_description)); 1186 if (!audio_transport) { 1187 return NULL; 1188 } 1189 1190 AudioCodecs audio_codecs = audio_codecs_; 1191 if (!options.vad_enabled) { 1192 StripCNCodecs(&audio_codecs); 1193 } 1194 1195 scoped_ptr<AudioContentDescription> audio_answer( 1196 new AudioContentDescription()); 1197 // Do not require or create SDES cryptos if DTLS is used. 1198 cricket::SecurePolicy sdes_policy = 1199 audio_transport->secure() ? cricket::SEC_DISABLED : secure(); 1200 if (!CreateMediaContentAnswer( 1201 static_cast<const AudioContentDescription*>( 1202 audio_content->description), 1203 options, 1204 audio_codecs, 1205 sdes_policy, 1206 GetCryptos(GetFirstAudioContentDescription(current_description)), 1207 audio_rtp_extensions_, 1208 ¤t_streams, 1209 add_legacy_, 1210 bundle_enabled, 1211 audio_answer.get())) { 1212 return NULL; // Fails the session setup. 1213 } 1214 1215 bool rejected = !options.has_audio || audio_content->rejected || 1216 !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, 1217 audio_answer->protocol()); 1218 if (!rejected) { 1219 AddTransportAnswer(audio_content->name, *(audio_transport.get()), 1220 answer.get()); 1221 } else { 1222 // RFC 3264 1223 // The answer MUST contain the same number of m-lines as the offer. 1224 LOG(LS_INFO) << "Audio is not supported in the answer."; 1225 } 1226 1227 answer->AddContent(audio_content->name, audio_content->type, rejected, 1228 audio_answer.release()); 1229 } else { 1230 LOG(LS_INFO) << "Audio is not available in the offer."; 1231 } 1232 1233 // Handle m=video. 1234 const ContentInfo* video_content = GetFirstVideoContent(offer); 1235 if (video_content) { 1236 scoped_ptr<TransportDescription> video_transport( 1237 CreateTransportAnswer(video_content->name, offer, 1238 options.transport_options, 1239 current_description)); 1240 if (!video_transport) { 1241 return NULL; 1242 } 1243 1244 scoped_ptr<VideoContentDescription> video_answer( 1245 new VideoContentDescription()); 1246 // Do not require or create SDES cryptos if DTLS is used. 1247 cricket::SecurePolicy sdes_policy = 1248 video_transport->secure() ? cricket::SEC_DISABLED : secure(); 1249 if (!CreateMediaContentAnswer( 1250 static_cast<const VideoContentDescription*>( 1251 video_content->description), 1252 options, 1253 video_codecs_, 1254 sdes_policy, 1255 GetCryptos(GetFirstVideoContentDescription(current_description)), 1256 video_rtp_extensions_, 1257 ¤t_streams, 1258 add_legacy_, 1259 bundle_enabled, 1260 video_answer.get())) { 1261 return NULL; 1262 } 1263 bool rejected = !options.has_video || video_content->rejected || 1264 !IsMediaProtocolSupported(MEDIA_TYPE_VIDEO, video_answer->protocol()); 1265 if (!rejected) { 1266 if (!AddTransportAnswer(video_content->name, *(video_transport.get()), 1267 answer.get())) { 1268 return NULL; 1269 } 1270 video_answer->set_bandwidth(options.video_bandwidth); 1271 } else { 1272 // RFC 3264 1273 // The answer MUST contain the same number of m-lines as the offer. 1274 LOG(LS_INFO) << "Video is not supported in the answer."; 1275 } 1276 answer->AddContent(video_content->name, video_content->type, rejected, 1277 video_answer.release()); 1278 } else { 1279 LOG(LS_INFO) << "Video is not available in the offer."; 1280 } 1281 1282 // Handle m=data. 1283 const ContentInfo* data_content = GetFirstDataContent(offer); 1284 if (data_content) { 1285 scoped_ptr<TransportDescription> data_transport( 1286 CreateTransportAnswer(data_content->name, offer, 1287 options.transport_options, 1288 current_description)); 1289 if (!data_transport) { 1290 return NULL; 1291 } 1292 scoped_ptr<DataContentDescription> data_answer( 1293 new DataContentDescription()); 1294 // Do not require or create SDES cryptos if DTLS is used. 1295 cricket::SecurePolicy sdes_policy = 1296 data_transport->secure() ? cricket::SEC_DISABLED : secure(); 1297 if (!CreateMediaContentAnswer( 1298 static_cast<const DataContentDescription*>( 1299 data_content->description), 1300 options, 1301 data_codecs_, 1302 sdes_policy, 1303 GetCryptos(GetFirstDataContentDescription(current_description)), 1304 RtpHeaderExtensions(), 1305 ¤t_streams, 1306 add_legacy_, 1307 bundle_enabled, 1308 data_answer.get())) { 1309 return NULL; // Fails the session setup. 1310 } 1311 1312 bool rejected = !options.has_data() || data_content->rejected || 1313 !IsMediaProtocolSupported(MEDIA_TYPE_DATA, data_answer->protocol()); 1314 if (!rejected) { 1315 data_answer->set_bandwidth(options.data_bandwidth); 1316 if (!AddTransportAnswer(data_content->name, *(data_transport.get()), 1317 answer.get())) { 1318 return NULL; 1319 } 1320 } else { 1321 // RFC 3264 1322 // The answer MUST contain the same number of m-lines as the offer. 1323 LOG(LS_INFO) << "Data is not supported in the answer."; 1324 } 1325 answer->AddContent(data_content->name, data_content->type, rejected, 1326 data_answer.release()); 1327 } else { 1328 LOG(LS_INFO) << "Data is not available in the offer."; 1329 } 1330 1331 // If the offer supports BUNDLE, and we want to use it too, create a BUNDLE 1332 // group in the answer with the appropriate content names. 1333 if (offer->HasGroup(GROUP_TYPE_BUNDLE) && options.bundle_enabled) { 1334 const ContentGroup* offer_bundle = offer->GetGroupByName(GROUP_TYPE_BUNDLE); 1335 ContentGroup answer_bundle(GROUP_TYPE_BUNDLE); 1336 for (ContentInfos::const_iterator content = answer->contents().begin(); 1337 content != answer->contents().end(); ++content) { 1338 if (!content->rejected && offer_bundle->HasContentName(content->name)) { 1339 answer_bundle.AddContentName(content->name); 1340 } 1341 } 1342 if (answer_bundle.FirstContentName()) { 1343 answer->AddGroup(answer_bundle); 1344 1345 // Share the same ICE credentials and crypto params across all contents, 1346 // as BUNDLE requires. 1347 if (!UpdateTransportInfoForBundle(answer_bundle, answer.get())) { 1348 LOG(LS_ERROR) << "CreateAnswer failed to UpdateTransportInfoForBundle."; 1349 return NULL; 1350 } 1351 1352 if (!UpdateCryptoParamsForBundle(answer_bundle, answer.get())) { 1353 LOG(LS_ERROR) << "CreateAnswer failed to UpdateCryptoParamsForBundle."; 1354 return NULL; 1355 } 1356 } 1357 } 1358 1359 return answer.release(); 1360 } 1361 1362 // Gets the TransportInfo of the given |content_name| from the 1363 // |current_description|. If doesn't exist, returns a new one. 1364 static const TransportDescription* GetTransportDescription( 1365 const std::string& content_name, 1366 const SessionDescription* current_description) { 1367 const TransportDescription* desc = NULL; 1368 if (current_description) { 1369 const TransportInfo* info = 1370 current_description->GetTransportInfoByName(content_name); 1371 if (info) { 1372 desc = &info->description; 1373 } 1374 } 1375 return desc; 1376 } 1377 1378 void MediaSessionDescriptionFactory::GetCodecsToOffer( 1379 const SessionDescription* current_description, 1380 AudioCodecs* audio_codecs, 1381 VideoCodecs* video_codecs, 1382 DataCodecs* data_codecs) const { 1383 UsedPayloadTypes used_pltypes; 1384 audio_codecs->clear(); 1385 video_codecs->clear(); 1386 data_codecs->clear(); 1387 1388 1389 // First - get all codecs from the current description if the media type 1390 // is used. 1391 // Add them to |used_pltypes| so the payloadtype is not reused if a new media 1392 // type is added. 1393 if (current_description) { 1394 const AudioContentDescription* audio = 1395 GetFirstAudioContentDescription(current_description); 1396 if (audio) { 1397 *audio_codecs = audio->codecs(); 1398 used_pltypes.FindAndSetIdUsed<AudioCodec>(audio_codecs); 1399 } 1400 const VideoContentDescription* video = 1401 GetFirstVideoContentDescription(current_description); 1402 if (video) { 1403 *video_codecs = video->codecs(); 1404 used_pltypes.FindAndSetIdUsed<VideoCodec>(video_codecs); 1405 } 1406 const DataContentDescription* data = 1407 GetFirstDataContentDescription(current_description); 1408 if (data) { 1409 *data_codecs = data->codecs(); 1410 used_pltypes.FindAndSetIdUsed<DataCodec>(data_codecs); 1411 } 1412 } 1413 1414 // Add our codecs that are not in |current_description|. 1415 FindCodecsToOffer<AudioCodec>(audio_codecs_, audio_codecs, &used_pltypes); 1416 FindCodecsToOffer<VideoCodec>(video_codecs_, video_codecs, &used_pltypes); 1417 FindCodecsToOffer<DataCodec>(data_codecs_, data_codecs, &used_pltypes); 1418 } 1419 1420 void MediaSessionDescriptionFactory::GetRtpHdrExtsToOffer( 1421 const SessionDescription* current_description, 1422 RtpHeaderExtensions* audio_extensions, 1423 RtpHeaderExtensions* video_extensions) const { 1424 UsedRtpHeaderExtensionIds used_ids; 1425 audio_extensions->clear(); 1426 video_extensions->clear(); 1427 1428 // First - get all extensions from the current description if the media type 1429 // is used. 1430 // Add them to |used_ids| so the local ids are not reused if a new media 1431 // type is added. 1432 if (current_description) { 1433 const AudioContentDescription* audio = 1434 GetFirstAudioContentDescription(current_description); 1435 if (audio) { 1436 *audio_extensions = audio->rtp_header_extensions(); 1437 used_ids.FindAndSetIdUsed(audio_extensions); 1438 } 1439 const VideoContentDescription* video = 1440 GetFirstVideoContentDescription(current_description); 1441 if (video) { 1442 *video_extensions = video->rtp_header_extensions(); 1443 used_ids.FindAndSetIdUsed(video_extensions); 1444 } 1445 } 1446 1447 // Add our default RTP header extensions that are not in 1448 // |current_description|. 1449 FindAndSetRtpHdrExtUsed(audio_rtp_header_extensions(), audio_extensions, 1450 &used_ids); 1451 FindAndSetRtpHdrExtUsed(video_rtp_header_extensions(), video_extensions, 1452 &used_ids); 1453 } 1454 1455 bool MediaSessionDescriptionFactory::AddTransportOffer( 1456 const std::string& content_name, 1457 const TransportOptions& transport_options, 1458 const SessionDescription* current_desc, 1459 SessionDescription* offer_desc) const { 1460 if (!transport_desc_factory_) 1461 return false; 1462 const TransportDescription* current_tdesc = 1463 GetTransportDescription(content_name, current_desc); 1464 talk_base::scoped_ptr<TransportDescription> new_tdesc( 1465 transport_desc_factory_->CreateOffer(transport_options, current_tdesc)); 1466 bool ret = (new_tdesc.get() != NULL && 1467 offer_desc->AddTransportInfo(TransportInfo(content_name, *new_tdesc))); 1468 if (!ret) { 1469 LOG(LS_ERROR) 1470 << "Failed to AddTransportOffer, content name=" << content_name; 1471 } 1472 return ret; 1473 } 1474 1475 TransportDescription* MediaSessionDescriptionFactory::CreateTransportAnswer( 1476 const std::string& content_name, 1477 const SessionDescription* offer_desc, 1478 const TransportOptions& transport_options, 1479 const SessionDescription* current_desc) const { 1480 if (!transport_desc_factory_) 1481 return NULL; 1482 const TransportDescription* offer_tdesc = 1483 GetTransportDescription(content_name, offer_desc); 1484 const TransportDescription* current_tdesc = 1485 GetTransportDescription(content_name, current_desc); 1486 return 1487 transport_desc_factory_->CreateAnswer(offer_tdesc, transport_options, 1488 current_tdesc); 1489 } 1490 1491 bool MediaSessionDescriptionFactory::AddTransportAnswer( 1492 const std::string& content_name, 1493 const TransportDescription& transport_desc, 1494 SessionDescription* answer_desc) const { 1495 if (!answer_desc->AddTransportInfo(TransportInfo(content_name, 1496 transport_desc))) { 1497 LOG(LS_ERROR) 1498 << "Failed to AddTransportAnswer, content name=" << content_name; 1499 return false; 1500 } 1501 return true; 1502 } 1503 1504 bool IsMediaContent(const ContentInfo* content) { 1505 return (content && 1506 (content->type == NS_JINGLE_RTP || 1507 content->type == NS_JINGLE_DRAFT_SCTP)); 1508 } 1509 1510 bool IsAudioContent(const ContentInfo* content) { 1511 return IsMediaContentOfType(content, MEDIA_TYPE_AUDIO); 1512 } 1513 1514 bool IsVideoContent(const ContentInfo* content) { 1515 return IsMediaContentOfType(content, MEDIA_TYPE_VIDEO); 1516 } 1517 1518 bool IsDataContent(const ContentInfo* content) { 1519 return IsMediaContentOfType(content, MEDIA_TYPE_DATA); 1520 } 1521 1522 static const ContentInfo* GetFirstMediaContent(const ContentInfos& contents, 1523 MediaType media_type) { 1524 for (ContentInfos::const_iterator content = contents.begin(); 1525 content != contents.end(); content++) { 1526 if (IsMediaContentOfType(&*content, media_type)) { 1527 return &*content; 1528 } 1529 } 1530 return NULL; 1531 } 1532 1533 const ContentInfo* GetFirstAudioContent(const ContentInfos& contents) { 1534 return GetFirstMediaContent(contents, MEDIA_TYPE_AUDIO); 1535 } 1536 1537 const ContentInfo* GetFirstVideoContent(const ContentInfos& contents) { 1538 return GetFirstMediaContent(contents, MEDIA_TYPE_VIDEO); 1539 } 1540 1541 const ContentInfo* GetFirstDataContent(const ContentInfos& contents) { 1542 return GetFirstMediaContent(contents, MEDIA_TYPE_DATA); 1543 } 1544 1545 static const ContentInfo* GetFirstMediaContent(const SessionDescription* sdesc, 1546 MediaType media_type) { 1547 if (sdesc == NULL) 1548 return NULL; 1549 1550 return GetFirstMediaContent(sdesc->contents(), media_type); 1551 } 1552 1553 const ContentInfo* GetFirstAudioContent(const SessionDescription* sdesc) { 1554 return GetFirstMediaContent(sdesc, MEDIA_TYPE_AUDIO); 1555 } 1556 1557 const ContentInfo* GetFirstVideoContent(const SessionDescription* sdesc) { 1558 return GetFirstMediaContent(sdesc, MEDIA_TYPE_VIDEO); 1559 } 1560 1561 const ContentInfo* GetFirstDataContent(const SessionDescription* sdesc) { 1562 return GetFirstMediaContent(sdesc, MEDIA_TYPE_DATA); 1563 } 1564 1565 const MediaContentDescription* GetFirstMediaContentDescription( 1566 const SessionDescription* sdesc, MediaType media_type) { 1567 const ContentInfo* content = GetFirstMediaContent(sdesc, media_type); 1568 const ContentDescription* description = content ? content->description : NULL; 1569 return static_cast<const MediaContentDescription*>(description); 1570 } 1571 1572 const AudioContentDescription* GetFirstAudioContentDescription( 1573 const SessionDescription* sdesc) { 1574 return static_cast<const AudioContentDescription*>( 1575 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO)); 1576 } 1577 1578 const VideoContentDescription* GetFirstVideoContentDescription( 1579 const SessionDescription* sdesc) { 1580 return static_cast<const VideoContentDescription*>( 1581 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO)); 1582 } 1583 1584 const DataContentDescription* GetFirstDataContentDescription( 1585 const SessionDescription* sdesc) { 1586 return static_cast<const DataContentDescription*>( 1587 GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA)); 1588 } 1589 1590 bool GetMediaChannelNameFromComponent( 1591 int component, MediaType media_type, std::string* channel_name) { 1592 if (media_type == MEDIA_TYPE_AUDIO) { 1593 if (component == ICE_CANDIDATE_COMPONENT_RTP) { 1594 *channel_name = GICE_CHANNEL_NAME_RTP; 1595 return true; 1596 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) { 1597 *channel_name = GICE_CHANNEL_NAME_RTCP; 1598 return true; 1599 } 1600 } else if (media_type == MEDIA_TYPE_VIDEO) { 1601 if (component == ICE_CANDIDATE_COMPONENT_RTP) { 1602 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTP; 1603 return true; 1604 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) { 1605 *channel_name = GICE_CHANNEL_NAME_VIDEO_RTCP; 1606 return true; 1607 } 1608 } else if (media_type == MEDIA_TYPE_DATA) { 1609 if (component == ICE_CANDIDATE_COMPONENT_RTP) { 1610 *channel_name = GICE_CHANNEL_NAME_DATA_RTP; 1611 return true; 1612 } else if (component == ICE_CANDIDATE_COMPONENT_RTCP) { 1613 *channel_name = GICE_CHANNEL_NAME_DATA_RTCP; 1614 return true; 1615 } 1616 } 1617 1618 return false; 1619 } 1620 1621 bool GetMediaComponentFromChannelName( 1622 const std::string& channel_name, int* component) { 1623 if (channel_name == GICE_CHANNEL_NAME_RTP || 1624 channel_name == GICE_CHANNEL_NAME_VIDEO_RTP || 1625 channel_name == GICE_CHANNEL_NAME_DATA_RTP) { 1626 *component = ICE_CANDIDATE_COMPONENT_RTP; 1627 return true; 1628 } else if (channel_name == GICE_CHANNEL_NAME_RTCP || 1629 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP || 1630 channel_name == GICE_CHANNEL_NAME_DATA_RTP) { 1631 *component = ICE_CANDIDATE_COMPONENT_RTCP; 1632 return true; 1633 } 1634 1635 return false; 1636 } 1637 1638 bool GetMediaTypeFromChannelName( 1639 const std::string& channel_name, MediaType* media_type) { 1640 if (channel_name == GICE_CHANNEL_NAME_RTP || 1641 channel_name == GICE_CHANNEL_NAME_RTCP) { 1642 *media_type = MEDIA_TYPE_AUDIO; 1643 return true; 1644 } else if (channel_name == GICE_CHANNEL_NAME_VIDEO_RTP || 1645 channel_name == GICE_CHANNEL_NAME_VIDEO_RTCP) { 1646 *media_type = MEDIA_TYPE_VIDEO; 1647 return true; 1648 } else if (channel_name == GICE_CHANNEL_NAME_DATA_RTP || 1649 channel_name == GICE_CHANNEL_NAME_DATA_RTCP) { 1650 *media_type = MEDIA_TYPE_DATA; 1651 return true; 1652 } 1653 1654 return false; 1655 } 1656 1657 } // namespace cricket 1658