blob: b6a8124b2c4ac50053ec9284d365760ac045cead [file] [log] [blame]
Steve Antonf1c6db12017-10-13 11:13:35 -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
11#include "p2p/base/fakeportallocator.h"
12#include "p2p/base/teststunserver.h"
13#include "p2p/client/basicportallocator.h"
14#include "pc/mediasession.h"
Qingsi Wange1692722017-11-29 13:27:20 -080015#include "pc/peerconnection.h"
Steve Antonf1c6db12017-10-13 11:13:35 -070016#include "pc/peerconnectionwrapper.h"
17#include "pc/sdputils.h"
18#ifdef WEBRTC_ANDROID
19#include "pc/test/androidtestinitializer.h"
20#endif
Karl Wiberg1b0eae32017-10-17 14:48:54 +020021#include "api/audio_codecs/builtin_audio_decoder_factory.h"
22#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Qingsi Wange1692722017-11-29 13:27:20 -080023#include "api/peerconnectionproxy.h"
Steve Antonf1c6db12017-10-13 11:13:35 -070024#include "pc/test/fakeaudiocapturemodule.h"
25#include "rtc_base/fakenetwork.h"
26#include "rtc_base/gunit.h"
27#include "rtc_base/ptr_util.h"
28#include "rtc_base/virtualsocketserver.h"
29
30namespace webrtc {
31
32using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
33using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
34using rtc::SocketAddress;
Steve Anton46d926a2018-01-23 10:23:06 -080035using ::testing::Combine;
Steve Antonf1c6db12017-10-13 11:13:35 -070036using ::testing::Values;
37
38constexpr int kIceCandidatesTimeout = 10000;
39
Steve Anton46d926a2018-01-23 10:23:06 -080040class PeerConnectionWrapperForIceTest : public PeerConnectionWrapper {
Steve Antonf1c6db12017-10-13 11:13:35 -070041 public:
42 using PeerConnectionWrapper::PeerConnectionWrapper;
43
44 // Adds a new ICE candidate to the first transport.
45 bool AddIceCandidate(cricket::Candidate* candidate) {
46 RTC_DCHECK(pc()->remote_description());
47 const auto* desc = pc()->remote_description()->description();
48 RTC_DCHECK(desc->contents().size() > 0);
49 const auto& first_content = desc->contents()[0];
50 candidate->set_transport_name(first_content.name);
51 JsepIceCandidate jsep_candidate(first_content.name, 0, *candidate);
52 return pc()->AddIceCandidate(&jsep_candidate);
53 }
54
55 // Returns ICE candidates from the remote session description.
56 std::vector<const IceCandidateInterface*>
57 GetIceCandidatesFromRemoteDescription() {
58 const SessionDescriptionInterface* sdesc = pc()->remote_description();
59 RTC_DCHECK(sdesc);
60 std::vector<const IceCandidateInterface*> candidates;
61 for (size_t mline_index = 0; mline_index < sdesc->number_of_mediasections();
62 mline_index++) {
63 const auto* candidate_collection = sdesc->candidates(mline_index);
64 for (size_t i = 0; i < candidate_collection->count(); i++) {
65 candidates.push_back(candidate_collection->at(i));
66 }
67 }
68 return candidates;
69 }
70
71 rtc::FakeNetworkManager* network() { return network_; }
72
73 void set_network(rtc::FakeNetworkManager* network) { network_ = network; }
74
75 private:
76 rtc::FakeNetworkManager* network_;
77};
78
Steve Anton46d926a2018-01-23 10:23:06 -080079class PeerConnectionIceBaseTest : public ::testing::Test {
Steve Antonf1c6db12017-10-13 11:13:35 -070080 protected:
Steve Anton46d926a2018-01-23 10:23:06 -080081 typedef std::unique_ptr<PeerConnectionWrapperForIceTest> WrapperPtr;
Steve Antonf1c6db12017-10-13 11:13:35 -070082
Steve Anton46d926a2018-01-23 10:23:06 -080083 explicit PeerConnectionIceBaseTest(SdpSemantics sdp_semantics)
84 : vss_(new rtc::VirtualSocketServer()),
85 main_(vss_.get()),
86 sdp_semantics_(sdp_semantics) {
Steve Antonf1c6db12017-10-13 11:13:35 -070087#ifdef WEBRTC_ANDROID
88 InitializeAndroidObjects();
89#endif
90 pc_factory_ = CreatePeerConnectionFactory(
91 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
Karl Wiberg1b0eae32017-10-17 14:48:54 +020092 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
93 CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
Steve Antonf1c6db12017-10-13 11:13:35 -070094 }
95
96 WrapperPtr CreatePeerConnection() {
97 return CreatePeerConnection(RTCConfiguration());
98 }
99
100 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
101 auto* fake_network = NewFakeNetwork();
102 auto port_allocator =
103 rtc::MakeUnique<cricket::BasicPortAllocator>(fake_network);
104 port_allocator->set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
105 cricket::PORTALLOCATOR_DISABLE_RELAY);
106 port_allocator->set_step_delay(cricket::kMinimumStepDelay);
Steve Anton46d926a2018-01-23 10:23:06 -0800107 RTCConfiguration modified_config = config;
108 modified_config.sdp_semantics = sdp_semantics_;
Steve Antonf1c6db12017-10-13 11:13:35 -0700109 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
110 auto pc = pc_factory_->CreatePeerConnection(
Steve Anton46d926a2018-01-23 10:23:06 -0800111 modified_config, std::move(port_allocator), nullptr, observer.get());
Steve Antonf1c6db12017-10-13 11:13:35 -0700112 if (!pc) {
113 return nullptr;
114 }
115
Steve Anton46d926a2018-01-23 10:23:06 -0800116 auto wrapper = rtc::MakeUnique<PeerConnectionWrapperForIceTest>(
Steve Antonf1c6db12017-10-13 11:13:35 -0700117 pc_factory_, pc, std::move(observer));
118 wrapper->set_network(fake_network);
119 return wrapper;
120 }
121
122 // Accepts the same arguments as CreatePeerConnection and adds default audio
123 // and video tracks.
124 template <typename... Args>
125 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
126 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
127 if (!wrapper) {
128 return nullptr;
129 }
Steve Anton8d3444d2017-10-20 15:30:51 -0700130 wrapper->AddAudioTrack("a");
131 wrapper->AddVideoTrack("v");
Steve Antonf1c6db12017-10-13 11:13:35 -0700132 return wrapper;
133 }
134
135 cricket::Candidate CreateLocalUdpCandidate(
136 const rtc::SocketAddress& address) {
137 cricket::Candidate candidate;
138 candidate.set_component(cricket::ICE_CANDIDATE_COMPONENT_DEFAULT);
139 candidate.set_protocol(cricket::UDP_PROTOCOL_NAME);
140 candidate.set_address(address);
141 candidate.set_type(cricket::LOCAL_PORT_TYPE);
142 return candidate;
143 }
144
145 // Remove all ICE ufrag/pwd lines from the given session description.
146 void RemoveIceUfragPwd(SessionDescriptionInterface* sdesc) {
147 SetIceUfragPwd(sdesc, "", "");
148 }
149
150 // Sets all ICE ufrag/pwds on the given session description.
151 void SetIceUfragPwd(SessionDescriptionInterface* sdesc,
152 const std::string& ufrag,
153 const std::string& pwd) {
154 auto* desc = sdesc->description();
155 for (const auto& content : desc->contents()) {
156 auto* transport_info = desc->GetTransportInfoByName(content.name);
157 transport_info->description.ice_ufrag = ufrag;
158 transport_info->description.ice_pwd = pwd;
159 }
160 }
161
Qingsi Wange1692722017-11-29 13:27:20 -0800162 // Set ICE mode on the given session description.
163 void SetIceMode(SessionDescriptionInterface* sdesc,
164 const cricket::IceMode ice_mode) {
165 auto* desc = sdesc->description();
166 for (const auto& content : desc->contents()) {
167 auto* transport_info = desc->GetTransportInfoByName(content.name);
168 transport_info->description.ice_mode = ice_mode;
169 }
170 }
171
Steve Antonf1c6db12017-10-13 11:13:35 -0700172 cricket::TransportDescription* GetFirstTransportDescription(
173 SessionDescriptionInterface* sdesc) {
174 auto* desc = sdesc->description();
175 RTC_DCHECK(desc->contents().size() > 0);
176 auto* transport_info =
177 desc->GetTransportInfoByName(desc->contents()[0].name);
178 RTC_DCHECK(transport_info);
179 return &transport_info->description;
180 }
181
182 const cricket::TransportDescription* GetFirstTransportDescription(
183 const SessionDescriptionInterface* sdesc) {
184 auto* desc = sdesc->description();
185 RTC_DCHECK(desc->contents().size() > 0);
186 auto* transport_info =
187 desc->GetTransportInfoByName(desc->contents()[0].name);
188 RTC_DCHECK(transport_info);
189 return &transport_info->description;
190 }
191
Qingsi Wange1692722017-11-29 13:27:20 -0800192 // TODO(qingsi): Rewrite this method in terms of the standard IceTransport
193 // after it is implemented.
194 cricket::IceRole GetIceRole(const WrapperPtr& pc_wrapper_ptr) {
Mirko Bonadeie97de912017-12-13 11:29:34 +0100195 auto* pc_proxy =
196 static_cast<PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(
197 pc_wrapper_ptr->pc());
198 PeerConnection* pc = static_cast<PeerConnection*>(pc_proxy->internal());
Steve Anton46d926a2018-01-23 10:23:06 -0800199 for (auto transceiver : pc->GetTransceiversForTesting()) {
200 if (transceiver->internal()->media_type() == cricket::MEDIA_TYPE_AUDIO) {
201 cricket::BaseChannel* channel = transceiver->internal()->channel();
202 if (channel) {
203 return channel->rtp_dtls_transport()->ice_transport()->GetIceRole();
204 }
205 }
206 }
207 RTC_NOTREACHED();
208 return cricket::ICEROLE_UNKNOWN;
Qingsi Wange1692722017-11-29 13:27:20 -0800209 }
210
Steve Antonf1c6db12017-10-13 11:13:35 -0700211 bool AddCandidateToFirstTransport(cricket::Candidate* candidate,
212 SessionDescriptionInterface* sdesc) {
213 auto* desc = sdesc->description();
214 RTC_DCHECK(desc->contents().size() > 0);
215 const auto& first_content = desc->contents()[0];
216 candidate->set_transport_name(first_content.name);
217 JsepIceCandidate jsep_candidate(first_content.name, 0, *candidate);
218 return sdesc->AddCandidate(&jsep_candidate);
219 }
220
221 rtc::FakeNetworkManager* NewFakeNetwork() {
222 // The PeerConnection's port allocator is tied to the PeerConnection's
223 // lifetime and expects the underlying NetworkManager to outlive it. That
224 // prevents us from having the PeerConnectionWrapper own the fake network.
225 // Therefore, the test fixture will own all the fake networks even though
226 // tests should access the fake network through the PeerConnectionWrapper.
227 auto* fake_network = new rtc::FakeNetworkManager();
228 fake_networks_.emplace_back(fake_network);
229 return fake_network;
230 }
231
232 std::unique_ptr<rtc::VirtualSocketServer> vss_;
233 rtc::AutoSocketServerThread main_;
234 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
235 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_networks_;
Steve Anton46d926a2018-01-23 10:23:06 -0800236 const SdpSemantics sdp_semantics_;
237};
238
239class PeerConnectionIceTest
240 : public PeerConnectionIceBaseTest,
241 public ::testing::WithParamInterface<SdpSemantics> {
242 protected:
243 PeerConnectionIceTest() : PeerConnectionIceBaseTest(GetParam()) {}
Steve Antonf1c6db12017-10-13 11:13:35 -0700244};
245
246::testing::AssertionResult AssertCandidatesEqual(const char* a_expr,
247 const char* b_expr,
248 const cricket::Candidate& a,
249 const cricket::Candidate& b) {
250 std::stringstream failure_info;
251 if (a.component() != b.component()) {
252 failure_info << "\ncomponent: " << a.component() << " != " << b.component();
253 }
254 if (a.protocol() != b.protocol()) {
255 failure_info << "\nprotocol: " << a.protocol() << " != " << b.protocol();
256 }
257 if (a.address() != b.address()) {
258 failure_info << "\naddress: " << a.address().ToString()
259 << " != " << b.address().ToString();
260 }
261 if (a.type() != b.type()) {
262 failure_info << "\ntype: " << a.type() << " != " << b.type();
263 }
264 std::string failure_info_str = failure_info.str();
265 if (failure_info_str.empty()) {
266 return ::testing::AssertionSuccess();
267 } else {
268 return ::testing::AssertionFailure()
269 << a_expr << " and " << b_expr << " are not equal"
270 << failure_info_str;
271 }
272}
273
Steve Anton46d926a2018-01-23 10:23:06 -0800274TEST_P(PeerConnectionIceTest, OfferContainsGatheredCandidates) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700275 const SocketAddress kLocalAddress("1.1.1.1", 0);
276
277 auto caller = CreatePeerConnectionWithAudioVideo();
278 caller->network()->AddInterface(kLocalAddress);
279
280 // Start ICE candidate gathering by setting the local offer.
281 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
282
283 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
284
285 auto offer = caller->CreateOffer();
286 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size());
287 EXPECT_EQ(caller->observer()->GetCandidatesByMline(0).size(),
288 offer->candidates(0)->count());
289 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(1).size());
290 EXPECT_EQ(caller->observer()->GetCandidatesByMline(1).size(),
291 offer->candidates(1)->count());
292}
293
Steve Anton46d926a2018-01-23 10:23:06 -0800294TEST_P(PeerConnectionIceTest, AnswerContainsGatheredCandidates) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700295 const SocketAddress kCallerAddress("1.1.1.1", 0);
296
297 auto caller = CreatePeerConnectionWithAudioVideo();
298 auto callee = CreatePeerConnectionWithAudioVideo();
299 caller->network()->AddInterface(kCallerAddress);
300
301 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
302 ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
303
304 EXPECT_TRUE_WAIT(callee->IsIceGatheringDone(), kIceCandidatesTimeout);
305
Oleh Prypinc22d6a82018-02-02 08:42:18 +0000306 auto answer = callee->CreateAnswer();
Steve Antonf1c6db12017-10-13 11:13:35 -0700307 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(0).size());
308 EXPECT_EQ(callee->observer()->GetCandidatesByMline(0).size(),
309 answer->candidates(0)->count());
310 EXPECT_LT(0u, caller->observer()->GetCandidatesByMline(1).size());
311 EXPECT_EQ(callee->observer()->GetCandidatesByMline(1).size(),
312 answer->candidates(1)->count());
313}
314
Steve Anton46d926a2018-01-23 10:23:06 -0800315TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700316 CanSetRemoteSessionDescriptionWithRemoteCandidates) {
317 const SocketAddress kCallerAddress("1.1.1.1", 1111);
318
319 auto caller = CreatePeerConnectionWithAudioVideo();
320 auto callee = CreatePeerConnectionWithAudioVideo();
321
322 auto offer = caller->CreateOfferAndSetAsLocal();
323 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
324 AddCandidateToFirstTransport(&candidate, offer.get());
325
326 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
327 auto remote_candidates = callee->GetIceCandidatesFromRemoteDescription();
328 ASSERT_EQ(1u, remote_candidates.size());
329 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate,
330 remote_candidates[0]->candidate());
331}
332
Steve Anton46d926a2018-01-23 10:23:06 -0800333TEST_P(PeerConnectionIceTest, SetLocalDescriptionFailsIfNoIceCredentials) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700334 auto caller = CreatePeerConnectionWithAudioVideo();
335
336 auto offer = caller->CreateOffer();
337 RemoveIceUfragPwd(offer.get());
338
339 EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
340}
341
Steve Anton46d926a2018-01-23 10:23:06 -0800342TEST_P(PeerConnectionIceTest, SetRemoteDescriptionFailsIfNoIceCredentials) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700343 auto caller = CreatePeerConnectionWithAudioVideo();
344 auto callee = CreatePeerConnectionWithAudioVideo();
345
346 auto offer = caller->CreateOfferAndSetAsLocal();
347 RemoveIceUfragPwd(offer.get());
348
349 EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
350}
351
352// The following group tests that ICE candidates are not generated before
353// SetLocalDescription is called on a PeerConnection.
354
Steve Anton46d926a2018-01-23 10:23:06 -0800355TEST_P(PeerConnectionIceTest, NoIceCandidatesBeforeSetLocalDescription) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700356 const SocketAddress kLocalAddress("1.1.1.1", 0);
357
358 auto caller = CreatePeerConnectionWithAudioVideo();
359 caller->network()->AddInterface(kLocalAddress);
360
361 // Pump for 1 second and verify that no candidates are generated.
362 rtc::Thread::Current()->ProcessMessages(1000);
363
364 EXPECT_EQ(0u, caller->observer()->candidates_.size());
365}
Steve Anton46d926a2018-01-23 10:23:06 -0800366TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700367 NoIceCandidatesBeforeAnswerSetAsLocalDescription) {
368 const SocketAddress kCallerAddress("1.1.1.1", 1111);
369
370 auto caller = CreatePeerConnectionWithAudioVideo();
371 auto callee = CreatePeerConnectionWithAudioVideo();
372 caller->network()->AddInterface(kCallerAddress);
373
374 auto offer = caller->CreateOfferAndSetAsLocal();
375 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
376 AddCandidateToFirstTransport(&candidate, offer.get());
377 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
378
379 // Pump for 1 second and verify that no candidates are generated.
380 rtc::Thread::Current()->ProcessMessages(1000);
381
382 EXPECT_EQ(0u, callee->observer()->candidates_.size());
383}
384
Steve Anton46d926a2018-01-23 10:23:06 -0800385TEST_P(PeerConnectionIceTest, CannotAddCandidateWhenRemoteDescriptionNotSet) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700386 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
387
388 auto caller = CreatePeerConnectionWithAudioVideo();
389 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
390 JsepIceCandidate jsep_candidate(cricket::CN_AUDIO, 0, candidate);
391
392 EXPECT_FALSE(caller->pc()->AddIceCandidate(&jsep_candidate));
393
394 caller->CreateOfferAndSetAsLocal();
395
396 EXPECT_FALSE(caller->pc()->AddIceCandidate(&jsep_candidate));
397}
398
Steve Anton46d926a2018-01-23 10:23:06 -0800399TEST_P(PeerConnectionIceTest, DuplicateIceCandidateIgnoredWhenAdded) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700400 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
401
402 auto caller = CreatePeerConnectionWithAudioVideo();
403 auto callee = CreatePeerConnectionWithAudioVideo();
404
405 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
406 ASSERT_TRUE(
407 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
408
409 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
410 caller->AddIceCandidate(&candidate);
411 EXPECT_TRUE(caller->AddIceCandidate(&candidate));
412 EXPECT_EQ(1u, caller->GetIceCandidatesFromRemoteDescription().size());
413}
414
Steve Anton46d926a2018-01-23 10:23:06 -0800415TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700416 AddRemoveCandidateWithEmptyTransportDoesNotCrash) {
417 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
418
419 auto caller = CreatePeerConnectionWithAudioVideo();
420 auto callee = CreatePeerConnectionWithAudioVideo();
421
422 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
423 ASSERT_TRUE(
424 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
425
426 // |candidate.transport_name()| is empty.
427 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
Steve Anton46d926a2018-01-23 10:23:06 -0800428 auto* audio_content = cricket::GetFirstAudioContent(
429 caller->pc()->local_description()->description());
430 JsepIceCandidate ice_candidate(audio_content->name, 0, candidate);
Steve Antonf1c6db12017-10-13 11:13:35 -0700431 EXPECT_TRUE(caller->pc()->AddIceCandidate(&ice_candidate));
432 EXPECT_TRUE(caller->pc()->RemoveIceCandidates({candidate}));
433}
434
Steve Anton46d926a2018-01-23 10:23:06 -0800435TEST_P(PeerConnectionIceTest, RemoveCandidateRemovesFromRemoteDescription) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700436 const SocketAddress kCalleeAddress("1.1.1.1", 1111);
437
438 auto caller = CreatePeerConnectionWithAudioVideo();
439 auto callee = CreatePeerConnectionWithAudioVideo();
440
441 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
442 ASSERT_TRUE(
443 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
444
445 cricket::Candidate candidate = CreateLocalUdpCandidate(kCalleeAddress);
446 ASSERT_TRUE(caller->AddIceCandidate(&candidate));
447 EXPECT_TRUE(caller->pc()->RemoveIceCandidates({candidate}));
448 EXPECT_EQ(0u, caller->GetIceCandidatesFromRemoteDescription().size());
449}
450
451// Test that if a candidate is added via AddIceCandidate and via an updated
452// remote description, then both candidates appear in the stored remote
453// description.
Steve Anton46d926a2018-01-23 10:23:06 -0800454TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700455 CandidateInSubsequentOfferIsAddedToRemoteDescription) {
456 const SocketAddress kCallerAddress1("1.1.1.1", 1111);
457 const SocketAddress kCallerAddress2("2.2.2.2", 2222);
458
459 auto caller = CreatePeerConnectionWithAudioVideo();
460 auto callee = CreatePeerConnectionWithAudioVideo();
461
462 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
463 ASSERT_TRUE(
464 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
465
466 // Add one candidate via |AddIceCandidate|.
467 cricket::Candidate candidate1 = CreateLocalUdpCandidate(kCallerAddress1);
468 ASSERT_TRUE(callee->AddIceCandidate(&candidate1));
469
470 // Add the second candidate via a reoffer.
471 auto offer = caller->CreateOffer();
472 cricket::Candidate candidate2 = CreateLocalUdpCandidate(kCallerAddress2);
473 AddCandidateToFirstTransport(&candidate2, offer.get());
474
475 // Expect both candidates to appear in the callee's remote description.
476 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
477 EXPECT_EQ(2u, callee->GetIceCandidatesFromRemoteDescription().size());
478}
479
480// The follow test verifies that SetLocal/RemoteDescription fails when an offer
481// has either ICE ufrag/pwd too short or too long and succeeds otherwise.
482// The standard (https://tools.ietf.org/html/rfc5245#section-15.4) says that
483// pwd must be 22-256 characters and ufrag must be 4-256 characters.
Steve Anton46d926a2018-01-23 10:23:06 -0800484TEST_P(PeerConnectionIceTest, VerifyUfragPwdLength) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700485 auto caller = CreatePeerConnectionWithAudioVideo();
486 auto callee = CreatePeerConnectionWithAudioVideo();
487
488 auto set_local_description_with_ufrag_pwd_length =
489 [this, &caller](int ufrag_len, int pwd_len) {
490 auto offer = caller->CreateOffer();
491 SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
492 std::string(pwd_len, 'x'));
493 return caller->SetLocalDescription(std::move(offer));
494 };
495
496 auto set_remote_description_with_ufrag_pwd_length =
497 [this, &caller, &callee](int ufrag_len, int pwd_len) {
498 auto offer = caller->CreateOffer();
499 SetIceUfragPwd(offer.get(), std::string(ufrag_len, 'x'),
500 std::string(pwd_len, 'x'));
501 return callee->SetRemoteDescription(std::move(offer));
502 };
503
504 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(3, 22));
505 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(3, 22));
506 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(257, 22));
507 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(257, 22));
508 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(4, 21));
509 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(4, 21));
510 EXPECT_FALSE(set_local_description_with_ufrag_pwd_length(4, 257));
511 EXPECT_FALSE(set_remote_description_with_ufrag_pwd_length(4, 257));
512 EXPECT_TRUE(set_local_description_with_ufrag_pwd_length(4, 22));
513 EXPECT_TRUE(set_remote_description_with_ufrag_pwd_length(4, 22));
514 EXPECT_TRUE(set_local_description_with_ufrag_pwd_length(256, 256));
515 EXPECT_TRUE(set_remote_description_with_ufrag_pwd_length(256, 256));
516}
517
518::testing::AssertionResult AssertIpInCandidates(
519 const char* address_expr,
520 const char* candidates_expr,
521 const SocketAddress& address,
522 const std::vector<IceCandidateInterface*> candidates) {
523 std::stringstream candidate_hosts;
524 for (const auto* candidate : candidates) {
525 const auto& candidate_ip = candidate->candidate().address().ipaddr();
526 if (candidate_ip == address.ipaddr()) {
527 return ::testing::AssertionSuccess();
528 }
529 candidate_hosts << "\n" << candidate_ip;
530 }
531 return ::testing::AssertionFailure()
532 << address_expr << " (host " << address.HostAsURIString()
533 << ") not in " << candidates_expr
534 << " which have the following address hosts:" << candidate_hosts.str();
535}
536
Steve Anton46d926a2018-01-23 10:23:06 -0800537TEST_P(PeerConnectionIceTest, CandidatesGeneratedForEachLocalInterface) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700538 const SocketAddress kLocalAddress1("1.1.1.1", 0);
539 const SocketAddress kLocalAddress2("2.2.2.2", 0);
540
541 auto caller = CreatePeerConnectionWithAudioVideo();
542 caller->network()->AddInterface(kLocalAddress1);
543 caller->network()->AddInterface(kLocalAddress2);
544
545 caller->CreateOfferAndSetAsLocal();
546 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
547
548 auto candidates = caller->observer()->GetCandidatesByMline(0);
549 EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress1, candidates);
550 EXPECT_PRED_FORMAT2(AssertIpInCandidates, kLocalAddress2, candidates);
551}
552
Steve Anton46d926a2018-01-23 10:23:06 -0800553TEST_P(PeerConnectionIceTest, TrickledSingleCandidateAddedToRemoteDescription) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700554 const SocketAddress kCallerAddress("1.1.1.1", 1111);
555
556 auto caller = CreatePeerConnectionWithAudioVideo();
557 auto callee = CreatePeerConnectionWithAudioVideo();
558
559 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
560
561 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
562 callee->AddIceCandidate(&candidate);
563 auto candidates = callee->GetIceCandidatesFromRemoteDescription();
564 ASSERT_EQ(1u, candidates.size());
565 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate,
566 candidates[0]->candidate());
567}
568
Steve Anton46d926a2018-01-23 10:23:06 -0800569TEST_P(PeerConnectionIceTest, TwoTrickledCandidatesAddedToRemoteDescription) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700570 const SocketAddress kCalleeAddress1("1.1.1.1", 1111);
571 const SocketAddress kCalleeAddress2("2.2.2.2", 2222);
572
573 auto caller = CreatePeerConnectionWithAudioVideo();
574 auto callee = CreatePeerConnectionWithAudioVideo();
575
576 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
577 ASSERT_TRUE(
578 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
579
580 cricket::Candidate candidate1 = CreateLocalUdpCandidate(kCalleeAddress1);
581 caller->AddIceCandidate(&candidate1);
582
583 cricket::Candidate candidate2 = CreateLocalUdpCandidate(kCalleeAddress2);
584 caller->AddIceCandidate(&candidate2);
585
586 auto candidates = caller->GetIceCandidatesFromRemoteDescription();
587 ASSERT_EQ(2u, candidates.size());
588 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate1,
589 candidates[0]->candidate());
590 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, candidate2,
591 candidates[1]->candidate());
592}
593
Steve Anton46d926a2018-01-23 10:23:06 -0800594TEST_P(PeerConnectionIceTest, LocalDescriptionUpdatedWhenContinualGathering) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700595 const SocketAddress kLocalAddress("1.1.1.1", 0);
596
597 RTCConfiguration config;
598 config.continual_gathering_policy =
599 PeerConnectionInterface::GATHER_CONTINUALLY;
600 auto caller = CreatePeerConnectionWithAudioVideo(config);
601 caller->network()->AddInterface(kLocalAddress);
602
603 // Start ICE candidate gathering by setting the local offer.
604 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
605
606 // Since we're using continual gathering, we won't get "gathering done".
607 EXPECT_TRUE_WAIT(
608 caller->pc()->local_description()->candidates(0)->count() > 0,
609 kIceCandidatesTimeout);
610}
611
612// Test that when continual gathering is enabled, and a network interface goes
613// down, the candidate is signaled as removed and removed from the local
614// description.
Steve Anton46d926a2018-01-23 10:23:06 -0800615TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700616 LocalCandidatesRemovedWhenNetworkDownIfGatheringContinually) {
617 const SocketAddress kLocalAddress("1.1.1.1", 0);
618
619 RTCConfiguration config;
620 config.continual_gathering_policy =
621 PeerConnectionInterface::GATHER_CONTINUALLY;
622 auto caller = CreatePeerConnectionWithAudioVideo(config);
623 caller->network()->AddInterface(kLocalAddress);
624
625 // Start ICE candidate gathering by setting the local offer.
626 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
627
628 EXPECT_TRUE_WAIT(
629 caller->pc()->local_description()->candidates(0)->count() > 0,
630 kIceCandidatesTimeout);
631
632 // Remove the only network interface, causing the PeerConnection to signal
633 // the removal of all candidates derived from this interface.
634 caller->network()->RemoveInterface(kLocalAddress);
635
636 EXPECT_EQ_WAIT(0u, caller->pc()->local_description()->candidates(0)->count(),
637 kIceCandidatesTimeout);
638 EXPECT_LT(0, caller->observer()->num_candidates_removed_);
639}
640
Steve Anton46d926a2018-01-23 10:23:06 -0800641TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700642 LocalCandidatesNotRemovedWhenNetworkDownIfGatheringOnce) {
643 const SocketAddress kLocalAddress("1.1.1.1", 0);
644
645 RTCConfiguration config;
646 config.continual_gathering_policy = PeerConnectionInterface::GATHER_ONCE;
647 auto caller = CreatePeerConnectionWithAudioVideo(config);
648 caller->network()->AddInterface(kLocalAddress);
649
650 // Start ICE candidate gathering by setting the local offer.
651 ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
652
653 EXPECT_TRUE_WAIT(caller->IsIceGatheringDone(), kIceCandidatesTimeout);
654
655 caller->network()->RemoveInterface(kLocalAddress);
656
657 // Verify that the local candidates are not removed;
658 rtc::Thread::Current()->ProcessMessages(1000);
659 EXPECT_EQ(0, caller->observer()->num_candidates_removed_);
660}
661
662// The following group tests that when an offer includes a new ufrag or pwd
663// (indicating an ICE restart) the old candidates are removed and new candidates
664// added to the remote description.
665
Steve Anton46d926a2018-01-23 10:23:06 -0800666TEST_P(PeerConnectionIceTest, IceRestartOfferClearsExistingCandidate) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700667 const SocketAddress kCallerAddress("1.1.1.1", 1111);
668
669 auto caller = CreatePeerConnectionWithAudioVideo();
670 auto callee = CreatePeerConnectionWithAudioVideo();
671
672 auto offer = caller->CreateOffer();
673 cricket::Candidate candidate = CreateLocalUdpCandidate(kCallerAddress);
674 AddCandidateToFirstTransport(&candidate, offer.get());
675
676 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
677
678 RTCOfferAnswerOptions options;
679 options.ice_restart = true;
680 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOffer(options)));
681
682 EXPECT_EQ(0u, callee->GetIceCandidatesFromRemoteDescription().size());
683}
Steve Anton46d926a2018-01-23 10:23:06 -0800684TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700685 IceRestartOfferCandidateReplacesExistingCandidate) {
686 const SocketAddress kFirstCallerAddress("1.1.1.1", 1111);
687 const SocketAddress kRestartedCallerAddress("2.2.2.2", 2222);
688
689 auto caller = CreatePeerConnectionWithAudioVideo();
690 auto callee = CreatePeerConnectionWithAudioVideo();
691
692 auto offer = caller->CreateOffer();
693 cricket::Candidate old_candidate =
694 CreateLocalUdpCandidate(kFirstCallerAddress);
695 AddCandidateToFirstTransport(&old_candidate, offer.get());
696
697 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
698
699 RTCOfferAnswerOptions options;
700 options.ice_restart = true;
701 auto restart_offer = caller->CreateOffer(options);
702 cricket::Candidate new_candidate =
703 CreateLocalUdpCandidate(kRestartedCallerAddress);
704 AddCandidateToFirstTransport(&new_candidate, restart_offer.get());
705
706 ASSERT_TRUE(callee->SetRemoteDescription(std::move(restart_offer)));
707
708 auto remote_candidates = callee->GetIceCandidatesFromRemoteDescription();
709 ASSERT_EQ(1u, remote_candidates.size());
710 EXPECT_PRED_FORMAT2(AssertCandidatesEqual, new_candidate,
711 remote_candidates[0]->candidate());
712}
713
714// Test that if there is not an ICE restart (i.e., nothing changes), then the
715// answer to a later offer should have the same ufrag/pwd as the first answer.
Steve Anton46d926a2018-01-23 10:23:06 -0800716TEST_P(PeerConnectionIceTest, LaterAnswerHasSameIceCredentialsIfNoIceRestart) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700717 auto caller = CreatePeerConnectionWithAudioVideo();
718 auto callee = CreatePeerConnectionWithAudioVideo();
719
720 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
721 ASSERT_TRUE(
722 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
723
724 // Re-offer.
725 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
726
727 auto answer = callee->CreateAnswer();
728 auto* answer_transport_desc = GetFirstTransportDescription(answer.get());
729 auto* local_transport_desc =
730 GetFirstTransportDescription(callee->pc()->local_description());
731
732 EXPECT_EQ(answer_transport_desc->ice_ufrag, local_transport_desc->ice_ufrag);
733 EXPECT_EQ(answer_transport_desc->ice_pwd, local_transport_desc->ice_pwd);
734}
735
736// The following parameterized test verifies that if an offer is sent with a
737// modified ICE ufrag and/or ICE pwd, then the answer should identify that the
738// other side has initiated an ICE restart and generate a new ufrag and pwd.
739// RFC 5245 says: "If the offer contained a change in the a=ice-ufrag or
740// a=ice-pwd attributes compared to the previous SDP from the peer, it
741// indicates that ICE is restarting for this media stream."
742
Steve Anton46d926a2018-01-23 10:23:06 -0800743class PeerConnectionIceUfragPwdAnswerTest
744 : public PeerConnectionIceBaseTest,
745 public ::testing::WithParamInterface<
746 std::tuple<SdpSemantics, std::tuple<bool, bool>>> {
Steve Antonf1c6db12017-10-13 11:13:35 -0700747 protected:
Steve Anton46d926a2018-01-23 10:23:06 -0800748 PeerConnectionIceUfragPwdAnswerTest()
749 : PeerConnectionIceBaseTest(std::get<0>(GetParam())) {
750 auto param = std::get<1>(GetParam());
751 offer_new_ufrag_ = std::get<0>(param);
752 offer_new_pwd_ = std::get<1>(param);
Steve Antonf1c6db12017-10-13 11:13:35 -0700753 }
754
755 bool offer_new_ufrag_;
756 bool offer_new_pwd_;
757};
758
Steve Anton46d926a2018-01-23 10:23:06 -0800759TEST_P(PeerConnectionIceUfragPwdAnswerTest, TestIncludedInAnswer) {
Steve Antonf1c6db12017-10-13 11:13:35 -0700760 auto caller = CreatePeerConnectionWithAudioVideo();
761 auto callee = CreatePeerConnectionWithAudioVideo();
762
763 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
764 ASSERT_TRUE(
765 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
766
767 auto offer = caller->CreateOffer();
768 auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
769 if (offer_new_ufrag_) {
770 offer_transport_desc->ice_ufrag += "_new";
771 }
772 if (offer_new_pwd_) {
773 offer_transport_desc->ice_pwd += "_new";
774 }
775
776 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
777
778 auto answer = callee->CreateAnswer();
779 auto* answer_transport_desc = GetFirstTransportDescription(answer.get());
780 auto* local_transport_desc =
781 GetFirstTransportDescription(callee->pc()->local_description());
782
783 EXPECT_NE(answer_transport_desc->ice_ufrag, local_transport_desc->ice_ufrag);
784 EXPECT_NE(answer_transport_desc->ice_pwd, local_transport_desc->ice_pwd);
785}
786
787INSTANTIATE_TEST_CASE_P(
Steve Anton46d926a2018-01-23 10:23:06 -0800788 PeerConnectionIceTest,
789 PeerConnectionIceUfragPwdAnswerTest,
790 Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
791 Values(std::make_pair(true, true), // Both changed.
792 std::make_pair(true, false), // Only ufrag changed.
793 std::make_pair(false, true)))); // Only pwd changed.
Steve Antonf1c6db12017-10-13 11:13:35 -0700794
795// Test that if an ICE restart is offered on one media section, then the answer
796// will only change ICE ufrag/pwd for that section and keep the other sections
797// the same.
798// Note that this only works if we have disabled BUNDLE, otherwise all media
799// sections will share the same transport.
Steve Anton46d926a2018-01-23 10:23:06 -0800800TEST_P(PeerConnectionIceTest,
Steve Antonf1c6db12017-10-13 11:13:35 -0700801 CreateAnswerHasNewUfragPwdForOnlyMediaSectionWhichRestarted) {
802 auto caller = CreatePeerConnectionWithAudioVideo();
803 auto callee = CreatePeerConnectionWithAudioVideo();
804
805 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
806 ASSERT_TRUE(
807 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
808
809 RTCOfferAnswerOptions disable_bundle_options;
810 disable_bundle_options.use_rtp_mux = false;
811
812 auto offer = caller->CreateOffer(disable_bundle_options);
813
814 // Signal ICE restart on the first media section.
815 auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
816 offer_transport_desc->ice_ufrag += "_new";
817 offer_transport_desc->ice_pwd += "_new";
818
819 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
820
821 auto answer = callee->CreateAnswer(disable_bundle_options);
822 const auto& answer_transports = answer->description()->transport_infos();
823 const auto& local_transports =
824 callee->pc()->local_description()->description()->transport_infos();
825
826 EXPECT_NE(answer_transports[0].description.ice_ufrag,
827 local_transports[0].description.ice_ufrag);
828 EXPECT_NE(answer_transports[0].description.ice_pwd,
829 local_transports[0].description.ice_pwd);
830 EXPECT_EQ(answer_transports[1].description.ice_ufrag,
831 local_transports[1].description.ice_ufrag);
832 EXPECT_EQ(answer_transports[1].description.ice_pwd,
833 local_transports[1].description.ice_pwd);
834}
835
Qingsi Wange1692722017-11-29 13:27:20 -0800836// Test that when the initial offerer (caller) uses the lite implementation of
837// ICE and the callee uses the full implementation, the caller takes the
838// CONTROLLED role and the callee takes the CONTROLLING role. This is specified
839// in RFC5245 Section 5.1.1.
Steve Anton46d926a2018-01-23 10:23:06 -0800840TEST_P(PeerConnectionIceTest,
Qingsi Wange1692722017-11-29 13:27:20 -0800841 OfferFromLiteIceControlledAndAnswerFromFullIceControlling) {
842 auto caller = CreatePeerConnectionWithAudioVideo();
843 auto callee = CreatePeerConnectionWithAudioVideo();
844
845 auto offer = caller->CreateOffer();
846 SetIceMode(offer.get(), cricket::IceMode::ICEMODE_LITE);
847 ASSERT_TRUE(
848 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
849 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
850
851 auto answer = callee->CreateAnswer();
852 SetIceMode(answer.get(), cricket::IceMode::ICEMODE_FULL);
853 ASSERT_TRUE(
854 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
855 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
856
857 EXPECT_EQ(cricket::ICEROLE_CONTROLLED, GetIceRole(caller));
858 EXPECT_EQ(cricket::ICEROLE_CONTROLLING, GetIceRole(callee));
859}
860
861// Test that when the caller and the callee both use the lite implementation of
862// ICE, the initial offerer (caller) takes the CONTROLLING role and the callee
863// takes the CONTROLLED role. This is specified in RFC5245 Section 5.1.1.
Steve Anton46d926a2018-01-23 10:23:06 -0800864TEST_P(PeerConnectionIceTest,
Qingsi Wange1692722017-11-29 13:27:20 -0800865 OfferFromLiteIceControllingAndAnswerFromLiteIceControlled) {
866 auto caller = CreatePeerConnectionWithAudioVideo();
867 auto callee = CreatePeerConnectionWithAudioVideo();
868
869 auto offer = caller->CreateOffer();
870 SetIceMode(offer.get(), cricket::IceMode::ICEMODE_LITE);
871 ASSERT_TRUE(
872 caller->SetLocalDescription(CloneSessionDescription(offer.get())));
873 ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
874
875 auto answer = callee->CreateAnswer();
876 SetIceMode(answer.get(), cricket::IceMode::ICEMODE_LITE);
877 ASSERT_TRUE(
878 callee->SetLocalDescription(CloneSessionDescription(answer.get())));
879 ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
880
881 EXPECT_EQ(cricket::ICEROLE_CONTROLLING, GetIceRole(caller));
882 EXPECT_EQ(cricket::ICEROLE_CONTROLLED, GetIceRole(callee));
883}
884
Steve Anton46d926a2018-01-23 10:23:06 -0800885INSTANTIATE_TEST_CASE_P(PeerConnectionIceTest,
886 PeerConnectionIceTest,
887 Values(SdpSemantics::kPlanB,
888 SdpSemantics::kUnifiedPlan));
889
Steve Antonf1c6db12017-10-13 11:13:35 -0700890} // namespace webrtc