blob: 93a4f6e1c61ab7b2a292f03f0adbc73cba98ac36 [file] [log] [blame]
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/test/fake_network_pipe.h"
12
13#include <assert.h>
14#include <math.h>
15#include <string.h>
16#include <algorithm>
17
18#include "webrtc/call.h"
19#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
20#include "webrtc/system_wrappers/interface/tick_util.h"
21
22namespace webrtc {
23
24const double kPi = 3.14159265;
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +000025
26static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) {
27 // Creating a Normal distribution variable from two independent uniform
28 // variables based on the Box-Muller transform.
29 double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
30 double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
31 return static_cast<int>(mean_delay_ms + standard_deviation_ms *
32 sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2));
33}
34
stefan@webrtc.orgbfe6e082014-07-31 12:30:18 +000035static bool UniformLoss(int loss_percent) {
36 int outcome = rand() % 100;
37 return outcome < loss_percent;
38}
39
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +000040class NetworkPacket {
41 public:
42 NetworkPacket(const uint8_t* data, size_t length, int64_t send_time,
43 int64_t arrival_time)
44 : data_(NULL),
45 data_length_(length),
46 send_time_(send_time),
47 arrival_time_(arrival_time) {
48 data_ = new uint8_t[length];
49 memcpy(data_, data, length);
50 }
51 ~NetworkPacket() {
52 delete [] data_;
53 }
54
55 uint8_t* data() const { return data_; }
56 size_t data_length() const { return data_length_; }
57 int64_t send_time() const { return send_time_; }
58 int64_t arrival_time() const { return arrival_time_; }
59 void IncrementArrivalTime(int64_t extra_delay) {
60 arrival_time_+= extra_delay;
61 }
62
63 private:
64 // The packet data.
65 uint8_t* data_;
66 // Length of data_.
67 size_t data_length_;
68 // The time the packet was sent out on the network.
69 const int64_t send_time_;
70 // The time the packet should arrive at the reciver.
71 int64_t arrival_time_;
72};
73
74FakeNetworkPipe::FakeNetworkPipe(
75 const FakeNetworkPipe::Config& config)
76 : lock_(CriticalSectionWrapper::CreateCriticalSection()),
77 packet_receiver_(NULL),
78 config_(config),
79 dropped_packets_(0),
80 sent_packets_(0),
81 total_packet_delay_(0),
82 next_process_time_(TickTime::MillisecondTimestamp()) {
83}
84
85FakeNetworkPipe::~FakeNetworkPipe() {
86 while (!capacity_link_.empty()) {
87 delete capacity_link_.front();
88 capacity_link_.pop();
89 }
90 while (!delay_link_.empty()) {
91 delete delay_link_.front();
92 delay_link_.pop();
93 }
94}
95
96void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) {
97 packet_receiver_ = receiver;
98}
99
henrik.lundin@webrtc.orgc0e9aeb2014-02-26 13:34:52 +0000100void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) {
101 CriticalSectionScoped crit(lock_.get());
102 config_ = config; // Shallow copy of the struct.
103}
104
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000105void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) {
106 // A NULL packet_receiver_ means that this pipe will terminate the flow of
107 // packets.
108 if (packet_receiver_ == NULL)
109 return;
110 CriticalSectionScoped crit(lock_.get());
stefan@webrtc.orgb8e9e442014-07-09 11:29:06 +0000111 if (config_.queue_length_packets > 0 &&
112 capacity_link_.size() >= config_.queue_length_packets) {
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000113 // Too many packet on the link, drop this one.
114 ++dropped_packets_;
115 return;
116 }
117
118 int64_t time_now = TickTime::MillisecondTimestamp();
119
120 // Delay introduced by the link capacity.
121 int64_t capacity_delay_ms = 0;
122 if (config_.link_capacity_kbps > 0)
123 capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8);
124 int64_t network_start_time = time_now;
125
126 // Check if there already are packets on the link and change network start
127 // time if there is.
128 if (capacity_link_.size() > 0)
129 network_start_time = capacity_link_.back()->arrival_time();
130
131 int64_t arrival_time = network_start_time + capacity_delay_ms;
132 NetworkPacket* packet = new NetworkPacket(data, data_length, time_now,
133 arrival_time);
134 capacity_link_.push(packet);
135}
136
137float FakeNetworkPipe::PercentageLoss() {
138 CriticalSectionScoped crit(lock_.get());
139 if (sent_packets_ == 0)
140 return 0;
141
142 return static_cast<float>(dropped_packets_) /
143 (sent_packets_ + dropped_packets_);
144}
145
146int FakeNetworkPipe::AverageDelay() {
147 CriticalSectionScoped crit(lock_.get());
148 if (sent_packets_ == 0)
149 return 0;
150
151 return total_packet_delay_ / static_cast<int>(sent_packets_);
152}
153
154void FakeNetworkPipe::Process() {
155 int64_t time_now = TickTime::MillisecondTimestamp();
156 std::queue<NetworkPacket*> packets_to_deliver;
157 {
158 CriticalSectionScoped crit(lock_.get());
159 // Check the capacity link first.
160 while (capacity_link_.size() > 0 &&
161 time_now >= capacity_link_.front()->arrival_time()) {
162 // Time to get this packet.
163 NetworkPacket* packet = capacity_link_.front();
164 capacity_link_.pop();
165
stefan@webrtc.orgbfe6e082014-07-31 12:30:18 +0000166 // Packets are randomly dropped after being affected by the bottleneck.
167 if (UniformLoss(config_.loss_percent)) {
168 delete packet;
169 continue;
170 }
171
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000172 // Add extra delay and jitter, but make sure the arrival time is not
173 // earlier than the last packet in the queue.
174 int extra_delay = GaussianRandom(config_.queue_delay_ms,
175 config_.delay_standard_deviation_ms);
176 if (delay_link_.size() > 0 &&
177 packet->arrival_time() + extra_delay <
178 delay_link_.back()->arrival_time()) {
179 extra_delay = delay_link_.back()->arrival_time() -
180 packet->arrival_time();
181 }
182 packet->IncrementArrivalTime(extra_delay);
183 if (packet->arrival_time() < next_process_time_)
184 next_process_time_ = packet->arrival_time();
185 delay_link_.push(packet);
186 }
187
188 // Check the extra delay queue.
189 while (delay_link_.size() > 0 &&
190 time_now >= delay_link_.front()->arrival_time()) {
191 // Deliver this packet.
192 NetworkPacket* packet = delay_link_.front();
193 packets_to_deliver.push(packet);
194 delay_link_.pop();
195 // |time_now| might be later than when the packet should have arrived, due
196 // to NetworkProcess being called too late. For stats, use the time it
197 // should have been on the link.
198 total_packet_delay_ += packet->arrival_time() - packet->send_time();
199 }
200 sent_packets_ += packets_to_deliver.size();
201 }
202 while (!packets_to_deliver.empty()) {
203 NetworkPacket* packet = packets_to_deliver.front();
204 packets_to_deliver.pop();
205 packet_receiver_->DeliverPacket(packet->data(), packet->data_length());
206 delete packet;
207 }
208}
209
pkasting@chromium.org0b1534c2014-12-15 22:09:40 +0000210int64_t FakeNetworkPipe::TimeUntilNextProcess() const {
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000211 CriticalSectionScoped crit(lock_.get());
pkasting@chromium.org0b1534c2014-12-15 22:09:40 +0000212 const int64_t kDefaultProcessIntervalMs = 30;
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000213 if (capacity_link_.size() == 0 || delay_link_.size() == 0)
214 return kDefaultProcessIntervalMs;
pkasting@chromium.org0b1534c2014-12-15 22:09:40 +0000215 return std::max<int64_t>(
216 next_process_time_ - TickTime::MillisecondTimestamp(), 0);
stefan@webrtc.orgfaada6e2013-12-18 20:28:25 +0000217}
218
219} // namespace webrtc