blob: 5a2424e75c5b624c93dd81cc813eca3e221e1905 [file] [log] [blame]
stefan@webrtc.orgcd117d22013-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;
25const int kDefaultProcessIntervalMs = 30;
26
27static 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
36class NetworkPacket {
37 public:
38 NetworkPacket(const uint8_t* data, size_t length, int64_t send_time,
39 int64_t arrival_time)
40 : data_(NULL),
41 data_length_(length),
42 send_time_(send_time),
43 arrival_time_(arrival_time) {
44 data_ = new uint8_t[length];
45 memcpy(data_, data, length);
46 }
47 ~NetworkPacket() {
48 delete [] data_;
49 }
50
51 uint8_t* data() const { return data_; }
52 size_t data_length() const { return data_length_; }
53 int64_t send_time() const { return send_time_; }
54 int64_t arrival_time() const { return arrival_time_; }
55 void IncrementArrivalTime(int64_t extra_delay) {
56 arrival_time_+= extra_delay;
57 }
58
59 private:
60 // The packet data.
61 uint8_t* data_;
62 // Length of data_.
63 size_t data_length_;
64 // The time the packet was sent out on the network.
65 const int64_t send_time_;
66 // The time the packet should arrive at the reciver.
67 int64_t arrival_time_;
68};
69
70FakeNetworkPipe::FakeNetworkPipe(
71 const FakeNetworkPipe::Config& config)
72 : lock_(CriticalSectionWrapper::CreateCriticalSection()),
73 packet_receiver_(NULL),
74 config_(config),
75 dropped_packets_(0),
76 sent_packets_(0),
77 total_packet_delay_(0),
78 next_process_time_(TickTime::MillisecondTimestamp()) {
79}
80
81FakeNetworkPipe::~FakeNetworkPipe() {
82 while (!capacity_link_.empty()) {
83 delete capacity_link_.front();
84 capacity_link_.pop();
85 }
86 while (!delay_link_.empty()) {
87 delete delay_link_.front();
88 delay_link_.pop();
89 }
90}
91
92void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) {
93 packet_receiver_ = receiver;
94}
95
96void FakeNetworkPipe::SendPacket(const uint8_t* data, size_t data_length) {
97 // A NULL packet_receiver_ means that this pipe will terminate the flow of
98 // packets.
99 if (packet_receiver_ == NULL)
100 return;
101 CriticalSectionScoped crit(lock_.get());
102 if (config_.queue_length > 0 &&
103 capacity_link_.size() >= config_.queue_length) {
104 // Too many packet on the link, drop this one.
105 ++dropped_packets_;
106 return;
107 }
108
109 int64_t time_now = TickTime::MillisecondTimestamp();
110
111 // Delay introduced by the link capacity.
112 int64_t capacity_delay_ms = 0;
113 if (config_.link_capacity_kbps > 0)
114 capacity_delay_ms = data_length / (config_.link_capacity_kbps / 8);
115 int64_t network_start_time = time_now;
116
117 // Check if there already are packets on the link and change network start
118 // time if there is.
119 if (capacity_link_.size() > 0)
120 network_start_time = capacity_link_.back()->arrival_time();
121
122 int64_t arrival_time = network_start_time + capacity_delay_ms;
123 NetworkPacket* packet = new NetworkPacket(data, data_length, time_now,
124 arrival_time);
125 capacity_link_.push(packet);
126}
127
128float FakeNetworkPipe::PercentageLoss() {
129 CriticalSectionScoped crit(lock_.get());
130 if (sent_packets_ == 0)
131 return 0;
132
133 return static_cast<float>(dropped_packets_) /
134 (sent_packets_ + dropped_packets_);
135}
136
137int FakeNetworkPipe::AverageDelay() {
138 CriticalSectionScoped crit(lock_.get());
139 if (sent_packets_ == 0)
140 return 0;
141
142 return total_packet_delay_ / static_cast<int>(sent_packets_);
143}
144
145void FakeNetworkPipe::Process() {
146 int64_t time_now = TickTime::MillisecondTimestamp();
147 std::queue<NetworkPacket*> packets_to_deliver;
148 {
149 CriticalSectionScoped crit(lock_.get());
150 // Check the capacity link first.
151 while (capacity_link_.size() > 0 &&
152 time_now >= capacity_link_.front()->arrival_time()) {
153 // Time to get this packet.
154 NetworkPacket* packet = capacity_link_.front();
155 capacity_link_.pop();
156
157 // Add extra delay and jitter, but make sure the arrival time is not
158 // earlier than the last packet in the queue.
159 int extra_delay = GaussianRandom(config_.queue_delay_ms,
160 config_.delay_standard_deviation_ms);
161 if (delay_link_.size() > 0 &&
162 packet->arrival_time() + extra_delay <
163 delay_link_.back()->arrival_time()) {
164 extra_delay = delay_link_.back()->arrival_time() -
165 packet->arrival_time();
166 }
167 packet->IncrementArrivalTime(extra_delay);
168 if (packet->arrival_time() < next_process_time_)
169 next_process_time_ = packet->arrival_time();
170 delay_link_.push(packet);
171 }
172
173 // Check the extra delay queue.
174 while (delay_link_.size() > 0 &&
175 time_now >= delay_link_.front()->arrival_time()) {
176 // Deliver this packet.
177 NetworkPacket* packet = delay_link_.front();
178 packets_to_deliver.push(packet);
179 delay_link_.pop();
180 // |time_now| might be later than when the packet should have arrived, due
181 // to NetworkProcess being called too late. For stats, use the time it
182 // should have been on the link.
183 total_packet_delay_ += packet->arrival_time() - packet->send_time();
184 }
185 sent_packets_ += packets_to_deliver.size();
186 }
187 while (!packets_to_deliver.empty()) {
188 NetworkPacket* packet = packets_to_deliver.front();
189 packets_to_deliver.pop();
190 packet_receiver_->DeliverPacket(packet->data(), packet->data_length());
191 delete packet;
192 }
193}
194
195int FakeNetworkPipe::TimeUntilNextProcess() const {
196 CriticalSectionScoped crit(lock_.get());
197 if (capacity_link_.size() == 0 || delay_link_.size() == 0)
198 return kDefaultProcessIntervalMs;
199 return std::max(static_cast<int>(next_process_time_ -
200 TickTime::MillisecondTimestamp()), 0);
201}
202
203} // namespace webrtc