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