blob: c80255f38855c82ae9890aec15bb44a6d8ff09ae [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>
Yves Gerey3e707812018-11-28 16:47:49 +010016
Sebastian Jansson2cd3b4c2018-11-06 19:18:28 +010017#include "api/units/data_rate.h"
Yves Gerey3e707812018-11-28 16:47:49 +010018#include "api/units/data_size.h"
19#include "api/units/time_delta.h"
20#include "rtc_base/checks.h"
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020021
22namespace webrtc {
Sebastian Jansson836fee12019-02-08 16:08:10 +010023namespace {
24constexpr int64_t kDefaultProcessDelayUs = 5000;
25}
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020026
27SimulatedNetwork::SimulatedNetwork(SimulatedNetwork::Config config,
28 uint64_t random_seed)
29 : random_(random_seed), bursting_(false) {
30 SetConfig(config);
31}
32
33SimulatedNetwork::~SimulatedNetwork() = default;
34
35void SimulatedNetwork::SetConfig(const SimulatedNetwork::Config& config) {
36 rtc::CritScope crit(&config_lock_);
Sebastian Janssoneceea312019-01-31 11:50:04 +010037 config_state_.config = config; // Shallow copy of the struct.
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020038 double prob_loss = config.loss_percent / 100.0;
Sebastian Janssoneceea312019-01-31 11:50:04 +010039 if (config_state_.config.avg_burst_loss_length == -1) {
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020040 // Uniform loss
Sebastian Janssoneceea312019-01-31 11:50:04 +010041 config_state_.prob_loss_bursting = prob_loss;
42 config_state_.prob_start_bursting = prob_loss;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020043 } else {
44 // Lose packets according to a gilbert-elliot model.
45 int avg_burst_loss_length = config.avg_burst_loss_length;
46 int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss));
47
48 RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length)
49 << "For a total packet loss of " << config.loss_percent << "%% then"
50 << " avg_burst_loss_length must be " << min_avg_burst_loss_length + 1
51 << " or higher.";
52
Sebastian Janssoneceea312019-01-31 11:50:04 +010053 config_state_.prob_loss_bursting = (1.0 - 1.0 / avg_burst_loss_length);
54 config_state_.prob_start_bursting =
55 prob_loss / (1 - prob_loss) / avg_burst_loss_length;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020056 }
57}
58
59void SimulatedNetwork::PauseTransmissionUntil(int64_t until_us) {
60 rtc::CritScope crit(&config_lock_);
Sebastian Janssoneceea312019-01-31 11:50:04 +010061 config_state_.pause_transmission_until_us = until_us;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020062}
63
64bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) {
Sebastian Janssoneceea312019-01-31 11:50:04 +010065 RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
66 ConfigState state = GetConfigState();
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010067
Sebastian Janssoneceea312019-01-31 11:50:04 +010068 UpdateCapacityQueue(state, packet.send_time_us);
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010069
Sebastian Janssoneceea312019-01-31 11:50:04 +010070 packet.size += state.config.packet_overhead;
71
72 if (state.config.queue_length_packets > 0 &&
73 capacity_link_.size() >= state.config.queue_length_packets) {
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020074 // Too many packet on the link, drop this one.
75 return false;
76 }
Sebastian Jansson2cd3b4c2018-11-06 19:18:28 +010077
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010078 // Set arrival time = send time for now; actual arrival time will be
79 // calculated in UpdateCapacityQueue.
80 queue_size_bytes_ += packet.size;
81 capacity_link_.push({packet, packet.send_time_us});
Sebastian Jansson836fee12019-02-08 16:08:10 +010082 if (!next_process_time_us_) {
83 next_process_time_us_ = packet.send_time_us + kDefaultProcessDelayUs;
84 }
Sebastian Jansson2cd3b4c2018-11-06 19:18:28 +010085
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020086 return true;
87}
88
89absl::optional<int64_t> SimulatedNetwork::NextDeliveryTimeUs() const {
Sebastian Janssoneceea312019-01-31 11:50:04 +010090 RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
Sebastian Jansson836fee12019-02-08 16:08:10 +010091 return next_process_time_us_;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020092}
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010093
Sebastian Janssoneceea312019-01-31 11:50:04 +010094void SimulatedNetwork::UpdateCapacityQueue(ConfigState state,
95 int64_t time_now_us) {
96 bool needs_sort = false;
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010097
Sebastian Janssoneceea312019-01-31 11:50:04 +010098 // Catch for thread races.
99 if (time_now_us < last_capacity_link_visit_us_.value_or(time_now_us))
100 return;
Christoffer Rodbro813c79b2019-01-31 09:25:12 +0100101
Sebastian Janssoneceea312019-01-31 11:50:04 +0100102 int64_t time_us = last_capacity_link_visit_us_.value_or(time_now_us);
103 // Check the capacity link first.
104 while (!capacity_link_.empty()) {
105 int64_t time_until_front_exits_us = 0;
106 if (state.config.link_capacity_kbps > 0) {
107 int64_t remaining_bits =
108 capacity_link_.front().packet.size * 8 - pending_drain_bits_;
109 RTC_DCHECK(remaining_bits > 0);
110 // Division rounded up - packet not delivered until its last bit is.
111 time_until_front_exits_us =
112 (1000 * remaining_bits + state.config.link_capacity_kbps - 1) /
113 state.config.link_capacity_kbps;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200114 }
115
Sebastian Janssoneceea312019-01-31 11:50:04 +0100116 if (time_us + time_until_front_exits_us > time_now_us) {
117 // Packet at front will not exit yet. Will not enter here on infinite
118 // capacity(=0) so no special handling needed.
119 pending_drain_bits_ +=
120 ((time_now_us - time_us) * state.config.link_capacity_kbps) / 1000;
121 break;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200122 }
Sebastian Janssoneceea312019-01-31 11:50:04 +0100123 if (state.config.link_capacity_kbps > 0) {
124 pending_drain_bits_ +=
125 (time_until_front_exits_us * state.config.link_capacity_kbps) / 1000;
126 } else {
127 // Enough to drain the whole queue.
128 pending_drain_bits_ = queue_size_bytes_ * 8;
129 }
130
131 // Time to get this packet.
Mirko Bonadei05cf6be2019-01-31 21:38:12 +0100132 PacketInfo packet = capacity_link_.front();
Sebastian Janssoneceea312019-01-31 11:50:04 +0100133 capacity_link_.pop();
134
135 time_us += time_until_front_exits_us;
136 RTC_DCHECK(time_us >= packet.packet.send_time_us);
137 packet.arrival_time_us =
138 std::max(state.pause_transmission_until_us, time_us);
139 queue_size_bytes_ -= packet.packet.size;
140 pending_drain_bits_ -= packet.packet.size * 8;
141 RTC_DCHECK(pending_drain_bits_ >= 0);
142
143 // Drop packets at an average rate of |state.config.loss_percent| with
144 // and average loss burst length of |state.config.avg_burst_loss_length|.
145 if ((bursting_ && random_.Rand<double>() < state.prob_loss_bursting) ||
146 (!bursting_ && random_.Rand<double>() < state.prob_start_bursting)) {
147 bursting_ = true;
148 packet.arrival_time_us = PacketDeliveryInfo::kNotReceived;
149 } else {
150 bursting_ = false;
151 int64_t arrival_time_jitter_us = std::max(
152 random_.Gaussian(state.config.queue_delay_ms * 1000,
153 state.config.delay_standard_deviation_ms * 1000),
154 0.0);
155
156 // If reordering is not allowed then adjust arrival_time_jitter
157 // to make sure all packets are sent in order.
158 int64_t last_arrival_time_us =
159 delay_link_.empty() ? -1 : delay_link_.back().arrival_time_us;
160 if (!state.config.allow_reordering && !delay_link_.empty() &&
161 packet.arrival_time_us + arrival_time_jitter_us <
162 last_arrival_time_us) {
163 arrival_time_jitter_us = last_arrival_time_us - packet.arrival_time_us;
164 }
165 packet.arrival_time_us += arrival_time_jitter_us;
166 if (packet.arrival_time_us >= last_arrival_time_us) {
167 last_arrival_time_us = packet.arrival_time_us;
168 } else {
169 needs_sort = true;
170 }
171 }
Mirko Bonadei05cf6be2019-01-31 21:38:12 +0100172 delay_link_.emplace_back(packet);
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200173 }
Sebastian Janssoneceea312019-01-31 11:50:04 +0100174 last_capacity_link_visit_us_ = time_now_us;
175 // Cannot save unused capacity for later.
176 pending_drain_bits_ = std::min(pending_drain_bits_, queue_size_bytes_ * 8);
177
178 if (needs_sort) {
179 // Packet(s) arrived out of order, make sure list is sorted.
180 std::sort(delay_link_.begin(), delay_link_.end(),
181 [](const PacketInfo& p1, const PacketInfo& p2) {
182 return p1.arrival_time_us < p2.arrival_time_us;
183 });
184 }
185}
186
187SimulatedNetwork::ConfigState SimulatedNetwork::GetConfigState() const {
188 rtc::CritScope crit(&config_lock_);
189 return config_state_;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200190}
191
Christoffer Rodbro813c79b2019-01-31 09:25:12 +0100192std::vector<PacketDeliveryInfo> SimulatedNetwork::DequeueDeliverablePackets(
193 int64_t receive_time_us) {
Sebastian Janssoneceea312019-01-31 11:50:04 +0100194 RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
195 UpdateCapacityQueue(GetConfigState(), receive_time_us);
Christoffer Rodbro813c79b2019-01-31 09:25:12 +0100196 std::vector<PacketDeliveryInfo> packets_to_deliver;
197 // Check the extra delay queue.
198 while (!delay_link_.empty() &&
199 receive_time_us >= delay_link_.front().arrival_time_us) {
200 PacketInfo packet_info = delay_link_.front();
201 packets_to_deliver.emplace_back(
202 PacketDeliveryInfo(packet_info.packet, packet_info.arrival_time_us));
203 delay_link_.pop_front();
204 }
Sebastian Jansson836fee12019-02-08 16:08:10 +0100205
206 if (!delay_link_.empty()) {
207 next_process_time_us_ = delay_link_.front().arrival_time_us;
208 } else if (!capacity_link_.empty()) {
209 next_process_time_us_ = receive_time_us + kDefaultProcessDelayUs;
210 } else {
211 next_process_time_us_.reset();
212 }
Christoffer Rodbro813c79b2019-01-31 09:25:12 +0100213 return packets_to_deliver;
214}
215
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200216} // namespace webrtc