Reland "Rewrite WebRtcSession BUNDLE tests as PeerConnection tests"
This is a reland of b49b66109ea8a0a33a3192ebccf91366af2e49ae. Original change's description: > 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} Bug: webrtc:8222 Change-Id: If3dcd8090875c641881e2b9e92fc1db387ba1de5 Reviewed-on: https://webrtc-review.googlesource.com/14400 Commit-Queue: Steve Anton <steveanton@webrtc.org> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20397}
This commit is contained in:
@ -391,6 +391,7 @@ if (rtc_include_tests) {
|
||||
"localaudiosource_unittest.cc",
|
||||
"mediaconstraintsinterface_unittest.cc",
|
||||
"mediastream_unittest.cc",
|
||||
"peerconnection_bundle_unittest.cc",
|
||||
"peerconnection_crypto_unittest.cc",
|
||||
"peerconnection_ice_unittest.cc",
|
||||
"peerconnection_integrationtest.cc",
|
||||
|
||||
616
pc/peerconnection_bundle_unittest.cc
Normal file
616
pc/peerconnection_bundle_unittest.cc
Normal file
@ -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
|
||||
@ -291,6 +291,9 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
||||
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 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithIceRenomination) {
|
||||
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.
|
||||
|
||||
@ -944,7 +944,7 @@ class PeerConnectionInterfaceTest : public testing::Test {
|
||||
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 @@ TEST_F(PeerConnectionInterfaceTest, IceCandidates) {
|
||||
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()));
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
const uint32_t kWaitTimeout = 10000U;
|
||||
const uint32_t kDefaultTimeout = 10000U;
|
||||
}
|
||||
|
||||
PeerConnectionWrapper::PeerConnectionWrapper(
|
||||
@ -122,7 +122,7 @@ std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateSdp(
|
||||
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 @@ bool PeerConnectionWrapper::SetSdp(
|
||||
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 @@ PeerConnectionWrapper::signaling_state() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -107,6 +107,13 @@ class PeerConnectionWrapper {
|
||||
// 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,
|
||||
|
||||
@ -73,12 +73,15 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
|
||||
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 @@ class MockPeerConnectionObserver : public PeerConnectionObserver {
|
||||
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_;
|
||||
|
||||
@ -56,8 +56,9 @@ using cricket::PRFLX_PORT_TYPE;
|
||||
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.";
|
||||
|
||||
@ -83,11 +83,9 @@ static const char kSessionVersion[] = "1";
|
||||
|
||||
// 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 @@ class WebRtcSessionTest
|
||||
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 @@ class WebRtcSessionTest
|
||||
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 @@ class WebRtcSessionTest
|
||||
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 @@ TEST_P(WebRtcSessionTest, TestCreateAnswerWithDifferentSslRoles) {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user