blob: be05af920c83d3895b5b66fa008f6d038b023e33 [file] [log] [blame]
Steve Anton6f25b092017-10-23 16:39:201/*
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
Mirko Bonadei317a1f02019-09-17 15:06:1811#include <memory>
12
Karl Wiberg32df86e2017-11-03 09:24:2713#include "api/audio_codecs/builtin_audio_decoder_factory.h"
14#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Mirko Bonadei2ff3f492018-11-22 08:00:1315#include "api/create_peerconnection_factory.h"
Anders Carlsson67537952018-05-03 09:28:2916#include "api/video_codecs/builtin_video_decoder_factory.h"
17#include "api/video_codecs/builtin_video_encoder_factory.h"
Steve Anton10542f22019-01-11 17:11:0018#include "p2p/base/fake_port_allocator.h"
19#include "p2p/base/test_stun_server.h"
20#include "p2p/client/basic_port_allocator.h"
21#include "pc/media_session.h"
22#include "pc/peer_connection.h"
Markus Handella1b82012021-05-26 16:56:3023#include "pc/peer_connection_proxy.h"
Steve Anton10542f22019-01-11 17:11:0024#include "pc/peer_connection_wrapper.h"
25#include "pc/sdp_utils.h"
Steve Anton6f25b092017-10-23 16:39:2026#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 17:11:0027#include "pc/test/android_test_initializer.h"
Steve Anton6f25b092017-10-23 16:39:2028#endif
Steve Anton10542f22019-01-11 17:11:0029#include "pc/test/fake_audio_capture_module.h"
30#include "rtc_base/fake_network.h"
Steve Anton6f25b092017-10-23 16:39:2031#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 17:11:0032#include "rtc_base/virtual_socket_server.h"
Steve Anton6f25b092017-10-23 16:39:2033#include "test/gmock.h"
34
35namespace webrtc {
36
37using BundlePolicy = PeerConnectionInterface::BundlePolicy;
38using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
39using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
40using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
41using rtc::SocketAddress;
Steve Anton7464fca2018-01-19 19:10:3742using ::testing::Combine;
Steve Anton6f25b092017-10-23 16:39:2043using ::testing::ElementsAre;
44using ::testing::UnorderedElementsAre;
45using ::testing::Values;
46
47constexpr 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
Steve Anton7464fca2018-01-19 19:10:3753// Should also be able to remove GetTransceiversForTesting at that point.
Steve Anton6f25b092017-10-23 16:39:2054
Harald Alvestrandad88c882018-11-28 15:47:4655class FakeNetworkManagerWithNoAnyNetwork : public rtc::FakeNetworkManager {
56 public:
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
Steve Anton6f25b092017-10-23 16:39:2067class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
68 public:
69 using PeerConnectionWrapper::PeerConnectionWrapper;
70
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];
Steve Antonb1c1de12017-12-21 23:14:3076 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 16:39:2077 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 22:11:5378 std::unique_ptr<IceCandidateInterface> jsep_candidate =
79 CreateIceCandidate(content.name, i, *candidate);
80 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 16:39:2081 }
82 }
83 RTC_NOTREACHED();
84 return false;
85 }
86
Bjorn A Mellem3a1b9272019-05-24 23:13:0887 RtpTransportInternal* voice_rtp_transport() {
88 return (voice_channel() ? voice_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 16:39:2089 }
90
91 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 18:07:5492 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 16:29:4293 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 19:43:0894 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 19:10:3795 return static_cast<cricket::VoiceChannel*>(
96 transceiver->internal()->channel());
97 }
98 }
99 return nullptr;
Steve Anton6f25b092017-10-23 16:39:20100 }
101
Bjorn A Mellem3a1b9272019-05-24 23:13:08102 RtpTransportInternal* video_rtp_transport() {
103 return (video_channel() ? video_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 16:39:20104 }
105
106 cricket::VideoChannel* video_channel() {
Steve Antonb8867112018-02-13 18:07:54107 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 16:29:42108 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 19:43:08109 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton7464fca2018-01-19 19:10:37110 return static_cast<cricket::VideoChannel*>(
111 transceiver->internal()->channel());
112 }
113 }
114 return nullptr;
Steve Anton6f25b092017-10-23 16:39:20115 }
116
117 PeerConnection* GetInternalPeerConnection() {
Mirko Bonadeie97de912017-12-13 10:29:34118 auto* pci =
119 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
120 pc());
121 return static_cast<PeerConnection*>(pci->internal());
Steve Anton6f25b092017-10-23 16:39:20122 }
123
124 // Returns true if the stats indicate that an ICE connection is either in
125 // progress or established with the given remote 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
155 rtc::FakeNetworkManager* network() { return network_; }
156
157 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
158
159 private:
160 rtc::FakeNetworkManager* network_;
161};
162
Steve Anton7464fca2018-01-19 19:10:37163class PeerConnectionBundleBaseTest : public ::testing::Test {
Steve Anton6f25b092017-10-23 16:39:20164 protected:
165 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
166
Steve Anton7464fca2018-01-19 19:10:37167 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
168 : vss_(new rtc::VirtualSocketServer()),
169 main_(vss_.get()),
170 sdp_semantics_(sdp_semantics) {
Steve Anton6f25b092017-10-23 16:39:20171#ifdef WEBRTC_ANDROID
172 InitializeAndroidObjects();
173#endif
174 pc_factory_ = CreatePeerConnectionFactory(
175 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Anders Carlsson67537952018-05-03 09:28:29176 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
177 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
178 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
179 nullptr /* audio_mixer */, nullptr /* audio_processing */);
Steve Anton6f25b092017-10-23 16:39:20180 }
181
182 WrapperPtr CreatePeerConnection() {
183 return CreatePeerConnection(RTCConfiguration());
184 }
185
186 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
187 auto* fake_network = NewFakeNetwork();
188 auto port_allocator =
Mirko Bonadei317a1f02019-09-17 15:06:18189 std::make_unique<cricket::BasicPortAllocator>(fake_network);
Steve Anton6f25b092017-10-23 16:39:20190 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
191 cricket::PORTALLOCATOR_DISABLE_RELAY);
192 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Mirko Bonadei317a1f02019-09-17 15:06:18193 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton7464fca2018-01-19 19:10:37194 RTCConfiguration modified_config = config;
195 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6f25b092017-10-23 16:39:20196 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton7464fca2018-01-19 19:10:37197 modified_config, std::move(port_allocator), nullptr, observer.get());
Steve Anton6f25b092017-10-23 16:39:20198 if (!pc) {
199 return nullptr;
200 }
201
Mirko Bonadei317a1f02019-09-17 15:06:18202 auto wrapper = std::make_unique<PeerConnectionWrapperForBundleTest>(
Steve Anton6f25b092017-10-23 16:39:20203 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>
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
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
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.
Harald Alvestrandad88c882018-11-28 15:47:46239 auto* fake_network = new FakeNetworkManagerWithNoAnyNetwork();
Steve Anton6f25b092017-10-23 16:39:20240 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_;
Steve Anton7464fca2018-01-19 19:10:37248 const SdpSemantics sdp_semantics_;
249};
250
251class PeerConnectionBundleTest
252 : public PeerConnectionBundleBaseTest,
253 public ::testing::WithParamInterface<SdpSemantics> {
254 protected:
255 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
Steve Anton6f25b092017-10-23 16:39:20256};
257
Taylor Brandstetter0ab56512018-04-12 17:30:48258class PeerConnectionBundleTestUnifiedPlan
259 : public PeerConnectionBundleBaseTest {
260 protected:
261 PeerConnectionBundleTestUnifiedPlan()
262 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
263};
264
Steve Anton6f25b092017-10-23 16:39:20265SdpContentMutator RemoveRtcpMux() {
266 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 23:14:30267 content->media_description()->set_rtcp_mux(false);
Steve Anton6f25b092017-10-23 16:39:20268 };
269}
270
271std::vector<int> GetCandidateComponents(
272 const std::vector<IceCandidateInterface*> candidates) {
273 std::vector<int> components;
Mirko Bonadei649a4c22019-01-29 09:11:53274 components.reserve(candidates.size());
Steve Anton6f25b092017-10-23 16:39:20275 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.
Steve Anton7464fca2018-01-19 19:10:37283TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20284 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.
Steve Anton7464fca2018-01-19 19:10:37329TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20330 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.
Steve Anton7464fca2018-01-19 19:10:37351TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20352 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
Zhi Huange830e682018-03-30 17:48:35370// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
371// doesn't support BUNDLE.
372TEST_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
Steve Anton6f25b092017-10-23 16:39:20388// 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
393enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
394std::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
404class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 19:10:37405 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 16:39:20406 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 19:10:37407 std::tuple<SdpSemantics,
408 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 16:39:20409 protected:
Steve Anton7464fca2018-01-19 19:10:37410 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);
Steve Anton6f25b092017-10-23 16:39:20417 }
418
419 PeerConnectionInterface::BundlePolicy bundle_policy_;
420 BundleIncluded bundle_included_;
421 bool expected_same_before_;
422 bool expected_same_after_;
423};
424
425TEST_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()));
Zhi Huange830e682018-03-30 17:48:35433 bool equal_before =
434 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 16:39:20435 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)));
Zhi Huange830e682018-03-30 17:48:35441 bool equal_after =
442 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 16:39:20443 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.
Mirko Bonadeic84f6612019-01-31 11:20:57450INSTANTIATE_TEST_SUITE_P(
Steve Anton6f25b092017-10-23 16:39:20451 PeerConnectionBundleTest,
452 PeerConnectionBundleMatrixTest,
Steve Anton7464fca2018-01-19 19:10:37453 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),
Steve Anton7464fca2018-01-19 19:10:37466 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))));
Steve Anton6f25b092017-10-23 16:39:20474
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.
Steve Anton7464fca2018-01-19 19:10:37478TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20479 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
Zhi Huange830e682018-03-30 17:48:35490 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 16:39:20491
492 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
493
Zhi Huange830e682018-03-30 17:48:35494 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 16:39:20495}
496
Steve Anton7464fca2018-01-19 19:10:37497TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20498 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
Steve Anton7464fca2018-01-19 19:10:37515TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20516 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
Zhi Huange830e682018-03-30 17:48:35530 EXPECT_FALSE(caller->voice_rtp_transport());
531 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 16:39:20532}
533
534// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
535// transport channels.
Steve Anton7464fca2018-01-19 19:10:37536TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 16:39:20537 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
Bjorn A Mellem3a1b9272019-05-24 23:13:08544 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
545 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 16:39:20546
547 ASSERT_TRUE(
548 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
549
Bjorn A Mellem3a1b9272019-05-24 23:13:08550 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
551 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 16:39:20552}
553
Zhi Huange830e682018-03-30 17:48:35554// 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.
Steve Anton7464fca2018-01-19 19:10:37556TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20557 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
Bjorn A Mellem3a1b9272019-05-24 23:13:08565 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
566 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 16:39:20567
568 ASSERT_TRUE(
569 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
570
Bjorn A Mellem3a1b9272019-05-24 23:13:08571 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
572 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 16:39:20573}
574
Steve Anton7464fca2018-01-19 19:10:37575TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 16:39:20576 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.
Steve Anton7464fca2018-01-19 19:10:37602TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 16:39:20603 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(
Zhi Huange830e682018-03-30 17:48:35621 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 16:39:20622
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.
Steve Anton7464fca2018-01-19 19:10:37651TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 16:39:20652 auto caller = CreatePeerConnectionWithAudioVideo();
653 auto callee = CreatePeerConnectionWithAudioVideo();
654
655 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
656
Zhi Huange830e682018-03-30 17:48:35657 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 16:39:20658
659 auto answer = callee->CreateAnswer();
660 auto* old_bundle_group =
661 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 19:10:37662 std::string first_mid = old_bundle_group->content_names()[0];
663 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 16:39:20664 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
665
666 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 19:10:37667 new_bundle_group.AddContentName(second_mid);
668 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 16:39:20669 answer->description()->AddGroup(new_bundle_group);
670
671 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
672
Zhi Huange830e682018-03-30 17:48:35673 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
674 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 16:39:20675}
676
Zhi Huang365381f2018-04-13 23:44:34677// This tests that applying description with conflicted RTP demuxing criteria
678// will fail.
679TEST_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]
Harald Alvestrand1716d392019-06-03 18:35:45691 .media_description()
692 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 23:44:34693 .ssrcs[0] = 1111222;
694 offer->description()
695 ->contents()[1]
Harald Alvestrand1716d392019-06-03 18:35:45696 .media_description()
697 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 23:44:34698 .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
Zhi Huangd2248f82018-04-10 21:41:03715// This tests that changing the pre-negotiated BUNDLE tag is not supported.
716TEST_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)
Harald Alvestrandbc959b62021-04-14 18:08:36756#ifdef HAVE_SCTP
Zhi Huangd2248f82018-04-10 21:41:03757TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
758 RTCConfiguration config;
Zhi Huangd2248f82018-04-10 21:41:03759 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
760 auto caller = CreatePeerConnectionWithAudioVideo(config);
761 caller->CreateDataChannel("dc");
762
763 auto offer = caller->CreateOfferAndSetAsLocal();
764 auto re_offer = CloneSessionDescription(offer.get());
765
766 // Removing the second MID from the BUNDLE group.
767 auto* old_bundle_group =
768 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
769 std::string first_mid = old_bundle_group->content_names()[0];
770 std::string third_mid = old_bundle_group->content_names()[2];
771 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
772 new_bundle_group.AddContentName(first_mid);
773 new_bundle_group.AddContentName(third_mid);
774
775 // Reject the entire new bundle group.
776 re_offer->description()->contents()[0].rejected = true;
777 re_offer->description()->contents()[2].rejected = true;
778 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
779 re_offer->description()->AddGroup(new_bundle_group);
780
781 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
782}
Harald Alvestrandbc959b62021-04-14 18:08:36783#endif
Zhi Huangd2248f82018-04-10 21:41:03784
785// This tests that the BUNDLE group in answer should be a subset of the offered
786// group.
787TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
788 auto caller = CreatePeerConnectionWithAudioVideo();
789 auto callee = CreatePeerConnectionWithAudioVideo();
790
791 auto offer = caller->CreateOffer();
792 std::string first_mid = offer->description()->contents()[0].name;
793 std::string second_mid = offer->description()->contents()[1].name;
794
795 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
796 bundle_group.AddContentName(first_mid);
797 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
798 offer->description()->AddGroup(bundle_group);
799 EXPECT_TRUE(
800 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
801 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
802
803 auto answer = callee->CreateAnswer();
804 bundle_group.AddContentName(second_mid);
805 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
806 answer->description()->AddGroup(bundle_group);
807
808 // The answer is expected to be rejected because second mid is not in the
809 // offered BUNDLE group.
810 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
811}
812
813// This tests that the BUNDLE group with non-existing MID should be rejectd.
814TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
815 auto caller = CreatePeerConnectionWithAudioVideo();
816 auto callee = CreatePeerConnectionWithAudioVideo();
817
818 auto offer = caller->CreateOffer();
819 auto invalid_bundle_group =
820 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
821 invalid_bundle_group.AddContentName("non-existing-MID");
822 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
823 offer->description()->AddGroup(invalid_bundle_group);
824
825 EXPECT_FALSE(
826 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
827 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
828}
829
830// This tests that an answer shouldn't be able to remove an m= section from an
831// established group without rejecting it.
832TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
833 auto caller = CreatePeerConnectionWithAudioVideo();
834 auto callee = CreatePeerConnectionWithAudioVideo();
835
836 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
837 EXPECT_TRUE(
838 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
839
840 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
841 auto answer = callee->CreateAnswer();
842 std::string second_mid = answer->description()->contents()[1].name;
843
844 auto invalid_bundle_group =
845 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
846 invalid_bundle_group.RemoveContentName(second_mid);
847 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
848 answer->description()->AddGroup(invalid_bundle_group);
849
850 EXPECT_FALSE(
851 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
852}
853
Mirko Bonadeic84f6612019-01-31 11:20:57854INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest,
855 PeerConnectionBundleTest,
856 Values(SdpSemantics::kPlanB,
857 SdpSemantics::kUnifiedPlan));
Steve Anton7464fca2018-01-19 19:10:37858
Taylor Brandstetter0ab56512018-04-12 17:30:48859// According to RFC5888, if an endpoint understands the semantics of an
860// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
861// group is valid when the answerer rejects all m= sections (by stopping all
862// transceivers), meaning there's nothing to bundle.
863//
864// Only writing this test for Unified Plan mode, since there's no way to reject
865// m= sections in answers for Plan B without SDP munging.
866TEST_F(PeerConnectionBundleTestUnifiedPlan,
867 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
868 auto caller = CreatePeerConnectionWithAudioVideo();
869 auto callee = CreatePeerConnection();
870
871 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
872
873 // Stop all transceivers, causing all m= sections to be rejected.
874 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
Harald Alvestrand6060df52020-08-11 07:54:02875 transceiver->StopInternal();
Taylor Brandstetter0ab56512018-04-12 17:30:48876 }
877 EXPECT_TRUE(
878 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
879
880 // Verify that the answer actually contained an empty bundle group.
881 const SessionDescriptionInterface* desc = callee->pc()->local_description();
882 ASSERT_NE(nullptr, desc);
883 const cricket::ContentGroup* bundle_group =
884 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
885 ASSERT_NE(nullptr, bundle_group);
886 EXPECT_TRUE(bundle_group->content_names().empty());
887}
888
Henrik Boströmf8187e02021-04-26 19:04:26889TEST_F(PeerConnectionBundleTestUnifiedPlan, MultipleBundleGroups) {
890 auto caller = CreatePeerConnection();
891 caller->AddAudioTrack("0_audio");
892 caller->AddAudioTrack("1_audio");
893 caller->AddVideoTrack("2_audio");
894 caller->AddVideoTrack("3_audio");
895 auto callee = CreatePeerConnection();
896
897 auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
898 // Modify the GROUP to have two BUNDLEs. We know that the MIDs will be 0,1,2,4
899 // because our implementation has predictable MIDs.
900 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
901 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
902 bundle_group1.AddContentName("0");
903 bundle_group1.AddContentName("1");
904 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
905 bundle_group2.AddContentName("2");
906 bundle_group2.AddContentName("3");
907 offer->description()->AddGroup(bundle_group1);
908 offer->description()->AddGroup(bundle_group2);
909
910 EXPECT_TRUE(
911 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
Taylor Brandstetterd2b885f2021-07-23 00:41:55912 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Henrik Boströmf8187e02021-04-26 19:04:26913 auto answer = callee->CreateAnswer();
914 EXPECT_TRUE(
915 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
Taylor Brandstetterd2b885f2021-07-23 00:41:55916 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
Henrik Boströmf8187e02021-04-26 19:04:26917
918 // Verify bundling on sender side.
919 auto senders = caller->pc()->GetSenders();
920 ASSERT_EQ(senders.size(), 4u);
921 auto sender0_transport = senders[0]->dtls_transport();
922 auto sender1_transport = senders[1]->dtls_transport();
923 auto sender2_transport = senders[2]->dtls_transport();
924 auto sender3_transport = senders[3]->dtls_transport();
925 EXPECT_EQ(sender0_transport, sender1_transport);
926 EXPECT_EQ(sender2_transport, sender3_transport);
927 EXPECT_NE(sender0_transport, sender2_transport);
928
929 // Verify bundling on receiver side.
930 auto receivers = callee->pc()->GetReceivers();
931 ASSERT_EQ(receivers.size(), 4u);
932 auto receiver0_transport = receivers[0]->dtls_transport();
933 auto receiver1_transport = receivers[1]->dtls_transport();
934 auto receiver2_transport = receivers[2]->dtls_transport();
935 auto receiver3_transport = receivers[3]->dtls_transport();
936 EXPECT_EQ(receiver0_transport, receiver1_transport);
937 EXPECT_EQ(receiver2_transport, receiver3_transport);
938 EXPECT_NE(receiver0_transport, receiver2_transport);
939}
940
Taylor Brandstetterd2b885f2021-07-23 00:41:55941// Test that, with the "max-compat" bundle policy, it's possible to add an m=
942// section that's not part of an existing bundle group.
943TEST_F(PeerConnectionBundleTestUnifiedPlan, AddNonBundledSection) {
944 RTCConfiguration config;
945 config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxCompat;
946 auto caller = CreatePeerConnection(config);
947 caller->AddAudioTrack("0_audio");
948 caller->AddAudioTrack("1_audio");
949 auto callee = CreatePeerConnection(config);
950
951 // Establish an existing BUNDLE group.
952 auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
953 EXPECT_TRUE(
954 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
955 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
956 auto answer = callee->CreateAnswer();
957 EXPECT_TRUE(
958 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
959 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
960
961 // Add a track but munge SDP so it's not part of the bundle group.
962 caller->AddAudioTrack("3_audio");
963 offer = caller->CreateOffer(RTCOfferAnswerOptions());
964 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
965 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
966 bundle_group.AddContentName("0");
967 bundle_group.AddContentName("1");
968 offer->description()->AddGroup(bundle_group);
969 EXPECT_TRUE(
970 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
971 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
972 answer = callee->CreateAnswer();
973 EXPECT_TRUE(
974 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
975 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
976
977 // Verify bundling on the sender side.
978 auto senders = caller->pc()->GetSenders();
979 ASSERT_EQ(senders.size(), 3u);
980 auto sender0_transport = senders[0]->dtls_transport();
981 auto sender1_transport = senders[1]->dtls_transport();
982 auto sender2_transport = senders[2]->dtls_transport();
983 EXPECT_EQ(sender0_transport, sender1_transport);
984 EXPECT_NE(sender0_transport, sender2_transport);
985
986 // Verify bundling on receiver side.
987 auto receivers = callee->pc()->GetReceivers();
988 ASSERT_EQ(receivers.size(), 3u);
989 auto receiver0_transport = receivers[0]->dtls_transport();
990 auto receiver1_transport = receivers[1]->dtls_transport();
991 auto receiver2_transport = receivers[2]->dtls_transport();
992 EXPECT_EQ(receiver0_transport, receiver1_transport);
993 EXPECT_NE(receiver0_transport, receiver2_transport);
994}
995
Steve Anton6f25b092017-10-23 16:39:20996} // namespace webrtc