JsepSessionImpl.cpp (88439B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "jsep/JsepSessionImpl.h" 6 7 #include <stdlib.h> 8 9 #include <bitset> 10 #include <set> 11 #include <string> 12 #include <utility> 13 14 #include "api/rtp_parameters.h" 15 #include "jsep/JsepTrack.h" 16 #include "jsep/JsepTransport.h" 17 #include "mozilla/Preferences.h" 18 #include "mozilla/StaticPrefs_media.h" 19 #include "mozilla/UniquePtr.h" 20 #include "mozilla/net/DataChannelProtocol.h" 21 #include "nsDebug.h" 22 #include "nspr.h" 23 #include "nss.h" 24 #include "pk11pub.h" 25 #include "sdp/HybridSdpParser.h" 26 #include "sdp/SipccSdp.h" 27 #include "transport/logging.h" 28 29 namespace mozilla { 30 31 MOZ_MTLOG_MODULE("jsep") 32 33 #define JSEP_SET_ERROR(error) \ 34 do { \ 35 std::ostringstream os; \ 36 os << error; \ 37 mLastError = os.str(); \ 38 MOZ_MTLOG(ML_ERROR, "[" << mName << "]: " << mLastError); \ 39 } while (0); 40 41 static std::bitset<128> GetForbiddenSdpPayloadTypes() { 42 std::bitset<128> forbidden(0); 43 forbidden[1] = true; 44 forbidden[2] = true; 45 forbidden[19] = true; 46 for (uint16_t i = 64; i < 96; ++i) { 47 forbidden[i] = true; 48 } 49 return forbidden; 50 } 51 52 static std::string GetRandomHex(size_t words) { 53 std::ostringstream os; 54 55 for (size_t i = 0; i < words; ++i) { 56 uint32_t rand; 57 SECStatus rv = PK11_GenerateRandom(reinterpret_cast<unsigned char*>(&rand), 58 sizeof(rand)); 59 if (rv != SECSuccess) { 60 MOZ_CRASH(); 61 return ""; 62 } 63 64 os << std::hex << std::setfill('0') << std::setw(8) << rand; 65 } 66 return os.str(); 67 } 68 69 JsepSessionImpl::JsepSessionImpl(const JsepSessionImpl& aOrig) 70 : JsepSession(aOrig), 71 JsepSessionCopyableStuff(aOrig), 72 mUuidGen(aOrig.mUuidGen->Clone()), 73 mGeneratedOffer(aOrig.mGeneratedOffer ? aOrig.mGeneratedOffer->Clone() 74 : nullptr), 75 mGeneratedAnswer(aOrig.mGeneratedAnswer ? aOrig.mGeneratedAnswer->Clone() 76 : nullptr), 77 mCurrentLocalDescription(aOrig.mCurrentLocalDescription 78 ? aOrig.mCurrentLocalDescription->Clone() 79 : nullptr), 80 mCurrentRemoteDescription(aOrig.mCurrentRemoteDescription 81 ? aOrig.mCurrentRemoteDescription->Clone() 82 : nullptr), 83 mPendingLocalDescription(aOrig.mPendingLocalDescription 84 ? aOrig.mPendingLocalDescription->Clone() 85 : nullptr), 86 mPendingRemoteDescription(aOrig.mPendingRemoteDescription 87 ? aOrig.mPendingRemoteDescription->Clone() 88 : nullptr), 89 mSdpHelper(&mLastError), 90 mParser(new HybridSdpParser()) { 91 for (const auto& codec : aOrig.mSupportedCodecs) { 92 mSupportedCodecs.emplace_back(codec->Clone()); 93 } 94 } 95 96 nsresult JsepSessionImpl::Init() { 97 mLastError.clear(); 98 99 MOZ_ASSERT(!mSessionId, "Init called more than once"); 100 101 nsresult rv = SetupIds(); 102 NS_ENSURE_SUCCESS(rv, rv); 103 104 mEncodeTrackId = 105 Preferences::GetBool("media.peerconnection.sdp.encode_track_id", true); 106 107 mIceUfrag = GetRandomHex(1); 108 mIcePwd = GetRandomHex(4); 109 return NS_OK; 110 } 111 112 static void GetIceCredentials( 113 const Sdp& aSdp, 114 std::set<std::pair<std::string, std::string>>* aCredentials) { 115 for (size_t i = 0; i < aSdp.GetMediaSectionCount(); ++i) { 116 const SdpAttributeList& attrs = aSdp.GetMediaSection(i).GetAttributeList(); 117 if (attrs.HasAttribute(SdpAttribute::kIceUfragAttribute) && 118 attrs.HasAttribute(SdpAttribute::kIcePwdAttribute)) { 119 aCredentials->insert( 120 std::make_pair(attrs.GetIceUfrag(), attrs.GetIcePwd())); 121 } 122 } 123 } 124 125 std::set<std::pair<std::string, std::string>> 126 JsepSessionImpl::GetLocalIceCredentials() const { 127 std::set<std::pair<std::string, std::string>> result; 128 if (mCurrentLocalDescription) { 129 GetIceCredentials(*mCurrentLocalDescription, &result); 130 } 131 if (mPendingLocalDescription) { 132 GetIceCredentials(*mPendingLocalDescription, &result); 133 } 134 return result; 135 } 136 137 void JsepSessionImpl::AddTransceiver(const JsepTransceiver& aTransceiver) { 138 mLastError.clear(); 139 MOZ_MTLOG(ML_DEBUG, 140 "[" << mName << "]: Adding transceiver " << aTransceiver.GetUuid()); 141 #ifdef DEBUG 142 if (aTransceiver.GetMediaType() == SdpMediaSection::kApplication) { 143 // Make sure we don't add more than one DataChannel transceiver 144 for (const auto& transceiver : mTransceivers) { 145 MOZ_ASSERT(transceiver.GetMediaType() != SdpMediaSection::kApplication); 146 } 147 } 148 #endif 149 mTransceivers.push_back(aTransceiver); 150 InitTransceiver(mTransceivers.back()); 151 } 152 153 void JsepSessionImpl::InitTransceiver(JsepTransceiver& aTransceiver) { 154 mLastError.clear(); 155 156 if (aTransceiver.GetMediaType() != SdpMediaSection::kApplication) { 157 // Make sure we have an ssrc. Might already be set. 158 aTransceiver.mSendTrack.EnsureSsrcs(mSsrcGenerator, 1U); 159 aTransceiver.mSendTrack.SetCNAME(mCNAME); 160 161 // Make sure we have identifiers for send track, just in case. 162 // (man I hate this) 163 if (mEncodeTrackId) { 164 aTransceiver.mSendTrack.SetTrackId(aTransceiver.GetUuid()); 165 } 166 } else { 167 // Datachannel transceivers should always be sendrecv. Just set it instead 168 // of asserting. 169 aTransceiver.mJsDirection = SdpDirectionAttribute::kSendrecv; 170 } 171 172 aTransceiver.mSendTrack.PopulateCodecs(mSupportedCodecs); 173 aTransceiver.mRecvTrack.PopulateCodecs(mSupportedCodecs); 174 // We do not set mLevel yet, we do that either on createOffer, or setRemote 175 } 176 177 nsresult JsepSessionImpl::SetBundlePolicy(JsepBundlePolicy policy) { 178 mLastError.clear(); 179 180 if (mBundlePolicy == policy) { 181 return NS_OK; 182 } 183 184 if (mCurrentLocalDescription) { 185 JSEP_SET_ERROR( 186 "Changing the bundle policy is only supported before the " 187 "first SetLocalDescription."); 188 return NS_ERROR_UNEXPECTED; 189 } 190 191 mBundlePolicy = policy; 192 return NS_OK; 193 } 194 195 nsresult JsepSessionImpl::AddDtlsFingerprint( 196 const nsACString& algorithm, const std::vector<uint8_t>& value) { 197 mLastError.clear(); 198 JsepDtlsFingerprint fp; 199 200 fp.mAlgorithm = algorithm; 201 fp.mValue = value; 202 203 mDtlsFingerprints.push_back(fp); 204 205 return NS_OK; 206 } 207 208 nsresult JsepSessionImpl::AddRtpExtension( 209 JsepMediaType mediaType, const std::string& extensionName, 210 SdpDirectionAttribute::Direction direction) { 211 mLastError.clear(); 212 213 for (auto& ext : mRtpExtensions) { 214 if (ext.mExtmap.direction == direction && 215 ext.mExtmap.extensionname == extensionName) { 216 if (ext.mMediaType != mediaType) { 217 ext.mMediaType = JsepMediaType::kAudioVideo; 218 } 219 return NS_OK; 220 } 221 } 222 223 uint16_t freeEntry = GetNeverUsedExtmapEntry(); 224 225 if (freeEntry == 0) { 226 return NS_ERROR_FAILURE; 227 } 228 229 JsepExtmapMediaType extMediaType = { 230 mediaType, 231 {freeEntry, direction, 232 // do we want to specify direction? 233 direction != SdpDirectionAttribute::kSendrecv, extensionName, ""}}; 234 235 mRtpExtensions.push_back(extMediaType); 236 return NS_OK; 237 } 238 239 nsresult JsepSessionImpl::AddAudioRtpExtension( 240 const std::string& extensionName, 241 SdpDirectionAttribute::Direction direction) { 242 return AddRtpExtension(JsepMediaType::kAudio, extensionName, direction); 243 } 244 245 nsresult JsepSessionImpl::AddVideoRtpExtension( 246 const std::string& extensionName, 247 SdpDirectionAttribute::Direction direction) { 248 return AddRtpExtension(JsepMediaType::kVideo, extensionName, direction); 249 } 250 251 nsresult JsepSessionImpl::AddAudioVideoRtpExtension( 252 const std::string& extensionName, 253 SdpDirectionAttribute::Direction direction) { 254 return AddRtpExtension(JsepMediaType::kAudioVideo, extensionName, direction); 255 } 256 257 nsresult JsepSessionImpl::CreateOfferMsection(const JsepOfferOptions& options, 258 JsepTransceiver& transceiver, 259 Sdp* local) { 260 SdpMediaSection::Protocol protocol( 261 SdpHelper::GetProtocolForMediaType(transceiver.GetMediaType())); 262 263 const Sdp* answer(GetAnswer()); 264 const SdpMediaSection* lastAnswerMsection = nullptr; 265 266 if (answer && 267 (local->GetMediaSectionCount() < answer->GetMediaSectionCount())) { 268 lastAnswerMsection = 269 &answer->GetMediaSection(local->GetMediaSectionCount()); 270 // Use the protocol the answer used, even if it is not what we would have 271 // used. 272 protocol = lastAnswerMsection->GetProtocol(); 273 } 274 275 SdpMediaSection* msection = &local->AddMediaSection( 276 transceiver.GetMediaType(), transceiver.mJsDirection, 0, protocol, 277 sdp::kIPv4, "0.0.0.0"); 278 279 // Some of this stuff (eg; mid) sticks around even if disabled 280 if (lastAnswerMsection) { 281 MOZ_ASSERT(lastAnswerMsection->GetMediaType() == 282 transceiver.GetMediaType()); 283 nsresult rv = mSdpHelper.CopyStickyParams(*lastAnswerMsection, msection); 284 NS_ENSURE_SUCCESS(rv, rv); 285 } 286 287 if (transceiver.IsStopping() || transceiver.IsStopped()) { 288 SdpHelper::DisableMsection(local, msection); 289 return NS_OK; 290 } 291 292 msection->SetPort(9); 293 294 // We don't do this in AddTransportAttributes because that is also used for 295 // making answers, and we don't want to unconditionally set rtcp-mux or 296 // rtcp-rsize there. 297 if (mSdpHelper.HasRtcp(msection->GetProtocol())) { 298 // Set RTCP-MUX. 299 msection->GetAttributeList().SetAttribute( 300 new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute)); 301 // Set RTCP-RSIZE 302 if (msection->GetMediaType() == SdpMediaSection::MediaType::kVideo && 303 Preferences::GetBool("media.navigator.video.offer_rtcp_rsize", false)) { 304 msection->GetAttributeList().SetAttribute( 305 new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute)); 306 } 307 } 308 // Ditto for extmap-allow-mixed 309 msection->GetAttributeList().SetAttribute( 310 new SdpFlagAttribute(SdpAttribute::kExtmapAllowMixedAttribute)); 311 312 nsresult rv = AddTransportAttributes(msection, SdpSetupAttribute::kActpass); 313 NS_ENSURE_SUCCESS(rv, rv); 314 315 transceiver.mSendTrack.AddToOffer(mSsrcGenerator, msection); 316 transceiver.mRecvTrack.AddToOffer(mSsrcGenerator, msection); 317 318 AddExtmap(msection); 319 320 std::string mid; 321 // We do not set the mid on the transceiver, that happens when a description 322 // is set. 323 if (transceiver.IsAssociated()) { 324 mid = transceiver.GetMid(); 325 } else { 326 mid = GetNewMid(); 327 } 328 329 msection->GetAttributeList().SetAttribute( 330 new SdpStringAttribute(SdpAttribute::kMidAttribute, mid)); 331 332 return NS_OK; 333 } 334 335 void JsepSessionImpl::SetupBundle(Sdp* sdp) const { 336 std::vector<std::string> mids; 337 std::set<SdpMediaSection::MediaType> observedTypes; 338 339 // This has the effect of changing the bundle level if the first m-section 340 // goes from disabled to enabled. This is kinda inefficient. 341 342 for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) { 343 auto& attrs = sdp->GetMediaSection(i).GetAttributeList(); 344 if ((sdp->GetMediaSection(i).GetPort() != 0) && 345 attrs.HasAttribute(SdpAttribute::kMidAttribute)) { 346 bool useBundleOnly = false; 347 switch (mBundlePolicy) { 348 case kBundleMaxCompat: 349 // We don't use bundle-only for max-compat 350 break; 351 case kBundleBalanced: 352 // balanced means we use bundle-only on everything but the first 353 // m-section of a given type 354 if (observedTypes.count(sdp->GetMediaSection(i).GetMediaType())) { 355 useBundleOnly = true; 356 } 357 observedTypes.insert(sdp->GetMediaSection(i).GetMediaType()); 358 break; 359 case kBundleMaxBundle: 360 // max-bundle means we use bundle-only on everything but the first 361 // m-section 362 useBundleOnly = !mids.empty(); 363 break; 364 } 365 366 if (useBundleOnly) { 367 attrs.SetAttribute( 368 new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute)); 369 // Set port to 0 for sections with bundle-only attribute. (mjf) 370 sdp->GetMediaSection(i).SetPort(0); 371 } 372 373 mids.push_back(attrs.GetMid()); 374 } 375 } 376 377 if (!mids.empty()) { 378 UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList); 379 groupAttr->PushEntry(SdpGroupAttributeList::kBundle, mids); 380 sdp->GetAttributeList().SetAttribute(groupAttr.release()); 381 } 382 } 383 384 JsepSession::Result JsepSessionImpl::CreateOffer( 385 const JsepOfferOptions& options, std::string* offer) { 386 mLastError.clear(); 387 388 if (mState != kJsepStateStable && mState != kJsepStateHaveLocalOffer) { 389 JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState)); 390 // Spec doesn't seem to say this is an error. It probably should. 391 return dom::PCError::InvalidStateError; 392 } 393 394 // This is one of those places where CreateOffer sets some state. 395 SetIceRestarting(options.mIceRestart.isSome() && *(options.mIceRestart)); 396 397 UniquePtr<Sdp> sdp; 398 399 // Make the basic SDP that is common to offer/answer. 400 nsresult rv = CreateGenericSDP(&sdp); 401 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 402 403 for (size_t level = 0; 404 Maybe<JsepTransceiver> transceiver = GetTransceiverForLocal(level); 405 ++level) { 406 rv = CreateOfferMsection(options, *transceiver, sdp.get()); 407 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 408 SetTransceiver(*transceiver); 409 } 410 411 SetupBundle(sdp.get()); 412 413 if (mCurrentLocalDescription && GetAnswer()) { 414 rv = CopyPreviousTransportParams(*GetAnswer(), *mCurrentLocalDescription, 415 *sdp, sdp.get()); 416 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 417 } 418 419 *offer = sdp->ToString(); 420 mGeneratedOffer = std::move(sdp); 421 ++mSessionVersion; 422 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: CreateOffer \nSDP=\n" << *offer); 423 424 return Result(); 425 } 426 427 std::string JsepSessionImpl::GetLocalDescription( 428 JsepDescriptionPendingOrCurrent type) const { 429 std::ostringstream os; 430 mozilla::Sdp* sdp = GetParsedLocalDescription(type); 431 if (sdp) { 432 sdp->Serialize(os); 433 } 434 return os.str(); 435 } 436 437 std::string JsepSessionImpl::GetRemoteDescription( 438 JsepDescriptionPendingOrCurrent type) const { 439 std::ostringstream os; 440 mozilla::Sdp* sdp = GetParsedRemoteDescription(type); 441 if (sdp) { 442 sdp->Serialize(os); 443 } 444 return os.str(); 445 } 446 447 void JsepSessionImpl::AddExtmap(SdpMediaSection* msection) { 448 auto extensions = GetRtpExtensions(*msection); 449 450 if (!extensions.empty()) { 451 SdpExtmapAttributeList* extmap = new SdpExtmapAttributeList; 452 extmap->mExtmaps = extensions; 453 msection->GetAttributeList().SetAttribute(extmap); 454 } 455 } 456 457 std::vector<SdpExtmapAttributeList::Extmap> JsepSessionImpl::GetRtpExtensions( 458 const SdpMediaSection& msection) { 459 std::vector<SdpExtmapAttributeList::Extmap> result; 460 JsepMediaType mediaType = JsepMediaType::kNone; 461 const auto direction = msection.GetDirection(); 462 const auto includes_send = direction == SdpDirectionAttribute::kSendrecv || 463 direction == SdpDirectionAttribute::kSendonly; 464 switch (msection.GetMediaType()) { 465 case SdpMediaSection::kAudio: 466 mediaType = JsepMediaType::kAudio; 467 break; 468 case SdpMediaSection::kVideo: 469 mediaType = JsepMediaType::kVideo; 470 // We need to add the dependency descriptor extension for simulcast 471 if (includes_send && StaticPrefs::media_peerconnection_video_use_dd() && 472 msection.GetAttributeList().HasAttribute( 473 SdpAttribute::kSimulcastAttribute)) { 474 AddVideoRtpExtension(webrtc::RtpExtension::kDependencyDescriptorUri, 475 SdpDirectionAttribute::kSendonly); 476 } 477 if (msection.GetAttributeList().HasAttribute( 478 SdpAttribute::kRidAttribute)) { 479 // We need RID support 480 // TODO: Would it be worth checking that the direction is sane? 481 AddVideoRtpExtension(webrtc::RtpExtension::kRidUri, 482 SdpDirectionAttribute::kSendonly); 483 484 if (mRtxIsAllowed && 485 Preferences::GetBool("media.peerconnection.video.use_rtx", false)) { 486 AddVideoRtpExtension(webrtc::RtpExtension::kRepairedRidUri, 487 SdpDirectionAttribute::kSendonly); 488 } 489 } 490 break; 491 default:; 492 } 493 if (mediaType != JsepMediaType::kNone) { 494 for (auto ext = mRtpExtensions.begin(); ext != mRtpExtensions.end(); 495 ++ext) { 496 if (ext->mMediaType == mediaType || 497 ext->mMediaType == JsepMediaType::kAudioVideo) { 498 result.push_back(ext->mExtmap); 499 } 500 } 501 } 502 return result; 503 } 504 505 std::string JsepSessionImpl::GetNewMid() { 506 std::string mid; 507 508 do { 509 std::ostringstream osMid; 510 osMid << mMidCounter++; 511 mid = osMid.str(); 512 } while (mUsedMids.count(mid)); 513 514 mUsedMids.insert(mid); 515 return mid; 516 } 517 518 void JsepSessionImpl::AddCommonExtmaps(const SdpMediaSection& remoteMsection, 519 SdpMediaSection* msection) { 520 auto negotiatedRtpExtensions = GetRtpExtensions(*msection); 521 mSdpHelper.NegotiateAndAddExtmaps(remoteMsection, negotiatedRtpExtensions, 522 msection); 523 } 524 525 uint16_t JsepSessionImpl::GetNeverUsedExtmapEntry() { 526 uint16_t result = 1; 527 528 // Walk the set in order, and return the first "hole" we find 529 for (const auto used : mExtmapEntriesEverUsed) { 530 if (result != used) { 531 MOZ_ASSERT(result < used); 532 break; 533 } 534 535 // RFC 5285 says entries >= 4096 are used in offers to force the answerer 536 // to pick, so we do not want to actually use these 537 if (used == 4095) { 538 JSEP_SET_ERROR( 539 "Too many rtp extensions have been added. " 540 "That's 4095. Who _does_ that?"); 541 return 0; 542 } 543 544 result = used + 1; 545 } 546 547 mExtmapEntriesEverUsed.insert(result); 548 return result; 549 } 550 551 JsepSession::Result JsepSessionImpl::CreateAnswer( 552 const JsepAnswerOptions& options, std::string* answer) { 553 mLastError.clear(); 554 555 if (mState != kJsepStateHaveRemoteOffer) { 556 JSEP_SET_ERROR("Cannot create answer in state " << GetStateStr(mState)); 557 return dom::PCError::InvalidStateError; 558 } 559 560 UniquePtr<Sdp> sdp; 561 562 // Make the basic SDP that is common to offer/answer. 563 nsresult rv = CreateGenericSDP(&sdp); 564 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 565 566 const Sdp& offer = *mPendingRemoteDescription; 567 568 // Copy the bundle groups into our answer 569 UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList); 570 mSdpHelper.GetBundleGroups(offer, &groupAttr->mGroups); 571 sdp->GetAttributeList().SetAttribute(groupAttr.release()); 572 573 // Copy EXTMAP-ALLOW-MIXED from the offer to the answer 574 if (offer.GetAttributeList().HasAttribute( 575 SdpAttribute::kExtmapAllowMixedAttribute)) { 576 sdp->GetAttributeList().SetAttribute( 577 new SdpFlagAttribute(SdpAttribute::kExtmapAllowMixedAttribute)); 578 } else { 579 sdp->GetAttributeList().RemoveAttribute( 580 SdpAttribute::kExtmapAllowMixedAttribute); 581 } 582 583 for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) { 584 // The transceivers are already in place, due to setRemote 585 Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i)); 586 if (!transceiver) { 587 JSEP_SET_ERROR("No transceiver for level " << i); 588 MOZ_ASSERT(false); 589 return dom::PCError::OperationError; 590 } 591 rv = CreateAnswerMsection(options, *transceiver, offer.GetMediaSection(i), 592 sdp.get()); 593 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 594 SetTransceiver(*transceiver); 595 } 596 597 // Ensure that each bundle-group starts with a mid that has a transport, in 598 // case we've disabled what the offerer wanted to use. If the group doesn't 599 // contain anything that has a transport, remove it. 600 groupAttr.reset(new SdpGroupAttributeList); 601 std::vector<SdpGroupAttributeList::Group> bundleGroups; 602 mSdpHelper.GetBundleGroups(*sdp, &bundleGroups); 603 for (auto& group : bundleGroups) { 604 for (auto& mid : group.tags) { 605 const SdpMediaSection* msection = 606 mSdpHelper.FindMsectionByMid(offer, mid); 607 608 if (msection && !msection->GetAttributeList().HasAttribute( 609 SdpAttribute::kBundleOnlyAttribute)) { 610 std::swap(group.tags[0], mid); 611 groupAttr->mGroups.push_back(group); 612 break; 613 } 614 } 615 } 616 sdp->GetAttributeList().SetAttribute(groupAttr.release()); 617 618 if (mCurrentLocalDescription) { 619 // per discussion with bwc, 3rd parm here should be offer, not *sdp. (mjf) 620 rv = CopyPreviousTransportParams(*GetAnswer(), *mCurrentRemoteDescription, 621 offer, sdp.get()); 622 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 623 } 624 625 *answer = sdp->ToString(); 626 mGeneratedAnswer = std::move(sdp); 627 ++mSessionVersion; 628 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: CreateAnswer \nSDP=\n" << *answer); 629 630 return Result(); 631 } 632 633 nsresult JsepSessionImpl::CreateAnswerMsection( 634 const JsepAnswerOptions& options, JsepTransceiver& transceiver, 635 const SdpMediaSection& remoteMsection, Sdp* sdp) { 636 MOZ_ASSERT(transceiver.GetMediaType() == remoteMsection.GetMediaType()); 637 SdpDirectionAttribute::Direction direction = 638 reverse(remoteMsection.GetDirection()) & transceiver.mJsDirection; 639 SdpMediaSection& msection = 640 sdp->AddMediaSection(remoteMsection.GetMediaType(), direction, 9, 641 remoteMsection.GetProtocol(), sdp::kIPv4, "0.0.0.0"); 642 643 nsresult rv = mSdpHelper.CopyStickyParams(remoteMsection, &msection); 644 NS_ENSURE_SUCCESS(rv, rv); 645 646 if (mSdpHelper.MsectionIsDisabled(remoteMsection)) { 647 SdpHelper::DisableMsection(sdp, &msection); 648 return NS_OK; 649 } 650 651 MOZ_ASSERT(transceiver.IsAssociated()); 652 if (msection.GetAttributeList().GetMid().empty()) { 653 msection.GetAttributeList().SetAttribute(new SdpStringAttribute( 654 SdpAttribute::kMidAttribute, transceiver.GetMid())); 655 } 656 657 MOZ_ASSERT(transceiver.GetMid() == msection.GetAttributeList().GetMid()); 658 659 SdpSetupAttribute::Role role; 660 if (transceiver.mTransport.mDtls && !IsIceRestarting()) { 661 role = (transceiver.mTransport.mDtls->mRole == 662 JsepDtlsTransport::kJsepDtlsClient) 663 ? SdpSetupAttribute::kActive 664 : SdpSetupAttribute::kPassive; 665 } else { 666 rv = DetermineAnswererSetupRole(remoteMsection, &role); 667 NS_ENSURE_SUCCESS(rv, rv); 668 } 669 670 rv = AddTransportAttributes(&msection, role); 671 NS_ENSURE_SUCCESS(rv, rv); 672 673 transceiver.mSendTrack.AddToAnswer(remoteMsection, mSsrcGenerator, &msection); 674 transceiver.mRecvTrack.AddToAnswer(remoteMsection, mSsrcGenerator, &msection); 675 676 // Add extmap attributes. This logic will probably be moved to the track, 677 // since it can be specified on a per-sender basis in JS. 678 // We will need some validation to ensure that the ids are identical for 679 // RTP streams that are bundled together, though (bug 1406529). 680 AddCommonExtmaps(remoteMsection, &msection); 681 682 if (msection.GetFormats().empty()) { 683 // Could not negotiate anything. Disable m-section. 684 SdpHelper::DisableMsection(sdp, &msection); 685 } 686 687 return NS_OK; 688 } 689 690 nsresult JsepSessionImpl::DetermineAnswererSetupRole( 691 const SdpMediaSection& remoteMsection, SdpSetupAttribute::Role* rolep) { 692 // Determine the role. 693 // RFC 5763 says: 694 // 695 // The endpoint MUST use the setup attribute defined in [RFC4145]. 696 // The endpoint that is the offerer MUST use the setup attribute 697 // value of setup:actpass and be prepared to receive a client_hello 698 // before it receives the answer. The answerer MUST use either a 699 // setup attribute value of setup:active or setup:passive. Note that 700 // if the answerer uses setup:passive, then the DTLS handshake will 701 // not begin until the answerer is received, which adds additional 702 // latency. setup:active allows the answer and the DTLS handshake to 703 // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever 704 // party is active MUST initiate a DTLS handshake by sending a 705 // ClientHello over each flow (host/port quartet). 706 // 707 // We default to assuming that the offerer is passive and we are active. 708 SdpSetupAttribute::Role role = SdpSetupAttribute::kActive; 709 710 if (remoteMsection.GetAttributeList().HasAttribute( 711 SdpAttribute::kSetupAttribute)) { 712 switch (remoteMsection.GetAttributeList().GetSetup().mRole) { 713 case SdpSetupAttribute::kActive: 714 role = SdpSetupAttribute::kPassive; 715 break; 716 case SdpSetupAttribute::kPassive: 717 case SdpSetupAttribute::kActpass: 718 role = SdpSetupAttribute::kActive; 719 break; 720 case SdpSetupAttribute::kHoldconn: 721 // This should have been caught by ParseSdp 722 MOZ_ASSERT(false); 723 JSEP_SET_ERROR( 724 "The other side used an illegal setup attribute" 725 " (\"holdconn\")."); 726 return NS_ERROR_INVALID_ARG; 727 } 728 } 729 730 *rolep = role; 731 return NS_OK; 732 } 733 734 JsepSession::Result JsepSessionImpl::SetLocalDescription( 735 JsepSdpType type, const std::string& constSdp) { 736 mLastError.clear(); 737 std::string sdp = constSdp; 738 739 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SetLocalDescription type=" << type 740 << "\nSDP=\n" 741 << sdp); 742 743 switch (type) { 744 case kJsepSdpOffer: 745 if (!mGeneratedOffer) { 746 JSEP_SET_ERROR( 747 "Cannot set local offer when createOffer has not been called."); 748 return dom::PCError::InvalidModificationError; 749 } 750 if (sdp.empty()) { 751 sdp = mGeneratedOffer->ToString(); 752 } 753 if (mState == kJsepStateHaveLocalOffer) { 754 // Rollback previous offer before applying the new one. 755 SetLocalDescription(kJsepSdpRollback, ""); 756 MOZ_ASSERT(mState == kJsepStateStable); 757 } 758 break; 759 case kJsepSdpAnswer: 760 case kJsepSdpPranswer: 761 if (!mGeneratedAnswer) { 762 JSEP_SET_ERROR( 763 "Cannot set local answer when createAnswer has not been called."); 764 return dom::PCError::InvalidModificationError; 765 } 766 if (sdp.empty()) { 767 sdp = mGeneratedAnswer->ToString(); 768 } 769 break; 770 case kJsepSdpRollback: 771 if (mState != kJsepStateHaveLocalOffer) { 772 JSEP_SET_ERROR("Cannot rollback local description in " 773 << GetStateStr(mState)); 774 // Currently, spec allows this in any state except stable, and 775 // sRD(rollback) and sLD(rollback) do exactly the same thing. 776 return dom::PCError::InvalidStateError; 777 } 778 779 mPendingLocalDescription.reset(); 780 SetState(kJsepStateStable); 781 RollbackLocalOffer(); 782 return Result(); 783 } 784 785 switch (mState) { 786 case kJsepStateStable: 787 if (type != kJsepSdpOffer) { 788 JSEP_SET_ERROR("Cannot set local answer in state " 789 << GetStateStr(mState)); 790 return dom::PCError::InvalidStateError; 791 } 792 break; 793 case kJsepStateHaveRemoteOffer: 794 if (type != kJsepSdpAnswer && type != kJsepSdpPranswer) { 795 JSEP_SET_ERROR("Cannot set local offer in state " 796 << GetStateStr(mState)); 797 return dom::PCError::InvalidStateError; 798 } 799 break; 800 default: 801 JSEP_SET_ERROR("Cannot set local offer or answer in state " 802 << GetStateStr(mState)); 803 return dom::PCError::InvalidStateError; 804 } 805 806 UniquePtr<Sdp> parsed; 807 nsresult rv = ParseSdp(sdp, &parsed); 808 // Needs to be RTCError with sdp-syntax-error 809 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 810 811 // Check that content hasn't done anything unsupported with the SDP 812 rv = ValidateLocalDescription(*parsed, type); 813 NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidModificationError); 814 815 switch (type) { 816 case kJsepSdpOffer: 817 rv = ValidateOffer(*parsed); 818 break; 819 case kJsepSdpAnswer: 820 case kJsepSdpPranswer: 821 rv = ValidateAnswer(*mPendingRemoteDescription, *parsed); 822 break; 823 case kJsepSdpRollback: 824 MOZ_CRASH(); // Handled above 825 } 826 NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidAccessError); 827 828 if (type == kJsepSdpOffer) { 829 // Save in case we need to rollback 830 mOldTransceivers = mTransceivers; 831 } 832 833 SdpHelper::BundledMids bundledMids; 834 rv = mSdpHelper.GetBundledMids(*parsed, &bundledMids); 835 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 836 837 SdpHelper::BundledMids remoteBundledMids; 838 if (type != kJsepSdpOffer) { 839 rv = mSdpHelper.GetBundledMids(*mPendingRemoteDescription, 840 &remoteBundledMids); 841 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 842 } 843 844 for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) { 845 Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i)); 846 if (!transceiver) { 847 MOZ_ASSERT(false); 848 JSEP_SET_ERROR("No transceiver for level " << i); 849 return dom::PCError::OperationError; 850 } 851 852 const auto& msection = parsed->GetMediaSection(i); 853 transceiver->Associate(msection.GetAttributeList().GetMid()); 854 transceiver->mRecvTrack.RecvTrackSetLocal(msection); 855 856 if (mSdpHelper.MsectionIsDisabled(msection)) { 857 transceiver->mTransport.Close(); 858 SetTransceiver(*transceiver); 859 continue; 860 } 861 862 bool hasOwnTransport = mSdpHelper.OwnsTransport( 863 msection, bundledMids, 864 (type == kJsepSdpOffer) ? sdp::kOffer : sdp::kAnswer); 865 if (type != kJsepSdpOffer) { 866 const auto& remoteMsection = 867 mPendingRemoteDescription->GetMediaSection(i); 868 // Don't allow the answer to override what the offer allowed for 869 hasOwnTransport &= mSdpHelper.OwnsTransport( 870 remoteMsection, remoteBundledMids, sdp::kOffer); 871 } 872 873 if (hasOwnTransport) { 874 EnsureHasOwnTransport(parsed->GetMediaSection(i), *transceiver); 875 } 876 877 if (type == kJsepSdpOffer) { 878 if (!hasOwnTransport) { 879 auto it = bundledMids.find(transceiver->GetMid()); 880 if (it != bundledMids.end()) { 881 transceiver->SetBundleLevel(it->second->GetLevel()); 882 } 883 } 884 } else { 885 auto it = remoteBundledMids.find(transceiver->GetMid()); 886 if (it != remoteBundledMids.end()) { 887 transceiver->SetBundleLevel(it->second->GetLevel()); 888 } 889 } 890 SetTransceiver(*transceiver); 891 } 892 893 CopyBundleTransports(); 894 895 switch (type) { 896 case kJsepSdpOffer: 897 rv = SetLocalDescriptionOffer(std::move(parsed)); 898 break; 899 case kJsepSdpAnswer: 900 case kJsepSdpPranswer: 901 rv = SetLocalDescriptionAnswer(type, std::move(parsed)); 902 break; 903 case kJsepSdpRollback: 904 MOZ_CRASH(); // Handled above 905 } 906 907 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 908 return Result(); 909 } 910 911 nsresult JsepSessionImpl::SetLocalDescriptionOffer(UniquePtr<Sdp> offer) { 912 MOZ_ASSERT(mState == kJsepStateStable); 913 mPendingLocalDescription = std::move(offer); 914 mIsPendingOfferer = Some(true); 915 SetState(kJsepStateHaveLocalOffer); 916 917 std::vector<JsepTrack*> recvTracks; 918 recvTracks.reserve(mTransceivers.size()); 919 for (auto& transceiver : mTransceivers) { 920 if (transceiver.mJsDirection & sdp::kRecv) { 921 recvTracks.push_back(&transceiver.mRecvTrack); 922 } else { 923 transceiver.mRecvTrack.ResetReceivePayloadTypes(); 924 } 925 } 926 927 JsepTrack::SetReceivePayloadTypes(recvTracks, true); 928 929 return NS_OK; 930 } 931 932 nsresult JsepSessionImpl::SetLocalDescriptionAnswer(JsepSdpType type, 933 UniquePtr<Sdp> answer) { 934 MOZ_ASSERT(mState == kJsepStateHaveRemoteOffer); 935 mPendingLocalDescription = std::move(answer); 936 937 nsresult rv = HandleNegotiatedSession(mPendingLocalDescription, 938 mPendingRemoteDescription); 939 NS_ENSURE_SUCCESS(rv, rv); 940 941 mCurrentRemoteDescription = std::move(mPendingRemoteDescription); 942 mCurrentLocalDescription = std::move(mPendingLocalDescription); 943 MOZ_ASSERT(mIsPendingOfferer.isSome() && !*mIsPendingOfferer); 944 mIsPendingOfferer.reset(); 945 mIsCurrentOfferer = Some(false); 946 947 SetState(kJsepStateStable); 948 return NS_OK; 949 } 950 951 JsepSession::Result JsepSessionImpl::SetRemoteDescription( 952 JsepSdpType type, const std::string& sdp) { 953 mLastError.clear(); 954 955 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SetRemoteDescription type=" << type 956 << "\nSDP=\n" 957 << sdp); 958 959 if (mState == kJsepStateHaveRemoteOffer && type == kJsepSdpOffer) { 960 // Rollback previous offer before applying the new one. 961 SetRemoteDescription(kJsepSdpRollback, ""); 962 MOZ_ASSERT(mState == kJsepStateStable); 963 } 964 965 if (type == kJsepSdpRollback) { 966 if (mState != kJsepStateHaveRemoteOffer) { 967 JSEP_SET_ERROR("Cannot rollback remote description in " 968 << GetStateStr(mState)); 969 return dom::PCError::InvalidStateError; 970 } 971 972 mPendingRemoteDescription.reset(); 973 SetState(kJsepStateStable); 974 RollbackRemoteOffer(); 975 976 return Result(); 977 } 978 979 switch (mState) { 980 case kJsepStateStable: 981 if (type != kJsepSdpOffer) { 982 JSEP_SET_ERROR("Cannot set remote answer in state " 983 << GetStateStr(mState)); 984 return dom::PCError::InvalidStateError; 985 } 986 break; 987 case kJsepStateHaveLocalOffer: 988 case kJsepStateHaveRemotePranswer: 989 if (type != kJsepSdpAnswer && type != kJsepSdpPranswer) { 990 JSEP_SET_ERROR("Cannot set remote offer in state " 991 << GetStateStr(mState)); 992 return dom::PCError::InvalidStateError; 993 } 994 break; 995 default: 996 JSEP_SET_ERROR("Cannot set remote offer or answer in current state " 997 << GetStateStr(mState)); 998 return dom::PCError::InvalidStateError; 999 } 1000 1001 // Parse. 1002 UniquePtr<Sdp> parsed; 1003 nsresult rv = ParseSdp(sdp, &parsed); 1004 // Needs to be RTCError with sdp-syntax-error 1005 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 1006 1007 rv = ValidateRemoteDescription(*parsed); 1008 NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidAccessError); 1009 1010 switch (type) { 1011 case kJsepSdpOffer: 1012 rv = ValidateOffer(*parsed); 1013 break; 1014 case kJsepSdpAnswer: 1015 case kJsepSdpPranswer: 1016 rv = ValidateAnswer(*mPendingLocalDescription, *parsed); 1017 break; 1018 case kJsepSdpRollback: 1019 MOZ_CRASH(); // Handled above 1020 } 1021 NS_ENSURE_SUCCESS(rv, dom::PCError::InvalidAccessError); 1022 1023 bool iceLite = 1024 parsed->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute); 1025 1026 // check for mismatch ufrag/pwd indicating ice restart 1027 // can't just check the first one because it might be disabled 1028 bool iceRestarting = false; 1029 if (mCurrentRemoteDescription.get()) { 1030 for (size_t i = 0; !iceRestarting && 1031 i < mCurrentRemoteDescription->GetMediaSectionCount(); 1032 ++i) { 1033 const SdpMediaSection& newMsection = parsed->GetMediaSection(i); 1034 const SdpMediaSection& oldMsection = 1035 mCurrentRemoteDescription->GetMediaSection(i); 1036 1037 if (mSdpHelper.MsectionIsDisabled(newMsection) || 1038 mSdpHelper.MsectionIsDisabled(oldMsection)) { 1039 continue; 1040 } 1041 1042 iceRestarting = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection); 1043 } 1044 } 1045 1046 std::vector<std::string> iceOptions; 1047 if (parsed->GetAttributeList().HasAttribute( 1048 SdpAttribute::kIceOptionsAttribute)) { 1049 iceOptions = parsed->GetAttributeList().GetIceOptions().mValues; 1050 } 1051 1052 if (type == kJsepSdpOffer) { 1053 // Save in case we need to rollback. 1054 mOldTransceivers = mTransceivers; 1055 for (auto& transceiver : mTransceivers) { 1056 if (!transceiver.IsNegotiated()) { 1057 // We chose a level for this transceiver, but never negotiated it. 1058 // Discard this state. 1059 transceiver.ClearLevel(); 1060 } 1061 } 1062 } 1063 1064 // TODO(bug 1095780): Note that we create remote tracks even when 1065 // They contain only codecs we can't negotiate or other craziness. 1066 rv = UpdateTransceiversFromRemoteDescription(*parsed); 1067 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 1068 1069 for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) { 1070 MOZ_ASSERT(GetTransceiverForLevel(i)); 1071 } 1072 1073 switch (type) { 1074 case kJsepSdpOffer: 1075 rv = SetRemoteDescriptionOffer(std::move(parsed)); 1076 break; 1077 case kJsepSdpAnswer: 1078 case kJsepSdpPranswer: 1079 rv = SetRemoteDescriptionAnswer(type, std::move(parsed)); 1080 break; 1081 case kJsepSdpRollback: 1082 MOZ_CRASH(); // Handled above 1083 } 1084 1085 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 1086 1087 mRemoteIsIceLite = iceLite; 1088 mIceOptions = iceOptions; 1089 SetIceRestarting(iceRestarting); 1090 return Result(); 1091 } 1092 1093 nsresult JsepSessionImpl::HandleNegotiatedSession( 1094 const UniquePtr<Sdp>& local, const UniquePtr<Sdp>& remote) { 1095 // local ufrag/pwd has been negotiated; we will never go back to the old ones 1096 mOldIceUfrag.clear(); 1097 mOldIcePwd.clear(); 1098 1099 bool remoteIceLite = 1100 remote->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute); 1101 1102 mIceControlling = remoteIceLite || *mIsPendingOfferer; 1103 1104 const Sdp& answer = *mIsPendingOfferer ? *remote : *local; 1105 1106 SdpHelper::BundledMids bundledMids; 1107 nsresult rv = mSdpHelper.GetBundledMids(answer, &bundledMids); 1108 NS_ENSURE_SUCCESS(rv, rv); 1109 1110 // First, set the bundle level on the transceivers 1111 for (auto& [mid, transportOwner] : bundledMids) { 1112 Maybe<JsepTransceiver> bundledTransceiver = GetTransceiverForMid(mid); 1113 if (!bundledTransceiver) { 1114 JSEP_SET_ERROR("No transceiver for bundled mid " << mid); 1115 return NS_ERROR_INVALID_ARG; 1116 } 1117 bundledTransceiver->SetBundleLevel(transportOwner->GetLevel()); 1118 SetTransceiver(*bundledTransceiver); 1119 } 1120 1121 // Now walk through the m-sections, perform negotiation, and update the 1122 // transceivers. 1123 for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) { 1124 Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i)); 1125 if (!transceiver) { 1126 MOZ_ASSERT(false); 1127 JSEP_SET_ERROR("No transceiver for level " << i); 1128 return NS_ERROR_FAILURE; 1129 } 1130 1131 if (mSdpHelper.MsectionIsDisabled(local->GetMediaSection(i))) { 1132 transceiver->SetRemoved(); 1133 } 1134 1135 // Skip disabled m-sections. 1136 if (mSdpHelper.MsectionIsDisabled(answer.GetMediaSection(i))) { 1137 transceiver->mTransport.Close(); 1138 transceiver->SetStopped(); 1139 transceiver->Disassociate(); 1140 transceiver->ClearBundleLevel(); 1141 transceiver->mSendTrack.SetActive(false); 1142 transceiver->mRecvTrack.SetActive(false); 1143 transceiver->SetCanRecycleMyMsection(); 1144 SetTransceiver(*transceiver); 1145 // Do not clear mLevel yet! That will happen on the next negotiation. 1146 continue; 1147 } 1148 1149 rv = MakeNegotiatedTransceiver(remote->GetMediaSection(i), 1150 local->GetMediaSection(i), *transceiver); 1151 NS_ENSURE_SUCCESS(rv, rv); 1152 SetTransceiver(*transceiver); 1153 } 1154 1155 CopyBundleTransports(); 1156 1157 std::vector<JsepTrack*> receiveTracks; 1158 receiveTracks.reserve(mTransceivers.size()); 1159 for (auto& transceiver : mTransceivers) { 1160 // Do not count payload types for non-active recv tracks as duplicates. If 1161 // we receive an RTP packet with a payload type that is used by both a 1162 // sendrecv and a sendonly m-section, there is no ambiguity; it is for the 1163 // sendrecv m-section. MediaPipelineFilter and conduits are informed of 1164 // their active status, so they know whether they can process packets and 1165 // learn new SSRCs. 1166 if (transceiver.mRecvTrack.GetActive()) { 1167 receiveTracks.push_back(&transceiver.mRecvTrack); 1168 } else { 1169 transceiver.mRecvTrack.ResetReceivePayloadTypes(); 1170 } 1171 } 1172 JsepTrack::SetReceivePayloadTypes(receiveTracks); 1173 1174 mNegotiations++; 1175 1176 mGeneratedAnswer.reset(); 1177 mGeneratedOffer.reset(); 1178 1179 return NS_OK; 1180 } 1181 1182 nsresult JsepSessionImpl::MakeNegotiatedTransceiver( 1183 const SdpMediaSection& remote, const SdpMediaSection& local, 1184 JsepTransceiver& transceiver) { 1185 const SdpMediaSection& answer = *mIsPendingOfferer ? remote : local; 1186 1187 bool sending = false; 1188 bool receiving = false; 1189 1190 // We do not pay any attention to whether the transceiver is stopped here, 1191 // because that is only a signal to the JSEP engine to _attempt_ to reject 1192 // the corresponding m-section the next time we're the offerer. 1193 if (*mIsPendingOfferer) { 1194 receiving = answer.IsSending(); 1195 sending = answer.IsReceiving(); 1196 } else { 1197 sending = answer.IsSending(); 1198 receiving = answer.IsReceiving(); 1199 } 1200 1201 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: Negotiated m= line" 1202 << " index=" << local.GetLevel() << " type=" 1203 << local.GetMediaType() << " sending=" << sending 1204 << " receiving=" << receiving); 1205 1206 transceiver.SetNegotiated(); 1207 1208 // Ensure that this is finalized in case we need to copy it below 1209 nsresult rv = 1210 FinalizeTransport(remote.GetAttributeList(), answer.GetAttributeList(), 1211 &transceiver.mTransport); 1212 NS_ENSURE_SUCCESS(rv, rv); 1213 1214 transceiver.mSendTrack.SetActive(sending); 1215 rv = transceiver.mSendTrack.Negotiate(answer, remote, local); 1216 if (NS_FAILED(rv)) { 1217 JSEP_SET_ERROR("Answer had no codecs in common with offer in m-section " 1218 << local.GetLevel()); 1219 return rv; 1220 } 1221 1222 JsepTrack& recvTrack = transceiver.mRecvTrack; 1223 recvTrack.SetActive(receiving); 1224 rv = recvTrack.Negotiate(answer, remote, local); 1225 if (NS_FAILED(rv)) { 1226 JSEP_SET_ERROR("Answer had no codecs in common with offer in m-section " 1227 << local.GetLevel()); 1228 return rv; 1229 } 1230 1231 if (transceiver.HasBundleLevel() && recvTrack.GetSsrcs().empty() && 1232 recvTrack.GetMediaType() != SdpMediaSection::kApplication) { 1233 // TODO(bug 1105005): Once we have urn:ietf:params:rtp-hdrext:sdes:mid 1234 // support, we should only fire this warning if that extension was not 1235 // negotiated. 1236 MOZ_MTLOG(ML_ERROR, "[" << mName 1237 << "]: Bundled m-section has no ssrc " 1238 "attributes. This may cause media packets to be " 1239 "dropped."); 1240 } 1241 1242 if (transceiver.mTransport.mComponents == 2) { 1243 // RTCP MUX or not. 1244 // TODO(bug 1095743): verify that the PTs are consistent with mux. 1245 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: RTCP-MUX is off"); 1246 } 1247 1248 if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) { 1249 const auto extmaps = answer.GetAttributeList().GetExtmap().mExtmaps; 1250 for (const auto& negotiatedExtension : extmaps) { 1251 if (negotiatedExtension.entry == 0) { 1252 MOZ_ASSERT(false, "This should have been caught sooner"); 1253 continue; 1254 } 1255 1256 mExtmapEntriesEverNegotiated[negotiatedExtension.entry] = 1257 negotiatedExtension.extensionname; 1258 1259 for (auto& originalExtension : mRtpExtensions) { 1260 if (negotiatedExtension.extensionname == 1261 originalExtension.mExtmap.extensionname) { 1262 // Update extmap to match what was negotiated 1263 originalExtension.mExtmap.entry = negotiatedExtension.entry; 1264 mExtmapEntriesEverUsed.insert(negotiatedExtension.entry); 1265 } else if (originalExtension.mExtmap.entry == 1266 negotiatedExtension.entry) { 1267 // If this extmap entry was claimed for a different extension, update 1268 // it to a new value so we don't end up with a duplicate. 1269 originalExtension.mExtmap.entry = GetNeverUsedExtmapEntry(); 1270 } 1271 } 1272 } 1273 } 1274 1275 return NS_OK; 1276 } 1277 1278 void JsepSessionImpl::EnsureHasOwnTransport(const SdpMediaSection& msection, 1279 JsepTransceiver& transceiver) { 1280 JsepTransport& transport = transceiver.mTransport; 1281 1282 if (!transceiver.HasOwnTransport()) { 1283 // Transceiver didn't own this transport last time, it won't now either 1284 transport.Close(); 1285 } 1286 1287 transport.mLocalUfrag = msection.GetAttributeList().GetIceUfrag(); 1288 transport.mLocalPwd = msection.GetAttributeList().GetIcePwd(); 1289 1290 transceiver.ClearBundleLevel(); 1291 1292 if (!transport.mComponents) { 1293 if (mSdpHelper.HasRtcp(msection.GetProtocol())) { 1294 transport.mComponents = 2; 1295 } else { 1296 transport.mComponents = 1; 1297 } 1298 } 1299 1300 if (transport.mTransportId.empty()) { 1301 // TODO: Once we use different ICE ufrag/pass for each m-section, we can 1302 // use that here. 1303 std::ostringstream os; 1304 os << "transport_" << mTransportIdCounter++; 1305 transport.mTransportId = os.str(); 1306 } 1307 } 1308 1309 void JsepSessionImpl::CopyBundleTransports() { 1310 for (auto& transceiver : mTransceivers) { 1311 if (transceiver.HasBundleLevel()) { 1312 MOZ_MTLOG(ML_DEBUG, 1313 "[" << mName << "] Transceiver " << transceiver.GetLevel() 1314 << " is in a bundle; transceiver " 1315 << transceiver.BundleLevel() << " owns the transport."); 1316 Maybe<const JsepTransceiver> transportOwner = 1317 GetTransceiverForLevel(transceiver.BundleLevel()); 1318 MOZ_ASSERT(transportOwner); 1319 if (transportOwner) { 1320 transceiver.mTransport = transportOwner->mTransport; 1321 } 1322 } else if (transceiver.HasLevel()) { 1323 MOZ_MTLOG(ML_DEBUG, "[" << mName << "] Transceiver " 1324 << transceiver.GetLevel() 1325 << " is not necessarily in a bundle."); 1326 } 1327 if (transceiver.HasLevel()) { 1328 MOZ_MTLOG(ML_DEBUG, 1329 "[" << mName << "] Transceiver " << transceiver.GetLevel() 1330 << " transport-id: " << transceiver.mTransport.mTransportId 1331 << " components: " << transceiver.mTransport.mComponents); 1332 } 1333 } 1334 } 1335 1336 nsresult JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote, 1337 const SdpAttributeList& answer, 1338 JsepTransport* transport) const { 1339 if (!transport->mComponents) { 1340 return NS_OK; 1341 } 1342 1343 if (!transport->mIce || transport->mIce->mUfrag != remote.GetIceUfrag() || 1344 transport->mIce->mPwd != remote.GetIcePwd()) { 1345 UniquePtr<JsepIceTransport> ice = MakeUnique<JsepIceTransport>(); 1346 transport->mDtls = nullptr; 1347 1348 // We do sanity-checking for these in ParseSdp 1349 ice->mUfrag = remote.GetIceUfrag(); 1350 ice->mPwd = remote.GetIcePwd(); 1351 transport->mIce = std::move(ice); 1352 } 1353 1354 if (remote.HasAttribute(SdpAttribute::kCandidateAttribute)) { 1355 transport->mIce->mCandidates = remote.GetCandidate(); 1356 } 1357 1358 if (!transport->mDtls) { 1359 // RFC 5763 says: 1360 // 1361 // The endpoint MUST use the setup attribute defined in [RFC4145]. 1362 // The endpoint that is the offerer MUST use the setup attribute 1363 // value of setup:actpass and be prepared to receive a client_hello 1364 // before it receives the answer. The answerer MUST use either a 1365 // setup attribute value of setup:active or setup:passive. Note that 1366 // if the answerer uses setup:passive, then the DTLS handshake will 1367 // not begin until the answerer is received, which adds additional 1368 // latency. setup:active allows the answer and the DTLS handshake to 1369 // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever 1370 // party is active MUST initiate a DTLS handshake by sending a 1371 // ClientHello over each flow (host/port quartet). 1372 UniquePtr<JsepDtlsTransport> dtls = MakeUnique<JsepDtlsTransport>(); 1373 dtls->mFingerprints = remote.GetFingerprint(); 1374 if (!answer.HasAttribute(mozilla::SdpAttribute::kSetupAttribute)) { 1375 dtls->mRole = *mIsPendingOfferer ? JsepDtlsTransport::kJsepDtlsServer 1376 : JsepDtlsTransport::kJsepDtlsClient; 1377 } else { 1378 if (*mIsPendingOfferer) { 1379 dtls->mRole = (answer.GetSetup().mRole == SdpSetupAttribute::kActive) 1380 ? JsepDtlsTransport::kJsepDtlsServer 1381 : JsepDtlsTransport::kJsepDtlsClient; 1382 } else { 1383 dtls->mRole = (answer.GetSetup().mRole == SdpSetupAttribute::kActive) 1384 ? JsepDtlsTransport::kJsepDtlsClient 1385 : JsepDtlsTransport::kJsepDtlsServer; 1386 } 1387 } 1388 1389 transport->mDtls = std::move(dtls); 1390 } 1391 1392 if (answer.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) { 1393 transport->mComponents = 1; 1394 } 1395 1396 return NS_OK; 1397 } 1398 1399 nsresult JsepSessionImpl::AddTransportAttributes( 1400 SdpMediaSection* msection, SdpSetupAttribute::Role dtlsRole) { 1401 if (mIceUfrag.empty() || mIcePwd.empty()) { 1402 JSEP_SET_ERROR("Missing ICE ufrag or password"); 1403 return NS_ERROR_FAILURE; 1404 } 1405 1406 SdpAttributeList& attrList = msection->GetAttributeList(); 1407 attrList.SetAttribute( 1408 new SdpStringAttribute(SdpAttribute::kIceUfragAttribute, mIceUfrag)); 1409 attrList.SetAttribute( 1410 new SdpStringAttribute(SdpAttribute::kIcePwdAttribute, mIcePwd)); 1411 1412 msection->GetAttributeList().SetAttribute(new SdpSetupAttribute(dtlsRole)); 1413 1414 return NS_OK; 1415 } 1416 1417 nsresult JsepSessionImpl::CopyPreviousTransportParams( 1418 const Sdp& oldAnswer, const Sdp& offerersPreviousSdp, const Sdp& newOffer, 1419 Sdp* newLocal) { 1420 for (size_t i = 0; i < oldAnswer.GetMediaSectionCount(); ++i) { 1421 if (!mSdpHelper.MsectionIsDisabled(newLocal->GetMediaSection(i)) && 1422 mSdpHelper.AreOldTransportParamsValid(oldAnswer, offerersPreviousSdp, 1423 newOffer, i)) { 1424 // If newLocal is an offer, this will be the number of components we used 1425 // last time, and if it is an answer, this will be the number of 1426 // components we've decided we're using now. 1427 Maybe<const JsepTransceiver> transceiver(GetTransceiverForLevel(i)); 1428 if (!transceiver) { 1429 MOZ_ASSERT(false); 1430 JSEP_SET_ERROR("No transceiver for level " << i); 1431 return NS_ERROR_FAILURE; 1432 } 1433 size_t numComponents = transceiver->mTransport.mComponents; 1434 nsresult rv = mSdpHelper.CopyTransportParams( 1435 numComponents, mCurrentLocalDescription->GetMediaSection(i), 1436 &newLocal->GetMediaSection(i)); 1437 NS_ENSURE_SUCCESS(rv, rv); 1438 } 1439 } 1440 1441 return NS_OK; 1442 } 1443 1444 nsresult JsepSessionImpl::ParseSdp(const std::string& sdp, 1445 UniquePtr<Sdp>* parsedp) { 1446 auto results = mParser->Parse(sdp); 1447 auto parsed = std::move(results->Sdp()); 1448 mLastSdpParsingErrors = results->Errors(); 1449 if (!parsed) { 1450 std::string error = results->ParserName() + " Failed to parse SDP: "; 1451 mSdpHelper.AppendSdpParseErrors(mLastSdpParsingErrors, &error); 1452 JSEP_SET_ERROR(error); 1453 return NS_ERROR_INVALID_ARG; 1454 } 1455 // Verify that the JSEP rules for all SDP are followed 1456 for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) { 1457 if (mSdpHelper.MsectionIsDisabled(parsed->GetMediaSection(i))) { 1458 // Disabled, let this stuff slide. 1459 continue; 1460 } 1461 1462 const SdpMediaSection& msection(parsed->GetMediaSection(i)); 1463 auto& mediaAttrs = msection.GetAttributeList(); 1464 1465 if (mediaAttrs.HasAttribute(SdpAttribute::kMidAttribute) && 1466 mediaAttrs.GetMid().length() > 16) { 1467 JSEP_SET_ERROR( 1468 "Invalid description, mid length greater than 16 " 1469 "unsupported until 2-byte rtp header extensions are " 1470 "supported in webrtc.org"); 1471 return NS_ERROR_INVALID_ARG; 1472 } 1473 1474 if (mediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) { 1475 std::set<uint16_t> extIds; 1476 for (const auto& ext : mediaAttrs.GetExtmap().mExtmaps) { 1477 uint16_t id = ext.entry; 1478 1479 if (id < 1 || id > 14) { 1480 JSEP_SET_ERROR("Description contains invalid extension id " 1481 << id << " on level " << i 1482 << " which is unsupported until 2-byte rtp" 1483 " header extensions are supported in webrtc.org"); 1484 return NS_ERROR_INVALID_ARG; 1485 } 1486 1487 if (extIds.find(id) != extIds.end()) { 1488 JSEP_SET_ERROR("Description contains duplicate extension id " 1489 << id << " on level " << i); 1490 return NS_ERROR_INVALID_ARG; 1491 } 1492 extIds.insert(id); 1493 } 1494 } 1495 1496 static const std::bitset<128> forbidden = GetForbiddenSdpPayloadTypes(); 1497 if (msection.GetMediaType() == SdpMediaSection::kAudio || 1498 msection.GetMediaType() == SdpMediaSection::kVideo) { 1499 // Sanity-check that payload type can work with RTP 1500 for (const std::string& fmt : msection.GetFormats()) { 1501 uint16_t payloadType; 1502 if (!SdpHelper::GetPtAsInt(fmt, &payloadType)) { 1503 JSEP_SET_ERROR("Payload type \"" 1504 << fmt << "\" is not a 16-bit unsigned int at level " 1505 << i); 1506 return NS_ERROR_INVALID_ARG; 1507 } 1508 if (payloadType > 127) { 1509 JSEP_SET_ERROR("audio/video payload type \"" 1510 << fmt << "\" is too large at level " << i); 1511 return NS_ERROR_INVALID_ARG; 1512 } 1513 if (forbidden.test(payloadType)) { 1514 JSEP_SET_ERROR("Illegal audio/video payload type \"" 1515 << fmt << "\" at level " << i); 1516 return NS_ERROR_INVALID_ARG; 1517 } 1518 } 1519 } 1520 } 1521 1522 *parsedp = std::move(parsed); 1523 return NS_OK; 1524 } 1525 1526 nsresult JsepSessionImpl::SetRemoteDescriptionOffer(UniquePtr<Sdp> offer) { 1527 MOZ_ASSERT(mState == kJsepStateStable); 1528 1529 mPendingRemoteDescription = std::move(offer); 1530 mIsPendingOfferer = Some(false); 1531 1532 SetState(kJsepStateHaveRemoteOffer); 1533 return NS_OK; 1534 } 1535 1536 nsresult JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type, 1537 UniquePtr<Sdp> answer) { 1538 MOZ_ASSERT(mState == kJsepStateHaveLocalOffer || 1539 mState == kJsepStateHaveRemotePranswer); 1540 1541 mPendingRemoteDescription = std::move(answer); 1542 1543 nsresult rv = HandleNegotiatedSession(mPendingLocalDescription, 1544 mPendingRemoteDescription); 1545 NS_ENSURE_SUCCESS(rv, rv); 1546 1547 mCurrentRemoteDescription = std::move(mPendingRemoteDescription); 1548 mCurrentLocalDescription = std::move(mPendingLocalDescription); 1549 MOZ_ASSERT(mIsPendingOfferer.isSome() && *mIsPendingOfferer); 1550 mIsPendingOfferer.reset(); 1551 mIsCurrentOfferer = Some(true); 1552 1553 SetState(kJsepStateStable); 1554 return NS_OK; 1555 } 1556 1557 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForLevel( 1558 size_t level) const { 1559 return FindTransceiver([level](const JsepTransceiver& transceiver) { 1560 return transceiver.HasLevel() && (transceiver.GetLevel() == level); 1561 }); 1562 } 1563 1564 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForMid( 1565 const std::string& mid) const { 1566 return FindTransceiver([mid](const JsepTransceiver& transceiver) { 1567 return transceiver.IsAssociated() && (transceiver.GetMid() == mid); 1568 }); 1569 } 1570 1571 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForLocal(size_t level) { 1572 if (Maybe<JsepTransceiver> transceiver = GetTransceiverForLevel(level)) { 1573 if (transceiver->CanRecycleMyMsection() && 1574 transceiver->GetMediaType() != SdpMediaSection::kApplication) { 1575 // Attempt to recycle. If this fails, the old transceiver stays put. 1576 transceiver->Disassociate(); 1577 Maybe<JsepTransceiver> newTransceiver = 1578 FindUnassociatedTransceiver(transceiver->GetMediaType(), false); 1579 if (newTransceiver) { 1580 newTransceiver->SetLevel(level); 1581 transceiver->ClearLevel(); 1582 transceiver->mSendTrack.ClearRids(); 1583 SetTransceiver(*newTransceiver); 1584 SetTransceiver(*transceiver); 1585 return newTransceiver; 1586 } 1587 } 1588 1589 SetTransceiver(*transceiver); 1590 return transceiver; 1591 } 1592 1593 // There is no transceiver for |level| right now. 1594 1595 // Look for an RTP transceiver (spec requires us to give the lower levels to 1596 // new RTP transceivers) 1597 for (auto& transceiver : mTransceivers) { 1598 if (transceiver.GetMediaType() != SdpMediaSection::kApplication && 1599 transceiver.IsFreeToUse()) { 1600 transceiver.SetLevel(level); 1601 return Some(transceiver); 1602 } 1603 } 1604 1605 // Ok, look for a datachannel 1606 for (auto& transceiver : mTransceivers) { 1607 if (transceiver.IsFreeToUse()) { 1608 transceiver.SetLevel(level); 1609 return Some(transceiver); 1610 } 1611 } 1612 1613 return Nothing(); 1614 } 1615 1616 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForRemote( 1617 const SdpMediaSection& msection) { 1618 size_t level = msection.GetLevel(); 1619 Maybe<JsepTransceiver> transceiver = GetTransceiverForLevel(level); 1620 if (transceiver) { 1621 if (!transceiver->CanRecycleMyMsection()) { 1622 return transceiver; 1623 } 1624 transceiver->Disassociate(); 1625 transceiver->ClearLevel(); 1626 transceiver->mSendTrack.ClearRids(); 1627 SetTransceiver(*transceiver); 1628 } 1629 1630 // No transceiver for |level| 1631 transceiver = FindUnassociatedTransceiver(msection.GetMediaType(), true); 1632 if (transceiver) { 1633 transceiver->SetLevel(level); 1634 SetTransceiver(*transceiver); 1635 return transceiver; 1636 } 1637 1638 // Make a new transceiver 1639 JsepTransceiver newTransceiver(msection.GetMediaType(), *mUuidGen, 1640 SdpDirectionAttribute::kRecvonly); 1641 newTransceiver.SetLevel(level); 1642 newTransceiver.SetOnlyExistsBecauseOfSetRemote(true); 1643 AddTransceiver(newTransceiver); 1644 return Some(mTransceivers.back()); 1645 } 1646 1647 Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverWithTransport( 1648 const std::string& transportId) const { 1649 for (const auto& transceiver : mTransceivers) { 1650 if (transceiver.HasOwnTransport() && 1651 (transceiver.mTransport.mTransportId == transportId)) { 1652 MOZ_ASSERT(transceiver.HasLevel(), 1653 "Transceiver has a transport, but no level!"); 1654 return Some(transceiver); 1655 } 1656 } 1657 1658 return Nothing(); 1659 } 1660 1661 nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription( 1662 const Sdp& remote) { 1663 // Iterate over the sdp, updating remote tracks as we go 1664 for (size_t i = 0; i < remote.GetMediaSectionCount(); ++i) { 1665 const SdpMediaSection& msection = remote.GetMediaSection(i); 1666 1667 Maybe<JsepTransceiver> transceiver(GetTransceiverForRemote(msection)); 1668 if (!transceiver) { 1669 return NS_ERROR_FAILURE; 1670 } 1671 1672 if (!mSdpHelper.MsectionIsDisabled(msection)) { 1673 if (msection.GetAttributeList().HasAttribute( 1674 SdpAttribute::kMidAttribute)) { 1675 transceiver->Associate(msection.GetAttributeList().GetMid()); 1676 } 1677 if (!transceiver->IsAssociated()) { 1678 transceiver->Associate(GetNewMid()); 1679 } else { 1680 mUsedMids.insert(transceiver->GetMid()); 1681 } 1682 } else { 1683 // We do not disassociate here, that happens when negotiation completes 1684 // These things cannot be rolled back. 1685 transceiver->mTransport.Close(); 1686 transceiver->SetStopped(); 1687 SetTransceiver(*transceiver); 1688 continue; 1689 } 1690 1691 if (msection.GetMediaType() == SdpMediaSection::MediaType::kApplication) { 1692 SetTransceiver(*transceiver); 1693 continue; 1694 } 1695 1696 transceiver->mSendTrack.SendTrackSetRemote(mSsrcGenerator, msection); 1697 1698 // Interop workaround for endpoints that don't support msid. 1699 // Ensures that there is a default stream id set, provided the remote is 1700 // sending. 1701 // TODO(bug 1426005): Remove this, or at least move it to JsepTrack. 1702 transceiver->mRecvTrack.UpdateStreamIds({mDefaultRemoteStreamId}); 1703 1704 // This will process a=msid if present, or clear the stream ids if the 1705 // msection is not sending. If the msection is sending, and there are no 1706 // a=msid, the previously set default will stay. 1707 transceiver->mRecvTrack.RecvTrackSetRemote(remote, msection); 1708 SetTransceiver(*transceiver); 1709 } 1710 1711 return NS_OK; 1712 } 1713 1714 Maybe<JsepTransceiver> JsepSessionImpl::FindUnassociatedTransceiver( 1715 SdpMediaSection::MediaType type, bool magic) { 1716 // Look through transceivers that are not mapped to an m-section 1717 for (auto& transceiver : mTransceivers) { 1718 if (type == SdpMediaSection::kApplication && 1719 type == transceiver.GetMediaType()) { 1720 transceiver.RestartDatachannelTransceiver(); 1721 return Some(transceiver); 1722 } 1723 if (transceiver.IsFreeToUse() && 1724 (!magic || transceiver.HasAddTrackMagic()) && 1725 (transceiver.GetMediaType() == type)) { 1726 return Some(transceiver); 1727 } 1728 } 1729 1730 return Nothing(); 1731 } 1732 1733 void JsepSessionImpl::RollbackLocalOffer() { 1734 for (size_t i = 0; i < mTransceivers.size(); ++i) { 1735 auto& transceiver = mTransceivers[i]; 1736 if (mOldTransceivers.size() > i) { 1737 transceiver.Rollback(mOldTransceivers[i], false); 1738 mOldTransceivers[i] = transceiver; 1739 continue; 1740 } 1741 1742 JsepTransceiver temp(transceiver.GetMediaType(), *mUuidGen); 1743 InitTransceiver(temp); 1744 transceiver.Rollback(temp, false); 1745 mOldTransceivers.push_back(transceiver); 1746 } 1747 1748 mTransceivers = std::move(mOldTransceivers); 1749 } 1750 1751 void JsepSessionImpl::RollbackRemoteOffer() { 1752 for (size_t i = 0; i < mTransceivers.size(); ++i) { 1753 auto& transceiver = mTransceivers[i]; 1754 if (mOldTransceivers.size() > i) { 1755 // Some stuff cannot be rolled back. Save this information. 1756 transceiver.Rollback(mOldTransceivers[i], true); 1757 mOldTransceivers[i] = transceiver; 1758 continue; 1759 } 1760 1761 if (transceiver.HasLevel()) { 1762 // New transceiver, that was either created by the remote offer, or 1763 // attached to the remote offer. 1764 // We rollback even for transceivers we will remove, just to ensure we end 1765 // up at the starting state. 1766 JsepTransceiver temp(transceiver.GetMediaType(), *mUuidGen); 1767 InitTransceiver(temp); 1768 transceiver.Rollback(temp, true); 1769 1770 if (transceiver.OnlyExistsBecauseOfSetRemote()) { 1771 transceiver.SetStopped(); 1772 transceiver.Disassociate(); 1773 transceiver.SetRemoved(); 1774 } else { 1775 // Oof. This hangs around because of addTrack. Make it magic! 1776 transceiver.SetAddTrackMagic(); 1777 } 1778 } // else, _we_ added this and it is not attached to the remote offer yet 1779 1780 mOldTransceivers.push_back(transceiver); 1781 } 1782 1783 mTransceivers = std::move(mOldTransceivers); 1784 } 1785 1786 nsresult JsepSessionImpl::ValidateLocalDescription(const Sdp& description, 1787 JsepSdpType type) { 1788 Sdp* generated = nullptr; 1789 // TODO(bug 1095226): Better checking. 1790 if (type == kJsepSdpOffer) { 1791 generated = mGeneratedOffer.get(); 1792 } else { 1793 generated = mGeneratedAnswer.get(); 1794 } 1795 1796 if (!generated) { 1797 JSEP_SET_ERROR( 1798 "Calling SetLocal without first calling CreateOffer/Answer" 1799 " is not supported."); 1800 return NS_ERROR_UNEXPECTED; 1801 } 1802 1803 if (description.GetMediaSectionCount() != generated->GetMediaSectionCount()) { 1804 JSEP_SET_ERROR("Changing the number of m-sections is not allowed"); 1805 return NS_ERROR_INVALID_ARG; 1806 } 1807 1808 for (size_t i = 0; i < description.GetMediaSectionCount(); ++i) { 1809 auto& origMsection = generated->GetMediaSection(i); 1810 auto& finalMsection = description.GetMediaSection(i); 1811 if (origMsection.GetMediaType() != finalMsection.GetMediaType()) { 1812 JSEP_SET_ERROR("Changing the media-type of m-sections is not allowed"); 1813 return NS_ERROR_INVALID_ARG; 1814 } 1815 1816 // These will be present in reoffer 1817 if (!mCurrentLocalDescription) { 1818 if (finalMsection.GetAttributeList().HasAttribute( 1819 SdpAttribute::kCandidateAttribute)) { 1820 JSEP_SET_ERROR("Adding your own candidate attributes is not supported"); 1821 return NS_ERROR_INVALID_ARG; 1822 } 1823 1824 if (finalMsection.GetAttributeList().HasAttribute( 1825 SdpAttribute::kEndOfCandidatesAttribute)) { 1826 JSEP_SET_ERROR("Why are you trying to set a=end-of-candidates?"); 1827 return NS_ERROR_INVALID_ARG; 1828 } 1829 } 1830 1831 if (mSdpHelper.MsectionIsDisabled(finalMsection)) { 1832 continue; 1833 } 1834 1835 if (!finalMsection.GetAttributeList().HasAttribute( 1836 SdpAttribute::kMidAttribute)) { 1837 JSEP_SET_ERROR("Local descriptions must have a=mid attributes."); 1838 return NS_ERROR_INVALID_ARG; 1839 } 1840 1841 if (finalMsection.GetAttributeList().GetMid() != 1842 origMsection.GetAttributeList().GetMid()) { 1843 JSEP_SET_ERROR("Changing the mid of m-sections is not allowed."); 1844 return NS_ERROR_INVALID_ARG; 1845 } 1846 1847 // TODO(bug 1095218): Check msid 1848 // TODO(bug 1095226): Check ice-ufrag and ice-pwd 1849 // TODO(bug 1095226): Check fingerprints 1850 // TODO(bug 1095226): Check payload types (at least ensure that payload 1851 // types we don't actually support weren't added) 1852 // TODO(bug 1095226): Check ice-options? 1853 } 1854 1855 if (description.GetAttributeList().HasAttribute( 1856 SdpAttribute::kIceLiteAttribute)) { 1857 JSEP_SET_ERROR("Running ICE in lite mode is unsupported"); 1858 return NS_ERROR_INVALID_ARG; 1859 } 1860 1861 return NS_OK; 1862 } 1863 1864 nsresult JsepSessionImpl::ValidateRemoteDescription(const Sdp& description) { 1865 if (!mCurrentLocalDescription) { 1866 // Initial offer; nothing to validate besides the stuff in ParseSdp 1867 return NS_OK; 1868 } 1869 1870 if (mCurrentLocalDescription->GetMediaSectionCount() > 1871 description.GetMediaSectionCount()) { 1872 JSEP_SET_ERROR( 1873 "New remote description has fewer m-sections than the " 1874 "previous remote description."); 1875 return NS_ERROR_INVALID_ARG; 1876 } 1877 1878 for (size_t i = 0; i < description.GetMediaSectionCount(); ++i) { 1879 const SdpAttributeList& attrs = 1880 description.GetMediaSection(i).GetAttributeList(); 1881 1882 if (attrs.HasAttribute(SdpAttribute::kExtmapAttribute)) { 1883 for (const auto& ext : attrs.GetExtmap().mExtmaps) { 1884 if (mExtmapEntriesEverNegotiated.count(ext.entry) && 1885 mExtmapEntriesEverNegotiated[ext.entry] != ext.extensionname) { 1886 JSEP_SET_ERROR( 1887 "Remote description attempted to remap RTP extension id " 1888 << ext.entry << " from " 1889 << mExtmapEntriesEverNegotiated[ext.entry] << " to " 1890 << ext.extensionname); 1891 return NS_ERROR_INVALID_ARG; 1892 } 1893 } 1894 } 1895 } 1896 1897 if (!mCurrentRemoteDescription) { 1898 // No further checking for initial answers 1899 return NS_OK; 1900 } 1901 1902 // These are solely to check that bundle is valid 1903 SdpHelper::BundledMids bundledMids; 1904 nsresult rv = GetNegotiatedBundledMids(&bundledMids); 1905 NS_ENSURE_SUCCESS(rv, rv); 1906 1907 SdpHelper::BundledMids newBundledMids; 1908 rv = mSdpHelper.GetBundledMids(description, &newBundledMids); 1909 NS_ENSURE_SUCCESS(rv, rv); 1910 1911 // check for partial ice restart, which is not supported 1912 Maybe<bool> iceCredsDiffer; 1913 for (size_t i = 0; i < mCurrentRemoteDescription->GetMediaSectionCount(); 1914 ++i) { 1915 const SdpMediaSection& newMsection = description.GetMediaSection(i); 1916 const SdpMediaSection& oldMsection = 1917 mCurrentRemoteDescription->GetMediaSection(i); 1918 1919 if (mSdpHelper.MsectionIsDisabled(newMsection) || 1920 mSdpHelper.MsectionIsDisabled(oldMsection)) { 1921 continue; 1922 } 1923 1924 if (oldMsection.GetMediaType() != newMsection.GetMediaType()) { 1925 JSEP_SET_ERROR("Remote description changes the media type of m-line " 1926 << i); 1927 return NS_ERROR_INVALID_ARG; 1928 } 1929 1930 bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection); 1931 1932 if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ && 1933 !IsIceRestarting()) { 1934 JSEP_SET_ERROR( 1935 "Remote description indicates ICE restart but offer did not " 1936 "request ICE restart (new remote description changes either " 1937 "the ice-ufrag or ice-pwd)"); 1938 return NS_ERROR_INVALID_ARG; 1939 } 1940 1941 // Detect whether all the creds are the same or all are different 1942 if (!iceCredsDiffer.isSome()) { 1943 // for the first msection capture whether creds are different or same 1944 iceCredsDiffer = mozilla::Some(differ); 1945 } else if (iceCredsDiffer.isSome() && *iceCredsDiffer != differ) { 1946 // subsequent msections must match the first sections 1947 JSEP_SET_ERROR( 1948 "Partial ICE restart is unsupported at this time " 1949 "(new remote description changes either the ice-ufrag " 1950 "or ice-pwd on fewer than all msections)"); 1951 return NS_ERROR_INVALID_ARG; 1952 } 1953 } 1954 1955 return NS_OK; 1956 } 1957 1958 nsresult JsepSessionImpl::ValidateOffer(const Sdp& offer) { 1959 return mSdpHelper.ValidateTransportAttributes(offer, sdp::kOffer); 1960 } 1961 1962 nsresult JsepSessionImpl::ValidateAnswer(const Sdp& offer, const Sdp& answer) { 1963 if (offer.GetMediaSectionCount() != answer.GetMediaSectionCount()) { 1964 JSEP_SET_ERROR("Offer and answer have different number of m-lines " 1965 << "(" << offer.GetMediaSectionCount() << " vs " 1966 << answer.GetMediaSectionCount() << ")"); 1967 return NS_ERROR_INVALID_ARG; 1968 } 1969 1970 nsresult rv = mSdpHelper.ValidateTransportAttributes(answer, sdp::kAnswer); 1971 NS_ENSURE_SUCCESS(rv, rv); 1972 1973 for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) { 1974 const SdpMediaSection& offerMsection = offer.GetMediaSection(i); 1975 const SdpMediaSection& answerMsection = answer.GetMediaSection(i); 1976 1977 if (offerMsection.GetMediaType() != answerMsection.GetMediaType()) { 1978 JSEP_SET_ERROR("Answer and offer have different media types at m-line " 1979 << i); 1980 return NS_ERROR_INVALID_ARG; 1981 } 1982 1983 if (mSdpHelper.MsectionIsDisabled(answerMsection)) { 1984 continue; 1985 } 1986 1987 if (mSdpHelper.MsectionIsDisabled(offerMsection)) { 1988 JSEP_SET_ERROR( 1989 "Answer tried to enable an m-section that was disabled in the offer"); 1990 return NS_ERROR_INVALID_ARG; 1991 } 1992 1993 if (!offerMsection.IsSending() && answerMsection.IsReceiving()) { 1994 JSEP_SET_ERROR("Answer tried to set recv when offer did not set send"); 1995 return NS_ERROR_INVALID_ARG; 1996 } 1997 1998 if (!offerMsection.IsReceiving() && answerMsection.IsSending()) { 1999 JSEP_SET_ERROR("Answer tried to set send when offer did not set recv"); 2000 return NS_ERROR_INVALID_ARG; 2001 } 2002 2003 const SdpAttributeList& answerAttrs(answerMsection.GetAttributeList()); 2004 const SdpAttributeList& offerAttrs(offerMsection.GetAttributeList()); 2005 if (answerAttrs.HasAttribute(SdpAttribute::kMidAttribute) && 2006 offerAttrs.HasAttribute(SdpAttribute::kMidAttribute) && 2007 offerAttrs.GetMid() != answerAttrs.GetMid()) { 2008 JSEP_SET_ERROR("Answer changes mid for level, was \'" 2009 << offerMsection.GetAttributeList().GetMid() 2010 << "\', now \'" 2011 << answerMsection.GetAttributeList().GetMid() << "\'"); 2012 return NS_ERROR_INVALID_ARG; 2013 } 2014 2015 // Sanity check extmap 2016 if (answerAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) { 2017 if (!offerAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) { 2018 JSEP_SET_ERROR("Answer adds extmap attributes to level " << i); 2019 return NS_ERROR_INVALID_ARG; 2020 } 2021 2022 for (const auto& ansExt : answerAttrs.GetExtmap().mExtmaps) { 2023 bool found = false; 2024 for (const auto& offExt : offerAttrs.GetExtmap().mExtmaps) { 2025 if (ansExt.extensionname == offExt.extensionname) { 2026 if ((ansExt.direction & reverse(offExt.direction)) != 2027 ansExt.direction) { 2028 // FIXME we do not return an error here, because Chrome up to 2029 // version 57 is actually tripping over this if they are the 2030 // answerer. See bug 1355010 for details. 2031 MOZ_MTLOG(ML_WARNING, 2032 "[" << mName 2033 << "]: Answer has inconsistent" 2034 " direction on extmap attribute at level " 2035 << i << " (" << ansExt.extensionname 2036 << "). Offer had " << offExt.direction 2037 << ", answer had " << ansExt.direction << "."); 2038 // return NS_ERROR_INVALID_ARG; 2039 } 2040 2041 if (offExt.entry < 4096 && (offExt.entry != ansExt.entry)) { 2042 JSEP_SET_ERROR("Answer changed id for extmap attribute at level " 2043 << i << " (" << offExt.extensionname << ") from " 2044 << offExt.entry << " to " << ansExt.entry << "."); 2045 return NS_ERROR_INVALID_ARG; 2046 } 2047 2048 if (ansExt.entry >= 4096) { 2049 JSEP_SET_ERROR("Answer used an invalid id (" 2050 << ansExt.entry 2051 << ") for extmap attribute at level " << i << " (" 2052 << ansExt.extensionname << ")."); 2053 return NS_ERROR_INVALID_ARG; 2054 } 2055 2056 found = true; 2057 break; 2058 } 2059 } 2060 2061 if (!found) { 2062 JSEP_SET_ERROR("Answer has extmap " 2063 << ansExt.extensionname 2064 << " at " 2065 "level " 2066 << i << " that was not present in offer."); 2067 return NS_ERROR_INVALID_ARG; 2068 } 2069 } 2070 } 2071 } 2072 2073 return NS_OK; 2074 } 2075 2076 nsresult JsepSessionImpl::CreateGenericSDP(UniquePtr<Sdp>* sdpp) { 2077 // draft-ietf-rtcweb-jsep-08 Section 5.2.1: 2078 // o The second SDP line MUST be an "o=" line, as specified in 2079 // [RFC4566], Section 5.2. The value of the <username> field SHOULD 2080 // be "-". The value of the <sess-id> field SHOULD be a 2081 // cryptographically random number. To ensure uniqueness, this 2082 // number SHOULD be at least 64 bits long. The value of the <sess- 2083 // version> field SHOULD be zero. The value of the <nettype> 2084 // <addrtype> <unicast-address> tuple SHOULD be set to a non- 2085 // meaningful address, such as IN IP4 0.0.0.0, to prevent leaking the 2086 // local address in this field. As mentioned in [RFC4566], the 2087 // entire o= line needs to be unique, but selecting a random number 2088 // for <sess-id> is sufficient to accomplish this. 2089 // 2090 // Historical note: we used to report the actual version number here, after 2091 // "SDPARTA-", but that becomes a problem starting with version 100, since 2092 // some services parse 100 as "10" and give us legacy/broken behavior. So 2093 // we're freezing the version number at 99.0 in this string. 2094 auto origin = SdpOrigin("mozilla...THIS_IS_SDPARTA-99.0", mSessionId, 2095 mSessionVersion, sdp::kIPv4, "0.0.0.0"); 2096 2097 UniquePtr<Sdp> sdp = MakeUnique<SipccSdp>(origin); 2098 2099 if (mDtlsFingerprints.empty()) { 2100 JSEP_SET_ERROR("Missing DTLS fingerprint"); 2101 return NS_ERROR_FAILURE; 2102 } 2103 2104 UniquePtr<SdpFingerprintAttributeList> fpl = 2105 MakeUnique<SdpFingerprintAttributeList>(); 2106 for (auto& dtlsFingerprint : mDtlsFingerprints) { 2107 fpl->PushEntry(dtlsFingerprint.mAlgorithm, dtlsFingerprint.mValue); 2108 } 2109 sdp->GetAttributeList().SetAttribute(fpl.release()); 2110 2111 auto* iceOpts = new SdpOptionsAttribute(SdpAttribute::kIceOptionsAttribute); 2112 iceOpts->PushEntry("trickle"); 2113 sdp->GetAttributeList().SetAttribute(iceOpts); 2114 2115 // This assumes content doesn't add a bunch of msid attributes with a 2116 // different semantic in mind. 2117 std::vector<std::string> msids; 2118 msids.push_back("*"); 2119 mSdpHelper.SetupMsidSemantic(msids, sdp.get()); 2120 2121 *sdpp = std::move(sdp); 2122 return NS_OK; 2123 } 2124 2125 nsresult JsepSessionImpl::SetupIds() { 2126 SECStatus rv = PK11_GenerateRandom( 2127 reinterpret_cast<unsigned char*>(&mSessionId), sizeof(mSessionId)); 2128 // RFC 3264 says that session-ids MUST be representable as a _signed_ 2129 // 64 bit number, meaning the MSB cannot be set. 2130 mSessionId = mSessionId >> 1; 2131 if (rv != SECSuccess) { 2132 JSEP_SET_ERROR("Failed to generate session id: " << rv); 2133 return NS_ERROR_FAILURE; 2134 } 2135 2136 if (!mUuidGen->Generate(&mDefaultRemoteStreamId)) { 2137 JSEP_SET_ERROR("Failed to generate default uuid for streams"); 2138 return NS_ERROR_FAILURE; 2139 } 2140 2141 if (!mUuidGen->Generate(&mCNAME)) { 2142 JSEP_SET_ERROR("Failed to generate CNAME"); 2143 return NS_ERROR_FAILURE; 2144 } 2145 2146 return NS_OK; 2147 } 2148 2149 void JsepSessionImpl::SetDefaultCodecs( 2150 const std::vector<UniquePtr<JsepCodecDescription>>& aPreferredCodecs) { 2151 mSupportedCodecs.clear(); 2152 2153 for (const auto& codec : aPreferredCodecs) { 2154 mSupportedCodecs.emplace_back(codec->Clone()); 2155 } 2156 } 2157 2158 void JsepSessionImpl::SetState(JsepSignalingState state) { 2159 if (state == mState) return; 2160 2161 MOZ_MTLOG(ML_NOTICE, "[" << mName << "]: " << GetStateStr(mState) << " -> " 2162 << GetStateStr(state)); 2163 mState = state; 2164 } 2165 2166 JsepSession::Result JsepSessionImpl::AddRemoteIceCandidate( 2167 const std::string& candidate, const std::string& mid, 2168 const Maybe<uint16_t>& level, const std::string& ufrag, 2169 std::string* transportId) { 2170 mLastError.clear(); 2171 if (!mCurrentRemoteDescription && !mPendingRemoteDescription) { 2172 JSEP_SET_ERROR("Cannot add ICE candidate when there is no remote SDP"); 2173 return dom::PCError::InvalidStateError; 2174 } 2175 2176 if (mid.empty() && !level.isSome() && candidate.empty()) { 2177 // Set end-of-candidates on SDP 2178 if (mCurrentRemoteDescription) { 2179 nsresult rv = mSdpHelper.SetIceGatheringComplete( 2180 mCurrentRemoteDescription.get(), ufrag); 2181 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 2182 } 2183 2184 if (mPendingRemoteDescription) { 2185 // If we had an error when adding the candidate to the current 2186 // description, we stomp them here. This is deliberate. 2187 nsresult rv = mSdpHelper.SetIceGatheringComplete( 2188 mPendingRemoteDescription.get(), ufrag); 2189 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 2190 } 2191 return Result(); 2192 } 2193 2194 Maybe<JsepTransceiver> transceiver; 2195 if (!mid.empty()) { 2196 transceiver = GetTransceiverForMid(mid); 2197 } else if (level.isSome()) { 2198 transceiver = GetTransceiverForLevel(level.value()); 2199 } 2200 2201 if (!transceiver) { 2202 JSEP_SET_ERROR("Cannot set ICE candidate for level=" 2203 << level << " mid=" << mid << ": No such transceiver."); 2204 return dom::PCError::OperationError; 2205 } 2206 2207 if (level.isSome() && transceiver->GetLevel() != level.value()) { 2208 MOZ_MTLOG(ML_WARNING, "Mismatch between mid and level - \"" 2209 << mid << "\" is not the mid for level " 2210 << level); 2211 } 2212 2213 *transportId = transceiver->mTransport.mTransportId; 2214 nsresult rv = NS_ERROR_UNEXPECTED; 2215 if (mCurrentRemoteDescription) { 2216 rv = 2217 mSdpHelper.AddCandidateToSdp(mCurrentRemoteDescription.get(), candidate, 2218 transceiver->GetLevel(), ufrag); 2219 } 2220 2221 if (mPendingRemoteDescription) { 2222 // If we had an error when adding the candidate to the current description, 2223 // we stomp them here. This is deliberate. 2224 rv = 2225 mSdpHelper.AddCandidateToSdp(mPendingRemoteDescription.get(), candidate, 2226 transceiver->GetLevel(), ufrag); 2227 } 2228 2229 NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError); 2230 return Result(); 2231 } 2232 2233 nsresult JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate, 2234 const std::string& transportId, 2235 const std::string& ufrag, 2236 uint16_t* level, 2237 std::string* mid, 2238 bool* skipped) { 2239 mLastError.clear(); 2240 *skipped = true; 2241 if (!mCurrentLocalDescription && !mPendingLocalDescription) { 2242 JSEP_SET_ERROR("Cannot add ICE candidate when there is no local SDP"); 2243 return NS_ERROR_UNEXPECTED; 2244 } 2245 2246 Maybe<const JsepTransceiver> transceiver = 2247 GetTransceiverWithTransport(transportId); 2248 if (!transceiver || !transceiver->IsAssociated()) { 2249 // mainly here to make some testing less complicated, but also just in case 2250 return NS_OK; 2251 } 2252 2253 *level = transceiver->GetLevel(); 2254 *mid = transceiver->GetMid(); 2255 2256 nsresult rv = NS_ERROR_INVALID_ARG; 2257 if (mCurrentLocalDescription) { 2258 rv = mSdpHelper.AddCandidateToSdp(mCurrentLocalDescription.get(), candidate, 2259 *level, ufrag); 2260 } 2261 2262 if (mPendingLocalDescription) { 2263 // If we had an error when adding the candidate to the current description, 2264 // we stomp them here. This is deliberate. 2265 rv = mSdpHelper.AddCandidateToSdp(mPendingLocalDescription.get(), candidate, 2266 *level, ufrag); 2267 } 2268 2269 *skipped = false; 2270 return rv; 2271 } 2272 2273 nsresult JsepSessionImpl::UpdateDefaultCandidate( 2274 const std::string& defaultCandidateAddr, uint16_t defaultCandidatePort, 2275 const std::string& defaultRtcpCandidateAddr, 2276 uint16_t defaultRtcpCandidatePort, const std::string& transportId) { 2277 mLastError.clear(); 2278 2279 mozilla::Sdp* sdp = 2280 GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent); 2281 2282 if (!sdp) { 2283 JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState)); 2284 return NS_ERROR_UNEXPECTED; 2285 } 2286 2287 for (const auto& transceiver : mTransceivers) { 2288 // We set the default address for bundled m-sections, but not candidate 2289 // attributes. Ugh. 2290 if (transceiver.mTransport.mTransportId == transportId) { 2291 MOZ_ASSERT(transceiver.HasLevel(), 2292 "Transceiver has a transport, but no level! " 2293 "This should never happen."); 2294 std::string defaultRtcpCandidateAddrCopy(defaultRtcpCandidateAddr); 2295 if (mState == kJsepStateStable) { 2296 if (transceiver.mTransport.mComponents == 1) { 2297 // We know we're doing rtcp-mux by now. Don't create an rtcp attr. 2298 defaultRtcpCandidateAddrCopy = ""; 2299 defaultRtcpCandidatePort = 0; 2300 } 2301 } 2302 2303 size_t level = transceiver.GetLevel(); 2304 if (level >= sdp->GetMediaSectionCount()) { 2305 MOZ_ASSERT(false, "Transceiver's level is too large!"); 2306 JSEP_SET_ERROR("Transceiver's level is too large!"); 2307 return NS_ERROR_FAILURE; 2308 } 2309 2310 auto& msection = sdp->GetMediaSection(level); 2311 2312 // Do not add default candidate to a bundle-only m-section, sinice that 2313 // might confuse endpoints that do not support bundle-only. 2314 if (!msection.GetAttributeList().HasAttribute( 2315 SdpAttribute::kBundleOnlyAttribute)) { 2316 mSdpHelper.SetDefaultAddresses( 2317 defaultCandidateAddr, defaultCandidatePort, 2318 defaultRtcpCandidateAddrCopy, defaultRtcpCandidatePort, &msection); 2319 } 2320 } 2321 } 2322 2323 return NS_OK; 2324 } 2325 2326 nsresult JsepSessionImpl::GetNegotiatedBundledMids( 2327 SdpHelper::BundledMids* bundledMids) { 2328 const Sdp* answerSdp = GetAnswer(); 2329 2330 if (!answerSdp) { 2331 return NS_OK; 2332 } 2333 2334 return mSdpHelper.GetBundledMids(*answerSdp, bundledMids); 2335 } 2336 2337 mozilla::Sdp* JsepSessionImpl::GetParsedLocalDescription( 2338 JsepDescriptionPendingOrCurrent type) const { 2339 if (type == kJsepDescriptionPending) { 2340 return mPendingLocalDescription.get(); 2341 } else if (mPendingLocalDescription && 2342 type == kJsepDescriptionPendingOrCurrent) { 2343 return mPendingLocalDescription.get(); 2344 } 2345 return mCurrentLocalDescription.get(); 2346 } 2347 2348 mozilla::Sdp* JsepSessionImpl::GetParsedRemoteDescription( 2349 JsepDescriptionPendingOrCurrent type) const { 2350 if (type == kJsepDescriptionPending) { 2351 return mPendingRemoteDescription.get(); 2352 } else if (mPendingRemoteDescription && 2353 type == kJsepDescriptionPendingOrCurrent) { 2354 return mPendingRemoteDescription.get(); 2355 } 2356 return mCurrentRemoteDescription.get(); 2357 } 2358 2359 const Sdp* JsepSessionImpl::GetAnswer() const { 2360 return (mIsCurrentOfferer.isSome() && *mIsCurrentOfferer) 2361 ? mCurrentRemoteDescription.get() 2362 : mCurrentLocalDescription.get(); 2363 } 2364 2365 void JsepSessionImpl::SetIceRestarting(bool restarting) { 2366 if (restarting) { 2367 // not restarting -> restarting 2368 if (!IsIceRestarting()) { 2369 // We don't set this more than once, so the old ufrag/pwd is preserved 2370 // even if we CreateOffer({iceRestart:true}) multiple times in a row. 2371 mOldIceUfrag = mIceUfrag; 2372 mOldIcePwd = mIcePwd; 2373 } 2374 mIceUfrag = GetRandomHex(1); 2375 mIcePwd = GetRandomHex(4); 2376 } else if (IsIceRestarting()) { 2377 // restarting -> not restarting, restore old ufrag/pwd 2378 mIceUfrag = mOldIceUfrag; 2379 mIcePwd = mOldIcePwd; 2380 mOldIceUfrag.clear(); 2381 mOldIcePwd.clear(); 2382 } 2383 } 2384 2385 nsresult JsepSessionImpl::Close() { 2386 mLastError.clear(); 2387 SetState(kJsepStateClosed); 2388 return NS_OK; 2389 } 2390 2391 const std::string JsepSessionImpl::GetLastError() const { return mLastError; } 2392 2393 const std::vector<std::pair<size_t, std::string>>& 2394 JsepSessionImpl::GetLastSdpParsingErrors() const { 2395 return mLastSdpParsingErrors; 2396 } 2397 2398 bool JsepSessionImpl::CheckNegotiationNeeded() const { 2399 MOZ_ASSERT(mState == kJsepStateStable); 2400 2401 for (const auto& transceiver : mTransceivers) { 2402 if (transceiver.IsStopped()) { 2403 // Nothing to do with this 2404 continue; 2405 } 2406 2407 if (transceiver.IsStopping()) { 2408 MOZ_MTLOG(ML_DEBUG, "[" << mName 2409 << "]: Negotiation needed because of " 2410 "transceiver we need to stop"); 2411 return true; 2412 } 2413 2414 if (!transceiver.IsAssociated()) { 2415 MOZ_MTLOG(ML_DEBUG, "[" << mName 2416 << "]: Negotiation needed because of " 2417 "transceiver we need to associate."); 2418 return true; 2419 } 2420 2421 MOZ_ASSERT(transceiver.IsAssociated() && !transceiver.IsStopping() && 2422 !transceiver.IsStopped()); 2423 2424 if (!mCurrentLocalDescription || !mCurrentRemoteDescription) { 2425 MOZ_CRASH( 2426 "Transceivers should not be associated if we're in stable " 2427 "before the first negotiation."); 2428 continue; 2429 } 2430 2431 if (!transceiver.HasLevel()) { 2432 MOZ_CRASH("Associated transceivers should always have a level."); 2433 continue; 2434 } 2435 2436 if (transceiver.GetMediaType() == SdpMediaSection::kApplication) { 2437 continue; 2438 } 2439 2440 size_t level = transceiver.GetLevel(); 2441 if (NS_WARN_IF(mCurrentLocalDescription->GetMediaSectionCount() <= level) || 2442 NS_WARN_IF(mCurrentRemoteDescription->GetMediaSectionCount() <= 2443 level)) { 2444 MOZ_ASSERT(false); 2445 continue; 2446 } 2447 2448 const SdpMediaSection& local = 2449 mCurrentLocalDescription->GetMediaSection(level); 2450 const SdpMediaSection& remote = 2451 mCurrentRemoteDescription->GetMediaSection(level); 2452 2453 if (transceiver.mJsDirection & sdp::kSend) { 2454 std::vector<std::string> sdpMsids; 2455 if (local.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) { 2456 for (const auto& msidAttr : local.GetAttributeList().GetMsid().mMsids) { 2457 if (msidAttr.identifier != "-") { 2458 sdpMsids.push_back(msidAttr.identifier); 2459 } 2460 } 2461 } 2462 std::sort(sdpMsids.begin(), sdpMsids.end()); 2463 2464 std::vector<std::string> jsepMsids; 2465 for (const auto& jsepMsid : transceiver.mSendTrack.GetStreamIds()) { 2466 jsepMsids.push_back(jsepMsid); 2467 } 2468 std::sort(jsepMsids.begin(), jsepMsids.end()); 2469 2470 if (!std::equal(sdpMsids.begin(), sdpMsids.end(), jsepMsids.begin(), 2471 jsepMsids.end())) { 2472 MOZ_MTLOG(ML_DEBUG, 2473 "[" << mName 2474 << "]: Negotiation needed because transceiver " 2475 "is sending, and the local SDP has different " 2476 "msids than the send track"); 2477 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SDP msids = ["); 2478 for (const auto& msid : sdpMsids) { 2479 MOZ_MTLOG(ML_DEBUG, msid << ", "); 2480 } 2481 MOZ_MTLOG(ML_DEBUG, "]"); 2482 MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: JSEP msids = ["); 2483 for (const auto& msid : jsepMsids) { 2484 MOZ_MTLOG(ML_DEBUG, msid << ", "); 2485 } 2486 MOZ_MTLOG(ML_DEBUG, "]"); 2487 return true; 2488 } 2489 } 2490 2491 if (mIsCurrentOfferer.isSome() && *mIsCurrentOfferer) { 2492 if ((local.GetDirection() != transceiver.mJsDirection) && 2493 reverse(remote.GetDirection()) != transceiver.mJsDirection) { 2494 MOZ_MTLOG(ML_DEBUG, "[" << mName 2495 << "]: Negotiation needed because " 2496 "the direction on our offer, and the remote " 2497 "answer, does not " 2498 "match the direction on a transceiver."); 2499 return true; 2500 } 2501 } else if (local.GetDirection() != 2502 (transceiver.mJsDirection & reverse(remote.GetDirection()))) { 2503 MOZ_MTLOG( 2504 ML_DEBUG, 2505 "[" << mName 2506 << "]: Negotiation needed because " 2507 "the direction on our answer doesn't match the direction on a " 2508 "transceiver, even though the remote offer would have allowed " 2509 "it."); 2510 return true; 2511 } 2512 } 2513 2514 return false; 2515 } 2516 2517 } // namespace mozilla