blob: fed4930f43dae56406852f1b2e3cf08aa610ef6b [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:
Niels Möller22211442022-04-07 11:43:28 +020095 std::vector<const rtc::Network*> GetAnyAddressNetworks() override {
Harald Alvestrandad88c882018-11-28 16:47:46 +010096 // 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";
Niels Möller22211442022-04-07 11:43:28 +0200102 return {};
Harald Alvestrandad88c882018-11-28 16:47:46 +0100103 }
104};
105
Steve Anton6f25b092017-10-23 09:39:20 -0700106class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
107 public:
108 using PeerConnectionWrapper::PeerConnectionWrapper;
109
110 bool AddIceCandidateToMedia(cricket::Candidate* candidate,
111 cricket::MediaType media_type) {
112 auto* desc = pc()->remote_description()->description();
113 for (size_t i = 0; i < desc->contents().size(); i++) {
114 const auto& content = desc->contents()[i];
Steve Antonb1c1de12017-12-21 15:14:30 -0800115 if (content.media_description()->type() == media_type) {
Steve Anton6f25b092017-10-23 09:39:20 -0700116 candidate->set_transport_name(content.name);
Steve Anton27ab0e52018-07-23 15:11:53 -0700117 std::unique_ptr<IceCandidateInterface> jsep_candidate =
118 CreateIceCandidate(content.name, i, *candidate);
119 return pc()->AddIceCandidate(jsep_candidate.get());
Steve Anton6f25b092017-10-23 09:39:20 -0700120 }
121 }
Artem Titovd3251962021-11-15 16:57:07 +0100122 RTC_DCHECK_NOTREACHED();
Steve Anton6f25b092017-10-23 09:39:20 -0700123 return false;
124 }
125
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700126 RtpTransportInternal* voice_rtp_transport() {
127 return (voice_channel() ? voice_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700128 }
129
130 cricket::VoiceChannel* voice_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800131 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +0100132 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800133 if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800134 return static_cast<cricket::VoiceChannel*>(
135 transceiver->internal()->channel());
136 }
137 }
138 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700139 }
140
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700141 RtpTransportInternal* video_rtp_transport() {
142 return (video_channel() ? video_channel()->rtp_transport() : nullptr);
Steve Anton6f25b092017-10-23 09:39:20 -0700143 }
144
145 cricket::VideoChannel* video_channel() {
Steve Antonb8867112018-02-13 10:07:54 -0800146 auto transceivers = GetInternalPeerConnection()->GetTransceiversInternal();
Mirko Bonadei739baf02019-01-27 17:29:42 +0100147 for (const auto& transceiver : transceivers) {
Steve Anton69470252018-02-09 11:43:08 -0800148 if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton7464fca2018-01-19 11:10:37 -0800149 return static_cast<cricket::VideoChannel*>(
150 transceiver->internal()->channel());
151 }
152 }
153 return nullptr;
Steve Anton6f25b092017-10-23 09:39:20 -0700154 }
155
156 PeerConnection* GetInternalPeerConnection() {
Mirko Bonadeie97de912017-12-13 11:29:34 +0100157 auto* pci =
158 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
159 pc());
160 return static_cast<PeerConnection*>(pci->internal());
Steve Anton6f25b092017-10-23 09:39:20 -0700161 }
162
163 // Returns true if the stats indicate that an ICE connection is either in
164 // progress or established with the given remote address.
165 bool HasConnectionWithRemoteAddress(const SocketAddress& address) {
166 auto report = GetStats();
167 if (!report) {
168 return false;
169 }
170 std::string matching_candidate_id;
171 for (auto* ice_candidate_stats :
172 report->GetStatsOfType<RTCRemoteIceCandidateStats>()) {
173 if (*ice_candidate_stats->ip == address.HostAsURIString() &&
174 *ice_candidate_stats->port == address.port()) {
175 matching_candidate_id = ice_candidate_stats->id();
176 break;
177 }
178 }
179 if (matching_candidate_id.empty()) {
180 return false;
181 }
182 for (auto* pair_stats :
183 report->GetStatsOfType<RTCIceCandidatePairStats>()) {
184 if (*pair_stats->remote_candidate_id == matching_candidate_id) {
185 if (*pair_stats->state == RTCStatsIceCandidatePairState::kInProgress ||
186 *pair_stats->state == RTCStatsIceCandidatePairState::kSucceeded) {
187 return true;
188 }
189 }
190 }
191 return false;
192 }
193
194 rtc::FakeNetworkManager* network() { return network_; }
195
196 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
197
198 private:
199 rtc::FakeNetworkManager* network_;
200};
201
Steve Anton7464fca2018-01-19 11:10:37 -0800202class PeerConnectionBundleBaseTest : public ::testing::Test {
Steve Anton6f25b092017-10-23 09:39:20 -0700203 protected:
204 typedef std::unique_ptr<PeerConnectionWrapperForBundleTest> WrapperPtr;
205
Steve Anton7464fca2018-01-19 11:10:37 -0800206 explicit PeerConnectionBundleBaseTest(SdpSemantics sdp_semantics)
207 : vss_(new rtc::VirtualSocketServer()),
208 main_(vss_.get()),
209 sdp_semantics_(sdp_semantics) {
Steve Anton6f25b092017-10-23 09:39:20 -0700210#ifdef WEBRTC_ANDROID
211 InitializeAndroidObjects();
212#endif
213 pc_factory_ = CreatePeerConnectionFactory(
214 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Anders Carlsson67537952018-05-03 11:28:29 +0200215 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
216 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
217 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
218 nullptr /* audio_mixer */, nullptr /* audio_processing */);
Steve Anton6f25b092017-10-23 09:39:20 -0700219 }
220
221 WrapperPtr CreatePeerConnection() {
222 return CreatePeerConnection(RTCConfiguration());
223 }
224
225 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
226 auto* fake_network = NewFakeNetwork();
227 auto port_allocator =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200228 std::make_unique<cricket::BasicPortAllocator>(fake_network);
Steve Anton6f25b092017-10-23 09:39:20 -0700229 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
230 cricket::PORTALLOCATOR_DISABLE_RELAY);
231 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200232 auto observer = std::make_unique<MockPeerConnectionObserver>();
Steve Anton7464fca2018-01-19 11:10:37 -0800233 RTCConfiguration modified_config = config;
234 modified_config.sdp_semantics = sdp_semantics_;
Florent Castelli72424402022-04-06 03:45:10 +0200235 PeerConnectionDependencies pc_dependencies(observer.get());
236 pc_dependencies.allocator = std::move(port_allocator);
237 auto result = pc_factory_->CreatePeerConnectionOrError(
238 modified_config, std::move(pc_dependencies));
239 if (!result.ok()) {
Steve Anton6f25b092017-10-23 09:39:20 -0700240 return nullptr;
241 }
242
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200243 auto wrapper = std::make_unique<PeerConnectionWrapperForBundleTest>(
Florent Castelli72424402022-04-06 03:45:10 +0200244 pc_factory_, result.MoveValue(), std::move(observer));
Steve Anton6f25b092017-10-23 09:39:20 -0700245 wrapper->set_network(fake_network);
246 return wrapper;
247 }
248
249 // Accepts the same arguments as CreatePeerConnection and adds default audio
250 // and video tracks.
251 template <typename... Args>
252 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
253 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
254 if (!wrapper) {
255 return nullptr;
256 }
257 wrapper->AddAudioTrack("a");
258 wrapper->AddVideoTrack("v");
259 return wrapper;
260 }
261
262 cricket::Candidate CreateLocalUdpCandidate(
263 const rtc::SocketAddress& address) {
264 cricket::Candidate candidate;
265 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
266 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
267 candidate.set_address(address);
268 candidate.set_type(cricket::LOCAL_PORT_TYPE);
269 return candidate;
270 }
271
272 rtc::FakeNetworkManager* NewFakeNetwork() {
273 // The PeerConnection's port allocator is tied to the PeerConnection's
274 // lifetime and expects the underlying NetworkManager to outlive it. If
275 // PeerConnectionWrapper owned the NetworkManager, it would be destroyed
276 // before the PeerConnection (since subclass members are destroyed before
277 // base class members). Therefore, the test fixture will own all the fake
278 // networks even though tests should access the fake network through the
279 // PeerConnectionWrapper.
Harald Alvestrandad88c882018-11-28 16:47:46 +0100280 auto* fake_network = new FakeNetworkManagerWithNoAnyNetwork();
Steve Anton6f25b092017-10-23 09:39:20 -0700281 fake_networks_.emplace_back(fake_network);
282 return fake_network;
283 }
284
285 std::unique_ptr<rtc::VirtualSocketServer> vss_;
286 rtc::AutoSocketServerThread main_;
287 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
288 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
Steve Anton7464fca2018-01-19 11:10:37 -0800289 const SdpSemantics sdp_semantics_;
290};
291
292class PeerConnectionBundleTest
293 : public PeerConnectionBundleBaseTest,
294 public ::testing::WithParamInterface<SdpSemantics> {
295 protected:
296 PeerConnectionBundleTest() : PeerConnectionBundleBaseTest(GetParam()) {}
Steve Anton6f25b092017-10-23 09:39:20 -0700297};
298
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700299class PeerConnectionBundleTestUnifiedPlan
300 : public PeerConnectionBundleBaseTest {
301 protected:
302 PeerConnectionBundleTestUnifiedPlan()
303 : PeerConnectionBundleBaseTest(SdpSemantics::kUnifiedPlan) {}
304};
305
Steve Anton6f25b092017-10-23 09:39:20 -0700306SdpContentMutator RemoveRtcpMux() {
307 return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800308 content->media_description()->set_rtcp_mux(false);
Steve Anton6f25b092017-10-23 09:39:20 -0700309 };
310}
311
312std::vector<int> GetCandidateComponents(
313 const std::vector<IceCandidateInterface*> candidates) {
314 std::vector<int> components;
Mirko Bonadei649a4c22019-01-29 10:11:53 +0100315 components.reserve(candidates.size());
Steve Anton6f25b092017-10-23 09:39:20 -0700316 for (auto* candidate : candidates) {
317 components.push_back(candidate->candidate().component());
318 }
319 return components;
320}
321
322// Test that there are 2 local UDP candidates (1 RTP and 1 RTCP candidate) for
323// each media section when disabling bundling and disabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800324TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700325 TwoCandidatesForEachTransportWhenNoBundleNoRtcpMux) {
326 const SocketAddress kCallerAddress("1.1.1.1", 0);
327 const SocketAddress kCalleeAddress("2.2.2.2", 0);
328
329 RTCConfiguration config;
330 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
331 auto caller = CreatePeerConnectionWithAudioVideo(config);
332 caller->network()->AddInterface(kCallerAddress);
333 auto callee = CreatePeerConnectionWithAudioVideo(config);
334 callee->network()->AddInterface(kCalleeAddress);
335
336 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
337 RTCOfferAnswerOptions options_no_bundle;
338 options_no_bundle.use_rtp_mux = false;
339 auto answer = callee->CreateAnswer(options_no_bundle);
340 SdpContentsForEach(RemoveRtcpMux(), answer->description());
341 ASSERT_TRUE(
342 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
343 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
344
345 // Check that caller has separate RTP and RTCP candidates for each media.
346 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
347 EXPECT_THAT(
348 GetCandidateComponents(caller->observer()->GetCandidatesByMline(0)),
349 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
350 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
351 EXPECT_THAT(
352 GetCandidateComponents(caller->observer()->GetCandidatesByMline(1)),
353 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
354 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
355
356 // Check that callee has separate RTP and RTCP candidates for each media.
357 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kDefaultTimeout);
358 EXPECT_THAT(
359 GetCandidateComponents(callee->observer()->GetCandidatesByMline(0)),
360 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
361 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
362 EXPECT_THAT(
363 GetCandidateComponents(callee->observer()->GetCandidatesByMline(1)),
364 UnorderedElementsAre(cricket::ICE_CANDIDATE_COMPONENT_RTP,
365 cricket::ICE_CANDIDATE_COMPONENT_RTCP));
366}
367
368// Test that there is 1 local UDP candidate for both RTP and RTCP for each media
369// section when disabling bundle but enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800370TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700371 OneCandidateForEachTransportWhenNoBundleButRtcpMux) {
372 const SocketAddress kCallerAddress("1.1.1.1", 0);
373
374 auto caller = CreatePeerConnectionWithAudioVideo();
375 caller->network()->AddInterface(kCallerAddress);
376 auto callee = CreatePeerConnectionWithAudioVideo();
377
378 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
379 RTCOfferAnswerOptions options_no_bundle;
380 options_no_bundle.use_rtp_mux = false;
381 ASSERT_TRUE(
382 caller->SetRemoteDescription(callee->CreateAnswer(options_no_bundle)));
383
384 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
385
386 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
387 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(1).size());
388}
389
390// Test that there is 1 local UDP candidate in only the first media section when
391// bundling and enabling RTCP multiplexing.
Steve Anton7464fca2018-01-19 11:10:37 -0800392TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700393 OneCandidateOnlyOnFirstTransportWhenBundleAndRtcpMux) {
394 const SocketAddress kCallerAddress("1.1.1.1", 0);
395
396 RTCConfiguration config;
397 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
398 auto caller = CreatePeerConnectionWithAudioVideo(config);
399 caller->network()->AddInterface(kCallerAddress);
400 auto callee = CreatePeerConnectionWithAudioVideo(config);
401
402 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
403 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
404
405 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kDefaultTimeout);
406
407 EXPECT_EQ(1u, caller->observer()->GetCandidatesByMline(0).size());
408 EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
409}
410
Zhi Huange830e682018-03-30 10:48:35 -0700411// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
412// doesn't support BUNDLE.
413TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
414 RTCConfiguration config;
415 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
416 auto caller = CreatePeerConnectionWithAudioVideo(config);
417 auto callee = CreatePeerConnectionWithAudioVideo();
418
419 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
420 bool equal_before =
421 (caller->voice_rtp_transport() == caller->video_rtp_transport());
422 EXPECT_EQ(true, equal_before);
423 RTCOfferAnswerOptions options;
424 options.use_rtp_mux = false;
425 EXPECT_FALSE(
426 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
427}
428
Steve Anton6f25b092017-10-23 09:39:20 -0700429// The following parameterized test verifies that an offer/answer with varying
430// bundle policies and either bundle in the answer or not will produce the
431// expected RTP transports for audio and video. In particular, for bundling we
432// care about whether they are separate transports or the same.
433
434enum class BundleIncluded { kBundleInAnswer, kBundleNotInAnswer };
435std::ostream& operator<<(std::ostream& out, BundleIncluded value) {
436 switch (value) {
437 case BundleIncluded::kBundleInAnswer:
438 return out << "bundle in answer";
439 case BundleIncluded::kBundleNotInAnswer:
440 return out << "bundle not in answer";
441 }
442 return out << "unknown";
443}
444
445class PeerConnectionBundleMatrixTest
Steve Anton7464fca2018-01-19 11:10:37 -0800446 : public PeerConnectionBundleBaseTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700447 public ::testing::WithParamInterface<
Steve Anton7464fca2018-01-19 11:10:37 -0800448 std::tuple<SdpSemantics,
449 std::tuple<BundlePolicy, BundleIncluded, bool, bool>>> {
Steve Anton6f25b092017-10-23 09:39:20 -0700450 protected:
Steve Anton7464fca2018-01-19 11:10:37 -0800451 PeerConnectionBundleMatrixTest()
452 : PeerConnectionBundleBaseTest(std::get<0>(GetParam())) {
453 auto param = std::get<1>(GetParam());
454 bundle_policy_ = std::get<0>(param);
455 bundle_included_ = std::get<1>(param);
456 expected_same_before_ = std::get<2>(param);
457 expected_same_after_ = std::get<3>(param);
Steve Anton6f25b092017-10-23 09:39:20 -0700458 }
459
460 PeerConnectionInterface::BundlePolicy bundle_policy_;
461 BundleIncluded bundle_included_;
462 bool expected_same_before_;
463 bool expected_same_after_;
464};
465
466TEST_P(PeerConnectionBundleMatrixTest,
467 VerifyTransportsBeforeAndAfterSettingRemoteAnswer) {
468 RTCConfiguration config;
469 config.bundle_policy = bundle_policy_;
470 auto caller = CreatePeerConnectionWithAudioVideo(config);
471 auto callee = CreatePeerConnectionWithAudioVideo();
472
473 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
Zhi Huange830e682018-03-30 10:48:35 -0700474 bool equal_before =
475 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700476 EXPECT_EQ(expected_same_before_, equal_before);
477
478 RTCOfferAnswerOptions options;
479 options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
480 ASSERT_TRUE(
481 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Zhi Huange830e682018-03-30 10:48:35 -0700482 bool equal_after =
483 (caller->voice_rtp_transport() == caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700484 EXPECT_EQ(expected_same_after_, equal_after);
485}
486
487// The max-bundle policy means we should anticipate bundling being negotiated,
488// and multiplex audio/video from the start.
489// For all other policies, bundling should only be enabled if negotiated by the
490// answer.
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100491INSTANTIATE_TEST_SUITE_P(
Steve Anton6f25b092017-10-23 09:39:20 -0700492 PeerConnectionBundleTest,
493 PeerConnectionBundleMatrixTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200494 Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
Steve Anton7464fca2018-01-19 11:10:37 -0800495 Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
496 BundleIncluded::kBundleInAnswer,
497 false,
498 true),
499 std::make_tuple(BundlePolicy::kBundlePolicyBalanced,
500 BundleIncluded::kBundleNotInAnswer,
501 false,
502 false),
503 std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
504 BundleIncluded::kBundleInAnswer,
505 true,
506 true),
Steve Anton7464fca2018-01-19 11:10:37 -0800507 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
508 BundleIncluded::kBundleInAnswer,
509 false,
510 true),
511 std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
512 BundleIncluded::kBundleNotInAnswer,
513 false,
514 false))));
Steve Anton6f25b092017-10-23 09:39:20 -0700515
516// Test that the audio/video transports on the callee side are the same before
517// and after setting a local answer when max BUNDLE is enabled and an offer with
518// BUNDLE is received.
Steve Anton7464fca2018-01-19 11:10:37 -0800519TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700520 TransportsSameForMaxBundleWithBundleInRemoteOffer) {
521 auto caller = CreatePeerConnectionWithAudioVideo();
522 RTCConfiguration config;
523 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
524 auto callee = CreatePeerConnectionWithAudioVideo(config);
525
526 RTCOfferAnswerOptions options_with_bundle;
527 options_with_bundle.use_rtp_mux = true;
528 ASSERT_TRUE(callee->SetRemoteDescription(
529 caller->CreateOfferAndSetAsLocal(options_with_bundle)));
530
Zhi Huange830e682018-03-30 10:48:35 -0700531 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700532
533 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
534
Zhi Huange830e682018-03-30 10:48:35 -0700535 EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700536}
537
Steve Anton7464fca2018-01-19 11:10:37 -0800538TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700539 FailToSetRemoteOfferWithNoBundleWhenBundlePolicyMaxBundle) {
540 auto caller = CreatePeerConnectionWithAudioVideo();
541 RTCConfiguration config;
542 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
543 auto callee = CreatePeerConnectionWithAudioVideo(config);
544
545 RTCOfferAnswerOptions options_no_bundle;
546 options_no_bundle.use_rtp_mux = false;
547 EXPECT_FALSE(callee->SetRemoteDescription(
548 caller->CreateOfferAndSetAsLocal(options_no_bundle)));
549}
550
551// Test that if the media section which has the bundled transport is rejected,
552// then the peers still connect and the bundled transport switches to the other
553// media section.
554// Note: This is currently failing because of the following bug:
555// https://bugs.chromium.org/p/webrtc/issues/detail?id=6280
Steve Anton7464fca2018-01-19 11:10:37 -0800556TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700557 DISABLED_SuccessfullyNegotiateMaxBundleIfBundleTransportMediaRejected) {
558 RTCConfiguration config;
559 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
560 auto caller = CreatePeerConnectionWithAudioVideo(config);
561 auto callee = CreatePeerConnection();
562 callee->AddVideoTrack("v");
563
564 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
565
566 RTCOfferAnswerOptions options;
567 options.offer_to_receive_audio = 0;
568 ASSERT_TRUE(
569 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
570
Zhi Huange830e682018-03-30 10:48:35 -0700571 EXPECT_FALSE(caller->voice_rtp_transport());
572 EXPECT_TRUE(caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700573}
574
575// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
576// transport channels.
Steve Anton7464fca2018-01-19 11:10:37 -0800577TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
Steve Anton6f25b092017-10-23 09:39:20 -0700578 RTCConfiguration config;
579 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyRequire;
580 auto caller = CreatePeerConnectionWithAudioVideo(config);
581 auto callee = CreatePeerConnectionWithAudioVideo();
582
583 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
584
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700585 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
586 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700587
588 ASSERT_TRUE(
589 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
590
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700591 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
592 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700593}
594
Zhi Huange830e682018-03-30 10:48:35 -0700595// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
596// when the offer is sent, but will destroy them once the remote answer is set.
Steve Anton7464fca2018-01-19 11:10:37 -0800597TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700598 CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
599 RTCConfiguration config;
600 config.rtcp_mux_policy = RtcpMuxPolicy::kRtcpMuxPolicyNegotiate;
601 auto caller = CreatePeerConnectionWithAudioVideo(config);
602 auto callee = CreatePeerConnectionWithAudioVideo();
603
604 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
605
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700606 EXPECT_FALSE(caller->voice_rtp_transport()->rtcp_mux_enabled());
607 EXPECT_FALSE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700608
609 ASSERT_TRUE(
610 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
611
Bjorn A Mellem3a1b9272019-05-24 16:13:08 -0700612 EXPECT_TRUE(caller->voice_rtp_transport()->rtcp_mux_enabled());
613 EXPECT_TRUE(caller->video_rtp_transport()->rtcp_mux_enabled());
Steve Anton6f25b092017-10-23 09:39:20 -0700614}
615
Steve Anton7464fca2018-01-19 11:10:37 -0800616TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
Steve Anton6f25b092017-10-23 09:39:20 -0700617 auto caller = CreatePeerConnectionWithAudioVideo();
618 auto callee = CreatePeerConnectionWithAudioVideo();
619
620 RTCOfferAnswerOptions options;
621 options.use_rtp_mux = true;
622
623 auto offer = caller->CreateOffer(options);
624 SdpContentsForEach(RemoveRtcpMux(), offer->description());
625
626 std::string error;
627 EXPECT_FALSE(caller->SetLocalDescription(CloneSessionDescription(offer.get()),
628 &error));
629 EXPECT_EQ(
630 "Failed to set local offer sdp: rtcp-mux must be enabled when BUNDLE is "
631 "enabled.",
632 error);
633
634 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer), &error));
635 EXPECT_EQ(
636 "Failed to set remote offer sdp: rtcp-mux must be enabled when BUNDLE is "
637 "enabled.",
638 error);
639}
640
641// Test that candidates sent to the "video" transport do not get pushed down to
642// the "audio" transport channel when bundling.
Steve Anton7464fca2018-01-19 11:10:37 -0800643TEST_P(PeerConnectionBundleTest,
Steve Anton6f25b092017-10-23 09:39:20 -0700644 IgnoreCandidatesForUnusedTransportWhenBundling) {
645 const SocketAddress kAudioAddress1("1.1.1.1", 1111);
646 const SocketAddress kAudioAddress2("2.2.2.2", 2222);
647 const SocketAddress kVideoAddress("3.3.3.3", 3333);
648 const SocketAddress kCallerAddress("4.4.4.4", 0);
649 const SocketAddress kCalleeAddress("5.5.5.5", 0);
650
651 auto caller = CreatePeerConnectionWithAudioVideo();
652 auto callee = CreatePeerConnectionWithAudioVideo();
653
654 caller->network()->AddInterface(kCallerAddress);
655 callee->network()->AddInterface(kCalleeAddress);
656
657 RTCOfferAnswerOptions options;
658 options.use_rtp_mux = true;
659
660 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
661 ASSERT_TRUE(
Zhi Huange830e682018-03-30 10:48:35 -0700662 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
Steve Anton6f25b092017-10-23 09:39:20 -0700663
664 // The way the *_WAIT checks work is they only wait if the condition fails,
665 // which does not help in the case where state is not changing. This is
666 // problematic in this test since we want to verify that adding a video
667 // candidate does _not_ change state. So we interleave candidates and assume
668 // that messages are executed in the order they were posted.
669
670 cricket::Candidate audio_candidate1 = CreateLocalUdpCandidate(kAudioAddress1);
671 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate1,
672 cricket::MEDIA_TYPE_AUDIO));
673
674 cricket::Candidate video_candidate = CreateLocalUdpCandidate(kVideoAddress);
675 ASSERT_TRUE(caller->AddIceCandidateToMedia(&video_candidate,
676 cricket::MEDIA_TYPE_VIDEO));
677
678 cricket::Candidate audio_candidate2 = CreateLocalUdpCandidate(kAudioAddress2);
679 ASSERT_TRUE(caller->AddIceCandidateToMedia(&audio_candidate2,
680 cricket::MEDIA_TYPE_AUDIO));
681
682 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress1),
683 kDefaultTimeout);
684 EXPECT_TRUE_WAIT(caller->HasConnectionWithRemoteAddress(kAudioAddress2),
685 kDefaultTimeout);
686 EXPECT_FALSE(caller->HasConnectionWithRemoteAddress(kVideoAddress));
687}
688
689// Test that the transport used by both audio and video is the transport
690// associated with the first MID in the answer BUNDLE group, even if it's in a
691// different order from the offer.
Steve Anton7464fca2018-01-19 11:10:37 -0800692TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
Steve Anton6f25b092017-10-23 09:39:20 -0700693 auto caller = CreatePeerConnectionWithAudioVideo();
694 auto callee = CreatePeerConnectionWithAudioVideo();
695
696 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
697
Zhi Huange830e682018-03-30 10:48:35 -0700698 auto* old_video_transport = caller->video_rtp_transport();
Steve Anton6f25b092017-10-23 09:39:20 -0700699
700 auto answer = callee->CreateAnswer();
701 auto* old_bundle_group =
702 answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800703 std::string first_mid = old_bundle_group->content_names()[0];
704 std::string second_mid = old_bundle_group->content_names()[1];
Steve Anton6f25b092017-10-23 09:39:20 -0700705 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
706
707 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
Steve Anton7464fca2018-01-19 11:10:37 -0800708 new_bundle_group.AddContentName(second_mid);
709 new_bundle_group.AddContentName(first_mid);
Steve Anton6f25b092017-10-23 09:39:20 -0700710 answer->description()->AddGroup(new_bundle_group);
711
712 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
713
Zhi Huange830e682018-03-30 10:48:35 -0700714 EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
715 EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
Steve Anton6f25b092017-10-23 09:39:20 -0700716}
717
Zhi Huang365381f2018-04-13 16:44:34 -0700718// This tests that applying description with conflicted RTP demuxing criteria
719// will fail.
720TEST_P(PeerConnectionBundleTest,
721 ApplyDescriptionWithConflictedDemuxCriteriaFail) {
722 auto caller = CreatePeerConnectionWithAudioVideo();
723 auto callee = CreatePeerConnectionWithAudioVideo();
724
725 RTCOfferAnswerOptions options;
726 options.use_rtp_mux = false;
727 auto offer = caller->CreateOffer(options);
728 // Modified the SDP to make two m= sections have the same SSRC.
729 ASSERT_GE(offer->description()->contents().size(), 2U);
730 offer->description()
731 ->contents()[0]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200732 .media_description()
733 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700734 .ssrcs[0] = 1111222;
735 offer->description()
736 ->contents()[1]
Harald Alvestrand1716d392019-06-03 20:35:45 +0200737 .media_description()
738 ->mutable_streams()[0]
Zhi Huang365381f2018-04-13 16:44:34 -0700739 .ssrcs[0] = 1111222;
740 EXPECT_TRUE(
741 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
742 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
743 EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal(options));
744
745 // Enable BUNDLE in subsequent offer/answer exchange and two m= sections are
746 // expectd to use one RtpTransport underneath.
747 options.use_rtp_mux = true;
748 EXPECT_TRUE(
749 callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options)));
750 auto answer = callee->CreateAnswer(options);
751 // When BUNDLE is enabled, applying the description is expected to fail
752 // because the demuxing criteria is conflicted.
753 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
754}
755
Zhi Huangd2248f82018-04-10 14:41:03 -0700756// This tests that changing the pre-negotiated BUNDLE tag is not supported.
757TEST_P(PeerConnectionBundleTest, RejectDescriptionChangingBundleTag) {
758 RTCConfiguration config;
759 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
760 auto caller = CreatePeerConnectionWithAudioVideo(config);
761 auto callee = CreatePeerConnectionWithAudioVideo(config);
762
763 RTCOfferAnswerOptions options;
764 options.use_rtp_mux = true;
765 auto offer = caller->CreateOfferAndSetAsLocal(options);
766
767 // Create a new bundle-group with different bundled_mid.
768 auto* old_bundle_group =
769 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
770 std::string first_mid = old_bundle_group->content_names()[0];
771 std::string second_mid = old_bundle_group->content_names()[1];
772 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
773 new_bundle_group.AddContentName(second_mid);
774
775 auto re_offer = CloneSessionDescription(offer.get());
776 callee->SetRemoteDescription(std::move(offer));
777 auto answer = callee->CreateAnswer(options);
778 // Reject the first MID.
779 answer->description()->contents()[0].rejected = true;
780 // Remove the first MID from the bundle group.
781 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
782 answer->description()->AddGroup(new_bundle_group);
783 // The answer is expected to be rejected.
784 EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
785
786 // Do the same thing for re-offer.
787 re_offer->description()->contents()[0].rejected = true;
788 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
789 re_offer->description()->AddGroup(new_bundle_group);
790 // The re-offer is expected to be rejected.
791 EXPECT_FALSE(caller->SetLocalDescription(std::move(re_offer)));
792}
793
794// This tests that removing contents from BUNDLE group and reject the whole
795// BUNDLE group could work. This is a regression test for
796// (https://bugs.chromium.org/p/chromium/issues/detail?id=827917)
Harald Alvestrandbc959b62021-04-14 18:08:36 +0000797#ifdef HAVE_SCTP
Zhi Huangd2248f82018-04-10 14:41:03 -0700798TEST_P(PeerConnectionBundleTest, RemovingContentAndRejectBundleGroup) {
799 RTCConfiguration config;
Zhi Huangd2248f82018-04-10 14:41:03 -0700800 config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
801 auto caller = CreatePeerConnectionWithAudioVideo(config);
802 caller->CreateDataChannel("dc");
803
804 auto offer = caller->CreateOfferAndSetAsLocal();
805 auto re_offer = CloneSessionDescription(offer.get());
806
807 // Removing the second MID from the BUNDLE group.
808 auto* old_bundle_group =
809 offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
810 std::string first_mid = old_bundle_group->content_names()[0];
811 std::string third_mid = old_bundle_group->content_names()[2];
812 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
813 new_bundle_group.AddContentName(first_mid);
814 new_bundle_group.AddContentName(third_mid);
815
816 // Reject the entire new bundle group.
817 re_offer->description()->contents()[0].rejected = true;
818 re_offer->description()->contents()[2].rejected = true;
819 re_offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
820 re_offer->description()->AddGroup(new_bundle_group);
821
822 EXPECT_TRUE(caller->SetLocalDescription(std::move(re_offer)));
823}
Harald Alvestrandbc959b62021-04-14 18:08:36 +0000824#endif
Zhi Huangd2248f82018-04-10 14:41:03 -0700825
826// This tests that the BUNDLE group in answer should be a subset of the offered
827// group.
828TEST_P(PeerConnectionBundleTest, AddContentToBundleGroupInAnswerNotSupported) {
829 auto caller = CreatePeerConnectionWithAudioVideo();
830 auto callee = CreatePeerConnectionWithAudioVideo();
831
832 auto offer = caller->CreateOffer();
833 std::string first_mid = offer->description()->contents()[0].name;
834 std::string second_mid = offer->description()->contents()[1].name;
835
836 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
837 bundle_group.AddContentName(first_mid);
838 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
839 offer->description()->AddGroup(bundle_group);
840 EXPECT_TRUE(
841 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
842 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
843
844 auto answer = callee->CreateAnswer();
845 bundle_group.AddContentName(second_mid);
846 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
847 answer->description()->AddGroup(bundle_group);
848
849 // The answer is expected to be rejected because second mid is not in the
850 // offered BUNDLE group.
851 EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
852}
853
854// This tests that the BUNDLE group with non-existing MID should be rejectd.
855TEST_P(PeerConnectionBundleTest, RejectBundleGroupWithNonExistingMid) {
856 auto caller = CreatePeerConnectionWithAudioVideo();
857 auto callee = CreatePeerConnectionWithAudioVideo();
858
859 auto offer = caller->CreateOffer();
860 auto invalid_bundle_group =
861 *offer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
862 invalid_bundle_group.AddContentName("non-existing-MID");
863 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
864 offer->description()->AddGroup(invalid_bundle_group);
865
866 EXPECT_FALSE(
867 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
868 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
869}
870
871// This tests that an answer shouldn't be able to remove an m= section from an
872// established group without rejecting it.
873TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) {
874 auto caller = CreatePeerConnectionWithAudioVideo();
875 auto callee = CreatePeerConnectionWithAudioVideo();
876
877 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
878 EXPECT_TRUE(
879 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
880
881 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
882 auto answer = callee->CreateAnswer();
883 std::string second_mid = answer->description()->contents()[1].name;
884
885 auto invalid_bundle_group =
886 *answer->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
887 invalid_bundle_group.RemoveContentName(second_mid);
888 answer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
889 answer->description()->AddGroup(invalid_bundle_group);
890
891 EXPECT_FALSE(
892 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
893}
894
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100895INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest,
896 PeerConnectionBundleTest,
Florent Castelli15a38de2022-04-06 00:38:21 +0200897 Values(SdpSemantics::kPlanB_DEPRECATED,
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100898 SdpSemantics::kUnifiedPlan));
Steve Anton7464fca2018-01-19 11:10:37 -0800899
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700900// According to RFC5888, if an endpoint understands the semantics of an
901// "a=group", it MUST return an answer with that group. So, an empty BUNDLE
902// group is valid when the answerer rejects all m= sections (by stopping all
903// transceivers), meaning there's nothing to bundle.
904//
905// Only writing this test for Unified Plan mode, since there's no way to reject
906// m= sections in answers for Plan B without SDP munging.
907TEST_F(PeerConnectionBundleTestUnifiedPlan,
908 EmptyBundleGroupCreatedInAnswerWhenAppropriate) {
909 auto caller = CreatePeerConnectionWithAudioVideo();
910 auto callee = CreatePeerConnection();
911
912 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
913
914 // Stop all transceivers, causing all m= sections to be rejected.
915 for (const auto& transceiver : callee->pc()->GetTransceivers()) {
Harald Alvestrand6060df52020-08-11 09:54:02 +0200916 transceiver->StopInternal();
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700917 }
918 EXPECT_TRUE(
919 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
920
921 // Verify that the answer actually contained an empty bundle group.
922 const SessionDescriptionInterface* desc = callee->pc()->local_description();
923 ASSERT_NE(nullptr, desc);
924 const cricket::ContentGroup* bundle_group =
925 desc->description()->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
926 ASSERT_NE(nullptr, bundle_group);
927 EXPECT_TRUE(bundle_group->content_names().empty());
928}
929
Henrik Boströmf8187e02021-04-26 21:04:26 +0200930TEST_F(PeerConnectionBundleTestUnifiedPlan, MultipleBundleGroups) {
931 auto caller = CreatePeerConnection();
932 caller->AddAudioTrack("0_audio");
933 caller->AddAudioTrack("1_audio");
934 caller->AddVideoTrack("2_audio");
935 caller->AddVideoTrack("3_audio");
936 auto callee = CreatePeerConnection();
937
938 auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
939 // Modify the GROUP to have two BUNDLEs. We know that the MIDs will be 0,1,2,4
940 // because our implementation has predictable MIDs.
941 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
942 cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
943 bundle_group1.AddContentName("0");
944 bundle_group1.AddContentName("1");
945 cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
946 bundle_group2.AddContentName("2");
947 bundle_group2.AddContentName("3");
948 offer->description()->AddGroup(bundle_group1);
949 offer->description()->AddGroup(bundle_group2);
950
951 EXPECT_TRUE(
952 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
Taylor Brandstetter8591eff2021-08-11 14:56:38 -0700953 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
Henrik Boströmf8187e02021-04-26 21:04:26 +0200954 auto answer = callee->CreateAnswer();
955 EXPECT_TRUE(
956 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
Taylor Brandstetter8591eff2021-08-11 14:56:38 -0700957 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
Henrik Boströmf8187e02021-04-26 21:04:26 +0200958
959 // Verify bundling on sender side.
960 auto senders = caller->pc()->GetSenders();
961 ASSERT_EQ(senders.size(), 4u);
962 auto sender0_transport = senders[0]->dtls_transport();
963 auto sender1_transport = senders[1]->dtls_transport();
964 auto sender2_transport = senders[2]->dtls_transport();
965 auto sender3_transport = senders[3]->dtls_transport();
966 EXPECT_EQ(sender0_transport, sender1_transport);
967 EXPECT_EQ(sender2_transport, sender3_transport);
968 EXPECT_NE(sender0_transport, sender2_transport);
969
970 // Verify bundling on receiver side.
971 auto receivers = callee->pc()->GetReceivers();
972 ASSERT_EQ(receivers.size(), 4u);
973 auto receiver0_transport = receivers[0]->dtls_transport();
974 auto receiver1_transport = receivers[1]->dtls_transport();
975 auto receiver2_transport = receivers[2]->dtls_transport();
976 auto receiver3_transport = receivers[3]->dtls_transport();
977 EXPECT_EQ(receiver0_transport, receiver1_transport);
978 EXPECT_EQ(receiver2_transport, receiver3_transport);
979 EXPECT_NE(receiver0_transport, receiver2_transport);
980}
981
Taylor Brandstetter8591eff2021-08-11 14:56:38 -0700982// Test that, with the "max-compat" bundle policy, it's possible to add an m=
983// section that's not part of an existing bundle group.
984TEST_F(PeerConnectionBundleTestUnifiedPlan, AddNonBundledSection) {
985 RTCConfiguration config;
986 config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxCompat;
987 auto caller = CreatePeerConnection(config);
988 caller->AddAudioTrack("0_audio");
989 caller->AddAudioTrack("1_audio");
990 auto callee = CreatePeerConnection(config);
991
992 // Establish an existing BUNDLE group.
993 auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
994 EXPECT_TRUE(
995 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
996 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
997 auto answer = callee->CreateAnswer();
998 EXPECT_TRUE(
999 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
1000 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
1001
1002 // Add a track but munge SDP so it's not part of the bundle group.
1003 caller->AddAudioTrack("3_audio");
1004 offer = caller->CreateOffer(RTCOfferAnswerOptions());
1005 offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1006 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1007 bundle_group.AddContentName("0");
1008 bundle_group.AddContentName("1");
1009 offer->description()->AddGroup(bundle_group);
1010 EXPECT_TRUE(
1011 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
1012 EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
1013 answer = callee->CreateAnswer();
1014 EXPECT_TRUE(
1015 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
1016 EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
1017
1018 // Verify bundling on the sender side.
1019 auto senders = caller->pc()->GetSenders();
1020 ASSERT_EQ(senders.size(), 3u);
1021 auto sender0_transport = senders[0]->dtls_transport();
1022 auto sender1_transport = senders[1]->dtls_transport();
1023 auto sender2_transport = senders[2]->dtls_transport();
1024 EXPECT_EQ(sender0_transport, sender1_transport);
1025 EXPECT_NE(sender0_transport, sender2_transport);
1026
1027 // Verify bundling on receiver side.
1028 auto receivers = callee->pc()->GetReceivers();
1029 ASSERT_EQ(receivers.size(), 3u);
1030 auto receiver0_transport = receivers[0]->dtls_transport();
1031 auto receiver1_transport = receivers[1]->dtls_transport();
1032 auto receiver2_transport = receivers[2]->dtls_transport();
1033 EXPECT_EQ(receiver0_transport, receiver1_transport);
1034 EXPECT_NE(receiver0_transport, receiver2_transport);
1035}
1036
Steve Anton6f25b092017-10-23 09:39:20 -07001037} // namespace webrtc