Rewrite WebRtcSession BUNDLE tests as PeerConnection tests
Bug: webrtc:8222
Change-Id: Id47e4544dc073564ad7e63d02865ca80dd5a85ff
Reviewed-on: https://webrtc-review.googlesource.com/8280
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20365}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 02a8e9a..3e552c9 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -391,6 +391,7 @@
"localaudiosource_unittest.cc",
"mediaconstraintsinterface_unittest.cc",
"mediastream_unittest.cc",
+ "peerconnection_bundle_unittest.cc",
"peerconnection_crypto_unittest.cc",
"peerconnection_ice_unittest.cc",
"peerconnection_integrationtest.cc",
diff --git a/pc/peerconnection_bundle_unittest.cc b/pc/peerconnection_bundle_unittest.cc
new file mode 100644
index 0000000..6bc177d
--- /dev/null
+++ b/pc/peerconnection_bundle_unittest.cc
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/peerconnectionproxy.h"
+#include "p2p/base/fakeportallocator.h"
+#include "p2p/base/teststunserver.h"
+#include "p2p/client/basicportallocator.h"
+#include "pc/mediasession.h"
+#include "pc/peerconnection.h"
+#include "pc/peerconnectionwrapper.h"
+#include "pc/sdputils.h"
+#ifdef WEBRTC_ANDROID
+#include "pc/test/androidtestinitializer.h"
+#endif
+#include "pc/test/fakeaudiocapturemodule.h"
+#include "rtc_base/fakenetwork.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/ptr_util.h"
+#include "rtc_base/virtualsocketserver.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+using BundlePolicy = PeerConnectionInterface::BundlePolicy;
+using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
+using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
+using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
+using rtc::SocketAddress;
+using ::testing::ElementsAre;
+using ::testing::UnorderedElementsAre;
+using ::testing::Values;
+
+constexpr int kDefaultTimeout = 10000;
+
+// TODO(steveanton): These tests should be rewritten to use the standard
+// RtpSenderInterface/DtlsTransportInterface objects once they're available in
+// the API. The RtpSender can be used to determine which transport a given media
+// will use: https://www.w3.org/TR/webrtc/#dom-rtcrtpsender-transport
+
+class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
+ public:
+ using PeerConnectionWrapper::PeerConnectionWrapper;
+
+ bool AddIceCandidateToMedia(cricket::Candidate* candidate,
+ cricket::MediaType media_type) {
+ auto* desc = pc()->remote_description()->description();
+ for (size_t i = 0; i < desc->contents().size(); i++) {
+ const auto& content = desc->contents()[i];
+ auto* media_desc =
+ static_cast<cricket::MediaContentDescription*>(content.description);
+ if (media_desc->type() == media_type) {
+ candidate->set_transport_name(content.name);
+ JsepIceCandidate jsep_candidate(content.name, i, *candidate);
+ return pc()->AddIceCandidate(&jsep_candidate);
+ }
+ }
+ RTC_NOTREACHED();
+ return false;
+ }
+
+ rtc::PacketTransportInternal* voice_rtp_transport_channel() {
+ return (voice_channel() ? voice_channel()->rtp_dtls_transport() : nullptr);
+ }
+
+ rtc::PacketTransportInternal* voice_rtcp_transport_channel() {
+ return (voice_channel() ? voice_channel()->rtcp_dtls_transport() : nullptr);
+ }
+
+ cricket::VoiceChannel* voice_channel() {
+ return GetInternalPeerConnection()->voice_channel();
+ }
+
+ rtc::PacketTransportInternal* video_rtp_transport_channel() {
+ return (video_channel() ? video_channel()->rtp_dtls_transport() : nullptr);
+ }
+
+ rtc::PacketTransportInternal* video_rtcp_transport_channel() {
+ return (video_channel() ? video_channel()->rtcp_dtls_transport() : nullptr);
+ }
+
+ cricket::VideoChannel* video_channel() {
+ return GetInternalPeerConnection()->video_channel();
+ }
+
+ PeerConnection* GetInternalPeerConnection() {
+ auto* pci = reinterpret_cast<
+ PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(pc());
+ return reinterpret_cast<PeerConnection*>(pci->internal());
+ }
+
+ // Returns true if the stats indicate that an ICE connection is either in
+ // progress or established with the given remote address.
+ bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
+ auto report = GetStats();
+ if (!report) {
+ return false;
+ }
+ std::string matching_candidate_id;
+ for (auto* ice_candidate_stats :
+ report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
+ if (*ice_candidate_stats->ip == address.HostAsURIString() &&
+ *ice_candidate_stats->port == address.port()) {
+ matching_candidate_id = ice_candidate_stats->id();
+ break;
+ }
+ }
+ if (matching_candidate_id.empty()) {
+ return false;
+ }
+ for (auto* pair_stats :
+ report->GetStatsOfType<RTCIceCandidatePairStats>()) {
+ if (*pair_stats->remote_candidate_id == matching_candidate_id) {
+ if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
+ *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ rtc::FakeNetworkManager* network() { return network_; }
+
+ void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
+
+ private:
+ rtc::FakeNetworkManager* network_;
+};
+
+class PeerConnectionBundleTest : public ::testing::Test {
+ protected:
+ typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
+
+ PeerConnectionBundleTest()
+ : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
+#ifdef WEBRTC_ANDROID
+ InitializeAndroidObjects();
+#endif
+ pc_factory_ = CreatePeerConnectionFactory(
+ rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
+ FakeAudioCaptureModule::Create(), nullptr, nullptr);
+ }
+
+ WrapperPtr CreatePeerConnection() {
+ return CreatePeerConnection(RTCConfiguration());
+ }
+
+ WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
+ auto* fake_network = NewFakeNetwork();
+ auto port_allocator =
+ rtc::MakeUnique<cricket::BasicPortAllocator>(fake_network);
+ port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
+ cricket::PORTALLOCATOR_DISABLE_RELAY);
+ port_allocator->set_step_delay(cricket::kMinimumStepDelay);
+ auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
+ auto pc = pc_factory_->CreatePeerConnection(
+ config, std::move(port_allocator), nullptr, observer.get());
+ if (!pc) {
+ return nullptr;
+ }
+
+ auto wrapper = rtc::MakeUnique<PeerConnectionWrapperForBundleTest>(
+ pc_factory_, pc, std::move(observer));
+ wrapper->set_network(fake_network);
+ return wrapper;
+ }
+
+ // Accepts the same arguments as CreatePeerConnection and adds default audio
+ // and video tracks.
+ template <typename... Args>
+ WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
+ auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
+ if (!wrapper) {
+ return nullptr;
+ }
+ wrapper->AddAudioTrack("a");
+ wrapper->AddVideoTrack("v");
+ return wrapper;
+ }
+
+ cricket::Candidate CreateLocalUdpCandidate(
+ const rtc::SocketAddress& address) {
+ cricket::Candidate candidate;
+ candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
+ candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
+ candidate.set_address(address);
+ candidate.set_type(cricket::LOCAL_PORT_TYPE);
+ return candidate;
+ }
+
+ rtc::FakeNetworkManager* NewFakeNetwork() {
+ // The PeerConnection's port allocator is tied to the PeerConnection's
+ // lifetime and expects the underlying NetworkManager to outlive it. If
+ // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
+ // before the PeerConnection (since subclass members are destroyed before
+ // base class members). Therefore, the test fixture will own all the fake
+ // networks even though tests should access the fake network through the
+ // PeerConnectionWrapper.
+ auto* fake_network = new rtc::FakeNetworkManager();
+ fake_networks_.emplace_back(fake_network);
+ return fake_network;
+ }
+
+ std::unique_ptr<rtc::VirtualSocketServer> vss_;
+ rtc::AutoSocketServerThread main_;
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
+ std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
+};
+
+SdpContentMutator RemoveRtcpMux() {
+ return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
+ auto* media_desc =
+ static_cast<cricket::MediaContentDescription*>(content->description);
+ media_desc->set_rtcp_mux(false);
+ };
+}
+
+std::vector<int> GetCandidateComponents(
+ const std::vector<IceCandidateInterface*> candidates) {
+ std::vector<int> components;
+ for (auto* candidate : candidates) {
+ components.push_back(candidate->candidate().component());
+ }
+ return components;
+}
+
+// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
+// each media section when disabling bundling and disabling RTCP multiplexing.
+TEST_F(PeerConnectionBundleTest,
+ TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
+ const SocketAddress kCallerAddress("1.1.1.1", 0);
+ const SocketAddress kCalleeAddress("2.2.2.2", 0);
+
+ RTCConfiguration config;
+ config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ caller->network()->AddInterface(kCallerAddress);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+ callee->network()->AddInterface(kCalleeAddress);
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ RTCOfferAnswerOptions options_no_bundle;
+ options_no_bundle.use_rtp_mux = false;
+ auto answer = callee->CreateAnswer(options_no_bundle);
+ SdpContentsForEach(RemoveRtcpMux(), answer->description());
+ ASSERT_TRUE(
+ callee->SetLocalDescription(CloneSessionDescription(answer.get())));
+ ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
+
+ // Check that caller has separate RTP and RTCP candidates for each media.
+ EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
+ EXPECT_THAT(
+ GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
+ UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
+ cricket::ICE_CANDIDATE_COMPONENT_RTCP));
+ EXPECT_THAT(
+ GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
+ UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
+ cricket::ICE_CANDIDATE_COMPONENT_RTCP));
+
+ // Check that callee has separate RTP and RTCP candidates for each media.
+ EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
+ EXPECT_THAT(
+ GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
+ UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
+ cricket::ICE_CANDIDATE_COMPONENT_RTCP));
+ EXPECT_THAT(
+ GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
+ UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
+ cricket::ICE_CANDIDATE_COMPONENT_RTCP));
+}
+
+// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
+// section when disabling bundle but enabling RTCP multiplexing.
+TEST_F(PeerConnectionBundleTest,
+ OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
+ const SocketAddress kCallerAddress("1.1.1.1", 0);
+
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ caller->network()->AddInterface(kCallerAddress);
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ RTCOfferAnswerOptions options_no_bundle;
+ options_no_bundle.use_rtp_mux = false;
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
+
+ EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
+
+ EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
+ EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
+}
+
+// Test that there is 1 local UDP candidate in only the first media section when
+// bundling and enabling RTCP multiplexing.
+TEST_F(PeerConnectionBundleTest,
+ OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
+ const SocketAddress kCallerAddress("1.1.1.1", 0);
+
+ RTCConfiguration config;
+ config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ caller->network()->AddInterface(kCallerAddress);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
+
+ EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
+
+ EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
+ EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
+}
+
+// The following parameterized test verifies that an offer/answer with varying
+// bundle policies and either bundle in the answer or not will produce the
+// expected RTP transports for audio and video. In particular, for bundling we
+// care about whether they are separate transports or the same.
+
+enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
+std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
+ switch (value) {
+ case BundleIncluded::kBundleInAnswer:
+ return out << "bundle in answer";
+ case BundleIncluded::kBundleNotInAnswer:
+ return out << "bundle not in answer";
+ }
+ return out << "unknown";
+}
+
+class PeerConnectionBundleMatrixTest
+ : public PeerConnectionBundleTest,
+ public ::testing::WithParamInterface<
+ std::tuple<BundlePolicy, BundleIncluded, bool, bool>> {
+ protected:
+ PeerConnectionBundleMatrixTest() {
+ bundle_policy_ = std::get<0>(GetParam());
+ bundle_included_ = std::get<1>(GetParam());
+ expected_same_before_ = std::get<2>(GetParam());
+ expected_same_after_ = std::get<3>(GetParam());
+ }
+
+ PeerConnectionInterface::BundlePolicy bundle_policy_;
+ BundleIncluded bundle_included_;
+ bool expected_same_before_;
+ bool expected_same_after_;
+};
+
+TEST_P(PeerConnectionBundleMatrixTest,
+ VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
+ RTCConfiguration config;
+ config.bundle_policy = bundle_policy_;
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ bool equal_before = (caller->voice_rtp_transport_channel() ==
+ caller->video_rtp_transport_channel());
+ EXPECT_EQ(expected_same_before_, equal_before);
+
+ RTCOfferAnswerOptions options;
+ options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
+ bool equal_after = (caller->voice_rtp_transport_channel() ==
+ caller->video_rtp_transport_channel());
+ EXPECT_EQ(expected_same_after_, equal_after);
+}
+
+// The max-bundle policy means we should anticipate bundling being negotiated,
+// and multiplex audio/video from the start.
+// For all other policies, bundling should only be enabled if negotiated by the
+// answer.
+INSTANTIATE_TEST_CASE_P(
+ PeerConnectionBundleTest,
+ PeerConnectionBundleMatrixTest,
+ Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
+ BundleIncluded::kBundleInAnswer,
+ false,
+ true),
+ std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
+ BundleIncluded::kBundleNotInAnswer,
+ false,
+ false),
+ std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
+ BundleIncluded::kBundleInAnswer,
+ true,
+ true),
+ std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
+ BundleIncluded::kBundleNotInAnswer,
+ true,
+ true),
+ std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
+ BundleIncluded::kBundleInAnswer,
+ false,
+ true),
+ std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
+ BundleIncluded::kBundleNotInAnswer,
+ false,
+ false)));
+
+// Test that the audio/video transports on the callee side are the same before
+// and after setting a local answer when max BUNDLE is enabled and an offer with
+// BUNDLE is received.
+TEST_F(PeerConnectionBundleTest,
+ TransportsSameForMaxBundleWithBundleInRemoteOffer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ RTCConfiguration config;
+ config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ RTCOfferAnswerOptions options_with_bundle;
+ options_with_bundle.use_rtp_mux = true;
+ ASSERT_TRUE(callee->SetRemoteDescription(
+ caller->CreateOfferAndSetAsLocal(options_with_bundle)));
+
+ EXPECT_EQ(callee->voice_rtp_transport_channel(),
+ callee->video_rtp_transport_channel());
+
+ ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
+
+ EXPECT_EQ(callee->voice_rtp_transport_channel(),
+ callee->video_rtp_transport_channel());
+}
+
+TEST_F(PeerConnectionBundleTest,
+ FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ RTCConfiguration config;
+ config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ RTCOfferAnswerOptions options_no_bundle;
+ options_no_bundle.use_rtp_mux = false;
+ EXPECT_FALSE(callee->SetRemoteDescription(
+ caller->CreateOfferAndSetAsLocal(options_no_bundle)));
+}
+
+// Test that if the media section which has the bundled transport is rejected,
+// then the peers still connect and the bundled transport switches to the other
+// media section.
+// Note: This is currently failing because of the following bug:
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
+TEST_F(PeerConnectionBundleTest,
+ DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
+ RTCConfiguration config;
+ config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnection();
+ callee->AddVideoTrack("v");
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ RTCOfferAnswerOptions options;
+ options.offer_to_receive_audio = 0;
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
+
+ EXPECT_FALSE(caller->voice_rtp_transport_channel());
+ EXPECT_TRUE(caller->video_rtp_transport_channel());
+}
+
+// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
+// transport channels.
+TEST_F(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
+ RTCConfiguration config;
+ config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ EXPECT_FALSE(caller->voice_rtcp_transport_channel());
+ EXPECT_FALSE(caller->video_rtcp_transport_channel());
+
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ EXPECT_FALSE(caller->voice_rtcp_transport_channel());
+ EXPECT_FALSE(caller->video_rtcp_transport_channel());
+}
+
+// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transport
+// channels when the offer is sent, but will destroy them once the remote answer
+// is set.
+TEST_F(PeerConnectionBundleTest,
+ CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
+ RTCConfiguration config;
+ config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ EXPECT_TRUE(caller->voice_rtcp_transport_channel());
+ EXPECT_TRUE(caller->video_rtcp_transport_channel());
+
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ EXPECT_FALSE(caller->voice_rtcp_transport_channel());
+ EXPECT_FALSE(caller->video_rtcp_transport_channel());
+}
+
+TEST_F(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ RTCOfferAnswerOptions options;
+ options.use_rtp_mux = true;
+
+ auto offer = caller->CreateOffer(options);
+ SdpContentsForEach(RemoveRtcpMux(), offer->description());
+
+ std::string error;
+ EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
+ &error));
+ EXPECT_EQ(
+ "Failed to set local offer SDP: rtcp-mux must be enabled when BUNDLE is "
+ "enabled.",
+ error);
+
+ EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
+ EXPECT_EQ(
+ "Failed to set remote offer SDP: rtcp-mux must be enabled when BUNDLE is "
+ "enabled.",
+ error);
+}
+
+// Test that candidates sent to the "video" transport do not get pushed down to
+// the "audio" transport channel when bundling.
+TEST_F(PeerConnectionBundleTest,
+ IgnoreCandidatesForUnusedTransportWhenBundling) {
+ const SocketAddress kAudioAddress1("1.1.1.1", 1111);
+ const SocketAddress kAudioAddress2("2.2.2.2", 2222);
+ const SocketAddress kVideoAddress("3.3.3.3", 3333);
+ const SocketAddress kCallerAddress("4.4.4.4", 0);
+ const SocketAddress kCalleeAddress("5.5.5.5", 0);
+
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ caller->network()->AddInterface(kCallerAddress);
+ callee->network()->AddInterface(kCalleeAddress);
+
+ RTCOfferAnswerOptions options;
+ options.use_rtp_mux = true;
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ ASSERT_TRUE(
+ caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+
+ // The way the *_WAIT checks work is they only wait if the condition fails,
+ // which does not help in the case where state is not changing. This is
+ // problematic in this test since we want to verify that adding a video
+ // candidate does _not_ change state. So we interleave candidates and assume
+ // that messages are executed in the order they were posted.
+
+ cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
+ ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
+ cricket::MEDIA_TYPE_AUDIO));
+
+ cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
+ ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
+ cricket::MEDIA_TYPE_VIDEO));
+
+ cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
+ ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
+ cricket::MEDIA_TYPE_AUDIO));
+
+ EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
+ kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
+ kDefaultTimeout);
+ EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
+}
+
+// Test that the transport used by both audio and video is the transport
+// associated with the first MID in the answer BUNDLE group, even if it's in a
+// different order from the offer.
+TEST_F(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
+ auto caller = CreatePeerConnectionWithAudioVideo();
+ auto callee = CreatePeerConnectionWithAudioVideo();
+
+ ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+
+ auto* old_video_transport = caller->video_rtp_transport_channel();
+
+ auto answer = callee->CreateAnswer();
+ auto* old_bundle_group =
+ answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+ ASSERT_THAT(old_bundle_group->content_names(),
+ ElementsAre(cricket::CN_AUDIO, cricket::CN_VIDEO));
+ answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
+
+ cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
+ new_bundle_group.AddContentName(cricket::CN_VIDEO);
+ new_bundle_group.AddContentName(cricket::CN_AUDIO);
+ answer->description()->AddGroup(new_bundle_group);
+
+ ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
+
+ EXPECT_EQ(old_video_transport, caller->video_rtp_transport_channel());
+ EXPECT_EQ(caller->voice_rtp_transport_channel(),
+ caller->video_rtp_transport_channel());
+}
+
+} // namespace webrtc
diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc
index 9453567..d38433c 100644
--- a/pc/peerconnection_integrationtest.cc
+++ b/pc/peerconnection_integrationtest.cc
@@ -291,6 +291,9 @@
ice_connection_state_history() const {
return ice_connection_state_history_;
}
+ void clear_ice_connection_state_history() {
+ ice_connection_state_history_.clear();
+ }
// Every ICE gathering state in order that has been seen by the observer.
std::vector<PeerConnectionInterface::IceGatheringState>
@@ -3082,6 +3085,31 @@
kMaxWaitForFramesMs);
}
+// With a max bundle policy and RTCP muxing, adding a new media description to
+// the connection should not affect ICE at all because the new media will use
+// the existing connection.
+TEST_F(PeerConnectionIntegrationTest,
+ AddMediaToConnectedBundleDoesNotRestartIce) {
+ PeerConnectionInterface::RTCConfiguration config;
+ config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
+ config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
+ ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(
+ config, PeerConnectionInterface::RTCConfiguration()));
+ ConnectFakeSignaling();
+
+ caller()->AddAudioOnlyMediaStream();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ caller()->clear_ice_connection_state_history();
+
+ caller()->AddVideoOnlyMediaStream();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ EXPECT_EQ(0u, caller()->ice_connection_state_history().size());
+}
+
// This test sets up a call between two parties with audio and video. It then
// renegotiates setting the video m-line to "port 0", then later renegotiates
// again, enabling video.
diff --git a/pc/peerconnectioninterface_unittest.cc b/pc/peerconnectioninterface_unittest.cc
index a8b4f72..d8fa486 100644
--- a/pc/peerconnectioninterface_unittest.cc
+++ b/pc/peerconnectioninterface_unittest.cc
@@ -944,7 +944,7 @@
EXPECT_TRUE(DoSetLocalDescription(std::move(new_offer)));
EXPECT_EQ(PeerConnectionInterface::kHaveLocalOffer, observer_.state_);
// Wait for the ice_complete message, so that SDP will have candidates.
- EXPECT_TRUE_WAIT(observer_.ice_complete_, kTimeout);
+ EXPECT_TRUE_WAIT(observer_.ice_gathering_complete_, kTimeout);
}
void CreateAnswerAsRemoteDescription(const std::string& sdp) {
@@ -1598,7 +1598,7 @@
EXPECT_TRUE(DoSetLocalDescription(std::move(answer)));
EXPECT_TRUE_WAIT(observer_.last_candidate() != nullptr, kTimeout);
- EXPECT_TRUE_WAIT(observer_.ice_complete_, kTimeout);
+ EXPECT_TRUE_WAIT(observer_.ice_gathering_complete_, kTimeout);
EXPECT_TRUE(pc_->AddIceCandidate(observer_.last_candidate()));
}
diff --git a/pc/peerconnectionwrapper.cc b/pc/peerconnectionwrapper.cc
index 9be9309..070deb9 100644
--- a/pc/peerconnectionwrapper.cc
+++ b/pc/peerconnectionwrapper.cc
@@ -23,7 +23,7 @@
namespace webrtc {
namespace {
-const uint32_t kWaitTimeout = 10000U;
+const uint32_t kDefaultTimeout = 10000U;
}
PeerConnectionWrapper::PeerConnectionWrapper(
@@ -122,7 +122,7 @@
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>());
fn(observer);
- EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout);
+ EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout);
if (error_out && !observer->result()) {
*error_out = observer->error();
}
@@ -155,7 +155,7 @@
rtc::scoped_refptr<MockSetSessionDescriptionObserver> observer(
new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
fn(observer);
- EXPECT_EQ_WAIT(true, observer->called(), kWaitTimeout);
+ EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout);
if (error_out && !observer->result()) {
*error_out = observer->error();
}
@@ -186,7 +186,20 @@
}
bool PeerConnectionWrapper::IsIceGatheringDone() {
- return observer()->ice_complete_;
+ return observer()->ice_gathering_complete_;
+}
+
+bool PeerConnectionWrapper::IsIceConnected() {
+ return observer()->ice_connected_;
+}
+
+rtc::scoped_refptr<const webrtc::RTCStatsReport>
+PeerConnectionWrapper::GetStats() {
+ rtc::scoped_refptr<webrtc::MockRTCStatsCollectorCallback> callback(
+ new rtc::RefCountedObject<webrtc::MockRTCStatsCollectorCallback>());
+ pc()->GetStats(callback);
+ EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout);
+ return callback->report();
}
} // namespace webrtc
diff --git a/pc/peerconnectionwrapper.h b/pc/peerconnectionwrapper.h
index f74fcdb..88d2f07 100644
--- a/pc/peerconnectionwrapper.h
+++ b/pc/peerconnectionwrapper.h
@@ -107,6 +107,13 @@
// Returns true if ICE has finished gathering candidates.
bool IsIceGatheringDone();
+ // Returns true if ICE has established a connection.
+ bool IsIceConnected();
+
+ // Calls GetStats() on the underlying PeerConnection and returns the resulting
+ // report. If GetStats() fails, this method returns null and fails the test.
+ rtc::scoped_refptr<const RTCStatsReport> GetStats();
+
private:
std::unique_ptr<SessionDescriptionInterface> CreateSdp(
std::function<void(CreateSessionDescriptionObserver*)> fn,
diff --git a/pc/test/mockpeerconnectionobservers.h b/pc/test/mockpeerconnectionobservers.h
index 82098ca..845dbc7 100644
--- a/pc/test/mockpeerconnectionobservers.h
+++ b/pc/test/mockpeerconnectionobservers.h
@@ -73,12 +73,15 @@
void OnIceConnectionChange(
PeerConnectionInterface::IceConnectionState new_state) override {
RTC_DCHECK(pc_->ice_connection_state() == new_state);
+ ice_connected_ =
+ (new_state == PeerConnectionInterface::kIceConnectionConnected);
callback_triggered_ = true;
}
void OnIceGatheringChange(
PeerConnectionInterface::IceGatheringState new_state) override {
RTC_DCHECK(pc_->ice_gathering_state() == new_state);
- ice_complete_ = new_state == PeerConnectionInterface::kIceGatheringComplete;
+ ice_gathering_complete_ =
+ new_state == PeerConnectionInterface::kIceGatheringComplete;
callback_triggered_ = true;
}
void OnIceCandidate(const IceCandidateInterface* candidate) override {
@@ -159,7 +162,8 @@
rtc::scoped_refptr<DataChannelInterface> last_datachannel_;
rtc::scoped_refptr<StreamCollection> remote_streams_;
bool renegotiation_needed_ = false;
- bool ice_complete_ = false;
+ bool ice_gathering_complete_ = false;
+ bool ice_connected_ = false;
bool callback_triggered_ = false;
int num_added_tracks_ = 0;
std::string last_added_track_label_;
diff --git a/pc/webrtcsession.cc b/pc/webrtcsession.cc
index 2e0ae50..4fd1e32 100644
--- a/pc/webrtcsession.cc
+++ b/pc/webrtcsession.cc
@@ -56,8 +56,9 @@
namespace webrtc {
// Error messages
-const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
- "is enabled.";
+const char kBundleWithoutRtcpMux[] =
+ "rtcp-mux must be enabled when BUNDLE "
+ "is enabled.";
const char kCreateChannelFailed[] = "Failed to create channels.";
const char kInvalidCandidates[] = "Description contains invalid candidates.";
const char kInvalidSdp[] = "Invalid session description.";
diff --git a/pc/webrtcsession_unittest.cc b/pc/webrtcsession_unittest.cc
index 0c54abc..a6fda21 100644
--- a/pc/webrtcsession_unittest.cc
+++ b/pc/webrtcsession_unittest.cc
@@ -83,11 +83,9 @@
// Media index of candidates belonging to the first media content.
static const int kMediaContentIndex0 = 0;
-static const char kMediaContentName0[] = "audio";
// Media index of candidates belonging to the second media content.
static const int kMediaContentIndex1 = 1;
-static const char kMediaContentName1[] = "video";
static const int kDefaultTimeout = 10000; // 10 seconds.
static const int kIceCandidatesTimeout = 10000;
@@ -400,12 +398,6 @@
Init();
}
- void InitWithRtcpMuxPolicy(
- PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy) {
- PeerConnectionInterface::RTCConfiguration configuration;
- Init(nullptr, rtcp_mux_policy, rtc::CryptoOptions());
- }
-
// Successfully init with DTLS; with a certificate generated and supplied or
// with a store that generates it for us.
void InitWithDtls(RTCCertificateGenerationMethod cert_gen_method) {
@@ -901,51 +893,6 @@
return CreateRemoteAnswer(offer, options, cricket::SEC_REQUIRED);
}
- void TestSessionCandidatesWithBundleRtcpMux(bool bundle, bool rtcp_mux) {
- AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
- Init();
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = bundle;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- // SetLocalDescription and SetRemoteDescriptions takes ownership of offer
- // and answer.
- SetLocalDescriptionWithoutError(offer);
-
- std::unique_ptr<SessionDescriptionInterface> answer(
- CreateRemoteAnswer(session_->local_description()));
- std::string sdp;
- EXPECT_TRUE(answer->ToString(&sdp));
-
- size_t expected_candidate_num = 2;
- if (!rtcp_mux) {
- // If rtcp_mux is enabled we should expect 4 candidates - host and srflex
- // for rtp and rtcp.
- expected_candidate_num = 4;
- // Disable rtcp-mux from the answer
- const std::string kRtcpMux = "a=rtcp-mux";
- const std::string kXRtcpMux = "a=xrtcp-mux";
- rtc::replace_substrs(kRtcpMux.c_str(), kRtcpMux.length(),
- kXRtcpMux.c_str(), kXRtcpMux.length(),
- &sdp);
- }
-
- SessionDescriptionInterface* new_answer = CreateSessionDescription(
- JsepSessionDescription::kAnswer, sdp, NULL);
-
- // SetRemoteDescription to enable rtcp mux.
- SetRemoteDescriptionWithoutError(new_answer);
- EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
- EXPECT_EQ(expected_candidate_num, observer_.mline_0_candidates_.size());
- if (bundle) {
- EXPECT_EQ(0, observer_.mline_1_candidates_.size());
- } else {
- EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size());
- }
- }
-
// The method sets up a call from the session to itself, in a loopback
// arrangement. It also uses a firewall rule to create a temporary
// disconnection, and then a permanent disconnection.
@@ -1067,20 +1014,6 @@
rtc::CryptoOptions crypto_options_;
};
-TEST_F(WebRtcSessionTest, TestSessionCandidates) {
- TestSessionCandidatesWithBundleRtcpMux(false, false);
-}
-
-// Below test cases (TestSessionCandidatesWith*) verify the candidates gathered
-// with rtcp-mux and/or bundle.
-TEST_F(WebRtcSessionTest, TestSessionCandidatesWithRtcpMux) {
- TestSessionCandidatesWithBundleRtcpMux(false, true);
-}
-
-TEST_F(WebRtcSessionTest, TestSessionCandidatesWithBundleRtcpMux) {
- TestSessionCandidatesWithBundleRtcpMux(true, true);
-}
-
// Test that we can create and set an answer correctly when different
// SSL roles have been negotiated for different transports.
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=4525
@@ -1144,466 +1077,6 @@
SetLocalDescriptionWithoutError(answer);
}
-// Test that candidates sent to the "video" transport do not get pushed down to
-// the "audio" transport channel when bundling.
-TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
- AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
-
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
- SendAudioVideoStream1();
-
- cricket::MediaSessionOptions offer_options;
- GetOptionsForRemoteOffer(&offer_options);
- offer_options.bundle_enabled = true;
-
- SessionDescriptionInterface* offer = CreateRemoteOffer(offer_options);
- SetRemoteDescriptionWithoutError(offer);
-
- cricket::MediaSessionOptions answer_options;
- answer_options.bundle_enabled = true;
- SessionDescriptionInterface* answer = CreateAnswer(answer_options);
- SetLocalDescriptionWithoutError(answer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- cricket::BaseChannel* voice_channel = session_->voice_channel();
- ASSERT_TRUE(voice_channel != NULL);
-
- // Checks if one of the transport channels contains a connection using a given
- // port.
- auto connection_with_remote_port = [this](int port) {
- std::unique_ptr<webrtc::SessionStats> stats = session_->GetStats_s();
- for (auto& kv : stats->transport_stats) {
- for (auto& chan_stat : kv.second.channel_stats) {
- for (auto& conn_info : chan_stat.connection_infos) {
- if (conn_info.remote_candidate.address().port() == port) {
- return true;
- }
- }
- }
- }
- return false;
- };
-
- EXPECT_FALSE(connection_with_remote_port(5000));
- EXPECT_FALSE(connection_with_remote_port(5001));
- EXPECT_FALSE(connection_with_remote_port(6000));
-
- // The way the *_WAIT checks work is they only wait if the condition fails,
- // which does not help in the case where state is not changing. This is
- // problematic in this test since we want to verify that adding a video
- // candidate does _not_ change state. So we interleave candidates and assume
- // that messages are executed in the order they were posted.
-
- // First audio candidate.
- cricket::Candidate candidate0;
- candidate0.set_address(rtc::SocketAddress("1.1.1.1", 5000));
- candidate0.set_component(1);
- candidate0.set_protocol("udp");
- candidate0.set_type("local");
- JsepIceCandidate ice_candidate0(kMediaContentName0, kMediaContentIndex0,
- candidate0);
- EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate0));
-
- // Video candidate.
- cricket::Candidate candidate1;
- candidate1.set_address(rtc::SocketAddress("1.1.1.1", 6000));
- candidate1.set_component(1);
- candidate1.set_protocol("udp");
- candidate1.set_type("local");
- JsepIceCandidate ice_candidate1(kMediaContentName1, kMediaContentIndex1,
- candidate1);
- EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate1));
-
- // Second audio candidate.
- cricket::Candidate candidate2;
- candidate2.set_address(rtc::SocketAddress("1.1.1.1", 5001));
- candidate2.set_component(1);
- candidate2.set_protocol("udp");
- candidate2.set_type("local");
- JsepIceCandidate ice_candidate2(kMediaContentName0, kMediaContentIndex0,
- candidate2);
- EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate2));
-
- EXPECT_TRUE_WAIT(connection_with_remote_port(5000), 1000);
- EXPECT_TRUE_WAIT(connection_with_remote_port(5001), 1000);
-
- // No need here for a _WAIT check since we are checking that state hasn't
- // changed: if this is false we would be doing waits for nothing and if this
- // is true then there will be no messages processed anyways.
- EXPECT_FALSE(connection_with_remote_port(6000));
-}
-
-// kBundlePolicyBalanced BUNDLE policy and answer contains BUNDLE.
-TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_NE(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyBalanced BUNDLE policy but no BUNDLE in the answer.
-TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_NE(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
-
- // Remove BUNDLE from the answer.
- std::unique_ptr<SessionDescriptionInterface> answer(
- CreateRemoteAnswer(session_->local_description()));
- cricket::SessionDescription* answer_copy = answer->description()->Copy();
- answer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
- JsepSessionDescription* modified_answer =
- new JsepSessionDescription(JsepSessionDescription::kAnswer);
- modified_answer->Initialize(answer_copy, "1", "1");
- SetRemoteDescriptionWithoutError(modified_answer); //
-
- EXPECT_NE(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyMaxBundle policy with BUNDLE in the answer.
-TEST_F(WebRtcSessionTest, TestMaxBundleBundleInAnswer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyMaxBundle policy with BUNDLE in the answer, but no
-// audio content in the answer.
-TEST_F(WebRtcSessionTest, TestMaxBundleRejectAudio) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendVideoOnlyStream2();
- local_send_audio_ = false;
- remote_recv_audio_ = false;
- cricket::MediaSessionOptions recv_options;
- GetOptionsForRemoteAnswer(&recv_options);
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description(), recv_options);
- SetRemoteDescriptionWithoutError(answer);
-
- EXPECT_TRUE(nullptr == session_->voice_channel());
- EXPECT_TRUE(nullptr != session_->video_rtp_transport_channel());
-
- session_->Close();
- EXPECT_TRUE(nullptr == session_->voice_rtp_transport_channel());
- EXPECT_TRUE(nullptr == session_->voice_rtcp_transport_channel());
- EXPECT_TRUE(nullptr == session_->video_rtp_transport_channel());
- EXPECT_TRUE(nullptr == session_->video_rtcp_transport_channel());
-}
-
-// kBundlePolicyMaxBundle policy but no BUNDLE in the answer.
-TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInAnswer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
-
- // Remove BUNDLE from the answer.
- std::unique_ptr<SessionDescriptionInterface> answer(
- CreateRemoteAnswer(session_->local_description()));
- cricket::SessionDescription* answer_copy = answer->description()->Copy();
- answer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
- JsepSessionDescription* modified_answer =
- new JsepSessionDescription(JsepSessionDescription::kAnswer);
- modified_answer->Initialize(answer_copy, "1", "1");
- SetRemoteDescriptionWithoutError(modified_answer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyMaxBundle policy with BUNDLE in the remote offer.
-TEST_F(WebRtcSessionTest, TestMaxBundleBundleInRemoteOffer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
- SendAudioVideoStream1();
-
- SessionDescriptionInterface* offer = CreateRemoteOffer();
- SetRemoteDescriptionWithoutError(offer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer = CreateAnswer();
- SetLocalDescriptionWithoutError(answer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyMaxBundle policy but no BUNDLE in the remote offer.
-TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInRemoteOffer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
- SendAudioVideoStream1();
-
- // Remove BUNDLE from the offer.
- std::unique_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer());
- cricket::SessionDescription* offer_copy = offer->description()->Copy();
- offer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
- JsepSessionDescription* modified_offer =
- new JsepSessionDescription(JsepSessionDescription::kOffer);
- modified_offer->Initialize(offer_copy, "1", "1");
-
- // Expect an error when applying the remote description
- SetRemoteDescriptionExpectError(JsepSessionDescription::kOffer,
- kCreateChannelFailed, modified_offer);
-}
-
-// kBundlePolicyMaxCompat bundle policy and answer contains BUNDLE.
-TEST_F(WebRtcSessionTest, TestMaxCompatBundleInAnswer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxCompat);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions rtc_options;
- rtc_options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(rtc_options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_NE(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- // This should lead to an audio-only call but isn't implemented
- // correctly yet.
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyMaxCompat BUNDLE policy but no BUNDLE in the answer.
-TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxCompat);
- SendAudioVideoStream1();
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_NE(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-
- SendAudioVideoStream2();
-
- // Remove BUNDLE from the answer.
- std::unique_ptr<SessionDescriptionInterface> answer(
- CreateRemoteAnswer(session_->local_description()));
- cricket::SessionDescription* answer_copy = answer->description()->Copy();
- answer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
- JsepSessionDescription* modified_answer =
- new JsepSessionDescription(JsepSessionDescription::kAnswer);
- modified_answer->Initialize(answer_copy, "1", "1");
- SetRemoteDescriptionWithoutError(modified_answer); //
-
- EXPECT_NE(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// kBundlePolicyMaxbundle and then we call SetRemoteDescription first.
-TEST_F(WebRtcSessionTest, TestMaxBundleWithSetRemoteDescriptionFirst) {
- InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetRemoteDescriptionWithoutError(offer);
-
- EXPECT_EQ(session_->voice_rtp_transport_channel(),
- session_->video_rtp_transport_channel());
-}
-
-// Adding a new channel to a BUNDLE which is already connected should directly
-// assign the bundle transport to the channel, without first setting a
-// disconnected non-bundle transport and then replacing it. The application
-// should not receive any changes in the ICE state.
-TEST_F(WebRtcSessionTest, TestAddChannelToConnectedBundle) {
- AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
- // Both BUNDLE and RTCP-mux need to be enabled for the ICE state to remain
- // connected. Disabling either of these two means that we need to wait for the
- // answer to find out if more transports are needed.
- configuration_.bundle_policy =
- PeerConnectionInterface::kBundlePolicyMaxBundle;
- options_.disable_encryption = true;
- InitWithRtcpMuxPolicy(PeerConnectionInterface::kRtcpMuxPolicyRequire);
-
- // Negotiate an audio channel with MAX_BUNDLE enabled.
- SendAudioOnlyStream2();
- SessionDescriptionInterface* offer = CreateOffer();
- SetLocalDescriptionWithoutError(offer);
- EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
- observer_.ice_gathering_state_, kIceCandidatesTimeout);
- std::string sdp;
- offer->ToString(&sdp);
- SessionDescriptionInterface* answer = webrtc::CreateSessionDescription(
- JsepSessionDescription::kAnswer, sdp, nullptr);
- ASSERT_TRUE(answer != NULL);
- SetRemoteDescriptionWithoutError(answer);
-
- // Wait for the ICE state to stabilize.
- EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
- observer_.ice_connection_state_, kIceCandidatesTimeout);
- observer_.ice_connection_state_history_.clear();
-
- // Now add a video channel which should be using the same bundle transport.
- SendAudioVideoStream2();
- offer = CreateOffer();
- offer->ToString(&sdp);
- SetLocalDescriptionWithoutError(offer);
- answer = webrtc::CreateSessionDescription(JsepSessionDescription::kAnswer,
- sdp, nullptr);
- ASSERT_TRUE(answer != NULL);
- SetRemoteDescriptionWithoutError(answer);
-
- // Wait for ICE state to stabilize
- rtc::Thread::Current()->ProcessMessages(0);
- EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
- observer_.ice_connection_state_, kIceCandidatesTimeout);
-
- // No ICE state changes are expected to happen.
- EXPECT_EQ(0, observer_.ice_connection_state_history_.size());
-}
-
-TEST_F(WebRtcSessionTest, TestRequireRtcpMux) {
- InitWithRtcpMuxPolicy(PeerConnectionInterface::kRtcpMuxPolicyRequire);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
- EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
- EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
-}
-
-TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) {
- InitWithRtcpMuxPolicy(PeerConnectionInterface::kRtcpMuxPolicyNegotiate);
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- SessionDescriptionInterface* offer = CreateOffer(options);
- SetLocalDescriptionWithoutError(offer);
-
- EXPECT_TRUE(session_->voice_rtcp_transport_channel() != NULL);
- EXPECT_TRUE(session_->video_rtcp_transport_channel() != NULL);
-
- SendAudioVideoStream2();
- SessionDescriptionInterface* answer =
- CreateRemoteAnswer(session_->local_description());
- SetRemoteDescriptionWithoutError(answer);
-
- EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
- EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
-}
-
-// This test verifies that SetLocalDescription and SetRemoteDescription fails
-// if BUNDLE is enabled but rtcp-mux is disabled in m-lines.
-TEST_F(WebRtcSessionTest, TestDisabledRtcpMuxWithBundleEnabled) {
- Init();
- SendAudioVideoStream1();
-
- PeerConnectionInterface::RTCOfferAnswerOptions options;
- options.use_rtp_mux = true;
-
- SessionDescriptionInterface* offer = CreateOffer(options);
- std::string offer_str;
- offer->ToString(&offer_str);
- // Disable rtcp-mux
- const std::string rtcp_mux = "rtcp-mux";
- const std::string xrtcp_mux = "xrtcp-mux";
- rtc::replace_substrs(rtcp_mux.c_str(), rtcp_mux.length(),
- xrtcp_mux.c_str(), xrtcp_mux.length(),
- &offer_str);
- SessionDescriptionInterface* local_offer = CreateSessionDescription(
- SessionDescriptionInterface::kOffer, offer_str, nullptr);
- ASSERT_TRUE(local_offer);
- SetLocalDescriptionOfferExpectError(kBundleWithoutRtcpMux, local_offer);
-
- SessionDescriptionInterface* remote_offer = CreateSessionDescription(
- SessionDescriptionInterface::kOffer, offer_str, nullptr);
- ASSERT_TRUE(remote_offer);
- SetRemoteDescriptionOfferExpectError(kBundleWithoutRtcpMux, remote_offer);
-
- // Trying unmodified SDP.
- SetLocalDescriptionWithoutError(offer);
-}
-
TEST_F(WebRtcSessionTest, TestRtpDataChannel) {
configuration_.enable_rtp_data_channel = true;
Init();