Reland "Fix bug where we assume new m= sections will always be bundled."
This is a reland of d2b885fd91909f1b17fb11292a8c989d5d883b22, after making sure transports that are just being kept alive in case of rollback don't contribute to connection state, which broke a WPT. Original change's description: > Fix bug where we assume new m= sections will always be bundled. > > A recent change [1] assumes that all new m= sections will share the > first BUNDLE group (if one already exists), which avoids generating > ICE candidates that are ultimately unnecessary. This is fine for JSEP > endpoints, but it breaks the following scenarios for non-JSEP endpoints: > > * Remote offer adding a new m= section that's not part of any BUNDLE > group. > * Remote offer adding an m= section to the second BUNDLE group. > > The latter is specifically problematic for any application that wants > to bundle all audio streams in one group and all video streams in > another group when using Unified Plan SDP, to replicate the behavior of > using Plan B without bundling. It may try to add a video stream only > for WebRTC to bundle it with audio. > > This is fixed by doing some minor re-factoring, having BundleManager > update the bundle groups at offer time. > > Also: > * Added some additional validation for multiple bundle groups in a > subsequent offer, since that now becomes relevant. > * Improved rollback support, because now rolling back an offer may need > to not only remove mid->transport mappings but alter them. > > [1]: https://webrtc-review.googlesource.com/c/src/+/221601 > > Bug: webrtc:12906, webrtc:12999 > Change-Id: I4c6e7020c0be33a782d3608dee88e4e2fceb1be1 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/225642 > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > Reviewed-by: Henrik Boström <hbos@webrtc.org> > Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#34544} Bug: webrtc:12906, webrtc:12999 Change-Id: I68bf988b1918dd2d51de76e53e4fd696fea5a09b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227120 Reviewed-by: Henrik Boström <hbos@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34596}
This commit is contained in:
committed by
WebRTC LUCI CQ
parent
024200483d
commit
704a834f68
@ -861,6 +861,67 @@ TEST_F(JsepTransportControllerTest,
|
||||
EXPECT_EQ(2, gathering_state_signal_count_);
|
||||
}
|
||||
|
||||
// Test that states immediately return to "new" if all transports are
|
||||
// discarded. This should happen at offer time, even though the transport
|
||||
// controller may keep the transport alive in case of rollback.
|
||||
TEST_F(JsepTransportControllerTest,
|
||||
IceStatesReturnToNewWhenTransportsDiscarded) {
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
auto description = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, description.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, description.get())
|
||||
.ok());
|
||||
|
||||
// Trigger and verify initial non-new states.
|
||||
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
|
||||
transport_controller_->GetDtlsTransport(kAudioMid1));
|
||||
fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
|
||||
fake_audio_dtls->fake_ice_transport()->SetTransportState(
|
||||
webrtc::IceTransportState::kChecking,
|
||||
cricket::IceTransportState::STATE_CONNECTING);
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
|
||||
ice_connection_state_, kTimeout);
|
||||
EXPECT_EQ(1, ice_connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
|
||||
combined_connection_state_, kTimeout);
|
||||
EXPECT_EQ(1, combined_connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(1, gathering_state_signal_count_);
|
||||
|
||||
// Reject m= section which should disconnect the transport and return states
|
||||
// to "new".
|
||||
description->contents()[0].rejected = true;
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kOffer, description.get())
|
||||
.ok());
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionNew,
|
||||
ice_connection_state_, kTimeout);
|
||||
EXPECT_EQ(2, ice_connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kNew,
|
||||
combined_connection_state_, kTimeout);
|
||||
EXPECT_EQ(2, combined_connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringNew, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(2, gathering_state_signal_count_);
|
||||
|
||||
// For good measure, rollback the offer and verify that states return to
|
||||
// their previous values.
|
||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
|
||||
ice_connection_state_, kTimeout);
|
||||
EXPECT_EQ(3, ice_connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
|
||||
combined_connection_state_, kTimeout);
|
||||
EXPECT_EQ(3, combined_connection_state_signal_count_);
|
||||
EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
|
||||
EXPECT_EQ(3, gathering_state_signal_count_);
|
||||
}
|
||||
|
||||
TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
auto description = CreateSessionDescriptionWithBundleGroup();
|
||||
@ -1608,6 +1669,356 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsChangeFirstMid) {
|
||||
EXPECT_EQ(mid5_transport, mid6_transport);
|
||||
}
|
||||
|
||||
TEST_F(JsepTransportControllerTest,
|
||||
MultipleBundleGroupsSectionsAddedInSubsequentOffer) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
static const char kMid2Audio[] = "2_audio";
|
||||
static const char kMid3Audio[] = "3_audio";
|
||||
static const char kMid4Video[] = "4_video";
|
||||
static const char kMid5Video[] = "5_video";
|
||||
static const char kMid6Video[] = "6_video";
|
||||
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
// Start by grouping (kMid1Audio,kMid2Audio) and (kMid4Video,kMid4f5Video).
|
||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group1.AddContentName(kMid1Audio);
|
||||
bundle_group1.AddContentName(kMid2Audio);
|
||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group2.AddContentName(kMid4Video);
|
||||
bundle_group2.AddContentName(kMid5Video);
|
||||
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_offer->AddGroup(bundle_group1);
|
||||
local_offer->AddGroup(bundle_group2);
|
||||
|
||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
remote_answer->AddGroup(bundle_group1);
|
||||
remote_answer->AddGroup(bundle_group2);
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
// Add kMid3Audio and kMid6Video to the respective audio/video bundle groups.
|
||||
cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group1.AddContentName(kMid3Audio);
|
||||
cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group2.AddContentName(kMid6Video);
|
||||
|
||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(subsequent_offer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
subsequent_offer->AddGroup(bundle_group1);
|
||||
subsequent_offer->AddGroup(bundle_group2);
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
||||
.ok());
|
||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
||||
auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
||||
auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
|
||||
auto mid5_transport = transport_controller_->GetRtpTransport(kMid5Video);
|
||||
auto mid6_transport = transport_controller_->GetRtpTransport(kMid6Video);
|
||||
EXPECT_NE(mid1_transport, mid4_transport);
|
||||
EXPECT_EQ(mid1_transport, mid2_transport);
|
||||
EXPECT_EQ(mid2_transport, mid3_transport);
|
||||
EXPECT_EQ(mid4_transport, mid5_transport);
|
||||
EXPECT_EQ(mid5_transport, mid6_transport);
|
||||
}
|
||||
|
||||
TEST_F(JsepTransportControllerTest,
|
||||
MultipleBundleGroupsCombinedInSubsequentOffer) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
static const char kMid2Audio[] = "2_audio";
|
||||
static const char kMid3Video[] = "3_video";
|
||||
static const char kMid4Video[] = "4_video";
|
||||
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
// Start by grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
|
||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group1.AddContentName(kMid1Audio);
|
||||
bundle_group1.AddContentName(kMid2Audio);
|
||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group2.AddContentName(kMid3Video);
|
||||
bundle_group2.AddContentName(kMid4Video);
|
||||
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_offer->AddGroup(bundle_group1);
|
||||
local_offer->AddGroup(bundle_group2);
|
||||
|
||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
remote_answer->AddGroup(bundle_group1);
|
||||
remote_answer->AddGroup(bundle_group2);
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
// Switch to grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
|
||||
// This is a illegal without first removing m= sections from their groups.
|
||||
cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
||||
new_bundle_group.AddContentName(kMid1Audio);
|
||||
new_bundle_group.AddContentName(kMid2Audio);
|
||||
new_bundle_group.AddContentName(kMid3Video);
|
||||
new_bundle_group.AddContentName(kMid4Video);
|
||||
|
||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
subsequent_offer->AddGroup(new_bundle_group);
|
||||
EXPECT_FALSE(
|
||||
transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
||||
.ok());
|
||||
}
|
||||
|
||||
TEST_F(JsepTransportControllerTest,
|
||||
MultipleBundleGroupsSplitInSubsequentOffer) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
static const char kMid2Audio[] = "2_audio";
|
||||
static const char kMid3Video[] = "3_video";
|
||||
static const char kMid4Video[] = "4_video";
|
||||
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
// Start by grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
|
||||
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group.AddContentName(kMid1Audio);
|
||||
bundle_group.AddContentName(kMid2Audio);
|
||||
bundle_group.AddContentName(kMid3Video);
|
||||
bundle_group.AddContentName(kMid4Video);
|
||||
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_offer->AddGroup(bundle_group);
|
||||
|
||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
remote_answer->AddGroup(bundle_group);
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
// Switch to grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
|
||||
// This is a illegal without first removing m= sections from their groups.
|
||||
cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
new_bundle_group1.AddContentName(kMid1Audio);
|
||||
new_bundle_group1.AddContentName(kMid2Audio);
|
||||
cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
new_bundle_group2.AddContentName(kMid3Video);
|
||||
new_bundle_group2.AddContentName(kMid4Video);
|
||||
|
||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
subsequent_offer->AddGroup(new_bundle_group1);
|
||||
subsequent_offer->AddGroup(new_bundle_group2);
|
||||
EXPECT_FALSE(
|
||||
transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
||||
.ok());
|
||||
}
|
||||
|
||||
TEST_F(JsepTransportControllerTest,
|
||||
MultipleBundleGroupsShuffledInSubsequentOffer) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
static const char kMid2Audio[] = "2_audio";
|
||||
static const char kMid3Video[] = "3_video";
|
||||
static const char kMid4Video[] = "4_video";
|
||||
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
// Start by grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
|
||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group1.AddContentName(kMid1Audio);
|
||||
bundle_group1.AddContentName(kMid2Audio);
|
||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group2.AddContentName(kMid3Video);
|
||||
bundle_group2.AddContentName(kMid4Video);
|
||||
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_offer->AddGroup(bundle_group1);
|
||||
local_offer->AddGroup(bundle_group2);
|
||||
|
||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
remote_answer->AddGroup(bundle_group1);
|
||||
remote_answer->AddGroup(bundle_group2);
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
// Switch to grouping (kMid1Audio,kMid3Video) and (kMid2Audio,kMid3Video).
|
||||
// This is a illegal without first removing m= sections from their groups.
|
||||
cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
new_bundle_group1.AddContentName(kMid1Audio);
|
||||
new_bundle_group1.AddContentName(kMid3Video);
|
||||
cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
new_bundle_group2.AddContentName(kMid2Audio);
|
||||
new_bundle_group2.AddContentName(kMid4Video);
|
||||
|
||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
subsequent_offer->AddGroup(new_bundle_group1);
|
||||
subsequent_offer->AddGroup(new_bundle_group2);
|
||||
EXPECT_FALSE(
|
||||
transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
||||
.ok());
|
||||
}
|
||||
|
||||
// Tests that only a subset of all the m= sections are bundled.
|
||||
TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
@ -2059,4 +2470,220 @@ TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
|
||||
.ok());
|
||||
}
|
||||
|
||||
TEST_F(JsepTransportControllerTest, RollbackRestoresRejectedTransport) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
|
||||
// Perform initial offer/answer.
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
std::unique_ptr<cricket::SessionDescription> remote_answer(
|
||||
local_offer->Clone());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
|
||||
// Apply a reoffer which rejects the m= section, causing the transport to be
|
||||
// set to null.
|
||||
auto local_reoffer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_reoffer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_reoffer->contents()[0].rejected = true;
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
|
||||
.ok());
|
||||
auto old_mid1_transport = mid1_transport;
|
||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
EXPECT_EQ(nullptr, mid1_transport);
|
||||
|
||||
// Rolling back shouldn't just create a new transport for MID 1, it should
|
||||
// restore the old transport.
|
||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
EXPECT_EQ(old_mid1_transport, mid1_transport);
|
||||
}
|
||||
|
||||
// If an offer with a modified BUNDLE group causes a MID->transport mapping to
|
||||
// change, rollback should restore the previous mapping.
|
||||
TEST_F(JsepTransportControllerTest, RollbackRestoresPreviousTransportMapping) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
static const char kMid2Audio[] = "2_audio";
|
||||
static const char kMid3Audio[] = "3_audio";
|
||||
|
||||
// Perform an initial offer/answer to establish a (kMid1Audio,kMid2Audio)
|
||||
// group.
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group.AddContentName(kMid1Audio);
|
||||
bundle_group.AddContentName(kMid2Audio);
|
||||
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_offer->AddGroup(bundle_group);
|
||||
|
||||
std::unique_ptr<cricket::SessionDescription> remote_answer(
|
||||
local_offer->Clone());
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
||||
auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
||||
EXPECT_EQ(mid1_transport, mid2_transport);
|
||||
EXPECT_NE(mid1_transport, mid3_transport);
|
||||
|
||||
// Apply a reoffer adding kMid3Audio to the group; transport mapping should
|
||||
// change, even without an answer, since this is an existing group.
|
||||
bundle_group.AddContentName(kMid3Audio);
|
||||
auto local_reoffer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_reoffer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_reoffer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddAudioSection(local_reoffer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_reoffer->AddGroup(bundle_group);
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
|
||||
.ok());
|
||||
|
||||
// Store the old transport pointer and verify that the offer actually changed
|
||||
// transports.
|
||||
auto old_mid3_transport = mid3_transport;
|
||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
||||
mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
||||
EXPECT_EQ(mid1_transport, mid2_transport);
|
||||
EXPECT_EQ(mid1_transport, mid3_transport);
|
||||
|
||||
// Rolling back shouldn't just create a new transport for MID 3, it should
|
||||
// restore the old transport.
|
||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
||||
mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
||||
EXPECT_EQ(old_mid3_transport, mid3_transport);
|
||||
}
|
||||
|
||||
// Test that if an offer adds a MID to a specific BUNDLE group and is then
|
||||
// rolled back, it can be added to a different BUNDLE group in a new offer.
|
||||
// This is effectively testing that rollback resets the BundleManager state.
|
||||
TEST_F(JsepTransportControllerTest, RollbackAndAddToDifferentBundleGroup) {
|
||||
static const char kMid1Audio[] = "1_audio";
|
||||
static const char kMid2Audio[] = "2_audio";
|
||||
static const char kMid3Audio[] = "3_audio";
|
||||
|
||||
// Perform an initial offer/answer to establish two bundle groups, each with
|
||||
// one MID.
|
||||
CreateJsepTransportController(JsepTransportController::Config());
|
||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group1.AddContentName(kMid1Audio);
|
||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
bundle_group2.AddContentName(kMid2Audio);
|
||||
|
||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(local_offer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
local_offer->AddGroup(bundle_group1);
|
||||
local_offer->AddGroup(bundle_group2);
|
||||
|
||||
std::unique_ptr<cricket::SessionDescription> remote_answer(
|
||||
local_offer->Clone());
|
||||
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
||||
.ok());
|
||||
EXPECT_TRUE(transport_controller_
|
||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
||||
.ok());
|
||||
|
||||
// Apply an offer that adds kMid3Audio to the first BUNDLE group.,
|
||||
cricket::ContentGroup modified_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
||||
modified_bundle_group1.AddContentName(kMid1Audio);
|
||||
modified_bundle_group1.AddContentName(kMid3Audio);
|
||||
auto subsequent_offer_1 = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(subsequent_offer_1.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer_1.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer_1.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
subsequent_offer_1->AddGroup(modified_bundle_group1);
|
||||
subsequent_offer_1->AddGroup(bundle_group2);
|
||||
|
||||
EXPECT_TRUE(
|
||||
transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer_1.get())
|
||||
.ok());
|
||||
|
||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
||||
auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
||||
EXPECT_NE(mid1_transport, mid2_transport);
|
||||
EXPECT_EQ(mid1_transport, mid3_transport);
|
||||
|
||||
// Rollback and expect the transport to be reset.
|
||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
||||
EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kMid3Audio));
|
||||
|
||||
// Apply an offer that adds kMid3Audio to the second BUNDLE group.,
|
||||
cricket::ContentGroup modified_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
||||
modified_bundle_group2.AddContentName(kMid2Audio);
|
||||
modified_bundle_group2.AddContentName(kMid3Audio);
|
||||
auto subsequent_offer_2 = std::make_unique<cricket::SessionDescription>();
|
||||
AddAudioSection(subsequent_offer_2.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer_2.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
AddVideoSection(subsequent_offer_2.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
||||
nullptr);
|
||||
subsequent_offer_2->AddGroup(bundle_group1);
|
||||
subsequent_offer_2->AddGroup(modified_bundle_group2);
|
||||
|
||||
EXPECT_TRUE(
|
||||
transport_controller_
|
||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer_2.get())
|
||||
.ok());
|
||||
|
||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
||||
mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
||||
mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
||||
EXPECT_NE(mid1_transport, mid2_transport);
|
||||
EXPECT_EQ(mid2_transport, mid3_transport);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Reference in New Issue
Block a user