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