blob: 74cbb0fdffc3a0308af1902d5966a6d39e0e6294 [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"
14#include "api/test/fakeconstraints.h"
15#include "api/video_codecs/builtin_video_decoder_factory.h"
16#include "api/video_codecs/builtin_video_encoder_factory.h"
17#include "p2p/base/testturnserver.h"
18#include "p2p/client/basicportallocator.h"
19#include "pc/peerconnection.h"
20#include "pc/peerconnectionwrapper.h"
21#include "pc/test/fakeaudiocapturemodule.h"
22#include "pc/test/fakeperiodicvideotracksource.h"
23#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"
30#include "rtc_base/virtualsocketserver.h"
31#include "test/gtest.h"
32#include "test/testsupport/perf_test.h"
33
34namespace webrtc {
35
36namespace {
37static const int kDefaultTestTimeMs = 15000;
38static const int kRampUpTimeMs = 5000;
39static const int kPollIntervalTimeMs = 50;
40static const int kDefaultTimeoutMs = 10000;
41static const rtc::SocketAddress kDefaultLocalAddress("1.1.1.1", 0);
42// The video's configured max bitrate in webrtcvideoengine.cc is 1.7 Mbps.
43// Setting the network bandwidth to 1 Mbps allows the video's bitrate to push
44// the network's limitations.
45static const int kNetworkBandwidth = 1000000;
46} // namespace
47
48using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
49
50// This is an end to end test to verify that BWE is functioning when setting
51// up a one to one call at the PeerConnection level. The intention of the test
52// is to catch potential regressions for different ICE path configurations. The
53// test uses a VirtualSocketServer for it's underlying simulated network and
54// fake audio and video sources. The test is based upon rampup_tests.cc, but
55// instead is at the PeerConnection level and uses a different fake network
56// (rampup_tests.cc uses SimulatedNetwork). In the future, this test could
57// potentially test different network conditions and test video quality as well
58// (video_quality_test.cc does this, but at the call level).
59//
60// The perf test results are printed using the perf test support. If the
61// isolated_script_test_perf_output flag is specified in test_main.cc, then
62// the results are written to a JSON formatted file for the Chrome perf
63// dashboard. Since this test is a webrtc_perf_test, it will be run in the perf
64// console every webrtc commit.
65class PeerConnectionWrapperForRampUpTest : public PeerConnectionWrapper {
66 public:
67 using PeerConnectionWrapper::PeerConnectionWrapper;
68
69 PeerConnectionWrapperForRampUpTest(
70 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
71 rtc::scoped_refptr<PeerConnectionInterface> pc,
72 std::unique_ptr<MockPeerConnectionObserver> observer,
73 rtc::FakeNetworkManager* fake_network_manager)
74 : PeerConnectionWrapper::PeerConnectionWrapper(pc_factory,
75 pc,
76 std::move(observer)),
77 fake_network_manager_(std::move(fake_network_manager)) {}
78
79 bool AddIceCandidates(std::vector<const IceCandidateInterface*> candidates) {
80 bool success = true;
81 for (const auto candidate : candidates) {
82 if (!pc()->AddIceCandidate(candidate)) {
83 success = false;
84 }
85 }
86 return success;
87 }
88
89 rtc::scoped_refptr<VideoTrackInterface> CreateLocalVideoTrack(
90 FrameGeneratorCapturerVideoTrackSource::Config config,
91 Clock* clock) {
92 video_track_sources_.emplace_back(
93 new rtc::RefCountedObject<FrameGeneratorCapturerVideoTrackSource>(
94 config, clock));
95 video_track_sources_.back()->Start();
96 return rtc::scoped_refptr<VideoTrackInterface>(
97 pc_factory()->CreateVideoTrack(rtc::CreateRandomUuid(),
98 video_track_sources_.back()));
99 }
100
101 rtc::scoped_refptr<AudioTrackInterface> CreateLocalAudioTrack(
102 const cricket::AudioOptions options) {
103 rtc::scoped_refptr<AudioSourceInterface> source =
104 pc_factory()->CreateAudioSource(options);
105 return pc_factory()->CreateAudioTrack(rtc::CreateRandomUuid(), source);
106 }
107
108 private:
109 // This is owned by the Test, not the Wrapper. It needs to outlive the
110 // Wrapper, because the port allocator expects its lifetime to be longer than
111 // the PeerConnection's lifetime.
112 rtc::FakeNetworkManager* fake_network_manager_;
113 std::vector<rtc::scoped_refptr<FrameGeneratorCapturerVideoTrackSource>>
114 video_track_sources_;
115};
116
117// TODO(shampson): Paramaterize the test to run for both Plan B & Unified Plan.
118class PeerConnectionRampUpTest : public ::testing::Test {
119 public:
120 PeerConnectionRampUpTest()
121 : clock_(Clock::GetRealTimeClock()),
122 virtual_socket_server_(new rtc::VirtualSocketServer()),
123 firewall_socket_server_(
124 new rtc::FirewallSocketServer(virtual_socket_server_.get())),
125 network_thread_(new rtc::Thread(firewall_socket_server_.get())),
126 worker_thread_(rtc::Thread::Create()) {
127 network_thread_->SetName("PCNetworkThread", this);
128 worker_thread_->SetName("PCWorkerThread", this);
129 RTC_CHECK(network_thread_->Start());
130 RTC_CHECK(worker_thread_->Start());
131
132 virtual_socket_server_->set_bandwidth(kNetworkBandwidth / 8);
133 pc_factory_ = CreatePeerConnectionFactory(
134 network_thread_.get(), worker_thread_.get(), rtc::Thread::Current(),
135 rtc::scoped_refptr<AudioDeviceModule>(FakeAudioCaptureModule::Create()),
136 CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory(),
137 CreateBuiltinVideoEncoderFactory(), CreateBuiltinVideoDecoderFactory(),
138 nullptr /* audio_mixer */, nullptr /* audio_processing */);
139 }
140
141 virtual ~PeerConnectionRampUpTest() {
142 network_thread()->Invoke<void>(RTC_FROM_HERE,
143 [this] { turn_servers_.clear(); });
144 }
145
146 bool CreatePeerConnectionWrappers(const RTCConfiguration& caller_config,
147 const RTCConfiguration& callee_config) {
148 caller_ = CreatePeerConnectionWrapper(caller_config);
149 callee_ = CreatePeerConnectionWrapper(callee_config);
150 return caller_ && callee_;
151 }
152
153 std::unique_ptr<PeerConnectionWrapperForRampUpTest>
154 CreatePeerConnectionWrapper(const RTCConfiguration& config) {
155 auto* fake_network_manager = new rtc::FakeNetworkManager();
156 fake_network_manager->AddInterface(kDefaultLocalAddress);
157 fake_network_managers_.emplace_back(fake_network_manager);
158 auto port_allocator =
159 rtc::MakeUnique<cricket::BasicPortAllocator>(fake_network_manager);
160
161 port_allocator->set_step_delay(cricket::kDefaultStepDelay);
162 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
163 auto pc = pc_factory_->CreatePeerConnection(
164 config, std::move(port_allocator), nullptr, observer.get());
165 if (!pc) {
166 return nullptr;
167 }
168
169 return rtc::MakeUnique<PeerConnectionWrapperForRampUpTest>(
170 pc_factory_, pc, std::move(observer), fake_network_manager);
171 }
172
173 void SetupOneWayCall() {
174 ASSERT_TRUE(caller_);
175 ASSERT_TRUE(callee_);
176 FrameGeneratorCapturerVideoTrackSource::Config config;
177 caller_->AddTrack(caller_->CreateLocalVideoTrack(config, clock_));
178 // Disable highpass filter so that we can get all the test audio frames.
179 cricket::AudioOptions options;
180 options.highpass_filter = false;
181 caller_->AddTrack(caller_->CreateLocalAudioTrack(options));
182
183 // Do the SDP negotiation, and also exchange ice candidates.
184 ASSERT_TRUE(caller_->ExchangeOfferAnswerWith(callee_.get()));
185 ASSERT_TRUE_WAIT(
186 caller_->signaling_state() == PeerConnectionInterface::kStable,
187 kDefaultTimeoutMs);
188 ASSERT_TRUE_WAIT(caller_->IsIceGatheringDone(), kDefaultTimeoutMs);
189 ASSERT_TRUE_WAIT(callee_->IsIceGatheringDone(), kDefaultTimeoutMs);
190
191 // Connect an ICE candidate pairs.
192 ASSERT_TRUE(
193 callee_->AddIceCandidates(caller_->observer()->GetAllCandidates()));
194 ASSERT_TRUE(
195 caller_->AddIceCandidates(callee_->observer()->GetAllCandidates()));
196 // This means that ICE and DTLS are connected.
197 ASSERT_TRUE_WAIT(callee_->IsIceConnected(), kDefaultTimeoutMs);
198 ASSERT_TRUE_WAIT(caller_->IsIceConnected(), kDefaultTimeoutMs);
199 }
200
201 void CreateTurnServer(cricket::ProtocolType type) {
202 rtc::Thread* thread = network_thread();
203 std::unique_ptr<cricket::TestTurnServer> turn_server =
204 network_thread_->Invoke<std::unique_ptr<cricket::TestTurnServer>>(
205 RTC_FROM_HERE, [thread, type] {
206 static const rtc::SocketAddress turn_server_internal_address{
207 "88.88.88.0", 3478};
208 static const rtc::SocketAddress turn_server_external_address{
209 "88.88.88.1", 0};
210 return rtc::MakeUnique<cricket::TestTurnServer>(
211 thread, turn_server_internal_address,
212 turn_server_external_address, type);
213 });
214 turn_servers_.push_back(std::move(turn_server));
215 }
216
217 // First runs the call for kRampUpTimeMs to ramp up the bandwidth estimate.
218 // Then runs the test for the remaining test time, grabbing the bandwidth
219 // estimation stat, every kPollIntervalTimeMs. When finished, averages the
220 // bandwidth estimations and prints the bandwidth estimation result as a perf
221 // metric.
222 void RunTest(const std::string& test_string) {
223 rtc::Thread::Current()->ProcessMessages(kRampUpTimeMs);
224 int number_of_polls =
225 (kDefaultTestTimeMs - kRampUpTimeMs) / kPollIntervalTimeMs;
226 int total_bwe = 0;
227 for (int i = 0; i < number_of_polls; ++i) {
228 rtc::Thread::Current()->ProcessMessages(kPollIntervalTimeMs);
229 total_bwe += static_cast<int>(GetCallerAvailableBitrateEstimate());
230 }
231 double average_bandwidth_estimate = total_bwe / number_of_polls;
232 std::string value_description =
233 "bwe_after_" + std::to_string(kDefaultTestTimeMs / 1000) + "_seconds";
234 test::PrintResult("peerconnection_ramp_up_", test_string, value_description,
235 average_bandwidth_estimate, "bwe", false);
236 }
237
238 rtc::Thread* network_thread() { return network_thread_.get(); }
239
240 PeerConnectionWrapperForRampUpTest* caller() { return caller_.get(); }
241
242 PeerConnectionWrapperForRampUpTest* callee() { return callee_.get(); }
243
244 private:
245 // Gets the caller's outgoing available bitrate from the stats. Returns 0 if
246 // something went wrong. It takes the outgoing bitrate from the current
247 // selected ICE candidate pair's stats.
248 double GetCallerAvailableBitrateEstimate() {
249 auto stats = caller_->GetStats();
250 auto transport_stats = stats->GetStatsOfType<RTCTransportStats>();
251 if (transport_stats.size() == 0u ||
252 !transport_stats[0]->selected_candidate_pair_id.is_defined()) {
253 return 0;
254 }
255 std::string selected_ice_id =
256 transport_stats[0]->selected_candidate_pair_id.ValueToString();
257 // Use the selected ICE candidate pair ID to get the appropriate ICE stats.
258 const RTCIceCandidatePairStats ice_candidate_pair_stats =
259 stats->Get(selected_ice_id)->cast_to<const RTCIceCandidatePairStats>();
260 if (ice_candidate_pair_stats.available_outgoing_bitrate.is_defined()) {
261 return *ice_candidate_pair_stats.available_outgoing_bitrate;
262 }
263 // We couldn't get the |available_outgoing_bitrate| for the active candidate
264 // pair.
265 return 0;
266 }
267
268 Clock* const clock_;
269 // The turn servers should be accessed & deleted on the network thread to
270 // avoid a race with the socket read/write which occurs on the network thread.
271 std::vector<std::unique_ptr<cricket::TestTurnServer>> turn_servers_;
272 // |virtual_socket_server_| is used by |network_thread_| so it must be
273 // destroyed later.
274 // TODO(bugs.webrtc.org/7668): We would like to update the virtual network we
275 // use for this test. VirtualSocketServer isn't ideal because:
276 // 1) It uses the same queue & network capacity for both directions.
277 // 2) VirtualSocketServer implements how the network bandwidth affects the
278 // send delay differently than the SimulatedNetwork, used by the
279 // FakeNetworkPipe. It would be ideal if all of levels of virtual
280 // networks used in testing were consistent.
281 // We would also like to update this test to record the time to ramp up,
282 // down, and back up (similar to in rampup_tests.cc). This is problematic with
283 // the VirtualSocketServer. The first ramp down time is very noisy and the
284 // second ramp up time can take up to 300 seconds, most likely due to a built
285 // up queue.
286 std::unique_ptr<rtc::VirtualSocketServer> virtual_socket_server_;
287 std::unique_ptr<rtc::FirewallSocketServer> firewall_socket_server_;
288 std::unique_ptr<rtc::Thread> network_thread_;
289 std::unique_ptr<rtc::Thread> worker_thread_;
290 // The |pc_factory| uses |network_thread_| & |worker_thread_|, so it must be
291 // destroyed first.
292 std::vector<std::unique_ptr<rtc::FakeNetworkManager>> fake_network_managers_;
293 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
294 std::unique_ptr<PeerConnectionWrapperForRampUpTest> caller_;
295 std::unique_ptr<PeerConnectionWrapperForRampUpTest> callee_;
296};
297
298TEST_F(PeerConnectionRampUpTest, TurnOverTCP) {
299 CreateTurnServer(cricket::ProtocolType::PROTO_TCP);
300 PeerConnectionInterface::IceServer ice_server;
301 ice_server.urls.push_back("turn:88.88.88.0:3478?transport=tcp");
302 ice_server.username = "test";
303 ice_server.password = "test";
304 PeerConnectionInterface::RTCConfiguration client_1_config;
305 client_1_config.servers.push_back(ice_server);
306 client_1_config.type = PeerConnectionInterface::kRelay;
307 PeerConnectionInterface::RTCConfiguration client_2_config;
308 client_2_config.servers.push_back(ice_server);
309 client_2_config.type = PeerConnectionInterface::kRelay;
310 ASSERT_TRUE(CreatePeerConnectionWrappers(client_1_config, client_2_config));
311
312 SetupOneWayCall();
313 RunTest("turn_over_tcp");
314}
315
316// TODO(bugs.webrtc.org/7668): Test other ICE configurations.
317
318} // namespace webrtc