Move SimulatedNetwork class to separate file.

Bug: webrtc:9467
Change-Id: Iaf91f27ea7ad9e9e59991bbeb0ef3868578e6a8f
Reviewed-on: https://webrtc-review.googlesource.com/92884
Reviewed-by: Niels Moller <nisse@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24221}
This commit is contained in:
Sebastian Jansson
2018-08-07 18:58:05 +02:00
committed by Commit Bot
parent d528ad542e
commit f96b1ca609
5 changed files with 281 additions and 221 deletions

View File

@ -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",

View File

@ -13,7 +13,6 @@
#include <string.h>
#include <algorithm>
#include <cmath>
#include <utility>
#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<int64_t> 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<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;
{
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<double>() < prob_loss_bursting) ||
(!bursting_ && random_.Rand<double>() < 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<PacketDeliveryInfo> 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<NetworkPacket> packets_to_deliver;

View File

@ -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<int64_t> 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<PacketDeliveryInfo> DequeueDeliverablePackets(
int64_t receive_time_us) override;
absl::optional<int64_t> 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<PacketInfo> capacity_link_ RTC_GUARDED_BY(process_lock_);
Random random_;
std::deque<PacketInfo> delay_link_;
// Link configuration.
Config config_ RTC_GUARDED_BY(config_lock_);
absl::optional<int64_t> 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 {

186
call/simulated_network.cc Normal file
View File

@ -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 <algorithm>
#include <cmath>
#include <utility>
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<int64_t> SimulatedNetwork::NextDeliveryTimeUs() const {
if (!delay_link_.empty())
return delay_link_.begin()->arrival_time_us;
return absl::nullopt;
}
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;
{
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<double>() < prob_loss_bursting) ||
(!bursting_ && random_.Rand<double>() < 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<PacketDeliveryInfo> 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

80
call/simulated_network.h Normal file
View File

@ -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 <deque>
#include <queue>
#include <vector>
#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<PacketDeliveryInfo> DequeueDeliverablePackets(
int64_t receive_time_us) override;
absl::optional<int64_t> 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<PacketInfo> capacity_link_ RTC_GUARDED_BY(process_lock_);
Random random_;
std::deque<PacketInfo> delay_link_;
// Link configuration.
Config config_ RTC_GUARDED_BY(config_lock_);
absl::optional<int64_t> 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_