aboutsummaryrefslogtreecommitdiff
path: root/webrtc/test/fake_network_pipe.cc
blob: 491a0526b9b39b991370be8616d4abe005b754ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
 *  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/clock.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(Clock* clock,
                                 const FakeNetworkPipe::Config& config)
    : clock_(clock),
      packet_receiver_(NULL),
      config_(config),
      dropped_packets_(0),
      sent_packets_(0),
      total_packet_delay_(0),
      next_process_time_(clock_->TimeInMilliseconds()) {}

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 = clock_->TimeInMilliseconds();

  // 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 static_cast<int>(total_packet_delay_ /
                          static_cast<int64_t>(sent_packets_));
}

void FakeNetworkPipe::Process() {
  int64_t time_now = clock_->TimeInMilliseconds();
  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_ - clock_->TimeInMilliseconds(),
                           0);
}

}  // namespace webrtc