1 /*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <memory>
12
13 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
14 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
15 #include "api/create_peerconnection_factory.h"
16 #include "api/peer_connection_proxy.h"
17 #include "api/video_codecs/builtin_video_decoder_factory.h"
18 #include "api/video_codecs/builtin_video_encoder_factory.h"
19 #include "p2p/base/fake_port_allocator.h"
20 #include "p2p/base/test_stun_server.h"
21 #include "p2p/client/basic_port_allocator.h"
22 #include "pc/media_session.h"
23 #include "pc/peer_connection.h"
24 #include "pc/peer_connection_wrapper.h"
25 #include "pc/sdp_utils.h"
26 #ifdef WEBRTC_ANDROID
27 #include "pc/test/android_test_initializer.h"
28 #endif
29 #include "pc/test/fake_audio_capture_module.h"
30 #include "rtc_base/fake_network.h"
31 #include "rtc_base/gunit.h"
32 #include "rtc_base/virtual_socket_server.h"
33 #include "test/gmock.h"
34
35 namespace webrtc {
36
37 using BundlePolicy = PeerConnectionInterface::BundlePolicy;
38 using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
39 using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
40 using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
41 using rtc::SocketAddress;
42 using ::testing::Combine;
43 using ::testing::ElementsAre;
44 using ::testing::UnorderedElementsAre;
45 using ::testing::Values;
46
47 constexpr int kDefaultTimeout = 10000;
48
49 // TODO(steveanton): These tests should be rewritten to use the standard
50 // RtpSenderInterface/DtlsTransportInterface objects once they're available in
51 // the API. The RtpSender can be used to determine which transport a given media
52 // will use: https://www.w3.org/TR/webrtc/#dom-rtcrtpsender-transport
53 // Should also be able to remove GetTransceiversForTesting at that point.
54
55 class FakeNetworkManagerWithNoAnyNetwork : public rtc::FakeNetworkManager {
56 public:
GetAnyAddressNetworks(NetworkList * networks)57 void GetAnyAddressNetworks(NetworkList* networks) override {
58 // This function allocates networks that are owned by the
59 // NetworkManager. But some tests assume that they can release
60 // all networks independent of the network manager.
61 // In order to prevent use-after-free issues, don't allow this
62 // function to have any effect when run in tests.
63 RTC_LOG(LS_INFO) << "FakeNetworkManager::GetAnyAddressNetworks ignored";
64 }
65 };
66
67 class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
68 public:
69 using PeerConnectionWrapper::PeerConnectionWrapper;
70
AddIceCandidateToMedia(cricket::Candidate * candidate,cricket::MediaType media_type)71 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
72 cricket::MediaType media_type) {
73 auto* desc = pc()->remote_description()->description();
74 for (size_t i = 0; i < desc->contents().size(); i++) {
75 const auto& content = desc->contents()[i];
76 if (content.media_description()->type() == media_type) {
77 candidate->set_transport_name(content.name);
78 std::unique_ptr<IceCandidateInterface> jsep_candidate =
79 CreateIceCandidate(content.name, i, *candidate);
80 return pc()->AddIceCandidate(jsep_candidate.get());
81 }
82 }
83 RTC_NOTREACHED();
84 return false;
85 }
86
voice_rtp_transport()87 RtpTransportInternal* voice_rtp_transport() {
88 return (voice_channel() ? voice_channel()->rtp_transport() : nullptr);
89 }
90
voice_channel()91 cricket::VoiceChannel* voice_channel() {
92 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
93 for (const auto& transceiver : transceivers) {
94 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
95 return static_cast<cricket::VoiceChannel*>(
96 transceiver->internal()->channel());
97 }
98 }
99 return nullptr;
100 }
101
video_rtp_transport()102 RtpTransportInternal* video_rtp_transport() {
103 return (video_channel() ? video_channel()->rtp_transport() : nullptr);
104 }
105
video_channel()106 cricket::VideoChannel* video_channel() {
107 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
108 for (const auto& transceiver : transceivers) {
109 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
110 return static_cast<cricket::VideoChannel*>(
111 transceiver->internal()->channel());
112 }
113 }
114 return nullptr;
115 }
116
GetInternalPeerConnection()117 PeerConnection* GetInternalPeerConnection() {
118 auto* pci =
119 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
120 pc());
121 return static_cast<PeerConnection*>(pci->internal());
122 }
123
124 // Returns true if the stats indicate that an ICE connection is either in
125 // progress or established with the given remote address.
HasConnectionWithRemoteAddress(const SocketAddress & address)126 bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
127 auto report = GetStats();
128 if (!report) {
129 return false;
130 }
131 std::string matching_candidate_id;
132 for (auto* ice_candidate_stats :
133 report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
134 if (*ice_candidate_stats->ip == address.HostAsURIString() &&
135 *ice_candidate_stats->port == address.port()) {
136 matching_candidate_id = ice_candidate_stats->id();
137 break;
138 }
139 }
140 if (matching_candidate_id.empty()) {
141 return false;
142 }
143 for (auto* pair_stats :
144 report->GetStatsOfType<RTCIceCandidatePairStats>()) {
145 if (*pair_stats->remote_candidate_id == matching_candidate_id) {
146 if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
147 *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
148 return true;
149 }
150 }
151 }
152 return false;
153 }
154
network()155 rtc::FakeNetworkManager* network() { return network_; }
156
set_network(rtc::FakeNetworkManager * network)157 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
158
159 private:
160 rtc::FakeNetworkManager* network_;
161 };
162
163 class PeerConnectionBundleBaseTest : public ::testing::Test {
164 protected:
165 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
166
PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)167 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
168 : vss_(new rtc::VirtualSocketServer()),
169 main_(vss_.get()),
170 sdp_semantics_(sdp_semantics) {
171 #ifdef WEBRTC_ANDROID
172 InitializeAndroidObjects();
173 #endif
174 pc_factory_ = CreatePeerConnectionFactory(
175 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
176 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
177 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
178 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
179 nullptr /* audio_mixer */, nullptr /* audio_processing */);
180 }
181
CreatePeerConnection()182 WrapperPtr CreatePeerConnection() {
183 return CreatePeerConnection(RTCConfiguration());
184 }
185
CreatePeerConnection(const RTCConfiguration & config)186 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
187 auto* fake_network = NewFakeNetwork();
188 auto port_allocator =
189 std::make_unique<cricket::BasicPortAllocator>(fake_network);
190 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
191 cricket::PORTALLOCATOR_DISABLE_RELAY);
192 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
193 auto observer = std::make_unique<MockPeerConnectionObserver>();
194 RTCConfiguration modified_config = config;
195 modified_config.sdp_semantics = sdp_semantics_;
196 auto pc = pc_factory_->CreatePeerConnection(
197 modified_config, std::move(port_allocator), nullptr, observer.get());
198 if (!pc) {
199 return nullptr;
200 }
201
202 auto wrapper = std::make_unique<PeerConnectionWrapperForBundleTest>(
203 pc_factory_, pc, std::move(observer));
204 wrapper->set_network(fake_network);
205 return wrapper;
206 }
207
208 // Accepts the same arguments as CreatePeerConnection and adds default audio
209 // and video tracks.
210 template <typename... Args>
CreatePeerConnectionWithAudioVideo(Args &&...args)211 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
212 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
213 if (!wrapper) {
214 return nullptr;
215 }
216 wrapper->AddAudioTrack("a");
217 wrapper->AddVideoTrack("v");
218 return wrapper;
219 }
220
CreateLocalUdpCandidate(const rtc::SocketAddress & address)221 cricket::Candidate CreateLocalUdpCandidate(
222 const rtc::SocketAddress& address) {
223 cricket::Candidate candidate;
224 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
225 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
226 candidate.set_address(address);
227 candidate.set_type(cricket::LOCAL_PORT_TYPE);
228 return candidate;
229 }
230
NewFakeNetwork()231 rtc::FakeNetworkManager* NewFakeNetwork() {
232 // The PeerConnection's port allocator is tied to the PeerConnection's
233 // lifetime and expects the underlying NetworkManager to outlive it. If
234 // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
235 // before the PeerConnection (since subclass members are destroyed before
236 // base class members). Therefore, the test fixture will own all the fake
237 // networks even though tests should access the fake network through the
238 // PeerConnectionWrapper.
239 auto* fake_network = new FakeNetworkManagerWithNoAnyNetwork();
240 fake_networks_.emplace_back(fake_network);
241 return fake_network;
242 }
243
244 std::unique_ptr<rtc::VirtualSocketServer> vss_;
245 rtc::AutoSocketServerThread main_;
246 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
247 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
248 const SdpSemantics sdp_semantics_;
249 };
250
251 class PeerConnectionBundleTest
252 : public PeerConnectionBundleBaseTest,
253 public ::testing::WithParamInterface<SdpSemantics> {
254 protected:
PeerConnectionBundleTest()255 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
256 };
257
258 class PeerConnectionBundleTestUnifiedPlan
259 : public PeerConnectionBundleBaseTest {
260 protected:
PeerConnectionBundleTestUnifiedPlan()261 PeerConnectionBundleTestUnifiedPlan()
262 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
263 };
264
RemoveRtcpMux()265 SdpContentMutator RemoveRtcpMux() {
266 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
267 content->media_description()->set_rtcp_mux(false);
268 };
269 }
270
GetCandidateComponents(const std::vector<IceCandidateInterface * > candidates)271 std::vector<int> GetCandidateComponents(
272 const std::vector<IceCandidateInterface*> candidates) {
273 std::vector<int> components;
274 components.reserve(candidates.size());
275 for (auto* candidate : candidates) {
276 components.push_back(candidate->candidate().component());
277 }
278 return components;
279 }
280
281 // Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
282 // each media section when disabling bundling and disabling RTCP multiplexing.
TEST_P(PeerConnectionBundleTest,TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux)283 TEST_P(PeerConnectionBundleTest,
284 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
285 const SocketAddress kCallerAddress("1.1.1.1", 0);
286 const SocketAddress kCalleeAddress("2.2.2.2", 0);
287
288 RTCConfiguration config;
289 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
290 auto caller = CreatePeerConnectionWithAudioVideo(config);
291 caller->network()->AddInterface(kCallerAddress);
292 auto callee = CreatePeerConnectionWithAudioVideo(config);
293 callee->network()->AddInterface(kCalleeAddress);
294
295 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
296 RTCOfferAnswerOptions options_no_bundle;
297 options_no_bundle.use_rtp_mux = false;
298 auto answer = callee->CreateAnswer(options_no_bundle);
299 SdpContentsForEach(RemoveRtcpMux(), answer->description());
300 ASSERT_TRUE(
301 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
302 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
303
304 // Check that caller has separate RTP and RTCP candidates for each media.
305 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
306 EXPECT_THAT(
307 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
308 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
309 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
310 EXPECT_THAT(
311 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
312 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
313 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
314
315 // Check that callee has separate RTP and RTCP candidates for each media.
316 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
317 EXPECT_THAT(
318 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
319 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
320 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
321 EXPECT_THAT(
322 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
323 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
324 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
325 }
326
327 // Test that there is 1 local UDP candidate for both RTP and RTCP for each media
328 // section when disabling bundle but enabling RTCP multiplexing.
TEST_P(PeerConnectionBundleTest,OneCandidateForEachTransportWhenNoBundleButRtcpMux)329 TEST_P(PeerConnectionBundleTest,
330 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
331 const SocketAddress kCallerAddress("1.1.1.1", 0);
332
333 auto caller = CreatePeerConnectionWithAudioVideo();
334 caller->network()->AddInterface(kCallerAddress);
335 auto callee = CreatePeerConnectionWithAudioVideo();
336
337 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
338 RTCOfferAnswerOptions options_no_bundle;
339 options_no_bundle.use_rtp_mux = false;
340 ASSERT_TRUE(
341 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
342
343 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
344
345 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
346 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
347 }
348
349 // Test that there is 1 local UDP candidate in only the first media section when
350 // bundling and enabling RTCP multiplexing.
TEST_P(PeerConnectionBundleTest,OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux)351 TEST_P(PeerConnectionBundleTest,
352 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
353 const SocketAddress kCallerAddress("1.1.1.1", 0);
354
355 RTCConfiguration config;
356 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
357 auto caller = CreatePeerConnectionWithAudioVideo(config);
358 caller->network()->AddInterface(kCallerAddress);
359 auto callee = CreatePeerConnectionWithAudioVideo(config);
360
361 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
362 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
363
364 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
365
366 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
367 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
368 }
369
370 // It will fail if the offerer uses the mux-BUNDLE policy but the answerer
371 // doesn't support BUNDLE.
TEST_P(PeerConnectionBundleTest,MaxBundleNotSupportedInAnswer)372 TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
373 RTCConfiguration config;
374 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
375 auto caller = CreatePeerConnectionWithAudioVideo(config);
376 auto callee = CreatePeerConnectionWithAudioVideo();
377
378 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
379 bool equal_before =
380 (caller->voice_rtp_transport() == caller->video_rtp_transport());
381 EXPECT_EQ(true, equal_before);
382 RTCOfferAnswerOptions options;
383 options.use_rtp_mux = false;
384 EXPECT_FALSE(
385 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
386 }
387
388 // The following parameterized test verifies that an offer/answer with varying
389 // bundle policies and either bundle in the answer or not will produce the
390 // expected RTP transports for audio and video. In particular, for bundling we
391 // care about whether they are separate transports or the same.
392
393 enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
operator <<(std::ostream & out,BundleIncluded value)394 std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
395 switch (value) {
396 case BundleIncluded::kBundleInAnswer:
397 return out << "bundle in answer";
398 case BundleIncluded::kBundleNotInAnswer:
399 return out << "bundle not in answer";
400 }
401 return out << "unknown";
402 }
403
404 class PeerConnectionBundleMatrixTest
405 : public PeerConnectionBundleBaseTest,
406 public ::testing::WithParamInterface<
407 std::tuple<SdpSemantics,
408 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
409 protected:
PeerConnectionBundleMatrixTest()410 PeerConnectionBundleMatrixTest()
411 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
412 auto param = std::get<1>(GetParam());
413 bundle_policy_ = std::get<0>(param);
414 bundle_included_ = std::get<1>(param);
415 expected_same_before_ = std::get<2>(param);
416 expected_same_after_ = std::get<3>(param);
417 }
418
419 PeerConnectionInterface::BundlePolicy bundle_policy_;
420 BundleIncluded bundle_included_;
421 bool expected_same_before_;
422 bool expected_same_after_;
423 };
424
TEST_P(PeerConnectionBundleMatrixTest,VerifyTransportsBeforeAndAfterSettingRemoteAnswer)425 TEST_P(PeerConnectionBundleMatrixTest,
426 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
427 RTCConfiguration config;
428 config.bundle_policy = bundle_policy_;
429 auto caller = CreatePeerConnectionWithAudioVideo(config);
430 auto callee = CreatePeerConnectionWithAudioVideo();
431
432 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
433 bool equal_before =
434 (caller->voice_rtp_transport() == caller->video_rtp_transport());
435 EXPECT_EQ(expected_same_before_, equal_before);
436
437 RTCOfferAnswerOptions options;
438 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
439 ASSERT_TRUE(
440 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
441 bool equal_after =
442 (caller->voice_rtp_transport() == caller->video_rtp_transport());
443 EXPECT_EQ(expected_same_after_, equal_after);
444 }
445
446 // The max-bundle policy means we should anticipate bundling being negotiated,
447 // and multiplex audio/video from the start.
448 // For all other policies, bundling should only be enabled if negotiated by the
449 // answer.
450 INSTANTIATE_TEST_SUITE_P(
451 PeerConnectionBundleTest,
452 PeerConnectionBundleMatrixTest,
453 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
454 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
455 BundleIncluded::kBundleInAnswer,
456 false,
457 true),
458 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
459 BundleIncluded::kBundleNotInAnswer,
460 false,
461 false),
462 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
463 BundleIncluded::kBundleInAnswer,
464 true,
465 true),
466 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
467 BundleIncluded::kBundleInAnswer,
468 false,
469 true),
470 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
471 BundleIncluded::kBundleNotInAnswer,
472 false,
473 false))));
474
475 // Test that the audio/video transports on the callee side are the same before
476 // and after setting a local answer when max BUNDLE is enabled and an offer with
477 // BUNDLE is received.
TEST_P(PeerConnectionBundleTest,TransportsSameForMaxBundleWithBundleInRemoteOffer)478 TEST_P(PeerConnectionBundleTest,
479 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
480 auto caller = CreatePeerConnectionWithAudioVideo();
481 RTCConfiguration config;
482 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
483 auto callee = CreatePeerConnectionWithAudioVideo(config);
484
485 RTCOfferAnswerOptions options_with_bundle;
486 options_with_bundle.use_rtp_mux = true;
487 ASSERT_TRUE(callee->SetRemoteDescription(
488 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
489
490 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
491
492 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
493
494 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
495 }
496
TEST_P(PeerConnectionBundleTest,FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle)497 TEST_P(PeerConnectionBundleTest,
498 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
499 auto caller = CreatePeerConnectionWithAudioVideo();
500 RTCConfiguration config;
501 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
502 auto callee = CreatePeerConnectionWithAudioVideo(config);
503
504 RTCOfferAnswerOptions options_no_bundle;
505 options_no_bundle.use_rtp_mux = false;
506 EXPECT_FALSE(callee->SetRemoteDescription(
507 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
508 }
509
510 // Test that if the media section which has the bundled transport is rejected,
511 // then the peers still connect and the bundled transport switches to the other
512 // media section.
513 // Note: This is currently failing because of the following bug:
514 // https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
TEST_P(PeerConnectionBundleTest,DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected)515 TEST_P(PeerConnectionBundleTest,
516 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
517 RTCConfiguration config;
518 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
519 auto caller = CreatePeerConnectionWithAudioVideo(config);
520 auto callee = CreatePeerConnection();
521 callee->AddVideoTrack("v");
522
523 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
524
525 RTCOfferAnswerOptions options;
526 options.offer_to_receive_audio = 0;
527 ASSERT_TRUE(
528 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
529
530 EXPECT_FALSE(caller->voice_rtp_transport());
531 EXPECT_TRUE(caller->video_rtp_transport());
532 }
533
534 // When requiring RTCP multiplexing, the PeerConnection never makes RTCP
535 // transport channels.
TEST_P(PeerConnectionBundleTest,NeverCreateRtcpTransportWithRtcpMuxRequired)536 TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
537 RTCConfiguration config;
538 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
539 auto caller = CreatePeerConnectionWithAudioVideo(config);
540 auto callee = CreatePeerConnectionWithAudioVideo();
541
542 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
543
544 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
545 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
546
547 ASSERT_TRUE(
548 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
549
550 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
551 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
552 }
553
554 // When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
555 // when the offer is sent, but will destroy them once the remote answer is set.
TEST_P(PeerConnectionBundleTest,CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate)556 TEST_P(PeerConnectionBundleTest,
557 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
558 RTCConfiguration config;
559 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
560 auto caller = CreatePeerConnectionWithAudioVideo(config);
561 auto callee = CreatePeerConnectionWithAudioVideo();
562
563 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
564
565 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
566 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
567
568 ASSERT_TRUE(
569 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
570
571 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
572 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
573 }
574
TEST_P(PeerConnectionBundleTest,FailToSetDescriptionWithBundleAndNoRtcpMux)575 TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
576 auto caller = CreatePeerConnectionWithAudioVideo();
577 auto callee = CreatePeerConnectionWithAudioVideo();
578
579 RTCOfferAnswerOptions options;
580 options.use_rtp_mux = true;
581
582 auto offer = caller->CreateOffer(options);
583 SdpContentsForEach(RemoveRtcpMux(), offer->description());
584
585 std::string error;
586 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
587 &error));
588 EXPECT_EQ(
589 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
590 "enabled.",
591 error);
592
593 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
594 EXPECT_EQ(
595 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
596 "enabled.",
597 error);
598 }
599
600 // Test that candidates sent to the "video" transport do not get pushed down to
601 // the "audio" transport channel when bundling.
TEST_P(PeerConnectionBundleTest,IgnoreCandidatesForUnusedTransportWhenBundling)602 TEST_P(PeerConnectionBundleTest,
603 IgnoreCandidatesForUnusedTransportWhenBundling) {
604 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
605 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
606 const SocketAddress kVideoAddress("3.3.3.3", 3333);
607 const SocketAddress kCallerAddress("4.4.4.4", 0);
608 const SocketAddress kCalleeAddress("5.5.5.5", 0);
609
610 auto caller = CreatePeerConnectionWithAudioVideo();
611 auto callee = CreatePeerConnectionWithAudioVideo();
612
613 caller->network()->AddInterface(kCallerAddress);
614 callee->network()->AddInterface(kCalleeAddress);
615
616 RTCOfferAnswerOptions options;
617 options.use_rtp_mux = true;
618
619 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
620 ASSERT_TRUE(
621 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
622
623 // The way the *_WAIT checks work is they only wait if the condition fails,
624 // which does not help in the case where state is not changing. This is
625 // problematic in this test since we want to verify that adding a video
626 // candidate does _not_ change state. So we interleave candidates and assume
627 // that messages are executed in the order they were posted.
628
629 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
630 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
631 cricket::MEDIA_TYPE_AUDIO));
632
633 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
634 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
635 cricket::MEDIA_TYPE_VIDEO));
636
637 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
638 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
639 cricket::MEDIA_TYPE_AUDIO));
640
641 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
642 kDefaultTimeout);
643 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
644 kDefaultTimeout);
645 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
646 }
647
648 // Test that the transport used by both audio and video is the transport
649 // associated with the first MID in the answer BUNDLE group, even if it's in a
650 // different order from the offer.
TEST_P(PeerConnectionBundleTest,BundleOnFirstMidInAnswer)651 TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
652 auto caller = CreatePeerConnectionWithAudioVideo();
653 auto callee = CreatePeerConnectionWithAudioVideo();
654
655 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
656
657 auto* old_video_transport = caller->video_rtp_transport();
658
659 auto answer = callee->CreateAnswer();
660 auto* old_bundle_group =
661 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
662 std::string first_mid = old_bundle_group->content_names()[0];
663 std::string second_mid = old_bundle_group->content_names()[1];
664 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
665
666 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
667 new_bundle_group.AddContentName(second_mid);
668 new_bundle_group.AddContentName(first_mid);
669 answer->description()->AddGroup(new_bundle_group);
670
671 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
672
673 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
674 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
675 }
676
677 // This tests that applying description with conflicted RTP demuxing criteria
678 // will fail.
TEST_P(PeerConnectionBundleTest,ApplyDescriptionWithConflictedDemuxCriteriaFail)679 TEST_P(PeerConnectionBundleTest,
680 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
681 auto caller = CreatePeerConnectionWithAudioVideo();
682 auto callee = CreatePeerConnectionWithAudioVideo();
683
684 RTCOfferAnswerOptions options;
685 options.use_rtp_mux = false;
686 auto offer = caller->CreateOffer(options);
687 // Modified the SDP to make two m= sections have the same SSRC.
688 ASSERT_GE(offer->description()->contents().size(), 2U);
689 offer->description()
690 ->contents()[0]
691 .media_description()
692 ->mutable_streams()[0]
693 .ssrcs[0] = 1111222;
694 offer->description()
695 ->contents()[1]
696 .media_description()
697 ->mutable_streams()[0]
698 .ssrcs[0] = 1111222;
699 EXPECT_TRUE(
700 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
701 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
702 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
703
704 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
705 // expectd to use one RtpTransport underneath.
706 options.use_rtp_mux = true;
707 EXPECT_TRUE(
708 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
709 auto answer = callee->CreateAnswer(options);
710 // When BUNDLE is enabled, applying the description is expected to fail
711 // because the demuxing criteria is conflicted.
712 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
713 }
714
715 // This tests that changing the pre-negotiated BUNDLE tag is not supported.
TEST_P(PeerConnectionBundleTest,RejectDescriptionChangingBundleTag)716 TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
717 RTCConfiguration config;
718 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
719 auto caller = CreatePeerConnectionWithAudioVideo(config);
720 auto callee = CreatePeerConnectionWithAudioVideo(config);
721
722 RTCOfferAnswerOptions options;
723 options.use_rtp_mux = true;
724 auto offer = caller->CreateOfferAndSetAsLocal(options);
725
726 // Create a new bundle-group with different bundled_mid.
727 auto* old_bundle_group =
728 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
729 std::string first_mid = old_bundle_group->content_names()[0];
730 std::string second_mid = old_bundle_group->content_names()[1];
731 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
732 new_bundle_group.AddContentName(second_mid);
733
734 auto re_offer = CloneSessionDescription(offer.get());
735 callee->SetRemoteDescription(std::move(offer));
736 auto answer = callee->CreateAnswer(options);
737 // Reject the first MID.
738 answer->description()->contents()[0].rejected = true;
739 // Remove the first MID from the bundle group.
740 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
741 answer->description()->AddGroup(new_bundle_group);
742 // The answer is expected to be rejected.
743 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
744
745 // Do the same thing for re-offer.
746 re_offer->description()->contents()[0].rejected = true;
747 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
748 re_offer->description()->AddGroup(new_bundle_group);
749 // The re-offer is expected to be rejected.
750 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
751 }
752
753 // This tests that removing contents from BUNDLE group and reject the whole
754 // BUNDLE group could work. This is a regression test for
755 // (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
TEST_P(PeerConnectionBundleTest,RemovingContentAndRejectBundleGroup)756 TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
757 RTCConfiguration config;
758 #ifndef HAVE_SCTP
759 config.enable_rtp_data_channel = true;
760 #endif
761 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
762 auto caller = CreatePeerConnectionWithAudioVideo(config);
763 caller->CreateDataChannel("dc");
764
765 auto offer = caller->CreateOfferAndSetAsLocal();
766 auto re_offer = CloneSessionDescription(offer.get());
767
768 // Removing the second MID from the BUNDLE group.
769 auto* old_bundle_group =
770 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
771 std::string first_mid = old_bundle_group->content_names()[0];
772 std::string third_mid = old_bundle_group->content_names()[2];
773 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
774 new_bundle_group.AddContentName(first_mid);
775 new_bundle_group.AddContentName(third_mid);
776
777 // Reject the entire new bundle group.
778 re_offer->description()->contents()[0].rejected = true;
779 re_offer->description()->contents()[2].rejected = true;
780 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
781 re_offer->description()->AddGroup(new_bundle_group);
782
783 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
784 }
785
786 // This tests that the BUNDLE group in answer should be a subset of the offered
787 // group.
TEST_P(PeerConnectionBundleTest,AddContentToBundleGroupInAnswerNotSupported)788 TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
789 auto caller = CreatePeerConnectionWithAudioVideo();
790 auto callee = CreatePeerConnectionWithAudioVideo();
791
792 auto offer = caller->CreateOffer();
793 std::string first_mid = offer->description()->contents()[0].name;
794 std::string second_mid = offer->description()->contents()[1].name;
795
796 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
797 bundle_group.AddContentName(first_mid);
798 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
799 offer->description()->AddGroup(bundle_group);
800 EXPECT_TRUE(
801 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
802 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
803
804 auto answer = callee->CreateAnswer();
805 bundle_group.AddContentName(second_mid);
806 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
807 answer->description()->AddGroup(bundle_group);
808
809 // The answer is expected to be rejected because second mid is not in the
810 // offered BUNDLE group.
811 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
812 }
813
814 // This tests that the BUNDLE group with non-existing MID should be rejectd.
TEST_P(PeerConnectionBundleTest,RejectBundleGroupWithNonExistingMid)815 TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
816 auto caller = CreatePeerConnectionWithAudioVideo();
817 auto callee = CreatePeerConnectionWithAudioVideo();
818
819 auto offer = caller->CreateOffer();
820 auto invalid_bundle_group =
821 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
822 invalid_bundle_group.AddContentName("non-existing-MID");
823 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
824 offer->description()->AddGroup(invalid_bundle_group);
825
826 EXPECT_FALSE(
827 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
828 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
829 }
830
831 // This tests that an answer shouldn't be able to remove an m= section from an
832 // established group without rejecting it.
TEST_P(PeerConnectionBundleTest,RemoveContentFromBundleGroup)833 TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
834 auto caller = CreatePeerConnectionWithAudioVideo();
835 auto callee = CreatePeerConnectionWithAudioVideo();
836
837 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
838 EXPECT_TRUE(
839 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
840
841 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
842 auto answer = callee->CreateAnswer();
843 std::string second_mid = answer->description()->contents()[1].name;
844
845 auto invalid_bundle_group =
846 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
847 invalid_bundle_group.RemoveContentName(second_mid);
848 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
849 answer->description()->AddGroup(invalid_bundle_group);
850
851 EXPECT_FALSE(
852 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
853 }
854
855 INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest,
856 PeerConnectionBundleTest,
857 Values(SdpSemantics::kPlanB,
858 SdpSemantics::kUnifiedPlan));
859
860 // According to RFC5888, if an endpoint understands the semantics of an
861 // "a=group", it MUST return an answer with that group. So, an empty BUNDLE
862 // group is valid when the answerer rejects all m= sections (by stopping all
863 // transceivers), meaning there's nothing to bundle.
864 //
865 // Only writing this test for Unified Plan mode, since there's no way to reject
866 // m= sections in answers for Plan B without SDP munging.
TEST_F(PeerConnectionBundleTestUnifiedPlan,EmptyBundleGroupCreatedInAnswerWhenAppropriate)867 TEST_F(PeerConnectionBundleTestUnifiedPlan,
868 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
869 auto caller = CreatePeerConnectionWithAudioVideo();
870 auto callee = CreatePeerConnection();
871
872 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
873
874 // Stop all transceivers, causing all m= sections to be rejected.
875 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
876 transceiver->Stop();
877 }
878 EXPECT_TRUE(
879 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
880
881 // Verify that the answer actually contained an empty bundle group.
882 const SessionDescriptionInterface* desc = callee->pc()->local_description();
883 ASSERT_NE(nullptr, desc);
884 const cricket::ContentGroup* bundle_group =
885 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
886 ASSERT_NE(nullptr, bundle_group);
887 EXPECT_TRUE(bundle_group->content_names().empty());
888 }
889
890 } // namespace webrtc
891