From f96b1ca609364655482b767c0cb77d3aafde4028 Mon Sep 17 00:00:00 2001 From: Sebastian Jansson Date: Tue, 7 Aug 2018 18:58:05 +0200 Subject: [PATCH] Move SimulatedNetwork class to separate file. Bug: webrtc:9467 Change-Id: Iaf91f27ea7ad9e9e59991bbeb0ef3868578e6a8f Reviewed-on: https://webrtc-review.googlesource.com/92884 Reviewed-by: Niels Moller Commit-Queue: Sebastian Jansson Cr-Commit-Position: refs/heads/master@{#24221} --- call/BUILD.gn | 14 +++ call/fake_network_pipe.cc | 168 ---------------------------------- call/fake_network_pipe.h | 54 +---------- call/simulated_network.cc | 186 ++++++++++++++++++++++++++++++++++++++ call/simulated_network.h | 80 ++++++++++++++++ 5 files changed, 281 insertions(+), 221 deletions(-) create mode 100644 call/simulated_network.cc create mode 100644 call/simulated_network.h diff --git a/call/BUILD.gn b/call/BUILD.gn index 9443746c80..2f797729f7 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -247,6 +247,19 @@ rtc_source_set("video_stream_api") { ] } +rtc_source_set("simulated_network") { + sources = [ + "simulated_network.cc", + "simulated_network.h", + ] + deps = [ + "../api:simulated_network_api", + "../rtc_base:rtc_base_approved", + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + rtc_source_set("fake_network") { sources = [ "fake_network_pipe.cc", @@ -254,6 +267,7 @@ rtc_source_set("fake_network") { ] deps = [ ":call_interfaces", + ":simulated_network", "..:webrtc_common", "../api:simulated_network_api", "../api:transport_api", diff --git a/call/fake_network_pipe.cc b/call/fake_network_pipe.cc index b9bf4e7398..21ee652b09 100644 --- a/call/fake_network_pipe.cc +++ b/call/fake_network_pipe.cc @@ -13,7 +13,6 @@ #include #include -#include #include #include "absl/memory/memory.h" @@ -159,98 +158,10 @@ void FakeNetworkPipe::SetClockOffset(int64_t offset_ms) { clock_offset_ms_ = offset_ms; } -SimulatedNetwork::SimulatedNetwork(SimulatedNetwork::Config config, - uint64_t random_seed) - : random_(random_seed), bursting_(false) { - SetConfig(config); -} - -SimulatedNetwork::~SimulatedNetwork() = default; - void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) { network_simulation_->SetConfig(config); } -void SimulatedNetwork::SetConfig(const SimulatedNetwork::Config& config) { - rtc::CritScope crit(&config_lock_); - config_ = config; // Shallow copy of the struct. - double prob_loss = config.loss_percent / 100.0; - if (config_.avg_burst_loss_length == -1) { - // Uniform loss - prob_loss_bursting_ = prob_loss; - prob_start_bursting_ = prob_loss; - } else { - // Lose packets according to a gilbert-elliot model. - int avg_burst_loss_length = config.avg_burst_loss_length; - int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss)); - - RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length) - << "For a total packet loss of " << config.loss_percent << "%% then" - << " avg_burst_loss_length must be " << min_avg_burst_loss_length + 1 - << " or higher."; - - prob_loss_bursting_ = (1.0 - 1.0 / avg_burst_loss_length); - prob_start_bursting_ = prob_loss / (1 - prob_loss) / avg_burst_loss_length; - } -} - -void SimulatedNetwork::PauseTransmissionUntil(int64_t until_us) { - rtc::CritScope crit(&config_lock_); - pause_transmission_until_us_ = until_us; -} - -bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) { - Config config; - { - rtc::CritScope crit(&config_lock_); - config = config_; - } - rtc::CritScope crit(&process_lock_); - if (config.queue_length_packets > 0 && - capacity_link_.size() >= config.queue_length_packets) { - // Too many packet on the link, drop this one. - return false; - } - - // Delay introduced by the link capacity. - int64_t capacity_delay_ms = 0; - if (config.link_capacity_kbps > 0) { - // Using bytes per millisecond to avoid losing precision. - const int64_t bytes_per_millisecond = config.link_capacity_kbps / 8; - // To round to the closest millisecond we add half a milliseconds worth of - // bytes to the delay calculation. - capacity_delay_ms = (packet.size + capacity_delay_error_bytes_ + - bytes_per_millisecond / 2) / - bytes_per_millisecond; - capacity_delay_error_bytes_ += - packet.size - capacity_delay_ms * bytes_per_millisecond; - } - int64_t network_start_time_us = packet.send_time_us; - - { - rtc::CritScope crit(&config_lock_); - if (pause_transmission_until_us_) { - network_start_time_us = - std::max(network_start_time_us, *pause_transmission_until_us_); - pause_transmission_until_us_.reset(); - } - } - // Check if there already are packets on the link and change network start - // time forward if there is. - if (!capacity_link_.empty() && - network_start_time_us < capacity_link_.back().arrival_time_us) - network_start_time_us = capacity_link_.back().arrival_time_us; - - int64_t arrival_time_us = network_start_time_us + capacity_delay_ms * 1000; - capacity_link_.push({packet, arrival_time_us}); - return true; -} - -absl::optional SimulatedNetwork::NextDeliveryTimeUs() const { - if (!delay_link_.empty()) - return delay_link_.begin()->arrival_time_us; - return absl::nullopt; -} FakeNetworkPipe::StoredPacket::StoredPacket(NetworkPacket&& packet) : packet(std::move(packet)) {} @@ -320,85 +231,6 @@ size_t FakeNetworkPipe::SentPackets() { return sent_packets_; } -std::vector SimulatedNetwork::DequeueDeliverablePackets( - int64_t receive_time_us) { - int64_t time_now_us = receive_time_us; - Config config; - double prob_loss_bursting; - double prob_start_bursting; - { - rtc::CritScope crit(&config_lock_); - config = config_; - prob_loss_bursting = prob_loss_bursting_; - prob_start_bursting = prob_start_bursting_; - } - { - rtc::CritScope crit(&process_lock_); - // Check the capacity link first. - if (!capacity_link_.empty()) { - int64_t last_arrival_time_us = - delay_link_.empty() ? -1 : delay_link_.back().arrival_time_us; - bool needs_sort = false; - while (!capacity_link_.empty() && - time_now_us >= capacity_link_.front().arrival_time_us) { - // Time to get this packet. - PacketInfo packet = std::move(capacity_link_.front()); - capacity_link_.pop(); - - // Drop packets at an average rate of |config_.loss_percent| with - // and average loss burst length of |config_.avg_burst_loss_length|. - if ((bursting_ && random_.Rand() < prob_loss_bursting) || - (!bursting_ && random_.Rand() < prob_start_bursting)) { - bursting_ = true; - continue; - } else { - bursting_ = false; - } - - int64_t arrival_time_jitter_us = std::max( - random_.Gaussian(config.queue_delay_ms * 1000, - config.delay_standard_deviation_ms * 1000), - 0.0); - - // If reordering is not allowed then adjust arrival_time_jitter - // to make sure all packets are sent in order. - if (!config.allow_reordering && !delay_link_.empty() && - packet.arrival_time_us + arrival_time_jitter_us < - last_arrival_time_us) { - arrival_time_jitter_us = - last_arrival_time_us - packet.arrival_time_us; - } - packet.arrival_time_us += arrival_time_jitter_us; - if (packet.arrival_time_us >= last_arrival_time_us) { - last_arrival_time_us = packet.arrival_time_us; - } else { - needs_sort = true; - } - delay_link_.emplace_back(std::move(packet)); - } - - if (needs_sort) { - // Packet(s) arrived out of order, make sure list is sorted. - std::sort(delay_link_.begin(), delay_link_.end(), - [](const PacketInfo& p1, const PacketInfo& p2) { - return p1.arrival_time_us < p2.arrival_time_us; - }); - } - } - - std::vector packets_to_deliver; - // Check the extra delay queue. - while (!delay_link_.empty() && - time_now_us >= delay_link_.front().arrival_time_us) { - PacketInfo packet_info = delay_link_.front(); - packets_to_deliver.emplace_back( - PacketDeliveryInfo(packet_info.packet, packet_info.arrival_time_us)); - delay_link_.pop_front(); - } - return packets_to_deliver; - } -} - void FakeNetworkPipe::Process() { int64_t time_now_us = clock_->TimeInMicroseconds(); std::queue packets_to_deliver; diff --git a/call/fake_network_pipe.h b/call/fake_network_pipe.h index ea9c60b047..6e2bc11c6e 100644 --- a/call/fake_network_pipe.h +++ b/call/fake_network_pipe.h @@ -22,11 +22,11 @@ #include "api/call/transport.h" #include "api/test/simulated_network.h" #include "call/call.h" +#include "call/simulated_network.h" #include "common_types.h" // NOLINT(build/include) #include "modules/include/module.h" #include "rtc_base/constructormagic.h" #include "rtc_base/criticalsection.h" -#include "rtc_base/random.h" #include "rtc_base/thread_annotations.h" namespace webrtc { @@ -98,58 +98,6 @@ class NetworkPacket { absl::optional packet_time_us_; }; -// Class simulating a network link. This is a simple and naive solution just -// faking capacity and adding an extra transport delay in addition to the -// capacity introduced delay. -class SimulatedNetwork : public NetworkSimulationInterface { - public: - using Config = NetworkSimulationInterface::SimulatedNetworkConfig; - explicit SimulatedNetwork(Config config, uint64_t random_seed = 1); - ~SimulatedNetwork() override; - - // Sets a new configuration. This won't affect packets already in the pipe. - void SetConfig(const Config& config); - void PauseTransmissionUntil(int64_t until_us); - - // NetworkSimulationInterface - bool EnqueuePacket(PacketInFlightInfo packet) override; - std::vector DequeueDeliverablePackets( - int64_t receive_time_us) override; - - absl::optional NextDeliveryTimeUs() const override; - - private: - struct PacketInfo { - PacketInFlightInfo packet; - int64_t arrival_time_us; - }; - rtc::CriticalSection config_lock_; - - // |process_lock| guards the data structures involved in delay and loss - // processes, such as the packet queues. - rtc::CriticalSection process_lock_; - std::queue capacity_link_ RTC_GUARDED_BY(process_lock_); - Random random_; - - std::deque delay_link_; - - // Link configuration. - Config config_ RTC_GUARDED_BY(config_lock_); - absl::optional pause_transmission_until_us_ - RTC_GUARDED_BY(config_lock_); - - // Are we currently dropping a burst of packets? - bool bursting_; - - // The probability to drop the packet if we are currently dropping a - // burst of packet - double prob_loss_bursting_ RTC_GUARDED_BY(config_lock_); - - // The probability to drop a burst of packets. - double prob_start_bursting_ RTC_GUARDED_BY(config_lock_); - int64_t capacity_delay_error_bytes_ = 0; -}; - // Class faking a network link, internally is uses an implementation of a // SimulatedNetworkInterface to simulate network behavior. class FakeNetworkPipe : public Transport, public PacketReceiver, public Module { diff --git a/call/simulated_network.cc b/call/simulated_network.cc new file mode 100644 index 0000000000..ee58dce718 --- /dev/null +++ b/call/simulated_network.cc @@ -0,0 +1,186 @@ +/* + * 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 "call/simulated_network.h" + +#include +#include +#include + +namespace webrtc { + +SimulatedNetwork::SimulatedNetwork(SimulatedNetwork::Config config, + uint64_t random_seed) + : random_(random_seed), bursting_(false) { + SetConfig(config); +} + +SimulatedNetwork::~SimulatedNetwork() = default; + +void SimulatedNetwork::SetConfig(const SimulatedNetwork::Config& config) { + rtc::CritScope crit(&config_lock_); + config_ = config; // Shallow copy of the struct. + double prob_loss = config.loss_percent / 100.0; + if (config_.avg_burst_loss_length == -1) { + // Uniform loss + prob_loss_bursting_ = prob_loss; + prob_start_bursting_ = prob_loss; + } else { + // Lose packets according to a gilbert-elliot model. + int avg_burst_loss_length = config.avg_burst_loss_length; + int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss)); + + RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length) + << "For a total packet loss of " << config.loss_percent << "%% then" + << " avg_burst_loss_length must be " << min_avg_burst_loss_length + 1 + << " or higher."; + + prob_loss_bursting_ = (1.0 - 1.0 / avg_burst_loss_length); + prob_start_bursting_ = prob_loss / (1 - prob_loss) / avg_burst_loss_length; + } +} + +void SimulatedNetwork::PauseTransmissionUntil(int64_t until_us) { + rtc::CritScope crit(&config_lock_); + pause_transmission_until_us_ = until_us; +} + +bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) { + Config config; + { + rtc::CritScope crit(&config_lock_); + config = config_; + } + rtc::CritScope crit(&process_lock_); + if (config.queue_length_packets > 0 && + capacity_link_.size() >= config.queue_length_packets) { + // Too many packet on the link, drop this one. + return false; + } + + // Delay introduced by the link capacity. + int64_t capacity_delay_ms = 0; + if (config.link_capacity_kbps > 0) { + // Using bytes per millisecond to avoid losing precision. + const int64_t bytes_per_millisecond = config.link_capacity_kbps / 8; + // To round to the closest millisecond we add half a milliseconds worth of + // bytes to the delay calculation. + capacity_delay_ms = (packet.size + capacity_delay_error_bytes_ + + bytes_per_millisecond / 2) / + bytes_per_millisecond; + capacity_delay_error_bytes_ += + packet.size - capacity_delay_ms * bytes_per_millisecond; + } + int64_t network_start_time_us = packet.send_time_us; + + { + rtc::CritScope crit(&config_lock_); + if (pause_transmission_until_us_) { + network_start_time_us = + std::max(network_start_time_us, *pause_transmission_until_us_); + pause_transmission_until_us_.reset(); + } + } + // Check if there already are packets on the link and change network start + // time forward if there is. + if (!capacity_link_.empty() && + network_start_time_us < capacity_link_.back().arrival_time_us) + network_start_time_us = capacity_link_.back().arrival_time_us; + + int64_t arrival_time_us = network_start_time_us + capacity_delay_ms * 1000; + capacity_link_.push({packet, arrival_time_us}); + return true; +} + +absl::optional SimulatedNetwork::NextDeliveryTimeUs() const { + if (!delay_link_.empty()) + return delay_link_.begin()->arrival_time_us; + return absl::nullopt; +} +std::vector SimulatedNetwork::DequeueDeliverablePackets( + int64_t receive_time_us) { + int64_t time_now_us = receive_time_us; + Config config; + double prob_loss_bursting; + double prob_start_bursting; + { + rtc::CritScope crit(&config_lock_); + config = config_; + prob_loss_bursting = prob_loss_bursting_; + prob_start_bursting = prob_start_bursting_; + } + { + rtc::CritScope crit(&process_lock_); + // Check the capacity link first. + if (!capacity_link_.empty()) { + int64_t last_arrival_time_us = + delay_link_.empty() ? -1 : delay_link_.back().arrival_time_us; + bool needs_sort = false; + while (!capacity_link_.empty() && + time_now_us >= capacity_link_.front().arrival_time_us) { + // Time to get this packet. + PacketInfo packet = std::move(capacity_link_.front()); + capacity_link_.pop(); + + // Drop packets at an average rate of |config_.loss_percent| with + // and average loss burst length of |config_.avg_burst_loss_length|. + if ((bursting_ && random_.Rand() < prob_loss_bursting) || + (!bursting_ && random_.Rand() < prob_start_bursting)) { + bursting_ = true; + continue; + } else { + bursting_ = false; + } + + int64_t arrival_time_jitter_us = std::max( + random_.Gaussian(config.queue_delay_ms * 1000, + config.delay_standard_deviation_ms * 1000), + 0.0); + + // If reordering is not allowed then adjust arrival_time_jitter + // to make sure all packets are sent in order. + if (!config.allow_reordering && !delay_link_.empty() && + packet.arrival_time_us + arrival_time_jitter_us < + last_arrival_time_us) { + arrival_time_jitter_us = + last_arrival_time_us - packet.arrival_time_us; + } + packet.arrival_time_us += arrival_time_jitter_us; + if (packet.arrival_time_us >= last_arrival_time_us) { + last_arrival_time_us = packet.arrival_time_us; + } else { + needs_sort = true; + } + delay_link_.emplace_back(std::move(packet)); + } + + if (needs_sort) { + // Packet(s) arrived out of order, make sure list is sorted. + std::sort(delay_link_.begin(), delay_link_.end(), + [](const PacketInfo& p1, const PacketInfo& p2) { + return p1.arrival_time_us < p2.arrival_time_us; + }); + } + } + + std::vector packets_to_deliver; + // Check the extra delay queue. + while (!delay_link_.empty() && + time_now_us >= delay_link_.front().arrival_time_us) { + PacketInfo packet_info = delay_link_.front(); + packets_to_deliver.emplace_back( + PacketDeliveryInfo(packet_info.packet, packet_info.arrival_time_us)); + delay_link_.pop_front(); + } + return packets_to_deliver; + } +} + +} // namespace webrtc diff --git a/call/simulated_network.h b/call/simulated_network.h new file mode 100644 index 0000000000..53283bfb16 --- /dev/null +++ b/call/simulated_network.h @@ -0,0 +1,80 @@ +/* + * 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. + */ +#ifndef CALL_SIMULATED_NETWORK_H_ +#define CALL_SIMULATED_NETWORK_H_ + +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/types/optional.h" +#include "api/test/simulated_network.h" +#include "rtc_base/criticalsection.h" +#include "rtc_base/random.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Class simulating a network link. This is a simple and naive solution just +// faking capacity and adding an extra transport delay in addition to the +// capacity introduced delay. +class SimulatedNetwork : public NetworkSimulationInterface { + public: + using Config = NetworkSimulationInterface::SimulatedNetworkConfig; + explicit SimulatedNetwork(Config config, uint64_t random_seed = 1); + ~SimulatedNetwork() override; + + // Sets a new configuration. This won't affect packets already in the pipe. + void SetConfig(const Config& config); + void PauseTransmissionUntil(int64_t until_us); + + // NetworkSimulationInterface + bool EnqueuePacket(PacketInFlightInfo packet) override; + std::vector DequeueDeliverablePackets( + int64_t receive_time_us) override; + + absl::optional NextDeliveryTimeUs() const override; + + private: + struct PacketInfo { + PacketInFlightInfo packet; + int64_t arrival_time_us; + }; + rtc::CriticalSection config_lock_; + + // |process_lock| guards the data structures involved in delay and loss + // processes, such as the packet queues. + rtc::CriticalSection process_lock_; + std::queue capacity_link_ RTC_GUARDED_BY(process_lock_); + Random random_; + + std::deque delay_link_; + + // Link configuration. + Config config_ RTC_GUARDED_BY(config_lock_); + absl::optional pause_transmission_until_us_ + RTC_GUARDED_BY(config_lock_); + + // Are we currently dropping a burst of packets? + bool bursting_; + + // The probability to drop the packet if we are currently dropping a + // burst of packet + double prob_loss_bursting_ RTC_GUARDED_BY(config_lock_); + + // The probability to drop a burst of packets. + double prob_start_bursting_ RTC_GUARDED_BY(config_lock_); + int64_t capacity_delay_error_bytes_ = 0; +}; + +} // namespace webrtc + +#endif // CALL_SIMULATED_NETWORK_H_