summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/test/scenario/scenario.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/test/scenario/scenario.cc')
-rw-r--r--third_party/libwebrtc/test/scenario/scenario.cc355
1 files changed, 355 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/scenario/scenario.cc b/third_party/libwebrtc/test/scenario/scenario.cc
new file mode 100644
index 0000000000..98f59e6c7d
--- /dev/null
+++ b/third_party/libwebrtc/test/scenario/scenario.cc
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "test/scenario/scenario.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "absl/flags/flag.h"
+#include "absl/flags/parse.h"
+#include "absl/strings/string_view.h"
+#include "api/audio_codecs/builtin_audio_decoder_factory.h"
+#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "rtc_base/socket_address.h"
+#include "test/logging/file_log_writer.h"
+#include "test/network/network_emulation.h"
+#include "test/scenario/video_stream.h"
+#include "test/testsupport/file_utils.h"
+
+ABSL_FLAG(bool, scenario_logs, false, "Save logs from scenario framework.");
+ABSL_FLAG(std::string,
+ scenario_logs_root,
+ "",
+ "Output root path, based on project root if unset.");
+
+namespace webrtc {
+namespace test {
+namespace {
+
+std::unique_ptr<FileLogWriterFactory> GetScenarioLogManager(
+ absl::string_view file_name) {
+ if (absl::GetFlag(FLAGS_scenario_logs) && !file_name.empty()) {
+ std::string output_root = absl::GetFlag(FLAGS_scenario_logs_root);
+ if (output_root.empty())
+ output_root = OutputPath() + "output_data/";
+
+ auto base_filename = output_root + std::string(file_name) + ".";
+ RTC_LOG(LS_INFO) << "Saving scenario logs to: " << base_filename;
+ return std::make_unique<FileLogWriterFactory>(base_filename);
+ }
+ return nullptr;
+}
+} // namespace
+
+Scenario::Scenario()
+ : Scenario(std::unique_ptr<LogWriterFactoryInterface>(),
+ /*real_time=*/false) {}
+
+Scenario::Scenario(const testing::TestInfo* test_info)
+ : Scenario(std::string(test_info->test_suite_name()) + "/" +
+ test_info->name()) {}
+
+Scenario::Scenario(absl::string_view file_name)
+ : Scenario(file_name, /*real_time=*/false) {}
+
+Scenario::Scenario(absl::string_view file_name, bool real_time)
+ : Scenario(GetScenarioLogManager(file_name), real_time) {}
+
+Scenario::Scenario(
+ std::unique_ptr<LogWriterFactoryInterface> log_writer_factory,
+ bool real_time)
+ : log_writer_factory_(std::move(log_writer_factory)),
+ network_manager_(real_time ? TimeMode::kRealTime : TimeMode::kSimulated,
+ EmulatedNetworkStatsGatheringMode::kDefault),
+ clock_(network_manager_.time_controller()->GetClock()),
+ audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()),
+ audio_encoder_factory_(CreateBuiltinAudioEncoderFactory()),
+ task_queue_(network_manager_.time_controller()
+ ->GetTaskQueueFactory()
+ ->CreateTaskQueue("Scenario",
+ TaskQueueFactory::Priority::NORMAL)) {}
+
+Scenario::~Scenario() {
+ if (start_time_.IsFinite())
+ Stop();
+ for (auto& call_client : clients_) {
+ call_client->SendTask([&] { call_client->transport_->Disconnect(); });
+ call_client->UnBind();
+ }
+}
+
+ColumnPrinter Scenario::TimePrinter() {
+ return ColumnPrinter::Lambda(
+ "time",
+ [this](rtc::SimpleStringBuilder& sb) {
+ sb.AppendFormat("%.3lf", Now().seconds<double>());
+ },
+ 32);
+}
+
+StatesPrinter* Scenario::CreatePrinter(absl::string_view name,
+ TimeDelta interval,
+ std::vector<ColumnPrinter> printers) {
+ std::vector<ColumnPrinter> all_printers{TimePrinter()};
+ for (auto& printer : printers)
+ all_printers.push_back(printer);
+ StatesPrinter* printer = new StatesPrinter(GetLogWriter(name), all_printers);
+ printers_.emplace_back(printer);
+ printer->PrintHeaders();
+ if (interval.IsFinite())
+ Every(interval, [printer] { printer->PrintRow(); });
+ return printer;
+}
+
+CallClient* Scenario::CreateClient(absl::string_view name,
+ CallClientConfig config) {
+ CallClient* client = new CallClient(network_manager_.time_controller(),
+ GetLogWriterFactory(name), config);
+ if (config.transport.state_log_interval.IsFinite()) {
+ Every(config.transport.state_log_interval, [this, client]() {
+ client->network_controller_factory_.LogCongestionControllerStats(Now());
+ });
+ }
+ clients_.emplace_back(client);
+ return client;
+}
+
+CallClient* Scenario::CreateClient(
+ absl::string_view name,
+ std::function<void(CallClientConfig*)> config_modifier) {
+ CallClientConfig config;
+ config_modifier(&config);
+ return CreateClient(name, config);
+}
+
+CallClientPair* Scenario::CreateRoutes(
+ CallClient* first,
+ std::vector<EmulatedNetworkNode*> send_link,
+ CallClient* second,
+ std::vector<EmulatedNetworkNode*> return_link) {
+ return CreateRoutes(first, send_link,
+ DataSize::Bytes(PacketOverhead::kDefault), second,
+ return_link, DataSize::Bytes(PacketOverhead::kDefault));
+}
+
+CallClientPair* Scenario::CreateRoutes(
+ CallClient* first,
+ std::vector<EmulatedNetworkNode*> send_link,
+ DataSize first_overhead,
+ CallClient* second,
+ std::vector<EmulatedNetworkNode*> return_link,
+ DataSize second_overhead) {
+ CallClientPair* client_pair = new CallClientPair(first, second);
+ ChangeRoute(client_pair->forward(), send_link, first_overhead);
+ ChangeRoute(client_pair->reverse(), return_link, second_overhead);
+ client_pairs_.emplace_back(client_pair);
+ return client_pair;
+}
+
+void Scenario::ChangeRoute(std::pair<CallClient*, CallClient*> clients,
+ std::vector<EmulatedNetworkNode*> over_nodes) {
+ ChangeRoute(clients, over_nodes, DataSize::Bytes(PacketOverhead::kDefault));
+}
+
+void Scenario::ChangeRoute(std::pair<CallClient*, CallClient*> clients,
+ std::vector<EmulatedNetworkNode*> over_nodes,
+ DataSize overhead) {
+ EmulatedRoute* route = network_manager_.CreateRoute(over_nodes);
+ uint16_t port = clients.second->Bind(route->to);
+ auto addr = rtc::SocketAddress(route->to->GetPeerLocalAddress(), port);
+ clients.first->transport_->Connect(route->from, addr, overhead);
+}
+
+EmulatedNetworkNode* Scenario::CreateSimulationNode(
+ std::function<void(NetworkSimulationConfig*)> config_modifier) {
+ NetworkSimulationConfig config;
+ config_modifier(&config);
+ return CreateSimulationNode(config);
+}
+
+EmulatedNetworkNode* Scenario::CreateSimulationNode(
+ NetworkSimulationConfig config) {
+ return network_manager_.CreateEmulatedNode(
+ SimulationNode::CreateBehavior(config));
+}
+
+SimulationNode* Scenario::CreateMutableSimulationNode(
+ std::function<void(NetworkSimulationConfig*)> config_modifier) {
+ NetworkSimulationConfig config;
+ config_modifier(&config);
+ return CreateMutableSimulationNode(config);
+}
+
+SimulationNode* Scenario::CreateMutableSimulationNode(
+ NetworkSimulationConfig config) {
+ std::unique_ptr<SimulatedNetwork> behavior =
+ SimulationNode::CreateBehavior(config);
+ SimulatedNetwork* behavior_ptr = behavior.get();
+ auto* emulated_node =
+ network_manager_.CreateEmulatedNode(std::move(behavior));
+ simulation_nodes_.emplace_back(
+ new SimulationNode(config, behavior_ptr, emulated_node));
+ return simulation_nodes_.back().get();
+}
+
+void Scenario::TriggerPacketBurst(std::vector<EmulatedNetworkNode*> over_nodes,
+ size_t num_packets,
+ size_t packet_size) {
+ network_manager_.CreateCrossTrafficRoute(over_nodes)
+ ->TriggerPacketBurst(num_packets, packet_size);
+}
+
+void Scenario::NetworkDelayedAction(
+ std::vector<EmulatedNetworkNode*> over_nodes,
+ size_t packet_size,
+ std::function<void()> action) {
+ network_manager_.CreateCrossTrafficRoute(over_nodes)
+ ->NetworkDelayedAction(packet_size, action);
+}
+
+VideoStreamPair* Scenario::CreateVideoStream(
+ std::pair<CallClient*, CallClient*> clients,
+ std::function<void(VideoStreamConfig*)> config_modifier) {
+ VideoStreamConfig config;
+ config_modifier(&config);
+ return CreateVideoStream(clients, config);
+}
+
+VideoStreamPair* Scenario::CreateVideoStream(
+ std::pair<CallClient*, CallClient*> clients,
+ VideoStreamConfig config) {
+ std::vector<RtpExtension> extensions = GetVideoRtpExtensions(config);
+ clients.first->SetVideoReceiveRtpHeaderExtensions(extensions);
+ clients.second->SetVideoReceiveRtpHeaderExtensions(extensions);
+ video_streams_.emplace_back(
+ new VideoStreamPair(clients.first, clients.second, config));
+ return video_streams_.back().get();
+}
+
+AudioStreamPair* Scenario::CreateAudioStream(
+ std::pair<CallClient*, CallClient*> clients,
+ std::function<void(AudioStreamConfig*)> config_modifier) {
+ AudioStreamConfig config;
+ config_modifier(&config);
+ return CreateAudioStream(clients, config);
+}
+
+AudioStreamPair* Scenario::CreateAudioStream(
+ std::pair<CallClient*, CallClient*> clients,
+ AudioStreamConfig config) {
+ std::vector<RtpExtension> extensions = GetAudioRtpExtensions(config);
+ clients.first->SetAudioReceiveRtpHeaderExtensions(extensions);
+ clients.second->SetAudioReceiveRtpHeaderExtensions(extensions);
+ audio_streams_.emplace_back(
+ new AudioStreamPair(clients.first, audio_encoder_factory_, clients.second,
+ audio_decoder_factory_, config));
+ return audio_streams_.back().get();
+}
+
+void Scenario::Every(TimeDelta interval,
+ absl::AnyInvocable<void(TimeDelta)> function) {
+ RepeatingTaskHandle::DelayedStart(
+ task_queue_.get(), interval,
+ [interval, function = std::move(function)]() mutable {
+ function(interval);
+ return interval;
+ });
+}
+
+void Scenario::Every(TimeDelta interval, absl::AnyInvocable<void()> function) {
+ RepeatingTaskHandle::DelayedStart(
+ task_queue_.get(), interval,
+ [interval, function = std::move(function)]() mutable {
+ function();
+ return interval;
+ });
+}
+
+void Scenario::Post(absl::AnyInvocable<void() &&> function) {
+ task_queue_->PostTask(std::move(function));
+}
+
+void Scenario::At(TimeDelta offset, absl::AnyInvocable<void() &&> function) {
+ RTC_DCHECK_GT(offset, TimeSinceStart());
+ task_queue_->PostDelayedTask(std::move(function), TimeUntilTarget(offset));
+}
+
+void Scenario::RunFor(TimeDelta duration) {
+ if (start_time_.IsInfinite())
+ Start();
+ network_manager_.time_controller()->AdvanceTime(duration);
+}
+
+void Scenario::RunUntil(TimeDelta target_time_since_start) {
+ RunFor(TimeUntilTarget(target_time_since_start));
+}
+
+void Scenario::RunUntil(TimeDelta target_time_since_start,
+ TimeDelta check_interval,
+ std::function<bool()> exit_function) {
+ if (start_time_.IsInfinite())
+ Start();
+ while (check_interval >= TimeUntilTarget(target_time_since_start)) {
+ network_manager_.time_controller()->AdvanceTime(check_interval);
+ if (exit_function())
+ return;
+ }
+ network_manager_.time_controller()->AdvanceTime(
+ TimeUntilTarget(target_time_since_start));
+}
+
+void Scenario::Start() {
+ start_time_ = clock_->CurrentTime();
+ for (auto& stream_pair : video_streams_)
+ stream_pair->receive()->Start();
+ for (auto& stream_pair : audio_streams_)
+ stream_pair->receive()->Start();
+ for (auto& stream_pair : video_streams_) {
+ if (stream_pair->config_.autostart) {
+ stream_pair->send()->Start();
+ }
+ }
+ for (auto& stream_pair : audio_streams_) {
+ if (stream_pair->config_.autostart) {
+ stream_pair->send()->Start();
+ }
+ }
+}
+
+void Scenario::Stop() {
+ RTC_DCHECK(start_time_.IsFinite());
+ for (auto& stream_pair : video_streams_) {
+ stream_pair->send()->Stop();
+ }
+ for (auto& stream_pair : audio_streams_)
+ stream_pair->send()->Stop();
+ for (auto& stream_pair : video_streams_)
+ stream_pair->receive()->Stop();
+ for (auto& stream_pair : audio_streams_)
+ stream_pair->receive()->Stop();
+ start_time_ = Timestamp::PlusInfinity();
+}
+
+Timestamp Scenario::Now() {
+ return clock_->CurrentTime();
+}
+
+TimeDelta Scenario::TimeSinceStart() {
+ if (start_time_.IsInfinite())
+ return TimeDelta::Zero();
+ return Now() - start_time_;
+}
+
+TimeDelta Scenario::TimeUntilTarget(TimeDelta target_time_offset) {
+ return target_time_offset - TimeSinceStart();
+}
+
+} // namespace test
+} // namespace webrtc