Extracting the simulation part of FakeNetworkPipe
This CL extracts the part of FakeNetworkPipe responsible for simulating
network behavior into the SimulatedNetwork class, which implements the
new FakeNetworkInterface.
This prepares for an upcoming CL where the network simulation can
be injected in FakeNetworkPipe, allowing custom simulation models to be
used.
Bug: None
Change-Id: I9b5fa0dd9ff1fd8ccd5a7ce2d9ea3a5b11c5215e
Reviewed-on: https://webrtc-review.googlesource.com/64405
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Christoffer Rodbro <crodbro@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23146}
diff --git a/call/fake_network_pipe.cc b/call/fake_network_pipe.cc
index 234e229..284a542 100644
--- a/call/fake_network_pipe.cc
+++ b/call/fake_network_pipe.cc
@@ -19,6 +19,7 @@
#include "call/call.h"
#include "call/fake_network_pipe.h"
#include "rtc_base/logging.h"
+#include "rtc_base/ptr_util.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
@@ -26,11 +27,6 @@
namespace {
constexpr int64_t kDefaultProcessIntervalMs = 5;
constexpr int64_t kLogIntervalMs = 5000;
-struct PacketArrivalTimeComparator {
- bool operator()(const NetworkPacket& p1, const NetworkPacket& p2) {
- return p1.arrival_time() < p2.arrival_time();
- }
-};
} // namespace
NetworkPacket::NetworkPacket(rtc::CopyOnWriteBuffer packet,
@@ -83,37 +79,29 @@
PacketReceiver* receiver,
uint64_t seed)
: clock_(clock),
+ network_simulation_(rtc::MakeUnique<SimulatedNetwork>(config, seed)),
receiver_(receiver),
transport_(nullptr),
- random_(seed),
clock_offset_ms_(0),
- config_(),
dropped_packets_(0),
sent_packets_(0),
total_packet_delay_us_(0),
- bursting_(false),
next_process_time_us_(clock_->TimeInMicroseconds()),
- last_log_time_us_(clock_->TimeInMicroseconds()) {
- SetConfig(config);
-}
+ last_log_time_us_(clock_->TimeInMicroseconds()) {}
FakeNetworkPipe::FakeNetworkPipe(Clock* clock,
const FakeNetworkPipe::Config& config,
Transport* transport)
: clock_(clock),
+ network_simulation_(rtc::MakeUnique<SimulatedNetwork>(config, 1)),
receiver_(nullptr),
transport_(transport),
- random_(1),
clock_offset_ms_(0),
- config_(),
dropped_packets_(0),
sent_packets_(0),
total_packet_delay_us_(0),
- bursting_(false),
next_process_time_us_(clock_->TimeInMicroseconds()),
- last_log_time_us_(clock_->TimeInMicroseconds()) {
- SetConfig(config);
-}
+ last_log_time_us_(clock_->TimeInMicroseconds()) {}
FakeNetworkPipe::~FakeNetworkPipe() = default;
@@ -153,7 +141,17 @@
clock_offset_ms_ = offset_ms;
}
+SimulatedNetwork::SimulatedNetwork(SimulatedNetwork::Config config,
+ uint64_t random_seed)
+ : random_(random_seed), bursting_(false) {
+ SetConfig(config);
+}
+
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;
@@ -176,11 +174,7 @@
}
}
-bool FakeNetworkPipe::EnqueuePacket(rtc::CopyOnWriteBuffer packet,
- rtc::Optional<PacketOptions> options,
- bool is_rtcp,
- MediaType media_type,
- rtc::Optional<PacketTime> packet_time) {
+bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) {
Config config;
{
rtc::CritScope crit(&config_lock_);
@@ -190,12 +184,9 @@
if (config.queue_length_packets > 0 &&
capacity_link_.size() >= config.queue_length_packets) {
// Too many packet on the link, drop this one.
- ++dropped_packets_;
return false;
}
- int64_t time_now_us = clock_->TimeInMicroseconds();
-
// Delay introduced by the link capacity.
int64_t capacity_delay_ms = 0;
if (config.link_capacity_kbps > 0) {
@@ -203,26 +194,57 @@
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_ +
+ 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;
+ packet.size - capacity_delay_ms * bytes_per_millisecond;
}
- int64_t network_start_time_us = time_now_us;
+ int64_t network_start_time_us = packet.send_time_us;
// 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())
- network_start_time_us = capacity_link_.back().arrival_time();
+ 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_.emplace(std::move(packet), time_now_us, arrival_time_us,
- options, is_rtcp, media_type, packet_time);
+ capacity_link_.push({packet, arrival_time_us});
return true;
}
+rtc::Optional<int64_t> SimulatedNetwork::NextDeliveryTimeUs() const {
+ if (!delay_link_.empty())
+ return delay_link_.begin()->arrival_time_us;
+ return rtc::nullopt;
+}
+
+FakeNetworkPipe::StoredPacket::StoredPacket(NetworkPacket&& packet)
+ : packet(std::move(packet)) {}
+
+bool FakeNetworkPipe::EnqueuePacket(rtc::CopyOnWriteBuffer packet,
+ rtc::Optional<PacketOptions> options,
+ bool is_rtcp,
+ MediaType media_type,
+ rtc::Optional<PacketTime> packet_time) {
+ int64_t time_now_us = clock_->TimeInMicroseconds();
+ rtc::CritScope crit(&process_lock_);
+ size_t packet_size = packet.size();
+ NetworkPacket net_packet(std::move(packet), time_now_us, time_now_us, options,
+ is_rtcp, media_type, packet_time);
+
+ packets_in_flight_.emplace_back(StoredPacket(std::move(net_packet)));
+ int64_t packet_id = reinterpret_cast<uint64_t>(&packets_in_flight_.back());
+ bool sent = network_simulation_->EnqueuePacket(
+ PacketInFlightInfo(packet_size, time_now_us, packet_id));
+
+ if (!sent) {
+ packets_in_flight_.pop_back();
+ ++dropped_packets_;
+ }
+ return sent;
+}
+
float FakeNetworkPipe::PercentageLoss() {
rtc::CritScope crit(&process_lock_);
if (sent_packets_ == 0)
@@ -251,9 +273,9 @@
return sent_packets_;
}
-void FakeNetworkPipe::Process() {
- int64_t time_now_us = clock_->TimeInMicroseconds();
- std::queue<NetworkPacket> packets_to_deliver;
+std::vector<PacketDeliveryInfo> 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;
@@ -265,24 +287,15 @@
}
{
rtc::CritScope crit(&process_lock_);
- if (time_now_us - last_log_time_us_ > kLogIntervalMs * 1000) {
- int64_t queueing_delay_us = 0;
- if (!capacity_link_.empty()) {
- queueing_delay_us = time_now_us - capacity_link_.front().send_time();
- }
- RTC_LOG(LS_INFO) << "Network queue: " << queueing_delay_us << " us.";
- last_log_time_us_ = time_now_us;
- }
-
// Check the capacity link first.
if (!capacity_link_.empty()) {
int64_t last_arrival_time_us =
- delay_link_.empty() ? -1 : delay_link_.back().arrival_time();
+ 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()) {
+ time_now_us >= capacity_link_.front().arrival_time_us) {
// Time to get this packet.
- NetworkPacket packet = std::move(capacity_link_.front());
+ PacketInfo packet = std::move(capacity_link_.front());
capacity_link_.pop();
// Drop packets at an average rate of |config_.loss_percent| with
@@ -303,13 +316,14 @@
// 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() + arrival_time_jitter_us <
+ packet.arrival_time_us + arrival_time_jitter_us <
last_arrival_time_us) {
- arrival_time_jitter_us = last_arrival_time_us - packet.arrival_time();
+ arrival_time_jitter_us =
+ last_arrival_time_us - packet.arrival_time_us;
}
- packet.IncrementArrivalTime(arrival_time_jitter_us);
- if (packet.arrival_time() >= last_arrival_time_us) {
- last_arrival_time_us = packet.arrival_time();
+ 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;
}
@@ -319,21 +333,76 @@
if (needs_sort) {
// Packet(s) arrived out of order, make sure list is sorted.
std::sort(delay_link_.begin(), delay_link_.end(),
- PacketArrivalTimeComparator());
+ [](const PacketInfo& p1, const PacketInfo& p2) {
+ return p1.arrival_time_us < p2.arrival_time_us;
+ });
}
}
+ std::vector<PacketDeliveryInfo> packets_to_deliver;
// Check the extra delay queue.
while (!delay_link_.empty() &&
- time_now_us >= delay_link_.front().arrival_time()) {
- // Deliver this packet.
- NetworkPacket packet(std::move(delay_link_.front()));
+ 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();
- // |time_now| might be later than when the packet should have arrived, due
- // to NetworkProcess being called too late. For stats, use the time it
- // should have been on the link.
- total_packet_delay_us_ += packet.arrival_time() - packet.send_time();
- packets_to_deliver.push(std::move(packet));
+ }
+ return packets_to_deliver;
+ }
+}
+
+void FakeNetworkPipe::Process() {
+ int64_t time_now_us = clock_->TimeInMicroseconds();
+ std::queue<NetworkPacket> packets_to_deliver;
+ {
+ rtc::CritScope crit(&process_lock_);
+ if (time_now_us - last_log_time_us_ > kLogIntervalMs * 1000) {
+ int64_t queueing_delay_us = 0;
+ if (!packets_in_flight_.empty())
+ queueing_delay_us =
+ time_now_us - packets_in_flight_.front().packet.send_time();
+
+ RTC_LOG(LS_INFO) << "Network queue: " << queueing_delay_us / 1000
+ << " ms.";
+ last_log_time_us_ = time_now_us;
+ }
+
+ std::vector<PacketDeliveryInfo> delivery_infos =
+ network_simulation_->DequeueDeliverablePackets(time_now_us);
+ for (auto& delivery_info : delivery_infos) {
+ // In the common case where no reordering happens, find will return early
+ // as the first packet will be a match.
+ auto packet_it =
+ std::find_if(packets_in_flight_.begin(), packets_in_flight_.end(),
+ [&delivery_info](StoredPacket& packet_ref) {
+ return reinterpret_cast<uint64_t>(&packet_ref) ==
+ delivery_info.packet_id;
+ });
+ // Check that the packet is in the deque of packets in flight.
+ RTC_CHECK(packet_it != packets_in_flight_.end());
+ // Check that the packet is not already removed.
+ RTC_DCHECK(!packet_it->removed);
+
+ NetworkPacket packet = std::move(packet_it->packet);
+ packet_it->removed = true;
+
+ // Cleanup of removed packets at the beginning of the deque.
+ while (!packets_in_flight_.empty() &&
+ packets_in_flight_.front().removed) {
+ packets_in_flight_.pop_front();
+ }
+
+ if (delivery_info.receive_time_us != PacketDeliveryInfo::kNotReceived) {
+ int64_t added_delay_us =
+ delivery_info.receive_time_us - packet.send_time();
+ packet.IncrementArrivalTime(added_delay_us);
+ packets_to_deliver.emplace(std::move(packet));
+ // |time_now_us| might be later than when the packet should have
+ // arrived, due to NetworkProcess being called too late. For stats, use
+ // the time it should have been on the link.
+ total_packet_delay_us_ += added_delay_us;
+ }
}
sent_packets_ += packets_to_deliver.size();
}
@@ -344,9 +413,10 @@
packets_to_deliver.pop();
DeliverPacket(&packet);
}
-
- next_process_time_us_ = !delay_link_.empty()
- ? delay_link_.begin()->arrival_time()
+ rtc::Optional<int64_t> delivery_us =
+ network_simulation_->NextDeliveryTimeUs();
+ next_process_time_us_ = delivery_us
+ ? *delivery_us
: time_now_us + kDefaultProcessIntervalMs * 1000;
}
@@ -399,11 +469,6 @@
total_packet_delay_us_ = 0;
}
-int FakeNetworkPipe::GetConfigCapacityKbps() const {
- rtc::CritScope crit(&config_lock_);
- return config_.link_capacity_kbps;
-}
-
void FakeNetworkPipe::AddToPacketDropCount() {
rtc::CritScope crit(&process_lock_);
++dropped_packets_;
@@ -423,10 +488,6 @@
return clock_->TimeInMicroseconds();
}
-bool FakeNetworkPipe::IsRandomLoss(double prob_loss) {
- return random_.Rand<double>() < prob_loss;
-}
-
bool FakeNetworkPipe::ShouldProcess(int64_t time_now_us) const {
return time_now_us >= next_process_time_us_;
}