blob: 9bb8bab360bae8b0dc8ceae53bf4bc590862638e [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 {
23
24SimulatedNetwork::SimulatedNetwork(SimulatedNetwork::Config config,
25 uint64_t random_seed)
26 : random_(random_seed), bursting_(false) {
27 SetConfig(config);
28}
29
30SimulatedNetwork::~SimulatedNetwork() = default;
31
32void SimulatedNetwork::SetConfig(const SimulatedNetwork::Config& config) {
33 rtc::CritScope crit(&config_lock_);
Sebastian Janssoneceea312019-01-31 11:50:04 +010034 config_state_.config = config; // Shallow copy of the struct.
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020035 double prob_loss = config.loss_percent / 100.0;
Sebastian Janssoneceea312019-01-31 11:50:04 +010036 if (config_state_.config.avg_burst_loss_length == -1) {
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020037 // Uniform loss
Sebastian Janssoneceea312019-01-31 11:50:04 +010038 config_state_.prob_loss_bursting = prob_loss;
39 config_state_.prob_start_bursting = prob_loss;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020040 } else {
41 // Lose packets according to a gilbert-elliot model.
42 int avg_burst_loss_length = config.avg_burst_loss_length;
43 int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss));
44
45 RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length)
46 << "For a total packet loss of " << config.loss_percent << "%% then"
47 << " avg_burst_loss_length must be " << min_avg_burst_loss_length + 1
48 << " or higher.";
49
Sebastian Janssoneceea312019-01-31 11:50:04 +010050 config_state_.prob_loss_bursting = (1.0 - 1.0 / avg_burst_loss_length);
51 config_state_.prob_start_bursting =
52 prob_loss / (1 - prob_loss) / avg_burst_loss_length;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020053 }
54}
55
56void SimulatedNetwork::PauseTransmissionUntil(int64_t until_us) {
57 rtc::CritScope crit(&config_lock_);
Sebastian Janssoneceea312019-01-31 11:50:04 +010058 config_state_.pause_transmission_until_us = until_us;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020059}
60
61bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) {
Sebastian Janssoneceea312019-01-31 11:50:04 +010062 RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
63 ConfigState state = GetConfigState();
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010064
Sebastian Janssoneceea312019-01-31 11:50:04 +010065 UpdateCapacityQueue(state, packet.send_time_us);
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010066
Sebastian Janssoneceea312019-01-31 11:50:04 +010067 packet.size += state.config.packet_overhead;
68
69 if (state.config.queue_length_packets > 0 &&
70 capacity_link_.size() >= state.config.queue_length_packets) {
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020071 // Too many packet on the link, drop this one.
72 return false;
73 }
Sebastian Jansson2cd3b4c2018-11-06 19:18:28 +010074
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010075 // Set arrival time = send time for now; actual arrival time will be
76 // calculated in UpdateCapacityQueue.
77 queue_size_bytes_ += packet.size;
78 capacity_link_.push({packet, packet.send_time_us});
Sebastian Jansson2cd3b4c2018-11-06 19:18:28 +010079
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020080 return true;
81}
82
83absl::optional<int64_t> SimulatedNetwork::NextDeliveryTimeUs() const {
Sebastian Janssoneceea312019-01-31 11:50:04 +010084 RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +020085 if (!delay_link_.empty())
86 return delay_link_.begin()->arrival_time_us;
87 return absl::nullopt;
88}
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010089
Sebastian Janssoneceea312019-01-31 11:50:04 +010090void SimulatedNetwork::UpdateCapacityQueue(ConfigState state,
91 int64_t time_now_us) {
92 bool needs_sort = false;
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010093
Sebastian Janssoneceea312019-01-31 11:50:04 +010094 // Catch for thread races.
95 if (time_now_us < last_capacity_link_visit_us_.value_or(time_now_us))
96 return;
Christoffer Rodbro813c79b2019-01-31 09:25:12 +010097
Sebastian Janssoneceea312019-01-31 11:50:04 +010098 int64_t time_us = last_capacity_link_visit_us_.value_or(time_now_us);
99 // Check the capacity link first.
100 while (!capacity_link_.empty()) {
101 int64_t time_until_front_exits_us = 0;
102 if (state.config.link_capacity_kbps > 0) {
103 int64_t remaining_bits =
104 capacity_link_.front().packet.size * 8 - pending_drain_bits_;
105 RTC_DCHECK(remaining_bits > 0);
106 // Division rounded up - packet not delivered until its last bit is.
107 time_until_front_exits_us =
108 (1000 * remaining_bits + state.config.link_capacity_kbps - 1) /
109 state.config.link_capacity_kbps;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200110 }
111
Sebastian Janssoneceea312019-01-31 11:50:04 +0100112 if (time_us + time_until_front_exits_us > time_now_us) {
113 // Packet at front will not exit yet. Will not enter here on infinite
114 // capacity(=0) so no special handling needed.
115 pending_drain_bits_ +=
116 ((time_now_us - time_us) * state.config.link_capacity_kbps) / 1000;
117 break;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200118 }
Sebastian Janssoneceea312019-01-31 11:50:04 +0100119 if (state.config.link_capacity_kbps > 0) {
120 pending_drain_bits_ +=
121 (time_until_front_exits_us * state.config.link_capacity_kbps) / 1000;
122 } else {
123 // Enough to drain the whole queue.
124 pending_drain_bits_ = queue_size_bytes_ * 8;
125 }
126
127 // Time to get this packet.
128 PacketInfo packet = std::move(capacity_link_.front());
129 capacity_link_.pop();
130
131 time_us += time_until_front_exits_us;
132 RTC_DCHECK(time_us >= packet.packet.send_time_us);
133 packet.arrival_time_us =
134 std::max(state.pause_transmission_until_us, time_us);
135 queue_size_bytes_ -= packet.packet.size;
136 pending_drain_bits_ -= packet.packet.size * 8;
137 RTC_DCHECK(pending_drain_bits_ >= 0);
138
139 // Drop packets at an average rate of |state.config.loss_percent| with
140 // and average loss burst length of |state.config.avg_burst_loss_length|.
141 if ((bursting_ && random_.Rand<double>() < state.prob_loss_bursting) ||
142 (!bursting_ && random_.Rand<double>() < state.prob_start_bursting)) {
143 bursting_ = true;
144 packet.arrival_time_us = PacketDeliveryInfo::kNotReceived;
145 } else {
146 bursting_ = false;
147 int64_t arrival_time_jitter_us = std::max(
148 random_.Gaussian(state.config.queue_delay_ms * 1000,
149 state.config.delay_standard_deviation_ms * 1000),
150 0.0);
151
152 // If reordering is not allowed then adjust arrival_time_jitter
153 // to make sure all packets are sent in order.
154 int64_t last_arrival_time_us =
155 delay_link_.empty() ? -1 : delay_link_.back().arrival_time_us;
156 if (!state.config.allow_reordering && !delay_link_.empty() &&
157 packet.arrival_time_us + arrival_time_jitter_us <
158 last_arrival_time_us) {
159 arrival_time_jitter_us = last_arrival_time_us - packet.arrival_time_us;
160 }
161 packet.arrival_time_us += arrival_time_jitter_us;
162 if (packet.arrival_time_us >= last_arrival_time_us) {
163 last_arrival_time_us = packet.arrival_time_us;
164 } else {
165 needs_sort = true;
166 }
167 }
168 delay_link_.emplace_back(std::move(packet));
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200169 }
Sebastian Janssoneceea312019-01-31 11:50:04 +0100170 last_capacity_link_visit_us_ = time_now_us;
171 // Cannot save unused capacity for later.
172 pending_drain_bits_ = std::min(pending_drain_bits_, queue_size_bytes_ * 8);
173
174 if (needs_sort) {
175 // Packet(s) arrived out of order, make sure list is sorted.
176 std::sort(delay_link_.begin(), delay_link_.end(),
177 [](const PacketInfo& p1, const PacketInfo& p2) {
178 return p1.arrival_time_us < p2.arrival_time_us;
179 });
180 }
181}
182
183SimulatedNetwork::ConfigState SimulatedNetwork::GetConfigState() const {
184 rtc::CritScope crit(&config_lock_);
185 return config_state_;
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200186}
187
Christoffer Rodbro813c79b2019-01-31 09:25:12 +0100188std::vector<PacketDeliveryInfo> SimulatedNetwork::DequeueDeliverablePackets(
189 int64_t receive_time_us) {
Sebastian Janssoneceea312019-01-31 11:50:04 +0100190 RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
191 UpdateCapacityQueue(GetConfigState(), receive_time_us);
Christoffer Rodbro813c79b2019-01-31 09:25:12 +0100192 std::vector<PacketDeliveryInfo> packets_to_deliver;
193 // Check the extra delay queue.
194 while (!delay_link_.empty() &&
195 receive_time_us >= delay_link_.front().arrival_time_us) {
196 PacketInfo packet_info = delay_link_.front();
197 packets_to_deliver.emplace_back(
198 PacketDeliveryInfo(packet_info.packet, packet_info.arrival_time_us));
199 delay_link_.pop_front();
200 }
201 return packets_to_deliver;
202}
203
Sebastian Janssonf96b1ca2018-08-07 18:58:05 +0200204} // namespace webrtc