• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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