diff options
Diffstat (limited to 'webrtc/test/fake_network_pipe.cc')
-rw-r--r-- | webrtc/test/fake_network_pipe.cc | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/webrtc/test/fake_network_pipe.cc b/webrtc/test/fake_network_pipe.cc new file mode 100644 index 0000000000..c36059356a --- /dev/null +++ b/webrtc/test/fake_network_pipe.cc @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/fake_network_pipe.h" + +#include <assert.h> +#include <math.h> +#include <string.h> +#include <algorithm> + +#include "webrtc/call.h" +#include "webrtc/system_wrappers/include/tick_util.h" + +namespace webrtc { + +const double kPi = 3.14159265; + +static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) { + // Creating a Normal distribution variable from two independent uniform + // variables based on the Box-Muller transform. + double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT + double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT + return static_cast<int>(mean_delay_ms + standard_deviation_ms * + sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2)); +} + +static bool UniformLoss(int loss_percent) { + int outcome = rand() % 100; + return outcome < loss_percent; +} + +class NetworkPacket { + public: + NetworkPacket(const uint8_t* data, size_t length, int64_t send_time, + int64_t arrival_time) + : data_(NULL), + data_length_(length), + send_time_(send_time), + arrival_time_(arrival_time) { + data_ = new uint8_t[length]; + memcpy(data_, data, length); + } + ~NetworkPacket() { + delete [] data_; + } + + uint8_t* data() const { return data_; } + size_t data_length() const { return data_length_; } + int64_t send_time() const { return send_time_; } + int64_t arrival_time() const { return arrival_time_; } + void IncrementArrivalTime(int64_t extra_delay) { + arrival_time_+= extra_delay; + } + + private: + // The packet data. + uint8_t* data_; + // Length of data_. + size_t data_length_; + // The time the packet was sent out on the network. + const int64_t send_time_; + // The time the packet should arrive at the reciver. + int64_t arrival_time_; +}; + +FakeNetworkPipe::FakeNetworkPipe(const FakeNetworkPipe::Config& config) + : packet_receiver_(NULL), + config_(config), + dropped_packets_(0), + sent_packets_(0), + total_packet_delay_(0), + next_process_time_(TickTime::MillisecondTimestamp()) { +} + +FakeNetworkPipe::~FakeNetworkPipe() { + while (!capacity_link_.empty()) { + delete capacity_link_.front(); + capacity_link_.pop(); + } + while (!delay_link_.empty()) { + delete delay_link_.front(); + delay_link_.pop(); + } +} + +void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) { + packet_receiver_ = receiver; +} + +void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) { + rtc::CritScope crit(&lock_); + config_ = config; // Shallow copy of the struct. +} + +void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) { + // A NULL packet_receiver_ means that this pipe will terminate the flow of + // packets. + if (packet_receiver_ == NULL) + return; + rtc::CritScope crit(&lock_); + 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; + } + + int64_t time_now = TickTime::MillisecondTimestamp(); + + // Delay introduced by the link capacity. + int64_t capacity_delay_ms = 0; + if (config_.link_capacity_kbps > 0) + capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8); + int64_t network_start_time = time_now; + + // Check if there already are packets on the link and change network start + // time if there is. + if (capacity_link_.size() > 0) + network_start_time = capacity_link_.back()->arrival_time(); + + int64_t arrival_time = network_start_time + capacity_delay_ms; + NetworkPacket* packet = new NetworkPacket(data, data_length, time_now, + arrival_time); + capacity_link_.push(packet); +} + +float FakeNetworkPipe::PercentageLoss() { + rtc::CritScope crit(&lock_); + if (sent_packets_ == 0) + return 0; + + return static_cast<float>(dropped_packets_) / + (sent_packets_ + dropped_packets_); +} + +int FakeNetworkPipe::AverageDelay() { + rtc::CritScope crit(&lock_); + if (sent_packets_ == 0) + return 0; + + return total_packet_delay_ / static_cast<int>(sent_packets_); +} + +void FakeNetworkPipe::Process() { + int64_t time_now = TickTime::MillisecondTimestamp(); + std::queue<NetworkPacket*> packets_to_deliver; + { + rtc::CritScope crit(&lock_); + // Check the capacity link first. + while (capacity_link_.size() > 0 && + time_now >= capacity_link_.front()->arrival_time()) { + // Time to get this packet. + NetworkPacket* packet = capacity_link_.front(); + capacity_link_.pop(); + + // Packets are randomly dropped after being affected by the bottleneck. + if (UniformLoss(config_.loss_percent)) { + delete packet; + continue; + } + + // Add extra delay and jitter, but make sure the arrival time is not + // earlier than the last packet in the queue. + int extra_delay = GaussianRandom(config_.queue_delay_ms, + config_.delay_standard_deviation_ms); + if (delay_link_.size() > 0 && + packet->arrival_time() + extra_delay < + delay_link_.back()->arrival_time()) { + extra_delay = delay_link_.back()->arrival_time() - + packet->arrival_time(); + } + packet->IncrementArrivalTime(extra_delay); + if (packet->arrival_time() < next_process_time_) + next_process_time_ = packet->arrival_time(); + delay_link_.push(packet); + } + + // Check the extra delay queue. + while (delay_link_.size() > 0 && + time_now >= delay_link_.front()->arrival_time()) { + // Deliver this packet. + NetworkPacket* packet = delay_link_.front(); + packets_to_deliver.push(packet); + delay_link_.pop(); + // |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_ += packet->arrival_time() - packet->send_time(); + } + sent_packets_ += packets_to_deliver.size(); + } + while (!packets_to_deliver.empty()) { + NetworkPacket* packet = packets_to_deliver.front(); + packets_to_deliver.pop(); + packet_receiver_->DeliverPacket(MediaType::ANY, packet->data(), + packet->data_length(), PacketTime()); + delete packet; + } +} + +int64_t FakeNetworkPipe::TimeUntilNextProcess() const { + rtc::CritScope crit(&lock_); + const int64_t kDefaultProcessIntervalMs = 30; + if (capacity_link_.size() == 0 || delay_link_.size() == 0) + return kDefaultProcessIntervalMs; + return std::max<int64_t>( + next_process_time_ - TickTime::MillisecondTimestamp(), 0); +} + +} // namespace webrtc |