1 /*
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
22 namespace webrtc {
23
24 const double kPi = 3.14159265;
25 const int kDefaultProcessIntervalMs = 30;
26
GaussianRandom(int mean_delay_ms,int standard_deviation_ms)27 static int GaussianRandom(int mean_delay_ms, int standard_deviation_ms) {
28 // Creating a Normal distribution variable from two independent uniform
29 // variables based on the Box-Muller transform.
30 double uniform1 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
31 double uniform2 = (rand() + 1.0) / (RAND_MAX + 1.0); // NOLINT
32 return static_cast<int>(mean_delay_ms + standard_deviation_ms *
33 sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2));
34 }
35
UniformLoss(int loss_percent)36 static bool UniformLoss(int loss_percent) {
37 int outcome = rand() % 100;
38 return outcome < loss_percent;
39 }
40
41 class NetworkPacket {
42 public:
NetworkPacket(const uint8_t * data,size_t length,int64_t send_time,int64_t arrival_time)43 NetworkPacket(const uint8_t* data, size_t length, int64_t send_time,
44 int64_t arrival_time)
45 : data_(NULL),
46 data_length_(length),
47 send_time_(send_time),
48 arrival_time_(arrival_time) {
49 data_ = new uint8_t[length];
50 memcpy(data_, data, length);
51 }
~NetworkPacket()52 ~NetworkPacket() {
53 delete [] data_;
54 }
55
data() const56 uint8_t* data() const { return data_; }
data_length() const57 size_t data_length() const { return data_length_; }
send_time() const58 int64_t send_time() const { return send_time_; }
arrival_time() const59 int64_t arrival_time() const { return arrival_time_; }
IncrementArrivalTime(int64_t extra_delay)60 void IncrementArrivalTime(int64_t extra_delay) {
61 arrival_time_+= extra_delay;
62 }
63
64 private:
65 // The packet data.
66 uint8_t* data_;
67 // Length of data_.
68 size_t data_length_;
69 // The time the packet was sent out on the network.
70 const int64_t send_time_;
71 // The time the packet should arrive at the reciver.
72 int64_t arrival_time_;
73 };
74
FakeNetworkPipe(const FakeNetworkPipe::Config & config)75 FakeNetworkPipe::FakeNetworkPipe(
76 const FakeNetworkPipe::Config& config)
77 : lock_(CriticalSectionWrapper::CreateCriticalSection()),
78 packet_receiver_(NULL),
79 config_(config),
80 dropped_packets_(0),
81 sent_packets_(0),
82 total_packet_delay_(0),
83 next_process_time_(TickTime::MillisecondTimestamp()) {
84 }
85
~FakeNetworkPipe()86 FakeNetworkPipe::~FakeNetworkPipe() {
87 while (!capacity_link_.empty()) {
88 delete capacity_link_.front();
89 capacity_link_.pop();
90 }
91 while (!delay_link_.empty()) {
92 delete delay_link_.front();
93 delay_link_.pop();
94 }
95 }
96
SetReceiver(PacketReceiver * receiver)97 void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) {
98 packet_receiver_ = receiver;
99 }
100
SetConfig(const FakeNetworkPipe::Config & config)101 void FakeNetworkPipe::SetConfig(const FakeNetworkPipe::Config& config) {
102 CriticalSectionScoped crit(lock_.get());
103 config_ = config; // Shallow copy of the struct.
104 }
105
SendPacket(const uint8_t * data,size_t data_length)106 void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) {
107 // A NULL packet_receiver_ means that this pipe will terminate the flow of
108 // packets.
109 if (packet_receiver_ == NULL)
110 return;
111 CriticalSectionScoped crit(lock_.get());
112 if (config_.queue_length_packets > 0 &&
113 capacity_link_.size() >= config_.queue_length_packets) {
114 // Too many packet on the link, drop this one.
115 ++dropped_packets_;
116 return;
117 }
118
119 int64_t time_now = TickTime::MillisecondTimestamp();
120
121 // Delay introduced by the link capacity.
122 int64_t capacity_delay_ms = 0;
123 if (config_.link_capacity_kbps > 0)
124 capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8);
125 int64_t network_start_time = time_now;
126
127 // Check if there already are packets on the link and change network start
128 // time if there is.
129 if (capacity_link_.size() > 0)
130 network_start_time = capacity_link_.back()->arrival_time();
131
132 int64_t arrival_time = network_start_time + capacity_delay_ms;
133 NetworkPacket* packet = new NetworkPacket(data, data_length, time_now,
134 arrival_time);
135 capacity_link_.push(packet);
136 }
137
PercentageLoss()138 float FakeNetworkPipe::PercentageLoss() {
139 CriticalSectionScoped crit(lock_.get());
140 if (sent_packets_ == 0)
141 return 0;
142
143 return static_cast<float>(dropped_packets_) /
144 (sent_packets_ + dropped_packets_);
145 }
146
AverageDelay()147 int FakeNetworkPipe::AverageDelay() {
148 CriticalSectionScoped crit(lock_.get());
149 if (sent_packets_ == 0)
150 return 0;
151
152 return total_packet_delay_ / static_cast<int>(sent_packets_);
153 }
154
Process()155 void FakeNetworkPipe::Process() {
156 int64_t time_now = TickTime::MillisecondTimestamp();
157 std::queue<NetworkPacket*> packets_to_deliver;
158 {
159 CriticalSectionScoped crit(lock_.get());
160 // Check the capacity link first.
161 while (capacity_link_.size() > 0 &&
162 time_now >= capacity_link_.front()->arrival_time()) {
163 // Time to get this packet.
164 NetworkPacket* packet = capacity_link_.front();
165 capacity_link_.pop();
166
167 // Packets are randomly dropped after being affected by the bottleneck.
168 if (UniformLoss(config_.loss_percent)) {
169 delete packet;
170 continue;
171 }
172
173 // Add extra delay and jitter, but make sure the arrival time is not
174 // earlier than the last packet in the queue.
175 int extra_delay = GaussianRandom(config_.queue_delay_ms,
176 config_.delay_standard_deviation_ms);
177 if (delay_link_.size() > 0 &&
178 packet->arrival_time() + extra_delay <
179 delay_link_.back()->arrival_time()) {
180 extra_delay = delay_link_.back()->arrival_time() -
181 packet->arrival_time();
182 }
183 packet->IncrementArrivalTime(extra_delay);
184 if (packet->arrival_time() < next_process_time_)
185 next_process_time_ = packet->arrival_time();
186 delay_link_.push(packet);
187 }
188
189 // Check the extra delay queue.
190 while (delay_link_.size() > 0 &&
191 time_now >= delay_link_.front()->arrival_time()) {
192 // Deliver this packet.
193 NetworkPacket* packet = delay_link_.front();
194 packets_to_deliver.push(packet);
195 delay_link_.pop();
196 // |time_now| might be later than when the packet should have arrived, due
197 // to NetworkProcess being called too late. For stats, use the time it
198 // should have been on the link.
199 total_packet_delay_ += packet->arrival_time() - packet->send_time();
200 }
201 sent_packets_ += packets_to_deliver.size();
202 }
203 while (!packets_to_deliver.empty()) {
204 NetworkPacket* packet = packets_to_deliver.front();
205 packets_to_deliver.pop();
206 packet_receiver_->DeliverPacket(packet->data(), packet->data_length());
207 delete packet;
208 }
209 }
210
TimeUntilNextProcess() const211 int FakeNetworkPipe::TimeUntilNextProcess() const {
212 CriticalSectionScoped crit(lock_.get());
213 if (capacity_link_.size() == 0 || delay_link_.size() == 0)
214 return kDefaultProcessIntervalMs;
215 return std::max(static_cast<int>(next_process_time_ -
216 TickTime::MillisecondTimestamp()), 0);
217 }
218
219 } // namespace webrtc
220