blob: ee58dce718248aa106bc3a817a34a921c8929fa6 [file] [log] [blame]
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +02001/*
2 * Copyright 2018 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 "call/simulated_network.h"
12
13#include <algorithm>
14#include <cmath>
15#include <utility>
16
17namespace webrtc {
18
19SimulatedNetwork::SimulatedNetwork(SimulatedNetwork::Config config,
20 uint64_t random_seed)
21 : random_(random_seed), bursting_(false) {
22 SetConfig(config);
23}
24
25SimulatedNetwork::~SimulatedNetwork() = default;
26
27void SimulatedNetwork::SetConfig(const SimulatedNetwork::Config& config) {
28 rtc::CritScope crit(&config_lock_);
29 config_ = config; // Shallow copy of the struct.
30 double prob_loss = config.loss_percent / 100.0;
31 if (config_.avg_burst_loss_length == -1) {
32 // Uniform loss
33 prob_loss_bursting_ = prob_loss;
34 prob_start_bursting_ = prob_loss;
35 } else {
36 // Lose packets according to a gilbert-elliot model.
37 int avg_burst_loss_length = config.avg_burst_loss_length;
38 int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss));
39
40 RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length)
41 << "For a total packet loss of " << config.loss_percent << "%% then"
42 << " avg_burst_loss_length must be " << min_avg_burst_loss_length + 1
43 << " or higher.";
44
45 prob_loss_bursting_ = (1.0 - 1.0 / avg_burst_loss_length);
46 prob_start_bursting_ = prob_loss / (1 - prob_loss) / avg_burst_loss_length;
47 }
48}
49
50void SimulatedNetwork::PauseTransmissionUntil(int64_t until_us) {
51 rtc::CritScope crit(&config_lock_);
52 pause_transmission_until_us_ = until_us;
53}
54
55bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) {
56 Config config;
57 {
58 rtc::CritScope crit(&config_lock_);
59 config = config_;
60 }
61 rtc::CritScope crit(&process_lock_);
62 if (config.queue_length_packets > 0 &&
63 capacity_link_.size() >= config.queue_length_packets) {
64 // Too many packet on the link, drop this one.
65 return false;
66 }
67
68 // Delay introduced by the link capacity.
69 int64_t capacity_delay_ms = 0;
70 if (config.link_capacity_kbps > 0) {
71 // Using bytes per millisecond to avoid losing precision.
72 const int64_t bytes_per_millisecond = config.link_capacity_kbps / 8;
73 // To round to the closest millisecond we add half a milliseconds worth of
74 // bytes to the delay calculation.
75 capacity_delay_ms = (packet.size + capacity_delay_error_bytes_ +
76 bytes_per_millisecond / 2) /
77 bytes_per_millisecond;
78 capacity_delay_error_bytes_ +=
79 packet.size - capacity_delay_ms * bytes_per_millisecond;
80 }
81 int64_t network_start_time_us = packet.send_time_us;
82
83 {
84 rtc::CritScope crit(&config_lock_);
85 if (pause_transmission_until_us_) {
86 network_start_time_us =
87 std::max(network_start_time_us, *pause_transmission_until_us_);
88 pause_transmission_until_us_.reset();
89 }
90 }
91 // Check if there already are packets on the link and change network start
92 // time forward if there is.
93 if (!capacity_link_.empty() &&
94 network_start_time_us < capacity_link_.back().arrival_time_us)
95 network_start_time_us = capacity_link_.back().arrival_time_us;
96
97 int64_t arrival_time_us = network_start_time_us + capacity_delay_ms * 1000;
98 capacity_link_.push({packet, arrival_time_us});
99 return true;
100}
101
102absl::optional<int64_t> SimulatedNetwork::NextDeliveryTimeUs() const {
103 if (!delay_link_.empty())
104 return delay_link_.begin()->arrival_time_us;
105 return absl::nullopt;
106}
107std::vector<PacketDeliveryInfo> SimulatedNetwork::DequeueDeliverablePackets(
108 int64_t receive_time_us) {
109 int64_t time_now_us = receive_time_us;
110 Config config;
111 double prob_loss_bursting;
112 double prob_start_bursting;
113 {
114 rtc::CritScope crit(&config_lock_);
115 config = config_;
116 prob_loss_bursting = prob_loss_bursting_;
117 prob_start_bursting = prob_start_bursting_;
118 }
119 {
120 rtc::CritScope crit(&process_lock_);
121 // Check the capacity link first.
122 if (!capacity_link_.empty()) {
123 int64_t last_arrival_time_us =
124 delay_link_.empty() ? -1 : delay_link_.back().arrival_time_us;
125 bool needs_sort = false;
126 while (!capacity_link_.empty() &&
127 time_now_us >= capacity_link_.front().arrival_time_us) {
128 // Time to get this packet.
129 PacketInfo packet = std::move(capacity_link_.front());
130 capacity_link_.pop();
131
132 // Drop packets at an average rate of |config_.loss_percent| with
133 // and average loss burst length of |config_.avg_burst_loss_length|.
134 if ((bursting_ && random_.Rand<double>() < prob_loss_bursting) ||
135 (!bursting_ && random_.Rand<double>() < prob_start_bursting)) {
136 bursting_ = true;
137 continue;
138 } else {
139 bursting_ = false;
140 }
141
142 int64_t arrival_time_jitter_us = std::max(
143 random_.Gaussian(config.queue_delay_ms * 1000,
144 config.delay_standard_deviation_ms * 1000),
145 0.0);
146
147 // If reordering is not allowed then adjust arrival_time_jitter
148 // to make sure all packets are sent in order.
149 if (!config.allow_reordering && !delay_link_.empty() &&
150 packet.arrival_time_us + arrival_time_jitter_us <
151 last_arrival_time_us) {
152 arrival_time_jitter_us =
153 last_arrival_time_us - packet.arrival_time_us;
154 }
155 packet.arrival_time_us += arrival_time_jitter_us;
156 if (packet.arrival_time_us >= last_arrival_time_us) {
157 last_arrival_time_us = packet.arrival_time_us;
158 } else {
159 needs_sort = true;
160 }
161 delay_link_.emplace_back(std::move(packet));
162 }
163
164 if (needs_sort) {
165 // Packet(s) arrived out of order, make sure list is sorted.
166 std::sort(delay_link_.begin(), delay_link_.end(),
167 [](const PacketInfo& p1, const PacketInfo& p2) {
168 return p1.arrival_time_us < p2.arrival_time_us;
169 });
170 }
171 }
172
173 std::vector<PacketDeliveryInfo> packets_to_deliver;
174 // Check the extra delay queue.
175 while (!delay_link_.empty() &&
176 time_now_us >= delay_link_.front().arrival_time_us) {
177 PacketInfo packet_info = delay_link_.front();
178 packets_to_deliver.emplace_back(
179 PacketDeliveryInfo(packet_info.packet, packet_info.arrival_time_us));
180 delay_link_.pop_front();
181 }
182 return packets_to_deliver;
183 }
184}
185
186} // namespace webrtc