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