jsep_session_unittest.cpp (302286B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <iostream> 8 #include <map> 9 10 #include "nss.h" 11 #include "ssl.h" 12 13 #include "mozilla/Preferences.h" 14 15 #define GTEST_HAS_RTTI 0 16 #include "gmock/gmock.h" 17 #include "gtest/gtest.h" 18 19 #include "CodecConfig.h" 20 #include "PeerConnectionImpl.h" 21 #include "sdp/SdpMediaSection.h" 22 #include "sdp/SipccSdpParser.h" 23 #include "jsep/JsepTrack.h" 24 #include "jsep/JsepSession.h" 25 #include "jsep/JsepSessionImpl.h" 26 27 using testing::ElementsAre; 28 using testing::Pair; 29 using testing::UnorderedElementsAre; 30 31 namespace mozilla { 32 MOZ_RUNINIT static std::string kAEqualsCandidate("a=candidate:"); 33 const static size_t kNumCandidatesPerComponent = 3; 34 35 class JsepSessionTestBase : public ::testing::Test { 36 public: 37 static void SetUpTestCase() { 38 NSS_NoDB_Init(nullptr); 39 NSS_SetDomesticPolicy(); 40 } 41 }; 42 43 class FakeUuidGenerator : public mozilla::JsepUuidGenerator { 44 public: 45 bool Generate(std::string* str) { 46 std::ostringstream os; 47 os << "FAKE_UUID_" << ++ctr; 48 *str = os.str(); 49 50 return true; 51 } 52 53 mozilla::JsepUuidGenerator* Clone() const { 54 return new FakeUuidGenerator(*this); 55 } 56 57 private: 58 static uint64_t ctr; 59 }; 60 61 uint64_t FakeUuidGenerator::ctr = 1000; 62 63 class JsepSessionTest : public JsepSessionTestBase, 64 public ::testing::WithParamInterface<std::string> { 65 public: 66 JsepSessionTest() : mSdpHelper(&mLastError) { 67 Preferences::SetCString("media.peerconnection.sdp.parser", "legacy"); 68 Preferences::SetCString("media.peerconnection.sdp.alternate_parse_mode", 69 "never"); 70 Preferences::SetBool("media.peerconnection.video.use_rtx", true); 71 Preferences::SetBool("media.navigator.video.use_transport_cc", true); 72 Preferences::SetBool("media.navigator.video.use_remb", true); 73 Preferences::SetBool("media.navigator.video.disable_h264_baseline", false); 74 Preferences::SetBool("media.webrtc.codec.video.av1.enabled", true); 75 Preferences::SetBool("media.navigator.audio.use_fec", false); 76 77 mSessionOff = 78 MakeUnique<JsepSessionImpl>("Offerer", MakeUnique<FakeUuidGenerator>()); 79 mSessionAns = MakeUnique<JsepSessionImpl>("Answerer", 80 MakeUnique<FakeUuidGenerator>()); 81 82 EXPECT_EQ(NS_OK, mSessionOff->Init()); 83 EXPECT_EQ(NS_OK, mSessionAns->Init()); 84 85 std::vector<UniquePtr<JsepCodecDescription>> preferredCodecs; 86 PeerConnectionImpl::SetupPreferredCodecs(preferredCodecs); 87 for (auto& codec : preferredCodecs) { 88 // Make H264 P0 recvonly everywhere for better test coverage. 89 // TODO: For unit testing JSEP, the preferred codecs list should be 90 // defined by the tests, not rely on Gecko runtime behavior. 91 if (codec->mName == "H264") { 92 auto* video = static_cast<JsepVideoCodecDescription*>(codec.get()); 93 if (video->mPacketizationMode == 0) { 94 video->mSupportedDirection = sdp::kRecv; 95 } 96 } 97 } 98 mSessionOff->SetDefaultCodecs(preferredCodecs); 99 mSessionAns->SetDefaultCodecs(preferredCodecs); 100 101 std::vector<PeerConnectionImpl::RtpExtensionHeader> preferredHeaders; 102 PeerConnectionImpl::SetupPreferredRtpExtensions(preferredHeaders); 103 104 for (const auto& header : preferredHeaders) { 105 mSessionOff->AddRtpExtension(header.mMediaType, header.extensionname, 106 header.direction); 107 mSessionAns->AddRtpExtension(header.mMediaType, header.extensionname, 108 header.direction); 109 } 110 111 mOffererTransport = MakeUnique<TransportData>(); 112 mAnswererTransport = MakeUnique<TransportData>(); 113 114 AddTransportData(*mSessionOff, *mOffererTransport); 115 AddTransportData(*mSessionAns, *mAnswererTransport); 116 117 mOffCandidates = MakeUnique<CandidateSet>(); 118 mAnsCandidates = MakeUnique<CandidateSet>(); 119 } 120 121 static std::vector<JsepTransceiver>& GetTransceivers(JsepSession& aSession) { 122 return aSession.GetTransceivers(); 123 } 124 125 static const std::vector<JsepTransceiver>& GetTransceivers( 126 const JsepSession& aSession) { 127 return aSession.GetTransceivers(); 128 } 129 130 protected: 131 struct TransportData { 132 std::map<nsCString, std::vector<uint8_t>> mFingerprints; 133 }; 134 135 void AddDtlsFingerprint(const nsCString& alg, JsepSessionImpl& session, 136 TransportData& tdata) { 137 std::vector<uint8_t> fp; 138 fp.assign((alg == "sha-1") ? 20 : 32, 139 (session.GetName() == "Offerer") ? 0x4f : 0x41); 140 session.AddDtlsFingerprint(alg, fp); 141 tdata.mFingerprints[alg] = fp; 142 } 143 144 void AddTransportData(JsepSessionImpl& session, TransportData& tdata) { 145 AddDtlsFingerprint("sha-1"_ns, session, tdata); 146 AddDtlsFingerprint("sha-256"_ns, session, tdata); 147 } 148 149 void CheckTransceiverInvariants( 150 const std::vector<JsepTransceiver>& oldTransceivers, 151 const std::vector<JsepTransceiver>& newTransceivers) { 152 ASSERT_LE(oldTransceivers.size(), newTransceivers.size()); 153 std::set<size_t> levels; 154 155 for (const auto& newTransceiver : newTransceivers) { 156 if (newTransceiver.HasLevel()) { 157 ASSERT_FALSE(levels.count(newTransceiver.GetLevel())) 158 << "Two new transceivers are mapped to level " 159 << newTransceiver.GetLevel(); 160 levels.insert(newTransceiver.GetLevel()); 161 } 162 } 163 164 auto last = levels.rbegin(); 165 if (last != levels.rend()) { 166 ASSERT_LE(*last, levels.size()) 167 << "Max level observed in transceivers was " << *last 168 << ", but there are only " << levels.size() 169 << " levels in the " 170 "transceivers."; 171 } 172 173 for (const auto& oldTransceiver : oldTransceivers) { 174 if (oldTransceiver.HasLevel()) { 175 ASSERT_TRUE(levels.count(oldTransceiver.GetLevel())) 176 << "Level " << oldTransceiver.GetLevel() 177 << " had a transceiver in the old, but not the new (or, " 178 "perhaps this level had more than one transceiver in the " 179 "old)"; 180 levels.erase(oldTransceiver.GetLevel()); 181 } 182 } 183 } 184 185 std::string CreateOffer(const Maybe<JsepOfferOptions>& options = Nothing()) { 186 std::vector<JsepTransceiver> transceiversBefore = 187 GetTransceivers(*mSessionOff); 188 JsepOfferOptions defaultOptions; 189 const JsepOfferOptions& optionsRef = options ? *options : defaultOptions; 190 std::string offer; 191 JsepSession::Result result = mSessionOff->CreateOffer(optionsRef, &offer); 192 EXPECT_FALSE(result.mError.isSome()) << mSessionOff->GetLastError(); 193 194 std::cerr << "OFFER: " << offer << std::endl; 195 196 ValidateTransport(*mOffererTransport, offer, sdp::kOffer); 197 198 if (transceiversBefore.size() != GetTransceivers(*mSessionOff).size()) { 199 EXPECT_TRUE(false) << "CreateOffer changed number of transceivers!"; 200 return offer; 201 } 202 203 CheckTransceiverInvariants(transceiversBefore, 204 GetTransceivers(*mSessionOff)); 205 206 for (size_t i = 0; i < transceiversBefore.size(); ++i) { 207 JsepTransceiver oldTransceiver = transceiversBefore[i]; 208 JsepTransceiver newTransceiver = GetTransceivers(*mSessionOff)[i]; 209 EXPECT_EQ(oldTransceiver.IsStopped(), newTransceiver.IsStopped()); 210 211 if (oldTransceiver.IsStopped()) { 212 if (!newTransceiver.HasLevel()) { 213 // Tolerate unmapping of stopped transceivers by removing this 214 // difference. 215 oldTransceiver.ClearLevel(); 216 } 217 } else if (!oldTransceiver.HasLevel()) { 218 EXPECT_TRUE(newTransceiver.HasLevel()); 219 // Tolerate new mappings. 220 oldTransceiver.SetLevel(newTransceiver.GetLevel()); 221 } 222 223 EXPECT_TRUE(Equals(oldTransceiver, newTransceiver)); 224 } 225 226 return offer; 227 } 228 229 typedef enum { NO_ADDTRACK_MAGIC, ADDTRACK_MAGIC } AddTrackMagic; 230 231 void AddTracks(JsepSessionImpl& side, AddTrackMagic magic = ADDTRACK_MAGIC) { 232 // Add tracks. 233 if (types.empty()) { 234 types = BuildTypes(GetParam()); 235 } 236 AddTracks(side, types, magic); 237 238 // Now, we move datachannel to the end 239 auto it = 240 std::find(types.begin(), types.end(), SdpMediaSection::kApplication); 241 if (it != types.end()) { 242 types.erase(it); 243 types.push_back(SdpMediaSection::kApplication); 244 } 245 } 246 247 void AddTracks(JsepSessionImpl& side, const std::string& mediatypes, 248 AddTrackMagic magic = ADDTRACK_MAGIC) { 249 AddTracks(side, BuildTypes(mediatypes), magic); 250 } 251 252 JsepTrack RemoveTrack(JsepSession& side, size_t index) { 253 if (GetTransceivers(side).size() <= index) { 254 EXPECT_TRUE(false) << "Index " << index << " out of bounds!"; 255 return JsepTrack(SdpMediaSection::kAudio, sdp::kSend); 256 } 257 258 JsepTransceiver transceiver(GetTransceivers(side)[index]); 259 JsepTrack& track = transceiver.mSendTrack; 260 EXPECT_FALSE(track.GetStreamIds().empty()) << "No track at index " << index; 261 262 JsepTrack original(track); 263 track.ClearStreamIds(); 264 transceiver.mJsDirection &= SdpDirectionAttribute::Direction::kRecvonly; 265 side.SetTransceiver(transceiver); 266 return original; 267 } 268 269 void SetDirection(JsepSession& side, size_t index, 270 SdpDirectionAttribute::Direction direction) { 271 ASSERT_LT(index, GetTransceivers(side).size()) 272 << "Index " << index << " out of bounds!"; 273 274 auto transceiver = GetTransceivers(side)[index]; 275 transceiver.mJsDirection = direction; 276 side.SetTransceiver(transceiver); 277 } 278 279 std::vector<SdpMediaSection::MediaType> BuildTypes( 280 const std::string& mediatypes) { 281 std::vector<SdpMediaSection::MediaType> result; 282 size_t ptr = 0; 283 284 for (;;) { 285 size_t comma = mediatypes.find(',', ptr); 286 std::string chunk = mediatypes.substr(ptr, comma - ptr); 287 288 if (chunk == "audio") { 289 result.push_back(SdpMediaSection::kAudio); 290 } else if (chunk == "video") { 291 result.push_back(SdpMediaSection::kVideo); 292 } else if (chunk == "datachannel") { 293 result.push_back(SdpMediaSection::kApplication); 294 } else { 295 MOZ_CRASH(); 296 } 297 298 if (comma == std::string::npos) break; 299 ptr = comma + 1; 300 } 301 302 return result; 303 } 304 305 void AddTracks(JsepSessionImpl& side, 306 const std::vector<SdpMediaSection::MediaType>& mediatypes, 307 AddTrackMagic magic = ADDTRACK_MAGIC) { 308 std::string stream_id; 309 std::string track_id; 310 311 ASSERT_TRUE(mUuidGen.Generate(&stream_id)); 312 313 AddTracksToStream(side, stream_id, mediatypes, magic); 314 } 315 316 void AddTracksToStream(JsepSessionImpl& side, const std::string stream_id, 317 const std::string& mediatypes, 318 AddTrackMagic magic = ADDTRACK_MAGIC) { 319 AddTracksToStream(side, stream_id, BuildTypes(mediatypes), magic); 320 } 321 322 // A bit of a hack. JsepSessionImpl populates the track-id automatically, just 323 // in case, because the w3c spec requires msid to be set even when there's no 324 // send track. 325 bool IsNull(const JsepTrack& track) const { 326 return track.GetStreamIds().empty() && 327 (track.GetMediaType() != SdpMediaSection::MediaType::kApplication); 328 } 329 330 void AddTracksToStream( 331 JsepSessionImpl& side, const std::string stream_id, 332 const std::vector<SdpMediaSection::MediaType>& mediatypes, 333 AddTrackMagic magic = ADDTRACK_MAGIC) 334 335 { 336 std::string track_id; 337 338 for (auto type : mediatypes) { 339 ASSERT_TRUE(mUuidGen.Generate(&track_id)); 340 341 Maybe<JsepTransceiver> suitableTransceiver; 342 size_t i; 343 if (magic == ADDTRACK_MAGIC) { 344 // We're simulating addTrack. 345 for (i = 0; i < GetTransceivers(side).size(); ++i) { 346 auto transceiver = GetTransceivers(side)[i]; 347 if (transceiver.mSendTrack.GetMediaType() != type) { 348 continue; 349 } 350 351 if (IsNull(transceiver.mSendTrack) || 352 transceiver.GetMediaType() == SdpMediaSection::kApplication) { 353 suitableTransceiver = Some(transceiver); 354 break; 355 } 356 } 357 } 358 359 if (!suitableTransceiver) { 360 i = GetTransceivers(side).size(); 361 side.AddTransceiver(JsepTransceiver(type, mUuidGen)); 362 suitableTransceiver = Some(GetTransceivers(side).back()); 363 if (magic == ADDTRACK_MAGIC) { 364 suitableTransceiver->SetAddTrackMagic(); 365 } 366 } 367 368 std::cerr << "Updating send track for transceiver " << i << std::endl; 369 suitableTransceiver->SetOnlyExistsBecauseOfSetRemote(false); 370 suitableTransceiver->mJsDirection |= 371 SdpDirectionAttribute::Direction::kSendonly; 372 suitableTransceiver->mSendTrack.UpdateStreamIds( 373 std::vector<std::string>(1, stream_id)); 374 side.SetTransceiver(*suitableTransceiver); 375 } 376 } 377 378 bool HasMediaStream(const std::vector<JsepTrack>& tracks) const { 379 for (const auto& track : tracks) { 380 if (track.GetMediaType() != SdpMediaSection::kApplication) { 381 return true; 382 } 383 } 384 return false; 385 } 386 387 const std::string GetFirstLocalStreamId(JsepSessionImpl& side) const { 388 auto tracks = GetLocalTracks(side); 389 return tracks.begin()->GetStreamIds()[0]; 390 } 391 392 std::vector<JsepTrack> GetLocalTracks(const JsepSession& session) const { 393 std::vector<JsepTrack> result; 394 for (const auto& transceiver : GetTransceivers(session)) { 395 if (!IsNull(transceiver.mSendTrack)) { 396 result.push_back(transceiver.mSendTrack); 397 } 398 } 399 return result; 400 } 401 402 std::vector<JsepTrack> GetRemoteTracks(const JsepSession& session) const { 403 std::vector<JsepTrack> result; 404 for (const auto& transceiver : GetTransceivers(session)) { 405 if (!IsNull(transceiver.mRecvTrack)) { 406 result.push_back(transceiver.mRecvTrack); 407 } 408 } 409 return result; 410 } 411 412 JsepTransceiver* GetDatachannelTransceiver(JsepSession& side) { 413 for (auto& transceiver : GetTransceivers(side)) { 414 if (transceiver.mSendTrack.GetMediaType() == 415 SdpMediaSection::MediaType::kApplication) { 416 return &transceiver; 417 } 418 } 419 420 return nullptr; 421 } 422 423 JsepTransceiver* GetNegotiatedTransceiver(JsepSession& side, size_t index) { 424 for (auto& transceiver : GetTransceivers(side)) { 425 if (transceiver.mSendTrack.GetNegotiatedDetails() || 426 transceiver.mRecvTrack.GetNegotiatedDetails()) { 427 if (index) { 428 --index; 429 continue; 430 } 431 432 return &transceiver; 433 } 434 } 435 436 return nullptr; 437 } 438 439 Maybe<JsepTransceiver> GetTransceiverByLevel( 440 const std::vector<JsepTransceiver>& transceivers, size_t level) { 441 for (auto& transceiver : transceivers) { 442 if (transceiver.HasLevel() && transceiver.GetLevel() == level) { 443 return Some(transceiver); 444 } 445 } 446 447 return Nothing(); 448 } 449 450 Maybe<JsepTransceiver> GetTransceiverByLevel(JsepSession& side, 451 size_t level) { 452 return GetTransceiverByLevel(GetTransceivers(side), level); 453 } 454 455 std::vector<std::string> GetMediaStreamIds( 456 const std::vector<JsepTrack>& tracks) const { 457 std::vector<std::string> ids; 458 for (const auto& track : tracks) { 459 // data channels don't have msid's 460 if (track.GetMediaType() == SdpMediaSection::kApplication) { 461 continue; 462 } 463 ids.insert(ids.end(), track.GetStreamIds().begin(), 464 track.GetStreamIds().end()); 465 } 466 return ids; 467 } 468 469 std::vector<std::string> GetLocalMediaStreamIds(JsepSessionImpl& side) const { 470 return GetMediaStreamIds(GetLocalTracks(side)); 471 } 472 473 std::vector<std::string> GetRemoteMediaStreamIds( 474 JsepSessionImpl& side) const { 475 return GetMediaStreamIds(GetRemoteTracks(side)); 476 } 477 478 std::vector<std::string> sortUniqueStrVector( 479 std::vector<std::string> in) const { 480 std::sort(in.begin(), in.end()); 481 auto it = std::unique(in.begin(), in.end()); 482 in.resize(std::distance(in.begin(), it)); 483 return in; 484 } 485 486 std::vector<std::string> GetLocalUniqueStreamIds( 487 JsepSessionImpl& side) const { 488 return sortUniqueStrVector(GetLocalMediaStreamIds(side)); 489 } 490 491 std::vector<std::string> GetRemoteUniqueStreamIds( 492 JsepSessionImpl& side) const { 493 return sortUniqueStrVector(GetRemoteMediaStreamIds(side)); 494 } 495 496 JsepTrack GetTrack(JsepSessionImpl& side, SdpMediaSection::MediaType type, 497 size_t index) const { 498 for (const auto& transceiver : GetTransceivers(side)) { 499 if (IsNull(transceiver.mSendTrack) || 500 transceiver.mSendTrack.GetMediaType() != type) { 501 continue; 502 } 503 504 if (index != 0) { 505 --index; 506 continue; 507 } 508 509 return transceiver.mSendTrack; 510 } 511 512 return JsepTrack(type, sdp::kSend); 513 } 514 515 JsepTrack GetTrackOff(size_t index, SdpMediaSection::MediaType type) { 516 return GetTrack(*mSessionOff, type, index); 517 } 518 519 JsepTrack GetTrackAns(size_t index, SdpMediaSection::MediaType type) { 520 return GetTrack(*mSessionAns, type, index); 521 } 522 523 bool Equals(const SdpFingerprintAttributeList::Fingerprint& f1, 524 const SdpFingerprintAttributeList::Fingerprint& f2) const { 525 if (f1.hashFunc != f2.hashFunc) { 526 return false; 527 } 528 529 if (f1.fingerprint != f2.fingerprint) { 530 return false; 531 } 532 533 return true; 534 } 535 536 bool Equals(const SdpFingerprintAttributeList& f1, 537 const SdpFingerprintAttributeList& f2) const { 538 if (f1.mFingerprints.size() != f2.mFingerprints.size()) { 539 return false; 540 } 541 542 for (size_t i = 0; i < f1.mFingerprints.size(); ++i) { 543 if (!Equals(f1.mFingerprints[i], f2.mFingerprints[i])) { 544 return false; 545 } 546 } 547 548 return true; 549 } 550 551 bool Equals(const UniquePtr<JsepDtlsTransport>& t1, 552 const UniquePtr<JsepDtlsTransport>& t2) const { 553 if (!t1 && !t2) { 554 return true; 555 } 556 557 if (!t1 || !t2) { 558 return false; 559 } 560 561 if (!Equals(t1->GetFingerprints(), t2->GetFingerprints())) { 562 return false; 563 } 564 565 if (t1->GetRole() != t2->GetRole()) { 566 return false; 567 } 568 569 return true; 570 } 571 572 bool Equals(const UniquePtr<JsepIceTransport>& t1, 573 const UniquePtr<JsepIceTransport>& t2) const { 574 if (!t1 && !t2) { 575 return true; 576 } 577 578 if (!t1 || !t2) { 579 return false; 580 } 581 582 if (t1->GetUfrag() != t2->GetUfrag()) { 583 return false; 584 } 585 586 if (t1->GetPassword() != t2->GetPassword()) { 587 return false; 588 } 589 590 return true; 591 } 592 593 bool Equals(const JsepTransport& t1, const JsepTransport& t2) const { 594 if (t1.mTransportId != t2.mTransportId) { 595 std::cerr << "Transport id differs: " << t1.mTransportId << " vs " 596 << t2.mTransportId << std::endl; 597 return false; 598 } 599 600 if (t1.mComponents != t2.mComponents) { 601 std::cerr << "Component count differs" << std::endl; 602 return false; 603 } 604 605 if (!Equals(t1.mIce, t2.mIce)) { 606 std::cerr << "ICE differs" << std::endl; 607 return false; 608 } 609 610 return true; 611 } 612 613 bool Equals(const JsepTrack& t1, const JsepTrack& t2) const { 614 if (t1.GetMediaType() != t2.GetMediaType()) { 615 return false; 616 } 617 618 if (t1.GetDirection() != t2.GetDirection()) { 619 return false; 620 } 621 622 if (t1.GetStreamIds() != t2.GetStreamIds()) { 623 return false; 624 } 625 626 if (t1.GetActive() != t2.GetActive()) { 627 return false; 628 } 629 630 if (t1.GetCNAME() != t2.GetCNAME()) { 631 return false; 632 } 633 634 if (t1.GetSsrcs() != t2.GetSsrcs()) { 635 return false; 636 } 637 638 return true; 639 } 640 641 bool Equals(const JsepTransceiver& p1, const JsepTransceiver& p2) const { 642 if (p1.HasLevel() != p2.HasLevel()) { 643 std::cerr << "One transceiver has a level, the other doesn't" 644 << std::endl; 645 return false; 646 } 647 648 if (p1.HasLevel() && (p1.GetLevel() != p2.GetLevel())) { 649 std::cerr << "Level differs: " << p1.GetLevel() << " vs " << p2.GetLevel() 650 << std::endl; 651 return false; 652 } 653 654 // We don't check things like BundleLevel(), since that can change without 655 // any changes to the transport, which is what we're really interested in. 656 657 if (p1.IsStopped() != p2.IsStopped()) { 658 std::cerr << "One transceiver is stopped, the other is not" << std::endl; 659 return false; 660 } 661 662 if (p1.IsAssociated() != p2.IsAssociated()) { 663 std::cerr << "One transceiver has a mid, the other doesn't" << std::endl; 664 return false; 665 } 666 667 if (p1.IsAssociated() && (p1.GetMid() != p2.GetMid())) { 668 std::cerr << "mid differs: " << p1.GetMid() << " vs " << p2.GetMid() 669 << std::endl; 670 return false; 671 } 672 673 if (!Equals(p1.mSendTrack, p2.mSendTrack)) { 674 std::cerr << "Send track differs" << std::endl; 675 return false; 676 } 677 678 if (!Equals(p1.mRecvTrack, p2.mRecvTrack)) { 679 std::cerr << "Receive track differs" << std::endl; 680 return false; 681 } 682 683 if (!Equals(p1.mTransport, p2.mTransport)) { 684 std::cerr << "Transport differs" << std::endl; 685 return false; 686 } 687 688 return true; 689 } 690 691 bool Equals(const std::vector<JsepTransceiver>& t1, 692 const std::vector<JsepTransceiver>& t2) const { 693 if (t1.size() != t2.size()) { 694 std::cerr << "Size differs: t1.size = " << t1.size() 695 << ", t2.size = " << t2.size() << std::endl; 696 return false; 697 } 698 699 for (size_t i = 0; i < t1.size(); ++i) { 700 if (!Equals(t1[i], t2[i])) { 701 return false; 702 } 703 } 704 705 return true; 706 } 707 708 size_t GetTrackCount(JsepSessionImpl& side, 709 SdpMediaSection::MediaType type) const { 710 size_t result = 0; 711 for (const auto& track : GetLocalTracks(side)) { 712 if (track.GetMediaType() == type) { 713 ++result; 714 } 715 } 716 return result; 717 } 718 719 UniquePtr<Sdp> GetParsedLocalDescription(const JsepSessionImpl& side) const { 720 return Parse(side.GetLocalDescription(kJsepDescriptionCurrent)); 721 } 722 723 SdpMediaSection* GetMsection(Sdp& sdp, SdpMediaSection::MediaType type, 724 size_t index) const { 725 for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) { 726 auto& msection = sdp.GetMediaSection(i); 727 if (msection.GetMediaType() != type) { 728 continue; 729 } 730 731 if (index) { 732 --index; 733 continue; 734 } 735 736 return &msection; 737 } 738 739 return nullptr; 740 } 741 742 void SetPayloadTypeNumber(JsepSession& session, const std::string& codecName, 743 const std::string& payloadType) { 744 for (auto& codec : session.Codecs()) { 745 if (codec->mName == codecName) { 746 codec->mDefaultPt = payloadType; 747 } 748 } 749 } 750 751 void SetCodecEnabled(JsepSession& session, const std::string& codecName, 752 bool enabled) { 753 for (auto& codec : session.Codecs()) { 754 if (codec->mName == codecName) { 755 codec->mEnabled = enabled; 756 } 757 } 758 } 759 760 void EnsureNegotiationFailure(SdpMediaSection::MediaType type, 761 const std::string& codecName) { 762 for (auto& codec : mSessionOff->Codecs()) { 763 if (codec->Type() == type && codec->mName != codecName) { 764 codec->mEnabled = false; 765 } 766 } 767 768 for (auto& codec : mSessionAns->Codecs()) { 769 if (codec->Type() == type && codec->mName == codecName) { 770 codec->mEnabled = false; 771 } 772 } 773 } 774 775 std::string CreateAnswer() { 776 std::vector<JsepTransceiver> transceiversBefore = 777 GetTransceivers(*mSessionAns); 778 779 JsepAnswerOptions options; 780 std::string answer; 781 782 JsepSession::Result result = mSessionAns->CreateAnswer(options, &answer); 783 EXPECT_FALSE(result.mError.isSome()); 784 785 std::cerr << "ANSWER: " << answer << std::endl; 786 787 ValidateTransport(*mAnswererTransport, answer, sdp::kAnswer); 788 CheckTransceiverInvariants(transceiversBefore, 789 GetTransceivers(*mSessionAns)); 790 791 return answer; 792 } 793 794 static const uint32_t NO_CHECKS = 0; 795 static const uint32_t CHECK_SUCCESS = 1; 796 static const uint32_t CHECK_TRACKS = 1 << 2; 797 static const uint32_t ALL_CHECKS = CHECK_SUCCESS | CHECK_TRACKS; 798 799 void OfferAnswer(uint32_t checkFlags = ALL_CHECKS, 800 const Maybe<JsepOfferOptions>& options = Nothing()) { 801 std::string offer = CreateOffer(options); 802 SetLocalOffer(offer, checkFlags); 803 SetRemoteOffer(offer, checkFlags); 804 805 std::string answer = CreateAnswer(); 806 SetLocalAnswer(answer, checkFlags); 807 SetRemoteAnswer(answer, checkFlags); 808 } 809 810 void SetLocalOffer(const std::string& offer, 811 uint32_t checkFlags = ALL_CHECKS) { 812 std::vector<JsepTransceiver> transceiversBefore = 813 GetTransceivers(*mSessionOff); 814 815 JsepSession::Result result = 816 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer); 817 818 CheckTransceiverInvariants(transceiversBefore, 819 GetTransceivers(*mSessionOff)); 820 821 if (checkFlags & CHECK_SUCCESS) { 822 ASSERT_FALSE(result.mError.isSome()); 823 } 824 825 if (checkFlags & CHECK_TRACKS) { 826 // This assumes no recvonly or inactive transceivers. 827 ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size()); 828 for (const auto& transceiver : GetTransceivers(*mSessionOff)) { 829 if (!transceiver.HasLevel()) { 830 continue; 831 } 832 const auto& track(transceiver.mSendTrack); 833 size_t level = transceiver.GetLevel(); 834 ASSERT_FALSE(IsNull(track)); 835 ASSERT_EQ(types[level], track.GetMediaType()); 836 if (track.GetMediaType() != SdpMediaSection::kApplication) { 837 std::string msidAttr("a=msid:"); 838 msidAttr += track.GetStreamIds()[0]; 839 ASSERT_NE(std::string::npos, offer.find(msidAttr)) 840 << "Did not find " << msidAttr << " in offer"; 841 } 842 } 843 if (types.size() == 1 && types[0] == SdpMediaSection::kApplication) { 844 ASSERT_EQ(std::string::npos, offer.find("a=ssrc")) 845 << "Data channel should not contain SSRC"; 846 } 847 } 848 } 849 850 void SetRemoteOffer(const std::string& offer, 851 uint32_t checkFlags = ALL_CHECKS) { 852 std::vector<JsepTransceiver> transceiversBefore = 853 GetTransceivers(*mSessionAns); 854 855 JsepSession::Result result = 856 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 857 858 CheckTransceiverInvariants(transceiversBefore, 859 GetTransceivers(*mSessionAns)); 860 861 if (checkFlags & CHECK_SUCCESS) { 862 ASSERT_FALSE(result.mError.isSome()); 863 } 864 865 if (checkFlags & CHECK_TRACKS) { 866 // This assumes no recvonly or inactive transceivers. 867 ASSERT_EQ(types.size(), GetTransceivers(*mSessionAns).size()); 868 for (const auto& transceiver : GetTransceivers(*mSessionAns)) { 869 if (!transceiver.HasLevel()) { 870 continue; 871 } 872 const auto& track(transceiver.mRecvTrack); 873 size_t level = transceiver.GetLevel(); 874 ASSERT_FALSE(IsNull(track)); 875 ASSERT_EQ(types[level], track.GetMediaType()); 876 if (track.GetMediaType() != SdpMediaSection::kApplication) { 877 std::string msidAttr("a=msid:"); 878 msidAttr += track.GetStreamIds()[0]; 879 // Track id will not match, and will eventually not be present at all 880 ASSERT_NE(std::string::npos, offer.find(msidAttr)) 881 << "Did not find " << msidAttr << " in offer"; 882 } 883 } 884 } 885 } 886 887 void SetLocalAnswer(const std::string& answer, 888 uint32_t checkFlags = ALL_CHECKS) { 889 std::vector<JsepTransceiver> transceiversBefore = 890 GetTransceivers(*mSessionAns); 891 892 JsepSession::Result result = 893 mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 894 if (checkFlags & CHECK_SUCCESS) { 895 ASSERT_FALSE(result.mError.isSome()); 896 } 897 898 CheckTransceiverInvariants(transceiversBefore, 899 GetTransceivers(*mSessionAns)); 900 901 if (checkFlags & CHECK_TRACKS) { 902 // Verify that the right stuff is in the tracks. 903 ASSERT_EQ(types.size(), GetTransceivers(*mSessionAns).size()); 904 for (const auto& transceiver : GetTransceivers(*mSessionAns)) { 905 if (!transceiver.HasLevel()) { 906 continue; 907 } 908 const auto& sendTrack(transceiver.mSendTrack); 909 const auto& recvTrack(transceiver.mRecvTrack); 910 size_t level = transceiver.GetLevel(); 911 ASSERT_FALSE(IsNull(sendTrack)); 912 ASSERT_EQ(types[level], sendTrack.GetMediaType()); 913 // These might have been in the SDP, or might have been randomly 914 // chosen by JsepSessionImpl 915 ASSERT_FALSE(IsNull(recvTrack)); 916 ASSERT_EQ(types[level], recvTrack.GetMediaType()); 917 918 if (recvTrack.GetMediaType() != SdpMediaSection::kApplication) { 919 std::string msidAttr("a=msid:"); 920 msidAttr += sendTrack.GetStreamIds()[0]; 921 ASSERT_NE(std::string::npos, answer.find(msidAttr)) 922 << "Did not find " << msidAttr << " in answer"; 923 } 924 } 925 if (types.size() == 1 && types[0] == SdpMediaSection::kApplication) { 926 ASSERT_EQ(std::string::npos, answer.find("a=ssrc")) 927 << "Data channel should not contain SSRC"; 928 } 929 } 930 std::cerr << "Answerer transceivers:" << std::endl; 931 DumpTransceivers(*mSessionAns); 932 } 933 934 void SetRemoteAnswer(const std::string& answer, 935 uint32_t checkFlags = ALL_CHECKS) { 936 std::vector<JsepTransceiver> transceiversBefore = 937 GetTransceivers(*mSessionOff); 938 939 JsepSession::Result result = 940 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer); 941 if (checkFlags & CHECK_SUCCESS) { 942 ASSERT_FALSE(result.mError.isSome()); 943 } 944 945 CheckTransceiverInvariants(transceiversBefore, 946 GetTransceivers(*mSessionOff)); 947 948 if (checkFlags & CHECK_TRACKS) { 949 // Verify that the right stuff is in the tracks. 950 ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size()); 951 for (const auto& transceiver : GetTransceivers(*mSessionOff)) { 952 if (!transceiver.HasLevel()) { 953 continue; 954 } 955 const auto& sendTrack(transceiver.mSendTrack); 956 const auto& recvTrack(transceiver.mRecvTrack); 957 size_t level = transceiver.GetLevel(); 958 ASSERT_FALSE(IsNull(sendTrack)); 959 ASSERT_EQ(types[level], sendTrack.GetMediaType()); 960 // These might have been in the SDP, or might have been randomly 961 // chosen by JsepSessionImpl 962 ASSERT_FALSE(IsNull(recvTrack)); 963 ASSERT_EQ(types[level], recvTrack.GetMediaType()); 964 965 if (recvTrack.GetMediaType() != SdpMediaSection::kApplication) { 966 std::string msidAttr("a=msid:"); 967 msidAttr += recvTrack.GetStreamIds()[0]; 968 // Track id will not match, and will eventually not be present at all 969 ASSERT_NE(std::string::npos, answer.find(msidAttr)) 970 << "Did not find " << msidAttr << " in answer"; 971 } 972 } 973 } 974 std::cerr << "Offerer transceivers:" << std::endl; 975 DumpTransceivers(*mSessionOff); 976 } 977 978 std::string GetTransportId(const JsepSession& session, size_t level) { 979 for (const auto& transceiver : GetTransceivers(session)) { 980 if (transceiver.HasLevel() && transceiver.GetLevel() == level) { 981 return transceiver.mTransport.mTransportId; 982 } 983 } 984 return std::string(); 985 } 986 987 typedef enum { RTP = 1, RTCP = 2 } ComponentType; 988 989 class CandidateSet { 990 public: 991 CandidateSet() {} 992 993 void Gather(JsepSession& session, ComponentType maxComponent = RTCP) { 994 for (const auto& transceiver : GetTransceivers(session)) { 995 if (transceiver.HasOwnTransport()) { 996 Gather(session, transceiver.mTransport.mTransportId, RTP); 997 if (transceiver.mTransport.mComponents > 1) { 998 Gather(session, transceiver.mTransport.mTransportId, RTCP); 999 } 1000 } 1001 } 1002 FinishGathering(session); 1003 } 1004 1005 void Gather(JsepSession& session, const std::string& transportId, 1006 ComponentType component) { 1007 static uint16_t port = 1000; 1008 std::vector<std::string> candidates; 1009 1010 for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) { 1011 ++port; 1012 std::ostringstream candidate; 1013 candidate << "0 " << static_cast<uint16_t>(component) 1014 << " UDP 9999 192.168.0.1 " << port << " typ host"; 1015 std::string mid; 1016 uint16_t level = 0; 1017 bool skipped; 1018 session.AddLocalIceCandidate(kAEqualsCandidate + candidate.str(), 1019 transportId, "", &level, &mid, &skipped); 1020 if (!skipped) { 1021 mCandidatesToTrickle.push_back(std::tuple<Level, Mid, Candidate>( 1022 level, mid, kAEqualsCandidate + candidate.str())); 1023 candidates.push_back(candidate.str()); 1024 } 1025 } 1026 1027 // Stomp existing candidates 1028 mCandidates[transportId][component] = candidates; 1029 1030 // Stomp existing defaults 1031 mDefaultCandidates[transportId][component] = 1032 std::make_pair("192.168.0.1", port); 1033 session.UpdateDefaultCandidate( 1034 mDefaultCandidates[transportId][RTP].first, 1035 mDefaultCandidates[transportId][RTP].second, 1036 // Will be empty string if not present, which is how we indicate 1037 // that there is no default for RTCP 1038 mDefaultCandidates[transportId][RTCP].first, 1039 mDefaultCandidates[transportId][RTCP].second, transportId); 1040 } 1041 1042 void FinishGathering(JsepSession& session) const { 1043 // Copy so we can be terse and use [] 1044 for (auto idAndCandidates : mDefaultCandidates) { 1045 ASSERT_EQ(1U, idAndCandidates.second.count(RTP)); 1046 // do a final UpdateDefaultCandidate here in case candidates were 1047 // cleared during renegotiation. 1048 session.UpdateDefaultCandidate( 1049 idAndCandidates.second[RTP].first, 1050 idAndCandidates.second[RTP].second, 1051 // Will be empty string if not present, which is how we indicate 1052 // that there is no default for RTCP 1053 idAndCandidates.second[RTCP].first, 1054 idAndCandidates.second[RTCP].second, idAndCandidates.first); 1055 std::string mid; 1056 uint16_t level = 0; 1057 bool skipped; 1058 session.AddLocalIceCandidate("", idAndCandidates.first, "", &level, 1059 &mid, &skipped); 1060 } 1061 } 1062 1063 void Trickle(JsepSession& session) { 1064 std::string transportId; 1065 for (const auto& levelMidAndCandidate : mCandidatesToTrickle) { 1066 auto [level, mid, candidate] = levelMidAndCandidate; 1067 std::cerr << "trickling candidate: " << candidate << " level: " << level 1068 << " mid: " << mid << std::endl; 1069 Maybe<unsigned long> lev = Some(level); 1070 session.AddRemoteIceCandidate(candidate, mid, lev, "", &transportId); 1071 } 1072 session.AddRemoteIceCandidate("", "", Maybe<uint16_t>(), "", 1073 &transportId); 1074 mCandidatesToTrickle.clear(); 1075 } 1076 1077 void CheckRtpCandidates(bool expectRtpCandidates, 1078 const SdpMediaSection& msection, 1079 const std::string& transportId, 1080 const std::string& context) const { 1081 auto& attrs = msection.GetAttributeList(); 1082 1083 ASSERT_EQ(expectRtpCandidates, 1084 attrs.HasAttribute(SdpAttribute::kCandidateAttribute)) 1085 << context << " (level " << msection.GetLevel() << ")"; 1086 1087 if (expectRtpCandidates) { 1088 // Copy so we can be terse and use [] 1089 auto expectedCandidates = mCandidates; 1090 ASSERT_LE(kNumCandidatesPerComponent, 1091 expectedCandidates[transportId][RTP].size()); 1092 1093 auto& candidates = attrs.GetCandidate(); 1094 ASSERT_LE(kNumCandidatesPerComponent, candidates.size()) 1095 << context << " (level " << msection.GetLevel() << ")"; 1096 for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) { 1097 ASSERT_EQ(expectedCandidates[transportId][RTP][i], candidates[i]) 1098 << context << " (level " << msection.GetLevel() << ")"; 1099 } 1100 } 1101 } 1102 1103 void CheckRtcpCandidates(bool expectRtcpCandidates, 1104 const SdpMediaSection& msection, 1105 const std::string& transportId, 1106 const std::string& context) const { 1107 auto& attrs = msection.GetAttributeList(); 1108 1109 if (expectRtcpCandidates) { 1110 // Copy so we can be terse and use [] 1111 auto expectedCandidates = mCandidates; 1112 ASSERT_LE(kNumCandidatesPerComponent, 1113 expectedCandidates[transportId][RTCP].size()); 1114 1115 ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kCandidateAttribute)) 1116 << context << " (level " << msection.GetLevel() << ")"; 1117 auto& candidates = attrs.GetCandidate(); 1118 ASSERT_EQ(kNumCandidatesPerComponent * 2, candidates.size()) 1119 << context << " (level " << msection.GetLevel() << ")"; 1120 for (size_t i = 0; i < kNumCandidatesPerComponent; ++i) { 1121 ASSERT_EQ(expectedCandidates[transportId][RTCP][i], 1122 candidates[i + kNumCandidatesPerComponent]) 1123 << context << " (level " << msection.GetLevel() << ")"; 1124 } 1125 } 1126 } 1127 1128 void CheckDefaultRtpCandidate(bool expectDefault, 1129 const SdpMediaSection& msection, 1130 const std::string& transportId, 1131 const std::string& context) const { 1132 Address expectedAddress = "0.0.0.0"; 1133 Port expectedPort = 9U; 1134 1135 if (expectDefault) { 1136 // Copy so we can be terse and use [] 1137 auto defaultCandidates = mDefaultCandidates; 1138 expectedAddress = defaultCandidates[transportId][RTP].first; 1139 expectedPort = defaultCandidates[transportId][RTP].second; 1140 } 1141 1142 // if bundle-only attribute is present, expect port 0 1143 const SdpAttributeList& attrs = msection.GetAttributeList(); 1144 if (attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) { 1145 expectedPort = 0U; 1146 } 1147 1148 ASSERT_EQ(expectedAddress, msection.GetConnection().GetAddress()) 1149 << context << " (level " << msection.GetLevel() << ")"; 1150 ASSERT_EQ(expectedPort, msection.GetPort()) 1151 << context << " (level " << msection.GetLevel() << ")"; 1152 } 1153 1154 void CheckDefaultRtcpCandidate(bool expectDefault, 1155 const SdpMediaSection& msection, 1156 const std::string& transportId, 1157 const std::string& context) const { 1158 if (expectDefault) { 1159 // Copy so we can be terse and use [] 1160 auto defaultCandidates = mDefaultCandidates; 1161 ASSERT_TRUE(msection.GetAttributeList().HasAttribute( 1162 SdpAttribute::kRtcpAttribute)) 1163 << context << " (level " << msection.GetLevel() << ")"; 1164 auto& rtcpAttr = msection.GetAttributeList().GetRtcp(); 1165 ASSERT_EQ(defaultCandidates[transportId][RTCP].second, rtcpAttr.mPort) 1166 << context << " (level " << msection.GetLevel() << ")"; 1167 ASSERT_EQ(sdp::kInternet, rtcpAttr.mNetType) 1168 << context << " (level " << msection.GetLevel() << ")"; 1169 ASSERT_EQ(sdp::kIPv4, rtcpAttr.mAddrType) 1170 << context << " (level " << msection.GetLevel() << ")"; 1171 ASSERT_EQ(defaultCandidates[transportId][RTCP].first, rtcpAttr.mAddress) 1172 << context << " (level " << msection.GetLevel() << ")"; 1173 } else { 1174 ASSERT_FALSE(msection.GetAttributeList().HasAttribute( 1175 SdpAttribute::kRtcpAttribute)) 1176 << context << " (level " << msection.GetLevel() << ")"; 1177 } 1178 } 1179 1180 private: 1181 using Level = size_t; 1182 using TransportId = std::string; 1183 using Mid = std::string; 1184 using Candidate = std::string; 1185 using Address = std::string; 1186 using Port = uint16_t; 1187 // Default candidates are put into the m-line, c-line, and rtcp 1188 // attribute for endpoints that don't support ICE. 1189 std::map<TransportId, std::map<ComponentType, std::pair<Address, Port>>> 1190 mDefaultCandidates; 1191 std::map<TransportId, std::map<ComponentType, std::vector<Candidate>>> 1192 mCandidates; 1193 // Level/mid/candidate tuples that need to be trickled 1194 std::vector<std::tuple<Level, Mid, Candidate>> mCandidatesToTrickle; 1195 }; 1196 1197 // For streaming parse errors 1198 std::string GetParseErrors( 1199 const UniquePtr<SdpParser::Results>& results) const { 1200 std::stringstream output; 1201 auto errors = std::move(results->Errors()); 1202 for (auto error : errors) { 1203 output << error.first << ": " << error.second << std::endl; 1204 } 1205 return output.str(); 1206 } 1207 1208 void CheckEndOfCandidates(bool expectEoc, const SdpMediaSection& msection, 1209 const std::string& context) { 1210 if (expectEoc) { 1211 ASSERT_TRUE(msection.GetAttributeList().HasAttribute( 1212 SdpAttribute::kEndOfCandidatesAttribute)) 1213 << context << " (level " << msection.GetLevel() << ")"; 1214 } else { 1215 ASSERT_FALSE(msection.GetAttributeList().HasAttribute( 1216 SdpAttribute::kEndOfCandidatesAttribute)) 1217 << context << " (level " << msection.GetLevel() << ")"; 1218 } 1219 } 1220 1221 void CheckTransceiversAreBundled(const JsepSession& session, 1222 const std::string& context) { 1223 for (const auto& transceiver : GetTransceivers(session)) { 1224 ASSERT_TRUE(transceiver.HasBundleLevel()) 1225 << context; 1226 ASSERT_EQ(0U, transceiver.BundleLevel()) << context; 1227 ASSERT_NE("", transceiver.mTransport.mTransportId); 1228 } 1229 } 1230 1231 void DisableMsid(std::string* sdp) const { 1232 while (true) { 1233 size_t pos = sdp->find("a=msid"); 1234 if (pos == std::string::npos) { 1235 break; 1236 } 1237 (*sdp)[pos + 2] = 'X'; // garble, a=Xsid 1238 } 1239 } 1240 1241 void DisableBundle(std::string* sdp) const { 1242 size_t pos = sdp->find("a=group:BUNDLE"); 1243 ASSERT_NE(std::string::npos, pos); 1244 (*sdp)[pos + 11] = 'G'; // garble, a=group:BUNGLE 1245 } 1246 1247 void DisableMsection(std::string* sdp, size_t level) const { 1248 UniquePtr<Sdp> parsed(Parse(*sdp)); 1249 ASSERT_TRUE(parsed.get()); 1250 ASSERT_LT(level, parsed->GetMediaSectionCount()); 1251 SdpHelper::DisableMsection(parsed.get(), &parsed->GetMediaSection(level)); 1252 (*sdp) = parsed->ToString(); 1253 } 1254 1255 void CopyTransportAttributes(std::string* sdp, size_t src_level, 1256 size_t dst_level) { 1257 UniquePtr<Sdp> parsed(Parse(*sdp)); 1258 ASSERT_TRUE(parsed.get()); 1259 ASSERT_LT(src_level, parsed->GetMediaSectionCount()); 1260 ASSERT_LT(dst_level, parsed->GetMediaSectionCount()); 1261 nsresult rv = 1262 mSdpHelper.CopyTransportParams(2, parsed->GetMediaSection(src_level), 1263 &parsed->GetMediaSection(dst_level)); 1264 ASSERT_EQ(NS_OK, rv); 1265 (*sdp) = parsed->ToString(); 1266 } 1267 1268 void ReplaceInSdp(std::string* sdp, const char* searchStr, 1269 const char* replaceStr) const { 1270 if (searchStr[0] == '\0') return; 1271 size_t pos = 0; 1272 while ((pos = sdp->find(searchStr, pos)) != std::string::npos) { 1273 sdp->replace(pos, strlen(searchStr), replaceStr); 1274 pos += strlen(replaceStr); 1275 } 1276 } 1277 1278 void ValidateDisabledMSection(const SdpMediaSection* msection) { 1279 ASSERT_EQ(1U, msection->GetFormats().size()); 1280 1281 auto& attrs = msection->GetAttributeList(); 1282 ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kMidAttribute)); 1283 ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kDirectionAttribute)); 1284 ASSERT_FALSE(attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)); 1285 ASSERT_EQ(SdpDirectionAttribute::kInactive, 1286 msection->GetDirectionAttribute().mValue); 1287 ASSERT_EQ(3U, attrs.Count()); 1288 if (msection->GetMediaType() == SdpMediaSection::kAudio) { 1289 ASSERT_EQ("0", msection->GetFormats()[0]); 1290 const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("0")); 1291 ASSERT_TRUE(rtpmap); 1292 ASSERT_EQ("0", rtpmap->pt); 1293 ASSERT_EQ("PCMU", rtpmap->name); 1294 } else if (msection->GetMediaType() == SdpMediaSection::kVideo) { 1295 ASSERT_EQ("120", msection->GetFormats()[0]); 1296 const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("120")); 1297 ASSERT_TRUE(rtpmap); 1298 ASSERT_EQ("120", rtpmap->pt); 1299 ASSERT_EQ("VP8", rtpmap->name); 1300 } else if (msection->GetMediaType() == SdpMediaSection::kApplication) { 1301 if (msection->GetProtocol() == SdpMediaSection::kUdpDtlsSctp || 1302 msection->GetProtocol() == SdpMediaSection::kTcpDtlsSctp) { 1303 // draft 21 format 1304 ASSERT_EQ("webrtc-datachannel", msection->GetFormats()[0]); 1305 ASSERT_FALSE(msection->GetSctpmap()); 1306 ASSERT_EQ(0U, msection->GetSctpPort()); 1307 } else { 1308 // old draft 05 format 1309 ASSERT_EQ("0", msection->GetFormats()[0]); 1310 const SdpSctpmapAttributeList::Sctpmap* sctpmap(msection->GetSctpmap()); 1311 ASSERT_TRUE(sctpmap); 1312 ASSERT_EQ("0", sctpmap->pt); 1313 ASSERT_EQ("rejected", sctpmap->name); 1314 ASSERT_EQ(0U, sctpmap->streams); 1315 } 1316 } else { 1317 // Not that we would have any test which tests this... 1318 ASSERT_EQ("19", msection->GetFormats()[0]); 1319 const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("19")); 1320 ASSERT_TRUE(rtpmap); 1321 ASSERT_EQ("19", rtpmap->pt); 1322 ASSERT_EQ("reserved", rtpmap->name); 1323 } 1324 1325 ASSERT_FALSE(msection->GetAttributeList().HasAttribute( 1326 SdpAttribute::kMsidAttribute)); 1327 } 1328 1329 void ValidateSetupAttribute(const JsepSessionImpl& side, 1330 const SdpSetupAttribute::Role expectedRole) { 1331 auto sdp = GetParsedLocalDescription(side); 1332 for (size_t i = 0; sdp && i < sdp->GetMediaSectionCount(); ++i) { 1333 if (sdp->GetMediaSection(i).GetAttributeList().HasAttribute( 1334 SdpAttribute::kSetupAttribute)) { 1335 auto role = sdp->GetMediaSection(i).GetAttributeList().GetSetup().mRole; 1336 ASSERT_EQ(expectedRole, role); 1337 } 1338 } 1339 } 1340 1341 void DumpTrack(const JsepTrack& track) { 1342 const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails(); 1343 std::cerr << " type=" << track.GetMediaType() << std::endl; 1344 if (!details) { 1345 std::cerr << " not negotiated" << std::endl; 1346 return; 1347 } 1348 std::cerr << " encodings=" << std::endl; 1349 for (size_t i = 0; i < details->GetEncodingCount(); ++i) { 1350 const JsepTrackEncoding& encoding = details->GetEncoding(i); 1351 std::cerr << " id=" << encoding.mRid << std::endl; 1352 for (const auto& codec : encoding.GetCodecs()) { 1353 std::cerr << " " << codec->mName << " enabled(" 1354 << (codec->mEnabled ? "yes" : "no") << ")"; 1355 if (track.GetMediaType() == SdpMediaSection::kAudio) { 1356 const JsepAudioCodecDescription* audioCodec = 1357 static_cast<const JsepAudioCodecDescription*>(codec.get()); 1358 std::cerr << " dtmf(" << (audioCodec->mDtmfEnabled ? "yes" : "no") 1359 << ")"; 1360 } 1361 if (track.GetMediaType() == SdpMediaSection::kVideo) { 1362 const JsepVideoCodecDescription* videoCodec = 1363 static_cast<const JsepVideoCodecDescription*>(codec.get()); 1364 std::cerr << " rtx(" 1365 << (videoCodec->mRtxEnabled ? videoCodec->mRtxPayloadType 1366 : "no") 1367 << ")"; 1368 } 1369 std::cerr << std::endl; 1370 } 1371 } 1372 } 1373 1374 void DumpTransport(const JsepTransport& transport) { 1375 std::cerr << " id=" << transport.mTransportId << std::endl; 1376 std::cerr << " components=" << transport.mComponents << std::endl; 1377 } 1378 1379 void DumpTransceivers(const JsepSessionImpl& session) { 1380 for (const auto& transceiver : GetTransceivers(session)) { 1381 std::cerr << "Transceiver "; 1382 if (transceiver.HasLevel()) { 1383 std::cerr << transceiver.GetLevel() << std::endl; 1384 } else { 1385 std::cerr << "<NO LEVEL>" << std::endl; 1386 } 1387 if (transceiver.HasBundleLevel()) { 1388 std::cerr << "(bundle level is " << transceiver.BundleLevel() << ")" 1389 << std::endl; 1390 } 1391 if (!IsNull(transceiver.mSendTrack)) { 1392 std::cerr << "Sending-->" << std::endl; 1393 DumpTrack(transceiver.mSendTrack); 1394 } 1395 if (!IsNull(transceiver.mRecvTrack)) { 1396 std::cerr << "Receiving-->" << std::endl; 1397 DumpTrack(transceiver.mRecvTrack); 1398 } 1399 std::cerr << "Transport-->" << std::endl; 1400 DumpTransport(transceiver.mTransport); 1401 } 1402 } 1403 1404 UniquePtr<Sdp> Parse(const std::string& sdp) const { 1405 SipccSdpParser parser; 1406 auto results = parser.Parse(sdp); 1407 UniquePtr<Sdp> parsed = std::move(results->Sdp()); 1408 EXPECT_TRUE(parsed.get()) << "Should have valid SDP" << std::endl 1409 << "Errors were: " << GetParseErrors(results); 1410 return parsed; 1411 } 1412 1413 std::string SetExtmap(const std::string& aSdp, const std::string& aUri, 1414 uint16_t aId, uint16_t* aOldId = nullptr) { 1415 UniquePtr<Sdp> munge(Parse(aSdp)); 1416 for (size_t i = 0; i < munge->GetMediaSectionCount(); ++i) { 1417 auto& attrs = munge->GetMediaSection(i).GetAttributeList(); 1418 if (attrs.HasAttribute(SdpAttribute::kExtmapAttribute)) { 1419 auto extmap = attrs.GetExtmap(); 1420 for (auto it = extmap.mExtmaps.begin(); it != extmap.mExtmaps.end(); 1421 ++it) { 1422 if (it->extensionname == aUri) { 1423 if (aOldId) { 1424 *aOldId = it->entry; 1425 } 1426 1427 if (aId) { 1428 it->entry = aId; 1429 } else { 1430 extmap.mExtmaps.erase(it); 1431 } 1432 break; 1433 } 1434 } 1435 attrs.SetAttribute(extmap.Clone()); 1436 } 1437 } 1438 return munge->ToString(); 1439 } 1440 1441 uint16_t GetExtmap(const std::string& aSdp, const std::string& aUri) { 1442 UniquePtr<Sdp> parsed(Parse(aSdp)); 1443 for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) { 1444 auto& attrs = parsed->GetMediaSection(i).GetAttributeList(); 1445 if (attrs.HasAttribute(SdpAttribute::kExtmapAttribute)) { 1446 auto extmap = attrs.GetExtmap(); 1447 for (auto& ext : extmap.mExtmaps) { 1448 if (ext.extensionname == aUri) { 1449 return ext.entry; 1450 } 1451 } 1452 } 1453 } 1454 return 0; 1455 } 1456 1457 void SwapOfferAnswerRoles() { 1458 mSessionOff.swap(mSessionAns); 1459 mOffCandidates.swap(mAnsCandidates); 1460 mOffererTransport.swap(mAnswererTransport); 1461 } 1462 1463 UniquePtr<JsepSessionImpl> mSessionOff; 1464 UniquePtr<CandidateSet> mOffCandidates; 1465 UniquePtr<JsepSessionImpl> mSessionAns; 1466 UniquePtr<CandidateSet> mAnsCandidates; 1467 1468 std::vector<SdpMediaSection::MediaType> types; 1469 std::vector<std::pair<std::string, uint16_t>> mGatheredCandidates; 1470 1471 FakeUuidGenerator mUuidGen; 1472 1473 private: 1474 void ValidateTransport(TransportData& source, const std::string& sdp_str, 1475 sdp::SdpType type) { 1476 UniquePtr<Sdp> sdp(Parse(sdp_str)); 1477 ASSERT_TRUE(!!sdp); 1478 size_t num_m_sections = sdp->GetMediaSectionCount(); 1479 for (size_t i = 0; i < num_m_sections; ++i) { 1480 auto& msection = sdp->GetMediaSection(i); 1481 1482 if (msection.GetMediaType() == SdpMediaSection::kApplication) { 1483 if (!(msection.GetProtocol() == SdpMediaSection::kUdpDtlsSctp || 1484 msection.GetProtocol() == SdpMediaSection::kTcpDtlsSctp)) { 1485 // old draft 05 format 1486 ASSERT_EQ(SdpMediaSection::kDtlsSctp, msection.GetProtocol()); 1487 } 1488 } else { 1489 ASSERT_EQ(SdpMediaSection::kUdpTlsRtpSavpf, msection.GetProtocol()); 1490 } 1491 1492 const SdpAttributeList& attrs = msection.GetAttributeList(); 1493 bool bundle_only = attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute); 1494 1495 // port 0 only means disabled when the bundle-only attribute is missing 1496 if (!bundle_only && msection.GetPort() == 0) { 1497 ValidateDisabledMSection(&msection); 1498 continue; 1499 } 1500 if (mSdpHelper.OwnsTransport(*sdp, i, type)) { 1501 const SdpAttributeList& attrs = msection.GetAttributeList(); 1502 1503 ASSERT_FALSE(attrs.GetIceUfrag().empty()); 1504 ASSERT_FALSE(attrs.GetIcePwd().empty()); 1505 const SdpFingerprintAttributeList& fps = attrs.GetFingerprint(); 1506 for (auto fp = fps.mFingerprints.begin(); fp != fps.mFingerprints.end(); 1507 ++fp) { 1508 nsCString alg_str = "None"_ns; 1509 1510 if (fp->hashFunc == SdpFingerprintAttributeList::kSha1) { 1511 alg_str = "sha-1"_ns; 1512 } else if (fp->hashFunc == SdpFingerprintAttributeList::kSha256) { 1513 alg_str = "sha-256"_ns; 1514 } 1515 ASSERT_EQ(source.mFingerprints[alg_str], fp->fingerprint); 1516 } 1517 1518 ASSERT_EQ(source.mFingerprints.size(), fps.mFingerprints.size()); 1519 } 1520 } 1521 } 1522 1523 protected: 1524 bool ExtmapAllowMixed(const JsepSessionImpl& aSession) { 1525 if (aSession.mCurrentLocalDescription) { 1526 return aSession.mCurrentLocalDescription->GetAttributeList().HasAttribute( 1527 SdpAttribute::kExtmapAllowMixedAttribute); 1528 } 1529 if (aSession.mPendingLocalDescription) { 1530 return aSession.mPendingLocalDescription->GetAttributeList().HasAttribute( 1531 SdpAttribute::kExtmapAllowMixedAttribute); 1532 } 1533 return false; 1534 } 1535 1536 private: 1537 std::string mLastError; 1538 SdpHelper mSdpHelper; 1539 1540 UniquePtr<TransportData> mOffererTransport; 1541 UniquePtr<TransportData> mAnswererTransport; 1542 }; 1543 1544 TEST_F(JsepSessionTestBase, CreateDestroy) {} 1545 1546 TEST_P(JsepSessionTest, CreateOffer) { 1547 AddTracks(*mSessionOff); 1548 CreateOffer(); 1549 } 1550 1551 TEST_P(JsepSessionTest, CreateOfferSetLocal) { 1552 AddTracks(*mSessionOff); 1553 std::string offer = CreateOffer(); 1554 SetLocalOffer(offer); 1555 } 1556 1557 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemote) { 1558 AddTracks(*mSessionOff); 1559 std::string offer = CreateOffer(); 1560 SetLocalOffer(offer); 1561 SetRemoteOffer(offer); 1562 } 1563 1564 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemoteCreateAnswer) { 1565 AddTracks(*mSessionOff); 1566 std::string offer = CreateOffer(); 1567 SetLocalOffer(offer); 1568 SetRemoteOffer(offer); 1569 AddTracks(*mSessionAns); 1570 std::string answer = CreateAnswer(); 1571 } 1572 1573 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemoteCreateAnswerSetLocal) { 1574 AddTracks(*mSessionOff); 1575 std::string offer = CreateOffer(); 1576 SetLocalOffer(offer); 1577 SetRemoteOffer(offer); 1578 AddTracks(*mSessionAns); 1579 std::string answer = CreateAnswer(); 1580 SetLocalAnswer(answer); 1581 } 1582 1583 TEST_P(JsepSessionTest, FullCall) { 1584 AddTracks(*mSessionOff); 1585 std::string offer = CreateOffer(); 1586 SetLocalOffer(offer); 1587 SetRemoteOffer(offer); 1588 AddTracks(*mSessionAns); 1589 std::string answer = CreateAnswer(); 1590 SetLocalAnswer(answer); 1591 SetRemoteAnswer(answer); 1592 } 1593 1594 TEST_P(JsepSessionTest, GetDescriptions) { 1595 AddTracks(*mSessionOff); 1596 std::string offer = CreateOffer(); 1597 SetLocalOffer(offer); 1598 std::string desc = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent); 1599 ASSERT_EQ(0U, desc.size()); 1600 desc = mSessionOff->GetLocalDescription(kJsepDescriptionPending); 1601 ASSERT_NE(0U, desc.size()); 1602 desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1603 ASSERT_NE(0U, desc.size()); 1604 desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1605 ASSERT_EQ(0U, desc.size()); 1606 desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1607 ASSERT_EQ(0U, desc.size()); 1608 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1609 ASSERT_EQ(0U, desc.size()); 1610 1611 SetRemoteOffer(offer); 1612 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionCurrent); 1613 ASSERT_EQ(0U, desc.size()); 1614 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPending); 1615 ASSERT_NE(0U, desc.size()); 1616 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1617 ASSERT_NE(0U, desc.size()); 1618 desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1619 ASSERT_EQ(0U, desc.size()); 1620 desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1621 ASSERT_NE(0U, desc.size()); 1622 desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1623 ASSERT_EQ(0U, desc.size()); 1624 1625 AddTracks(*mSessionAns); 1626 std::string answer = CreateAnswer(); 1627 SetLocalAnswer(answer); 1628 desc = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent); 1629 ASSERT_NE(0U, desc.size()); 1630 desc = mSessionAns->GetLocalDescription(kJsepDescriptionPending); 1631 ASSERT_EQ(0U, desc.size()); 1632 desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1633 ASSERT_NE(0U, desc.size()); 1634 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionCurrent); 1635 ASSERT_NE(0U, desc.size()); 1636 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPending); 1637 ASSERT_EQ(0U, desc.size()); 1638 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1639 ASSERT_NE(0U, desc.size()); 1640 desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1641 ASSERT_NE(0U, desc.size()); 1642 desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1643 ASSERT_EQ(0U, desc.size()); 1644 1645 SetRemoteAnswer(answer); 1646 desc = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent); 1647 ASSERT_NE(0U, desc.size()); 1648 desc = mSessionOff->GetLocalDescription(kJsepDescriptionPending); 1649 ASSERT_EQ(0U, desc.size()); 1650 desc = mSessionOff->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1651 ASSERT_NE(0U, desc.size()); 1652 desc = mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent); 1653 ASSERT_NE(0U, desc.size()); 1654 desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPending); 1655 ASSERT_EQ(0U, desc.size()); 1656 desc = mSessionOff->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1657 ASSERT_NE(0U, desc.size()); 1658 desc = mSessionAns->GetLocalDescription(kJsepDescriptionPendingOrCurrent); 1659 ASSERT_NE(0U, desc.size()); 1660 desc = mSessionAns->GetRemoteDescription(kJsepDescriptionPendingOrCurrent); 1661 ASSERT_NE(0U, desc.size()); 1662 } 1663 1664 TEST_P(JsepSessionTest, RenegotiationNoChange) { 1665 AddTracks(*mSessionOff); 1666 std::string offer = CreateOffer(); 1667 SetLocalOffer(offer); 1668 SetRemoteOffer(offer); 1669 1670 AddTracks(*mSessionAns); 1671 std::string answer = CreateAnswer(); 1672 SetLocalAnswer(answer); 1673 SetRemoteAnswer(answer); 1674 1675 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1676 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1677 1678 std::vector<JsepTransceiver> origOffererTransceivers = 1679 GetTransceivers(*mSessionOff); 1680 std::vector<JsepTransceiver> origAnswererTransceivers = 1681 GetTransceivers(*mSessionAns); 1682 1683 std::string reoffer = CreateOffer(); 1684 SetLocalOffer(reoffer); 1685 SetRemoteOffer(reoffer); 1686 1687 std::string reanswer = CreateAnswer(); 1688 SetLocalAnswer(reanswer); 1689 SetRemoteAnswer(reanswer); 1690 1691 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1692 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1693 1694 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 1695 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 1696 1697 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 1698 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 1699 } 1700 1701 // Disabled: See Bug 1329028 1702 TEST_P(JsepSessionTest, DISABLED_RenegotiationSwappedRolesNoChange) { 1703 AddTracks(*mSessionOff); 1704 std::string offer = CreateOffer(); 1705 SetLocalOffer(offer); 1706 SetRemoteOffer(offer); 1707 1708 AddTracks(*mSessionAns); 1709 std::string answer = CreateAnswer(); 1710 SetLocalAnswer(answer); 1711 SetRemoteAnswer(answer); 1712 1713 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1714 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1715 1716 auto offererTransceivers = GetTransceivers(*mSessionOff); 1717 auto answererTransceivers = GetTransceivers(*mSessionAns); 1718 1719 SwapOfferAnswerRoles(); 1720 1721 std::string reoffer = CreateOffer(); 1722 SetLocalOffer(reoffer); 1723 SetRemoteOffer(reoffer); 1724 1725 std::string reanswer = CreateAnswer(); 1726 SetLocalAnswer(reanswer); 1727 SetRemoteAnswer(reanswer); 1728 1729 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1730 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive); 1731 1732 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 1733 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 1734 1735 ASSERT_TRUE(Equals(offererTransceivers, newAnswererTransceivers)); 1736 ASSERT_TRUE(Equals(answererTransceivers, newOffererTransceivers)); 1737 } 1738 1739 static void RemoveLastN(std::vector<JsepTransceiver>& aTransceivers, 1740 size_t aNum) { 1741 while (aNum--) { 1742 // erase doesn't take reverse_iterator :( 1743 aTransceivers.erase(--aTransceivers.end()); 1744 } 1745 } 1746 1747 TEST_P(JsepSessionTest, RenegotiationOffererAddsTrack) { 1748 AddTracks(*mSessionOff); 1749 AddTracks(*mSessionAns); 1750 1751 OfferAnswer(); 1752 1753 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1754 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1755 1756 std::vector<JsepTransceiver> origOffererTransceivers = 1757 GetTransceivers(*mSessionOff); 1758 std::vector<JsepTransceiver> origAnswererTransceivers = 1759 GetTransceivers(*mSessionAns); 1760 1761 std::vector<SdpMediaSection::MediaType> extraTypes; 1762 extraTypes.push_back(SdpMediaSection::kAudio); 1763 extraTypes.push_back(SdpMediaSection::kVideo); 1764 AddTracks(*mSessionOff, extraTypes); 1765 types.insert(types.end(), extraTypes.begin(), extraTypes.end()); 1766 1767 OfferAnswer(CHECK_SUCCESS); 1768 1769 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1770 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1771 1772 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 1773 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 1774 1775 ASSERT_LE(2U, newOffererTransceivers.size()); 1776 RemoveLastN(newOffererTransceivers, 2); 1777 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 1778 1779 ASSERT_LE(2U, newAnswererTransceivers.size()); 1780 RemoveLastN(newAnswererTransceivers, 2); 1781 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 1782 } 1783 1784 TEST_P(JsepSessionTest, RenegotiationAnswererAddsTrack) { 1785 AddTracks(*mSessionOff); 1786 AddTracks(*mSessionAns); 1787 1788 OfferAnswer(); 1789 1790 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1791 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1792 1793 std::vector<JsepTransceiver> origOffererTransceivers = 1794 GetTransceivers(*mSessionOff); 1795 std::vector<JsepTransceiver> origAnswererTransceivers = 1796 GetTransceivers(*mSessionAns); 1797 1798 std::vector<SdpMediaSection::MediaType> extraTypes; 1799 extraTypes.push_back(SdpMediaSection::kAudio); 1800 extraTypes.push_back(SdpMediaSection::kVideo); 1801 AddTracks(*mSessionAns, extraTypes); 1802 types.insert(types.end(), extraTypes.begin(), extraTypes.end()); 1803 1804 // We need to add a recvonly m-section to the offer for this to work 1805 mSessionOff->AddTransceiver( 1806 JsepTransceiver(SdpMediaSection::kAudio, mUuidGen, 1807 SdpDirectionAttribute::Direction::kRecvonly)); 1808 mSessionOff->AddTransceiver( 1809 JsepTransceiver(SdpMediaSection::kVideo, mUuidGen, 1810 SdpDirectionAttribute::Direction::kRecvonly)); 1811 1812 std::string offer = CreateOffer(); 1813 SetLocalOffer(offer, CHECK_SUCCESS); 1814 SetRemoteOffer(offer, CHECK_SUCCESS); 1815 1816 std::string answer = CreateAnswer(); 1817 SetLocalAnswer(answer, CHECK_SUCCESS); 1818 SetRemoteAnswer(answer, CHECK_SUCCESS); 1819 1820 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1821 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1822 1823 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 1824 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 1825 1826 ASSERT_LE(2U, newOffererTransceivers.size()); 1827 RemoveLastN(newOffererTransceivers, 2); 1828 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 1829 1830 ASSERT_LE(2U, newAnswererTransceivers.size()); 1831 RemoveLastN(newAnswererTransceivers, 2); 1832 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 1833 } 1834 1835 TEST_P(JsepSessionTest, RenegotiationBothAddTrack) { 1836 AddTracks(*mSessionOff); 1837 AddTracks(*mSessionAns); 1838 1839 OfferAnswer(); 1840 1841 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1842 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1843 1844 std::vector<JsepTransceiver> origOffererTransceivers = 1845 GetTransceivers(*mSessionOff); 1846 std::vector<JsepTransceiver> origAnswererTransceivers = 1847 GetTransceivers(*mSessionAns); 1848 1849 std::vector<SdpMediaSection::MediaType> extraTypes; 1850 extraTypes.push_back(SdpMediaSection::kAudio); 1851 extraTypes.push_back(SdpMediaSection::kVideo); 1852 AddTracks(*mSessionAns, extraTypes); 1853 AddTracks(*mSessionOff, extraTypes); 1854 types.insert(types.end(), extraTypes.begin(), extraTypes.end()); 1855 1856 OfferAnswer(CHECK_SUCCESS); 1857 1858 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 1859 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 1860 1861 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 1862 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 1863 1864 ASSERT_LE(2U, newOffererTransceivers.size()); 1865 RemoveLastN(newOffererTransceivers, 2); 1866 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 1867 1868 ASSERT_LE(2U, newAnswererTransceivers.size()); 1869 RemoveLastN(newAnswererTransceivers, 2); 1870 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 1871 } 1872 1873 TEST_P(JsepSessionTest, RenegotiationBothAddTracksToExistingStream) { 1874 AddTracks(*mSessionOff); 1875 AddTracks(*mSessionAns); 1876 if (GetParam() == "datachannel") { 1877 return; 1878 } 1879 1880 OfferAnswer(); 1881 1882 auto oHasStream = HasMediaStream(GetLocalTracks(*mSessionOff)); 1883 auto aHasStream = HasMediaStream(GetLocalTracks(*mSessionAns)); 1884 ASSERT_EQ(oHasStream, !GetLocalUniqueStreamIds(*mSessionOff).empty()); 1885 ASSERT_EQ(aHasStream, !GetLocalUniqueStreamIds(*mSessionAns).empty()); 1886 ASSERT_EQ(aHasStream, !GetRemoteUniqueStreamIds(*mSessionOff).empty()); 1887 ASSERT_EQ(oHasStream, !GetRemoteUniqueStreamIds(*mSessionAns).empty()); 1888 1889 auto firstOffId = GetFirstLocalStreamId(*mSessionOff); 1890 auto firstAnsId = GetFirstLocalStreamId(*mSessionAns); 1891 1892 auto offererTransceivers = GetTransceivers(*mSessionOff); 1893 auto answererTransceivers = GetTransceivers(*mSessionAns); 1894 1895 std::vector<SdpMediaSection::MediaType> extraTypes; 1896 extraTypes.push_back(SdpMediaSection::kAudio); 1897 extraTypes.push_back(SdpMediaSection::kVideo); 1898 AddTracksToStream(*mSessionOff, firstOffId, extraTypes); 1899 AddTracksToStream(*mSessionAns, firstAnsId, extraTypes); 1900 types.insert(types.end(), extraTypes.begin(), extraTypes.end()); 1901 1902 OfferAnswer(CHECK_SUCCESS); 1903 1904 oHasStream = HasMediaStream(GetLocalTracks(*mSessionOff)); 1905 aHasStream = HasMediaStream(GetLocalTracks(*mSessionAns)); 1906 1907 ASSERT_EQ(oHasStream, !GetLocalUniqueStreamIds(*mSessionOff).empty()); 1908 ASSERT_EQ(aHasStream, !GetLocalUniqueStreamIds(*mSessionAns).empty()); 1909 ASSERT_EQ(aHasStream, !GetRemoteUniqueStreamIds(*mSessionOff).empty()); 1910 ASSERT_EQ(oHasStream, !GetRemoteUniqueStreamIds(*mSessionAns).empty()); 1911 if (oHasStream) { 1912 ASSERT_STREQ(firstOffId.c_str(), 1913 GetFirstLocalStreamId(*mSessionOff).c_str()); 1914 } 1915 if (aHasStream) { 1916 ASSERT_STREQ(firstAnsId.c_str(), 1917 GetFirstLocalStreamId(*mSessionAns).c_str()); 1918 1919 auto oHasStream = HasMediaStream(GetLocalTracks(*mSessionOff)); 1920 auto aHasStream = HasMediaStream(GetLocalTracks(*mSessionAns)); 1921 ASSERT_EQ(oHasStream, !GetLocalUniqueStreamIds(*mSessionOff).empty()); 1922 ASSERT_EQ(aHasStream, !GetLocalUniqueStreamIds(*mSessionAns).empty()); 1923 } 1924 } 1925 1926 // The JSEP draft explicitly forbids changing the msid on an m-section, but 1927 // that is a bug. 1928 TEST_P(JsepSessionTest, RenegotiationOffererChangesMsid) { 1929 AddTracks(*mSessionOff); 1930 AddTracks(*mSessionAns); 1931 1932 OfferAnswer(); 1933 1934 std::string offer = CreateOffer(); 1935 SetLocalOffer(offer); 1936 1937 JsepTransceiver* transceiver = GetNegotiatedTransceiver(*mSessionOff, 0); 1938 ASSERT_TRUE(transceiver); 1939 if (transceiver->GetMediaType() == SdpMediaSection::kApplication) { 1940 return; 1941 } 1942 std::string streamId = transceiver->mSendTrack.GetStreamIds()[0]; 1943 std::string msidToReplace("a=msid:"); 1944 msidToReplace += streamId; 1945 size_t msidOffset = offer.find(msidToReplace); 1946 ASSERT_NE(std::string::npos, msidOffset); 1947 offer.replace(msidOffset, msidToReplace.size(), "a=msid:foo"); 1948 1949 SetRemoteOffer(offer); 1950 transceiver = GetNegotiatedTransceiver(*mSessionAns, 0); 1951 ASSERT_EQ("foo", transceiver->mRecvTrack.GetStreamIds()[0]); 1952 1953 std::string answer = CreateAnswer(); 1954 SetLocalAnswer(answer); 1955 SetRemoteAnswer(answer); 1956 } 1957 1958 // The JSEP draft explicitly forbids changing the msid on an m-section, but 1959 // that is a bug. 1960 TEST_P(JsepSessionTest, RenegotiationAnswererChangesMsid) { 1961 AddTracks(*mSessionOff); 1962 AddTracks(*mSessionAns); 1963 1964 OfferAnswer(); 1965 1966 JsepTransceiver* transceiver = GetNegotiatedTransceiver(*mSessionOff, 0); 1967 ASSERT_TRUE(transceiver); 1968 if (transceiver->GetMediaType() == SdpMediaSection::kApplication) { 1969 return; 1970 } 1971 1972 std::string offer = CreateOffer(); 1973 SetLocalOffer(offer); 1974 SetRemoteOffer(offer); 1975 std::string answer = CreateAnswer(); 1976 SetLocalAnswer(answer); 1977 1978 transceiver = GetNegotiatedTransceiver(*mSessionAns, 0); 1979 ASSERT_TRUE(transceiver); 1980 if (transceiver->GetMediaType() == SdpMediaSection::kApplication) { 1981 return; 1982 } 1983 std::string streamId = transceiver->mSendTrack.GetStreamIds()[0]; 1984 std::string msidToReplace("a=msid:"); 1985 msidToReplace += streamId; 1986 size_t msidOffset = answer.find(msidToReplace); 1987 ASSERT_NE(std::string::npos, msidOffset); 1988 answer.replace(msidOffset, msidToReplace.size(), "a=msid:foo"); 1989 1990 SetRemoteAnswer(answer); 1991 1992 transceiver = GetNegotiatedTransceiver(*mSessionOff, 0); 1993 ASSERT_EQ("foo", transceiver->mRecvTrack.GetStreamIds()[0]); 1994 } 1995 1996 TEST_P(JsepSessionTest, RenegotiationOffererStopsTransceiver) { 1997 AddTracks(*mSessionOff); 1998 AddTracks(*mSessionAns); 1999 if (types.back() == SdpMediaSection::kApplication) { 2000 return; 2001 } 2002 2003 OfferAnswer(); 2004 2005 std::vector<JsepTransceiver> origOffererTransceivers = 2006 GetTransceivers(*mSessionOff); 2007 std::vector<JsepTransceiver> origAnswererTransceivers = 2008 GetTransceivers(*mSessionAns); 2009 2010 JsepTransceiver lastTransceiver = GetTransceivers(*mSessionOff).back(); 2011 // Avoid bundle transport side effects; don't stop the BUNDLE-tag! 2012 lastTransceiver.Stop(); 2013 mSessionOff->SetTransceiver(lastTransceiver); 2014 JsepTrack removedTrack(lastTransceiver.mSendTrack); 2015 2016 OfferAnswer(CHECK_SUCCESS); 2017 2018 // Last m-section should be disabled 2019 auto offer = GetParsedLocalDescription(*mSessionOff); 2020 const SdpMediaSection* msection = 2021 &offer->GetMediaSection(offer->GetMediaSectionCount() - 1); 2022 ASSERT_TRUE(msection); 2023 ValidateDisabledMSection(msection); 2024 2025 // Last m-section should be disabled 2026 auto answer = GetParsedLocalDescription(*mSessionAns); 2027 msection = &answer->GetMediaSection(answer->GetMediaSectionCount() - 1); 2028 ASSERT_TRUE(msection); 2029 ValidateDisabledMSection(msection); 2030 2031 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2032 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2033 2034 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2035 2036 ASSERT_FALSE(origOffererTransceivers.back().IsStopped()); 2037 ASSERT_TRUE(newOffererTransceivers.back().IsStopped()); 2038 2039 ASSERT_FALSE(origAnswererTransceivers.back().IsStopped()); 2040 ASSERT_TRUE(newAnswererTransceivers.back().IsStopped()); 2041 RemoveLastN(origOffererTransceivers, 1); // Ignore this one 2042 RemoveLastN(newOffererTransceivers, 1); // Ignore this one 2043 RemoveLastN(origAnswererTransceivers, 1); // Ignore this one 2044 RemoveLastN(newAnswererTransceivers, 1); // Ignore this one 2045 2046 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 2047 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 2048 } 2049 2050 TEST_P(JsepSessionTest, RenegotiationAnswererStopsTransceiver) { 2051 AddTracks(*mSessionOff); 2052 AddTracks(*mSessionAns); 2053 if (types.back() == SdpMediaSection::kApplication) { 2054 return; 2055 } 2056 2057 OfferAnswer(); 2058 2059 std::vector<JsepTransceiver> origOffererTransceivers = 2060 GetTransceivers(*mSessionOff); 2061 std::vector<JsepTransceiver> origAnswererTransceivers = 2062 GetTransceivers(*mSessionAns); 2063 2064 GetTransceivers(*mSessionAns).back().Stop(); 2065 2066 OfferAnswer(CHECK_SUCCESS); 2067 ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded()); 2068 2069 // Last m-section should be sendrecv 2070 auto offer = GetParsedLocalDescription(*mSessionOff); 2071 const SdpMediaSection* msection = 2072 &offer->GetMediaSection(offer->GetMediaSectionCount() - 1); 2073 ASSERT_TRUE(msection); 2074 ASSERT_TRUE(msection->IsReceiving()); 2075 ASSERT_TRUE(msection->IsSending()); 2076 2077 // Last m-section should be sendrecv; answerer does not reject! 2078 auto answer = GetParsedLocalDescription(*mSessionAns); 2079 msection = &answer->GetMediaSection(answer->GetMediaSectionCount() - 1); 2080 ASSERT_TRUE(msection); 2081 ASSERT_TRUE(msection->IsReceiving()); 2082 ASSERT_TRUE(msection->IsSending()); 2083 2084 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2085 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2086 2087 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2088 2089 ASSERT_FALSE(origOffererTransceivers.back().IsStopped()); 2090 ASSERT_FALSE(newOffererTransceivers.back().IsStopped()); 2091 ASSERT_FALSE(origAnswererTransceivers.back().IsStopped()); 2092 ASSERT_TRUE(newAnswererTransceivers.back().IsStopping()); 2093 ASSERT_FALSE(newAnswererTransceivers.back().IsStopped()); 2094 RemoveLastN(origAnswererTransceivers, 1); // Ignore this one 2095 RemoveLastN(newAnswererTransceivers, 1); // Ignore this one 2096 2097 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 2098 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 2099 } 2100 2101 TEST_P(JsepSessionTest, RenegotiationBothStopSameTransceiver) { 2102 AddTracks(*mSessionOff); 2103 AddTracks(*mSessionAns); 2104 if (types.back() == SdpMediaSection::kApplication) { 2105 return; 2106 } 2107 2108 OfferAnswer(); 2109 2110 std::vector<JsepTransceiver> origOffererTransceivers = 2111 GetTransceivers(*mSessionOff); 2112 std::vector<JsepTransceiver> origAnswererTransceivers = 2113 GetTransceivers(*mSessionAns); 2114 2115 // Avoid bundle transport side effects; don't stop the BUNDLE-tag! 2116 GetTransceivers(*mSessionOff).back().Stop(); 2117 JsepTrack removedTrackOffer(GetTransceivers(*mSessionOff).back().mSendTrack); 2118 GetTransceivers(*mSessionAns).back().Stop(); 2119 JsepTrack removedTrackAnswer(GetTransceivers(*mSessionAns).back().mSendTrack); 2120 ASSERT_TRUE(mSessionOff->CheckNegotiationNeeded()); 2121 ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded()); 2122 2123 OfferAnswer(CHECK_SUCCESS); 2124 ASSERT_FALSE(mSessionOff->CheckNegotiationNeeded()); 2125 ASSERT_FALSE(mSessionAns->CheckNegotiationNeeded()); 2126 2127 // Last m-section should be disabled 2128 auto offer = GetParsedLocalDescription(*mSessionOff); 2129 const SdpMediaSection* msection = 2130 &offer->GetMediaSection(offer->GetMediaSectionCount() - 1); 2131 ASSERT_TRUE(msection); 2132 ValidateDisabledMSection(msection); 2133 2134 // Last m-section should be disabled 2135 auto answer = GetParsedLocalDescription(*mSessionAns); 2136 msection = &answer->GetMediaSection(answer->GetMediaSectionCount() - 1); 2137 ASSERT_TRUE(msection); 2138 ValidateDisabledMSection(msection); 2139 2140 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2141 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2142 2143 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2144 2145 ASSERT_FALSE(origOffererTransceivers.back().IsStopped()); 2146 ASSERT_TRUE(newOffererTransceivers.back().IsStopped()); 2147 ASSERT_FALSE(origAnswererTransceivers.back().IsStopped()); 2148 ASSERT_TRUE(newAnswererTransceivers.back().IsStopped()); 2149 RemoveLastN(origOffererTransceivers, 1); // Ignore this one 2150 RemoveLastN(newOffererTransceivers, 1); // Ignore this one 2151 RemoveLastN(origAnswererTransceivers, 1); // Ignore this one 2152 RemoveLastN(newAnswererTransceivers, 1); // Ignore this one 2153 2154 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 2155 ASSERT_TRUE(Equals(origAnswererTransceivers, newAnswererTransceivers)); 2156 } 2157 2158 TEST_P(JsepSessionTest, RenegotiationBothStopTransceiverThenAddTrack) { 2159 AddTracks(*mSessionOff); 2160 AddTracks(*mSessionAns); 2161 if (types.back() == SdpMediaSection::kApplication) { 2162 return; 2163 } 2164 2165 SdpMediaSection::MediaType removedType = types.back(); 2166 2167 OfferAnswer(); 2168 2169 // Avoid bundle transport side effects; don't stop the BUNDLE-tag! 2170 GetTransceivers(*mSessionOff).back().Stop(); 2171 JsepTrack removedTrackOffer(GetTransceivers(*mSessionOff).back().mSendTrack); 2172 GetTransceivers(*mSessionOff).back().Stop(); 2173 JsepTrack removedTrackAnswer(GetTransceivers(*mSessionOff).back().mSendTrack); 2174 2175 OfferAnswer(CHECK_SUCCESS); 2176 2177 std::vector<JsepTransceiver> origOffererTransceivers = 2178 GetTransceivers(*mSessionOff); 2179 std::vector<JsepTransceiver> origAnswererTransceivers = 2180 GetTransceivers(*mSessionAns); 2181 2182 std::vector<SdpMediaSection::MediaType> extraTypes; 2183 extraTypes.push_back(removedType); 2184 AddTracks(*mSessionAns, extraTypes); 2185 AddTracks(*mSessionOff, extraTypes); 2186 types.insert(types.end(), extraTypes.begin(), extraTypes.end()); 2187 2188 OfferAnswer(CHECK_SUCCESS); 2189 2190 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2191 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2192 2193 ASSERT_EQ(origOffererTransceivers.size() + 1, newOffererTransceivers.size()); 2194 ASSERT_EQ(origAnswererTransceivers.size() + 1, 2195 newAnswererTransceivers.size()); 2196 2197 // Ensure that the m-section was re-used; no gaps 2198 ASSERT_EQ(origOffererTransceivers.back().GetLevel(), 2199 newOffererTransceivers.back().GetLevel()); 2200 2201 ASSERT_EQ(origAnswererTransceivers.back().GetLevel(), 2202 newAnswererTransceivers.back().GetLevel()); 2203 } 2204 2205 TEST_P(JsepSessionTest, RenegotiationBothStopTransceiverDifferentMsection) { 2206 AddTracks(*mSessionOff); 2207 AddTracks(*mSessionAns); 2208 2209 if (types.size() < 2) { 2210 return; 2211 } 2212 2213 if (GetTransceivers(*mSessionOff)[0].GetMediaType() == 2214 SdpMediaSection::kApplication || 2215 GetTransceivers(*mSessionOff)[1].GetMediaType() == 2216 SdpMediaSection::kApplication) { 2217 return; 2218 } 2219 2220 OfferAnswer(); 2221 2222 GetTransceivers(*mSessionOff)[0].Stop(); 2223 GetTransceivers(*mSessionOff)[1].Stop(); 2224 2225 OfferAnswer(CHECK_SUCCESS); 2226 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 2227 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsStopped()); 2228 } 2229 2230 TEST_P(JsepSessionTest, RenegotiationOffererChangesStreamId) { 2231 AddTracks(*mSessionOff); 2232 AddTracks(*mSessionAns); 2233 2234 if (GetTransceivers(*mSessionOff)[0].GetMediaType() == 2235 SdpMediaSection::kApplication) { 2236 return; 2237 } 2238 2239 OfferAnswer(); 2240 2241 GetTransceivers(*mSessionOff)[0].mSendTrack.UpdateStreamIds( 2242 std::vector<std::string>(1, "newstream")); 2243 2244 OfferAnswer(CHECK_SUCCESS); 2245 2246 ASSERT_EQ("newstream", 2247 GetTransceivers(*mSessionAns)[0].mRecvTrack.GetStreamIds()[0]); 2248 } 2249 2250 TEST_P(JsepSessionTest, RenegotiationAnswererChangesStreamId) { 2251 AddTracks(*mSessionOff); 2252 AddTracks(*mSessionAns); 2253 2254 if (GetTransceivers(*mSessionOff)[0].GetMediaType() == 2255 SdpMediaSection::kApplication) { 2256 return; 2257 } 2258 2259 OfferAnswer(); 2260 2261 GetTransceivers(*mSessionAns)[0].mSendTrack.UpdateStreamIds( 2262 std::vector<std::string>(1, "newstream")); 2263 2264 OfferAnswer(CHECK_SUCCESS); 2265 2266 ASSERT_EQ("newstream", 2267 GetTransceivers(*mSessionOff)[0].mRecvTrack.GetStreamIds()[0]); 2268 } 2269 2270 // Tests whether auto-assigned remote msids (ie; what happens when the other 2271 // side doesn't use msid attributes) are stable across renegotiation. 2272 TEST_P(JsepSessionTest, RenegotiationAutoAssignedMsidIsStable) { 2273 AddTracks(*mSessionOff); 2274 std::string offer = CreateOffer(); 2275 SetLocalOffer(offer); 2276 SetRemoteOffer(offer); 2277 AddTracks(*mSessionAns); 2278 std::string answer = CreateAnswer(); 2279 SetLocalAnswer(answer); 2280 2281 DisableMsid(&answer); 2282 2283 SetRemoteAnswer(answer, CHECK_SUCCESS); 2284 2285 std::vector<JsepTransceiver> origOffererTransceivers = 2286 GetTransceivers(*mSessionOff); 2287 std::vector<JsepTransceiver> origAnswererTransceivers = 2288 GetTransceivers(*mSessionAns); 2289 2290 ASSERT_EQ(origOffererTransceivers.size(), origAnswererTransceivers.size()); 2291 for (size_t i = 0; i < origOffererTransceivers.size(); ++i) { 2292 ASSERT_FALSE(IsNull(origOffererTransceivers[i].mRecvTrack)); 2293 ASSERT_FALSE(IsNull(origAnswererTransceivers[i].mSendTrack)); 2294 // These should not match since we've monkeyed with the msid 2295 ASSERT_NE(origOffererTransceivers[i].mRecvTrack.GetStreamIds(), 2296 origAnswererTransceivers[i].mSendTrack.GetStreamIds()); 2297 } 2298 2299 offer = CreateOffer(); 2300 SetLocalOffer(offer); 2301 SetRemoteOffer(offer); 2302 answer = CreateAnswer(); 2303 SetLocalAnswer(answer); 2304 2305 DisableMsid(&answer); 2306 2307 SetRemoteAnswer(answer, CHECK_SUCCESS); 2308 2309 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2310 2311 ASSERT_TRUE(Equals(origOffererTransceivers, newOffererTransceivers)); 2312 } 2313 2314 TEST_P(JsepSessionTest, RenegotiationOffererDisablesTelephoneEvent) { 2315 AddTracks(*mSessionOff); 2316 AddTracks(*mSessionAns); 2317 OfferAnswer(); 2318 2319 // check all the audio tracks to make sure they have 2 codecs (109 and 101), 2320 // and dtmf is enabled on all audio tracks 2321 std::vector<JsepTrack> tracks; 2322 for (const auto& transceiver : GetTransceivers(*mSessionOff)) { 2323 tracks.push_back(transceiver.mSendTrack); 2324 tracks.push_back(transceiver.mRecvTrack); 2325 } 2326 2327 for (const JsepTrack& track : tracks) { 2328 if (track.GetMediaType() != SdpMediaSection::kAudio) { 2329 continue; 2330 } 2331 const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails(); 2332 ASSERT_EQ(1U, details->GetEncodingCount()); 2333 const JsepTrackEncoding& encoding = details->GetEncoding(0); 2334 ASSERT_EQ(5U, encoding.GetCodecs().size()); 2335 ASSERT_TRUE(encoding.HasFormat("109")); 2336 ASSERT_TRUE(encoding.HasFormat("101")); 2337 for (const auto& codec : encoding.GetCodecs()) { 2338 ASSERT_TRUE(codec); 2339 // we can cast here because we've already checked for audio track 2340 const JsepAudioCodecDescription* audioCodec = 2341 static_cast<const JsepAudioCodecDescription*>(codec.get()); 2342 ASSERT_TRUE(audioCodec->mDtmfEnabled); 2343 } 2344 } 2345 2346 std::string offer = CreateOffer(); 2347 ReplaceInSdp(&offer, "8 101", "8"); 2348 ReplaceInSdp(&offer, "a=fmtp:101 0-15\r\n", ""); 2349 ReplaceInSdp(&offer, "a=rtpmap:101 telephone-event/8000/1\r\n", ""); 2350 std::cerr << "modified OFFER: " << offer << std::endl; 2351 2352 SetLocalOffer(offer); 2353 SetRemoteOffer(offer); 2354 std::string answer = CreateAnswer(); 2355 SetLocalAnswer(answer); 2356 SetRemoteAnswer(answer); 2357 2358 // check all the audio tracks to make sure they have 1 codec (109), 2359 // and dtmf is disabled on all audio tracks 2360 tracks.clear(); 2361 for (const auto& transceiver : GetTransceivers(*mSessionOff)) { 2362 tracks.push_back(transceiver.mSendTrack); 2363 tracks.push_back(transceiver.mRecvTrack); 2364 } 2365 2366 for (const JsepTrack& track : tracks) { 2367 if (track.GetMediaType() != SdpMediaSection::kAudio) { 2368 continue; 2369 } 2370 const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails(); 2371 ASSERT_EQ(1U, details->GetEncodingCount()); 2372 const JsepTrackEncoding& encoding = details->GetEncoding(0); 2373 auto expectedSize = (track.GetDirection() != sdp::kSend) ? 5U : 4U; 2374 ASSERT_EQ(expectedSize, encoding.GetCodecs().size()); 2375 ASSERT_TRUE(encoding.HasFormat("109")); 2376 // we can cast here because we've already checked for audio track 2377 const JsepAudioCodecDescription* audioCodec = 2378 static_cast<const JsepAudioCodecDescription*>( 2379 encoding.GetCodecs()[0].get()); 2380 ASSERT_TRUE(audioCodec); 2381 ASSERT_EQ(track.GetDirection() != sdp::kSend, audioCodec->mDtmfEnabled); 2382 } 2383 } 2384 2385 // Tests behavior when the answerer does not use msid in the initial exchange, 2386 // but does on renegotiation. 2387 TEST_P(JsepSessionTest, RenegotiationAnswererEnablesMsid) { 2388 AddTracks(*mSessionOff); 2389 std::string offer = CreateOffer(); 2390 SetLocalOffer(offer); 2391 SetRemoteOffer(offer); 2392 AddTracks(*mSessionAns); 2393 std::string answer = CreateAnswer(); 2394 SetLocalAnswer(answer); 2395 2396 DisableMsid(&answer); 2397 2398 SetRemoteAnswer(answer, CHECK_SUCCESS); 2399 2400 std::vector<JsepTransceiver> origOffererTransceivers = 2401 GetTransceivers(*mSessionOff); 2402 std::vector<JsepTransceiver> origAnswererTransceivers = 2403 GetTransceivers(*mSessionAns); 2404 2405 offer = CreateOffer(); 2406 SetLocalOffer(offer); 2407 SetRemoteOffer(offer); 2408 answer = CreateAnswer(); 2409 SetLocalAnswer(answer); 2410 SetRemoteAnswer(answer, CHECK_SUCCESS); 2411 2412 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2413 2414 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2415 for (size_t i = 0; i < origOffererTransceivers.size(); ++i) { 2416 ASSERT_EQ(origOffererTransceivers[i].mRecvTrack.GetMediaType(), 2417 newOffererTransceivers[i].mRecvTrack.GetMediaType()); 2418 2419 ASSERT_TRUE(Equals(origOffererTransceivers[i].mSendTrack, 2420 newOffererTransceivers[i].mSendTrack)); 2421 ASSERT_TRUE(Equals(origOffererTransceivers[i].mTransport, 2422 newOffererTransceivers[i].mTransport)); 2423 2424 if (origOffererTransceivers[i].mRecvTrack.GetMediaType() == 2425 SdpMediaSection::kApplication) { 2426 ASSERT_TRUE(Equals(origOffererTransceivers[i].mRecvTrack, 2427 newOffererTransceivers[i].mRecvTrack)); 2428 } else { 2429 // This should be the only difference 2430 ASSERT_FALSE(Equals(origOffererTransceivers[i].mRecvTrack, 2431 newOffererTransceivers[i].mRecvTrack)); 2432 } 2433 } 2434 } 2435 2436 TEST_P(JsepSessionTest, RenegotiationAnswererDisablesMsid) { 2437 AddTracks(*mSessionOff); 2438 std::string offer = CreateOffer(); 2439 SetLocalOffer(offer); 2440 SetRemoteOffer(offer); 2441 AddTracks(*mSessionAns); 2442 std::string answer = CreateAnswer(); 2443 SetLocalAnswer(answer); 2444 SetRemoteAnswer(answer, CHECK_SUCCESS); 2445 2446 std::vector<JsepTransceiver> origOffererTransceivers = 2447 GetTransceivers(*mSessionOff); 2448 std::vector<JsepTransceiver> origAnswererTransceivers = 2449 GetTransceivers(*mSessionAns); 2450 2451 offer = CreateOffer(); 2452 SetLocalOffer(offer); 2453 SetRemoteOffer(offer); 2454 answer = CreateAnswer(); 2455 SetLocalAnswer(answer); 2456 2457 DisableMsid(&answer); 2458 2459 SetRemoteAnswer(answer, CHECK_SUCCESS); 2460 2461 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2462 2463 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2464 for (size_t i = 0; i < origOffererTransceivers.size(); ++i) { 2465 ASSERT_EQ(origOffererTransceivers[i].mRecvTrack.GetMediaType(), 2466 newOffererTransceivers[i].mRecvTrack.GetMediaType()); 2467 2468 ASSERT_TRUE(Equals(origOffererTransceivers[i].mSendTrack, 2469 newOffererTransceivers[i].mSendTrack)); 2470 ASSERT_TRUE(Equals(origOffererTransceivers[i].mTransport, 2471 newOffererTransceivers[i].mTransport)); 2472 2473 if (origOffererTransceivers[i].mRecvTrack.GetMediaType() == 2474 SdpMediaSection::kApplication) { 2475 ASSERT_TRUE(Equals(origOffererTransceivers[i].mRecvTrack, 2476 newOffererTransceivers[i].mRecvTrack)); 2477 } else { 2478 // This should be the only difference 2479 ASSERT_FALSE(Equals(origOffererTransceivers[i].mRecvTrack, 2480 newOffererTransceivers[i].mRecvTrack)); 2481 } 2482 } 2483 } 2484 2485 // Tests behavior when offerer does not use bundle on the initial offer/answer, 2486 // but does on renegotiation. 2487 TEST_P(JsepSessionTest, RenegotiationOffererEnablesBundle) { 2488 AddTracks(*mSessionOff); 2489 AddTracks(*mSessionAns); 2490 2491 if (types.size() < 2) { 2492 // No bundle will happen here. 2493 return; 2494 } 2495 2496 std::string offer = CreateOffer(); 2497 2498 DisableBundle(&offer); 2499 2500 SetLocalOffer(offer); 2501 SetRemoteOffer(offer); 2502 std::string answer = CreateAnswer(); 2503 SetLocalAnswer(answer); 2504 SetRemoteAnswer(answer); 2505 2506 std::vector<JsepTransceiver> origOffererTransceivers = 2507 GetTransceivers(*mSessionOff); 2508 std::vector<JsepTransceiver> origAnswererTransceivers = 2509 GetTransceivers(*mSessionAns); 2510 2511 OfferAnswer(); 2512 2513 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2514 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2515 2516 ASSERT_EQ(newOffererTransceivers.size(), newAnswererTransceivers.size()); 2517 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2518 ASSERT_EQ(origAnswererTransceivers.size(), newAnswererTransceivers.size()); 2519 2520 for (size_t i = 0; i < newOffererTransceivers.size(); ++i) { 2521 // No bundle initially 2522 ASSERT_FALSE(origOffererTransceivers[i].HasBundleLevel()); 2523 ASSERT_FALSE(origAnswererTransceivers[i].HasBundleLevel()); 2524 if (i != 0) { 2525 ASSERT_FALSE(Equals(origOffererTransceivers[0].mTransport, 2526 origOffererTransceivers[i].mTransport)); 2527 ASSERT_FALSE(Equals(origAnswererTransceivers[0].mTransport, 2528 origAnswererTransceivers[i].mTransport)); 2529 } 2530 2531 // Verify that bundle worked after renegotiation 2532 ASSERT_TRUE(newOffererTransceivers[i].HasBundleLevel()); 2533 ASSERT_TRUE(newAnswererTransceivers[i].HasBundleLevel()); 2534 ASSERT_TRUE(Equals(newOffererTransceivers[0].mTransport, 2535 newOffererTransceivers[i].mTransport)); 2536 ASSERT_TRUE(Equals(newAnswererTransceivers[0].mTransport, 2537 newAnswererTransceivers[i].mTransport)); 2538 } 2539 } 2540 2541 TEST_P(JsepSessionTest, RenegotiationOffererDisablesBundleTransport) { 2542 AddTracks(*mSessionOff); 2543 AddTracks(*mSessionAns); 2544 2545 if (types.size() < 2) { 2546 return; 2547 } 2548 2549 OfferAnswer(); 2550 2551 auto stopped = GetTransceiverByLevel(*mSessionOff, 0); 2552 stopped->Stop(); 2553 mSessionOff->SetTransceiver(*stopped); 2554 2555 std::vector<JsepTransceiver> origOffererTransceivers = 2556 GetTransceivers(*mSessionOff); 2557 std::vector<JsepTransceiver> origAnswererTransceivers = 2558 GetTransceivers(*mSessionAns); 2559 2560 OfferAnswer(CHECK_SUCCESS); 2561 2562 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2563 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2564 2565 ASSERT_EQ(newOffererTransceivers.size(), newAnswererTransceivers.size()); 2566 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2567 ASSERT_EQ(origAnswererTransceivers.size(), newAnswererTransceivers.size()); 2568 2569 Maybe<JsepTransceiver> ot0 = GetTransceiverByLevel(newOffererTransceivers, 0); 2570 Maybe<JsepTransceiver> at0 = 2571 GetTransceiverByLevel(newAnswererTransceivers, 0); 2572 ASSERT_FALSE(ot0->HasBundleLevel()); 2573 ASSERT_FALSE(at0->HasBundleLevel()); 2574 2575 ASSERT_FALSE( 2576 Equals(ot0->mTransport, 2577 GetTransceiverByLevel(origOffererTransceivers, 0)->mTransport)); 2578 ASSERT_FALSE( 2579 Equals(at0->mTransport, 2580 GetTransceiverByLevel(origAnswererTransceivers, 0)->mTransport)); 2581 2582 ASSERT_EQ(0U, ot0->mTransport.mComponents); 2583 ASSERT_EQ(0U, at0->mTransport.mComponents); 2584 2585 for (size_t i = 1; i < types.size() - 1; ++i) { 2586 Maybe<JsepTransceiver> ot = 2587 GetTransceiverByLevel(newOffererTransceivers, i); 2588 Maybe<JsepTransceiver> at = 2589 GetTransceiverByLevel(newAnswererTransceivers, i); 2590 ASSERT_TRUE(ot->HasBundleLevel()); 2591 ASSERT_TRUE(at->HasBundleLevel()); 2592 ASSERT_EQ(1U, ot->BundleLevel()); 2593 ASSERT_EQ(1U, at->BundleLevel()); 2594 ASSERT_FALSE(Equals(ot0->mTransport, ot->mTransport)); 2595 ASSERT_FALSE(Equals(at0->mTransport, at->mTransport)); 2596 } 2597 } 2598 2599 TEST_P(JsepSessionTest, RenegotiationAnswererDoesNotRejectStoppedTransceiver) { 2600 AddTracks(*mSessionOff); 2601 AddTracks(*mSessionAns); 2602 2603 if (types.size() < 2) { 2604 return; 2605 } 2606 2607 OfferAnswer(); 2608 2609 std::vector<JsepTransceiver> origOffererTransceivers = 2610 GetTransceivers(*mSessionOff); 2611 std::vector<JsepTransceiver> origAnswererTransceivers = 2612 GetTransceivers(*mSessionAns); 2613 2614 auto stopped = GetTransceiverByLevel(*mSessionAns, 0); 2615 stopped->Stop(); 2616 mSessionAns->SetTransceiver(*stopped); 2617 ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded()); 2618 2619 OfferAnswer(CHECK_SUCCESS); 2620 ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded()); 2621 2622 auto newOffererTransceivers = GetTransceivers(*mSessionOff); 2623 auto newAnswererTransceivers = GetTransceivers(*mSessionAns); 2624 2625 DumpTransceivers(*mSessionAns); 2626 2627 ASSERT_EQ(newOffererTransceivers.size(), newAnswererTransceivers.size()); 2628 ASSERT_EQ(origOffererTransceivers.size(), newOffererTransceivers.size()); 2629 ASSERT_EQ(origAnswererTransceivers.size(), newAnswererTransceivers.size()); 2630 2631 Maybe<JsepTransceiver> ot0 = GetTransceiverByLevel(newOffererTransceivers, 0); 2632 Maybe<JsepTransceiver> at0 = 2633 GetTransceiverByLevel(newAnswererTransceivers, 0); 2634 ASSERT_TRUE(ot0->HasBundleLevel()); 2635 ASSERT_TRUE(at0->HasBundleLevel()); 2636 2637 ASSERT_TRUE( 2638 Equals(ot0->mTransport, 2639 GetTransceiverByLevel(origOffererTransceivers, 0)->mTransport)); 2640 ASSERT_TRUE( 2641 Equals(at0->mTransport, 2642 GetTransceiverByLevel(origAnswererTransceivers, 0)->mTransport)); 2643 2644 ASSERT_EQ(1U, ot0->mTransport.mComponents); 2645 ASSERT_EQ(1U, at0->mTransport.mComponents); 2646 2647 for (size_t i = 1; i < newOffererTransceivers.size(); ++i) { 2648 auto ot = GetTransceiverByLevel(newOffererTransceivers, i); 2649 auto at = GetTransceiverByLevel(newAnswererTransceivers, i); 2650 auto otWithTransport = GetTransceiverByLevel(newOffererTransceivers, 0); 2651 auto atWithTransport = GetTransceiverByLevel(newAnswererTransceivers, 0); 2652 ASSERT_TRUE(ot->HasBundleLevel()); 2653 ASSERT_TRUE(at->HasBundleLevel()); 2654 ASSERT_EQ(0U, ot->BundleLevel()); 2655 ASSERT_EQ(0U, at->BundleLevel()); 2656 ASSERT_TRUE(Equals(otWithTransport->mTransport, ot->mTransport)); 2657 ASSERT_TRUE(Equals(atWithTransport->mTransport, at->mTransport)); 2658 } 2659 } 2660 2661 TEST_P(JsepSessionTest, ParseRejectsBadMediaFormat) { 2662 AddTracks(*mSessionOff); 2663 if (types.front() == SdpMediaSection::MediaType::kApplication) { 2664 return; 2665 } 2666 std::string offer = CreateOffer(); 2667 UniquePtr<Sdp> munge(Parse(offer)); 2668 SdpMediaSection& mediaSection = munge->GetMediaSection(0); 2669 mediaSection.AddCodec("75", "DummyFormatVal", 8000, 1); 2670 std::string sdpString = munge->ToString(); 2671 JsepSession::Result result = 2672 mSessionOff->SetLocalDescription(kJsepSdpOffer, sdpString); 2673 ASSERT_EQ(dom::PCError::OperationError, *result.mError); 2674 } 2675 2676 TEST_P(JsepSessionTest, FullCallWithCandidates) { 2677 AddTracks(*mSessionOff); 2678 std::string offer = CreateOffer(); 2679 SetLocalOffer(offer); 2680 mOffCandidates->Gather(*mSessionOff); 2681 2682 UniquePtr<Sdp> localOffer( 2683 Parse(mSessionOff->GetLocalDescription(kJsepDescriptionPending))); 2684 for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) { 2685 std::string id = GetTransportId(*mSessionOff, i); 2686 bool bundleOnly = 2687 localOffer->GetMediaSection(i).GetAttributeList().HasAttribute( 2688 SdpAttribute::kBundleOnlyAttribute); 2689 mOffCandidates->CheckRtpCandidates( 2690 !bundleOnly, localOffer->GetMediaSection(i), id, 2691 "Local offer after gathering should have RTP candidates " 2692 "(unless bundle-only)"); 2693 mOffCandidates->CheckDefaultRtpCandidate( 2694 !bundleOnly, localOffer->GetMediaSection(i), id, 2695 "Local offer after gathering should have a default RTP candidate " 2696 "(unless bundle-only)"); 2697 mOffCandidates->CheckRtcpCandidates( 2698 !bundleOnly && types[i] != SdpMediaSection::kApplication, 2699 localOffer->GetMediaSection(i), id, 2700 "Local offer after gathering should have RTCP candidates " 2701 "(unless m=application or bundle-only)"); 2702 mOffCandidates->CheckDefaultRtcpCandidate( 2703 !bundleOnly && types[i] != SdpMediaSection::kApplication, 2704 localOffer->GetMediaSection(i), id, 2705 "Local offer after gathering should have a default RTCP candidate " 2706 "(unless m=application or bundle-only)"); 2707 CheckEndOfCandidates( 2708 !bundleOnly, localOffer->GetMediaSection(i), 2709 "Local offer after gathering should have an end-of-candidates " 2710 "(unless bundle-only)"); 2711 } 2712 2713 SetRemoteOffer(offer); 2714 mOffCandidates->Trickle(*mSessionAns); 2715 2716 UniquePtr<Sdp> remoteOffer( 2717 Parse(mSessionAns->GetRemoteDescription(kJsepDescriptionPending))); 2718 for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) { 2719 std::string id = GetTransportId(*mSessionOff, i); 2720 bool bundleOnly = 2721 remoteOffer->GetMediaSection(i).GetAttributeList().HasAttribute( 2722 SdpAttribute::kBundleOnlyAttribute); 2723 mOffCandidates->CheckRtpCandidates( 2724 !bundleOnly, remoteOffer->GetMediaSection(i), id, 2725 "Remote offer after trickle should have RTP candidates " 2726 "(unless bundle-only)"); 2727 mOffCandidates->CheckDefaultRtpCandidate( 2728 false, remoteOffer->GetMediaSection(i), id, 2729 "Remote offer after trickle should not have a default RTP candidate."); 2730 mOffCandidates->CheckRtcpCandidates( 2731 !bundleOnly && types[i] != SdpMediaSection::kApplication, 2732 remoteOffer->GetMediaSection(i), id, 2733 "Remote offer after trickle should have RTCP candidates " 2734 "(unless m=application or bundle-only)"); 2735 mOffCandidates->CheckDefaultRtcpCandidate( 2736 false, remoteOffer->GetMediaSection(i), id, 2737 "Remote offer after trickle should not have a default RTCP candidate."); 2738 CheckEndOfCandidates( 2739 true, remoteOffer->GetMediaSection(i), 2740 "Remote offer after trickle should have an end-of-candidates."); 2741 } 2742 2743 AddTracks(*mSessionAns); 2744 std::string answer = CreateAnswer(); 2745 SetLocalAnswer(answer); 2746 // This will gather candidates that mSessionAns knows it doesn't need. 2747 // They should not be present in the SDP. 2748 mAnsCandidates->Gather(*mSessionAns); 2749 2750 UniquePtr<Sdp> localAnswer( 2751 Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent))); 2752 std::string id0 = GetTransportId(*mSessionAns, 0); 2753 for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) { 2754 std::string id = GetTransportId(*mSessionAns, i); 2755 mAnsCandidates->CheckRtpCandidates( 2756 i == 0, localAnswer->GetMediaSection(i), id, 2757 "Local answer after gathering should have RTP candidates on level 0."); 2758 mAnsCandidates->CheckDefaultRtpCandidate( 2759 true, localAnswer->GetMediaSection(i), id0, 2760 "Local answer after gathering should have a default RTP candidate " 2761 "on all levels that matches transport level 0."); 2762 mAnsCandidates->CheckRtcpCandidates( 2763 false, localAnswer->GetMediaSection(i), id, 2764 "Local answer after gathering should not have RTCP candidates " 2765 "(because we're answering with rtcp-mux)"); 2766 mAnsCandidates->CheckDefaultRtcpCandidate( 2767 false, localAnswer->GetMediaSection(i), id, 2768 "Local answer after gathering should not have a default RTCP candidate " 2769 "(because we're answering with rtcp-mux)"); 2770 CheckEndOfCandidates( 2771 i == 0, localAnswer->GetMediaSection(i), 2772 "Local answer after gathering should have an end-of-candidates only for" 2773 " level 0."); 2774 } 2775 2776 SetRemoteAnswer(answer); 2777 mAnsCandidates->Trickle(*mSessionOff); 2778 2779 UniquePtr<Sdp> remoteAnswer( 2780 Parse(mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent))); 2781 for (size_t i = 0; i < remoteAnswer->GetMediaSectionCount(); ++i) { 2782 std::string id = GetTransportId(*mSessionAns, i); 2783 mAnsCandidates->CheckRtpCandidates( 2784 i == 0, remoteAnswer->GetMediaSection(i), id, 2785 "Remote answer after trickle should have RTP candidates on level 0."); 2786 mAnsCandidates->CheckDefaultRtpCandidate( 2787 false, remoteAnswer->GetMediaSection(i), id, 2788 "Remote answer after trickle should not have a default RTP candidate."); 2789 mAnsCandidates->CheckRtcpCandidates( 2790 false, remoteAnswer->GetMediaSection(i), id, 2791 "Remote answer after trickle should not have RTCP candidates " 2792 "(because we're answering with rtcp-mux)"); 2793 mAnsCandidates->CheckDefaultRtcpCandidate( 2794 false, remoteAnswer->GetMediaSection(i), id, 2795 "Remote answer after trickle should not have a default RTCP " 2796 "candidate."); 2797 CheckEndOfCandidates( 2798 true, remoteAnswer->GetMediaSection(i), 2799 "Remote answer after trickle should have an end-of-candidates."); 2800 } 2801 } 2802 2803 TEST_P(JsepSessionTest, RenegotiationWithCandidates) { 2804 AddTracks(*mSessionOff); 2805 std::string offer = CreateOffer(); 2806 SetLocalOffer(offer); 2807 mOffCandidates->Gather(*mSessionOff); 2808 SetRemoteOffer(offer); 2809 mOffCandidates->Trickle(*mSessionAns); 2810 AddTracks(*mSessionAns); 2811 std::string answer = CreateAnswer(); 2812 SetLocalAnswer(answer); 2813 mAnsCandidates->Gather(*mSessionAns); 2814 SetRemoteAnswer(answer); 2815 mAnsCandidates->Trickle(*mSessionOff); 2816 2817 offer = CreateOffer(); 2818 SetLocalOffer(offer); 2819 2820 UniquePtr<Sdp> parsedOffer(Parse(offer)); 2821 std::string id0 = GetTransportId(*mSessionOff, 0); 2822 for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) { 2823 std::string id = GetTransportId(*mSessionOff, i); 2824 mOffCandidates->CheckRtpCandidates( 2825 i == 0, parsedOffer->GetMediaSection(i), id, 2826 "Local reoffer before gathering should have RTP candidates on level 0" 2827 " only."); 2828 mOffCandidates->CheckDefaultRtpCandidate( 2829 i == 0, parsedOffer->GetMediaSection(i), id0, 2830 "Local reoffer before gathering should have a default RTP candidate " 2831 "on level 0 only."); 2832 mOffCandidates->CheckRtcpCandidates( 2833 false, parsedOffer->GetMediaSection(i), id, 2834 "Local reoffer before gathering should not have RTCP candidates."); 2835 mOffCandidates->CheckDefaultRtcpCandidate( 2836 false, parsedOffer->GetMediaSection(i), id, 2837 "Local reoffer before gathering should not have a default RTCP " 2838 "candidate."); 2839 CheckEndOfCandidates( 2840 i == 0, parsedOffer->GetMediaSection(i), 2841 "Local reoffer before gathering should have an end-of-candidates " 2842 "(level 0 only)"); 2843 } 2844 2845 // mSessionAns should generate a reoffer that is similar 2846 std::string otherOffer; 2847 JsepOfferOptions defaultOptions; 2848 JsepSession::Result result = 2849 mSessionAns->CreateOffer(defaultOptions, &otherOffer); 2850 ASSERT_FALSE(result.mError.isSome()); 2851 parsedOffer = Parse(otherOffer); 2852 id0 = GetTransportId(*mSessionAns, 0); 2853 for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) { 2854 std::string id = GetTransportId(*mSessionAns, i); 2855 mAnsCandidates->CheckRtpCandidates( 2856 i == 0, parsedOffer->GetMediaSection(i), id, 2857 "Local reoffer before gathering should have RTP candidates on level 0" 2858 " only. (previous answerer)"); 2859 mAnsCandidates->CheckDefaultRtpCandidate( 2860 i == 0, parsedOffer->GetMediaSection(i), id0, 2861 "Local reoffer before gathering should have a default RTP candidate " 2862 "on level 0 only. (previous answerer)"); 2863 mAnsCandidates->CheckRtcpCandidates( 2864 false, parsedOffer->GetMediaSection(i), id, 2865 "Local reoffer before gathering should not have RTCP candidates." 2866 " (previous answerer)"); 2867 mAnsCandidates->CheckDefaultRtcpCandidate( 2868 false, parsedOffer->GetMediaSection(i), id, 2869 "Local reoffer before gathering should not have a default RTCP " 2870 "candidate. (previous answerer)"); 2871 CheckEndOfCandidates( 2872 i == 0, parsedOffer->GetMediaSection(i), 2873 "Local reoffer before gathering should have an end-of-candidates " 2874 "(level 0 only)"); 2875 } 2876 2877 // Ok, let's continue with the renegotiation 2878 SetRemoteOffer(offer); 2879 2880 // PeerConnection will not re-gather for RTP, but it will for RTCP in case 2881 // the answerer decides to turn off rtcp-mux. 2882 if (types[0] != SdpMediaSection::kApplication) { 2883 mOffCandidates->Gather(*mSessionOff, GetTransportId(*mSessionOff, 0), RTCP); 2884 } 2885 2886 // Since the remaining levels were bundled, PeerConnection will re-gather for 2887 // both RTP and RTCP, in case the answerer rejects bundle, provided 2888 // bundle-only isn't being used. 2889 UniquePtr<Sdp> localOffer( 2890 Parse(mSessionOff->GetLocalDescription(kJsepDescriptionPending))); 2891 for (size_t level = 1; level < types.size(); ++level) { 2892 std::string id = GetTransportId(*mSessionOff, level); 2893 bool bundleOnly = 2894 localOffer->GetMediaSection(level).GetAttributeList().HasAttribute( 2895 SdpAttribute::kBundleOnlyAttribute); 2896 if (!id.empty() && !bundleOnly) { 2897 mOffCandidates->Gather(*mSessionOff, id, RTP); 2898 if (types[level] != SdpMediaSection::kApplication) { 2899 mOffCandidates->Gather(*mSessionOff, id, RTCP); 2900 } 2901 } 2902 } 2903 mOffCandidates->FinishGathering(*mSessionOff); 2904 localOffer = Parse(mSessionOff->GetLocalDescription(kJsepDescriptionPending)); 2905 2906 mOffCandidates->Trickle(*mSessionAns); 2907 2908 for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) { 2909 std::string id = GetTransportId(*mSessionOff, i); 2910 bool bundleOnly = 2911 localOffer->GetMediaSection(i).GetAttributeList().HasAttribute( 2912 SdpAttribute::kBundleOnlyAttribute); 2913 mOffCandidates->CheckRtpCandidates( 2914 !bundleOnly, localOffer->GetMediaSection(i), id, 2915 "Local reoffer after gathering should have RTP candidates " 2916 "(unless bundle-only)"); 2917 mOffCandidates->CheckDefaultRtpCandidate( 2918 !bundleOnly, localOffer->GetMediaSection(i), id, 2919 "Local reoffer after gathering should have a default RTP candidate " 2920 "(unless bundle-only)"); 2921 mOffCandidates->CheckRtcpCandidates( 2922 !bundleOnly && (types[i] != SdpMediaSection::kApplication), 2923 localOffer->GetMediaSection(i), id, 2924 "Local reoffer after gathering should have RTCP candidates " 2925 "(unless m=application or bundle-only)"); 2926 mOffCandidates->CheckDefaultRtcpCandidate( 2927 !bundleOnly && (types[i] != SdpMediaSection::kApplication), 2928 localOffer->GetMediaSection(i), id, 2929 "Local reoffer after gathering should have a default RTCP candidate " 2930 "(unless m=application or bundle-only)"); 2931 CheckEndOfCandidates( 2932 !bundleOnly, localOffer->GetMediaSection(i), 2933 "Local reoffer after gathering should have an end-of-candidates " 2934 "(unless bundle-only)"); 2935 } 2936 2937 UniquePtr<Sdp> remoteOffer( 2938 Parse(mSessionAns->GetRemoteDescription(kJsepDescriptionPending))); 2939 for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) { 2940 bool bundleOnly = 2941 remoteOffer->GetMediaSection(i).GetAttributeList().HasAttribute( 2942 SdpAttribute::kBundleOnlyAttribute); 2943 std::string id = GetTransportId(*mSessionOff, i); 2944 mOffCandidates->CheckRtpCandidates( 2945 !bundleOnly, remoteOffer->GetMediaSection(i), id, 2946 "Remote reoffer after trickle should have RTP candidates " 2947 "(unless bundle-only)"); 2948 mOffCandidates->CheckDefaultRtpCandidate( 2949 i == 0, remoteOffer->GetMediaSection(i), id, 2950 "Remote reoffer should have a default RTP candidate on level 0 " 2951 "(because it was gathered last offer/answer)."); 2952 mOffCandidates->CheckRtcpCandidates( 2953 !bundleOnly && types[i] != SdpMediaSection::kApplication, 2954 remoteOffer->GetMediaSection(i), id, 2955 "Remote reoffer after trickle should have RTCP candidates " 2956 "(unless m=application or bundle-only)"); 2957 mOffCandidates->CheckDefaultRtcpCandidate( 2958 false, remoteOffer->GetMediaSection(i), id, 2959 "Remote reoffer should not have a default RTCP candidate."); 2960 CheckEndOfCandidates(true, remoteOffer->GetMediaSection(i), 2961 "Remote reoffer should have an end-of-candidates."); 2962 } 2963 2964 answer = CreateAnswer(); 2965 SetLocalAnswer(answer); 2966 SetRemoteAnswer(answer); 2967 // No candidates should be gathered at the answerer, but default candidates 2968 // should be set. 2969 mAnsCandidates->FinishGathering(*mSessionAns); 2970 2971 UniquePtr<Sdp> localAnswer( 2972 Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent))); 2973 id0 = GetTransportId(*mSessionAns, 0); 2974 for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) { 2975 std::string id = GetTransportId(*mSessionAns, 0); 2976 mAnsCandidates->CheckRtpCandidates( 2977 i == 0, localAnswer->GetMediaSection(i), id, 2978 "Local reanswer after gathering should have RTP candidates on level " 2979 "0."); 2980 mAnsCandidates->CheckDefaultRtpCandidate( 2981 true, localAnswer->GetMediaSection(i), id0, 2982 "Local reanswer after gathering should have a default RTP candidate " 2983 "on all levels that matches transport level 0."); 2984 mAnsCandidates->CheckRtcpCandidates( 2985 false, localAnswer->GetMediaSection(i), id, 2986 "Local reanswer after gathering should not have RTCP candidates " 2987 "(because we're reanswering with rtcp-mux)"); 2988 mAnsCandidates->CheckDefaultRtcpCandidate( 2989 false, localAnswer->GetMediaSection(i), id, 2990 "Local reanswer after gathering should not have a default RTCP " 2991 "candidate (because we're reanswering with rtcp-mux)"); 2992 CheckEndOfCandidates( 2993 i == 0, localAnswer->GetMediaSection(i), 2994 "Local reanswer after gathering should have an end-of-candidates only " 2995 "for level 0."); 2996 } 2997 2998 UniquePtr<Sdp> remoteAnswer( 2999 Parse(mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent))); 3000 for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) { 3001 std::string id = GetTransportId(*mSessionAns, 0); 3002 mAnsCandidates->CheckRtpCandidates( 3003 i == 0, remoteAnswer->GetMediaSection(i), id, 3004 "Remote reanswer after trickle should have RTP candidates on level 0."); 3005 mAnsCandidates->CheckDefaultRtpCandidate( 3006 i == 0, remoteAnswer->GetMediaSection(i), id, 3007 "Remote reanswer should have a default RTP candidate on level 0 " 3008 "(because it was gathered last offer/answer)."); 3009 mAnsCandidates->CheckRtcpCandidates( 3010 false, remoteAnswer->GetMediaSection(i), id, 3011 "Remote reanswer after trickle should not have RTCP candidates " 3012 "(because we're reanswering with rtcp-mux)"); 3013 mAnsCandidates->CheckDefaultRtcpCandidate( 3014 false, remoteAnswer->GetMediaSection(i), id, 3015 "Remote reanswer after trickle should not have a default RTCP " 3016 "candidate."); 3017 CheckEndOfCandidates(i == 0, remoteAnswer->GetMediaSection(i), 3018 "Remote reanswer after trickle should have an " 3019 "end-of-candidates on level 0 only."); 3020 } 3021 } 3022 3023 TEST_P(JsepSessionTest, RenegotiationAnswererSendonly) { 3024 AddTracks(*mSessionOff); 3025 AddTracks(*mSessionAns); 3026 OfferAnswer(); 3027 3028 std::string offer = CreateOffer(); 3029 SetLocalOffer(offer); 3030 SetRemoteOffer(offer); 3031 std::string answer = CreateAnswer(); 3032 SetLocalAnswer(answer); 3033 3034 UniquePtr<Sdp> parsedAnswer(Parse(answer)); 3035 for (size_t i = 0; i < parsedAnswer->GetMediaSectionCount(); ++i) { 3036 SdpMediaSection& msection = parsedAnswer->GetMediaSection(i); 3037 if (msection.GetMediaType() != SdpMediaSection::kApplication) { 3038 msection.SetReceiving(false); 3039 } 3040 } 3041 3042 answer = parsedAnswer->ToString(); 3043 3044 SetRemoteAnswer(answer); 3045 3046 for (const JsepTrack& track : GetLocalTracks(*mSessionOff)) { 3047 if (track.GetMediaType() != SdpMediaSection::kApplication) { 3048 ASSERT_FALSE(track.GetActive()); 3049 } 3050 } 3051 3052 ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size()); 3053 } 3054 3055 TEST_P(JsepSessionTest, RenegotiationAnswererInactive) { 3056 AddTracks(*mSessionOff); 3057 AddTracks(*mSessionAns); 3058 OfferAnswer(); 3059 3060 std::string offer = CreateOffer(); 3061 SetLocalOffer(offer); 3062 SetRemoteOffer(offer); 3063 std::string answer = CreateAnswer(); 3064 SetLocalAnswer(answer); 3065 3066 UniquePtr<Sdp> parsedAnswer(Parse(answer)); 3067 for (size_t i = 0; i < parsedAnswer->GetMediaSectionCount(); ++i) { 3068 SdpMediaSection& msection = parsedAnswer->GetMediaSection(i); 3069 if (msection.GetMediaType() != SdpMediaSection::kApplication) { 3070 msection.SetReceiving(false); 3071 msection.SetSending(false); 3072 } 3073 } 3074 3075 answer = parsedAnswer->ToString(); 3076 3077 SetRemoteAnswer(answer, CHECK_SUCCESS); // Won't have answerer tracks 3078 3079 for (const JsepTrack& track : GetLocalTracks(*mSessionOff)) { 3080 if (track.GetMediaType() != SdpMediaSection::kApplication) { 3081 ASSERT_FALSE(track.GetActive()); 3082 } 3083 } 3084 3085 ASSERT_EQ(types.size(), GetTransceivers(*mSessionOff).size()); 3086 } 3087 3088 INSTANTIATE_TEST_SUITE_P( 3089 Variants, JsepSessionTest, 3090 ::testing::Values("audio", "video", "datachannel", "audio,video", 3091 "video,audio", "audio,datachannel", "video,datachannel", 3092 "video,audio,datachannel", "audio,video,datachannel", 3093 "datachannel,audio", "datachannel,video", 3094 "datachannel,audio,video", "datachannel,video,audio", 3095 "audio,datachannel,video", "video,datachannel,audio", 3096 "audio,audio", "video,video", "audio,audio,video", 3097 "audio,video,video", "audio,audio,video,video", 3098 "audio,audio,video,video,datachannel")); 3099 3100 // offerToReceiveXxx variants 3101 3102 TEST_F(JsepSessionTest, OfferAnswerRecvOnlyLines) { 3103 mSessionOff->AddTransceiver(JsepTransceiver( 3104 SdpMediaSection::kAudio, mUuidGen, SdpDirectionAttribute::kRecvonly)); 3105 mSessionOff->AddTransceiver(JsepTransceiver( 3106 SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly)); 3107 mSessionOff->AddTransceiver(JsepTransceiver( 3108 SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly)); 3109 std::string offer = CreateOffer(); 3110 3111 UniquePtr<Sdp> parsedOffer(Parse(offer)); 3112 ASSERT_TRUE(!!parsedOffer); 3113 3114 ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount()); 3115 ASSERT_EQ(SdpMediaSection::kAudio, 3116 parsedOffer->GetMediaSection(0).GetMediaType()); 3117 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3118 parsedOffer->GetMediaSection(0).GetAttributeList().GetDirection()); 3119 ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute( 3120 SdpAttribute::kSsrcAttribute)); 3121 3122 ASSERT_EQ(SdpMediaSection::kVideo, 3123 parsedOffer->GetMediaSection(1).GetMediaType()); 3124 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3125 parsedOffer->GetMediaSection(1).GetAttributeList().GetDirection()); 3126 ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute( 3127 SdpAttribute::kSsrcAttribute)); 3128 3129 ASSERT_EQ(SdpMediaSection::kVideo, 3130 parsedOffer->GetMediaSection(2).GetMediaType()); 3131 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3132 parsedOffer->GetMediaSection(2).GetAttributeList().GetDirection()); 3133 ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute( 3134 SdpAttribute::kSsrcAttribute)); 3135 3136 ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute( 3137 SdpAttribute::kRtcpMuxAttribute)); 3138 ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute( 3139 SdpAttribute::kRtcpMuxAttribute)); 3140 ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute( 3141 SdpAttribute::kRtcpMuxAttribute)); 3142 3143 SetLocalOffer(offer, CHECK_SUCCESS); 3144 3145 AddTracks(*mSessionAns, "audio,video"); 3146 SetRemoteOffer(offer, CHECK_SUCCESS); 3147 3148 std::string answer = CreateAnswer(); 3149 UniquePtr<Sdp> parsedAnswer(Parse(answer)); 3150 3151 ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount()); 3152 ASSERT_EQ(SdpMediaSection::kAudio, 3153 parsedAnswer->GetMediaSection(0).GetMediaType()); 3154 ASSERT_EQ(SdpDirectionAttribute::kSendonly, 3155 parsedAnswer->GetMediaSection(0).GetAttributeList().GetDirection()); 3156 ASSERT_EQ(SdpMediaSection::kVideo, 3157 parsedAnswer->GetMediaSection(1).GetMediaType()); 3158 ASSERT_EQ(SdpDirectionAttribute::kSendonly, 3159 parsedAnswer->GetMediaSection(1).GetAttributeList().GetDirection()); 3160 ASSERT_EQ(SdpMediaSection::kVideo, 3161 parsedAnswer->GetMediaSection(2).GetMediaType()); 3162 ASSERT_EQ(SdpDirectionAttribute::kInactive, 3163 parsedAnswer->GetMediaSection(2).GetAttributeList().GetDirection()); 3164 3165 SetLocalAnswer(answer, CHECK_SUCCESS); 3166 SetRemoteAnswer(answer, CHECK_SUCCESS); 3167 3168 std::vector<JsepTransceiver> transceivers(GetTransceivers(*mSessionOff)); 3169 ASSERT_EQ(3U, transceivers.size()); 3170 for (const auto& transceiver : transceivers) { 3171 const auto& msection = parsedOffer->GetMediaSection(transceiver.GetLevel()); 3172 const auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs; 3173 ASSERT_EQ(1U, ssrcs.size()); 3174 } 3175 } 3176 3177 TEST_F(JsepSessionTest, OfferAnswerSendOnlyLines) { 3178 AddTracks(*mSessionOff, "audio,video,video"); 3179 3180 SetDirection(*mSessionOff, 0, SdpDirectionAttribute::kSendonly); 3181 SetDirection(*mSessionOff, 2, SdpDirectionAttribute::kSendonly); 3182 std::string offer = CreateOffer(); 3183 3184 UniquePtr<Sdp> outputSdp(Parse(offer)); 3185 ASSERT_TRUE(!!outputSdp); 3186 3187 ASSERT_EQ(3U, outputSdp->GetMediaSectionCount()); 3188 ASSERT_EQ(SdpMediaSection::kAudio, 3189 outputSdp->GetMediaSection(0).GetMediaType()); 3190 ASSERT_EQ(SdpDirectionAttribute::kSendonly, 3191 outputSdp->GetMediaSection(0).GetAttributeList().GetDirection()); 3192 ASSERT_EQ(SdpMediaSection::kVideo, 3193 outputSdp->GetMediaSection(1).GetMediaType()); 3194 ASSERT_EQ(SdpDirectionAttribute::kSendrecv, 3195 outputSdp->GetMediaSection(1).GetAttributeList().GetDirection()); 3196 ASSERT_EQ(SdpMediaSection::kVideo, 3197 outputSdp->GetMediaSection(2).GetMediaType()); 3198 ASSERT_EQ(SdpDirectionAttribute::kSendonly, 3199 outputSdp->GetMediaSection(2).GetAttributeList().GetDirection()); 3200 3201 ASSERT_TRUE(outputSdp->GetMediaSection(0).GetAttributeList().HasAttribute( 3202 SdpAttribute::kRtcpMuxAttribute)); 3203 ASSERT_TRUE(outputSdp->GetMediaSection(1).GetAttributeList().HasAttribute( 3204 SdpAttribute::kRtcpMuxAttribute)); 3205 ASSERT_TRUE(outputSdp->GetMediaSection(2).GetAttributeList().HasAttribute( 3206 SdpAttribute::kRtcpMuxAttribute)); 3207 3208 SetLocalOffer(offer, CHECK_SUCCESS); 3209 3210 AddTracks(*mSessionAns, "audio,video"); 3211 SetRemoteOffer(offer, CHECK_SUCCESS); 3212 3213 std::string answer = CreateAnswer(); 3214 outputSdp = Parse(answer); 3215 3216 ASSERT_EQ(3U, outputSdp->GetMediaSectionCount()); 3217 ASSERT_EQ(SdpMediaSection::kAudio, 3218 outputSdp->GetMediaSection(0).GetMediaType()); 3219 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3220 outputSdp->GetMediaSection(0).GetAttributeList().GetDirection()); 3221 ASSERT_EQ(SdpMediaSection::kVideo, 3222 outputSdp->GetMediaSection(1).GetMediaType()); 3223 ASSERT_EQ(SdpDirectionAttribute::kSendrecv, 3224 outputSdp->GetMediaSection(1).GetAttributeList().GetDirection()); 3225 ASSERT_EQ(SdpMediaSection::kVideo, 3226 outputSdp->GetMediaSection(2).GetMediaType()); 3227 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3228 outputSdp->GetMediaSection(2).GetAttributeList().GetDirection()); 3229 } 3230 3231 TEST_F(JsepSessionTest, OfferToReceiveAudioNotUsed) { 3232 mSessionOff->AddTransceiver(JsepTransceiver( 3233 SdpMediaSection::kAudio, mUuidGen, SdpDirectionAttribute::kRecvonly)); 3234 3235 OfferAnswer(CHECK_SUCCESS); 3236 3237 UniquePtr<Sdp> offer( 3238 Parse(mSessionOff->GetLocalDescription(kJsepDescriptionCurrent))); 3239 ASSERT_TRUE(offer.get()); 3240 ASSERT_EQ(1U, offer->GetMediaSectionCount()); 3241 ASSERT_EQ(SdpMediaSection::kAudio, offer->GetMediaSection(0).GetMediaType()); 3242 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3243 offer->GetMediaSection(0).GetAttributeList().GetDirection()); 3244 3245 UniquePtr<Sdp> answer( 3246 Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent))); 3247 ASSERT_TRUE(answer.get()); 3248 ASSERT_EQ(1U, answer->GetMediaSectionCount()); 3249 ASSERT_EQ(SdpMediaSection::kAudio, answer->GetMediaSection(0).GetMediaType()); 3250 ASSERT_EQ(SdpDirectionAttribute::kInactive, 3251 answer->GetMediaSection(0).GetAttributeList().GetDirection()); 3252 } 3253 3254 TEST_F(JsepSessionTest, OfferToReceiveVideoNotUsed) { 3255 mSessionOff->AddTransceiver(JsepTransceiver( 3256 SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly)); 3257 3258 OfferAnswer(CHECK_SUCCESS); 3259 3260 UniquePtr<Sdp> offer( 3261 Parse(mSessionOff->GetLocalDescription(kJsepDescriptionCurrent))); 3262 ASSERT_TRUE(offer.get()); 3263 ASSERT_EQ(1U, offer->GetMediaSectionCount()); 3264 ASSERT_EQ(SdpMediaSection::kVideo, offer->GetMediaSection(0).GetMediaType()); 3265 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, 3266 offer->GetMediaSection(0).GetAttributeList().GetDirection()); 3267 3268 UniquePtr<Sdp> answer( 3269 Parse(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent))); 3270 ASSERT_TRUE(answer.get()); 3271 ASSERT_EQ(1U, answer->GetMediaSectionCount()); 3272 ASSERT_EQ(SdpMediaSection::kVideo, answer->GetMediaSection(0).GetMediaType()); 3273 ASSERT_EQ(SdpDirectionAttribute::kInactive, 3274 answer->GetMediaSection(0).GetAttributeList().GetDirection()); 3275 } 3276 3277 TEST_F(JsepSessionTest, CreateOfferNoDatachannelDefault) { 3278 JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen); 3279 audio.mSendTrack.UpdateStreamIds( 3280 std::vector<std::string>(1, "offerer_stream")); 3281 mSessionOff->AddTransceiver(audio); 3282 3283 JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen); 3284 video.mSendTrack.UpdateStreamIds( 3285 std::vector<std::string>(1, "offerer_stream")); 3286 mSessionOff->AddTransceiver(video); 3287 3288 std::string offer = CreateOffer(); 3289 3290 UniquePtr<Sdp> outputSdp(Parse(offer)); 3291 ASSERT_TRUE(!!outputSdp); 3292 3293 ASSERT_EQ(2U, outputSdp->GetMediaSectionCount()); 3294 ASSERT_EQ(SdpMediaSection::kAudio, 3295 outputSdp->GetMediaSection(0).GetMediaType()); 3296 ASSERT_EQ(SdpMediaSection::kVideo, 3297 outputSdp->GetMediaSection(1).GetMediaType()); 3298 } 3299 3300 TEST_F(JsepSessionTest, ValidateOfferedVideoCodecParams) { 3301 types.push_back(SdpMediaSection::kAudio); 3302 types.push_back(SdpMediaSection::kVideo); 3303 3304 JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen); 3305 audio.mSendTrack.UpdateStreamIds( 3306 std::vector<std::string>(1, "offerer_stream")); 3307 mSessionOff->AddTransceiver(audio); 3308 3309 JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen); 3310 video.mSendTrack.UpdateStreamIds( 3311 std::vector<std::string>(1, "offerer_stream")); 3312 mSessionOff->AddTransceiver(video); 3313 3314 std::string offer = CreateOffer(); 3315 3316 UniquePtr<Sdp> outputSdp(Parse(offer)); 3317 ASSERT_TRUE(!!outputSdp); 3318 3319 ASSERT_EQ(2U, outputSdp->GetMediaSectionCount()); 3320 const auto& video_section = outputSdp->GetMediaSection(1); 3321 ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType()); 3322 const auto& video_attrs = video_section.GetAttributeList(); 3323 ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection()); 3324 3325 ASSERT_THAT(video_section.GetFormats(), 3326 ElementsAre("120", "124", "121", "125", "126", "127", "105", 3327 "106", "99", "100", "123", "122", "119")); 3328 3329 // Validate rtpmap 3330 ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)); 3331 const auto& rtpmaps = video_attrs.GetRtpmap().mRtpmaps; 3332 std::multimap<std::string, std::string> rtpmapPtNames; 3333 for (const auto& rtpmap : rtpmaps) { 3334 rtpmapPtNames.insert({rtpmap.pt, rtpmap.name}); 3335 } 3336 ASSERT_THAT(rtpmapPtNames, 3337 UnorderedElementsAre( 3338 Pair("120", "VP8"), Pair("124", "rtx"), Pair("121", "VP9"), 3339 Pair("125", "rtx"), Pair("126", "H264"), Pair("127", "rtx"), 3340 Pair("105", "H264"), Pair("106", "rtx"), Pair("99", "AV1"), 3341 Pair("100", "rtx"), Pair("123", "ulpfec"), Pair("122", "red"), 3342 Pair("119", "rtx"))); 3343 3344 // Validate fmtps 3345 ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute)); 3346 const auto& fmtps = video_attrs.GetFmtp().mFmtps; 3347 3348 ASSERT_EQ(10U, fmtps.size()); 3349 3350 // VP8 3351 const SdpFmtpAttributeList::Parameters* vp8_params = 3352 video_section.FindFmtp("120"); 3353 ASSERT_TRUE(vp8_params); 3354 ASSERT_EQ(SdpRtpmapAttributeList::kVP8, vp8_params->codec_type); 3355 3356 const auto& parsed_vp8_params = 3357 *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp8_params); 3358 3359 ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs); 3360 ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr); 3361 3362 // VP8 RTX 3363 const SdpFmtpAttributeList::Parameters* vp8_rtx_params = 3364 video_section.FindFmtp("124"); 3365 ASSERT_TRUE(vp8_rtx_params); 3366 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp8_rtx_params->codec_type); 3367 3368 const auto& parsed_vp8_rtx_params = 3369 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp8_rtx_params); 3370 3371 ASSERT_EQ((uint32_t)120, parsed_vp8_rtx_params.apt); 3372 3373 // VP9 3374 const SdpFmtpAttributeList::Parameters* vp9_params = 3375 video_section.FindFmtp("121"); 3376 ASSERT_TRUE(vp9_params); 3377 ASSERT_EQ(SdpRtpmapAttributeList::kVP9, vp9_params->codec_type); 3378 3379 const auto& parsed_vp9_params = 3380 *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp9_params); 3381 3382 ASSERT_EQ((uint32_t)12288, parsed_vp9_params.max_fs); 3383 ASSERT_EQ((uint32_t)60, parsed_vp9_params.max_fr); 3384 3385 // VP9 RTX 3386 const SdpFmtpAttributeList::Parameters* vp9_rtx_params = 3387 video_section.FindFmtp("125"); 3388 ASSERT_TRUE(vp9_rtx_params); 3389 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp9_rtx_params->codec_type); 3390 3391 const auto& parsed_vp9_rtx_params = 3392 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp9_rtx_params); 3393 ASSERT_EQ((uint32_t)121, parsed_vp9_rtx_params.apt); 3394 3395 // H264 packetization mode 1 3396 const SdpFmtpAttributeList::Parameters* h264_1_params = 3397 video_section.FindFmtp("126"); 3398 ASSERT_TRUE(h264_1_params); 3399 ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_1_params->codec_type); 3400 3401 const auto& parsed_h264_1_params = 3402 *static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_1_params); 3403 3404 ASSERT_EQ((uint32_t)0x42e01f, parsed_h264_1_params.profile_level_id); 3405 ASSERT_TRUE(parsed_h264_1_params.level_asymmetry_allowed); 3406 ASSERT_EQ(1U, parsed_h264_1_params.packetization_mode); 3407 3408 // H264 packetization mode 1 RTX 3409 const SdpFmtpAttributeList::Parameters* h264_1_rtx_params = 3410 video_section.FindFmtp("127"); 3411 ASSERT_TRUE(h264_1_rtx_params); 3412 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, h264_1_rtx_params->codec_type); 3413 3414 const auto& parsed_h264_1_rtx_params = 3415 *static_cast<const SdpFmtpAttributeList::RtxParameters*>( 3416 h264_1_rtx_params); 3417 3418 ASSERT_EQ((uint32_t)126, parsed_h264_1_rtx_params.apt); 3419 3420 // H264 packetization mode 0 3421 const SdpFmtpAttributeList::Parameters* h264_0_params = 3422 video_section.FindFmtp("97"); 3423 ASSERT_FALSE(h264_0_params); 3424 3425 // H264 packetization mode 0 RTX 3426 const SdpFmtpAttributeList::Parameters* h264_0_rtx_params = 3427 video_section.FindFmtp("98"); 3428 ASSERT_FALSE(h264_0_rtx_params); 3429 3430 // H264 Baseline packetization mode 1 3431 const SdpFmtpAttributeList::Parameters* h264_baseline_1_params = 3432 video_section.FindFmtp("105"); 3433 ASSERT_TRUE(h264_baseline_1_params); 3434 ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_baseline_1_params->codec_type); 3435 3436 const auto& parsed_h264_baseline_1_params = 3437 *static_cast<const SdpFmtpAttributeList::H264Parameters*>( 3438 h264_baseline_1_params); 3439 3440 ASSERT_EQ((uint32_t)0x42001f, parsed_h264_baseline_1_params.profile_level_id); 3441 ASSERT_TRUE(parsed_h264_baseline_1_params.level_asymmetry_allowed); 3442 ASSERT_EQ(1U, parsed_h264_baseline_1_params.packetization_mode); 3443 3444 // H264 Baseline packetization mode 1 RTX 3445 const SdpFmtpAttributeList::Parameters* h264_baseline_1_rtx_params = 3446 video_section.FindFmtp("106"); 3447 ASSERT_TRUE(h264_baseline_1_rtx_params); 3448 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, 3449 h264_baseline_1_rtx_params->codec_type); 3450 3451 const auto& parsed_h264__baseline_1_rtx_params = 3452 *static_cast<const SdpFmtpAttributeList::RtxParameters*>( 3453 h264_baseline_1_rtx_params); 3454 3455 ASSERT_EQ((uint32_t)105, parsed_h264__baseline_1_rtx_params.apt); 3456 3457 // H264 Baseline packetization mode 0 3458 const SdpFmtpAttributeList::Parameters* h264_baseline_0_params = 3459 video_section.FindFmtp("103"); 3460 ASSERT_FALSE(h264_baseline_0_params); 3461 3462 // H264 Baseline packetization mode 0 RTX 3463 const SdpFmtpAttributeList::Parameters* h264__baseline_0_rtx_params = 3464 video_section.FindFmtp("104"); 3465 ASSERT_FALSE(h264__baseline_0_rtx_params); 3466 3467 // AV1 has no default FMTP parameters so there is no FMTP entry for AV1 in the 3468 // test. 3469 // AV1 RTX 3470 const SdpFmtpAttributeList::Parameters* av1_rtx_params = 3471 video_section.FindFmtp("100"); 3472 ASSERT_TRUE(av1_rtx_params); 3473 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, av1_rtx_params->codec_type); 3474 const auto& parsed_av1_rtx_params = 3475 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(av1_rtx_params); 3476 ASSERT_EQ((uint32_t)99, parsed_av1_rtx_params.apt); 3477 3478 // red RTX 3479 const SdpFmtpAttributeList::Parameters* red_rtx_params = 3480 video_section.FindFmtp("119"); 3481 ASSERT_TRUE(red_rtx_params); 3482 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, red_rtx_params->codec_type); 3483 3484 const auto& parsed_red_rtx_params = 3485 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(red_rtx_params); 3486 3487 ASSERT_EQ((uint32_t)122, parsed_red_rtx_params.apt); 3488 } 3489 3490 TEST_F(JsepSessionTest, ValidateOfferedRecvonlyVideoCodecParams) { 3491 types.push_back(SdpMediaSection::kAudio); 3492 types.push_back(SdpMediaSection::kVideo); 3493 3494 JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen, 3495 SdpDirectionAttribute::kRecvonly); 3496 audio.mSendTrack.UpdateStreamIds( 3497 std::vector<std::string>(1, "offerer_stream")); 3498 mSessionOff->AddTransceiver(audio); 3499 3500 JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen, 3501 SdpDirectionAttribute::kRecvonly); 3502 video.mSendTrack.UpdateStreamIds( 3503 std::vector<std::string>(1, "offerer_stream")); 3504 mSessionOff->AddTransceiver(video); 3505 3506 std::string offer = CreateOffer(); 3507 3508 UniquePtr<Sdp> outputSdp(Parse(offer)); 3509 ASSERT_TRUE(!!outputSdp); 3510 3511 ASSERT_EQ(2U, outputSdp->GetMediaSectionCount()); 3512 const auto& video_section = outputSdp->GetMediaSection(1); 3513 ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType()); 3514 const auto& video_attrs = video_section.GetAttributeList(); 3515 ASSERT_EQ(SdpDirectionAttribute::kRecvonly, video_attrs.GetDirection()); 3516 3517 ASSERT_THAT( 3518 video_section.GetFormats(), 3519 ElementsAre("120", "124", "121", "125", "126", "127", "97", "98", "105", 3520 "106", "103", "104", "99", "100", "123", "122", "119")); 3521 3522 // Validate rtpmap 3523 ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)); 3524 const auto& rtpmaps = video_attrs.GetRtpmap().mRtpmaps; 3525 std::multimap<std::string, std::string> rtpmapPtNames; 3526 for (const auto& rtpmap : rtpmaps) { 3527 rtpmapPtNames.insert({rtpmap.pt, rtpmap.name}); 3528 } 3529 ASSERT_THAT(rtpmapPtNames, 3530 UnorderedElementsAre( 3531 Pair("97", "H264"), Pair("98", "rtx"), Pair("103", "H264"), 3532 Pair("104", "rtx"), Pair("120", "VP8"), Pair("124", "rtx"), 3533 Pair("121", "VP9"), Pair("125", "rtx"), Pair("126", "H264"), 3534 Pair("127", "rtx"), Pair("105", "H264"), Pair("106", "rtx"), 3535 Pair("99", "AV1"), Pair("100", "rtx"), Pair("123", "ulpfec"), 3536 Pair("122", "red"), Pair("119", "rtx"))); 3537 3538 // Validate fmtps 3539 ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute)); 3540 const auto& fmtps = video_attrs.GetFmtp().mFmtps; 3541 3542 ASSERT_EQ(14U, fmtps.size()); 3543 3544 // VP8 3545 const SdpFmtpAttributeList::Parameters* vp8_params = 3546 video_section.FindFmtp("120"); 3547 ASSERT_TRUE(vp8_params); 3548 ASSERT_EQ(SdpRtpmapAttributeList::kVP8, vp8_params->codec_type); 3549 3550 const auto& parsed_vp8_params = 3551 *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp8_params); 3552 3553 ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs); 3554 ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr); 3555 3556 // VP8 RTX 3557 const SdpFmtpAttributeList::Parameters* vp8_rtx_params = 3558 video_section.FindFmtp("124"); 3559 ASSERT_TRUE(vp8_rtx_params); 3560 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp8_rtx_params->codec_type); 3561 3562 const auto& parsed_vp8_rtx_params = 3563 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp8_rtx_params); 3564 3565 ASSERT_EQ((uint32_t)120, parsed_vp8_rtx_params.apt); 3566 3567 // VP9 3568 const SdpFmtpAttributeList::Parameters* vp9_params = 3569 video_section.FindFmtp("121"); 3570 ASSERT_TRUE(vp9_params); 3571 ASSERT_EQ(SdpRtpmapAttributeList::kVP9, vp9_params->codec_type); 3572 3573 const auto& parsed_vp9_params = 3574 *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp9_params); 3575 3576 ASSERT_EQ((uint32_t)12288, parsed_vp9_params.max_fs); 3577 ASSERT_EQ((uint32_t)60, parsed_vp9_params.max_fr); 3578 3579 // VP9 RTX 3580 const SdpFmtpAttributeList::Parameters* vp9_rtx_params = 3581 video_section.FindFmtp("125"); 3582 ASSERT_TRUE(vp9_rtx_params); 3583 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, vp9_rtx_params->codec_type); 3584 3585 const auto& parsed_vp9_rtx_params = 3586 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(vp9_rtx_params); 3587 ASSERT_EQ((uint32_t)121, parsed_vp9_rtx_params.apt); 3588 3589 // H264 packetization mode 1 3590 const SdpFmtpAttributeList::Parameters* h264_1_params = 3591 video_section.FindFmtp("126"); 3592 ASSERT_TRUE(h264_1_params); 3593 ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_1_params->codec_type); 3594 3595 const auto& parsed_h264_1_params = 3596 *static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_1_params); 3597 3598 ASSERT_EQ((uint32_t)0x42e01f, parsed_h264_1_params.profile_level_id); 3599 ASSERT_TRUE(parsed_h264_1_params.level_asymmetry_allowed); 3600 ASSERT_EQ(1U, parsed_h264_1_params.packetization_mode); 3601 3602 // H264 packetization mode 1 RTX 3603 const SdpFmtpAttributeList::Parameters* h264_1_rtx_params = 3604 video_section.FindFmtp("127"); 3605 ASSERT_TRUE(h264_1_rtx_params); 3606 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, h264_1_rtx_params->codec_type); 3607 3608 const auto& parsed_h264_1_rtx_params = 3609 *static_cast<const SdpFmtpAttributeList::RtxParameters*>( 3610 h264_1_rtx_params); 3611 3612 ASSERT_EQ((uint32_t)126, parsed_h264_1_rtx_params.apt); 3613 3614 // H264 packetization mode 0 3615 const SdpFmtpAttributeList::Parameters* h264_0_params = 3616 video_section.FindFmtp("97"); 3617 ASSERT_TRUE(h264_0_params); 3618 ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_0_params->codec_type); 3619 3620 const auto& parsed_h264_0_params = 3621 *static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_0_params); 3622 3623 ASSERT_EQ((uint32_t)0x42e01f, parsed_h264_0_params.profile_level_id); 3624 ASSERT_TRUE(parsed_h264_0_params.level_asymmetry_allowed); 3625 ASSERT_EQ(0U, parsed_h264_0_params.packetization_mode); 3626 3627 // H264 packetization mode 0 RTX 3628 const SdpFmtpAttributeList::Parameters* h264_0_rtx_params = 3629 video_section.FindFmtp("98"); 3630 ASSERT_TRUE(h264_0_rtx_params); 3631 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, h264_0_rtx_params->codec_type); 3632 3633 const auto& parsed_h264_0_rtx_params = 3634 *static_cast<const SdpFmtpAttributeList::RtxParameters*>( 3635 h264_0_rtx_params); 3636 3637 ASSERT_EQ((uint32_t)97, parsed_h264_0_rtx_params.apt); 3638 3639 // H264 Baseline packetization mode 1 3640 const SdpFmtpAttributeList::Parameters* h264_baseline_1_params = 3641 video_section.FindFmtp("105"); 3642 ASSERT_TRUE(h264_baseline_1_params); 3643 ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_baseline_1_params->codec_type); 3644 3645 const auto& parsed_h264_baseline_1_params = 3646 *static_cast<const SdpFmtpAttributeList::H264Parameters*>( 3647 h264_baseline_1_params); 3648 3649 ASSERT_EQ((uint32_t)0x42001f, parsed_h264_baseline_1_params.profile_level_id); 3650 ASSERT_TRUE(parsed_h264_baseline_1_params.level_asymmetry_allowed); 3651 ASSERT_EQ(1U, parsed_h264_baseline_1_params.packetization_mode); 3652 3653 // H264 Baseline packetization mode 1 RTX 3654 const SdpFmtpAttributeList::Parameters* h264_baseline_1_rtx_params = 3655 video_section.FindFmtp("106"); 3656 ASSERT_TRUE(h264_baseline_1_rtx_params); 3657 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, 3658 h264_baseline_1_rtx_params->codec_type); 3659 3660 const auto& parsed_h264__baseline_1_rtx_params = 3661 *static_cast<const SdpFmtpAttributeList::RtxParameters*>( 3662 h264_baseline_1_rtx_params); 3663 3664 ASSERT_EQ((uint32_t)105, parsed_h264__baseline_1_rtx_params.apt); 3665 3666 // H264 Baseline packetization mode 0 3667 const SdpFmtpAttributeList::Parameters* h264_baseline_0_params = 3668 video_section.FindFmtp("103"); 3669 ASSERT_TRUE(h264_baseline_0_params); 3670 ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_baseline_0_params->codec_type); 3671 3672 const auto& parsed_h264_baseline_0_params = 3673 *static_cast<const SdpFmtpAttributeList::H264Parameters*>( 3674 h264_baseline_0_params); 3675 3676 ASSERT_EQ((uint32_t)0x42001f, parsed_h264_baseline_0_params.profile_level_id); 3677 ASSERT_TRUE(parsed_h264_baseline_0_params.level_asymmetry_allowed); 3678 ASSERT_EQ(0U, parsed_h264_baseline_0_params.packetization_mode); 3679 3680 // H264 Baseline packetization mode 0 RTX 3681 const SdpFmtpAttributeList::Parameters* h264__baseline_0_rtx_params = 3682 video_section.FindFmtp("104"); 3683 ASSERT_TRUE(h264__baseline_0_rtx_params); 3684 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, 3685 h264__baseline_0_rtx_params->codec_type); 3686 3687 const auto& parsed_h264_baseline_0_rtx_params = 3688 *static_cast<const SdpFmtpAttributeList::RtxParameters*>( 3689 h264__baseline_0_rtx_params); 3690 3691 ASSERT_EQ((uint32_t)103, parsed_h264_baseline_0_rtx_params.apt); 3692 3693 // AV1 has no default FMTP parameters so there is no FMTP entry for AV1 in the 3694 // test. 3695 // AV1 RTX 3696 const SdpFmtpAttributeList::Parameters* av1_rtx_params = 3697 video_section.FindFmtp("100"); 3698 ASSERT_TRUE(av1_rtx_params); 3699 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, av1_rtx_params->codec_type); 3700 const auto& parsed_av1_rtx_params = 3701 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(av1_rtx_params); 3702 ASSERT_EQ((uint32_t)99, parsed_av1_rtx_params.apt); 3703 3704 // red RTX 3705 const SdpFmtpAttributeList::Parameters* red_rtx_params = 3706 video_section.FindFmtp("119"); 3707 ASSERT_TRUE(red_rtx_params); 3708 ASSERT_EQ(SdpRtpmapAttributeList::kRtx, red_rtx_params->codec_type); 3709 3710 const auto& parsed_red_rtx_params = 3711 *static_cast<const SdpFmtpAttributeList::RtxParameters*>(red_rtx_params); 3712 3713 ASSERT_EQ((uint32_t)122, parsed_red_rtx_params.apt); 3714 } 3715 3716 TEST_F(JsepSessionTest, ValidateOfferedAudioCodecParams) { 3717 types.push_back(SdpMediaSection::kAudio); 3718 types.push_back(SdpMediaSection::kVideo); 3719 3720 JsepTransceiver audio(SdpMediaSection::kAudio, mUuidGen); 3721 audio.mSendTrack.UpdateStreamIds( 3722 std::vector<std::string>(1, "offerer_stream")); 3723 mSessionOff->AddTransceiver(audio); 3724 3725 JsepTransceiver video(SdpMediaSection::kVideo, mUuidGen); 3726 video.mSendTrack.UpdateStreamIds( 3727 std::vector<std::string>(1, "offerer_stream")); 3728 mSessionOff->AddTransceiver(video); 3729 3730 std::string offer = CreateOffer(); 3731 3732 UniquePtr<Sdp> outputSdp(Parse(offer)); 3733 ASSERT_TRUE(!!outputSdp); 3734 3735 ASSERT_EQ(2U, outputSdp->GetMediaSectionCount()); 3736 auto& audio_section = outputSdp->GetMediaSection(0); 3737 ASSERT_EQ(SdpMediaSection::kAudio, audio_section.GetMediaType()); 3738 auto& audio_attrs = audio_section.GetAttributeList(); 3739 ASSERT_EQ(SdpDirectionAttribute::kSendrecv, audio_attrs.GetDirection()); 3740 ASSERT_EQ(5U, audio_section.GetFormats().size()); 3741 ASSERT_EQ("109", audio_section.GetFormats()[0]); 3742 ASSERT_EQ("9", audio_section.GetFormats()[1]); 3743 ASSERT_EQ("0", audio_section.GetFormats()[2]); 3744 ASSERT_EQ("8", audio_section.GetFormats()[3]); 3745 ASSERT_EQ("101", audio_section.GetFormats()[4]); 3746 3747 // Validate rtpmap 3748 ASSERT_TRUE(audio_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)); 3749 auto& rtpmaps = audio_attrs.GetRtpmap(); 3750 ASSERT_TRUE(rtpmaps.HasEntry("109")); 3751 ASSERT_TRUE(rtpmaps.HasEntry("9")); 3752 ASSERT_TRUE(rtpmaps.HasEntry("0")); 3753 ASSERT_TRUE(rtpmaps.HasEntry("8")); 3754 ASSERT_TRUE(rtpmaps.HasEntry("101")); 3755 3756 auto& opus_entry = rtpmaps.GetEntry("109"); 3757 auto& g722_entry = rtpmaps.GetEntry("9"); 3758 auto& pcmu_entry = rtpmaps.GetEntry("0"); 3759 auto& pcma_entry = rtpmaps.GetEntry("8"); 3760 auto& telephone_event_entry = rtpmaps.GetEntry("101"); 3761 3762 ASSERT_EQ("opus", opus_entry.name); 3763 ASSERT_EQ("G722", g722_entry.name); 3764 ASSERT_EQ("PCMU", pcmu_entry.name); 3765 ASSERT_EQ("PCMA", pcma_entry.name); 3766 ASSERT_EQ("telephone-event", telephone_event_entry.name); 3767 3768 // Validate fmtps 3769 ASSERT_TRUE(audio_attrs.HasAttribute(SdpAttribute::kFmtpAttribute)); 3770 auto& fmtps = audio_attrs.GetFmtp().mFmtps; 3771 3772 ASSERT_EQ(2U, fmtps.size()); 3773 3774 // opus 3775 const SdpFmtpAttributeList::Parameters* opus_params = 3776 audio_section.FindFmtp("109"); 3777 ASSERT_TRUE(opus_params); 3778 ASSERT_EQ(SdpRtpmapAttributeList::kOpus, opus_params->codec_type); 3779 3780 auto& parsed_opus_params = 3781 *static_cast<const SdpFmtpAttributeList::OpusParameters*>(opus_params); 3782 3783 ASSERT_EQ((uint32_t)48000, parsed_opus_params.maxplaybackrate); 3784 ASSERT_EQ((uint32_t)1, parsed_opus_params.stereo); 3785 ASSERT_EQ((uint32_t)0, parsed_opus_params.useInBandFec); 3786 ASSERT_EQ((uint32_t)0, parsed_opus_params.maxAverageBitrate); 3787 ASSERT_EQ((uint32_t)0, parsed_opus_params.useDTX); 3788 ASSERT_EQ((uint32_t)0, parsed_opus_params.useCbr); 3789 ASSERT_EQ((uint32_t)0, parsed_opus_params.frameSizeMs); 3790 ASSERT_EQ((uint32_t)0, parsed_opus_params.minFrameSizeMs); 3791 ASSERT_EQ((uint32_t)0, parsed_opus_params.maxFrameSizeMs); 3792 3793 // dtmf 3794 const SdpFmtpAttributeList::Parameters* dtmf_params = 3795 audio_section.FindFmtp("101"); 3796 ASSERT_TRUE(dtmf_params); 3797 ASSERT_EQ(SdpRtpmapAttributeList::kTelephoneEvent, dtmf_params->codec_type); 3798 3799 auto& parsed_dtmf_params = 3800 *static_cast<const SdpFmtpAttributeList::TelephoneEventParameters*>( 3801 dtmf_params); 3802 3803 ASSERT_EQ("0-15", parsed_dtmf_params.dtmfTones); 3804 } 3805 3806 TEST_F(JsepSessionTest, ValidateNoFmtpLineForRedInOfferAndAnswer) { 3807 types.push_back(SdpMediaSection::kAudio); 3808 types.push_back(SdpMediaSection::kVideo); 3809 3810 AddTracksToStream(*mSessionOff, "offerer_stream", "audio,video"); 3811 3812 std::string offer = CreateOffer(); 3813 3814 SetLocalOffer(offer); 3815 SetRemoteOffer(offer); 3816 3817 AddTracksToStream(*mSessionAns, "answerer_stream", "audio,video"); 3818 3819 std::string answer = CreateAnswer(); 3820 // because parsing will throw out the malformed fmtp, make sure it is not 3821 // in the answer sdp string 3822 ASSERT_EQ(std::string::npos, answer.find("a=fmtp:122")); 3823 3824 UniquePtr<Sdp> outputSdp(Parse(answer)); 3825 ASSERT_TRUE(!!outputSdp); 3826 3827 ASSERT_EQ(2U, outputSdp->GetMediaSectionCount()); 3828 auto& video_section = outputSdp->GetMediaSection(1); 3829 ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType()); 3830 auto& video_attrs = video_section.GetAttributeList(); 3831 ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection()); 3832 3833 ASSERT_THAT(video_section.GetFormats(), 3834 ElementsAre("120", "124", "121", "125", "126", "127", "105", 3835 "106", "99", "100", "123", "122", "119")); 3836 3837 // Validate rtpmap 3838 ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)); 3839 const auto& rtpmaps = video_attrs.GetRtpmap().mRtpmaps; 3840 std::vector<std::string> rtpmapPts; 3841 rtpmapPts.reserve(rtpmaps.size()); 3842 for (const auto& rtpmap : rtpmaps) { 3843 rtpmapPts.push_back(rtpmap.pt); 3844 } 3845 ASSERT_THAT(rtpmapPts, UnorderedElementsAre("120", "124", "121", "125", "126", 3846 "127", "105", "106", "99", "100", 3847 "123", "122", "119")); 3848 3849 // Validate fmtps 3850 ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute)); 3851 const auto& fmtps = video_attrs.GetFmtp().mFmtps; 3852 std::vector<std::string> fmtpFormats; 3853 fmtpFormats.reserve(fmtps.size()); 3854 for (const auto& fmtp : fmtps) { 3855 fmtpFormats.push_back(fmtp.format); 3856 } 3857 3858 ASSERT_THAT(fmtpFormats, ElementsAre("126", "105", "120", "124", "121", "125", 3859 "127", "106", "100", "119")); 3860 SetLocalAnswer(answer); 3861 SetRemoteAnswer(answer); 3862 3863 auto offerTransceivers = GetTransceivers(*mSessionOff); 3864 ASSERT_EQ(2U, offerTransceivers.size()); 3865 ASSERT_FALSE(IsNull(offerTransceivers[1].mSendTrack)); 3866 ASSERT_FALSE(IsNull(offerTransceivers[1].mRecvTrack)); 3867 ASSERT_TRUE(offerTransceivers[1].mSendTrack.GetNegotiatedDetails()); 3868 ASSERT_TRUE(offerTransceivers[1].mRecvTrack.GetNegotiatedDetails()); 3869 // Note that the number of recv/send codecs here differ because some codecs 3870 // are recvonly. See SetupPreferredCodecs above. 3871 ASSERT_EQ(7U, offerTransceivers[1] 3872 .mSendTrack.GetNegotiatedDetails() 3873 ->GetEncoding(0) 3874 .GetCodecs() 3875 .size()); 3876 ASSERT_EQ(9U, offerTransceivers[1] 3877 .mRecvTrack.GetNegotiatedDetails() 3878 ->GetEncoding(0) 3879 .GetCodecs() 3880 .size()); 3881 3882 auto answerTransceivers = GetTransceivers(*mSessionAns); 3883 ASSERT_EQ(2U, answerTransceivers.size()); 3884 ASSERT_FALSE(IsNull(answerTransceivers[1].mSendTrack)); 3885 ASSERT_FALSE(IsNull(answerTransceivers[1].mRecvTrack)); 3886 ASSERT_TRUE(answerTransceivers[1].mSendTrack.GetNegotiatedDetails()); 3887 ASSERT_TRUE(answerTransceivers[1].mRecvTrack.GetNegotiatedDetails()); 3888 ASSERT_EQ(7U, answerTransceivers[1] 3889 .mSendTrack.GetNegotiatedDetails() 3890 ->GetEncoding(0) 3891 .GetCodecs() 3892 .size()); 3893 ASSERT_EQ(7U, answerTransceivers[1] 3894 .mRecvTrack.GetNegotiatedDetails() 3895 ->GetEncoding(0) 3896 .GetCodecs() 3897 .size()); 3898 } 3899 3900 TEST_F(JsepSessionTest, OfferWithBundleGroupNoTags) { 3901 AddTracks(*mSessionOff, "audio,video"); 3902 AddTracks(*mSessionAns, "audio,video"); 3903 3904 std::string offer = CreateOffer(); 3905 size_t i = offer.find("a=group:BUNDLE"); 3906 offer.insert(i, "a=group:BUNDLE\r\n"); 3907 3908 SetLocalOffer(offer, CHECK_SUCCESS); 3909 SetRemoteOffer(offer, CHECK_SUCCESS); 3910 std::string answer(CreateAnswer()); 3911 } 3912 3913 static void Replace(const std::string& toReplace, const std::string& with, 3914 std::string* in) { 3915 size_t pos = in->find(toReplace); 3916 ASSERT_NE(std::string::npos, pos); 3917 in->replace(pos, toReplace.size(), with); 3918 } 3919 3920 static void ReplaceAll(const std::string& toReplace, const std::string& with, 3921 std::string* in) { 3922 while (in->find(toReplace) != std::string::npos) { 3923 Replace(toReplace, with, in); 3924 } 3925 } 3926 3927 static void GetCodec(JsepSession& session, size_t transceiverIndex, 3928 sdp::Direction direction, size_t encodingIndex, 3929 size_t codecIndex, 3930 UniquePtr<JsepCodecDescription>* codecOut) { 3931 codecOut->reset(); 3932 ASSERT_LT(transceiverIndex, JsepSessionTest::GetTransceivers(session).size()); 3933 JsepTransceiver transceiver( 3934 JsepSessionTest::GetTransceivers(session)[transceiverIndex]); 3935 JsepTrack& track = (direction == sdp::kSend) ? transceiver.mSendTrack 3936 : transceiver.mRecvTrack; 3937 ASSERT_TRUE(track.GetNegotiatedDetails()); 3938 ASSERT_LT(encodingIndex, track.GetNegotiatedDetails()->GetEncodingCount()); 3939 ASSERT_LT(codecIndex, track.GetNegotiatedDetails() 3940 ->GetEncoding(encodingIndex) 3941 .GetCodecs() 3942 .size()); 3943 codecOut->reset(track.GetNegotiatedDetails() 3944 ->GetEncoding(encodingIndex) 3945 .GetCodecs()[codecIndex] 3946 ->Clone()); 3947 } 3948 3949 static void ForceH264(JsepSession& session, uint32_t profileLevelId) { 3950 for (auto& codec : session.Codecs()) { 3951 if (codec->mName == "H264") { 3952 JsepVideoCodecDescription* h264 = 3953 static_cast<JsepVideoCodecDescription*>(codec.get()); 3954 h264->mProfileLevelId = profileLevelId; 3955 } else { 3956 codec->mEnabled = false; 3957 } 3958 } 3959 } 3960 3961 TEST_F(JsepSessionTest, TestH264Negotiation) { 3962 ForceH264(*mSessionOff, 0x42e00b); 3963 ForceH264(*mSessionAns, 0x42e01f); 3964 3965 AddTracks(*mSessionOff, "video"); 3966 AddTracks(*mSessionAns, "video"); 3967 3968 std::string offer(CreateOffer()); 3969 SetLocalOffer(offer, CHECK_SUCCESS); 3970 3971 SetRemoteOffer(offer, CHECK_SUCCESS); 3972 std::string answer(CreateAnswer()); 3973 3974 SetRemoteAnswer(answer, CHECK_SUCCESS); 3975 SetLocalAnswer(answer, CHECK_SUCCESS); 3976 3977 UniquePtr<JsepCodecDescription> offererSendCodec; 3978 GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec); 3979 ASSERT_TRUE(offererSendCodec); 3980 ASSERT_EQ("H264", offererSendCodec->mName); 3981 const JsepVideoCodecDescription* offererVideoSendCodec( 3982 static_cast<const JsepVideoCodecDescription*>(offererSendCodec.get())); 3983 ASSERT_EQ((uint32_t)0x42e01f, offererVideoSendCodec->mProfileLevelId); 3984 3985 UniquePtr<JsepCodecDescription> offererRecvCodec; 3986 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec); 3987 ASSERT_EQ("H264", offererRecvCodec->mName); 3988 const JsepVideoCodecDescription* offererVideoRecvCodec( 3989 static_cast<const JsepVideoCodecDescription*>(offererRecvCodec.get())); 3990 ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId); 3991 3992 UniquePtr<JsepCodecDescription> answererSendCodec; 3993 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec); 3994 ASSERT_TRUE(answererSendCodec); 3995 ASSERT_EQ("H264", answererSendCodec->mName); 3996 const JsepVideoCodecDescription* answererVideoSendCodec( 3997 static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get())); 3998 ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId); 3999 4000 UniquePtr<JsepCodecDescription> answererRecvCodec; 4001 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec); 4002 ASSERT_EQ("H264", answererRecvCodec->mName); 4003 const JsepVideoCodecDescription* answererVideoRecvCodec( 4004 static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get())); 4005 ASSERT_EQ((uint32_t)0x42e01f, answererVideoRecvCodec->mProfileLevelId); 4006 } 4007 4008 TEST_F(JsepSessionTest, TestH264NegotiationFails) { 4009 ForceH264(*mSessionOff, 0x42000b); 4010 ForceH264(*mSessionAns, 0x42e01f); 4011 4012 AddTracks(*mSessionOff, "video"); 4013 AddTracks(*mSessionAns, "video"); 4014 4015 std::string offer(CreateOffer()); 4016 SetLocalOffer(offer, CHECK_SUCCESS); 4017 4018 SetRemoteOffer(offer, CHECK_SUCCESS); 4019 std::string answer(CreateAnswer()); 4020 4021 SetRemoteAnswer(answer, CHECK_SUCCESS); 4022 SetLocalAnswer(answer, CHECK_SUCCESS); 4023 4024 ASSERT_EQ(nullptr, GetNegotiatedTransceiver(*mSessionOff, 0)); 4025 ASSERT_EQ(nullptr, GetNegotiatedTransceiver(*mSessionAns, 0)); 4026 } 4027 4028 TEST_F(JsepSessionTest, TestH264NegotiationOffererDefault) { 4029 ForceH264(*mSessionOff, 0x42000d); 4030 ForceH264(*mSessionAns, 0x42000d); 4031 4032 AddTracks(*mSessionOff, "video"); 4033 AddTracks(*mSessionAns, "video"); 4034 4035 std::string offer(CreateOffer()); 4036 SetLocalOffer(offer, CHECK_SUCCESS); 4037 4038 Replace("profile-level-id=42000d", "some-unknown-param=0", &offer); 4039 4040 SetRemoteOffer(offer, CHECK_SUCCESS); 4041 std::string answer(CreateAnswer()); 4042 4043 SetRemoteAnswer(answer, CHECK_SUCCESS); 4044 SetLocalAnswer(answer, CHECK_SUCCESS); 4045 4046 UniquePtr<JsepCodecDescription> answererSendCodec; 4047 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec); 4048 ASSERT_TRUE(answererSendCodec); 4049 ASSERT_EQ("H264", answererSendCodec->mName); 4050 const JsepVideoCodecDescription* answererVideoSendCodec( 4051 static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get())); 4052 ASSERT_EQ((uint32_t)0x42000A, answererVideoSendCodec->mProfileLevelId); 4053 } 4054 4055 TEST_F(JsepSessionTest, TestH264NegotiationOffererNoFmtp) { 4056 ForceH264(*mSessionOff, 0x42000d); 4057 ForceH264(*mSessionAns, 0x42001e); 4058 4059 AddTracks(*mSessionOff, "video"); 4060 AddTracks(*mSessionAns, "video"); 4061 4062 std::string offer(CreateOffer()); 4063 SetLocalOffer(offer, CHECK_SUCCESS); 4064 4065 Replace("a=fmtp", "a=oops", &offer); 4066 4067 SetRemoteOffer(offer, CHECK_SUCCESS); 4068 std::string answer(CreateAnswer()); 4069 4070 SetRemoteAnswer(answer, CHECK_SUCCESS); 4071 SetLocalAnswer(answer, CHECK_SUCCESS); 4072 4073 UniquePtr<JsepCodecDescription> answererSendCodec; 4074 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec); 4075 ASSERT_TRUE(answererSendCodec); 4076 ASSERT_EQ("H264", answererSendCodec->mName); 4077 const JsepVideoCodecDescription* answererVideoSendCodec( 4078 static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get())); 4079 ASSERT_EQ((uint32_t)0x42000A, answererVideoSendCodec->mProfileLevelId); 4080 4081 UniquePtr<JsepCodecDescription> answererRecvCodec; 4082 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec); 4083 ASSERT_EQ("H264", answererRecvCodec->mName); 4084 const JsepVideoCodecDescription* answererVideoRecvCodec( 4085 static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get())); 4086 ASSERT_EQ((uint32_t)0x42000A, answererVideoRecvCodec->mProfileLevelId); 4087 } 4088 4089 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithLowLevel) { 4090 ForceH264(*mSessionOff, 0x42e00b); 4091 ForceH264(*mSessionAns, 0x42e01f); 4092 4093 AddTracks(*mSessionOff, "video"); 4094 AddTracks(*mSessionAns, "video"); 4095 4096 std::string offer(CreateOffer()); 4097 SetLocalOffer(offer, CHECK_SUCCESS); 4098 4099 Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &offer); 4100 4101 SetRemoteOffer(offer, CHECK_SUCCESS); 4102 std::string answer(CreateAnswer()); 4103 4104 SetRemoteAnswer(answer, CHECK_SUCCESS); 4105 SetLocalAnswer(answer, CHECK_SUCCESS); 4106 4107 // Offerer doesn't know about the shenanigans we've pulled here, so will 4108 // behave normally, and we test the normal behavior elsewhere. 4109 4110 UniquePtr<JsepCodecDescription> answererSendCodec; 4111 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec); 4112 ASSERT_TRUE(answererSendCodec); 4113 ASSERT_EQ("H264", answererSendCodec->mName); 4114 const JsepVideoCodecDescription* answererVideoSendCodec( 4115 static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get())); 4116 ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId); 4117 4118 UniquePtr<JsepCodecDescription> answererRecvCodec; 4119 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec); 4120 ASSERT_EQ("H264", answererRecvCodec->mName); 4121 const JsepVideoCodecDescription* answererVideoRecvCodec( 4122 static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get())); 4123 ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId); 4124 } 4125 4126 TEST_F(JsepSessionTest, 4127 TestH264LevelAsymmetryDisallowedByOffererWithHighLevel) { 4128 ForceH264(*mSessionOff, 0x42e01f); 4129 ForceH264(*mSessionAns, 0x42e00b); 4130 4131 AddTracks(*mSessionOff, "video"); 4132 AddTracks(*mSessionAns, "video"); 4133 4134 std::string offer(CreateOffer()); 4135 SetLocalOffer(offer, CHECK_SUCCESS); 4136 4137 Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &offer); 4138 4139 SetRemoteOffer(offer, CHECK_SUCCESS); 4140 std::string answer(CreateAnswer()); 4141 4142 SetRemoteAnswer(answer, CHECK_SUCCESS); 4143 SetLocalAnswer(answer, CHECK_SUCCESS); 4144 4145 // Offerer doesn't know about the shenanigans we've pulled here, so will 4146 // behave normally, and we test the normal behavior elsewhere. 4147 4148 UniquePtr<JsepCodecDescription> answererSendCodec; 4149 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec); 4150 ASSERT_TRUE(answererSendCodec); 4151 ASSERT_EQ("H264", answererSendCodec->mName); 4152 const JsepVideoCodecDescription* answererVideoSendCodec( 4153 static_cast<const JsepVideoCodecDescription*>(answererSendCodec.get())); 4154 ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId); 4155 4156 UniquePtr<JsepCodecDescription> answererRecvCodec; 4157 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec); 4158 ASSERT_EQ("H264", answererRecvCodec->mName); 4159 const JsepVideoCodecDescription* answererVideoRecvCodec( 4160 static_cast<const JsepVideoCodecDescription*>(answererRecvCodec.get())); 4161 ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId); 4162 } 4163 4164 TEST_F(JsepSessionTest, 4165 TestH264LevelAsymmetryDisallowedByAnswererWithLowLevel) { 4166 ForceH264(*mSessionOff, 0x42e01f); 4167 ForceH264(*mSessionAns, 0x42e00b); 4168 4169 AddTracks(*mSessionOff, "video"); 4170 AddTracks(*mSessionAns, "video"); 4171 4172 std::string offer(CreateOffer()); 4173 SetLocalOffer(offer, CHECK_SUCCESS); 4174 SetRemoteOffer(offer, CHECK_SUCCESS); 4175 std::string answer(CreateAnswer()); 4176 4177 Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &answer); 4178 4179 SetRemoteAnswer(answer, CHECK_SUCCESS); 4180 SetLocalAnswer(answer, CHECK_SUCCESS); 4181 4182 UniquePtr<JsepCodecDescription> offererSendCodec; 4183 GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec); 4184 ASSERT_TRUE(offererSendCodec); 4185 ASSERT_EQ("H264", offererSendCodec->mName); 4186 const JsepVideoCodecDescription* offererVideoSendCodec( 4187 static_cast<const JsepVideoCodecDescription*>(offererSendCodec.get())); 4188 ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId); 4189 4190 UniquePtr<JsepCodecDescription> offererRecvCodec; 4191 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec); 4192 ASSERT_EQ("H264", offererRecvCodec->mName); 4193 const JsepVideoCodecDescription* offererVideoRecvCodec( 4194 static_cast<const JsepVideoCodecDescription*>(offererRecvCodec.get())); 4195 ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId); 4196 4197 // Answerer doesn't know we've pulled these shenanigans, it should act as if 4198 // it did not set level-asymmetry-required, and we already check that 4199 // elsewhere 4200 } 4201 4202 TEST_F(JsepSessionTest, 4203 TestH264LevelAsymmetryDisallowedByAnswererWithHighLevel) { 4204 ForceH264(*mSessionOff, 0x42e00b); 4205 ForceH264(*mSessionAns, 0x42e01f); 4206 4207 AddTracks(*mSessionOff, "video"); 4208 AddTracks(*mSessionAns, "video"); 4209 4210 std::string offer(CreateOffer()); 4211 SetLocalOffer(offer, CHECK_SUCCESS); 4212 SetRemoteOffer(offer, CHECK_SUCCESS); 4213 std::string answer(CreateAnswer()); 4214 4215 Replace("level-asymmetry-allowed=1", "level-asymmetry-allowed=0", &answer); 4216 4217 SetRemoteAnswer(answer, CHECK_SUCCESS); 4218 SetLocalAnswer(answer, CHECK_SUCCESS); 4219 4220 UniquePtr<JsepCodecDescription> offererSendCodec; 4221 GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec); 4222 ASSERT_TRUE(offererSendCodec); 4223 ASSERT_EQ("H264", offererSendCodec->mName); 4224 const JsepVideoCodecDescription* offererVideoSendCodec( 4225 static_cast<const JsepVideoCodecDescription*>(offererSendCodec.get())); 4226 ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId); 4227 4228 UniquePtr<JsepCodecDescription> offererRecvCodec; 4229 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec); 4230 ASSERT_EQ("H264", offererRecvCodec->mName); 4231 const JsepVideoCodecDescription* offererVideoRecvCodec( 4232 static_cast<const JsepVideoCodecDescription*>(offererRecvCodec.get())); 4233 ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId); 4234 4235 // Answerer doesn't know we've pulled these shenanigans, it should act as if 4236 // it did not set level-asymmetry-required, and we already check that 4237 // elsewhere 4238 } 4239 4240 TEST_P(JsepSessionTest, TestRejectMline) { 4241 // We need to do this before adding tracks 4242 types = BuildTypes(GetParam()); 4243 4244 SdpMediaSection::MediaType type = types.front(); 4245 4246 switch (type) { 4247 case SdpMediaSection::kAudio: 4248 // Sabotage audio 4249 EnsureNegotiationFailure(types.front(), "opus"); 4250 break; 4251 case SdpMediaSection::kVideo: 4252 // Sabotage video 4253 EnsureNegotiationFailure(types.front(), "H264"); 4254 break; 4255 case SdpMediaSection::kApplication: 4256 // Sabotage datachannel 4257 EnsureNegotiationFailure(types.front(), "webrtc-datachannel"); 4258 break; 4259 default: 4260 ASSERT_TRUE(false) 4261 << "Unknown media type"; 4262 } 4263 4264 AddTracks(*mSessionOff); 4265 AddTracks(*mSessionAns); 4266 4267 std::string offer = CreateOffer(); 4268 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer); 4269 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 4270 4271 std::string answer = CreateAnswer(); 4272 4273 UniquePtr<Sdp> outputSdp(Parse(answer)); 4274 ASSERT_TRUE(!!outputSdp); 4275 4276 ASSERT_NE(0U, outputSdp->GetMediaSectionCount()); 4277 SdpMediaSection* failed_section = nullptr; 4278 4279 for (size_t i = 0; i < outputSdp->GetMediaSectionCount(); ++i) { 4280 if (outputSdp->GetMediaSection(i).GetMediaType() == type) { 4281 failed_section = &outputSdp->GetMediaSection(i); 4282 } 4283 } 4284 4285 ASSERT_TRUE(failed_section) 4286 << "Failed type was entirely absent from SDP"; 4287 auto& failed_attrs = failed_section->GetAttributeList(); 4288 ASSERT_EQ(SdpDirectionAttribute::kInactive, failed_attrs.GetDirection()); 4289 ASSERT_EQ(0U, failed_section->GetPort()); 4290 4291 mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 4292 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer); 4293 4294 size_t numRejected = std::count(types.begin(), types.end(), type); 4295 size_t numAccepted = types.size() - numRejected; 4296 4297 if (type == SdpMediaSection::MediaType::kApplication) { 4298 ASSERT_TRUE(GetDatachannelTransceiver(*mSessionOff)); 4299 ASSERT_FALSE( 4300 GetDatachannelTransceiver(*mSessionOff)->mRecvTrack.GetActive()); 4301 ASSERT_TRUE(GetDatachannelTransceiver(*mSessionAns)); 4302 ASSERT_FALSE( 4303 GetDatachannelTransceiver(*mSessionAns)->mRecvTrack.GetActive()); 4304 } else { 4305 ASSERT_EQ(types.size(), GetLocalTracks(*mSessionOff).size()); 4306 ASSERT_EQ(numAccepted, GetRemoteTracks(*mSessionOff).size()); 4307 4308 ASSERT_EQ(types.size(), GetLocalTracks(*mSessionAns).size()); 4309 ASSERT_EQ(types.size(), GetRemoteTracks(*mSessionAns).size()); 4310 } 4311 } 4312 4313 TEST_F(JsepSessionTest, NegotiationNoMlines) { OfferAnswer(); } 4314 4315 TEST_F(JsepSessionTest, TestIceLite) { 4316 AddTracks(*mSessionOff, "audio"); 4317 AddTracks(*mSessionAns, "audio"); 4318 std::string offer = CreateOffer(); 4319 SetLocalOffer(offer, CHECK_SUCCESS); 4320 4321 UniquePtr<Sdp> parsedOffer(Parse(offer)); 4322 parsedOffer->GetAttributeList().SetAttribute( 4323 new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute)); 4324 4325 std::ostringstream os; 4326 parsedOffer->Serialize(os); 4327 SetRemoteOffer(os.str(), CHECK_SUCCESS); 4328 4329 ASSERT_TRUE(mSessionAns->RemoteIsIceLite()); 4330 ASSERT_FALSE(mSessionOff->RemoteIsIceLite()); 4331 } 4332 4333 TEST_F(JsepSessionTest, TestIceOptions) { 4334 AddTracks(*mSessionOff, "audio"); 4335 AddTracks(*mSessionAns, "audio"); 4336 std::string offer = CreateOffer(); 4337 SetLocalOffer(offer, CHECK_SUCCESS); 4338 SetRemoteOffer(offer, CHECK_SUCCESS); 4339 std::string answer = CreateAnswer(); 4340 SetLocalAnswer(answer, CHECK_SUCCESS); 4341 SetRemoteAnswer(answer, CHECK_SUCCESS); 4342 4343 ASSERT_EQ(1U, mSessionOff->GetIceOptions().size()); 4344 ASSERT_EQ("trickle", mSessionOff->GetIceOptions()[0]); 4345 4346 ASSERT_EQ(1U, mSessionAns->GetIceOptions().size()); 4347 ASSERT_EQ("trickle", mSessionAns->GetIceOptions()[0]); 4348 } 4349 4350 TEST_F(JsepSessionTest, TestIceRestart) { 4351 AddTracks(*mSessionOff, "audio"); 4352 AddTracks(*mSessionAns, "audio"); 4353 std::string offer = CreateOffer(); 4354 SetLocalOffer(offer, CHECK_SUCCESS); 4355 SetRemoteOffer(offer, CHECK_SUCCESS); 4356 std::string answer = CreateAnswer(); 4357 SetLocalAnswer(answer, CHECK_SUCCESS); 4358 SetRemoteAnswer(answer, CHECK_SUCCESS); 4359 4360 JsepOfferOptions options; 4361 options.mIceRestart = Some(true); 4362 4363 std::string reoffer = CreateOffer(Some(options)); 4364 SetLocalOffer(reoffer, CHECK_SUCCESS); 4365 SetRemoteOffer(reoffer, CHECK_SUCCESS); 4366 std::string reanswer = CreateAnswer(); 4367 SetLocalAnswer(reanswer, CHECK_SUCCESS); 4368 SetRemoteAnswer(reanswer, CHECK_SUCCESS); 4369 4370 UniquePtr<Sdp> parsedOffer(Parse(offer)); 4371 ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount()); 4372 UniquePtr<Sdp> parsedReoffer(Parse(reoffer)); 4373 ASSERT_EQ(1U, parsedReoffer->GetMediaSectionCount()); 4374 4375 UniquePtr<Sdp> parsedAnswer(Parse(answer)); 4376 ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount()); 4377 UniquePtr<Sdp> parsedReanswer(Parse(reanswer)); 4378 ASSERT_EQ(1U, parsedReanswer->GetMediaSectionCount()); 4379 4380 // verify ice pwd/ufrag are present in offer/answer and reoffer/reanswer 4381 auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList(); 4382 ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute)); 4383 ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute)); 4384 4385 auto& reofferMediaAttrs = 4386 parsedReoffer->GetMediaSection(0).GetAttributeList(); 4387 ASSERT_TRUE(reofferMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute)); 4388 ASSERT_TRUE(reofferMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute)); 4389 4390 auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList(); 4391 ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute)); 4392 ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute)); 4393 4394 auto& reanswerMediaAttrs = 4395 parsedReanswer->GetMediaSection(0).GetAttributeList(); 4396 ASSERT_TRUE(reanswerMediaAttrs.HasAttribute(SdpAttribute::kIcePwdAttribute)); 4397 ASSERT_TRUE( 4398 reanswerMediaAttrs.HasAttribute(SdpAttribute::kIceUfragAttribute)); 4399 4400 // make sure offer/reoffer ice pwd/ufrag changed on ice restart 4401 ASSERT_NE(offerMediaAttrs.GetIcePwd().c_str(), 4402 reofferMediaAttrs.GetIcePwd().c_str()); 4403 ASSERT_NE(offerMediaAttrs.GetIceUfrag().c_str(), 4404 reofferMediaAttrs.GetIceUfrag().c_str()); 4405 4406 // make sure answer/reanswer ice pwd/ufrag changed on ice restart 4407 ASSERT_NE(answerMediaAttrs.GetIcePwd().c_str(), 4408 reanswerMediaAttrs.GetIcePwd().c_str()); 4409 ASSERT_NE(answerMediaAttrs.GetIceUfrag().c_str(), 4410 reanswerMediaAttrs.GetIceUfrag().c_str()); 4411 4412 auto offererTransceivers = GetTransceivers(*mSessionOff); 4413 auto answererTransceivers = GetTransceivers(*mSessionAns); 4414 ASSERT_EQ(reofferMediaAttrs.GetIceUfrag(), 4415 offererTransceivers[0].mTransport.mLocalUfrag); 4416 ASSERT_EQ(reofferMediaAttrs.GetIceUfrag(), 4417 answererTransceivers[0].mTransport.mIce->GetUfrag()); 4418 ASSERT_EQ(reofferMediaAttrs.GetIcePwd(), 4419 offererTransceivers[0].mTransport.mLocalPwd); 4420 ASSERT_EQ(reofferMediaAttrs.GetIcePwd(), 4421 answererTransceivers[0].mTransport.mIce->GetPassword()); 4422 4423 ASSERT_EQ(reanswerMediaAttrs.GetIceUfrag(), 4424 answererTransceivers[0].mTransport.mLocalUfrag); 4425 ASSERT_EQ(reanswerMediaAttrs.GetIceUfrag(), 4426 offererTransceivers[0].mTransport.mIce->GetUfrag()); 4427 ASSERT_EQ(reanswerMediaAttrs.GetIcePwd(), 4428 answererTransceivers[0].mTransport.mLocalPwd); 4429 ASSERT_EQ(reanswerMediaAttrs.GetIcePwd(), 4430 offererTransceivers[0].mTransport.mIce->GetPassword()); 4431 } 4432 4433 TEST_F(JsepSessionTest, TestAnswererIndicatingIceRestart) { 4434 AddTracks(*mSessionOff, "audio"); 4435 AddTracks(*mSessionAns, "audio"); 4436 std::string offer = CreateOffer(); 4437 SetLocalOffer(offer, CHECK_SUCCESS); 4438 SetRemoteOffer(offer, CHECK_SUCCESS); 4439 std::string answer = CreateAnswer(); 4440 SetLocalAnswer(answer, CHECK_SUCCESS); 4441 SetRemoteAnswer(answer, CHECK_SUCCESS); 4442 4443 // reoffer, but we'll improperly indicate an ice restart in the answer by 4444 // modifying the ice pwd and ufrag 4445 std::string reoffer = CreateOffer(); 4446 SetLocalOffer(reoffer, CHECK_SUCCESS); 4447 SetRemoteOffer(reoffer, CHECK_SUCCESS); 4448 std::string reanswer = CreateAnswer(); 4449 4450 // change the ice pwd and ufrag 4451 ReplaceInSdp(&reanswer, "a=ice-ufrag:", "a=ice-ufrag:bad-"); 4452 ReplaceInSdp(&reanswer, "a=ice-pwd:", "a=ice-pwd:bad-"); 4453 SetLocalAnswer(reanswer, CHECK_SUCCESS); 4454 JsepSession::Result result = 4455 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, reanswer); 4456 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 4457 } 4458 4459 TEST_F(JsepSessionTest, TestExtmap) { 4460 AddTracks(*mSessionOff, "audio"); 4461 AddTracks(*mSessionAns, "audio"); 4462 // ssrc-audio-level will be extmap 1 for both 4463 // csrc-audio-level will be 2 for both 4464 // mid will be 3 for both 4465 // video related extensions take 4 - 7 4466 mSessionOff->AddAudioRtpExtension("foo"); // Default mapping of 8 4467 mSessionOff->AddAudioRtpExtension("bar"); // Default mapping of 9 4468 mSessionAns->AddAudioRtpExtension("bar"); // Default mapping of 8 4469 std::string offer = CreateOffer(); 4470 SetLocalOffer(offer, CHECK_SUCCESS); 4471 SetRemoteOffer(offer, CHECK_SUCCESS); 4472 std::string answer = CreateAnswer(); 4473 SetLocalAnswer(answer, CHECK_SUCCESS); 4474 SetRemoteAnswer(answer, CHECK_SUCCESS); 4475 4476 UniquePtr<Sdp> parsedOffer(Parse(offer)); 4477 ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount()); 4478 4479 auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList(); 4480 ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4481 auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps; 4482 ASSERT_EQ(5U, offerExtmap.size()); 4483 ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 4484 offerExtmap[0].extensionname); 4485 ASSERT_EQ(1U, offerExtmap[0].entry); 4486 ASSERT_EQ("urn:ietf:params:rtp-hdrext:csrc-audio-level", 4487 offerExtmap[1].extensionname); 4488 ASSERT_EQ(2U, offerExtmap[1].entry); 4489 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4490 offerExtmap[2].extensionname); 4491 ASSERT_EQ(3U, offerExtmap[2].entry); 4492 ASSERT_EQ("foo", offerExtmap[3].extensionname); 4493 ASSERT_EQ(8U, offerExtmap[3].entry); 4494 ASSERT_EQ("bar", offerExtmap[4].extensionname); 4495 ASSERT_EQ(9U, offerExtmap[4].entry); 4496 4497 UniquePtr<Sdp> parsedAnswer(Parse(answer)); 4498 ASSERT_EQ(1U, parsedAnswer->GetMediaSectionCount()); 4499 4500 auto& answerMediaAttrs = parsedAnswer->GetMediaSection(0).GetAttributeList(); 4501 ASSERT_TRUE(answerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4502 auto& answerExtmap = answerMediaAttrs.GetExtmap().mExtmaps; 4503 ASSERT_EQ(3U, answerExtmap.size()); 4504 ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 4505 answerExtmap[0].extensionname); 4506 ASSERT_EQ(1U, answerExtmap[0].entry); 4507 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4508 answerExtmap[1].extensionname); 4509 ASSERT_EQ(3U, answerExtmap[1].entry); 4510 // We ensure that the entry for "bar" matches what was in the offer 4511 ASSERT_EQ("bar", answerExtmap[2].extensionname); 4512 ASSERT_EQ(9U, answerExtmap[2].entry); 4513 } 4514 4515 TEST_F(JsepSessionTest, TestExtmapDefaults) { 4516 types.push_back(SdpMediaSection::kAudio); 4517 types.push_back(SdpMediaSection::kVideo); 4518 AddTracks(*mSessionOff, "audio,video"); 4519 4520 std::string offer = CreateOffer(); 4521 SetLocalOffer(offer, CHECK_SUCCESS); 4522 SetRemoteOffer(offer, CHECK_SUCCESS); 4523 4524 std::string answer = CreateAnswer(); 4525 SetLocalAnswer(answer, CHECK_SUCCESS); 4526 SetRemoteAnswer(answer, CHECK_SUCCESS); 4527 4528 UniquePtr<Sdp> parsedOffer(Parse(offer)); 4529 ASSERT_EQ(2U, parsedOffer->GetMediaSectionCount()); 4530 4531 auto& offerAudioMediaAttrs = 4532 parsedOffer->GetMediaSection(0).GetAttributeList(); 4533 ASSERT_TRUE( 4534 offerAudioMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4535 auto& offerAudioExtmap = offerAudioMediaAttrs.GetExtmap().mExtmaps; 4536 ASSERT_EQ(3U, offerAudioExtmap.size()); 4537 4538 ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 4539 offerAudioExtmap[0].extensionname); 4540 ASSERT_EQ(1U, offerAudioExtmap[0].entry); 4541 ASSERT_EQ("urn:ietf:params:rtp-hdrext:csrc-audio-level", 4542 offerAudioExtmap[1].extensionname); 4543 ASSERT_EQ(2U, offerAudioExtmap[1].entry); 4544 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4545 offerAudioExtmap[2].extensionname); 4546 4547 auto& offerVideoMediaAttrs = 4548 parsedOffer->GetMediaSection(1).GetAttributeList(); 4549 ASSERT_TRUE( 4550 offerVideoMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4551 auto& offerVideoExtmap = offerVideoMediaAttrs.GetExtmap().mExtmaps; 4552 ASSERT_EQ(5U, offerVideoExtmap.size()); 4553 4554 ASSERT_EQ(3U, offerVideoExtmap[0].entry); 4555 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4556 offerVideoExtmap[0].extensionname); 4557 ASSERT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", 4558 offerVideoExtmap[1].extensionname); 4559 ASSERT_EQ(4U, offerVideoExtmap[1].entry); 4560 ASSERT_EQ("urn:ietf:params:rtp-hdrext:toffset", 4561 offerVideoExtmap[2].extensionname); 4562 ASSERT_EQ(5U, offerVideoExtmap[2].entry); 4563 ASSERT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", 4564 offerVideoExtmap[3].extensionname); 4565 ASSERT_EQ(6U, offerVideoExtmap[3].entry); 4566 ASSERT_EQ( 4567 "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-" 4568 "extensions-01", 4569 offerVideoExtmap[4].extensionname); 4570 ASSERT_EQ(7U, offerVideoExtmap[4].entry); 4571 4572 UniquePtr<Sdp> parsedAnswer(Parse(answer)); 4573 ASSERT_EQ(2U, parsedAnswer->GetMediaSectionCount()); 4574 4575 auto& answerAudioMediaAttrs = 4576 parsedAnswer->GetMediaSection(0).GetAttributeList(); 4577 ASSERT_TRUE( 4578 answerAudioMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4579 auto& answerAudioExtmap = answerAudioMediaAttrs.GetExtmap().mExtmaps; 4580 ASSERT_EQ(2U, answerAudioExtmap.size()); 4581 4582 ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 4583 answerAudioExtmap[0].extensionname); 4584 ASSERT_EQ(1U, answerAudioExtmap[0].entry); 4585 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4586 answerAudioExtmap[1].extensionname); 4587 ASSERT_EQ(3U, answerAudioExtmap[1].entry); 4588 4589 auto& answerVideoMediaAttrs = 4590 parsedAnswer->GetMediaSection(1).GetAttributeList(); 4591 ASSERT_TRUE( 4592 answerVideoMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4593 auto& answerVideoExtmap = answerVideoMediaAttrs.GetExtmap().mExtmaps; 4594 ASSERT_EQ(4U, answerVideoExtmap.size()); 4595 4596 ASSERT_EQ(3U, answerVideoExtmap[0].entry); 4597 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4598 answerVideoExtmap[0].extensionname); 4599 ASSERT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", 4600 answerVideoExtmap[1].extensionname); 4601 ASSERT_EQ(4U, answerVideoExtmap[1].entry); 4602 ASSERT_EQ("urn:ietf:params:rtp-hdrext:toffset", 4603 answerVideoExtmap[2].extensionname); 4604 ASSERT_EQ(5U, answerVideoExtmap[2].entry); 4605 ASSERT_EQ( 4606 "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-" 4607 "extensions-01", 4608 answerVideoExtmap[3].extensionname); 4609 ASSERT_EQ(7U, answerVideoExtmap[3].entry); 4610 } 4611 4612 TEST_F(JsepSessionTest, TestExtmapWithDuplicates) { 4613 AddTracks(*mSessionOff, "audio"); 4614 AddTracks(*mSessionAns, "audio"); 4615 // ssrc-audio-level will be extmap 1 for both 4616 // csrc-audio-level will be 2 for both 4617 // mid will be 3 for both 4618 // video related extensions take 4 - 7 4619 mSessionOff->AddAudioRtpExtension("foo"); // Default mapping of 8 4620 mSessionOff->AddAudioRtpExtension("bar"); // Default mapping of 9 4621 mSessionOff->AddAudioRtpExtension("bar"); // Should be ignored 4622 mSessionOff->AddAudioRtpExtension("bar"); // Should be ignored 4623 mSessionOff->AddAudioRtpExtension("baz"); // Default mapping of 10 4624 mSessionOff->AddAudioRtpExtension("bar"); // Should be ignored 4625 4626 std::string offer = CreateOffer(); 4627 UniquePtr<Sdp> parsedOffer(Parse(offer)); 4628 ASSERT_EQ(1U, parsedOffer->GetMediaSectionCount()); 4629 4630 auto& offerMediaAttrs = parsedOffer->GetMediaSection(0).GetAttributeList(); 4631 ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4632 auto& offerExtmap = offerMediaAttrs.GetExtmap().mExtmaps; 4633 ASSERT_EQ(6U, offerExtmap.size()); 4634 ASSERT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 4635 offerExtmap[0].extensionname); 4636 ASSERT_EQ(1U, offerExtmap[0].entry); 4637 ASSERT_EQ("urn:ietf:params:rtp-hdrext:csrc-audio-level", 4638 offerExtmap[1].extensionname); 4639 ASSERT_EQ(2U, offerExtmap[1].entry); 4640 ASSERT_EQ("urn:ietf:params:rtp-hdrext:sdes:mid", 4641 offerExtmap[2].extensionname); 4642 ASSERT_EQ(3U, offerExtmap[2].entry); 4643 ASSERT_EQ("foo", offerExtmap[3].extensionname); 4644 ASSERT_EQ(8U, offerExtmap[3].entry); 4645 ASSERT_EQ("bar", offerExtmap[4].extensionname); 4646 ASSERT_EQ(9U, offerExtmap[4].entry); 4647 ASSERT_EQ("baz", offerExtmap[5].extensionname); 4648 ASSERT_EQ(10U, offerExtmap[5].entry); 4649 } 4650 4651 TEST_F(JsepSessionTest, TestExtmapZeroId) { 4652 AddTracks(*mSessionOff, "video"); 4653 AddTracks(*mSessionAns, "video"); 4654 4655 std::string sdp = 4656 "v=0\r\n" 4657 "o=- 6 2 IN IP4 1r\r\n" 4658 "t=0 0a\r\n" 4659 "a=ice-ufrag:Xp\r\n" 4660 "a=ice-pwd:he\r\n" 4661 "a=setup:actpass\r\n" 4662 "a=fingerprint:sha-256 " 4663 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 4664 "08:D2:F7:9D:F5:E2:C1:15\r\n" 4665 "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n" 4666 "c=IN IP4 0\r\n" 4667 "a=rtpmap:100 VP8/90000\r\n" 4668 "a=extmap:0 urn:ietf:params:rtp-hdrext:toffset\r\n"; 4669 auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp); 4670 ASSERT_TRUE(result.mError == Some(dom::PCError::OperationError)); 4671 ASSERT_EQ( 4672 "Description contains invalid extension id 0 on level 0 which is" 4673 " unsupported until 2-byte rtp header extensions are supported in" 4674 " webrtc.org", 4675 mSessionAns->GetLastError()); 4676 } 4677 4678 TEST_F(JsepSessionTest, TestExtmapInvalidId) { 4679 AddTracks(*mSessionAns, "video"); 4680 4681 std::string sdp = 4682 "v=0\r\n" 4683 "o=- 6 2 IN IP4 1r\r\n" 4684 "t=0 0a\r\n" 4685 "a=ice-ufrag:Xp\r\n" 4686 "a=ice-pwd:he\r\n" 4687 "a=setup:actpass\r\n" 4688 "a=fingerprint:sha-256 " 4689 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 4690 "08:D2:F7:9D:F5:E2:C1:15\r\n" 4691 "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n" 4692 "c=IN IP4 0\r\n" 4693 "a=rtpmap:100 VP8/90000\r\n" 4694 "a=extmap:15 urn:ietf:params:rtp-hdrext:toffset\r\n"; 4695 auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp); 4696 ASSERT_TRUE(result.mError == Some(dom::PCError::OperationError)); 4697 ASSERT_EQ( 4698 "Description contains invalid extension id 15 on level 0 which is" 4699 " unsupported until 2-byte rtp header extensions are supported in" 4700 " webrtc.org", 4701 mSessionAns->GetLastError()); 4702 } 4703 4704 TEST_F(JsepSessionTest, TestExtmapDuplicateId) { 4705 AddTracks(*mSessionAns, "video"); 4706 4707 std::string sdp = 4708 "v=0\r\n" 4709 "o=- 6 2 IN IP4 1r\r\n" 4710 "t=0 0a\r\n" 4711 "a=ice-ufrag:Xp\r\n" 4712 "a=ice-pwd:he\r\n" 4713 "a=setup:actpass\r\n" 4714 "a=fingerprint:sha-256 " 4715 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 4716 "08:D2:F7:9D:F5:E2:C1:15\r\n" 4717 "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n" 4718 "c=IN IP4 0\r\n" 4719 "a=rtpmap:100 VP8/90000\r\n" 4720 "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n" 4721 "a=extmap:2 " 4722 "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"; 4723 auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp); 4724 ASSERT_TRUE(result.mError == Some(dom::PCError::OperationError)); 4725 ASSERT_EQ("Description contains duplicate extension id 2 on level 0", 4726 mSessionAns->GetLastError()); 4727 } 4728 4729 TEST_F(JsepSessionTest, TestNegotiatedExtmapStability) { 4730 AddTracks(*mSessionAns, "audio,video"); 4731 4732 std::string sdp = 4733 "v=0\r\n" 4734 "o=- 6 2 IN IP4 1r\r\n" 4735 "t=0 0a\r\n" 4736 "a=ice-ufrag:Xp\r\n" 4737 "a=ice-pwd:he\r\n" 4738 "a=setup:actpass\r\n" 4739 "a=fingerprint:sha-256 " 4740 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 4741 "08:D2:F7:9D:F5:E2:C1:15\r\n" 4742 "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" 4743 "c=IN IP4 51.81.107.13\r\n" 4744 "a=sendrecv\r\n" 4745 "a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" 4746 "a=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n" 4747 "a=mid:audio\r\n" 4748 "a=rtcp-mux\r\n" 4749 "a=rtpmap:111 opus/48000/2\r\n" 4750 "a=setup:active\r\n" 4751 "a=ssrc:3463672643 cname:{ec9a356a-8d2c-504e-9977-99070a51f929}\r\n" 4752 "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n" 4753 "c=IN IP4 0\r\n" 4754 "a=sendrecv\r\n" 4755 "a=rtpmap:100 VP8/90000\r\n" 4756 "a=extmap:12 urn:ietf:params:rtp-hdrext:toffset\r\n" 4757 "a=extmap:13 " 4758 "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n"; 4759 auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp); 4760 ASSERT_FALSE(result.mError.isSome()); 4761 auto answer = CreateAnswer(); 4762 result = mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 4763 ASSERT_FALSE(result.mError.isSome()); 4764 4765 // Verify that we've negotiated the right extmap based on the unusual values 4766 // in the offer. 4767 auto transceivers = GetTransceivers(*mSessionAns); 4768 ASSERT_EQ(2U, transceivers.size()); 4769 auto* audioSend = transceivers[0].mSendTrack.GetNegotiatedDetails(); 4770 auto* audioRecv = transceivers[0].mRecvTrack.GetNegotiatedDetails(); 4771 auto* videoSend = transceivers[1].mSendTrack.GetNegotiatedDetails(); 4772 auto* videoRecv = transceivers[1].mRecvTrack.GetNegotiatedDetails(); 4773 ASSERT_TRUE(audioSend); 4774 ASSERT_TRUE(audioRecv); 4775 ASSERT_TRUE(videoSend); 4776 ASSERT_TRUE(videoRecv); 4777 ASSERT_EQ( 4778 11U, 4779 audioSend->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry); 4780 ASSERT_EQ( 4781 11U, 4782 audioRecv->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry); 4783 ASSERT_EQ(12U, 4784 videoSend->GetExt("urn:ietf:params:rtp-hdrext:toffset")->entry); 4785 ASSERT_EQ(12U, 4786 videoRecv->GetExt("urn:ietf:params:rtp-hdrext:toffset")->entry); 4787 ASSERT_EQ( 4788 13U, 4789 videoSend 4790 ->GetExt("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time") 4791 ->entry); 4792 ASSERT_EQ( 4793 13U, 4794 videoRecv 4795 ->GetExt("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time") 4796 ->entry); 4797 4798 SwapOfferAnswerRoles(); 4799 4800 // Make sure a reoffer uses the negotiated extmap 4801 auto reoffer = CreateOffer(); 4802 ASSERT_NE(std::string::npos, reoffer.find("a=extmap:11")); 4803 ASSERT_NE(std::string::npos, reoffer.find("a=extmap:12")); 4804 ASSERT_NE(std::string::npos, reoffer.find("a=extmap:13")); 4805 } 4806 4807 TEST_F(JsepSessionTest, TestNegotiatedExtmapCollision) { 4808 AddTracks(*mSessionAns, "audio"); 4809 // ssrc-audio-level will be extmap 1 for both 4810 // csrc-audio-level will be 2 for both 4811 // mid will be 3 for both 4812 mSessionAns->AddAudioRtpExtension("foo"); 4813 mSessionAns->AddAudioRtpExtension("bar"); 4814 mSessionAns->AddAudioRtpExtension("baz"); 4815 4816 // Set up an offer that uses the same extmap entries, but for different 4817 // things, causing collisions. 4818 std::string sdp = 4819 "v=0\r\n" 4820 "o=- 6 2 IN IP4 1r\r\n" 4821 "t=0 0a\r\n" 4822 "a=ice-ufrag:Xp\r\n" 4823 "a=ice-pwd:he\r\n" 4824 "a=setup:actpass\r\n" 4825 "a=fingerprint:sha-256 " 4826 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 4827 "08:D2:F7:9D:F5:E2:C1:15\r\n" 4828 "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" 4829 "c=IN IP4 51.81.107.13\r\n" 4830 "a=sendrecv\r\n" 4831 "a=extmap:1 foo\r\n" 4832 "a=extmap:2 bar\r\n" 4833 "a=extmap:3 baz\r\n" 4834 "a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" 4835 "a=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n" 4836 "a=mid:audio\r\n" 4837 "a=rtcp-mux\r\n" 4838 "a=rtpmap:111 opus/48000/2\r\n" 4839 "a=setup:active\r\n" 4840 "a=ssrc:3463672643 cname:{ec9a356a-8d2c-504e-9977-99070a51f929}\r\n"; 4841 auto result = mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdp); 4842 ASSERT_FALSE(result.mError.isSome()); 4843 auto answer = CreateAnswer(); 4844 result = mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 4845 ASSERT_FALSE(result.mError.isSome()); 4846 4847 // Verify that we've negotiated the right extmap based on the unusual values 4848 // in the offer. 4849 auto transceivers = GetTransceivers(*mSessionAns); 4850 ASSERT_EQ(1U, transceivers.size()); 4851 auto* audioSend = transceivers[0].mSendTrack.GetNegotiatedDetails(); 4852 auto* audioRecv = transceivers[0].mRecvTrack.GetNegotiatedDetails(); 4853 ASSERT_TRUE(audioSend); 4854 ASSERT_TRUE(audioRecv); 4855 ASSERT_EQ(1U, audioSend->GetExt("foo")->entry); 4856 ASSERT_EQ(1U, audioRecv->GetExt("foo")->entry); 4857 ASSERT_EQ(2U, audioSend->GetExt("bar")->entry); 4858 ASSERT_EQ(2U, audioRecv->GetExt("bar")->entry); 4859 ASSERT_EQ(3U, audioSend->GetExt("baz")->entry); 4860 ASSERT_EQ(3U, audioRecv->GetExt("baz")->entry); 4861 ASSERT_EQ( 4862 11U, 4863 audioSend->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry); 4864 ASSERT_EQ( 4865 11U, 4866 audioRecv->GetExt("urn:ietf:params:rtp-hdrext:ssrc-audio-level")->entry); 4867 SwapOfferAnswerRoles(); 4868 4869 // Make sure a reoffer uses the negotiated extmap 4870 auto reoffer = CreateOffer(); 4871 ASSERT_NE(std::string::npos, reoffer.find("a=extmap:1 foo")); 4872 ASSERT_NE(std::string::npos, reoffer.find("a=extmap:2 bar")); 4873 ASSERT_NE(std::string::npos, reoffer.find("a=extmap:3 baz")); 4874 ASSERT_NE( 4875 std::string::npos, 4876 reoffer.find("a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level")); 4877 4878 // Ensure no duplicates 4879 ASSERT_EQ(std::string::npos, 4880 reoffer.find("a=extmap:1 ", reoffer.find("a=extmap:1 ") + 1)); 4881 ASSERT_EQ(std::string::npos, 4882 reoffer.find("a=extmap:2 ", reoffer.find("a=extmap:2 ") + 1)); 4883 ASSERT_EQ(std::string::npos, 4884 reoffer.find("a=extmap:3 ", reoffer.find("a=extmap:3 ") + 1)); 4885 ASSERT_EQ(std::string::npos, 4886 reoffer.find("a=extmap:11 ", reoffer.find("a=extmap:11 ") + 1)); 4887 } 4888 4889 TEST_F(JsepSessionTest, TestExtmapAnswerChangesId) { 4890 types.push_back(SdpMediaSection::kAudio); 4891 AddTracks(*mSessionOff, "audio"); 4892 AddTracks(*mSessionAns, "audio"); 4893 4894 std::string offer = CreateOffer(); 4895 SetLocalOffer(offer, ALL_CHECKS); 4896 SetRemoteOffer(offer, ALL_CHECKS); 4897 4898 std::string answer = CreateAnswer(); 4899 std::string mungedAnswer = 4900 SetExtmap(answer, "urn:ietf:params:rtp-hdrext:sdes:mid", 14); 4901 JsepSession::Result result = 4902 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, mungedAnswer); 4903 ASSERT_TRUE(result.mError.isSome()); 4904 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 4905 ASSERT_NE(mSessionOff->GetLastError().find( 4906 "Answer changed id for extmap attribute at level 0"), 4907 std::string::npos); 4908 } 4909 4910 TEST_F(JsepSessionTest, TestExtmapChangeId) { 4911 types.push_back(SdpMediaSection::kAudio); 4912 AddTracks(*mSessionOff, "audio"); 4913 AddTracks(*mSessionAns, "audio"); 4914 4915 OfferAnswer(); 4916 4917 // RFC 5285 does not seem to forbid giving a pre-existing extension a new id, 4918 // as long as that id has never been used before 4919 { 4920 std::string offer = CreateOffer(); 4921 SetLocalOffer(offer, ALL_CHECKS); 4922 uint16_t oldId = 0; 4923 std::string mungedOffer = 4924 SetExtmap(offer, "urn:ietf:params:rtp-hdrext:sdes:mid", 14, &oldId); 4925 ASSERT_NE(oldId, 0); 4926 SetRemoteOffer(mungedOffer, ALL_CHECKS); 4927 4928 std::string answer = CreateAnswer(); 4929 SetLocalAnswer(answer, ALL_CHECKS); 4930 4931 std::string mungedAnswer = 4932 SetExtmap(answer, "urn:ietf:params:rtp-hdrext:sdes:mid", oldId); 4933 SetRemoteAnswer(mungedAnswer, ALL_CHECKS); 4934 } 4935 4936 // Make sure going back to the previous id works 4937 OfferAnswer(); 4938 } 4939 4940 TEST_F(JsepSessionTest, TestExtmapSwap) { 4941 types.push_back(SdpMediaSection::kAudio); 4942 AddTracks(*mSessionOff, "audio"); 4943 AddTracks(*mSessionAns, "audio"); 4944 4945 OfferAnswer(); 4946 4947 std::string offer = CreateOffer(); 4948 uint16_t midId = GetExtmap(offer, "urn:ietf:params:rtp-hdrext:sdes:mid"); 4949 uint16_t ssrcLevelId = 0; 4950 std::string mungedOffer = 4951 SetExtmap(offer, "urn:ietf:params:rtp-hdrext:ssrc-audio-level", midId, 4952 &ssrcLevelId); 4953 mungedOffer = SetExtmap(mungedOffer, "urn:ietf:params:rtp-hdrext:sdes:mid", 4954 ssrcLevelId); 4955 4956 JsepSession::Result result = 4957 mSessionAns->SetRemoteDescription(kJsepSdpOffer, mungedOffer); 4958 ASSERT_TRUE(result.mError.isSome()); 4959 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 4960 ASSERT_NE(mSessionAns->GetLastError().find( 4961 "Remote description attempted to remap RTP extension id"), 4962 std::string::npos); 4963 } 4964 4965 TEST_F(JsepSessionTest, TestExtmapReuse) { 4966 types.push_back(SdpMediaSection::kAudio); 4967 AddTracks(*mSessionOff, "audio"); 4968 AddTracks(*mSessionAns, "audio"); 4969 4970 OfferAnswer(); 4971 4972 std::string offer = CreateOffer(); 4973 UniquePtr<Sdp> munge(Parse(offer)); 4974 ASSERT_EQ(1U, munge->GetMediaSectionCount()); 4975 4976 auto& offerMediaAttrs = munge->GetMediaSection(0).GetAttributeList(); 4977 ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 4978 auto offerExtmap = offerMediaAttrs.GetExtmap(); 4979 for (auto& ext : offerExtmap.mExtmaps) { 4980 if (ext.extensionname == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") { 4981 ext.extensionname = "foo"; 4982 } 4983 } 4984 4985 offerMediaAttrs.SetAttribute(offerExtmap.Clone()); 4986 4987 std::string sdpString = munge->ToString(); 4988 JsepSession::Result result = 4989 mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdpString); 4990 ASSERT_TRUE(result.mError.isSome()); 4991 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 4992 ASSERT_NE(mSessionAns->GetLastError().find( 4993 "Remote description attempted to remap RTP extension id"), 4994 std::string::npos); 4995 } 4996 4997 TEST_F(JsepSessionTest, TestExtmapReuseAfterRenegotiation) { 4998 types.push_back(SdpMediaSection::kAudio); 4999 AddTracks(*mSessionOff, "audio"); 5000 AddTracks(*mSessionAns, "audio"); 5001 5002 OfferAnswer(); 5003 5004 // Renegotiate without ssrc-audio-level 5005 { 5006 std::string offer = CreateOffer(); 5007 SetLocalOffer(offer, ALL_CHECKS); 5008 // Passing 0 removes urn:ietf:params:rtp-hdrext:ssrc-audio-level 5009 std::string mungedOffer = 5010 SetExtmap(offer, "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 0); 5011 SetRemoteOffer(mungedOffer, ALL_CHECKS); 5012 5013 std::string answer = CreateAnswer(); 5014 SetLocalAnswer(answer, ALL_CHECKS); 5015 SetRemoteAnswer(answer, ALL_CHECKS); 5016 } 5017 5018 // Make sure trying to reuse the id for ssrc-audio-level fails, even though we 5019 // did not use it last round. 5020 { 5021 std::string offer = CreateOffer(); 5022 UniquePtr<Sdp> munge(Parse(offer)); 5023 ASSERT_EQ(1U, munge->GetMediaSectionCount()); 5024 5025 auto& offerMediaAttrs = munge->GetMediaSection(0).GetAttributeList(); 5026 ASSERT_TRUE(offerMediaAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)); 5027 auto offerExtmap = offerMediaAttrs.GetExtmap(); 5028 for (auto& ext : offerExtmap.mExtmaps) { 5029 if (ext.extensionname == "urn:ietf:params:rtp-hdrext:ssrc-audio-level") { 5030 ext.extensionname = "foo"; 5031 } 5032 } 5033 5034 offerMediaAttrs.SetAttribute(offerExtmap.Clone()); 5035 5036 std::string sdpString = munge->ToString(); 5037 JsepSession::Result result = 5038 mSessionAns->SetRemoteDescription(kJsepSdpOffer, sdpString); 5039 ASSERT_TRUE(result.mError.isSome()); 5040 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 5041 ASSERT_NE(mSessionAns->GetLastError().find( 5042 "Remote description attempted to remap RTP extension id"), 5043 std::string::npos); 5044 } 5045 } 5046 5047 TEST_F(JsepSessionTest, TestRtcpFbStar) { 5048 AddTracks(*mSessionOff, "video"); 5049 AddTracks(*mSessionAns, "video"); 5050 5051 std::string offer = CreateOffer(); 5052 5053 UniquePtr<Sdp> parsedOffer(Parse(offer)); 5054 auto* rtcpfbs = new SdpRtcpFbAttributeList; 5055 rtcpfbs->PushEntry("*", SdpRtcpFbAttributeList::kNack); 5056 parsedOffer->GetMediaSection(0).GetAttributeList().SetAttribute(rtcpfbs); 5057 offer = parsedOffer->ToString(); 5058 5059 SetLocalOffer(offer, CHECK_SUCCESS); 5060 SetRemoteOffer(offer, CHECK_SUCCESS); 5061 std::string answer = CreateAnswer(); 5062 SetLocalAnswer(answer, CHECK_SUCCESS); 5063 SetRemoteAnswer(answer, CHECK_SUCCESS); 5064 5065 ASSERT_EQ(1U, GetRemoteTracks(*mSessionAns).size()); 5066 JsepTrack track = GetRemoteTracks(*mSessionAns)[0]; 5067 ASSERT_TRUE(track.GetNegotiatedDetails()); 5068 auto* details = track.GetNegotiatedDetails(); 5069 for (const auto& codec : details->GetEncoding(0).GetCodecs()) { 5070 const JsepVideoCodecDescription* videoCodec = 5071 static_cast<const JsepVideoCodecDescription*>(codec.get()); 5072 ASSERT_EQ(1U, videoCodec->mNackFbTypes.size()); 5073 ASSERT_EQ("", videoCodec->mNackFbTypes[0]); 5074 } 5075 } 5076 5077 TEST_F(JsepSessionTest, TestUniqueReceivePayloadTypes) { 5078 // The audio payload types will all appear more than once. 5079 // For the offerer, only one video m-section will be receiving, so those 5080 // video payload types will be unique. 5081 // On the other hand, the answerer will have two video m-sections receiving, 5082 // so those _won't_ be unique. 5083 AddTracks(*mSessionOff, "audio,audio,video,video"); 5084 AddTracks(*mSessionAns, "audio,audio,video"); 5085 5086 std::string offer = CreateOffer(); 5087 SetLocalOffer(offer, CHECK_SUCCESS); 5088 SetRemoteOffer(offer, CHECK_SUCCESS); 5089 std::string answer = CreateAnswer(); 5090 SetLocalAnswer(answer, CHECK_SUCCESS); 5091 SetRemoteAnswer(answer, CHECK_SUCCESS); 5092 5093 auto offerTransceivers = GetTransceivers(*mSessionOff); 5094 auto answerTransceivers = GetTransceivers(*mSessionAns); 5095 ASSERT_EQ(4U, offerTransceivers.size()); 5096 ASSERT_EQ(4U, answerTransceivers.size()); 5097 5098 ASSERT_FALSE(IsNull(offerTransceivers[0].mRecvTrack)); 5099 ASSERT_TRUE(offerTransceivers[0].mRecvTrack.GetNegotiatedDetails()); 5100 ASSERT_THAT(offerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes(), 5101 UnorderedElementsAre()); 5102 ASSERT_THAT(offerTransceivers[0].mRecvTrack.GetOtherReceivePayloadTypes(), 5103 UnorderedElementsAre(0, 8, 9, 101, 109, 97, 99, 103, 105, 120, 5104 121, 122, 123, 126)); 5105 5106 ASSERT_FALSE(IsNull(offerTransceivers[1].mRecvTrack)); 5107 ASSERT_TRUE(offerTransceivers[1].mRecvTrack.GetNegotiatedDetails()); 5108 ASSERT_THAT(offerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes(), 5109 UnorderedElementsAre()); 5110 ASSERT_THAT(offerTransceivers[1].mRecvTrack.GetOtherReceivePayloadTypes(), 5111 UnorderedElementsAre(0, 8, 9, 101, 109, 97, 99, 103, 105, 120, 5112 121, 122, 123, 126)); 5113 5114 ASSERT_FALSE(IsNull(offerTransceivers[2].mRecvTrack)); 5115 ASSERT_TRUE(offerTransceivers[2].mRecvTrack.GetNegotiatedDetails()); 5116 ASSERT_THAT(offerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes(), 5117 UnorderedElementsAre(97, 99, 103, 105, 120, 121, 122, 123, 126)); 5118 ASSERT_THAT(offerTransceivers[2].mRecvTrack.GetOtherReceivePayloadTypes(), 5119 UnorderedElementsAre(0, 8, 9, 101, 109)); 5120 5121 ASSERT_TRUE(IsNull(offerTransceivers[3].mRecvTrack)); 5122 ASSERT_TRUE(offerTransceivers[3].mRecvTrack.GetNegotiatedDetails()); 5123 ASSERT_THAT(offerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes(), 5124 UnorderedElementsAre()); 5125 ASSERT_THAT(offerTransceivers[3].mRecvTrack.GetOtherReceivePayloadTypes(), 5126 UnorderedElementsAre()); 5127 5128 ASSERT_FALSE(IsNull(answerTransceivers[0].mRecvTrack)); 5129 ASSERT_TRUE(answerTransceivers[0].mRecvTrack.GetNegotiatedDetails()); 5130 ASSERT_THAT(answerTransceivers[0].mRecvTrack.GetUniqueReceivePayloadTypes(), 5131 UnorderedElementsAre()); 5132 ASSERT_THAT(answerTransceivers[0].mRecvTrack.GetOtherReceivePayloadTypes(), 5133 UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122, 5134 123, 126)); 5135 5136 ASSERT_FALSE(IsNull(answerTransceivers[1].mRecvTrack)); 5137 ASSERT_TRUE(answerTransceivers[1].mRecvTrack.GetNegotiatedDetails()); 5138 ASSERT_THAT(answerTransceivers[1].mRecvTrack.GetUniqueReceivePayloadTypes(), 5139 UnorderedElementsAre()); 5140 ASSERT_THAT(answerTransceivers[1].mRecvTrack.GetOtherReceivePayloadTypes(), 5141 UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122, 5142 123, 126)); 5143 5144 // Answerer is receiving two video streams with the same payload types. 5145 // Neither recv track should have unique pts. 5146 ASSERT_FALSE(IsNull(answerTransceivers[2].mRecvTrack)); 5147 ASSERT_TRUE(answerTransceivers[2].mRecvTrack.GetNegotiatedDetails()); 5148 ASSERT_THAT(answerTransceivers[2].mRecvTrack.GetUniqueReceivePayloadTypes(), 5149 UnorderedElementsAre()); 5150 ASSERT_THAT(answerTransceivers[2].mRecvTrack.GetOtherReceivePayloadTypes(), 5151 UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122, 5152 123, 126)); 5153 5154 ASSERT_FALSE(IsNull(answerTransceivers[3].mRecvTrack)); 5155 ASSERT_TRUE(answerTransceivers[3].mRecvTrack.GetNegotiatedDetails()); 5156 ASSERT_THAT(answerTransceivers[3].mRecvTrack.GetUniqueReceivePayloadTypes(), 5157 UnorderedElementsAre()); 5158 ASSERT_THAT(answerTransceivers[3].mRecvTrack.GetOtherReceivePayloadTypes(), 5159 UnorderedElementsAre(0, 8, 9, 101, 109, 99, 105, 120, 121, 122, 5160 123, 126)); 5161 } 5162 5163 TEST_F(JsepSessionTest, UnknownFingerprintAlgorithm) { 5164 types.push_back(SdpMediaSection::kAudio); 5165 AddTracks(*mSessionOff, "audio"); 5166 AddTracks(*mSessionAns, "audio"); 5167 5168 std::string offer(CreateOffer()); 5169 SetLocalOffer(offer); 5170 ReplaceAll("fingerprint:sha", "fingerprint:foo", &offer); 5171 JsepSession::Result result = 5172 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 5173 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 5174 ASSERT_NE("", mSessionAns->GetLastError()); 5175 } 5176 5177 TEST(H264ProfileLevelIdTest, TestLevelComparisons) 5178 { 5179 ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x421D0B), // 1b 5180 JsepVideoCodecDescription::GetSaneH264Level(0x420D0B)); // 1.1 5181 ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x420D0A), // 1.0 5182 JsepVideoCodecDescription::GetSaneH264Level(0x421D0B)); // 1b 5183 ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x420D0A), // 1.0 5184 JsepVideoCodecDescription::GetSaneH264Level(0x420D0B)); // 1.1 5185 5186 ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x640009), // 1b 5187 JsepVideoCodecDescription::GetSaneH264Level(0x64000B)); // 1.1 5188 ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x64000A), // 1.0 5189 JsepVideoCodecDescription::GetSaneH264Level(0x640009)); // 1b 5190 ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x64000A), // 1.0 5191 JsepVideoCodecDescription::GetSaneH264Level(0x64000B)); // 1.1 5192 } 5193 5194 TEST(H264ProfileLevelIdTest, TestLevelSetting) 5195 { 5196 uint32_t profileLevelId = 0x420D0A; 5197 JsepVideoCodecDescription::SetSaneH264Level( 5198 JsepVideoCodecDescription::GetSaneH264Level(0x42100B), &profileLevelId); 5199 ASSERT_EQ((uint32_t)0x421D0B, profileLevelId); 5200 5201 JsepVideoCodecDescription::SetSaneH264Level( 5202 JsepVideoCodecDescription::GetSaneH264Level(0x42000A), &profileLevelId); 5203 ASSERT_EQ((uint32_t)0x420D0A, profileLevelId); 5204 5205 profileLevelId = 0x6E100A; 5206 JsepVideoCodecDescription::SetSaneH264Level( 5207 JsepVideoCodecDescription::GetSaneH264Level(0x640009), &profileLevelId); 5208 ASSERT_EQ((uint32_t)0x6E1009, profileLevelId); 5209 5210 JsepVideoCodecDescription::SetSaneH264Level( 5211 JsepVideoCodecDescription::GetSaneH264Level(0x64000B), &profileLevelId); 5212 ASSERT_EQ((uint32_t)0x6E100B, profileLevelId); 5213 } 5214 5215 TEST_F(JsepSessionTest, StronglyPreferredCodec) { 5216 for (auto& codec : mSessionAns->Codecs()) { 5217 if (codec->mName == "H264") { 5218 codec->mStronglyPreferred = true; 5219 } 5220 } 5221 5222 types.push_back(SdpMediaSection::kVideo); 5223 AddTracks(*mSessionOff, "video"); 5224 AddTracks(*mSessionAns, "video"); 5225 5226 OfferAnswer(); 5227 5228 UniquePtr<JsepCodecDescription> codec; 5229 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 5230 ASSERT_TRUE(codec); 5231 ASSERT_EQ("H264", codec->mName); 5232 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 5233 ASSERT_TRUE(codec); 5234 ASSERT_EQ("H264", codec->mName); 5235 } 5236 5237 TEST_F(JsepSessionTest, LowDynamicPayloadType) { 5238 SetPayloadTypeNumber(*mSessionOff, "opus", "12"); 5239 types.push_back(SdpMediaSection::kAudio); 5240 AddTracks(*mSessionOff, "audio"); 5241 AddTracks(*mSessionAns, "audio"); 5242 5243 OfferAnswer(); 5244 UniquePtr<JsepCodecDescription> codec; 5245 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 5246 ASSERT_TRUE(codec); 5247 ASSERT_EQ("opus", codec->mName); 5248 ASSERT_EQ("12", codec->mDefaultPt); 5249 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 5250 ASSERT_TRUE(codec); 5251 ASSERT_EQ("opus", codec->mName); 5252 ASSERT_EQ("12", codec->mDefaultPt); 5253 } 5254 5255 TEST_F(JsepSessionTest, TestOfferPTAsymmetry) { 5256 SetPayloadTypeNumber(*mSessionAns, "opus", "105"); 5257 types.push_back(SdpMediaSection::kAudio); 5258 AddTracks(*mSessionOff, "audio"); 5259 AddTracks(*mSessionAns, "audio"); 5260 JsepOfferOptions options; 5261 5262 // Ensure that mSessionAns is appropriately configured. Also ensure that 5263 // creating an offer with 105 does not prompt mSessionAns to ignore the 5264 // PT in the offer. 5265 std::string offer; 5266 JsepSession::Result result = mSessionAns->CreateOffer(options, &offer); 5267 ASSERT_FALSE(result.mError.isSome()); 5268 ASSERT_NE(std::string::npos, offer.find("a=rtpmap:105 opus")) << offer; 5269 5270 OfferAnswer(); 5271 5272 // Answerer should use what the offerer suggested 5273 UniquePtr<JsepCodecDescription> codec; 5274 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 5275 ASSERT_TRUE(codec); 5276 ASSERT_EQ("opus", codec->mName); 5277 ASSERT_EQ("109", codec->mDefaultPt); 5278 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 5279 ASSERT_TRUE(codec); 5280 ASSERT_EQ("opus", codec->mName); 5281 ASSERT_EQ("109", codec->mDefaultPt); 5282 5283 // Answerer should not change when it reoffers 5284 result = mSessionAns->CreateOffer(options, &offer); 5285 ASSERT_FALSE(result.mError.isSome()); 5286 ASSERT_NE(std::string::npos, offer.find("a=rtpmap:109 opus")) << offer; 5287 } 5288 5289 TEST_F(JsepSessionTest, TestAnswerPTAsymmetry) { 5290 // JsepSessionImpl will never answer with an asymmetric payload type 5291 // (tested in TestOfferPTAsymmetry), so we have to rewrite SDP a little. 5292 types.push_back(SdpMediaSection::kAudio); 5293 AddTracks(*mSessionOff, "audio"); 5294 AddTracks(*mSessionAns, "audio"); 5295 5296 std::string offer = CreateOffer(); 5297 SetLocalOffer(offer); 5298 5299 Replace("a=rtpmap:109 opus", "a=rtpmap:105 opus", &offer); 5300 Replace("m=audio 9 UDP/TLS/RTP/SAVPF 109", "m=audio 9 UDP/TLS/RTP/SAVPF 105", 5301 &offer); 5302 ReplaceAll("a=fmtp:109", "a=fmtp:105", &offer); 5303 SetRemoteOffer(offer); 5304 5305 std::string answer = CreateAnswer(); 5306 SetLocalAnswer(answer); 5307 SetRemoteAnswer(answer); 5308 5309 UniquePtr<JsepCodecDescription> codec; 5310 GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &codec); 5311 ASSERT_TRUE(codec); 5312 ASSERT_EQ("opus", codec->mName); 5313 ASSERT_EQ("105", codec->mDefaultPt); 5314 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &codec); 5315 ASSERT_TRUE(codec); 5316 ASSERT_EQ("opus", codec->mName); 5317 ASSERT_EQ("109", codec->mDefaultPt); 5318 5319 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 5320 ASSERT_TRUE(codec); 5321 ASSERT_EQ("opus", codec->mName); 5322 ASSERT_EQ("105", codec->mDefaultPt); 5323 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 5324 ASSERT_TRUE(codec); 5325 ASSERT_EQ("opus", codec->mName); 5326 ASSERT_EQ("105", codec->mDefaultPt); 5327 5328 // Offerer should use 105 for reoffers 5329 offer = CreateOffer(); 5330 ASSERT_NE(std::string::npos, offer.find("a=rtpmap:105 opus")) << offer; 5331 ASSERT_EQ(std::string::npos, offer.find("a=rtpmap:109 opus")) << offer; 5332 ASSERT_NE(std::string::npos, offer.find("a=fmtp:105")) << offer; 5333 ASSERT_EQ(std::string::npos, offer.find("a=fmtp:109")) << offer; 5334 } 5335 5336 TEST_F(JsepSessionTest, PayloadTypeClash) { 5337 // Set up a scenario where mSessionAns' favorite codec (opus) is unsupported 5338 // by mSessionOff, and mSessionOff uses that payload type for something else. 5339 SetCodecEnabled(*mSessionOff, "opus", false); 5340 SetPayloadTypeNumber(*mSessionOff, "opus", "0"); 5341 SetPayloadTypeNumber(*mSessionOff, "G722", "109"); 5342 SetPayloadTypeNumber(*mSessionAns, "opus", "109"); 5343 types.push_back(SdpMediaSection::kAudio); 5344 AddTracks(*mSessionOff, "audio"); 5345 AddTracks(*mSessionAns, "audio"); 5346 5347 OfferAnswer(); 5348 UniquePtr<JsepCodecDescription> codec; 5349 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 5350 ASSERT_TRUE(codec); 5351 ASSERT_EQ("G722", codec->mName); 5352 ASSERT_EQ("109", codec->mDefaultPt); 5353 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 5354 ASSERT_TRUE(codec); 5355 ASSERT_EQ("G722", codec->mName); 5356 ASSERT_EQ("109", codec->mDefaultPt); 5357 5358 // Now, make sure that mSessionAns does not put a=rtpmap:109 opus in a 5359 // reoffer, since pt 109 is taken for PCMU (the answerer still supports opus, 5360 // and will reoffer it, but it should choose a new payload type for it) 5361 JsepOfferOptions options; 5362 std::string reoffer; 5363 JsepSession::Result result = mSessionAns->CreateOffer(options, &reoffer); 5364 ASSERT_FALSE(result.mError.isSome()); 5365 ASSERT_EQ(std::string::npos, reoffer.find("a=rtpmap:109 opus")) << reoffer; 5366 ASSERT_NE(std::string::npos, reoffer.find(" opus")) << reoffer; 5367 } 5368 5369 TEST_P(JsepSessionTest, TestGlareRollback) { 5370 AddTracks(*mSessionOff); 5371 AddTracks(*mSessionAns); 5372 JsepOfferOptions options; 5373 5374 std::string offer; 5375 ASSERT_FALSE(mSessionAns->CreateOffer(options, &offer).mError.isSome()); 5376 ASSERT_FALSE( 5377 mSessionAns->SetLocalDescription(kJsepSdpOffer, offer).mError.isSome()); 5378 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionAns->GetState()); 5379 5380 ASSERT_FALSE(mSessionOff->CreateOffer(options, &offer).mError.isSome()); 5381 ASSERT_FALSE( 5382 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer).mError.isSome()); 5383 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 5384 5385 ASSERT_EQ(dom::PCError::InvalidStateError, 5386 *mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer).mError); 5387 ASSERT_FALSE( 5388 mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError.isSome()); 5389 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 5390 5391 SetRemoteOffer(offer); 5392 5393 std::string answer = CreateAnswer(); 5394 SetLocalAnswer(answer); 5395 SetRemoteAnswer(answer); 5396 } 5397 5398 TEST_P(JsepSessionTest, TestRejectOfferRollback) { 5399 AddTracks(*mSessionOff); 5400 AddTracks(*mSessionAns); 5401 5402 std::string offer = CreateOffer(); 5403 SetLocalOffer(offer); 5404 SetRemoteOffer(offer); 5405 5406 ASSERT_FALSE( 5407 mSessionAns->SetRemoteDescription(kJsepSdpRollback, "").mError.isSome()); 5408 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 5409 for (const auto& transceiver : GetTransceivers(*mSessionAns)) { 5410 ASSERT_EQ(0U, transceiver.mRecvTrack.GetStreamIds().size()); 5411 } 5412 5413 ASSERT_FALSE( 5414 mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError.isSome()); 5415 ASSERT_EQ(kJsepStateStable, mSessionOff->GetState()); 5416 5417 OfferAnswer(); 5418 } 5419 5420 TEST_P(JsepSessionTest, TestInvalidRollback) { 5421 AddTracks(*mSessionOff); 5422 AddTracks(*mSessionAns); 5423 5424 ASSERT_EQ(dom::PCError::InvalidStateError, 5425 *mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError); 5426 ASSERT_EQ(dom::PCError::InvalidStateError, 5427 *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError); 5428 5429 std::string offer = CreateOffer(); 5430 ASSERT_EQ(dom::PCError::InvalidStateError, 5431 *mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError); 5432 ASSERT_EQ(dom::PCError::InvalidStateError, 5433 *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError); 5434 5435 SetLocalOffer(offer); 5436 ASSERT_EQ(dom::PCError::InvalidStateError, 5437 *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError); 5438 5439 SetRemoteOffer(offer); 5440 ASSERT_EQ(dom::PCError::InvalidStateError, 5441 *mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError); 5442 5443 std::string answer = CreateAnswer(); 5444 ASSERT_EQ(dom::PCError::InvalidStateError, 5445 *mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError); 5446 5447 SetLocalAnswer(answer); 5448 ASSERT_EQ(dom::PCError::InvalidStateError, 5449 *mSessionAns->SetLocalDescription(kJsepSdpRollback, "").mError); 5450 ASSERT_EQ(dom::PCError::InvalidStateError, 5451 *mSessionAns->SetRemoteDescription(kJsepSdpRollback, "").mError); 5452 5453 SetRemoteAnswer(answer); 5454 ASSERT_EQ(dom::PCError::InvalidStateError, 5455 *mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError); 5456 ASSERT_EQ(dom::PCError::InvalidStateError, 5457 *mSessionOff->SetRemoteDescription(kJsepSdpRollback, "").mError); 5458 } 5459 5460 size_t GetActiveTransportCount(const JsepSession& session) { 5461 size_t activeTransportCount = 0; 5462 for (const auto& transceiver : JsepSessionTest::GetTransceivers(session)) { 5463 if (!transceiver.HasBundleLevel() || 5464 (transceiver.BundleLevel() == transceiver.GetLevel())) { 5465 activeTransportCount += transceiver.mTransport.mComponents; 5466 } 5467 } 5468 return activeTransportCount; 5469 } 5470 5471 TEST_P(JsepSessionTest, TestBalancedBundle) { 5472 AddTracks(*mSessionOff); 5473 AddTracks(*mSessionAns); 5474 5475 mSessionOff->SetBundlePolicy(kBundleBalanced); 5476 5477 std::string offer = CreateOffer(); 5478 UniquePtr<Sdp> parsedOffer = std::move(SipccSdpParser().Parse(offer)->Sdp()); 5479 5480 ASSERT_TRUE(parsedOffer.get()); 5481 5482 std::map<SdpMediaSection::MediaType, SdpMediaSection*> firstByType; 5483 5484 for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) { 5485 SdpMediaSection& msection(parsedOffer->GetMediaSection(i)); 5486 bool firstOfType = !firstByType.count(msection.GetMediaType()); 5487 if (firstOfType) { 5488 firstByType[msection.GetMediaType()] = &msection; 5489 } 5490 ASSERT_EQ(!firstOfType, msection.GetAttributeList().HasAttribute( 5491 SdpAttribute::kBundleOnlyAttribute)); 5492 } 5493 5494 SetLocalOffer(offer); 5495 SetRemoteOffer(offer); 5496 std::string answer = CreateAnswer(); 5497 SetLocalAnswer(answer); 5498 SetRemoteAnswer(answer); 5499 5500 CheckTransceiversAreBundled(*mSessionOff, "Offerer transceivers"); 5501 CheckTransceiversAreBundled(*mSessionAns, "Answerer transceivers"); 5502 EXPECT_EQ(1U, GetActiveTransportCount(*mSessionOff)); 5503 EXPECT_EQ(1U, GetActiveTransportCount(*mSessionAns)); 5504 } 5505 5506 TEST_P(JsepSessionTest, TestMaxBundle) { 5507 AddTracks(*mSessionOff); 5508 AddTracks(*mSessionAns); 5509 5510 mSessionOff->SetBundlePolicy(kBundleMaxBundle); 5511 std::string offer = CreateOffer(); 5512 UniquePtr<Sdp> parsedOffer = std::move(SipccSdpParser().Parse(offer)->Sdp()); 5513 5514 ASSERT_TRUE(parsedOffer.get()); 5515 5516 ASSERT_FALSE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute( 5517 SdpAttribute::kBundleOnlyAttribute)); 5518 ASSERT_NE(0U, parsedOffer->GetMediaSection(0).GetPort()); 5519 for (size_t i = 1; i < parsedOffer->GetMediaSectionCount(); ++i) { 5520 ASSERT_TRUE(parsedOffer->GetMediaSection(i).GetAttributeList().HasAttribute( 5521 SdpAttribute::kBundleOnlyAttribute)); 5522 ASSERT_EQ(0U, parsedOffer->GetMediaSection(i).GetPort()); 5523 } 5524 5525 SetLocalOffer(offer); 5526 for (const auto& transceiver : GetTransceivers(*mSessionOff)) { 5527 if (transceiver.GetLevel() == 0) { 5528 // We do not set the bundle-level in have-local-offer unless the 5529 // m-section is bundle-only. 5530 ASSERT_FALSE(transceiver.HasBundleLevel()); 5531 } else { 5532 ASSERT_TRUE(transceiver.HasBundleLevel()); 5533 ASSERT_EQ(0U, transceiver.BundleLevel()); 5534 } 5535 ASSERT_NE("", transceiver.mTransport.mTransportId); 5536 } 5537 5538 SetRemoteOffer(offer); 5539 std::string answer = CreateAnswer(); 5540 SetLocalAnswer(answer); 5541 SetRemoteAnswer(answer); 5542 5543 CheckTransceiversAreBundled(*mSessionOff, "Offerer transceivers"); 5544 CheckTransceiversAreBundled(*mSessionAns, "Answerer transceivers"); 5545 EXPECT_EQ(1U, GetActiveTransportCount(*mSessionOff)); 5546 EXPECT_EQ(1U, GetActiveTransportCount(*mSessionAns)); 5547 } 5548 5549 TEST_F(JsepSessionTest, TestNonDefaultProtocol) { 5550 AddTracks(*mSessionOff, "audio,video,datachannel"); 5551 AddTracks(*mSessionAns, "audio,video,datachannel"); 5552 5553 std::string offer; 5554 ASSERT_FALSE( 5555 mSessionOff->CreateOffer(JsepOfferOptions(), &offer).mError.isSome()); 5556 offer.replace(offer.find("UDP/TLS/RTP/SAVPF"), strlen("UDP/TLS/RTP/SAVPF"), 5557 "RTP/SAVPF"); 5558 offer.replace(offer.find("UDP/TLS/RTP/SAVPF"), strlen("UDP/TLS/RTP/SAVPF"), 5559 "RTP/SAVPF"); 5560 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer); 5561 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 5562 5563 std::string answer; 5564 mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer); 5565 UniquePtr<Sdp> parsedAnswer = Parse(answer); 5566 ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount()); 5567 ASSERT_EQ(SdpMediaSection::kRtpSavpf, 5568 parsedAnswer->GetMediaSection(0).GetProtocol()); 5569 ASSERT_EQ(SdpMediaSection::kRtpSavpf, 5570 parsedAnswer->GetMediaSection(1).GetProtocol()); 5571 5572 mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 5573 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer); 5574 5575 // Make sure reoffer uses the same protocol as before 5576 mSessionOff->CreateOffer(JsepOfferOptions(), &offer); 5577 UniquePtr<Sdp> parsedOffer = Parse(offer); 5578 ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount()); 5579 ASSERT_EQ(SdpMediaSection::kRtpSavpf, 5580 parsedOffer->GetMediaSection(0).GetProtocol()); 5581 ASSERT_EQ(SdpMediaSection::kRtpSavpf, 5582 parsedOffer->GetMediaSection(1).GetProtocol()); 5583 5584 // Make sure reoffer from other side uses the same protocol as before 5585 mSessionAns->CreateOffer(JsepOfferOptions(), &offer); 5586 parsedOffer = Parse(offer); 5587 ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount()); 5588 ASSERT_EQ(SdpMediaSection::kRtpSavpf, 5589 parsedOffer->GetMediaSection(0).GetProtocol()); 5590 ASSERT_EQ(SdpMediaSection::kRtpSavpf, 5591 parsedOffer->GetMediaSection(1).GetProtocol()); 5592 } 5593 5594 TEST_F(JsepSessionTest, CreateOfferNoVideoStreamRecvVideo) { 5595 types.push_back(SdpMediaSection::kAudio); 5596 AddTracks(*mSessionOff, "audio"); 5597 5598 JsepOfferOptions options; 5599 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5600 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5601 5602 CreateOffer(Some(options)); 5603 } 5604 5605 TEST_F(JsepSessionTest, CreateOfferNoAudioStreamRecvAudio) { 5606 types.push_back(SdpMediaSection::kVideo); 5607 AddTracks(*mSessionOff, "video"); 5608 5609 JsepOfferOptions options; 5610 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5611 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5612 5613 CreateOffer(Some(options)); 5614 } 5615 5616 TEST_F(JsepSessionTest, CreateOfferNoVideoStream) { 5617 types.push_back(SdpMediaSection::kAudio); 5618 AddTracks(*mSessionOff, "audio"); 5619 5620 JsepOfferOptions options; 5621 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5622 options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U)); 5623 5624 CreateOffer(Some(options)); 5625 } 5626 5627 TEST_F(JsepSessionTest, CreateOfferNoAudioStream) { 5628 types.push_back(SdpMediaSection::kVideo); 5629 AddTracks(*mSessionOff, "video"); 5630 5631 JsepOfferOptions options; 5632 options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U)); 5633 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5634 5635 CreateOffer(Some(options)); 5636 } 5637 5638 TEST_F(JsepSessionTest, CreateOfferDontReceiveAudio) { 5639 types.push_back(SdpMediaSection::kAudio); 5640 types.push_back(SdpMediaSection::kVideo); 5641 AddTracks(*mSessionOff, "audio,video"); 5642 5643 JsepOfferOptions options; 5644 options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U)); 5645 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5646 5647 CreateOffer(Some(options)); 5648 } 5649 5650 TEST_F(JsepSessionTest, CreateOfferDontReceiveVideo) { 5651 types.push_back(SdpMediaSection::kAudio); 5652 types.push_back(SdpMediaSection::kVideo); 5653 AddTracks(*mSessionOff, "audio,video"); 5654 5655 JsepOfferOptions options; 5656 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5657 options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U)); 5658 5659 CreateOffer(Some(options)); 5660 } 5661 5662 TEST_F(JsepSessionTest, CreateOfferRemoveAudioTrack) { 5663 types.push_back(SdpMediaSection::kAudio); 5664 types.push_back(SdpMediaSection::kVideo); 5665 AddTracks(*mSessionOff, "audio,video"); 5666 5667 SetDirection(*mSessionOff, 1, SdpDirectionAttribute::kSendonly); 5668 JsepTrack removedTrack = RemoveTrack(*mSessionOff, 0); 5669 ASSERT_FALSE(IsNull(removedTrack)); 5670 5671 CreateOffer(); 5672 } 5673 5674 TEST_F(JsepSessionTest, CreateOfferDontReceiveAudioRemoveAudioTrack) { 5675 types.push_back(SdpMediaSection::kAudio); 5676 types.push_back(SdpMediaSection::kVideo); 5677 AddTracks(*mSessionOff, "audio,video"); 5678 5679 SetDirection(*mSessionOff, 0, SdpDirectionAttribute::kSendonly); 5680 JsepTrack removedTrack = RemoveTrack(*mSessionOff, 0); 5681 ASSERT_FALSE(IsNull(removedTrack)); 5682 5683 CreateOffer(); 5684 } 5685 5686 TEST_F(JsepSessionTest, CreateOfferDontReceiveVideoRemoveVideoTrack) { 5687 types.push_back(SdpMediaSection::kAudio); 5688 types.push_back(SdpMediaSection::kVideo); 5689 AddTracks(*mSessionOff, "audio,video"); 5690 5691 JsepOfferOptions options; 5692 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5693 options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U)); 5694 5695 JsepTrack removedTrack = RemoveTrack(*mSessionOff, 0); 5696 ASSERT_FALSE(IsNull(removedTrack)); 5697 5698 CreateOffer(Some(options)); 5699 } 5700 5701 MOZ_RUNINIT static const std::string strSampleCandidate = 5702 "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n"; 5703 5704 static const unsigned short nSamplelevel = 2; 5705 5706 TEST_F(JsepSessionTest, CreateOfferAddCandidate) { 5707 types.push_back(SdpMediaSection::kAudio); 5708 AddTracks(*mSessionOff, "audio"); 5709 std::string offer = CreateOffer(); 5710 SetLocalOffer(offer); 5711 5712 uint16_t level; 5713 std::string mid; 5714 bool skipped; 5715 nsresult rv; 5716 rv = mSessionOff->AddLocalIceCandidate(strSampleCandidate, 5717 GetTransportId(*mSessionOff, 0), "", 5718 &level, &mid, &skipped); 5719 ASSERT_EQ(NS_OK, rv); 5720 } 5721 5722 TEST_F(JsepSessionTest, AddIceCandidateEarly) { 5723 uint16_t level; 5724 std::string mid; 5725 bool skipped; 5726 nsresult rv; 5727 rv = mSessionOff->AddLocalIceCandidate(strSampleCandidate, 5728 GetTransportId(*mSessionOff, 0), "", 5729 &level, &mid, &skipped); 5730 5731 // This can't succeed without a local description 5732 ASSERT_NE(NS_OK, rv); 5733 } 5734 5735 TEST_F(JsepSessionTest, OfferAnswerDontAddAudioStreamOnAnswerNoOptions) { 5736 types.push_back(SdpMediaSection::kAudio); 5737 types.push_back(SdpMediaSection::kVideo); 5738 AddTracks(*mSessionOff, "audio,video"); 5739 AddTracks(*mSessionAns, "video"); 5740 5741 JsepOfferOptions options; 5742 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5743 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5744 5745 CreateOffer(Some(options)); 5746 std::string offer = CreateOffer(Some(options)); 5747 SetLocalOffer(offer); 5748 SetRemoteOffer(offer); 5749 std::string answer = CreateAnswer(); 5750 SetLocalAnswer(answer, CHECK_SUCCESS); 5751 SetRemoteAnswer(answer, CHECK_SUCCESS); 5752 } 5753 5754 TEST_F(JsepSessionTest, OfferAnswerDontAddVideoStreamOnAnswerNoOptions) { 5755 types.push_back(SdpMediaSection::kAudio); 5756 types.push_back(SdpMediaSection::kVideo); 5757 AddTracks(*mSessionOff, "audio,video"); 5758 AddTracks(*mSessionAns, "audio"); 5759 5760 JsepOfferOptions options; 5761 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5762 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5763 5764 CreateOffer(Some(options)); 5765 std::string offer = CreateOffer(Some(options)); 5766 SetLocalOffer(offer); 5767 SetRemoteOffer(offer); 5768 std::string answer = CreateAnswer(); 5769 SetLocalAnswer(answer, CHECK_SUCCESS); 5770 SetRemoteAnswer(answer, CHECK_SUCCESS); 5771 } 5772 5773 TEST_F(JsepSessionTest, OfferAnswerDontAddAudioVideoStreamsOnAnswerNoOptions) { 5774 types.push_back(SdpMediaSection::kAudio); 5775 types.push_back(SdpMediaSection::kVideo); 5776 AddTracks(*mSessionOff, "audio,video"); 5777 AddTracks(*mSessionAns); 5778 5779 JsepOfferOptions options; 5780 options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U)); 5781 options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U)); 5782 5783 OfferAnswer(); 5784 } 5785 5786 TEST_F(JsepSessionTest, OfferAndAnswerWithExtraCodec) { 5787 types.push_back(SdpMediaSection::kAudio); 5788 AddTracks(*mSessionOff, "audio"); 5789 AddTracks(*mSessionAns, "audio"); 5790 std::string offer = CreateOffer(); 5791 SetLocalOffer(offer); 5792 SetRemoteOffer(offer); 5793 std::string answer = CreateAnswer(); 5794 5795 UniquePtr<Sdp> munge = Parse(answer); 5796 SdpMediaSection& mediaSection = munge->GetMediaSection(0); 5797 mediaSection.AddCodec("8", "PCMA", 8000, 1); 5798 std::string sdpString = munge->ToString(); 5799 5800 SetLocalAnswer(sdpString); 5801 SetRemoteAnswer(answer); 5802 } 5803 5804 TEST_F(JsepSessionTest, AddCandidateInHaveLocalOffer) { 5805 types.push_back(SdpMediaSection::kAudio); 5806 AddTracks(*mSessionOff, "audio"); 5807 std::string offer = CreateOffer(); 5808 SetLocalOffer(offer); 5809 5810 std::string mid; 5811 std::string transportId; 5812 JsepSession::Result result = mSessionOff->AddRemoteIceCandidate( 5813 strSampleCandidate, mid, Some(nSamplelevel), "", &transportId); 5814 ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError); 5815 } 5816 5817 TEST_F(JsepSessionTest, SetLocalWithoutCreateOffer) { 5818 types.push_back(SdpMediaSection::kAudio); 5819 AddTracks(*mSessionOff, "audio"); 5820 AddTracks(*mSessionAns, "audio"); 5821 5822 std::string offer = CreateOffer(); 5823 JsepSession::Result result = 5824 mSessionAns->SetLocalDescription(kJsepSdpOffer, offer); 5825 ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError); 5826 } 5827 5828 TEST_F(JsepSessionTest, SetLocalWithoutCreateAnswer) { 5829 types.push_back(SdpMediaSection::kAudio); 5830 AddTracks(*mSessionOff, "audio"); 5831 AddTracks(*mSessionAns, "audio"); 5832 5833 std::string offer = CreateOffer(); 5834 SetRemoteOffer(offer); 5835 JsepSession::Result result = 5836 mSessionAns->SetLocalDescription(kJsepSdpAnswer, offer); 5837 ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError); 5838 } 5839 5840 // Test for Bug 843595 5841 TEST_F(JsepSessionTest, missingUfrag) { 5842 types.push_back(SdpMediaSection::kAudio); 5843 AddTracks(*mSessionOff, "audio"); 5844 AddTracks(*mSessionAns, "audio"); 5845 std::string offer = CreateOffer(); 5846 std::string ufrag = "ice-ufrag"; 5847 std::size_t pos = offer.find(ufrag); 5848 ASSERT_NE(pos, std::string::npos); 5849 offer.replace(pos, ufrag.length(), "ice-ufrog"); 5850 JsepSession::Result result = 5851 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 5852 ASSERT_EQ(dom::PCError::InvalidAccessError, *result.mError); 5853 } 5854 5855 TEST_F(JsepSessionTest, AudioOnlyCalleeNoRtcpMux) { 5856 types.push_back(SdpMediaSection::kAudio); 5857 AddTracks(*mSessionOff, "audio"); 5858 AddTracks(*mSessionAns, "audio"); 5859 std::string offer = CreateOffer(); 5860 std::string rtcp_mux = "a=rtcp-mux\r\n"; 5861 std::size_t pos = offer.find(rtcp_mux); 5862 ASSERT_NE(pos, std::string::npos); 5863 offer.replace(pos, rtcp_mux.length(), ""); 5864 SetLocalOffer(offer); 5865 SetRemoteOffer(offer); 5866 std::string answer = CreateAnswer(); 5867 pos = answer.find(rtcp_mux); 5868 ASSERT_EQ(pos, std::string::npos); 5869 } 5870 5871 // This test comes from Bug 810220 5872 TEST_F(JsepSessionTest, AudioOnlyG711Call) { 5873 std::string offer = 5874 "v=0\r\n" 5875 "o=- 1 1 IN IP4 148.147.200.251\r\n" 5876 "s=-\r\n" 5877 "b=AS:64\r\n" 5878 "t=0 0\r\n" 5879 "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" 5880 "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" 5881 "m=audio 9000 UDP/TLS/RTP/SAVPF 0 8 126\r\n" 5882 "c=IN IP4 148.147.200.251\r\n" 5883 "b=TIAS:64000\r\n" 5884 "a=rtpmap:0 PCMU/8000\r\n" 5885 "a=rtpmap:8 PCMA/8000\r\n" 5886 "a=rtpmap:126 telephone-event/8000\r\n" 5887 "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n" 5888 "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n" 5889 "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" 5890 "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" 5891 "a=setup:active\r\n" 5892 "a=sendrecv\r\n"; 5893 5894 types.push_back(SdpMediaSection::kAudio); 5895 AddTracks(*mSessionAns, "audio"); 5896 SetRemoteOffer(offer, CHECK_SUCCESS); 5897 std::string answer = CreateAnswer(); 5898 5899 // They didn't offer opus, so our answer shouldn't include it. 5900 ASSERT_EQ(answer.find(" opus/"), std::string::npos); 5901 5902 // They also didn't offer video or application 5903 ASSERT_EQ(answer.find("video"), std::string::npos); 5904 ASSERT_EQ(answer.find("application"), std::string::npos); 5905 5906 // We should answer with PCMU and telephone-event 5907 ASSERT_NE(answer.find(" PCMU/8000"), std::string::npos); 5908 5909 // Double-check the directionality 5910 ASSERT_NE(answer.find("\r\na=sendrecv"), std::string::npos); 5911 } 5912 5913 TEST_F(JsepSessionTest, AudioOnlyG722Only) { 5914 types.push_back(SdpMediaSection::kAudio); 5915 AddTracks(*mSessionOff, "audio"); 5916 AddTracks(*mSessionAns, "audio"); 5917 std::string offer = CreateOffer(); 5918 SetLocalOffer(offer); 5919 5920 std::string audio = "m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101\r\n"; 5921 std::size_t pos = offer.find(audio); 5922 ASSERT_NE(pos, std::string::npos); 5923 offer.replace(pos, audio.length(), "m=audio 65375 UDP/TLS/RTP/SAVPF 9\r\n"); 5924 SetRemoteOffer(offer); 5925 5926 std::string answer = CreateAnswer(); 5927 SetLocalAnswer(answer); 5928 ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5929 .find("UDP/TLS/RTP/SAVPF 9\r"), 5930 std::string::npos); 5931 ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5932 .find("a=rtpmap:9 G722/8000"), 5933 std::string::npos); 5934 } 5935 5936 TEST_F(JsepSessionTest, AudioOnlyG722Rejected) { 5937 types.push_back(SdpMediaSection::kAudio); 5938 AddTracks(*mSessionOff, "audio"); 5939 AddTracks(*mSessionAns, "audio"); 5940 std::string offer = CreateOffer(); 5941 SetLocalOffer(offer); 5942 5943 std::string audio = "m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101\r\n"; 5944 std::size_t pos = offer.find(audio); 5945 ASSERT_NE(pos, std::string::npos); 5946 offer.replace(pos, audio.length(), "m=audio 65375 UDP/TLS/RTP/SAVPF 0 8\r\n"); 5947 SetRemoteOffer(offer); 5948 5949 std::string answer = CreateAnswer(); 5950 SetLocalAnswer(answer); 5951 SetRemoteAnswer(answer); 5952 5953 ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5954 .find("UDP/TLS/RTP/SAVPF 0 8\r"), 5955 std::string::npos); 5956 ASSERT_NE(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5957 .find("a=rtpmap:0 PCMU/8000"), 5958 std::string::npos); 5959 ASSERT_EQ(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5960 .find("a=rtpmap:109 opus/48000/2"), 5961 std::string::npos); 5962 ASSERT_EQ(mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5963 .find("a=rtpmap:9 G722/8000"), 5964 std::string::npos); 5965 } 5966 5967 // This test doesn't make sense for bundle 5968 TEST_F(JsepSessionTest, DISABLED_FullCallAudioNoMuxVideoMux) { 5969 types.push_back(SdpMediaSection::kAudio); 5970 AddTracks(*mSessionOff, "audio,video"); 5971 AddTracks(*mSessionAns, "audio,video"); 5972 std::string offer = CreateOffer(); 5973 SetLocalOffer(offer); 5974 std::string rtcp_mux = "a=rtcp-mux\r\n"; 5975 std::size_t pos = offer.find(rtcp_mux); 5976 ASSERT_NE(pos, std::string::npos); 5977 offer.replace(pos, rtcp_mux.length(), ""); 5978 SetRemoteOffer(offer); 5979 std::string answer = CreateAnswer(); 5980 5981 size_t match = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5982 .find("\r\na=rtcp-mux"); 5983 ASSERT_NE(match, std::string::npos); 5984 match = mSessionAns->GetLocalDescription(kJsepDescriptionCurrent) 5985 .find("\r\na=rtcp-mux", match + 1); 5986 ASSERT_EQ(match, std::string::npos); 5987 } 5988 5989 // Disabled pending resolution of bug 818640. 5990 // Actually, this test is completely broken; you can't just call 5991 // SetRemote/CreateAnswer over and over again. 5992 TEST_F(JsepSessionTest, DISABLED_OfferAllDynamicTypes) { 5993 types.push_back(SdpMediaSection::kAudio); 5994 AddTracks(*mSessionAns, "audio"); 5995 5996 std::string offer; 5997 for (int i = 96; i < 128; i++) { 5998 std::stringstream ss; 5999 ss << i; 6000 std::cout << "Trying dynamic pt = " << i << std::endl; 6001 offer = 6002 "v=0\r\n" 6003 "o=- 1 1 IN IP4 148.147.200.251\r\n" 6004 "s=-\r\n" 6005 "b=AS:64\r\n" 6006 "t=0 0\r\n" 6007 "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" 6008 "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" 6009 "m=audio 9000 RTP/AVP " + 6010 ss.str() + 6011 "\r\n" 6012 "c=IN IP4 148.147.200.251\r\n" 6013 "b=TIAS:64000\r\n" 6014 "a=rtpmap:" + 6015 ss.str() + 6016 " opus/48000/2\r\n" 6017 "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n" 6018 "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n" 6019 "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" 6020 "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" 6021 "a=sendrecv\r\n"; 6022 6023 SetRemoteOffer(offer, CHECK_SUCCESS); 6024 std::string answer = CreateAnswer(); 6025 ASSERT_NE(answer.find(ss.str() + " opus/"), std::string::npos); 6026 } 6027 } 6028 6029 TEST_F(JsepSessionTest, ipAddrAnyOffer) { 6030 std::string offer = 6031 "v=0\r\n" 6032 "o=- 1 1 IN IP4 127.0.0.1\r\n" 6033 "s=-\r\n" 6034 "b=AS:64\r\n" 6035 "t=0 0\r\n" 6036 "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" 6037 "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" 6038 "m=audio 9000 UDP/TLS/RTP/SAVPF 99\r\n" 6039 "c=IN IP4 0.0.0.0\r\n" 6040 "a=rtpmap:99 opus/48000/2\r\n" 6041 "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" 6042 "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" 6043 "a=setup:active\r\n" 6044 "a=sendrecv\r\n"; 6045 6046 types.push_back(SdpMediaSection::kAudio); 6047 AddTracks(*mSessionAns, "audio"); 6048 SetRemoteOffer(offer, CHECK_SUCCESS); 6049 std::string answer = CreateAnswer(); 6050 6051 ASSERT_NE(answer.find("a=sendrecv"), std::string::npos); 6052 } 6053 6054 static void CreateSDPForBigOTests(std::string& offer, 6055 const std::string& number) { 6056 offer = 6057 "v=0\r\n" 6058 "o=- "; 6059 offer += number; 6060 offer += " "; 6061 offer += number; 6062 offer += 6063 " IN IP4 127.0.0.1\r\n" 6064 "s=-\r\n" 6065 "b=AS:64\r\n" 6066 "t=0 0\r\n" 6067 "a=fingerprint:sha-256 F3:FA:20:C0:CD:48:C4:5F:02:5F:A5:D3:21:D0:2D:48:" 6068 "7B:31:60:5C:5A:D8:0D:CD:78:78:6C:6D:CE:CC:0C:67\r\n" 6069 "m=audio 9000 RTP/AVP 99\r\n" 6070 "c=IN IP4 0.0.0.0\r\n" 6071 "a=rtpmap:99 opus/48000/2\r\n" 6072 "a=ice-ufrag:cYuakxkEKH+RApYE\r\n" 6073 "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n" 6074 "a=setup:active\r\n" 6075 "a=sendrecv\r\n"; 6076 } 6077 6078 TEST_F(JsepSessionTest, BigOValues) { 6079 std::string offer; 6080 6081 CreateSDPForBigOTests(offer, "12345678901234567"); 6082 6083 types.push_back(SdpMediaSection::kAudio); 6084 AddTracks(*mSessionAns, "audio"); 6085 SetRemoteOffer(offer, CHECK_SUCCESS); 6086 } 6087 6088 TEST_F(JsepSessionTest, BigOValuesExtraChars) { 6089 std::string offer; 6090 6091 CreateSDPForBigOTests(offer, "12345678901234567FOOBAR"); 6092 6093 types.push_back(SdpMediaSection::kAudio); 6094 AddTracks(*mSessionAns, "audio"); 6095 // The signaling state will remain "stable" because the unparsable 6096 // SDP leads to a failure in SetRemoteDescription. 6097 SetRemoteOffer(offer, NO_CHECKS); 6098 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6099 } 6100 6101 TEST_F(JsepSessionTest, BigOValuesTooBig) { 6102 std::string offer; 6103 6104 CreateSDPForBigOTests(offer, "18446744073709551615"); 6105 types.push_back(SdpMediaSection::kAudio); 6106 AddTracks(*mSessionAns, "audio"); 6107 6108 // The signaling state will remain "stable" because the unparsable 6109 // SDP leads to a failure in SetRemoteDescription. 6110 SetRemoteOffer(offer, NO_CHECKS); 6111 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6112 } 6113 6114 TEST_F(JsepSessionTest, SetLocalAnswerInStable) { 6115 types.push_back(SdpMediaSection::kAudio); 6116 AddTracks(*mSessionOff, "audio"); 6117 std::string offer = CreateOffer(); 6118 6119 // The signaling state will remain "stable" because the 6120 // SetLocalDescription call fails. 6121 SetLocalAnswer(offer, NO_CHECKS); 6122 ASSERT_EQ(kJsepStateStable, mSessionOff->GetState()); 6123 } 6124 6125 TEST_F(JsepSessionTest, SetRemoteAnswerInStable) { 6126 const std::string answer = 6127 "v=0\r\n" 6128 "o=Mozilla-SIPUA 4949 0 IN IP4 10.86.255.143\r\n" 6129 "s=SIP Call\r\n" 6130 "t=0 0\r\n" 6131 "a=ice-ufrag:qkEP\r\n" 6132 "a=ice-pwd:ed6f9GuHjLcoCN6sC/Eh7fVl\r\n" 6133 "m=audio 16384 RTP/AVP 0 8 9 101\r\n" 6134 "c=IN IP4 10.86.255.143\r\n" 6135 "a=rtpmap:0 PCMU/8000\r\n" 6136 "a=rtpmap:8 PCMA/8000\r\n" 6137 "a=rtpmap:9 G722/8000\r\n" 6138 "a=rtpmap:101 telephone-event/8000\r\n" 6139 "a=fmtp:101 0-15\r\n" 6140 "a=sendrecv\r\n" 6141 "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n" 6142 "a=candidate:2 2 UDP 2130706431 192.168.2.2 50006 typ host\r\n" 6143 "m=video 1024 RTP/AVP 97\r\n" 6144 "c=IN IP4 10.86.255.143\r\n" 6145 "a=rtpmap:120 VP8/90000\r\n" 6146 "a=fmtp:97 profile-level-id=42E00C\r\n" 6147 "a=sendrecv\r\n" 6148 "a=candidate:1 1 UDP 2130706431 192.168.2.3 50007 typ host\r\n" 6149 "a=candidate:2 2 UDP 2130706431 192.168.2.4 50008 typ host\r\n"; 6150 6151 // The signaling state will remain "stable" because the 6152 // SetRemoteDescription call fails. 6153 JsepSession::Result result = 6154 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer); 6155 ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError); 6156 ASSERT_EQ(kJsepStateStable, mSessionOff->GetState()); 6157 } 6158 6159 TEST_F(JsepSessionTest, SetLocalAnswerInHaveLocalOffer) { 6160 types.push_back(SdpMediaSection::kAudio); 6161 AddTracks(*mSessionOff, "audio"); 6162 std::string offer = CreateOffer(); 6163 6164 SetLocalOffer(offer); 6165 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6166 6167 // The signaling state will remain "have-local-offer" because the 6168 // SetLocalDescription call fails. 6169 JsepSession::Result result = 6170 mSessionOff->SetLocalDescription(kJsepSdpAnswer, offer); 6171 ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError); 6172 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6173 } 6174 6175 TEST_F(JsepSessionTest, SetRemoteOfferInHaveLocalOffer) { 6176 types.push_back(SdpMediaSection::kAudio); 6177 AddTracks(*mSessionOff, "audio"); 6178 std::string offer = CreateOffer(); 6179 6180 SetLocalOffer(offer); 6181 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6182 6183 // The signaling state will remain "have-local-offer" because the 6184 // SetRemoteDescription call fails. 6185 JsepSession::Result result = 6186 mSessionOff->SetRemoteDescription(kJsepSdpOffer, offer); 6187 ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError); 6188 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6189 } 6190 6191 TEST_F(JsepSessionTest, SetLocalOfferInHaveRemoteOffer) { 6192 types.push_back(SdpMediaSection::kAudio); 6193 AddTracks(*mSessionOff, "audio"); 6194 std::string offer = CreateOffer(); 6195 6196 SetRemoteOffer(offer); 6197 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6198 6199 // The signaling state will remain "have-remote-offer" because the 6200 // SetLocalDescription call fails. 6201 JsepSession::Result result = 6202 mSessionAns->SetLocalDescription(kJsepSdpOffer, offer); 6203 ASSERT_EQ(dom::PCError::InvalidModificationError, *result.mError); 6204 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6205 } 6206 6207 TEST_F(JsepSessionTest, SetRemoteAnswerInHaveRemoteOffer) { 6208 types.push_back(SdpMediaSection::kAudio); 6209 AddTracks(*mSessionOff, "audio"); 6210 std::string offer = CreateOffer(); 6211 6212 SetRemoteOffer(offer); 6213 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6214 6215 // The signaling state will remain "have-remote-offer" because the 6216 // SetRemoteDescription call fails. 6217 JsepSession::Result result = 6218 mSessionAns->SetRemoteDescription(kJsepSdpAnswer, offer); 6219 ASSERT_EQ(dom::PCError::InvalidStateError, *result.mError); 6220 6221 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6222 } 6223 6224 TEST_F(JsepSessionTest, RtcpFbInOffer) { 6225 types.push_back(SdpMediaSection::kAudio); 6226 types.push_back(SdpMediaSection::kVideo); 6227 AddTracks(*mSessionOff, "audio,video"); 6228 std::string offer = CreateOffer(); 6229 6230 std::map<std::string, bool> expected; 6231 expected["nack"] = false; 6232 expected["nack pli"] = false; 6233 expected["ccm fir"] = false; 6234 expected["goog-remb"] = false; 6235 expected["transport-cc"] = false; 6236 6237 size_t prev = 0; 6238 size_t found = 0; 6239 for (;;) { 6240 found = offer.find('\n', found + 1); 6241 if (found == std::string::npos) break; 6242 6243 std::string line = offer.substr(prev, (found - prev)); 6244 6245 // ensure no other rtcp-fb values are present 6246 if (line.find("a=rtcp-fb:") != std::string::npos) { 6247 size_t space = line.find(' '); 6248 // strip trailing \r\n 6249 std::string value = line.substr(space + 1, line.length() - space - 2); 6250 std::map<std::string, bool>::iterator entry = expected.find(value); 6251 ASSERT_NE(entry, expected.end()); 6252 entry->second = true; 6253 } 6254 6255 prev = found + 1; 6256 } 6257 6258 // ensure all values are present 6259 for (std::map<std::string, bool>::iterator it = expected.begin(); 6260 it != expected.end(); ++it) { 6261 ASSERT_EQ(it->second, true); 6262 } 6263 } 6264 6265 // In this test we will change the offer SDP's a=setup value 6266 // from actpass to passive. This will force the answer to do active. 6267 TEST_F(JsepSessionTest, AudioCallForceDtlsRoles) { 6268 types.push_back(SdpMediaSection::kAudio); 6269 AddTracks(*mSessionOff, "audio"); 6270 AddTracks(*mSessionAns, "audio"); 6271 std::string offer = CreateOffer(); 6272 6273 std::string actpass = "\r\na=setup:actpass"; 6274 size_t match = offer.find(actpass); 6275 ASSERT_NE(match, std::string::npos); 6276 offer.replace(match, actpass.length(), "\r\na=setup:passive"); 6277 6278 SetLocalOffer(offer); 6279 SetRemoteOffer(offer); 6280 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6281 std::string answer = CreateAnswer(); 6282 match = answer.find("\r\na=setup:active"); 6283 ASSERT_NE(match, std::string::npos); 6284 6285 SetLocalAnswer(answer); 6286 SetRemoteAnswer(answer); 6287 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6288 } 6289 6290 // In this test we will change the offer SDP's a=setup value 6291 // from actpass to active. This will force the answer to do passive. 6292 TEST_F(JsepSessionTest, AudioCallReverseDtlsRoles) { 6293 types.push_back(SdpMediaSection::kAudio); 6294 AddTracks(*mSessionOff, "audio"); 6295 AddTracks(*mSessionAns, "audio"); 6296 std::string offer = CreateOffer(); 6297 6298 std::string actpass = "\r\na=setup:actpass"; 6299 size_t match = offer.find(actpass); 6300 ASSERT_NE(match, std::string::npos); 6301 offer.replace(match, actpass.length(), "\r\na=setup:active"); 6302 6303 SetLocalOffer(offer); 6304 SetRemoteOffer(offer); 6305 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6306 std::string answer = CreateAnswer(); 6307 match = answer.find("\r\na=setup:passive"); 6308 ASSERT_NE(match, std::string::npos); 6309 6310 SetLocalAnswer(answer); 6311 SetRemoteAnswer(answer); 6312 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6313 } 6314 6315 // In this test we will change the answer SDP's a=setup value 6316 // from active to passive. This will make both sides do 6317 // active and should not connect. 6318 TEST_F(JsepSessionTest, AudioCallMismatchDtlsRoles) { 6319 types.push_back(SdpMediaSection::kAudio); 6320 AddTracks(*mSessionOff, "audio"); 6321 AddTracks(*mSessionAns, "audio"); 6322 std::string offer = CreateOffer(); 6323 6324 std::string actpass = "\r\na=setup:actpass"; 6325 size_t match = offer.find(actpass); 6326 ASSERT_NE(match, std::string::npos); 6327 SetLocalOffer(offer); 6328 SetRemoteOffer(offer); 6329 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6330 std::string answer = CreateAnswer(); 6331 SetLocalAnswer(answer); 6332 6333 std::string active = "\r\na=setup:active"; 6334 match = answer.find(active); 6335 ASSERT_NE(match, std::string::npos); 6336 answer.replace(match, active.length(), "\r\na=setup:passive"); 6337 SetRemoteAnswer(answer); 6338 6339 // This is as good as it gets in a JSEP test (w/o starting DTLS) 6340 ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient, 6341 GetTransceivers(*mSessionOff)[0].mTransport.mDtls->GetRole()); 6342 ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient, 6343 GetTransceivers(*mSessionAns)[0].mTransport.mDtls->GetRole()); 6344 } 6345 6346 // Verify that missing a=setup in offer gets rejected 6347 TEST_F(JsepSessionTest, AudioCallOffererNoSetup) { 6348 types.push_back(SdpMediaSection::kAudio); 6349 AddTracks(*mSessionOff, "audio"); 6350 AddTracks(*mSessionAns, "audio"); 6351 std::string offer = CreateOffer(); 6352 SetLocalOffer(offer); 6353 6354 std::string actpass = "\r\na=setup:actpass"; 6355 size_t match = offer.find(actpass); 6356 ASSERT_NE(match, std::string::npos); 6357 offer.replace(match, actpass.length(), ""); 6358 6359 // The signaling state will remain "stable" because the unparsable 6360 // SDP leads to a failure in SetRemoteDescription. 6361 SetRemoteOffer(offer, NO_CHECKS); 6362 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6363 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6364 } 6365 6366 // In this test we will change the answer SDP to remove the 6367 // a=setup line, which results in active being assumed. 6368 TEST_F(JsepSessionTest, AudioCallAnswerNoSetup) { 6369 types.push_back(SdpMediaSection::kAudio); 6370 AddTracks(*mSessionOff, "audio"); 6371 AddTracks(*mSessionAns, "audio"); 6372 std::string offer = CreateOffer(); 6373 size_t match = offer.find("\r\na=setup:actpass"); 6374 ASSERT_NE(match, std::string::npos); 6375 6376 SetLocalOffer(offer); 6377 SetRemoteOffer(offer); 6378 ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState()); 6379 std::string answer = CreateAnswer(); 6380 SetLocalAnswer(answer); 6381 6382 std::string active = "\r\na=setup:active"; 6383 match = answer.find(active); 6384 ASSERT_NE(match, std::string::npos); 6385 answer.replace(match, active.length(), ""); 6386 SetRemoteAnswer(answer); 6387 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6388 6389 // This is as good as it gets in a JSEP test (w/o starting DTLS) 6390 ASSERT_EQ(JsepDtlsTransport::kJsepDtlsServer, 6391 GetTransceivers(*mSessionOff)[0].mTransport.mDtls->GetRole()); 6392 ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient, 6393 GetTransceivers(*mSessionAns)[0].mTransport.mDtls->GetRole()); 6394 } 6395 6396 // Verify that 'holdconn' gets rejected 6397 TEST_F(JsepSessionTest, AudioCallDtlsRoleHoldconn) { 6398 types.push_back(SdpMediaSection::kAudio); 6399 AddTracks(*mSessionOff, "audio"); 6400 AddTracks(*mSessionAns, "audio"); 6401 std::string offer = CreateOffer(); 6402 SetLocalOffer(offer); 6403 6404 std::string actpass = "\r\na=setup:actpass"; 6405 size_t match = offer.find(actpass); 6406 ASSERT_NE(match, std::string::npos); 6407 offer.replace(match, actpass.length(), "\r\na=setup:holdconn"); 6408 6409 // The signaling state will remain "stable" because the unparsable 6410 // SDP leads to a failure in SetRemoteDescription. 6411 SetRemoteOffer(offer, NO_CHECKS); 6412 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6413 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6414 } 6415 6416 // Verify that 'actpass' in answer gets rejected 6417 TEST_F(JsepSessionTest, AudioCallAnswererUsesActpass) { 6418 types.push_back(SdpMediaSection::kAudio); 6419 AddTracks(*mSessionOff, "audio"); 6420 AddTracks(*mSessionAns, "audio"); 6421 std::string offer = CreateOffer(); 6422 SetLocalOffer(offer); 6423 SetRemoteOffer(offer); 6424 std::string answer = CreateAnswer(); 6425 SetLocalAnswer(answer); 6426 6427 std::string active = "\r\na=setup:active"; 6428 size_t match = answer.find(active); 6429 ASSERT_NE(match, std::string::npos); 6430 answer.replace(match, active.length(), "\r\na=setup:actpass"); 6431 6432 // The signaling state will remain "stable" because the unparsable 6433 // SDP leads to a failure in SetRemoteDescription. 6434 SetRemoteAnswer(answer, NO_CHECKS); 6435 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6436 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6437 } 6438 6439 // Verify that 'actpass' in reoffer from previous answerer doesn't result 6440 // in a role switch. 6441 TEST_F(JsepSessionTest, AudioCallPreviousAnswererUsesActpassInReoffer) { 6442 types.push_back(SdpMediaSection::kAudio); 6443 AddTracks(*mSessionOff, "audio"); 6444 AddTracks(*mSessionAns, "audio"); 6445 6446 OfferAnswer(); 6447 6448 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 6449 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 6450 6451 SwapOfferAnswerRoles(); 6452 6453 OfferAnswer(); 6454 6455 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 6456 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive); 6457 } 6458 6459 // Disabled: See Bug 1329028 6460 TEST_F(JsepSessionTest, DISABLED_AudioCallOffererAttemptsSetupRoleSwitch) { 6461 types.push_back(SdpMediaSection::kAudio); 6462 AddTracks(*mSessionOff, "audio"); 6463 AddTracks(*mSessionAns, "audio"); 6464 6465 OfferAnswer(); 6466 6467 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 6468 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 6469 6470 std::string reoffer = CreateOffer(); 6471 SetLocalOffer(reoffer); 6472 6473 std::string actpass = "\r\na=setup:actpass"; 6474 size_t match = reoffer.find(actpass); 6475 ASSERT_NE(match, std::string::npos); 6476 reoffer.replace(match, actpass.length(), "\r\na=setup:active"); 6477 6478 // This is expected to fail. 6479 SetRemoteOffer(reoffer, NO_CHECKS); 6480 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6481 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6482 } 6483 6484 // Disabled: See Bug 1329028 6485 TEST_F(JsepSessionTest, DISABLED_AudioCallAnswererAttemptsSetupRoleSwitch) { 6486 types.push_back(SdpMediaSection::kAudio); 6487 AddTracks(*mSessionOff, "audio"); 6488 AddTracks(*mSessionAns, "audio"); 6489 6490 OfferAnswer(); 6491 6492 ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass); 6493 ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive); 6494 6495 std::string reoffer = CreateOffer(); 6496 SetLocalOffer(reoffer); 6497 SetRemoteOffer(reoffer); 6498 6499 std::string reanswer = CreateAnswer(); 6500 SetLocalAnswer(reanswer); 6501 6502 std::string actpass = "\r\na=setup:active"; 6503 size_t match = reanswer.find(actpass); 6504 ASSERT_NE(match, std::string::npos); 6505 reanswer.replace(match, actpass.length(), "\r\na=setup:passive"); 6506 6507 // This is expected to fail. 6508 SetRemoteAnswer(reanswer, NO_CHECKS); 6509 ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState()); 6510 ASSERT_EQ(kJsepStateStable, mSessionAns->GetState()); 6511 } 6512 6513 // Remove H.264 CB and VP8 from offer, check answer negotiates H.264 B 6514 TEST_F(JsepSessionTest, OfferWithOnlyH264Baseline) { 6515 for (auto& codec : mSessionOff->Codecs()) { 6516 if (codec->mName != "H264" || codec->mDefaultPt == "126" || 6517 codec->mDefaultPt == "97") { 6518 codec->mEnabled = false; 6519 } 6520 } 6521 6522 types.push_back(SdpMediaSection::kAudio); 6523 types.push_back(SdpMediaSection::kVideo); 6524 AddTracks(*mSessionOff, "audio,video"); 6525 AddTracks(*mSessionAns, "audio,video"); 6526 std::string offer = CreateOffer(); 6527 6528 ASSERT_EQ(offer.find("a=rtpmap:126 H264/90000"), std::string::npos); 6529 ASSERT_EQ(offer.find("a=rtpmap:97 H264/90000"), std::string::npos); 6530 ASSERT_EQ(offer.find("a=rtpmap:120 VP8/90000"), std::string::npos); 6531 6532 SetLocalOffer(offer); 6533 SetRemoteOffer(offer); 6534 std::string answer = CreateAnswer(); 6535 size_t match = answer.find("\r\na=setup:active"); 6536 ASSERT_NE(match, std::string::npos); 6537 6538 // validate answer SDP 6539 ASSERT_NE(answer.find("a=rtpmap:105 H264/90000"), std::string::npos); 6540 ASSERT_NE(answer.find("a=rtcp-fb:105 nack"), std::string::npos); 6541 ASSERT_NE(answer.find("a=rtcp-fb:105 nack pli"), std::string::npos); 6542 ASSERT_NE(answer.find("a=rtcp-fb:105 ccm fir"), std::string::npos); 6543 // Ensure VP8 and H264 CB removed 6544 ASSERT_EQ(answer.find("a=rtpmap:126 H264/90000"), std::string::npos); 6545 ASSERT_EQ(answer.find("a=rtpmap:97 H264/90000"), std::string::npos); 6546 ASSERT_EQ(answer.find("a=rtpmap:120 VP8/90000"), std::string::npos); 6547 ASSERT_EQ(answer.find("a=rtcp-fb:120"), std::string::npos); 6548 ASSERT_EQ(answer.find("a=rtcp-fb:126"), std::string::npos); 6549 ASSERT_EQ(answer.find("a=rtcp-fb:97"), std::string::npos); 6550 } 6551 6552 // Test negotiating an answer which has only H.264 P1 6553 // Which means replace VP8 with H.264 P1 in answer 6554 TEST_F(JsepSessionTest, AnswerWithoutVP8) { 6555 types.push_back(SdpMediaSection::kAudio); 6556 types.push_back(SdpMediaSection::kVideo); 6557 AddTracks(*mSessionOff, "audio,video"); 6558 AddTracks(*mSessionAns, "audio,video"); 6559 std::string offer = CreateOffer(); 6560 SetLocalOffer(offer); 6561 SetRemoteOffer(offer); 6562 6563 for (auto& codec : mSessionOff->Codecs()) { 6564 if (codec->mName != "H264" || codec->mDefaultPt == "126") { 6565 codec->mEnabled = false; 6566 } 6567 } 6568 6569 std::string answer = CreateAnswer(); 6570 6571 SetLocalAnswer(answer); 6572 SetRemoteAnswer(answer); 6573 } 6574 6575 // Ok. Hear me out. 6576 // The JSEP spec specifies very different behavior for the following two cases: 6577 // 1. AddTrack caused a transceiver to be created. 6578 // 2. The transceiver was not created as a side-effect of AddTrack. 6579 // 6580 // All together now... 6581 // 6582 // SADFACE :( 6583 // 6584 // Ok, enough of that. The upshot is we need to test two different codepaths for 6585 // the same thing here. Most of this unit-test suite tests the "magic" case 6586 // (case 1 above). Case 2 (the non-magic case) is simpler, so we have just a 6587 // handful of tests. 6588 TEST_F(JsepSessionTest, OffererNoAddTrackMagic) { 6589 types = BuildTypes("audio,video"); 6590 AddTracks(*mSessionOff, NO_ADDTRACK_MAGIC); 6591 AddTracks(*mSessionAns); 6592 6593 // Offerer's transceivers aren't "magic"; they will not associate with the 6594 // remote side's m-sections automatically. But, since they went into the 6595 // offer, everything works normally. 6596 OfferAnswer(); 6597 6598 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6599 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6600 } 6601 6602 TEST_F(JsepSessionTest, AnswererNoAddTrackMagic) { 6603 types = BuildTypes("audio,video"); 6604 AddTracks(*mSessionOff); 6605 AddTracks(*mSessionAns, NO_ADDTRACK_MAGIC); 6606 6607 OfferAnswer(CHECK_SUCCESS); 6608 6609 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6610 // Since answerer's transceivers aren't "magic", they cannot automatically be 6611 // attached to the offerer's m-sections. 6612 ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size()); 6613 6614 SwapOfferAnswerRoles(); 6615 6616 OfferAnswer(CHECK_SUCCESS); 6617 ASSERT_EQ(4U, GetTransceivers(*mSessionOff).size()); 6618 ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size()); 6619 } 6620 6621 // JSEP has rules about when a disabled m-section can be reused; the gist is 6622 // that the m-section has to be negotiated disabled, then it becomes a candidate 6623 // for reuse on the next renegotiation. Stopping a transceiver does not allow 6624 // you to reuse on the next negotiation. 6625 TEST_F(JsepSessionTest, OffererRecycle) { 6626 types = BuildTypes("audio,video"); 6627 AddTracks(*mSessionOff); 6628 AddTracks(*mSessionAns); 6629 6630 OfferAnswer(); 6631 6632 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6633 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6634 GetTransceivers(*mSessionOff)[0].Stop(); 6635 AddTracks(*mSessionOff, "audio"); 6636 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6637 ASSERT_TRUE(mSessionOff->CheckNegotiationNeeded()); 6638 6639 OfferAnswer(CHECK_SUCCESS); 6640 6641 ASSERT_FALSE(mSessionOff->CheckNegotiationNeeded()); 6642 // It is too soon to recycle msection 0, so the new track should have been 6643 // given a new msection. 6644 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6645 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel()); 6646 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6647 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6648 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6649 ASSERT_EQ(2U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6650 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6651 6652 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 6653 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 6654 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6655 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6656 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6657 ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[2].GetLevel()); 6658 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6659 6660 UniquePtr<Sdp> offer = GetParsedLocalDescription(*mSessionOff); 6661 ASSERT_EQ(3U, offer->GetMediaSectionCount()); 6662 ValidateDisabledMSection(&offer->GetMediaSection(0)); 6663 6664 UniquePtr<Sdp> answer = GetParsedLocalDescription(*mSessionAns); 6665 ASSERT_EQ(3U, answer->GetMediaSectionCount()); 6666 ValidateDisabledMSection(&answer->GetMediaSection(0)); 6667 6668 // Ok. Now renegotiating should recycle m-section 0. 6669 AddTracks(*mSessionOff, "audio"); 6670 ASSERT_EQ(4U, GetTransceivers(*mSessionOff).size()); 6671 OfferAnswer(CHECK_SUCCESS); 6672 6673 // Transceiver 3 should now be attached to m-section 0 6674 ASSERT_EQ(4U, GetTransceivers(*mSessionOff).size()); 6675 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6676 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6677 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6678 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6679 ASSERT_EQ(2U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6680 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6681 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[3].GetLevel()); 6682 ASSERT_FALSE(GetTransceivers(*mSessionOff)[3].IsStopped()); 6683 6684 ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size()); 6685 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel()); 6686 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6687 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6688 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6689 ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[2].GetLevel()); 6690 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6691 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[3].GetLevel()); 6692 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped()); 6693 } 6694 6695 TEST_F(JsepSessionTest, RecycleAnswererStopsTransceiver) { 6696 types = BuildTypes("audio,video"); 6697 AddTracks(*mSessionOff); 6698 AddTracks(*mSessionAns); 6699 6700 OfferAnswer(); 6701 6702 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6703 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6704 GetTransceivers(*mSessionAns)[0].Stop(); 6705 ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded()); 6706 6707 OfferAnswer(CHECK_SUCCESS); 6708 6709 // Answerer is not allowed to handle Stop(); it needs to be done in an offer 6710 ASSERT_TRUE(mSessionAns->CheckNegotiationNeeded()); 6711 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6712 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel()); 6713 // webrtc-pc pulled a fast one and modified JSEP to say that the answerer 6714 // does not reject when it has a stopped transceiver. That's an offerer thing 6715 // only. 6716 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6717 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6718 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6719 6720 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6721 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 6722 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopping()); 6723 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6724 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6725 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopping()); 6726 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6727 6728 auto offer = GetParsedLocalDescription(*mSessionOff); 6729 ASSERT_EQ(2U, offer->GetMediaSectionCount()); 6730 6731 auto answer = GetParsedLocalDescription(*mSessionAns); 6732 ASSERT_EQ(2U, answer->GetMediaSectionCount()); 6733 6734 // Switching roles and renegotiating will cause the m-section to be rejected 6735 SwapOfferAnswerRoles(); 6736 OfferAnswer(); 6737 6738 ASSERT_FALSE(mSessionAns->CheckNegotiationNeeded()); 6739 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6740 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel()); 6741 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6742 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6743 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6744 6745 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6746 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 6747 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6748 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6749 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6750 6751 offer = GetParsedLocalDescription(*mSessionOff); 6752 ASSERT_EQ(2U, offer->GetMediaSectionCount()); 6753 6754 answer = GetParsedLocalDescription(*mSessionAns); 6755 ASSERT_EQ(2U, answer->GetMediaSectionCount()); 6756 6757 ValidateDisabledMSection(&offer->GetMediaSection(0)); 6758 ValidateDisabledMSection(&offer->GetMediaSection(0)); 6759 ValidateDisabledMSection(&answer->GetMediaSection(0)); 6760 6761 // Renegotiating should recycle m-section 0. 6762 AddTracks(*mSessionOff, "audio"); 6763 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6764 OfferAnswer(CHECK_SUCCESS); 6765 6766 // Transceiver 3 should now be attached to m-section 0 6767 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6768 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6769 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6770 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6771 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6772 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6773 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6774 6775 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 6776 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel()); 6777 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6778 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6779 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6780 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel()); 6781 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6782 } 6783 6784 // TODO: Have a test where offerer stops, and answerer adds a track and reoffers 6785 // once Nils' role swap code lands. 6786 6787 // TODO: Have a test where answerer stops and adds a track. 6788 6789 TEST_F(JsepSessionTest, OffererRecycleNoMagic) { 6790 types = BuildTypes("audio,video"); 6791 AddTracks(*mSessionOff); 6792 AddTracks(*mSessionAns); 6793 6794 OfferAnswer(); 6795 6796 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6797 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6798 GetTransceivers(*mSessionOff)[0].Stop(); 6799 6800 OfferAnswer(CHECK_SUCCESS); 6801 6802 // Ok. Now renegotiating should recycle m-section 0. 6803 AddTracks(*mSessionOff, "audio", NO_ADDTRACK_MAGIC); 6804 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6805 OfferAnswer(CHECK_SUCCESS); 6806 6807 // Transceiver 2 should now be attached to m-section 0 6808 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6809 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6810 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6811 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6812 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6813 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6814 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6815 6816 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 6817 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel()); 6818 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6819 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6820 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6821 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel()); 6822 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6823 } 6824 6825 TEST_F(JsepSessionTest, OffererRecycleNoMagicAnswererStopsTransceiver) { 6826 types = BuildTypes("audio,video"); 6827 AddTracks(*mSessionOff); 6828 AddTracks(*mSessionAns); 6829 6830 OfferAnswer(); 6831 6832 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6833 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6834 GetTransceivers(*mSessionAns)[0].Stop(); 6835 6836 SwapOfferAnswerRoles(); 6837 OfferAnswer(CHECK_SUCCESS); 6838 6839 // Ok. Now renegotiating should recycle m-section 0. 6840 AddTracks(*mSessionOff, "audio", NO_ADDTRACK_MAGIC); 6841 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6842 OfferAnswer(CHECK_SUCCESS); 6843 6844 // Transceiver 2 should now be attached to m-section 0 6845 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6846 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6847 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6848 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6849 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6850 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6851 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6852 6853 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 6854 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel()); 6855 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6856 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6857 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6858 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel()); 6859 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6860 } 6861 6862 TEST_F(JsepSessionTest, RecycleRollback) { 6863 types = BuildTypes("audio,video"); 6864 AddTracks(*mSessionOff); 6865 AddTracks(*mSessionAns); 6866 6867 OfferAnswer(); 6868 6869 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6870 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6871 GetTransceivers(*mSessionOff)[0].Stop(); 6872 6873 OfferAnswer(CHECK_SUCCESS); 6874 6875 AddTracks(*mSessionOff, "audio"); 6876 6877 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6878 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel()); 6879 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6880 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 6881 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6882 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6883 ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 6884 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].HasLevel()); 6885 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6886 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsAssociated()); 6887 6888 std::string offer = CreateOffer(); 6889 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6890 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6891 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6892 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 6893 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6894 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6895 ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 6896 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6897 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6898 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsAssociated()); 6899 6900 SetLocalOffer(offer, CHECK_SUCCESS); 6901 6902 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6903 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6904 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6905 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 6906 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6907 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6908 ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 6909 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6910 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6911 // This should now be associated 6912 ASSERT_TRUE(GetTransceivers(*mSessionOff)[2].IsAssociated()); 6913 6914 ASSERT_FALSE( 6915 mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError.isSome()); 6916 6917 // Rollback should not change the levels of any of these, since those are set 6918 // in CreateOffer. 6919 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6920 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].HasLevel()); 6921 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6922 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 6923 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6924 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6925 ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 6926 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6927 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6928 // This should no longer be associated 6929 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsAssociated()); 6930 } 6931 6932 TEST_F(JsepSessionTest, AddTrackMagicWithNullReplaceTrack) { 6933 types = BuildTypes("audio,video"); 6934 AddTracks(*mSessionOff); 6935 AddTracks(*mSessionAns); 6936 6937 OfferAnswer(); 6938 6939 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 6940 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 6941 6942 AddTracks(*mSessionAns, "audio"); 6943 AddTracks(*mSessionOff, "audio"); 6944 6945 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 6946 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 6947 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6948 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 6949 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6950 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6951 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 6952 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 6953 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6954 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 6955 ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 6956 6957 // Ok, transceiver 2 is "magical". Ensure it still has this "magical" 6958 // auto-matching property even if we null it out with replaceTrack. 6959 GetTransceivers(*mSessionAns)[2].mSendTrack.ClearStreamIds(); 6960 GetTransceivers(*mSessionAns)[2].mJsDirection = 6961 SdpDirectionAttribute::Direction::kRecvonly; 6962 6963 OfferAnswer(CHECK_SUCCESS); 6964 6965 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 6966 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 6967 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 6968 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 6969 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 6970 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 6971 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 6972 ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[2].GetLevel()); 6973 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 6974 ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 6975 ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 6976 6977 ASSERT_EQ(3U, GetTransceivers(*mSessionOff).size()); 6978 ASSERT_EQ(0U, GetTransceivers(*mSessionOff)[0].GetLevel()); 6979 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsStopped()); 6980 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 6981 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[1].GetLevel()); 6982 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsStopped()); 6983 ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 6984 ASSERT_EQ(2U, GetTransceivers(*mSessionOff)[2].GetLevel()); 6985 ASSERT_FALSE(GetTransceivers(*mSessionOff)[2].IsStopped()); 6986 ASSERT_TRUE(GetTransceivers(*mSessionOff)[2].IsAssociated()); 6987 ASSERT_TRUE(GetTransceivers(*mSessionOff)[2].HasAddTrackMagic()); 6988 } 6989 6990 // Flipside of AddTrackMagicWithNullReplaceTrack; we want to check that 6991 // auto-matching does not work for transceivers that were created without a 6992 // track, but were later given a track with replaceTrack. 6993 TEST_F(JsepSessionTest, NoAddTrackMagicReplaceTrack) { 6994 types = BuildTypes("audio,video"); 6995 AddTracks(*mSessionOff); 6996 AddTracks(*mSessionAns); 6997 6998 OfferAnswer(); 6999 7000 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 7001 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 7002 AddTracks(*mSessionOff, "audio"); 7003 mSessionAns->AddTransceiver( 7004 JsepTransceiver(SdpMediaSection::MediaType::kAudio, mUuidGen)); 7005 7006 GetTransceivers(*mSessionAns)[2].mSendTrack.UpdateStreamIds({"newstream"}); 7007 7008 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 7009 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 7010 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7011 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7012 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 7013 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7014 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7015 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 7016 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7017 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7018 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7019 7020 OfferAnswer(CHECK_SUCCESS); 7021 7022 ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size()); 7023 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 7024 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7025 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7026 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 7027 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7028 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7029 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 7030 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7031 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7032 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7033 ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[3].GetLevel()); 7034 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped()); 7035 ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsAssociated()); 7036 } 7037 7038 TEST_F(JsepSessionTest, AddTrackDoesNotMakeTransceiverMagical) { 7039 types = BuildTypes("audio,video"); 7040 AddTracks(*mSessionOff); 7041 AddTracks(*mSessionAns); 7042 7043 OfferAnswer(); 7044 7045 ASSERT_EQ(2U, GetTransceivers(*mSessionOff).size()); 7046 ASSERT_EQ(2U, GetTransceivers(*mSessionAns).size()); 7047 AddTracks(*mSessionOff, "audio"); 7048 mSessionAns->AddTransceiver( 7049 JsepTransceiver(SdpMediaSection::MediaType::kAudio, mUuidGen)); 7050 7051 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 7052 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 7053 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7054 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7055 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 7056 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7057 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7058 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 7059 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7060 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7061 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7062 7063 // This attaches a track to the third transceiver, but does _not_ set the 7064 // addTrack magic bit, meaning it will not auto-pair with the track added 7065 // to the offerer. 7066 AddTracks(*mSessionAns, "audio"); 7067 7068 ASSERT_EQ(3U, GetTransceivers(*mSessionAns).size()); 7069 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 7070 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7071 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7072 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 7073 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7074 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7075 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 7076 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7077 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7078 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7079 7080 OfferAnswer(CHECK_SUCCESS); 7081 7082 // The offer's new transceiver does not pair up with the transceiver we added 7083 ASSERT_EQ(4U, GetTransceivers(*mSessionAns).size()); 7084 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[0].GetLevel()); 7085 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7086 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7087 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[1].GetLevel()); 7088 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7089 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7090 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 7091 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7092 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7093 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7094 ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[3].GetLevel()); 7095 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped()); 7096 ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsAssociated()); 7097 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasAddTrackMagic()); 7098 } 7099 7100 TEST_F(JsepSessionTest, ComplicatedRemoteRollback) { 7101 AddTracks(*mSessionOff, "audio,audio,audio,video"); 7102 AddTracks(*mSessionAns, "video,video"); 7103 7104 std::string offer = CreateOffer(); 7105 SetLocalOffer(offer, CHECK_SUCCESS); 7106 SetRemoteOffer(offer, CHECK_SUCCESS); 7107 7108 // Three recvonly for audio, one sendrecv for video, and one (unmapped) for 7109 // the second video track. 7110 ASSERT_EQ(5U, GetTransceivers(*mSessionAns).size()); 7111 // First video transceiver; auto matched with offer 7112 ASSERT_EQ(3U, GetTransceivers(*mSessionAns)[0].GetLevel()); 7113 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7114 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7115 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].HasAddTrackMagic()); 7116 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].OnlyExistsBecauseOfSetRemote()); 7117 7118 // Second video transceiver, not matched with offer 7119 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].HasLevel()); 7120 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7121 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7122 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].HasAddTrackMagic()); 7123 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].OnlyExistsBecauseOfSetRemote()); 7124 7125 // Audio transceiver, created due to application of SetRemote 7126 ASSERT_EQ(0U, GetTransceivers(*mSessionAns)[2].GetLevel()); 7127 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7128 ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7129 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7130 ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].OnlyExistsBecauseOfSetRemote()); 7131 7132 // Audio transceiver, created due to application of SetRemote 7133 ASSERT_EQ(1U, GetTransceivers(*mSessionAns)[3].GetLevel()); 7134 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsStopped()); 7135 ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsAssociated()); 7136 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasAddTrackMagic()); 7137 ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].OnlyExistsBecauseOfSetRemote()); 7138 7139 // Audio transceiver, created due to application of SetRemote 7140 ASSERT_EQ(2U, GetTransceivers(*mSessionAns)[4].GetLevel()); 7141 ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].IsStopped()); 7142 ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].IsAssociated()); 7143 ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].HasAddTrackMagic()); 7144 ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].OnlyExistsBecauseOfSetRemote()); 7145 7146 // This will prevent rollback from eating this transceiver, even though we 7147 // call replaceTrack(null) on it. 7148 AddTracks(*mSessionAns, "audio"); 7149 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7150 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].OnlyExistsBecauseOfSetRemote()); 7151 GetTransceivers(*mSessionAns)[2].mSendTrack.ClearStreamIds(); 7152 GetTransceivers(*mSessionAns)[2].mJsDirection = 7153 SdpDirectionAttribute::Direction::kRecvonly; 7154 7155 // We do nothing with the second audio transceiver; when we rollback, it will 7156 // be marked as removed. 7157 7158 // This will not cause the third audio transceiver to stick around; having a 7159 // track is _not_ enough to preserve it. It must have addTrack "magic"! 7160 GetTransceivers(*mSessionAns)[4].mSendTrack.UpdateStreamIds({"newstream"}); 7161 7162 // Create a fourth audio transceiver. Rollback will leave it alone, since we 7163 // created it. 7164 mSessionAns->AddTransceiver( 7165 JsepTransceiver(SdpMediaSection::MediaType::kAudio, mUuidGen, 7166 SdpDirectionAttribute::Direction::kRecvonly)); 7167 7168 ASSERT_FALSE( 7169 mSessionAns->SetRemoteDescription(kJsepSdpRollback, "").mError.isSome()); 7170 7171 // Two of these (3 and 4) will be marked removed, if this all worked 7172 ASSERT_EQ(6U, GetTransceivers(*mSessionAns).size()); 7173 7174 // First video transceiver 7175 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].HasLevel()); 7176 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsStopped()); 7177 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsAssociated()); 7178 ASSERT_TRUE(GetTransceivers(*mSessionAns)[0].HasAddTrackMagic()); 7179 ASSERT_FALSE(IsNull(GetTransceivers(*mSessionAns)[0].mSendTrack)); 7180 ASSERT_FALSE(GetTransceivers(*mSessionAns)[0].IsRemoved()); 7181 7182 // Second video transceiver 7183 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].HasLevel()); 7184 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsStopped()); 7185 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsAssociated()); 7186 ASSERT_TRUE(GetTransceivers(*mSessionAns)[1].HasAddTrackMagic()); 7187 ASSERT_FALSE(IsNull(GetTransceivers(*mSessionAns)[1].mSendTrack)); 7188 ASSERT_FALSE(GetTransceivers(*mSessionAns)[1].IsRemoved()); 7189 7190 // First audio transceiver, kept because AddTrack touched it, even though we 7191 // removed the send track after. Gets magic because that's what other browsers 7192 // do (ugh). 7193 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].HasLevel()); 7194 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsStopped()); 7195 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsAssociated()); 7196 ASSERT_TRUE(GetTransceivers(*mSessionAns)[2].HasAddTrackMagic()); 7197 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].OnlyExistsBecauseOfSetRemote()); 7198 ASSERT_TRUE(IsNull(GetTransceivers(*mSessionAns)[2].mSendTrack)); 7199 ASSERT_FALSE(GetTransceivers(*mSessionAns)[2].IsRemoved()); 7200 7201 // Second audio transceiver should be gone. 7202 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasLevel()); 7203 ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsStopped()); 7204 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].IsAssociated()); 7205 ASSERT_FALSE(GetTransceivers(*mSessionAns)[3].HasAddTrackMagic()); 7206 ASSERT_TRUE(IsNull(GetTransceivers(*mSessionAns)[3].mSendTrack)); 7207 ASSERT_TRUE(GetTransceivers(*mSessionAns)[3].IsRemoved()); 7208 7209 // Third audio transceiver should also be gone. 7210 ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].HasLevel()); 7211 ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].IsStopped()); 7212 ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].IsAssociated()); 7213 ASSERT_FALSE(GetTransceivers(*mSessionAns)[4].HasAddTrackMagic()); 7214 ASSERT_TRUE(IsNull(GetTransceivers(*mSessionAns)[4].mSendTrack)); 7215 ASSERT_TRUE(GetTransceivers(*mSessionAns)[4].IsRemoved()); 7216 7217 // Fourth audio transceiver, created after SetRemote 7218 ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].HasLevel()); 7219 ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].IsStopped()); 7220 ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].IsAssociated()); 7221 ASSERT_FALSE(GetTransceivers(*mSessionAns)[5].HasAddTrackMagic()); 7222 ASSERT_TRUE( 7223 GetTransceivers(*mSessionAns)[5].mSendTrack.GetStreamIds().empty()); 7224 } 7225 7226 TEST_F(JsepSessionTest, LocalRollback) { 7227 AddTracks(*mSessionOff, "audio,video"); 7228 AddTracks(*mSessionAns, "audio,video"); 7229 7230 std::string offer = CreateOffer(); 7231 SetLocalOffer(offer, CHECK_SUCCESS); 7232 7233 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 7234 ASSERT_TRUE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 7235 ASSERT_FALSE( 7236 mSessionOff->SetLocalDescription(kJsepSdpRollback, "").mError.isSome()); 7237 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsAssociated()); 7238 ASSERT_FALSE(GetTransceivers(*mSessionOff)[1].IsAssociated()); 7239 } 7240 7241 TEST_F(JsepSessionTest, JsStopsTransceiverBeforeAnswer) { 7242 AddTracks(*mSessionOff, "audio,video"); 7243 AddTracks(*mSessionAns, "audio,video"); 7244 7245 std::string offer = CreateOffer(); 7246 SetLocalOffer(offer, CHECK_SUCCESS); 7247 SetRemoteOffer(offer, CHECK_SUCCESS); 7248 7249 std::string answer = CreateAnswer(); 7250 SetLocalAnswer(answer, CHECK_SUCCESS); 7251 7252 // Now JS decides to stop a transceiver. This should have absolutely no effect 7253 // on JSEP this negotiation. Any effects will be on the JS side of things. 7254 // JSEP _will_ advertise that negotiation is needed. 7255 7256 GetTransceivers(*mSessionOff)[0].Stop(); 7257 SetRemoteAnswer(answer, CHECK_SUCCESS); 7258 7259 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].IsStopping()); 7260 ASSERT_FALSE(GetTransceivers(*mSessionOff)[0].IsStopped()); 7261 ASSERT_EQ(1U, GetTransceivers(*mSessionOff)[0].mTransport.mComponents); 7262 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].mSendTrack.GetActive()); 7263 ASSERT_TRUE(GetTransceivers(*mSessionOff)[0].mRecvTrack.GetActive()); 7264 ASSERT_TRUE(mSessionOff->CheckNegotiationNeeded()); 7265 } 7266 7267 TEST_F(JsepSessionTest, TestOfferPTAsymmetryRtxApt) { 7268 for (auto& codec : mSessionAns->Codecs()) { 7269 if (codec->mName == "VP8") { 7270 JsepVideoCodecDescription* vp8 = 7271 static_cast<JsepVideoCodecDescription*>(codec.get()); 7272 vp8->EnableRtx("42"); 7273 break; 7274 } 7275 } 7276 7277 types.push_back(SdpMediaSection::kVideo); 7278 AddTracks(*mSessionOff, "video"); 7279 AddTracks(*mSessionAns, "video"); 7280 JsepOfferOptions options; 7281 7282 // Ensure that mSessionAns is appropriately configured. 7283 std::string offer; 7284 JsepSession::Result result = mSessionAns->CreateOffer(options, &offer); 7285 ASSERT_FALSE(result.mError.isSome()); 7286 ASSERT_NE(std::string::npos, offer.find("a=rtpmap:42 rtx")) << offer; 7287 7288 OfferAnswer(); 7289 7290 // Answerer should use what the offerer suggested 7291 UniquePtr<JsepCodecDescription> codec; 7292 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 7293 ASSERT_TRUE(codec); 7294 ASSERT_EQ("VP8", codec->mName); 7295 JsepVideoCodecDescription* vp8 = 7296 static_cast<JsepVideoCodecDescription*>(codec.get()); 7297 ASSERT_EQ("120", vp8->mDefaultPt); 7298 ASSERT_EQ("124", vp8->mRtxPayloadType); 7299 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 7300 ASSERT_TRUE(codec); 7301 ASSERT_EQ("VP8", codec->mName); 7302 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7303 ASSERT_EQ("120", vp8->mDefaultPt); 7304 ASSERT_EQ("124", vp8->mRtxPayloadType); 7305 7306 // Answerer should not change back when it reoffers 7307 result = mSessionAns->CreateOffer(options, &offer); 7308 ASSERT_FALSE(result.mError.isSome()); 7309 ASSERT_NE(std::string::npos, offer.find("a=rtpmap:124 rtx")) << offer; 7310 } 7311 7312 TEST_F(JsepSessionTest, TestAnswerPTAsymmetryRtx) { 7313 // JsepSessionImpl will never answer with an asymmetric payload type 7314 // (tested in TestOfferPTAsymmetry), so we have to rewrite SDP a little. 7315 types.push_back(SdpMediaSection::kVideo); 7316 AddTracks(*mSessionOff, "video"); 7317 AddTracks(*mSessionAns, "video"); 7318 7319 std::string offer = CreateOffer(); 7320 SetLocalOffer(offer); 7321 7322 Replace("a=rtpmap:120 VP8", "a=rtpmap:119 VP8", &offer); 7323 Replace("m=video 9 UDP/TLS/RTP/SAVPF 120", "m=video 9 UDP/TLS/RTP/SAVPF 119", 7324 &offer); 7325 ReplaceAll("a=fmtp:120", "a=fmtp:119", &offer); 7326 ReplaceAll("a=fmtp:122 120", "a=fmtp:122 119", &offer); 7327 ReplaceAll("a=fmtp:124 apt=120", "a=fmtp:124 apt=119", &offer); 7328 ReplaceAll("a=rtcp-fb:120", "a=rtcp-fb:119", &offer); 7329 7330 SetRemoteOffer(offer); 7331 7332 std::string answer = CreateAnswer(); 7333 SetLocalAnswer(answer); 7334 SetRemoteAnswer(answer); 7335 7336 UniquePtr<JsepCodecDescription> codec; 7337 GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &codec); 7338 ASSERT_TRUE(codec); 7339 ASSERT_EQ("VP8", codec->mName); 7340 ASSERT_EQ("119", codec->mDefaultPt); 7341 JsepVideoCodecDescription* vp8 = 7342 static_cast<JsepVideoCodecDescription*>(codec.get()); 7343 ASSERT_EQ("124", vp8->mRtxPayloadType); 7344 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &codec); 7345 ASSERT_TRUE(codec); 7346 ASSERT_EQ("VP8", codec->mName); 7347 ASSERT_EQ("120", codec->mDefaultPt); 7348 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7349 ASSERT_EQ("124", vp8->mRtxPayloadType); 7350 7351 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 7352 ASSERT_TRUE(codec); 7353 ASSERT_EQ("VP8", codec->mName); 7354 ASSERT_EQ("119", codec->mDefaultPt); 7355 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7356 ASSERT_EQ("124", vp8->mRtxPayloadType); 7357 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 7358 ASSERT_TRUE(codec); 7359 ASSERT_EQ("VP8", codec->mName); 7360 ASSERT_EQ("119", codec->mDefaultPt); 7361 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7362 ASSERT_EQ("124", vp8->mRtxPayloadType); 7363 } 7364 7365 TEST_F(JsepSessionTest, TestAnswerPTAsymmetryRtxApt) { 7366 // JsepSessionImpl will never answer with an asymmetric payload type 7367 // so we have to rewrite SDP a little. 7368 types.push_back(SdpMediaSection::kVideo); 7369 AddTracks(*mSessionOff, "video"); 7370 AddTracks(*mSessionAns, "video"); 7371 7372 std::string offer = CreateOffer(); 7373 SetLocalOffer(offer); 7374 7375 Replace("a=rtpmap:124 rtx", "a=rtpmap:42 rtx", &offer); 7376 Replace("m=video 9 UDP/TLS/RTP/SAVPF 120 124", 7377 "m=video 9 UDP/TLS/RTP/SAVPF 120 42", &offer); 7378 ReplaceAll("a=fmtp:124", "a=fmtp:42", &offer); 7379 7380 SetRemoteOffer(offer); 7381 7382 std::string answer = CreateAnswer(); 7383 SetLocalAnswer(answer); 7384 SetRemoteAnswer(answer); 7385 7386 UniquePtr<JsepCodecDescription> codec; 7387 GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &codec); 7388 ASSERT_TRUE(codec); 7389 ASSERT_EQ("VP8", codec->mName); 7390 ASSERT_EQ("120", codec->mDefaultPt); 7391 JsepVideoCodecDescription* vp8 = 7392 static_cast<JsepVideoCodecDescription*>(codec.get()); 7393 ASSERT_EQ("42", vp8->mRtxPayloadType); 7394 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &codec); 7395 ASSERT_TRUE(codec); 7396 ASSERT_EQ("VP8", codec->mName); 7397 ASSERT_EQ("120", codec->mDefaultPt); 7398 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7399 ASSERT_EQ("124", vp8->mRtxPayloadType); 7400 7401 GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec); 7402 ASSERT_TRUE(codec); 7403 ASSERT_EQ("VP8", codec->mName); 7404 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7405 ASSERT_EQ("120", vp8->mDefaultPt); 7406 ASSERT_EQ("42", vp8->mRtxPayloadType); 7407 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec); 7408 ASSERT_TRUE(codec); 7409 ASSERT_EQ("VP8", codec->mName); 7410 vp8 = static_cast<JsepVideoCodecDescription*>(codec.get()); 7411 ASSERT_EQ("120", vp8->mDefaultPt); 7412 ASSERT_EQ("42", vp8->mRtxPayloadType); 7413 } 7414 7415 TEST_F(JsepSessionTest, TestOfferNoRtx) { 7416 for (auto& codec : mSessionOff->Codecs()) { 7417 if (codec->Type() == SdpMediaSection::kVideo) { 7418 JsepVideoCodecDescription* videoCodec = 7419 static_cast<JsepVideoCodecDescription*>(codec.get()); 7420 videoCodec->mRtxEnabled = false; 7421 } 7422 } 7423 7424 types.push_back(SdpMediaSection::kVideo); 7425 AddTracks(*mSessionOff, "video"); 7426 AddTracks(*mSessionAns, "video"); 7427 JsepOfferOptions options; 7428 7429 std::string offer; 7430 JsepSession::Result result = mSessionOff->CreateOffer(options, &offer); 7431 ASSERT_FALSE(result.mError.isSome()); 7432 ASSERT_EQ(std::string::npos, offer.find("rtx")) << offer; 7433 7434 OfferAnswer(); 7435 7436 // Answerer should use what the offerer suggested 7437 UniquePtr<JsepCodecDescription> codec; 7438 for (size_t i = 0; i < 4; ++i) { 7439 GetCodec(*mSessionAns, 0, sdp::kSend, 0, i, &codec); 7440 ASSERT_TRUE(codec); 7441 JsepVideoCodecDescription* videoCodec = 7442 static_cast<JsepVideoCodecDescription*>(codec.get()); 7443 ASSERT_FALSE(videoCodec->mRtxEnabled); 7444 GetCodec(*mSessionAns, 0, sdp::kRecv, 0, i, &codec); 7445 ASSERT_TRUE(codec); 7446 videoCodec = static_cast<JsepVideoCodecDescription*>(codec.get()); 7447 ASSERT_FALSE(videoCodec->mRtxEnabled); 7448 } 7449 } 7450 7451 TEST_F(JsepSessionTest, TestOneWayRtx) { 7452 for (auto& codec : mSessionAns->Codecs()) { 7453 if (codec->Type() == SdpMediaSection::kVideo) { 7454 JsepVideoCodecDescription* videoCodec = 7455 static_cast<JsepVideoCodecDescription*>(codec.get()); 7456 videoCodec->mRtxEnabled = false; 7457 } 7458 } 7459 7460 types.push_back(SdpMediaSection::kVideo); 7461 AddTracks(*mSessionOff, "video"); 7462 AddTracks(*mSessionAns, "video"); 7463 JsepOfferOptions options; 7464 7465 std::string offer; 7466 JsepSession::Result result = mSessionAns->CreateOffer(options, &offer); 7467 ASSERT_FALSE(result.mError.isSome()); 7468 ASSERT_EQ(std::string::npos, offer.find("rtx")) << offer; 7469 7470 OfferAnswer(); 7471 7472 // If the answerer does not support rtx, the offerer should not send it, 7473 // but it is too late to turn off recv on the offerer side. 7474 UniquePtr<JsepCodecDescription> codec; 7475 for (size_t i = 0; i < 4; ++i) { 7476 GetCodec(*mSessionOff, 0, sdp::kSend, 0, i, &codec); 7477 ASSERT_TRUE(codec); 7478 JsepVideoCodecDescription* videoCodec = 7479 static_cast<JsepVideoCodecDescription*>(codec.get()); 7480 ASSERT_FALSE(videoCodec->mRtxEnabled); 7481 GetCodec(*mSessionOff, 0, sdp::kRecv, 0, i, &codec); 7482 ASSERT_TRUE(codec); 7483 videoCodec = static_cast<JsepVideoCodecDescription*>(codec.get()); 7484 ASSERT_TRUE(videoCodec->mRtxEnabled); 7485 } 7486 } 7487 7488 TEST_F(JsepSessionTest, TestRtxNoSsrcGroup) { 7489 mSessionOff->AddTransceiver(JsepTransceiver( 7490 SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kRecvonly)); 7491 7492 OfferAnswer(CHECK_SUCCESS); 7493 7494 std::string offer = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent); 7495 ASSERT_EQ(std::string::npos, offer.find("FID")) << offer; 7496 7497 std::string answer = 7498 mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent); 7499 ASSERT_EQ(std::string::npos, answer.find("FID")) << answer; 7500 } 7501 7502 TEST_F(JsepSessionTest, TestRtxSsrcGroupOnlyOffered) { 7503 mSessionOff->AddTransceiver(JsepTransceiver( 7504 SdpMediaSection::kVideo, mUuidGen, SdpDirectionAttribute::kSendonly)); 7505 7506 OfferAnswer(CHECK_SUCCESS); 7507 7508 std::string offer = mSessionOff->GetLocalDescription(kJsepDescriptionCurrent); 7509 ASSERT_NE(std::string::npos, offer.find("FID")) << offer; 7510 7511 std::string answer = 7512 mSessionOff->GetRemoteDescription(kJsepDescriptionCurrent); 7513 ASSERT_EQ(std::string::npos, answer.find("FID")) << answer; 7514 } 7515 7516 TEST_F(JsepSessionTest, TestOfferRtxNoMsid) { 7517 for (auto& codec : mSessionOff->Codecs()) { 7518 if (codec->mName == "VP8") { 7519 JsepVideoCodecDescription* vp8 = 7520 static_cast<JsepVideoCodecDescription*>(codec.get()); 7521 vp8->EnableRtx("42"); 7522 break; 7523 } 7524 } 7525 7526 types.push_back(SdpMediaSection::kVideo); 7527 AddTracks(*mSessionOff, "video"); 7528 7529 mSessionOff->ForEachTransceiver([this](JsepTransceiver& aTransceiver) { 7530 if (!IsNull(aTransceiver.mSendTrack)) { 7531 std::vector<std::string> empty; 7532 aTransceiver.mSendTrack.UpdateStreamIds(empty); 7533 } 7534 }); 7535 7536 // MSID stream absence should not influence FID ssrc-group 7537 JsepOfferOptions options; 7538 std::string offer; 7539 JsepSession::Result result = mSessionOff->CreateOffer(options, &offer); 7540 ASSERT_FALSE(result.mError.isSome()); 7541 ASSERT_NE(std::string::npos, offer.find("FID")) << offer; 7542 } 7543 7544 TEST_F(JsepSessionTest, TestRedRtxAddedToVideoCodec) { 7545 types.push_back(SdpMediaSection::kVideo); 7546 AddTracks(*mSessionOff, "video"); 7547 AddTracks(*mSessionAns, "video"); 7548 7549 OfferAnswer(); 7550 7551 std::vector<sdp::Direction> directions = {sdp::kSend, sdp::kRecv}; 7552 7553 for (auto direction : directions) { 7554 UniquePtr<JsepCodecDescription> codec; 7555 std::set<std::string> payloadTypes; 7556 std::string redPt, ulpfecPt, redRtxPt; 7557 for (size_t i = 0; i < 4; ++i) { 7558 GetCodec(*mSessionOff, 0, direction, 0, i, &codec); 7559 ASSERT_TRUE(codec); 7560 JsepVideoCodecDescription* videoCodec = 7561 static_cast<JsepVideoCodecDescription*>(codec.get()); 7562 7563 // Ensure RED, ULPFEC, and RTX RED are not empty which validates that 7564 // EnableFEC worked. 7565 ASSERT_FALSE(videoCodec->mREDPayloadType.empty()); 7566 ASSERT_FALSE(videoCodec->mULPFECPayloadType.empty()); 7567 ASSERT_FALSE(videoCodec->mREDRTXPayloadType.empty()); 7568 ASSERT_TRUE(payloadTypes.insert(videoCodec->mDefaultPt).second); 7569 ASSERT_TRUE(payloadTypes.insert(videoCodec->mRtxPayloadType).second); 7570 // ULPFEC, RED, and RED RTX payload types are the same for each codec, so 7571 // we only check them for the first one. 7572 if (i == 0) { 7573 ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDPayloadType).second) 7574 << "RED is using a duplicate payload type."; 7575 ASSERT_TRUE(payloadTypes.insert(videoCodec->mULPFECPayloadType).second) 7576 << "ULPFEC is using a duplicate payload type."; 7577 ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDRTXPayloadType).second) 7578 << "RED RTX is using a duplicate payload type."; 7579 redPt = videoCodec->mREDPayloadType; 7580 ulpfecPt = videoCodec->mULPFECPayloadType; 7581 redRtxPt = videoCodec->mREDRTXPayloadType; 7582 } else { 7583 ASSERT_TRUE(redPt == videoCodec->mREDPayloadType); 7584 ASSERT_TRUE(ulpfecPt == videoCodec->mULPFECPayloadType); 7585 ASSERT_TRUE(redRtxPt == videoCodec->mREDRTXPayloadType); 7586 } 7587 } 7588 } 7589 } 7590 7591 TEST_P(JsepSessionTest, TestNegotiatedDetailsToVideoCodecConfigs) { 7592 AddTracks(*mSessionOff); 7593 AddTracks(*mSessionAns); 7594 OfferAnswer(); 7595 7596 // Check all the video tracks to ensure negotiated details is added to 7597 // VideoCodecConfig. Especially information related to FEC, RED, and RED RTX. 7598 std::vector<JsepTrack> tracks; 7599 for (const auto& transceiver : GetTransceivers(*mSessionOff)) { 7600 tracks.push_back(transceiver.mSendTrack); 7601 tracks.push_back(transceiver.mRecvTrack); 7602 } 7603 7604 for (const JsepTrack& track : tracks) { 7605 if (track.GetMediaType() != SdpMediaSection::kVideo) { 7606 continue; 7607 } 7608 7609 const auto& details(*track.GetNegotiatedDetails()); 7610 std::vector<VideoCodecConfig> videoConfigs; 7611 dom::RTCRtpTransceiver::NegotiatedDetailsToVideoCodecConfigs(details, 7612 &videoConfigs); 7613 ASSERT_FALSE(videoConfigs.empty()); 7614 ASSERT_EQ(1U, details.GetEncodingCount()); 7615 7616 const JsepTrackEncoding& encoding = details.GetEncoding(0); 7617 7618 ASSERT_EQ(encoding.GetCodecs().size(), videoConfigs.size()); 7619 // Since encodings and videoConfigs is the same size and order we can loop 7620 // through them both at the same time and validate that videoConfigs 7621 // contains the expected encoding data from negotiated details. 7622 for (unsigned int i = 0; i < videoConfigs.size(); i++) { 7623 const JsepVideoCodecDescription& codec = 7624 static_cast<const JsepVideoCodecDescription&>( 7625 *encoding.GetCodecs().at(i)); 7626 const auto& config = videoConfigs.at(i); 7627 7628 uint16_t payloadType; 7629 ASSERT_TRUE(codec.GetPtAsInt(&payloadType)); 7630 ASSERT_EQ(payloadType, config.mType); 7631 ASSERT_EQ(codec.mName, config.mName); 7632 ASSERT_EQ(codec.RtcpFbRembIsSet(), config.mRembFbSet); 7633 ASSERT_EQ(codec.mFECEnabled, config.mFECFbSet); 7634 ASSERT_EQ(codec.RtcpFbTransportCCIsSet(), config.mTransportCCFbSet); 7635 ASSERT_EQ(details.GetTias(), config.mTias); 7636 ASSERT_EQ(codec.mConstraints, config.mEncodingConstraints); 7637 7638 if (codec.mName == "H264") { 7639 ASSERT_EQ((codec.mProfileLevelId & 0x00FF0000) >> 16, config.mProfile); 7640 ASSERT_EQ((codec.mProfileLevelId & 0x0000FF00) >> 8, 7641 config.mConstraints); 7642 ASSERT_EQ(codec.mProfileLevelId & 0x000000FF, config.mLevel); 7643 ASSERT_EQ(codec.mPacketizationMode, config.mPacketizationMode); 7644 ASSERT_EQ(codec.mSpropParameterSets, config.mSpropParameterSets); 7645 } 7646 7647 if (codec.mFECEnabled) { 7648 uint16_t redPayloadType, ulpFecPayloadType, redRtxPayloadType; 7649 7650 ASSERT_TRUE( 7651 SdpHelper::GetPtAsInt(codec.mREDPayloadType, &redPayloadType)); 7652 ASSERT_TRUE(SdpHelper::GetPtAsInt(codec.mULPFECPayloadType, 7653 &ulpFecPayloadType)); 7654 ASSERT_TRUE(SdpHelper::GetPtAsInt(codec.mREDRTXPayloadType, 7655 &redRtxPayloadType)); 7656 7657 ASSERT_EQ(redPayloadType, config.mREDPayloadType); 7658 ASSERT_EQ(ulpFecPayloadType, config.mULPFECPayloadType); 7659 ASSERT_EQ(redRtxPayloadType, config.mREDRTXPayloadType); 7660 } 7661 7662 if (codec.mRtxEnabled) { 7663 uint16_t rtxPayloadType; 7664 7665 ASSERT_TRUE( 7666 SdpHelper::GetPtAsInt(codec.mRtxPayloadType, &rtxPayloadType)); 7667 ASSERT_EQ(rtxPayloadType, config.mRTXPayloadType); 7668 } 7669 } 7670 } 7671 } 7672 7673 TEST_F(JsepSessionTest, TestDuplicatePayloadTypes) { 7674 for (auto& codec : mSessionOff->Codecs()) { 7675 if (codec->Type() == SdpMediaSection::kVideo) { 7676 JsepVideoCodecDescription* videoCodec = 7677 static_cast<JsepVideoCodecDescription*>(codec.get()); 7678 videoCodec->mRtxPayloadType = "97"; 7679 videoCodec->EnableFec("97", "97", "97"); 7680 } 7681 } 7682 7683 types.push_back(SdpMediaSection::kVideo); 7684 AddTracks(*mSessionOff, "video"); 7685 AddTracks(*mSessionAns, "video"); 7686 7687 OfferAnswer(); 7688 7689 std::vector<sdp::Direction> directions = {sdp::kSend, sdp::kRecv}; 7690 for (auto direction : directions) { 7691 UniquePtr<JsepCodecDescription> codec; 7692 std::set<std::string> payloadTypes; 7693 std::string redPt, ulpfecPt, redRtxPt; 7694 for (size_t i = 0; i < 4; ++i) { 7695 GetCodec(*mSessionOff, 0, direction, 0, i, &codec); 7696 ASSERT_TRUE(codec); 7697 JsepVideoCodecDescription* videoCodec = 7698 static_cast<JsepVideoCodecDescription*>(codec.get()); 7699 ASSERT_TRUE(payloadTypes.insert(videoCodec->mDefaultPt).second); 7700 ASSERT_TRUE(payloadTypes.insert(videoCodec->mRtxPayloadType).second); 7701 // ULPFEC and RED payload types are the same for each codec, so we only 7702 // check them for the first one. 7703 if (i == 0) { 7704 ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDPayloadType).second); 7705 ASSERT_TRUE(payloadTypes.insert(videoCodec->mULPFECPayloadType).second); 7706 ASSERT_TRUE(payloadTypes.insert(videoCodec->mREDRTXPayloadType).second); 7707 redPt = videoCodec->mREDPayloadType; 7708 ulpfecPt = videoCodec->mULPFECPayloadType; 7709 redRtxPt = videoCodec->mREDRTXPayloadType; 7710 } else { 7711 ASSERT_TRUE(redPt == videoCodec->mREDPayloadType); 7712 ASSERT_TRUE(ulpfecPt == videoCodec->mULPFECPayloadType); 7713 ASSERT_TRUE(redRtxPt == videoCodec->mREDRTXPayloadType); 7714 } 7715 } 7716 } 7717 } 7718 7719 TEST_F(JsepSessionTest, TestTransportAttributeValidation) { 7720 const std::string sdpTemplate = 7721 "v=0\r\n" 7722 "o=- 6 2 IN IP4 1r\r\n" 7723 "t=0 0a\r\n" 7724 "a=group:BUNDLE audio video\r\n" 7725 "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n" 7726 "c=IN IP4 51.81.107.13\r\n" 7727 "a=ice-ufrag:Xp\r\n" 7728 "a=ice-pwd:he\r\n" 7729 "a=setup:actpass\r\n" 7730 "a=fingerprint:sha-256 " 7731 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 7732 "08:D2:F7:9D:F5:E2:C1:15\r\n" 7733 "a=sendrecv\r\n" 7734 "a=extmap:11 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n" 7735 "a=fmtp:111 maxplaybackrate=48000;stereo=1;useinbandfec=1\r\n" 7736 "a=mid:audio\r\n" 7737 "a=rtcp-mux\r\n" 7738 "a=rtpmap:111 opus/48000/2\r\n" 7739 "a=ssrc:3463672643 cname:{ec9a356a-8d2c-504e-9977-99070a51f929}\r\n" 7740 "m=video 9 UDP/TLS/RTP/SAVPF 100\r\n" 7741 "c=IN IP4 51.81.107.13\r\n" 7742 "a=rtpmap:100 VP8/90000\r\n" 7743 "a=extmap:1 urn:ietf:params:rtp-hdrext:toffset\r\n" 7744 "a=mid:video\r\n" 7745 "a=ice-ufrag:Xp\r\n" 7746 "a=ice-pwd:he\r\n" 7747 "a=setup:actpass\r\n" 7748 "a=fingerprint:sha-256 " 7749 "DC:FC:25:56:2B:88:77:2F:E4:FA:97:4E:2E:F1:D6:34:A6:A0:11:E2:E4:38:B3:98:" 7750 "08:D2:F7:9D:F5:E2:C1:15\r\n"; 7751 7752 // Control case 7753 { 7754 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdpTemplate); 7755 ASSERT_FALSE(result.mError.isSome()); 7756 } 7757 7758 // Missing ufrag 7759 { 7760 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7761 parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute( 7762 SdpAttribute::kIceUfragAttribute); 7763 auto sdp = parsed->ToString(); 7764 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7765 ASSERT_TRUE(result.mError.isSome()); 7766 } 7767 7768 // Missing pwd, bundle tag 7769 { 7770 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7771 parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute( 7772 SdpAttribute::kIcePwdAttribute); 7773 auto sdp = parsed->ToString(); 7774 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7775 ASSERT_TRUE(result.mError.isSome()); 7776 } 7777 7778 // Missing setup, bundle tag 7779 { 7780 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7781 parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute( 7782 SdpAttribute::kSetupAttribute); 7783 auto sdp = parsed->ToString(); 7784 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7785 ASSERT_TRUE(result.mError.isSome()); 7786 } 7787 7788 // Invalid setup attribute (holdconn), bundle tag 7789 { 7790 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7791 parsed->GetMediaSection(0).GetAttributeList().SetAttribute( 7792 new SdpSetupAttribute(SdpSetupAttribute::kHoldconn)); 7793 auto sdp = parsed->ToString(); 7794 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7795 ASSERT_TRUE(result.mError.isSome()); 7796 } 7797 7798 // Missing fingerprint, bundle tag 7799 { 7800 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7801 parsed->GetMediaSection(0).GetAttributeList().RemoveAttribute( 7802 SdpAttribute::kFingerprintAttribute); 7803 auto sdp = parsed->ToString(); 7804 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7805 ASSERT_TRUE(result.mError.isSome()); 7806 } 7807 7808 // Unknown fingerprint algorithm 7809 { 7810 std::string mungedSdp = sdpTemplate; 7811 ReplaceAll("fingerprint:sha", "fingerprint:foo", &mungedSdp); 7812 UniquePtr<Sdp> parsed = Parse(mungedSdp); 7813 auto sdp = parsed->ToString(); 7814 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7815 ASSERT_TRUE(result.mError.isSome()); 7816 } 7817 7818 // Missing pwd, bundled msection without bundle-only 7819 { 7820 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7821 parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute( 7822 SdpAttribute::kIcePwdAttribute); 7823 auto sdp = parsed->ToString(); 7824 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7825 ASSERT_TRUE(result.mError.isSome()); 7826 } 7827 7828 // Missing setup, bundled msection without bundle-only 7829 { 7830 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7831 parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute( 7832 SdpAttribute::kSetupAttribute); 7833 auto sdp = parsed->ToString(); 7834 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7835 ASSERT_TRUE(result.mError.isSome()); 7836 } 7837 7838 // Missing fingerprint, bundled msection without bundle-only 7839 { 7840 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7841 parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute( 7842 SdpAttribute::kFingerprintAttribute); 7843 auto sdp = parsed->ToString(); 7844 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7845 ASSERT_TRUE(result.mError.isSome()); 7846 } 7847 7848 // Missing ufrag attribute, bundle-only msection 7849 { 7850 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7851 parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute( 7852 SdpAttribute::kIceUfragAttribute); 7853 parsed->GetMediaSection(1).GetAttributeList().SetAttribute( 7854 new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute)); 7855 auto sdp = parsed->ToString(); 7856 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7857 ASSERT_FALSE(result.mError.isSome()); 7858 } 7859 7860 // Missing pwd attribute, bundle-only msection 7861 { 7862 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7863 parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute( 7864 SdpAttribute::kIcePwdAttribute); 7865 parsed->GetMediaSection(1).GetAttributeList().SetAttribute( 7866 new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute)); 7867 auto sdp = parsed->ToString(); 7868 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7869 ASSERT_FALSE(result.mError.isSome()); 7870 } 7871 7872 // Missing fingerprint attribute, bundle-only msection 7873 { 7874 UniquePtr<Sdp> parsed = Parse(sdpTemplate); 7875 parsed->GetMediaSection(1).GetAttributeList().RemoveAttribute( 7876 SdpAttribute::kFingerprintAttribute); 7877 parsed->GetMediaSection(1).GetAttributeList().SetAttribute( 7878 new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute)); 7879 auto sdp = parsed->ToString(); 7880 auto result = mSessionOff->SetRemoteDescription(kJsepSdpOffer, sdp); 7881 ASSERT_FALSE(result.mError.isSome()); 7882 } 7883 } 7884 7885 TEST_F(JsepSessionTest, TestBundleSupportWithZeroPort) { 7886 AddTracks(*mSessionOff, "audio,video,video,datachannel"); 7887 AddTracks(*mSessionAns, "audio,video,video,datachannel"); 7888 7889 std::string offer; 7890 mSessionOff->CreateOffer(JsepOfferOptions(), &offer); 7891 7892 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer); 7893 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 7894 7895 std::string answer; 7896 mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer); 7897 7898 // Manipulate the bundling on the answer 7899 UniquePtr<Sdp> sdp(Parse(answer)); 7900 ASSERT_TRUE(!!sdp); 7901 size_t num_m_sections = sdp->GetMediaSectionCount(); 7902 for (size_t i = 0; i < num_m_sections; ++i) { 7903 auto& msection = sdp->GetMediaSection(i); 7904 const SdpAttributeList& attrs = msection.GetAttributeList(); 7905 // If this is not the last msection then ensure we have bundle only set and 7906 // port 0. 7907 if (!attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute) && 7908 i < num_m_sections - 1) { 7909 sdp->GetMediaSection(i).GetAttributeList().SetAttribute( 7910 new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute)); 7911 sdp->GetMediaSection(i).SetPort(0); 7912 } else { 7913 // For the last msection setting port to non 0 and removing bundle only if 7914 // it existed. 7915 if (attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) { 7916 sdp->GetMediaSection(i).GetAttributeList().RemoveAttribute( 7917 SdpAttribute::kBundleOnlyAttribute); 7918 } 7919 sdp->GetMediaSection(i).SetPort(9); 7920 } 7921 } 7922 auto answerSdp = sdp->ToString(); 7923 7924 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answerSdp); 7925 7926 // Ensure all the transcievers are still active bug 1923416 7927 for (const auto& offerTransceiver : GetTransceivers(*mSessionOff)) { 7928 ASSERT_TRUE(offerTransceiver.mRecvTrack.GetActive()); 7929 ASSERT_TRUE(offerTransceiver.mSendTrack.GetActive()); 7930 } 7931 } 7932 7933 TEST_F(JsepSessionTest, ExtmapAllowMixedTrueWhenPrensentAtSessionLevel) { 7934 AddTracks(*mSessionOff, "audio,video,datachannel"); 7935 AddTracks(*mSessionAns, "audio,video,datachannel"); 7936 std::string offer; 7937 mSessionOff->CreateOffer(JsepOfferOptions(), &offer); 7938 7939 // Remove extmap-allow-mixed from the media level 7940 ReplaceAll("a=extmap-allow-mixed\r\n", "", &offer); 7941 7942 // Add extmap-allow-mixed to the session level 7943 Replace("m=audio", "a=extmap-allow-mixed\r\nm=audio", &offer); 7944 7945 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer); 7946 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 7947 7948 std::string answer; 7949 mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer); 7950 7951 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer); 7952 mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 7953 7954 ASSERT_TRUE(ExtmapAllowMixed(*mSessionOff)); 7955 ASSERT_TRUE(ExtmapAllowMixed(*mSessionAns)); 7956 7957 mSessionAns->ForEachTransceiver([](JsepTransceiver& aTransceiver) { 7958 if (aTransceiver.mSendTrack.GetMediaType()) { 7959 ASSERT_TRUE(aTransceiver.mSendTrack.GetNegotiatedDetails() 7960 ->GetRtpRtcpConfig() 7961 .GetExtmapAllowMixed()); 7962 } 7963 }); 7964 } 7965 7966 TEST_F(JsepSessionTest, ExtmapAllowMixedCheckDoNotDefaultToSessionLevel) { 7967 AddTracks(*mSessionOff, "audio,video,datachannel"); 7968 AddTracks(*mSessionAns, "audio,video,datachannel"); 7969 7970 std::string offer; 7971 mSessionOff->CreateOffer(JsepOfferOptions(), &offer); 7972 7973 mSessionOff->SetLocalDescription(kJsepSdpOffer, offer); 7974 mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer); 7975 7976 std::string answer; 7977 mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer); 7978 7979 mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer); 7980 mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer); 7981 7982 ASSERT_FALSE(ExtmapAllowMixed(*mSessionOff)); 7983 ASSERT_FALSE(ExtmapAllowMixed(*mSessionAns)); 7984 } 7985 7986 } // namespace mozilla