blob: e77e3087af1e404e44ea3ba5af06e40d58062466 [file] [log] [blame]
Sebastian Jansson98b07e92018-09-27 13:47:01 +02001/*
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#include "test/scenario/scenario.h"
11
12#include <algorithm>
13
14#include "api/audio_codecs/builtin_audio_decoder_factory.h"
15#include "api/audio_codecs/builtin_audio_encoder_factory.h"
16#include "rtc_base/flags.h"
17#include "test/testsupport/fileutils.h"
18
Mirko Bonadei2dfa9982018-10-18 11:35:32 +020019WEBRTC_DEFINE_bool(scenario_logs, false, "Save logs from scenario framework.");
Sebastian Jansson98b07e92018-09-27 13:47:01 +020020
21namespace webrtc {
22namespace test {
23namespace {
24int64_t kMicrosPerSec = 1000000;
25}
26
27RepeatedActivity::RepeatedActivity(TimeDelta interval,
28 std::function<void(TimeDelta)> function)
29 : interval_(interval), function_(function) {}
30
31void RepeatedActivity::Stop() {
32 interval_ = TimeDelta::PlusInfinity();
33}
34
35void RepeatedActivity::Poll(Timestamp time) {
36 RTC_DCHECK(last_update_.IsFinite());
37 if (time >= last_update_ + interval_) {
38 function_(time - last_update_);
39 last_update_ = time;
40 }
41}
42
43void RepeatedActivity::SetStartTime(Timestamp time) {
44 last_update_ = time;
45}
46
47Timestamp RepeatedActivity::NextTime() {
48 RTC_DCHECK(last_update_.IsFinite());
49 return last_update_ + interval_;
50}
51
52Scenario::Scenario() : Scenario("", true) {}
53
54Scenario::Scenario(std::string file_name) : Scenario(file_name, true) {}
55
56Scenario::Scenario(std::string file_name, bool real_time)
57 : real_time_mode_(real_time),
58 sim_clock_(100000 * kMicrosPerSec),
59 clock_(real_time ? Clock::GetRealTimeClock() : &sim_clock_),
60 audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()),
61 audio_encoder_factory_(CreateBuiltinAudioEncoderFactory()) {
62 if (FLAG_scenario_logs && !file_name.empty()) {
63 CreateDir(OutputPath() + "output_data");
64 for (size_t i = 0; i < file_name.size(); ++i) {
65 if (file_name[i] == '/')
66 CreateDir(OutputPath() + "output_data/" + file_name.substr(0, i));
67 }
68 base_filename_ = OutputPath() + "output_data/" + file_name;
69 RTC_LOG(LS_INFO) << "Saving scenario logs to: " << base_filename_;
70 }
Sebastian Jansson71a091e2018-09-27 19:08:21 +020071 if (!real_time_mode_ && !base_filename_.empty()) {
Sebastian Jansson98b07e92018-09-27 13:47:01 +020072 rtc::SetClockForTesting(&event_log_fake_clock_);
73 event_log_fake_clock_.SetTimeNanos(sim_clock_.TimeInMicroseconds() * 1000);
74 }
75}
76
77Scenario::~Scenario() {
78 if (!real_time_mode_)
79 rtc::SetClockForTesting(nullptr);
80}
81
82ColumnPrinter Scenario::TimePrinter() {
83 return ColumnPrinter::Lambda("time",
84 [this](rtc::SimpleStringBuilder& sb) {
85 sb.AppendFormat("%.3lf",
86 Now().seconds<double>());
87 },
88 32);
89}
90
91StatesPrinter* Scenario::CreatePrinter(std::string name,
92 TimeDelta interval,
93 std::vector<ColumnPrinter> printers) {
94 std::vector<ColumnPrinter> all_printers{TimePrinter()};
95 for (auto& printer : printers)
96 all_printers.push_back(printer);
97 StatesPrinter* printer =
98 new StatesPrinter(GetFullPathOrEmpty(name), all_printers);
99 printers_.emplace_back(printer);
100 printer->PrintHeaders();
101 if (interval.IsFinite())
102 Every(interval, [printer] { printer->PrintRow(); });
103 return printer;
104}
105
106CallClient* Scenario::CreateClient(std::string name, CallClientConfig config) {
Sebastian Jansson71a091e2018-09-27 19:08:21 +0200107 RTC_DCHECK(real_time_mode_);
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200108 CallClient* client = new CallClient(clock_, GetFullPathOrEmpty(name), config);
109 if (config.transport.state_log_interval.IsFinite()) {
110 Every(config.transport.state_log_interval, [this, client]() {
111 client->network_controller_factory_.LogCongestionControllerStats(Now());
112 });
113 }
114 clients_.emplace_back(client);
115 return client;
116}
117
118CallClient* Scenario::CreateClient(
119 std::string name,
120 std::function<void(CallClientConfig*)> config_modifier) {
121 CallClientConfig config;
122 config_modifier(&config);
123 return CreateClient(name, config);
124}
125
Sebastian Jansson800e1212018-10-22 11:49:03 +0200126CallClientPair* Scenario::CreateRoutes(CallClient* first,
127 std::vector<NetworkNode*> send_link,
128 CallClient* second,
129 std::vector<NetworkNode*> return_link) {
130 return CreateRoutes(first, send_link,
131 DataSize::bytes(PacketOverhead::kDefault), second,
132 return_link, DataSize::bytes(PacketOverhead::kDefault));
133}
134
135CallClientPair* Scenario::CreateRoutes(CallClient* first,
136 std::vector<NetworkNode*> send_link,
137 DataSize first_overhead,
138 CallClient* second,
139 std::vector<NetworkNode*> return_link,
140 DataSize second_overhead) {
141 CallClientPair* client_pair = new CallClientPair(first, second);
142 ChangeRoute(client_pair->forward(), send_link, first_overhead);
143 ChangeRoute(client_pair->reverse(), return_link, second_overhead);
144 client_pairs_.emplace_back(client_pair);
145 return client_pair;
146}
147
148void Scenario::ChangeRoute(std::pair<CallClient*, CallClient*> clients,
149 std::vector<NetworkNode*> over_nodes) {
150 ChangeRoute(clients, over_nodes, DataSize::bytes(PacketOverhead::kDefault));
151}
152
153void Scenario::ChangeRoute(std::pair<CallClient*, CallClient*> clients,
154 std::vector<NetworkNode*> over_nodes,
155 DataSize overhead) {
156 uint64_t route_id = next_route_id_++;
157 clients.second->route_overhead_.insert({route_id, overhead});
158 NetworkNode::Route(route_id, over_nodes, clients.second);
159 clients.first->transport_.Connect(over_nodes.front(), route_id, overhead);
160}
161
Sebastian Jansson71a091e2018-09-27 19:08:21 +0200162SimulatedTimeClient* Scenario::CreateSimulatedTimeClient(
163 std::string name,
164 SimulatedTimeClientConfig config,
165 std::vector<PacketStreamConfig> stream_configs,
166 std::vector<NetworkNode*> send_link,
167 std::vector<NetworkNode*> return_link) {
Sebastian Jansson800e1212018-10-22 11:49:03 +0200168 uint64_t send_id = next_route_id_++;
169 uint64_t return_id = next_route_id_++;
Sebastian Jansson71a091e2018-09-27 19:08:21 +0200170 SimulatedTimeClient* client = new SimulatedTimeClient(
171 GetFullPathOrEmpty(name), config, stream_configs, send_link, return_link,
172 send_id, return_id, Now());
173 if (!base_filename_.empty() && !name.empty() &&
174 config.transport.state_log_interval.IsFinite()) {
175 Every(config.transport.state_log_interval, [this, client]() {
176 client->network_controller_factory_.LogCongestionControllerStats(Now());
177 });
178 }
179
180 Every(client->GetNetworkControllerProcessInterval(),
181 [this, client] { client->CongestionProcess(Now()); });
182 Every(TimeDelta::ms(5), [this, client] { client->PacerProcess(Now()); });
183 simulated_time_clients_.emplace_back(client);
184 return client;
185}
186
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200187SimulationNode* Scenario::CreateSimulationNode(
188 std::function<void(NetworkNodeConfig*)> config_modifier) {
189 NetworkNodeConfig config;
190 config_modifier(&config);
191 return CreateSimulationNode(config);
192}
193
194SimulationNode* Scenario::CreateSimulationNode(NetworkNodeConfig config) {
195 RTC_DCHECK(config.mode == NetworkNodeConfig::TrafficMode::kSimulation);
196 auto network_node = SimulationNode::Create(config);
197 SimulationNode* sim_node = network_node.get();
198 network_nodes_.emplace_back(std::move(network_node));
199 Every(config.update_frequency,
200 [this, sim_node] { sim_node->Process(Now()); });
201 return sim_node;
202}
203
204NetworkNode* Scenario::CreateNetworkNode(
205 NetworkNodeConfig config,
Artem Titov8ea1e9d2018-10-04 14:46:31 +0200206 std::unique_ptr<NetworkBehaviorInterface> behavior) {
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200207 RTC_DCHECK(config.mode == NetworkNodeConfig::TrafficMode::kCustom);
Artem Titov8ea1e9d2018-10-04 14:46:31 +0200208 network_nodes_.emplace_back(new NetworkNode(config, std::move(behavior)));
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200209 NetworkNode* network_node = network_nodes_.back().get();
210 Every(config.update_frequency,
211 [this, network_node] { network_node->Process(Now()); });
212 return network_node;
213}
214
215void Scenario::TriggerPacketBurst(std::vector<NetworkNode*> over_nodes,
216 size_t num_packets,
217 size_t packet_size) {
Sebastian Jansson800e1212018-10-22 11:49:03 +0200218 int64_t route_id = next_route_id_++;
219 NetworkNode::Route(route_id, over_nodes, &null_receiver_);
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200220 for (size_t i = 0; i < num_packets; ++i)
221 over_nodes[0]->TryDeliverPacket(rtc::CopyOnWriteBuffer(packet_size),
Sebastian Jansson800e1212018-10-22 11:49:03 +0200222 route_id, Now());
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200223}
224
225void Scenario::NetworkDelayedAction(std::vector<NetworkNode*> over_nodes,
226 size_t packet_size,
227 std::function<void()> action) {
Sebastian Jansson800e1212018-10-22 11:49:03 +0200228 int64_t route_id = next_route_id_++;
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200229 action_receivers_.emplace_back(new ActionReceiver(action));
Sebastian Jansson800e1212018-10-22 11:49:03 +0200230 NetworkNode::Route(route_id, over_nodes, action_receivers_.back().get());
231 over_nodes[0]->TryDeliverPacket(rtc::CopyOnWriteBuffer(packet_size), route_id,
232 Now());
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200233}
234
235CrossTrafficSource* Scenario::CreateCrossTraffic(
236 std::vector<NetworkNode*> over_nodes,
237 std::function<void(CrossTrafficConfig*)> config_modifier) {
238 CrossTrafficConfig cross_config;
239 config_modifier(&cross_config);
240 return CreateCrossTraffic(over_nodes, cross_config);
241}
242
243CrossTrafficSource* Scenario::CreateCrossTraffic(
244 std::vector<NetworkNode*> over_nodes,
245 CrossTrafficConfig config) {
Sebastian Jansson800e1212018-10-22 11:49:03 +0200246 int64_t route_id = next_route_id_++;
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200247 cross_traffic_sources_.emplace_back(
Sebastian Jansson800e1212018-10-22 11:49:03 +0200248 new CrossTrafficSource(over_nodes.front(), route_id, config));
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200249 CrossTrafficSource* node = cross_traffic_sources_.back().get();
Sebastian Jansson800e1212018-10-22 11:49:03 +0200250 NetworkNode::Route(route_id, over_nodes, &null_receiver_);
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200251 Every(config.min_packet_interval,
252 [this, node](TimeDelta delta) { node->Process(Now(), delta); });
253 return node;
254}
255
256VideoStreamPair* Scenario::CreateVideoStream(
Sebastian Jansson800e1212018-10-22 11:49:03 +0200257 std::pair<CallClient*, CallClient*> clients,
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200258 std::function<void(VideoStreamConfig*)> config_modifier) {
259 VideoStreamConfig config;
260 config_modifier(&config);
Sebastian Jansson800e1212018-10-22 11:49:03 +0200261 return CreateVideoStream(clients, config);
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200262}
263
264VideoStreamPair* Scenario::CreateVideoStream(
Sebastian Jansson800e1212018-10-22 11:49:03 +0200265 std::pair<CallClient*, CallClient*> clients,
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200266 VideoStreamConfig config) {
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200267 video_streams_.emplace_back(
Sebastian Jansson800e1212018-10-22 11:49:03 +0200268 new VideoStreamPair(clients.first, clients.second, config));
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200269 return video_streams_.back().get();
270}
271
272AudioStreamPair* Scenario::CreateAudioStream(
Sebastian Jansson800e1212018-10-22 11:49:03 +0200273 std::pair<CallClient*, CallClient*> clients,
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200274 std::function<void(AudioStreamConfig*)> config_modifier) {
275 AudioStreamConfig config;
276 config_modifier(&config);
Sebastian Jansson800e1212018-10-22 11:49:03 +0200277 return CreateAudioStream(clients, config);
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200278}
279
280AudioStreamPair* Scenario::CreateAudioStream(
Sebastian Jansson800e1212018-10-22 11:49:03 +0200281 std::pair<CallClient*, CallClient*> clients,
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200282 AudioStreamConfig config) {
Sebastian Jansson800e1212018-10-22 11:49:03 +0200283 audio_streams_.emplace_back(
284 new AudioStreamPair(clients.first, audio_encoder_factory_, clients.second,
285 audio_decoder_factory_, config));
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200286 return audio_streams_.back().get();
287}
288
289RepeatedActivity* Scenario::Every(TimeDelta interval,
290 std::function<void(TimeDelta)> function) {
291 repeated_activities_.emplace_back(new RepeatedActivity(interval, function));
292 return repeated_activities_.back().get();
293}
294
295RepeatedActivity* Scenario::Every(TimeDelta interval,
296 std::function<void()> function) {
297 auto function_with_argument = [function](TimeDelta) { function(); };
298 repeated_activities_.emplace_back(
299 new RepeatedActivity(interval, function_with_argument));
300 return repeated_activities_.back().get();
301}
302
303void Scenario::At(TimeDelta offset, std::function<void()> function) {
304 pending_activities_.emplace_back(new PendingActivity{offset, function});
305}
306
307void Scenario::RunFor(TimeDelta duration) {
308 RunUntil(duration, TimeDelta::PlusInfinity(), []() { return false; });
309}
310
311void Scenario::RunUntil(TimeDelta max_duration,
312 TimeDelta poll_interval,
313 std::function<bool()> exit_function) {
314 start_time_ = Timestamp::us(clock_->TimeInMicroseconds());
315 for (auto& activity : repeated_activities_) {
316 activity->SetStartTime(start_time_);
317 }
318
319 for (auto& stream_pair : video_streams_)
320 stream_pair->receive()->receive_stream_->Start();
321 for (auto& stream_pair : audio_streams_)
322 stream_pair->receive()->receive_stream_->Start();
323 for (auto& stream_pair : video_streams_) {
324 if (stream_pair->config_.autostart) {
325 stream_pair->send()->Start();
326 }
327 }
328 for (auto& stream_pair : audio_streams_) {
329 if (stream_pair->config_.autostart) {
330 stream_pair->send()->Start();
331 }
332 }
333 for (auto& call : clients_) {
334 call->call_->SignalChannelNetworkState(MediaType::AUDIO, kNetworkUp);
335 call->call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkUp);
336 }
337
338 rtc::Event done_(false, false);
339 while (!exit_function() && Duration() < max_duration) {
340 Timestamp current_time = Now();
341 TimeDelta duration = current_time - start_time_;
342 Timestamp next_time = current_time + poll_interval;
343 for (auto& activity : repeated_activities_) {
344 activity->Poll(current_time);
345 next_time = std::min(next_time, activity->NextTime());
346 }
347 for (auto activity = pending_activities_.begin();
348 activity < pending_activities_.end(); activity++) {
349 if (duration > (*activity)->after_duration) {
350 (*activity)->function();
351 pending_activities_.erase(activity);
352 }
353 }
354 TimeDelta wait_time = next_time - current_time;
355 if (real_time_mode_) {
356 done_.Wait(wait_time.ms<int>());
357 } else {
358 sim_clock_.AdvanceTimeMicroseconds(wait_time.us());
Sebastian Jansson71a091e2018-09-27 19:08:21 +0200359 // The fake clock is quite slow to update, we only update it if logging is
360 // turned on to save time.
361 if (!base_filename_.empty())
362 event_log_fake_clock_.SetTimeNanos(sim_clock_.TimeInMicroseconds() *
363 1000);
Sebastian Jansson98b07e92018-09-27 13:47:01 +0200364 }
365 }
366 for (auto& stream_pair : video_streams_) {
367 stream_pair->send()->video_capturer_->Stop();
368 stream_pair->send()->send_stream_->Stop();
369 }
370 for (auto& stream_pair : audio_streams_)
371 stream_pair->send()->send_stream_->Stop();
372 for (auto& stream_pair : video_streams_)
373 stream_pair->receive()->receive_stream_->Stop();
374 for (auto& stream_pair : audio_streams_)
375 stream_pair->receive()->receive_stream_->Stop();
376}
377
378Timestamp Scenario::Now() {
379 return Timestamp::us(clock_->TimeInMicroseconds());
380}
381
382TimeDelta Scenario::Duration() {
383 return Now() - start_time_;
384}
385
386} // namespace test
387} // namespace webrtc