blob: 4a5d3733106a5ff7359d0ac11e27ff4beaa38ca9 [file] [log] [blame]
Steve Anton6f25b092017-10-23 09:39:20 -07001/*
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
Harald Alvestrandc24a2182022-02-23 13:44:59 +000011#include <stddef.h>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020012
Harald Alvestrandc24a2182022-02-23 13:44:59 +000013#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"
Karl Wiberg32df86e2017-11-03 10:24:27 +010023#include "api/audio_codecs/builtin_audio_decoder_factory.h"
24#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000025#include "api/candidate.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010026#include "api/create_peerconnection_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000027#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"
Anders Carlsson67537952018-05-03 11:28:29 +020037#include "api/video_codecs/builtin_video_decoder_factory.h"
38#include "api/video_codecs/builtin_video_encoder_factory.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000039#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"
Steve Anton10542f22019-01-11 09:11:00 -080046#include "p2p/client/basic_port_allocator.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000047#include "pc/channel.h"
Steve Anton10542f22019-01-11 09:11:00 -080048#include "pc/peer_connection.h"
Markus Handella1b82012021-05-26 18:56:30 +020049#include "pc/peer_connection_proxy.h"
Steve Anton10542f22019-01-11 09:11:00 -080050#include "pc/peer_connection_wrapper.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000051#include "pc/rtp_transceiver.h"
52#include "pc/rtp_transport_internal.h"
Steve Anton10542f22019-01-11 09:11:00 -080053#include "pc/sdp_utils.h"
Harald Alvestrandc24a2182022-02-23 13:44:59 +000054#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"
Steve Anton6f25b092017-10-23 09:39:20 -070064#ifdef WEBRTC_ANDROID
Steve Anton10542f22019-01-11 09:11:00 -080065#include "pc/test/android_test_initializer.h"
Steve Anton6f25b092017-10-23 09:39:20 -070066#endif
Steve Anton10542f22019-01-11 09:11:00 -080067#include "pc/test/fake_audio_capture_module.h"
68#include "rtc_base/fake_network.h"
Steve Anton6f25b092017-10-23 09:39:20 -070069#include "rtc_base/gunit.h"
Steve Anton10542f22019-01-11 09:11:00 -080070#include "rtc_base/virtual_socket_server.h"
Steve Anton6f25b092017-10-23 09:39:20 -070071#include "test/gmock.h"
72
73namespace webrtc {
74
75using BundlePolicy = PeerConnectionInterface::BundlePolicy;
76using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
77using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
78using RtcpMuxPolicy = PeerConnectionInterface::RtcpMuxPolicy;
79using rtc::SocketAddress;
Steve Anton7464fca2018-01-19 11:10:37 -080080using ::testing::Combine;
Steve Anton6f25b092017-10-23 09:39:20 -070081using ::testing::ElementsAre;
82using ::testing::UnorderedElementsAre;
83using ::testing::Values;
84
85constexpr 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
Steve Anton7464fca2018-01-19 11:10:37 -080091// Should also be able to remove GetTransceiversForTesting at that point.
Steve Anton6f25b092017-10-23 09:39:20 -070092
Harald Alvestrandad88c882018-11-28 16:47:46 +010093class FakeNetworkManagerWithNoAnyNetwork : public rtc::FakeNetworkManager {
94 public:
95 void GetAnyAddressNetworks(NetworkList* networks) 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 }
103};
104
Steve Anton6f25b092017-10-23 09:39:20 -0700105class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
106 public:
107 using PeerConnectionWrapper::PeerConnectionWrapper;
108
109 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
110 cricket::MediaType media_type) {
111 auto* desc = pc()->remote_description()->description();
112 for (size_t i = 0; i < desc->contents().size(); i++) {
113 const auto& content = desc->contents()[i];
Steve Antonb1c1de12017-12-21 15:14:30 -0800114 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 09:39:20 -0700115 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 15:11:53 -0700116 std::unique_ptr<IceCandidateInterface> jsep_candidate =
117 CreateIceCandidate(content.name, i, *candidate);
118 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 09:39:20 -0700119 }
120 }
Artem Titovd3251962021-11-15 16:57:07 +0100121 RTC_DCHECK_NOTREACHED();
Steve Anton6f25b092017-10-23 09:39:20 -0700122 return false;
123 }
124
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700125 RtpTransportInternal* voice_rtp_transport() {
126 return (voice_channel() ? voice_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700127 }
128
129 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800130 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +0100131 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800132 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800133 return static_cast<cricket::VoiceChannel*>(
134 transceiver->internal()->channel());
135 }
136 }
137 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700138 }
139
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700140 RtpTransportInternal* video_rtp_transport() {
141 return (video_channel() ? video_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700142 }
143
144 cricket::VideoChannel* video_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800145 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +0100146 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800147 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800148 return static_cast<cricket::VideoChannel*>(
149 transceiver->internal()->channel());
150 }
151 }
152 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700153 }
154
155 PeerConnection* GetInternalPeerConnection() {
Mirko Bonadeie97de912017-12-13 11:29:34 +0100156 auto* pci =
157 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
158 pc());
159 return static_cast<PeerConnection*>(pci->internal());
Steve Anton6f25b092017-10-23 09:39:20 -0700160 }
161
162 // Returns true if the stats indicate that an ICE connection is either in
163 // progress or established with the given remote address.
164 bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
165 auto report = GetStats();
166 if (!report) {
167 return false;
168 }
169 std::string matching_candidate_id;
170 for (auto* ice_candidate_stats :
171 report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
172 if (*ice_candidate_stats->ip == address.HostAsURIString() &&
173 *ice_candidate_stats->port == address.port()) {
174 matching_candidate_id = ice_candidate_stats->id();
175 break;
176 }
177 }
178 if (matching_candidate_id.empty()) {
179 return false;
180 }
181 for (auto* pair_stats :
182 report->GetStatsOfType<RTCIceCandidatePairStats>()) {
183 if (*pair_stats->remote_candidate_id == matching_candidate_id) {
184 if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
185 *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
186 return true;
187 }
188 }
189 }
190 return false;
191 }
192
193 rtc::FakeNetworkManager* network() { return network_; }
194
195 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
196
197 private:
198 rtc::FakeNetworkManager* network_;
199};
200
Steve Anton7464fca2018-01-19 11:10:37 -0800201class PeerConnectionBundleBaseTest : public ::testing::Test {
Steve Anton6f25b092017-10-23 09:39:20 -0700202 protected:
203 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
204
Steve Anton7464fca2018-01-19 11:10:37 -0800205 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
206 : vss_(new rtc::VirtualSocketServer()),
207 main_(vss_.get()),
208 sdp_semantics_(sdp_semantics) {
Steve Anton6f25b092017-10-23 09:39:20 -0700209#ifdef WEBRTC_ANDROID
210 InitializeAndroidObjects();
211#endif
212 pc_factory_ = CreatePeerConnectionFactory(
213 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Anders Carlsson67537952018-05-03 11:28:29 +0200214 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
215 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
216 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
217 nullptr /* audio_mixer */, nullptr /* audio_processing */);
Steve Anton6f25b092017-10-23 09:39:20 -0700218 }
219
220 WrapperPtr CreatePeerConnection() {
221 return CreatePeerConnection(RTCConfiguration());
222 }
223
224 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
225 auto* fake_network = NewFakeNetwork();
226 auto port_allocator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200227 std::make_unique<cricket::BasicPortAllocator>(fake_network);
Steve Anton6f25b092017-10-23 09:39:20 -0700228 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
229 cricket::PORTALLOCATOR_DISABLE_RELAY);
230 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200231 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton7464fca2018-01-19 11:10:37 -0800232 RTCConfiguration modified_config = config;
233 modified_config.sdp_semantics = sdp_semantics_;
Steve Anton6f25b092017-10-23 09:39:20 -0700234 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton7464fca2018-01-19 11:10:37 -0800235 modified_config, std::move(port_allocator), nullptr, observer.get());
Steve Anton6f25b092017-10-23 09:39:20 -0700236 if (!pc) {
237 return nullptr;
238 }
239
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200240 auto wrapper = std::make_unique<PeerConnectionWrapperForBundleTest>(
Steve Anton6f25b092017-10-23 09:39:20 -0700241 pc_factory_, pc, std::move(observer));
242 wrapper->set_network(fake_network);
243 return wrapper;
244 }
245
246 // Accepts the same arguments as CreatePeerConnection and adds default audio
247 // and video tracks.
248 template <typename... Args>
249 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
250 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
251 if (!wrapper) {
252 return nullptr;
253 }
254 wrapper->AddAudioTrack("a");
255 wrapper->AddVideoTrack("v");
256 return wrapper;
257 }
258
259 cricket::Candidate CreateLocalUdpCandidate(
260 const rtc::SocketAddress& address) {
261 cricket::Candidate candidate;
262 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
263 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
264 candidate.set_address(address);
265 candidate.set_type(cricket::LOCAL_PORT_TYPE);
266 return candidate;
267 }
268
269 rtc::FakeNetworkManager* NewFakeNetwork() {
270 // The PeerConnection's port allocator is tied to the PeerConnection's
271 // lifetime and expects the underlying NetworkManager to outlive it. If
272 // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
273 // before the PeerConnection (since subclass members are destroyed before
274 // base class members). Therefore, the test fixture will own all the fake
275 // networks even though tests should access the fake network through the
276 // PeerConnectionWrapper.
Harald Alvestrandad88c882018-11-28 16:47:46 +0100277 auto* fake_network = new FakeNetworkManagerWithNoAnyNetwork();
Steve Anton6f25b092017-10-23 09:39:20 -0700278 fake_networks_.emplace_back(fake_network);
279 return fake_network;
280 }
281
282 std::unique_ptr<rtc::VirtualSocketServer> vss_;
283 rtc::AutoSocketServerThread main_;
284 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
285 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
Steve Anton7464fca2018-01-19 11:10:37 -0800286 const SdpSemantics sdp_semantics_;
287};
288
289class PeerConnectionBundleTest
290 : public PeerConnectionBundleBaseTest,
291 public ::testing::WithParamInterface<SdpSemantics> {
292 protected:
293 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
Steve Anton6f25b092017-10-23 09:39:20 -0700294};
295
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700296class PeerConnectionBundleTestUnifiedPlan
297 : public PeerConnectionBundleBaseTest {
298 protected:
299 PeerConnectionBundleTestUnifiedPlan()
300 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
301};
302
Steve Anton6f25b092017-10-23 09:39:20 -0700303SdpContentMutator RemoveRtcpMux() {
304 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800305 content->media_description()->set_rtcp_mux(false);
Steve Anton6f25b092017-10-23 09:39:20 -0700306 };
307}
308
309std::vector<int> GetCandidateComponents(
310 const std::vector<IceCandidateInterface*> candidates) {
311 std::vector<int> components;
Mirko Bonadei649a4c22019-01-29 10:11:53 +0100312 components.reserve(candidates.size());
Steve Anton6f25b092017-10-23 09:39:20 -0700313 for (auto* candidate : candidates) {
314 components.push_back(candidate->candidate().component());
315 }
316 return components;
317}
318
319// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
320// each media section when disabling bundling and disabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800321TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700322 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
323 const SocketAddress kCallerAddress("1.1.1.1", 0);
324 const SocketAddress kCalleeAddress("2.2.2.2", 0);
325
326 RTCConfiguration config;
327 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
328 auto caller = CreatePeerConnectionWithAudioVideo(config);
329 caller->network()->AddInterface(kCallerAddress);
330 auto callee = CreatePeerConnectionWithAudioVideo(config);
331 callee->network()->AddInterface(kCalleeAddress);
332
333 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
334 RTCOfferAnswerOptions options_no_bundle;
335 options_no_bundle.use_rtp_mux = false;
336 auto answer = callee->CreateAnswer(options_no_bundle);
337 SdpContentsForEach(RemoveRtcpMux(), answer->description());
338 ASSERT_TRUE(
339 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
340 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
341
342 // Check that caller has separate RTP and RTCP candidates for each media.
343 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
344 EXPECT_THAT(
345 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
346 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
347 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
348 EXPECT_THAT(
349 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
350 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
351 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
352
353 // Check that callee has separate RTP and RTCP candidates for each media.
354 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
355 EXPECT_THAT(
356 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
357 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
358 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
359 EXPECT_THAT(
360 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
361 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
362 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
363}
364
365// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
366// section when disabling bundle but enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800367TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700368 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
369 const SocketAddress kCallerAddress("1.1.1.1", 0);
370
371 auto caller = CreatePeerConnectionWithAudioVideo();
372 caller->network()->AddInterface(kCallerAddress);
373 auto callee = CreatePeerConnectionWithAudioVideo();
374
375 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
376 RTCOfferAnswerOptions options_no_bundle;
377 options_no_bundle.use_rtp_mux = false;
378 ASSERT_TRUE(
379 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
380
381 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
382
383 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
384 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
385}
386
387// Test that there is 1 local UDP candidate in only the first media section when
388// bundling and enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800389TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700390 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
391 const SocketAddress kCallerAddress("1.1.1.1", 0);
392
393 RTCConfiguration config;
394 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
395 auto caller = CreatePeerConnectionWithAudioVideo(config);
396 caller->network()->AddInterface(kCallerAddress);
397 auto callee = CreatePeerConnectionWithAudioVideo(config);
398
399 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
400 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
401
402 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
403
404 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
405 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
406}
407
Zhi Huange830e682018-03-30 10:48:35 -0700408// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
409// doesn't support BUNDLE.
410TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
411 RTCConfiguration config;
412 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
413 auto caller = CreatePeerConnectionWithAudioVideo(config);
414 auto callee = CreatePeerConnectionWithAudioVideo();
415
416 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
417 bool equal_before =
418 (caller->voice_rtp_transport() == caller->video_rtp_transport());
419 EXPECT_EQ(true, equal_before);
420 RTCOfferAnswerOptions options;
421 options.use_rtp_mux = false;
422 EXPECT_FALSE(
423 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
424}
425
Steve Anton6f25b092017-10-23 09:39:20 -0700426// The following parameterized test verifies that an offer/answer with varying
427// bundle policies and either bundle in the answer or not will produce the
428// expected RTP transports for audio and video. In particular, for bundling we
429// care about whether they are separate transports or the same.
430
431enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
432std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
433 switch (value) {
434 case BundleIncluded::kBundleInAnswer:
435 return out << "bundle in answer";
436 case BundleIncluded::kBundleNotInAnswer:
437 return out << "bundle not in answer";
438 }
439 return out << "unknown";
440}
441
442class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 11:10:37 -0800443 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700444 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 11:10:37 -0800445 std::tuple<SdpSemantics,
446 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 09:39:20 -0700447 protected:
Steve Anton7464fca2018-01-19 11:10:37 -0800448 PeerConnectionBundleMatrixTest()
449 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
450 auto param = std::get<1>(GetParam());
451 bundle_policy_ = std::get<0>(param);
452 bundle_included_ = std::get<1>(param);
453 expected_same_before_ = std::get<2>(param);
454 expected_same_after_ = std::get<3>(param);
Steve Anton6f25b092017-10-23 09:39:20 -0700455 }
456
457 PeerConnectionInterface::BundlePolicy bundle_policy_;
458 BundleIncluded bundle_included_;
459 bool expected_same_before_;
460 bool expected_same_after_;
461};
462
463TEST_P(PeerConnectionBundleMatrixTest,
464 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
465 RTCConfiguration config;
466 config.bundle_policy = bundle_policy_;
467 auto caller = CreatePeerConnectionWithAudioVideo(config);
468 auto callee = CreatePeerConnectionWithAudioVideo();
469
470 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Zhi Huange830e682018-03-30 10:48:35 -0700471 bool equal_before =
472 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700473 EXPECT_EQ(expected_same_before_, equal_before);
474
475 RTCOfferAnswerOptions options;
476 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
477 ASSERT_TRUE(
478 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Zhi Huange830e682018-03-30 10:48:35 -0700479 bool equal_after =
480 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700481 EXPECT_EQ(expected_same_after_, equal_after);
482}
483
484// The max-bundle policy means we should anticipate bundling being negotiated,
485// and multiplex audio/video from the start.
486// For all other policies, bundling should only be enabled if negotiated by the
487// answer.
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100488INSTANTIATE_TEST_SUITE_P(
Steve Anton6f25b092017-10-23 09:39:20 -0700489 PeerConnectionBundleTest,
490 PeerConnectionBundleMatrixTest,
Steve Anton7464fca2018-01-19 11:10:37 -0800491 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
492 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
493 BundleIncluded::kBundleInAnswer,
494 false,
495 true),
496 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
497 BundleIncluded::kBundleNotInAnswer,
498 false,
499 false),
500 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
501 BundleIncluded::kBundleInAnswer,
502 true,
503 true),
Steve Anton7464fca2018-01-19 11:10:37 -0800504 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
505 BundleIncluded::kBundleInAnswer,
506 false,
507 true),
508 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
509 BundleIncluded::kBundleNotInAnswer,
510 false,
511 false))));
Steve Anton6f25b092017-10-23 09:39:20 -0700512
513// Test that the audio/video transports on the callee side are the same before
514// and after setting a local answer when max BUNDLE is enabled and an offer with
515// BUNDLE is received.
Steve Anton7464fca2018-01-19 11:10:37 -0800516TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700517 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
518 auto caller = CreatePeerConnectionWithAudioVideo();
519 RTCConfiguration config;
520 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
521 auto callee = CreatePeerConnectionWithAudioVideo(config);
522
523 RTCOfferAnswerOptions options_with_bundle;
524 options_with_bundle.use_rtp_mux = true;
525 ASSERT_TRUE(callee->SetRemoteDescription(
526 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
527
Zhi Huange830e682018-03-30 10:48:35 -0700528 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700529
530 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
531
Zhi Huange830e682018-03-30 10:48:35 -0700532 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700533}
534
Steve Anton7464fca2018-01-19 11:10:37 -0800535TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700536 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
537 auto caller = CreatePeerConnectionWithAudioVideo();
538 RTCConfiguration config;
539 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
540 auto callee = CreatePeerConnectionWithAudioVideo(config);
541
542 RTCOfferAnswerOptions options_no_bundle;
543 options_no_bundle.use_rtp_mux = false;
544 EXPECT_FALSE(callee->SetRemoteDescription(
545 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
546}
547
548// Test that if the media section which has the bundled transport is rejected,
549// then the peers still connect and the bundled transport switches to the other
550// media section.
551// Note: This is currently failing because of the following bug:
552// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
Steve Anton7464fca2018-01-19 11:10:37 -0800553TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700554 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
555 RTCConfiguration config;
556 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
557 auto caller = CreatePeerConnectionWithAudioVideo(config);
558 auto callee = CreatePeerConnection();
559 callee->AddVideoTrack("v");
560
561 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
562
563 RTCOfferAnswerOptions options;
564 options.offer_to_receive_audio = 0;
565 ASSERT_TRUE(
566 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
567
Zhi Huange830e682018-03-30 10:48:35 -0700568 EXPECT_FALSE(caller->voice_rtp_transport());
569 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700570}
571
572// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
573// transport channels.
Steve Anton7464fca2018-01-19 11:10:37 -0800574TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 09:39:20 -0700575 RTCConfiguration config;
576 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
577 auto caller = CreatePeerConnectionWithAudioVideo(config);
578 auto callee = CreatePeerConnectionWithAudioVideo();
579
580 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
581
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700582 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
583 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700584
585 ASSERT_TRUE(
586 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
587
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700588 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
589 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700590}
591
Zhi Huange830e682018-03-30 10:48:35 -0700592// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
593// when the offer is sent, but will destroy them once the remote answer is set.
Steve Anton7464fca2018-01-19 11:10:37 -0800594TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700595 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
596 RTCConfiguration config;
597 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
598 auto caller = CreatePeerConnectionWithAudioVideo(config);
599 auto callee = CreatePeerConnectionWithAudioVideo();
600
601 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
602
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700603 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
604 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700605
606 ASSERT_TRUE(
607 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
608
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700609 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
610 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700611}
612
Steve Anton7464fca2018-01-19 11:10:37 -0800613TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 09:39:20 -0700614 auto caller = CreatePeerConnectionWithAudioVideo();
615 auto callee = CreatePeerConnectionWithAudioVideo();
616
617 RTCOfferAnswerOptions options;
618 options.use_rtp_mux = true;
619
620 auto offer = caller->CreateOffer(options);
621 SdpContentsForEach(RemoveRtcpMux(), offer->description());
622
623 std::string error;
624 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
625 &error));
626 EXPECT_EQ(
627 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
628 "enabled.",
629 error);
630
631 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
632 EXPECT_EQ(
633 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
634 "enabled.",
635 error);
636}
637
638// Test that candidates sent to the "video" transport do not get pushed down to
639// the "audio" transport channel when bundling.
Steve Anton7464fca2018-01-19 11:10:37 -0800640TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700641 IgnoreCandidatesForUnusedTransportWhenBundling) {
642 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
643 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
644 const SocketAddress kVideoAddress("3.3.3.3", 3333);
645 const SocketAddress kCallerAddress("4.4.4.4", 0);
646 const SocketAddress kCalleeAddress("5.5.5.5", 0);
647
648 auto caller = CreatePeerConnectionWithAudioVideo();
649 auto callee = CreatePeerConnectionWithAudioVideo();
650
651 caller->network()->AddInterface(kCallerAddress);
652 callee->network()->AddInterface(kCalleeAddress);
653
654 RTCOfferAnswerOptions options;
655 options.use_rtp_mux = true;
656
657 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
658 ASSERT_TRUE(
Zhi Huange830e682018-03-30 10:48:35 -0700659 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 09:39:20 -0700660
661 // The way the *_WAIT checks work is they only wait if the condition fails,
662 // which does not help in the case where state is not changing. This is
663 // problematic in this test since we want to verify that adding a video
664 // candidate does _not_ change state. So we interleave candidates and assume
665 // that messages are executed in the order they were posted.
666
667 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
668 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
669 cricket::MEDIA_TYPE_AUDIO));
670
671 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
672 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
673 cricket::MEDIA_TYPE_VIDEO));
674
675 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
676 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
677 cricket::MEDIA_TYPE_AUDIO));
678
679 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
680 kDefaultTimeout);
681 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
682 kDefaultTimeout);
683 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
684}
685
686// Test that the transport used by both audio and video is the transport
687// associated with the first MID in the answer BUNDLE group, even if it's in a
688// different order from the offer.
Steve Anton7464fca2018-01-19 11:10:37 -0800689TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 09:39:20 -0700690 auto caller = CreatePeerConnectionWithAudioVideo();
691 auto callee = CreatePeerConnectionWithAudioVideo();
692
693 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
694
Zhi Huange830e682018-03-30 10:48:35 -0700695 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 09:39:20 -0700696
697 auto answer = callee->CreateAnswer();
698 auto* old_bundle_group =
699 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800700 std::string first_mid = old_bundle_group->content_names()[0];
701 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 09:39:20 -0700702 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
703
704 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800705 new_bundle_group.AddContentName(second_mid);
706 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 09:39:20 -0700707 answer->description()->AddGroup(new_bundle_group);
708
709 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
710
Zhi Huange830e682018-03-30 10:48:35 -0700711 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
712 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700713}
714
Zhi Huang365381f2018-04-13 16:44:34 -0700715// This tests that applying description with conflicted RTP demuxing criteria
716// will fail.
717TEST_P(PeerConnectionBundleTest,
718 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
719 auto caller = CreatePeerConnectionWithAudioVideo();
720 auto callee = CreatePeerConnectionWithAudioVideo();
721
722 RTCOfferAnswerOptions options;
723 options.use_rtp_mux = false;
724 auto offer = caller->CreateOffer(options);
725 // Modified the SDP to make two m= sections have the same SSRC.
726 ASSERT_GE(offer->description()->contents().size(), 2U);
727 offer->description()
728 ->contents()[0]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200729 .media_description()
730 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700731 .ssrcs[0] = 1111222;
732 offer->description()
733 ->contents()[1]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200734 .media_description()
735 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700736 .ssrcs[0] = 1111222;
737 EXPECT_TRUE(
738 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
739 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
740 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
741
742 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
743 // expectd to use one RtpTransport underneath.
744 options.use_rtp_mux = true;
745 EXPECT_TRUE(
746 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
747 auto answer = callee->CreateAnswer(options);
748 // When BUNDLE is enabled, applying the description is expected to fail
749 // because the demuxing criteria is conflicted.
750 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
751}
752
Zhi Huangd2248f82018-04-10 14:41:03 -0700753// This tests that changing the pre-negotiated BUNDLE tag is not supported.
754TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
755 RTCConfiguration config;
756 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
757 auto caller = CreatePeerConnectionWithAudioVideo(config);
758 auto callee = CreatePeerConnectionWithAudioVideo(config);
759
760 RTCOfferAnswerOptions options;
761 options.use_rtp_mux = true;
762 auto offer = caller->CreateOfferAndSetAsLocal(options);
763
764 // Create a new bundle-group with different bundled_mid.
765 auto* old_bundle_group =
766 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
767 std::string first_mid = old_bundle_group->content_names()[0];
768 std::string second_mid = old_bundle_group->content_names()[1];
769 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
770 new_bundle_group.AddContentName(second_mid);
771
772 auto re_offer = CloneSessionDescription(offer.get());
773 callee->SetRemoteDescription(std::move(offer));
774 auto answer = callee->CreateAnswer(options);
775 // Reject the first MID.
776 answer->description()->contents()[0].rejected = true;
777 // Remove the first MID from the bundle group.
778 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
779 answer->description()->AddGroup(new_bundle_group);
780 // The answer is expected to be rejected.
781 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
782
783 // Do the same thing for re-offer.
784 re_offer->description()->contents()[0].rejected = true;
785 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
786 re_offer->description()->AddGroup(new_bundle_group);
787 // The re-offer is expected to be rejected.
788 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
789}
790
791// This tests that removing contents from BUNDLE group and reject the whole
792// BUNDLE group could work. This is a regression test for
793// (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
Harald Alvestrandbc959b62021-04-14 18:08:36 +0000794#ifdef HAVE_SCTP
Zhi Huangd2248f82018-04-10 14:41:03 -0700795TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
796 RTCConfiguration config;
Zhi Huangd2248f82018-04-10 14:41:03 -0700797 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
798 auto caller = CreatePeerConnectionWithAudioVideo(config);
799 caller->CreateDataChannel("dc");
800
801 auto offer = caller->CreateOfferAndSetAsLocal();
802 auto re_offer = CloneSessionDescription(offer.get());
803
804 // Removing the second MID from the BUNDLE group.
805 auto* old_bundle_group =
806 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
807 std::string first_mid = old_bundle_group->content_names()[0];
808 std::string third_mid = old_bundle_group->content_names()[2];
809 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
810 new_bundle_group.AddContentName(first_mid);
811 new_bundle_group.AddContentName(third_mid);
812
813 // Reject the entire new bundle group.
814 re_offer->description()->contents()[0].rejected = true;
815 re_offer->description()->contents()[2].rejected = true;
816 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
817 re_offer->description()->AddGroup(new_bundle_group);
818
819 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
820}
Harald Alvestrandbc959b62021-04-14 18:08:36 +0000821#endif
Zhi Huangd2248f82018-04-10 14:41:03 -0700822
823// This tests that the BUNDLE group in answer should be a subset of the offered
824// group.
825TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
826 auto caller = CreatePeerConnectionWithAudioVideo();
827 auto callee = CreatePeerConnectionWithAudioVideo();
828
829 auto offer = caller->CreateOffer();
830 std::string first_mid = offer->description()->contents()[0].name;
831 std::string second_mid = offer->description()->contents()[1].name;
832
833 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
834 bundle_group.AddContentName(first_mid);
835 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
836 offer->description()->AddGroup(bundle_group);
837 EXPECT_TRUE(
838 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
839 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
840
841 auto answer = callee->CreateAnswer();
842 bundle_group.AddContentName(second_mid);
843 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
844 answer->description()->AddGroup(bundle_group);
845
846 // The answer is expected to be rejected because second mid is not in the
847 // offered BUNDLE group.
848 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
849}
850
851// This tests that the BUNDLE group with non-existing MID should be rejectd.
852TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
853 auto caller = CreatePeerConnectionWithAudioVideo();
854 auto callee = CreatePeerConnectionWithAudioVideo();
855
856 auto offer = caller->CreateOffer();
857 auto invalid_bundle_group =
858 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
859 invalid_bundle_group.AddContentName("non-existing-MID");
860 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
861 offer->description()->AddGroup(invalid_bundle_group);
862
863 EXPECT_FALSE(
864 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
865 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
866}
867
868// This tests that an answer shouldn't be able to remove an m= section from an
869// established group without rejecting it.
870TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
871 auto caller = CreatePeerConnectionWithAudioVideo();
872 auto callee = CreatePeerConnectionWithAudioVideo();
873
874 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
875 EXPECT_TRUE(
876 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
877
878 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
879 auto answer = callee->CreateAnswer();
880 std::string second_mid = answer->description()->contents()[1].name;
881
882 auto invalid_bundle_group =
883 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
884 invalid_bundle_group.RemoveContentName(second_mid);
885 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
886 answer->description()->AddGroup(invalid_bundle_group);
887
888 EXPECT_FALSE(
889 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
890}
891
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100892INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest,
893 PeerConnectionBundleTest,
894 Values(SdpSemantics::kPlanB,
895 SdpSemantics::kUnifiedPlan));
Steve Anton7464fca2018-01-19 11:10:37 -0800896
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700897// According to RFC5888, if an endpoint understands the semantics of an
898// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
899// group is valid when the answerer rejects all m= sections (by stopping all
900// transceivers), meaning there's nothing to bundle.
901//
902// Only writing this test for Unified Plan mode, since there's no way to reject
903// m= sections in answers for Plan B without SDP munging.
904TEST_F(PeerConnectionBundleTestUnifiedPlan,
905 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
906 auto caller = CreatePeerConnectionWithAudioVideo();
907 auto callee = CreatePeerConnection();
908
909 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
910
911 // Stop all transceivers, causing all m= sections to be rejected.
912 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
Harald Alvestrand6060df52020-08-11 09:54:02 +0200913 transceiver->StopInternal();
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700914 }
915 EXPECT_TRUE(
916 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
917
918 // Verify that the answer actually contained an empty bundle group.
919 const SessionDescriptionInterface* desc = callee->pc()->local_description();
920 ASSERT_NE(nullptr, desc);
921 const cricket::ContentGroup* bundle_group =
922 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
923 ASSERT_NE(nullptr, bundle_group);
924 EXPECT_TRUE(bundle_group->content_names().empty());
925}
926
Henrik Boströmf8187e02021-04-26 21:04:26 +0200927TEST_F(PeerConnectionBundleTestUnifiedPlan, MultipleBundleGroups) {
928 auto caller = CreatePeerConnection();
929 caller->AddAudioTrack("0_audio");
930 caller->AddAudioTrack("1_audio");
931 caller->AddVideoTrack("2_audio");
932 caller->AddVideoTrack("3_audio");
933 auto callee = CreatePeerConnection();
934
935 auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
936 // Modify the GROUP to have two BUNDLEs. We know that the MIDs will be 0,1,2,4
937 // because our implementation has predictable MIDs.
938 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
939 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
940 bundle_group1.AddContentName("0");
941 bundle_group1.AddContentName("1");
942 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
943 bundle_group2.AddContentName("2");
944 bundle_group2.AddContentName("3");
945 offer->description()->AddGroup(bundle_group1);
946 offer->description()->AddGroup(bundle_group2);
947
948 EXPECT_TRUE(
949 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
Taylor Brandstetter8591eff2021-08-11 14:56:38 -0700950 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Henrik Boströmf8187e02021-04-26 21:04:26 +0200951 auto answer = callee->CreateAnswer();
952 EXPECT_TRUE(
953 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
Taylor Brandstetter8591eff2021-08-11 14:56:38 -0700954 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
Henrik Boströmf8187e02021-04-26 21:04:26 +0200955
956 // Verify bundling on sender side.
957 auto senders = caller->pc()->GetSenders();
958 ASSERT_EQ(senders.size(), 4u);
959 auto sender0_transport = senders[0]->dtls_transport();
960 auto sender1_transport = senders[1]->dtls_transport();
961 auto sender2_transport = senders[2]->dtls_transport();
962 auto sender3_transport = senders[3]->dtls_transport();
963 EXPECT_EQ(sender0_transport, sender1_transport);
964 EXPECT_EQ(sender2_transport, sender3_transport);
965 EXPECT_NE(sender0_transport, sender2_transport);
966
967 // Verify bundling on receiver side.
968 auto receivers = callee->pc()->GetReceivers();
969 ASSERT_EQ(receivers.size(), 4u);
970 auto receiver0_transport = receivers[0]->dtls_transport();
971 auto receiver1_transport = receivers[1]->dtls_transport();
972 auto receiver2_transport = receivers[2]->dtls_transport();
973 auto receiver3_transport = receivers[3]->dtls_transport();
974 EXPECT_EQ(receiver0_transport, receiver1_transport);
975 EXPECT_EQ(receiver2_transport, receiver3_transport);
976 EXPECT_NE(receiver0_transport, receiver2_transport);
977}
978
Taylor Brandstetter8591eff2021-08-11 14:56:38 -0700979// Test that, with the "max-compat" bundle policy, it's possible to add an m=
980// section that's not part of an existing bundle group.
981TEST_F(PeerConnectionBundleTestUnifiedPlan, AddNonBundledSection) {
982 RTCConfiguration config;
983 config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxCompat;
984 auto caller = CreatePeerConnection(config);
985 caller->AddAudioTrack("0_audio");
986 caller->AddAudioTrack("1_audio");
987 auto callee = CreatePeerConnection(config);
988
989 // Establish an existing BUNDLE group.
990 auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
991 EXPECT_TRUE(
992 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
993 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
994 auto answer = callee->CreateAnswer();
995 EXPECT_TRUE(
996 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
997 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
998
999 // Add a track but munge SDP so it's not part of the bundle group.
1000 caller->AddAudioTrack("3_audio");
1001 offer = caller->CreateOffer(RTCOfferAnswerOptions());
1002 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1003 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1004 bundle_group.AddContentName("0");
1005 bundle_group.AddContentName("1");
1006 offer->description()->AddGroup(bundle_group);
1007 EXPECT_TRUE(
1008 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
1009 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1010 answer = callee->CreateAnswer();
1011 EXPECT_TRUE(
1012 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
1013 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
1014
1015 // Verify bundling on the sender side.
1016 auto senders = caller->pc()->GetSenders();
1017 ASSERT_EQ(senders.size(), 3u);
1018 auto sender0_transport = senders[0]->dtls_transport();
1019 auto sender1_transport = senders[1]->dtls_transport();
1020 auto sender2_transport = senders[2]->dtls_transport();
1021 EXPECT_EQ(sender0_transport, sender1_transport);
1022 EXPECT_NE(sender0_transport, sender2_transport);
1023
1024 // Verify bundling on receiver side.
1025 auto receivers = callee->pc()->GetReceivers();
1026 ASSERT_EQ(receivers.size(), 3u);
1027 auto receiver0_transport = receivers[0]->dtls_transport();
1028 auto receiver1_transport = receivers[1]->dtls_transport();
1029 auto receiver2_transport = receivers[2]->dtls_transport();
1030 EXPECT_EQ(receiver0_transport, receiver1_transport);
1031 EXPECT_NE(receiver0_transport, receiver2_transport);
1032}
1033
Steve Anton6f25b092017-10-23 09:39:20 -07001034} // namespace webrtc