blob: 850212bf0dca4bd75282828869032ceda8c9330d [file] [log] [blame]
Seth Hampsond1003d72018-06-22 15:40:16 -07001/*
2 * Copyright 2018 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 "api/audio_codecs/builtin_audio_decoder_factory.h"
12#include "api/audio_codecs/builtin_audio_encoder_factory.h"
13#include "api/stats/rtcstats_objects.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070014#include "api/video_codecs/builtin_video_decoder_factory.h"
15#include "api/video_codecs/builtin_video_encoder_factory.h"
16#include "p2p/base/testturnserver.h"
17#include "p2p/client/basicportallocator.h"
18#include "pc/peerconnection.h"
19#include "pc/peerconnectionwrapper.h"
20#include "pc/test/fakeaudiocapturemodule.h"
21#include "pc/test/fakeperiodicvideotracksource.h"
Seth Hampsonec207102018-06-29 11:12:19 -070022#include "pc/test/fakertccertificategenerator.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070023#include "pc/test/fakevideotrackrenderer.h"
24#include "pc/test/framegeneratorcapturervideotracksource.h"
25#include "rtc_base/fakenetwork.h"
26#include "rtc_base/firewallsocketserver.h"
27#include "rtc_base/gunit.h"
28#include "rtc_base/platform_thread.h"
29#include "rtc_base/socketaddress.h"
Seth Hampsonec207102018-06-29 11:12:19 -070030#include "rtc_base/testcertificateverifier.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070031#include "rtc_base/virtualsocketserver.h"
32#include "test/gtest.h"
33#include "test/testsupport/perf_test.h"
34
35namespace webrtc {
36
37namespace {
38static const int kDefaultTestTimeMs = 15000;
39static const int kRampUpTimeMs = 5000;
40static const int kPollIntervalTimeMs = 50;
41static const int kDefaultTimeoutMs = 10000;
42static const rtc::SocketAddress kDefaultLocalAddress("1.1.1.1", 0);
Seth Hampsonec207102018-06-29 11:12:19 -070043static const char kTurnInternalAddress[] = "88.88.88.0";
44static const char kTurnExternalAddress[] = "88.88.88.1";
45static const int kTurnInternalPort = 3478;
46static const int kTurnExternalPort = 0;
Seth Hampsond1003d72018-06-22 15:40:16 -070047// The video's configured max bitrate in webrtcvideoengine.cc is 1.7 Mbps.
48// Setting the network bandwidth to 1 Mbps allows the video's bitrate to push
49// the network's limitations.
50static const int kNetworkBandwidth = 1000000;
51} // namespace
52
53using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
54
55// This is an end to end test to verify that BWE is functioning when setting
56// up a one to one call at the PeerConnection level. The intention of the test
57// is to catch potential regressions for different ICE path configurations. The
58// test uses a VirtualSocketServer for it's underlying simulated network and
59// fake audio and video sources. The test is based upon rampup_tests.cc, but
60// instead is at the PeerConnection level and uses a different fake network
61// (rampup_tests.cc uses SimulatedNetwork). In the future, this test could
62// potentially test different network conditions and test video quality as well
63// (video_quality_test.cc does this, but at the call level).
64//
65// The perf test results are printed using the perf test support. If the
66// isolated_script_test_perf_output flag is specified in test_main.cc, then
67// the results are written to a JSON formatted file for the Chrome perf
68// dashboard. Since this test is a webrtc_perf_test, it will be run in the perf
69// console every webrtc commit.
70class PeerConnectionWrapperForRampUpTest : public PeerConnectionWrapper {
71 public:
72 using PeerConnectionWrapper::PeerConnectionWrapper;
73
74 PeerConnectionWrapperForRampUpTest(
75 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
76 rtc::scoped_refptr<PeerConnectionInterface> pc,
Seth Hampsonec207102018-06-29 11:12:19 -070077 std::unique_ptr<MockPeerConnectionObserver> observer)
Seth Hampsond1003d72018-06-22 15:40:16 -070078 : PeerConnectionWrapper::PeerConnectionWrapper(pc_factory,
79 pc,
Seth Hampsonec207102018-06-29 11:12:19 -070080 std::move(observer)) {}
Seth Hampsond1003d72018-06-22 15:40:16 -070081
82 bool AddIceCandidates(std::vector<const IceCandidateInterface*> candidates) {
83 bool success = true;
84 for (const auto candidate : candidates) {
85 if (!pc()->AddIceCandidate(candidate)) {
86 success = false;
87 }
88 }
89 return success;
90 }
91
92 rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack(
93 FrameGeneratorCapturerVideoTrackSource::Config config,
94 Clock* clock) {
95 video_track_sources_.emplace_back(
96 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
97 config, clock));
98 video_track_sources_.back()->Start();
99 return rtc::scoped_refptr<VideoTrackInterface>(
100 pc_factory()->CreateVideoTrack(rtc::CreateRandomUuid(),
101 video_track_sources_.back()));
102 }
103
104 rtc::scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack(
105 const cricket::AudioOptions options) {
106 rtc::scoped_refptr<AudioSourceInterface> source =
107 pc_factory()->CreateAudioSource(options);
108 return pc_factory()->CreateAudioTrack(rtc::CreateRandomUuid(), source);
109 }
110
111 private:
Seth Hampsond1003d72018-06-22 15:40:16 -0700112 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
113 video_track_sources_;
114};
115
116// TODO(shampson): Paramaterize the test to run for both Plan B & Unified Plan.
117class PeerConnectionRampUpTest : public ::testing::Test {
118 public:
119 PeerConnectionRampUpTest()
120 : clock_(Clock::GetRealTimeClock()),
121 virtual_socket_server_(new rtc::VirtualSocketServer()),
122 firewall_socket_server_(
123 new rtc::FirewallSocketServer(virtual_socket_server_.get())),
124 network_thread_(new rtc::Thread(firewall_socket_server_.get())),
125 worker_thread_(rtc::Thread::Create()) {
126 network_thread_->SetName("PCNetworkThread", this);
127 worker_thread_->SetName("PCWorkerThread", this);
128 RTC_CHECK(network_thread_->Start());
129 RTC_CHECK(worker_thread_->Start());
130
131 virtual_socket_server_->set_bandwidth(kNetworkBandwidth / 8);
132 pc_factory_ = CreatePeerConnectionFactory(
133 network_thread_.get(), worker_thread_.get(), rtc::Thread::Current(),
134 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
135 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
136 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
137 nullptr /* audio_mixer */, nullptr /* audio_processing */);
138 }
139
140 virtual ~PeerConnectionRampUpTest() {
141 network_thread()->Invoke<void>(RTC_FROM_HERE,
142 [this] { turn_servers_.clear(); });
143 }
144
145 bool CreatePeerConnectionWrappers(const RTCConfiguration& caller_config,
146 const RTCConfiguration& callee_config) {
147 caller_ = CreatePeerConnectionWrapper(caller_config);
148 callee_ = CreatePeerConnectionWrapper(callee_config);
149 return caller_ && callee_;
150 }
151
152 std::unique_ptr<PeerConnectionWrapperForRampUpTest>
153 CreatePeerConnectionWrapper(const RTCConfiguration& config) {
154 auto* fake_network_manager = new rtc::FakeNetworkManager();
155 fake_network_manager->AddInterface(kDefaultLocalAddress);
156 fake_network_managers_.emplace_back(fake_network_manager);
Seth Hampsond1003d72018-06-22 15:40:16 -0700157
Karl Wiberg918f50c2018-07-05 11:40:33 +0200158 auto observer = absl::make_unique<MockPeerConnectionObserver>();
Seth Hampsonec207102018-06-29 11:12:19 -0700159 webrtc::PeerConnectionDependencies dependencies(observer.get());
160 cricket::BasicPortAllocator* port_allocator =
161 new cricket::BasicPortAllocator(fake_network_manager);
162 port_allocator->set_step_delay(cricket::kDefaultStepDelay);
163 dependencies.allocator =
164 std::unique_ptr<cricket::BasicPortAllocator>(port_allocator);
165 dependencies.tls_cert_verifier =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200166 absl::make_unique<rtc::TestCertificateVerifier>();
Seth Hampsonec207102018-06-29 11:12:19 -0700167
168 auto pc =
169 pc_factory_->CreatePeerConnection(config, std::move(dependencies));
Seth Hampsond1003d72018-06-22 15:40:16 -0700170 if (!pc) {
171 return nullptr;
172 }
173
Karl Wiberg918f50c2018-07-05 11:40:33 +0200174 return absl::make_unique<PeerConnectionWrapperForRampUpTest>(
Seth Hampsonec207102018-06-29 11:12:19 -0700175 pc_factory_, pc, std::move(observer));
Seth Hampsond1003d72018-06-22 15:40:16 -0700176 }
177
178 void SetupOneWayCall() {
179 ASSERT_TRUE(caller_);
180 ASSERT_TRUE(callee_);
181 FrameGeneratorCapturerVideoTrackSource::Config config;
182 caller_->AddTrack(caller_->CreateLocalVideoTrack(config, clock_));
183 // Disable highpass filter so that we can get all the test audio frames.
184 cricket::AudioOptions options;
185 options.highpass_filter = false;
186 caller_->AddTrack(caller_->CreateLocalAudioTrack(options));
187
188 // Do the SDP negotiation, and also exchange ice candidates.
189 ASSERT_TRUE(caller_->ExchangeOfferAnswerWith(callee_.get()));
190 ASSERT_TRUE_WAIT(
191 caller_->signaling_state() == PeerConnectionInterface::kStable,
192 kDefaultTimeoutMs);
193 ASSERT_TRUE_WAIT(caller_->IsIceGatheringDone(), kDefaultTimeoutMs);
194 ASSERT_TRUE_WAIT(callee_->IsIceGatheringDone(), kDefaultTimeoutMs);
195
196 // Connect an ICE candidate pairs.
197 ASSERT_TRUE(
198 callee_->AddIceCandidates(caller_->observer()->GetAllCandidates()));
199 ASSERT_TRUE(
200 caller_->AddIceCandidates(callee_->observer()->GetAllCandidates()));
201 // This means that ICE and DTLS are connected.
202 ASSERT_TRUE_WAIT(callee_->IsIceConnected(), kDefaultTimeoutMs);
203 ASSERT_TRUE_WAIT(caller_->IsIceConnected(), kDefaultTimeoutMs);
204 }
205
Seth Hampsonec207102018-06-29 11:12:19 -0700206 void CreateTurnServer(cricket::ProtocolType type,
207 const std::string& common_name = "test turn server") {
Seth Hampsond1003d72018-06-22 15:40:16 -0700208 rtc::Thread* thread = network_thread();
209 std::unique_ptr<cricket::TestTurnServer> turn_server =
210 network_thread_->Invoke<std::unique_ptr<cricket::TestTurnServer>>(
Seth Hampsonec207102018-06-29 11:12:19 -0700211 RTC_FROM_HERE, [thread, type, common_name] {
Seth Hampsond1003d72018-06-22 15:40:16 -0700212 static const rtc::SocketAddress turn_server_internal_address{
Seth Hampsonec207102018-06-29 11:12:19 -0700213 kTurnInternalAddress, kTurnInternalPort};
Seth Hampsond1003d72018-06-22 15:40:16 -0700214 static const rtc::SocketAddress turn_server_external_address{
Seth Hampsonec207102018-06-29 11:12:19 -0700215 kTurnExternalAddress, kTurnExternalPort};
Karl Wiberg918f50c2018-07-05 11:40:33 +0200216 return absl::make_unique<cricket::TestTurnServer>(
Seth Hampsond1003d72018-06-22 15:40:16 -0700217 thread, turn_server_internal_address,
Seth Hampsonec207102018-06-29 11:12:19 -0700218 turn_server_external_address, type,
219 true /*ignore_bad_certs=*/, common_name);
Seth Hampsond1003d72018-06-22 15:40:16 -0700220 });
221 turn_servers_.push_back(std::move(turn_server));
222 }
223
224 // First runs the call for kRampUpTimeMs to ramp up the bandwidth estimate.
225 // Then runs the test for the remaining test time, grabbing the bandwidth
226 // estimation stat, every kPollIntervalTimeMs. When finished, averages the
227 // bandwidth estimations and prints the bandwidth estimation result as a perf
228 // metric.
229 void RunTest(const std::string& test_string) {
230 rtc::Thread::Current()->ProcessMessages(kRampUpTimeMs);
231 int number_of_polls =
232 (kDefaultTestTimeMs - kRampUpTimeMs) / kPollIntervalTimeMs;
233 int total_bwe = 0;
234 for (int i = 0; i < number_of_polls; ++i) {
235 rtc::Thread::Current()->ProcessMessages(kPollIntervalTimeMs);
236 total_bwe += static_cast<int>(GetCallerAvailableBitrateEstimate());
237 }
238 double average_bandwidth_estimate = total_bwe / number_of_polls;
239 std::string value_description =
240 "bwe_after_" + std::to_string(kDefaultTestTimeMs / 1000) + "_seconds";
241 test::PrintResult("peerconnection_ramp_up_", test_string, value_description,
242 average_bandwidth_estimate, "bwe", false);
243 }
244
245 rtc::Thread* network_thread() { return network_thread_.get(); }
246
Seth Hampsonec207102018-06-29 11:12:19 -0700247 rtc::FirewallSocketServer* firewall_socket_server() {
248 return firewall_socket_server_.get();
249 }
250
Seth Hampsond1003d72018-06-22 15:40:16 -0700251 PeerConnectionWrapperForRampUpTest* caller() { return caller_.get(); }
252
253 PeerConnectionWrapperForRampUpTest* callee() { return callee_.get(); }
254
255 private:
256 // Gets the caller's outgoing available bitrate from the stats. Returns 0 if
257 // something went wrong. It takes the outgoing bitrate from the current
258 // selected ICE candidate pair's stats.
259 double GetCallerAvailableBitrateEstimate() {
260 auto stats = caller_->GetStats();
261 auto transport_stats = stats->GetStatsOfType<RTCTransportStats>();
262 if (transport_stats.size() == 0u ||
263 !transport_stats[0]->selected_candidate_pair_id.is_defined()) {
264 return 0;
265 }
266 std::string selected_ice_id =
267 transport_stats[0]->selected_candidate_pair_id.ValueToString();
268 // Use the selected ICE candidate pair ID to get the appropriate ICE stats.
269 const RTCIceCandidatePairStats ice_candidate_pair_stats =
270 stats->Get(selected_ice_id)->cast_to<const RTCIceCandidatePairStats>();
271 if (ice_candidate_pair_stats.available_outgoing_bitrate.is_defined()) {
272 return *ice_candidate_pair_stats.available_outgoing_bitrate;
273 }
274 // We couldn't get the |available_outgoing_bitrate| for the active candidate
275 // pair.
276 return 0;
277 }
278
279 Clock* const clock_;
280 // The turn servers should be accessed & deleted on the network thread to
281 // avoid a race with the socket read/write which occurs on the network thread.
282 std::vector<std::unique_ptr<cricket::TestTurnServer>> turn_servers_;
283 // |virtual_socket_server_| is used by |network_thread_| so it must be
284 // destroyed later.
285 // TODO(bugs.webrtc.org/7668): We would like to update the virtual network we
286 // use for this test. VirtualSocketServer isn't ideal because:
287 // 1) It uses the same queue & network capacity for both directions.
288 // 2) VirtualSocketServer implements how the network bandwidth affects the
289 // send delay differently than the SimulatedNetwork, used by the
290 // FakeNetworkPipe. It would be ideal if all of levels of virtual
291 // networks used in testing were consistent.
292 // We would also like to update this test to record the time to ramp up,
293 // down, and back up (similar to in rampup_tests.cc). This is problematic with
294 // the VirtualSocketServer. The first ramp down time is very noisy and the
295 // second ramp up time can take up to 300 seconds, most likely due to a built
296 // up queue.
297 std::unique_ptr<rtc::VirtualSocketServer> virtual_socket_server_;
298 std::unique_ptr<rtc::FirewallSocketServer> firewall_socket_server_;
299 std::unique_ptr<rtc::Thread> network_thread_;
300 std::unique_ptr<rtc::Thread> worker_thread_;
301 // The |pc_factory| uses |network_thread_| & |worker_thread_|, so it must be
302 // destroyed first.
303 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_network_managers_;
304 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
305 std::unique_ptr<PeerConnectionWrapperForRampUpTest> caller_;
306 std::unique_ptr<PeerConnectionWrapperForRampUpTest> callee_;
307};
308
309TEST_F(PeerConnectionRampUpTest, TurnOverTCP) {
310 CreateTurnServer(cricket::ProtocolType::PROTO_TCP);
311 PeerConnectionInterface::IceServer ice_server;
Seth Hampsonec207102018-06-29 11:12:19 -0700312 std::string ice_server_url = "turn:" + std::string(kTurnInternalAddress) +
313 ":" + std::to_string(kTurnInternalPort) +
314 "?transport=tcp";
315 ice_server.urls.push_back(ice_server_url);
Seth Hampsond1003d72018-06-22 15:40:16 -0700316 ice_server.username = "test";
317 ice_server.password = "test";
318 PeerConnectionInterface::RTCConfiguration client_1_config;
319 client_1_config.servers.push_back(ice_server);
320 client_1_config.type = PeerConnectionInterface::kRelay;
321 PeerConnectionInterface::RTCConfiguration client_2_config;
322 client_2_config.servers.push_back(ice_server);
323 client_2_config.type = PeerConnectionInterface::kRelay;
324 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
325
326 SetupOneWayCall();
327 RunTest("turn_over_tcp");
328}
329
Seth Hampsonec207102018-06-29 11:12:19 -0700330TEST_F(PeerConnectionRampUpTest, TurnOverUDP) {
331 CreateTurnServer(cricket::ProtocolType::PROTO_UDP);
332 PeerConnectionInterface::IceServer ice_server;
333 std::string ice_server_url = "turn:" + std::string(kTurnInternalAddress) +
334 ":" + std::to_string(kTurnInternalPort);
335
336 ice_server.urls.push_back(ice_server_url);
337 ice_server.username = "test";
338 ice_server.password = "test";
339 PeerConnectionInterface::RTCConfiguration client_1_config;
340 client_1_config.servers.push_back(ice_server);
341 client_1_config.type = PeerConnectionInterface::kRelay;
342 PeerConnectionInterface::RTCConfiguration client_2_config;
343 client_2_config.servers.push_back(ice_server);
344 client_2_config.type = PeerConnectionInterface::kRelay;
345 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
346
347 SetupOneWayCall();
348 RunTest("turn_over_udp");
349}
350
351TEST_F(PeerConnectionRampUpTest, TurnOverTLS) {
352 CreateTurnServer(cricket::ProtocolType::PROTO_TLS, kTurnInternalAddress);
353 PeerConnectionInterface::IceServer ice_server;
354 std::string ice_server_url = "turns:" + std::string(kTurnInternalAddress) +
355 ":" + std::to_string(kTurnInternalPort) +
356 "?transport=tcp";
357 ice_server.urls.push_back(ice_server_url);
358 ice_server.username = "test";
359 ice_server.password = "test";
360 PeerConnectionInterface::RTCConfiguration client_1_config;
361 client_1_config.servers.push_back(ice_server);
362 client_1_config.type = PeerConnectionInterface::kRelay;
363 PeerConnectionInterface::RTCConfiguration client_2_config;
364 client_2_config.servers.push_back(ice_server);
365 client_2_config.type = PeerConnectionInterface::kRelay;
366
367 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
368
369 SetupOneWayCall();
370 RunTest("turn_over_tls");
371}
372
373TEST_F(PeerConnectionRampUpTest, UDPPeerToPeer) {
374 PeerConnectionInterface::RTCConfiguration client_1_config;
375 client_1_config.tcp_candidate_policy =
376 PeerConnection::kTcpCandidatePolicyDisabled;
377 PeerConnectionInterface::RTCConfiguration client_2_config;
378 client_2_config.tcp_candidate_policy =
379 PeerConnection::kTcpCandidatePolicyDisabled;
380 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
381
382 SetupOneWayCall();
383 RunTest("udp_peer_to_peer");
384}
385
386TEST_F(PeerConnectionRampUpTest, TCPPeerToPeer) {
387 firewall_socket_server()->set_udp_sockets_enabled(false);
388 ASSERT_TRUE(CreatePeerConnectionWrappers(
389 PeerConnectionInterface::RTCConfiguration(),
390 PeerConnectionInterface::RTCConfiguration()));
391
392 SetupOneWayCall();
393 RunTest("tcp_peer_to_peer");
394}
Seth Hampsond1003d72018-06-22 15:40:16 -0700395
396} // namespace webrtc