aboutsummaryrefslogtreecommitdiff
path: root/webrtc/test/fake_network_pipe.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/test/fake_network_pipe.cc')
-rw-r--r--webrtc/test/fake_network_pipe.cc217
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