blob: 93a2724d95ddeff87a759b84b906a307ee0b53b6 [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
Yves Gerey3e707812018-11-28 16:47:49 +010011#include <memory>
12#include <string>
13#include <utility>
14#include <vector>
15
16#include "absl/memory/memory.h"
17#include "absl/types/optional.h"
18#include "api/audio/audio_mixer.h"
19#include "api/audio_codecs/audio_decoder_factory.h"
20#include "api/audio_codecs/audio_encoder_factory.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070021#include "api/audio_codecs/builtin_audio_decoder_factory.h"
22#include "api/audio_codecs/builtin_audio_encoder_factory.h"
Yves Gerey3e707812018-11-28 16:47:49 +010023#include "api/audio_options.h"
Mirko Bonadei2ff3f492018-11-22 09:00:13 +010024#include "api/create_peerconnection_factory.h"
Yves Gerey3e707812018-11-28 16:47:49 +010025#include "api/jsep.h"
Steve Anton10542f22019-01-11 09:11:00 -080026#include "api/media_stream_interface.h"
27#include "api/peer_connection_interface.h"
Mirko Bonadeid9708072019-01-25 20:26:48 +010028#include "api/scoped_refptr.h"
Steve Anton10542f22019-01-11 09:11:00 -080029#include "api/stats/rtc_stats.h"
30#include "api/stats/rtc_stats_report.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070031#include "api/stats/rtcstats_objects.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070032#include "api/video_codecs/builtin_video_decoder_factory.h"
33#include "api/video_codecs/builtin_video_encoder_factory.h"
Yves Gerey3e707812018-11-28 16:47:49 +010034#include "api/video_codecs/video_decoder_factory.h"
35#include "api/video_codecs/video_encoder_factory.h"
36#include "modules/audio_device/include/audio_device.h"
37#include "modules/audio_processing/include/audio_processing.h"
Steve Anton10542f22019-01-11 09:11:00 -080038#include "p2p/base/port_allocator.h"
39#include "p2p/base/port_interface.h"
40#include "p2p/base/test_turn_server.h"
41#include "p2p/client/basic_port_allocator.h"
42#include "pc/peer_connection.h"
43#include "pc/peer_connection_wrapper.h"
44#include "pc/test/fake_audio_capture_module.h"
45#include "pc/test/frame_generator_capturer_video_track_source.h"
46#include "pc/test/mock_peer_connection_observers.h"
Yves Gerey3e707812018-11-28 16:47:49 +010047#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 09:11:00 -080048#include "rtc_base/fake_network.h"
49#include "rtc_base/firewall_socket_server.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070050#include "rtc_base/gunit.h"
Yves Gerey3e707812018-11-28 16:47:49 +010051#include "rtc_base/helpers.h"
52#include "rtc_base/location.h"
Steve Anton10542f22019-01-11 09:11:00 -080053#include "rtc_base/ref_counted_object.h"
Steve Anton10542f22019-01-11 09:11:00 -080054#include "rtc_base/socket_address.h"
55#include "rtc_base/ssl_certificate.h"
56#include "rtc_base/test_certificate_verifier.h"
Yves Gerey3e707812018-11-28 16:47:49 +010057#include "rtc_base/thread.h"
Steve Anton10542f22019-01-11 09:11:00 -080058#include "rtc_base/virtual_socket_server.h"
Yves Gerey3e707812018-11-28 16:47:49 +010059#include "system_wrappers/include/clock.h"
Seth Hampsond1003d72018-06-22 15:40:16 -070060#include "test/gtest.h"
61#include "test/testsupport/perf_test.h"
62
63namespace webrtc {
64
65namespace {
66static const int kDefaultTestTimeMs = 15000;
67static const int kRampUpTimeMs = 5000;
68static const int kPollIntervalTimeMs = 50;
69static const int kDefaultTimeoutMs = 10000;
70static const rtc::SocketAddress kDefaultLocalAddress("1.1.1.1", 0);
Seth Hampsonec207102018-06-29 11:12:19 -070071static const char kTurnInternalAddress[] = "88.88.88.0";
72static const char kTurnExternalAddress[] = "88.88.88.1";
73static const int kTurnInternalPort = 3478;
74static const int kTurnExternalPort = 0;
Seth Hampsond1003d72018-06-22 15:40:16 -070075// The video's configured max bitrate in webrtcvideoengine.cc is 1.7 Mbps.
76// Setting the network bandwidth to 1 Mbps allows the video's bitrate to push
77// the network's limitations.
78static const int kNetworkBandwidth = 1000000;
79} // namespace
80
81using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
82
83// This is an end to end test to verify that BWE is functioning when setting
84// up a one to one call at the PeerConnection level. The intention of the test
85// is to catch potential regressions for different ICE path configurations. The
86// test uses a VirtualSocketServer for it's underlying simulated network and
87// fake audio and video sources. The test is based upon rampup_tests.cc, but
88// instead is at the PeerConnection level and uses a different fake network
89// (rampup_tests.cc uses SimulatedNetwork). In the future, this test could
90// potentially test different network conditions and test video quality as well
91// (video_quality_test.cc does this, but at the call level).
92//
93// The perf test results are printed using the perf test support. If the
94// isolated_script_test_perf_output flag is specified in test_main.cc, then
95// the results are written to a JSON formatted file for the Chrome perf
96// dashboard. Since this test is a webrtc_perf_test, it will be run in the perf
97// console every webrtc commit.
98class PeerConnectionWrapperForRampUpTest : public PeerConnectionWrapper {
99 public:
100 using PeerConnectionWrapper::PeerConnectionWrapper;
101
102 PeerConnectionWrapperForRampUpTest(
103 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
104 rtc::scoped_refptr<PeerConnectionInterface> pc,
Seth Hampsonec207102018-06-29 11:12:19 -0700105 std::unique_ptr<MockPeerConnectionObserver> observer)
Seth Hampsond1003d72018-06-22 15:40:16 -0700106 : PeerConnectionWrapper::PeerConnectionWrapper(pc_factory,
107 pc,
Seth Hampsonec207102018-06-29 11:12:19 -0700108 std::move(observer)) {}
Seth Hampsond1003d72018-06-22 15:40:16 -0700109
110 bool AddIceCandidates(std::vector<const IceCandidateInterface*> candidates) {
111 bool success = true;
112 for (const auto candidate : candidates) {
113 if (!pc()->AddIceCandidate(candidate)) {
114 success = false;
115 }
116 }
117 return success;
118 }
119
120 rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack(
121 FrameGeneratorCapturerVideoTrackSource::Config config,
122 Clock* clock) {
123 video_track_sources_.emplace_back(
124 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
125 config, clock));
126 video_track_sources_.back()->Start();
127 return rtc::scoped_refptr<VideoTrackInterface>(
128 pc_factory()->CreateVideoTrack(rtc::CreateRandomUuid(),
129 video_track_sources_.back()));
130 }
131
132 rtc::scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack(
133 const cricket::AudioOptions options) {
134 rtc::scoped_refptr<AudioSourceInterface> source =
135 pc_factory()->CreateAudioSource(options);
136 return pc_factory()->CreateAudioTrack(rtc::CreateRandomUuid(), source);
137 }
138
139 private:
Seth Hampsond1003d72018-06-22 15:40:16 -0700140 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
141 video_track_sources_;
142};
143
144// TODO(shampson): Paramaterize the test to run for both Plan B & Unified Plan.
145class PeerConnectionRampUpTest : public ::testing::Test {
146 public:
147 PeerConnectionRampUpTest()
148 : clock_(Clock::GetRealTimeClock()),
149 virtual_socket_server_(new rtc::VirtualSocketServer()),
150 firewall_socket_server_(
151 new rtc::FirewallSocketServer(virtual_socket_server_.get())),
152 network_thread_(new rtc::Thread(firewall_socket_server_.get())),
153 worker_thread_(rtc::Thread::Create()) {
154 network_thread_->SetName("PCNetworkThread", this);
155 worker_thread_->SetName("PCWorkerThread", this);
156 RTC_CHECK(network_thread_->Start());
157 RTC_CHECK(worker_thread_->Start());
158
159 virtual_socket_server_->set_bandwidth(kNetworkBandwidth / 8);
160 pc_factory_ = CreatePeerConnectionFactory(
161 network_thread_.get(), worker_thread_.get(), rtc::Thread::Current(),
162 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
163 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
164 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
165 nullptr /* audio_mixer */, nullptr /* audio_processing */);
166 }
167
168 virtual ~PeerConnectionRampUpTest() {
169 network_thread()->Invoke<void>(RTC_FROM_HERE,
170 [this] { turn_servers_.clear(); });
171 }
172
173 bool CreatePeerConnectionWrappers(const RTCConfiguration& caller_config,
174 const RTCConfiguration& callee_config) {
175 caller_ = CreatePeerConnectionWrapper(caller_config);
176 callee_ = CreatePeerConnectionWrapper(callee_config);
177 return caller_ && callee_;
178 }
179
180 std::unique_ptr<PeerConnectionWrapperForRampUpTest>
181 CreatePeerConnectionWrapper(const RTCConfiguration& config) {
182 auto* fake_network_manager = new rtc::FakeNetworkManager();
183 fake_network_manager->AddInterface(kDefaultLocalAddress);
184 fake_network_managers_.emplace_back(fake_network_manager);
Seth Hampsond1003d72018-06-22 15:40:16 -0700185
Karl Wiberg918f50c2018-07-05 11:40:33 +0200186 auto observer = absl::make_unique<MockPeerConnectionObserver>();
Seth Hampsonec207102018-06-29 11:12:19 -0700187 webrtc::PeerConnectionDependencies dependencies(observer.get());
188 cricket::BasicPortAllocator* port_allocator =
189 new cricket::BasicPortAllocator(fake_network_manager);
190 port_allocator->set_step_delay(cricket::kDefaultStepDelay);
191 dependencies.allocator =
192 std::unique_ptr<cricket::BasicPortAllocator>(port_allocator);
193 dependencies.tls_cert_verifier =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200194 absl::make_unique<rtc::TestCertificateVerifier>();
Seth Hampsonec207102018-06-29 11:12:19 -0700195
196 auto pc =
197 pc_factory_->CreatePeerConnection(config, std::move(dependencies));
Seth Hampsond1003d72018-06-22 15:40:16 -0700198 if (!pc) {
199 return nullptr;
200 }
201
Karl Wiberg918f50c2018-07-05 11:40:33 +0200202 return absl::make_unique<PeerConnectionWrapperForRampUpTest>(
Seth Hampsonec207102018-06-29 11:12:19 -0700203 pc_factory_, pc, std::move(observer));
Seth Hampsond1003d72018-06-22 15:40:16 -0700204 }
205
206 void SetupOneWayCall() {
207 ASSERT_TRUE(caller_);
208 ASSERT_TRUE(callee_);
209 FrameGeneratorCapturerVideoTrackSource::Config config;
210 caller_->AddTrack(caller_->CreateLocalVideoTrack(config, clock_));
211 // Disable highpass filter so that we can get all the test audio frames.
212 cricket::AudioOptions options;
213 options.highpass_filter = false;
214 caller_->AddTrack(caller_->CreateLocalAudioTrack(options));
215
216 // Do the SDP negotiation, and also exchange ice candidates.
217 ASSERT_TRUE(caller_->ExchangeOfferAnswerWith(callee_.get()));
218 ASSERT_TRUE_WAIT(
219 caller_->signaling_state() == PeerConnectionInterface::kStable,
220 kDefaultTimeoutMs);
221 ASSERT_TRUE_WAIT(caller_->IsIceGatheringDone(), kDefaultTimeoutMs);
222 ASSERT_TRUE_WAIT(callee_->IsIceGatheringDone(), kDefaultTimeoutMs);
223
224 // Connect an ICE candidate pairs.
225 ASSERT_TRUE(
226 callee_->AddIceCandidates(caller_->observer()->GetAllCandidates()));
227 ASSERT_TRUE(
228 caller_->AddIceCandidates(callee_->observer()->GetAllCandidates()));
229 // This means that ICE and DTLS are connected.
230 ASSERT_TRUE_WAIT(callee_->IsIceConnected(), kDefaultTimeoutMs);
231 ASSERT_TRUE_WAIT(caller_->IsIceConnected(), kDefaultTimeoutMs);
232 }
233
Seth Hampsonec207102018-06-29 11:12:19 -0700234 void CreateTurnServer(cricket::ProtocolType type,
235 const std::string& common_name = "test turn server") {
Seth Hampsond1003d72018-06-22 15:40:16 -0700236 rtc::Thread* thread = network_thread();
237 std::unique_ptr<cricket::TestTurnServer> turn_server =
238 network_thread_->Invoke<std::unique_ptr<cricket::TestTurnServer>>(
Seth Hampsonec207102018-06-29 11:12:19 -0700239 RTC_FROM_HERE, [thread, type, common_name] {
Seth Hampsond1003d72018-06-22 15:40:16 -0700240 static const rtc::SocketAddress turn_server_internal_address{
Seth Hampsonec207102018-06-29 11:12:19 -0700241 kTurnInternalAddress, kTurnInternalPort};
Seth Hampsond1003d72018-06-22 15:40:16 -0700242 static const rtc::SocketAddress turn_server_external_address{
Seth Hampsonec207102018-06-29 11:12:19 -0700243 kTurnExternalAddress, kTurnExternalPort};
Karl Wiberg918f50c2018-07-05 11:40:33 +0200244 return absl::make_unique<cricket::TestTurnServer>(
Seth Hampsond1003d72018-06-22 15:40:16 -0700245 thread, turn_server_internal_address,
Seth Hampsonec207102018-06-29 11:12:19 -0700246 turn_server_external_address, type,
247 true /*ignore_bad_certs=*/, common_name);
Seth Hampsond1003d72018-06-22 15:40:16 -0700248 });
249 turn_servers_.push_back(std::move(turn_server));
250 }
251
252 // First runs the call for kRampUpTimeMs to ramp up the bandwidth estimate.
253 // Then runs the test for the remaining test time, grabbing the bandwidth
254 // estimation stat, every kPollIntervalTimeMs. When finished, averages the
255 // bandwidth estimations and prints the bandwidth estimation result as a perf
256 // metric.
257 void RunTest(const std::string& test_string) {
258 rtc::Thread::Current()->ProcessMessages(kRampUpTimeMs);
259 int number_of_polls =
260 (kDefaultTestTimeMs - kRampUpTimeMs) / kPollIntervalTimeMs;
261 int total_bwe = 0;
262 for (int i = 0; i < number_of_polls; ++i) {
263 rtc::Thread::Current()->ProcessMessages(kPollIntervalTimeMs);
264 total_bwe += static_cast<int>(GetCallerAvailableBitrateEstimate());
265 }
266 double average_bandwidth_estimate = total_bwe / number_of_polls;
267 std::string value_description =
268 "bwe_after_" + std::to_string(kDefaultTestTimeMs / 1000) + "_seconds";
269 test::PrintResult("peerconnection_ramp_up_", test_string, value_description,
270 average_bandwidth_estimate, "bwe", false);
271 }
272
273 rtc::Thread* network_thread() { return network_thread_.get(); }
274
Seth Hampsonec207102018-06-29 11:12:19 -0700275 rtc::FirewallSocketServer* firewall_socket_server() {
276 return firewall_socket_server_.get();
277 }
278
Seth Hampsond1003d72018-06-22 15:40:16 -0700279 PeerConnectionWrapperForRampUpTest* caller() { return caller_.get(); }
280
281 PeerConnectionWrapperForRampUpTest* callee() { return callee_.get(); }
282
283 private:
284 // Gets the caller's outgoing available bitrate from the stats. Returns 0 if
285 // something went wrong. It takes the outgoing bitrate from the current
286 // selected ICE candidate pair's stats.
287 double GetCallerAvailableBitrateEstimate() {
288 auto stats = caller_->GetStats();
289 auto transport_stats = stats->GetStatsOfType<RTCTransportStats>();
290 if (transport_stats.size() == 0u ||
291 !transport_stats[0]->selected_candidate_pair_id.is_defined()) {
292 return 0;
293 }
294 std::string selected_ice_id =
295 transport_stats[0]->selected_candidate_pair_id.ValueToString();
296 // Use the selected ICE candidate pair ID to get the appropriate ICE stats.
297 const RTCIceCandidatePairStats ice_candidate_pair_stats =
298 stats->Get(selected_ice_id)->cast_to<const RTCIceCandidatePairStats>();
299 if (ice_candidate_pair_stats.available_outgoing_bitrate.is_defined()) {
300 return *ice_candidate_pair_stats.available_outgoing_bitrate;
301 }
302 // We couldn't get the |available_outgoing_bitrate| for the active candidate
303 // pair.
304 return 0;
305 }
306
307 Clock* const clock_;
308 // The turn servers should be accessed & deleted on the network thread to
309 // avoid a race with the socket read/write which occurs on the network thread.
310 std::vector<std::unique_ptr<cricket::TestTurnServer>> turn_servers_;
311 // |virtual_socket_server_| is used by |network_thread_| so it must be
312 // destroyed later.
313 // TODO(bugs.webrtc.org/7668): We would like to update the virtual network we
314 // use for this test. VirtualSocketServer isn't ideal because:
315 // 1) It uses the same queue & network capacity for both directions.
316 // 2) VirtualSocketServer implements how the network bandwidth affects the
317 // send delay differently than the SimulatedNetwork, used by the
318 // FakeNetworkPipe. It would be ideal if all of levels of virtual
319 // networks used in testing were consistent.
320 // We would also like to update this test to record the time to ramp up,
321 // down, and back up (similar to in rampup_tests.cc). This is problematic with
322 // the VirtualSocketServer. The first ramp down time is very noisy and the
323 // second ramp up time can take up to 300 seconds, most likely due to a built
324 // up queue.
325 std::unique_ptr<rtc::VirtualSocketServer> virtual_socket_server_;
326 std::unique_ptr<rtc::FirewallSocketServer> firewall_socket_server_;
327 std::unique_ptr<rtc::Thread> network_thread_;
328 std::unique_ptr<rtc::Thread> worker_thread_;
329 // The |pc_factory| uses |network_thread_| & |worker_thread_|, so it must be
330 // destroyed first.
331 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_network_managers_;
332 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
333 std::unique_ptr<PeerConnectionWrapperForRampUpTest> caller_;
334 std::unique_ptr<PeerConnectionWrapperForRampUpTest> callee_;
335};
336
337TEST_F(PeerConnectionRampUpTest, TurnOverTCP) {
338 CreateTurnServer(cricket::ProtocolType::PROTO_TCP);
339 PeerConnectionInterface::IceServer ice_server;
Seth Hampsonec207102018-06-29 11:12:19 -0700340 std::string ice_server_url = "turn:" + std::string(kTurnInternalAddress) +
341 ":" + std::to_string(kTurnInternalPort) +
342 "?transport=tcp";
343 ice_server.urls.push_back(ice_server_url);
Seth Hampsond1003d72018-06-22 15:40:16 -0700344 ice_server.username = "test";
345 ice_server.password = "test";
346 PeerConnectionInterface::RTCConfiguration client_1_config;
347 client_1_config.servers.push_back(ice_server);
348 client_1_config.type = PeerConnectionInterface::kRelay;
349 PeerConnectionInterface::RTCConfiguration client_2_config;
350 client_2_config.servers.push_back(ice_server);
351 client_2_config.type = PeerConnectionInterface::kRelay;
352 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
353
354 SetupOneWayCall();
355 RunTest("turn_over_tcp");
356}
357
Seth Hampsonec207102018-06-29 11:12:19 -0700358TEST_F(PeerConnectionRampUpTest, TurnOverUDP) {
359 CreateTurnServer(cricket::ProtocolType::PROTO_UDP);
360 PeerConnectionInterface::IceServer ice_server;
361 std::string ice_server_url = "turn:" + std::string(kTurnInternalAddress) +
362 ":" + std::to_string(kTurnInternalPort);
363
364 ice_server.urls.push_back(ice_server_url);
365 ice_server.username = "test";
366 ice_server.password = "test";
367 PeerConnectionInterface::RTCConfiguration client_1_config;
368 client_1_config.servers.push_back(ice_server);
369 client_1_config.type = PeerConnectionInterface::kRelay;
370 PeerConnectionInterface::RTCConfiguration client_2_config;
371 client_2_config.servers.push_back(ice_server);
372 client_2_config.type = PeerConnectionInterface::kRelay;
373 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
374
375 SetupOneWayCall();
376 RunTest("turn_over_udp");
377}
378
379TEST_F(PeerConnectionRampUpTest, TurnOverTLS) {
380 CreateTurnServer(cricket::ProtocolType::PROTO_TLS, kTurnInternalAddress);
381 PeerConnectionInterface::IceServer ice_server;
382 std::string ice_server_url = "turns:" + std::string(kTurnInternalAddress) +
383 ":" + std::to_string(kTurnInternalPort) +
384 "?transport=tcp";
385 ice_server.urls.push_back(ice_server_url);
386 ice_server.username = "test";
387 ice_server.password = "test";
388 PeerConnectionInterface::RTCConfiguration client_1_config;
389 client_1_config.servers.push_back(ice_server);
390 client_1_config.type = PeerConnectionInterface::kRelay;
391 PeerConnectionInterface::RTCConfiguration client_2_config;
392 client_2_config.servers.push_back(ice_server);
393 client_2_config.type = PeerConnectionInterface::kRelay;
394
395 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
396
397 SetupOneWayCall();
398 RunTest("turn_over_tls");
399}
400
401TEST_F(PeerConnectionRampUpTest, UDPPeerToPeer) {
402 PeerConnectionInterface::RTCConfiguration client_1_config;
403 client_1_config.tcp_candidate_policy =
404 PeerConnection::kTcpCandidatePolicyDisabled;
405 PeerConnectionInterface::RTCConfiguration client_2_config;
406 client_2_config.tcp_candidate_policy =
407 PeerConnection::kTcpCandidatePolicyDisabled;
408 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
409
410 SetupOneWayCall();
411 RunTest("udp_peer_to_peer");
412}
413
414TEST_F(PeerConnectionRampUpTest, TCPPeerToPeer) {
415 firewall_socket_server()->set_udp_sockets_enabled(false);
416 ASSERT_TRUE(CreatePeerConnectionWrappers(
417 PeerConnectionInterface::RTCConfiguration(),
418 PeerConnectionInterface::RTCConfiguration()));
419
420 SetupOneWayCall();
421 RunTest("tcp_peer_to_peer");
422}
Seth Hampsond1003d72018-06-22 15:40:16 -0700423
424} // namespace webrtc