1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright 2018 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "call/simulated_network.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <cmath>
15*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
16*d9f75844SAndroid Build Coastguard Worker #include <utility>
17*d9f75844SAndroid Build Coastguard Worker
18*d9f75844SAndroid Build Coastguard Worker #include "api/units/data_rate.h"
19*d9f75844SAndroid Build Coastguard Worker #include "api/units/data_size.h"
20*d9f75844SAndroid Build Coastguard Worker #include "api/units/time_delta.h"
21*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
22*d9f75844SAndroid Build Coastguard Worker
23*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
24*d9f75844SAndroid Build Coastguard Worker namespace {
25*d9f75844SAndroid Build Coastguard Worker
26*d9f75844SAndroid Build Coastguard Worker // Calculate the time (in microseconds) that takes to send N `bits` on a
27*d9f75844SAndroid Build Coastguard Worker // network with link capacity equal to `capacity_kbps` starting at time
28*d9f75844SAndroid Build Coastguard Worker // `start_time_us`.
CalculateArrivalTimeUs(int64_t start_time_us,int64_t bits,int capacity_kbps)29*d9f75844SAndroid Build Coastguard Worker int64_t CalculateArrivalTimeUs(int64_t start_time_us,
30*d9f75844SAndroid Build Coastguard Worker int64_t bits,
31*d9f75844SAndroid Build Coastguard Worker int capacity_kbps) {
32*d9f75844SAndroid Build Coastguard Worker // If capacity is 0, the link capacity is assumed to be infinite.
33*d9f75844SAndroid Build Coastguard Worker if (capacity_kbps == 0) {
34*d9f75844SAndroid Build Coastguard Worker return start_time_us;
35*d9f75844SAndroid Build Coastguard Worker }
36*d9f75844SAndroid Build Coastguard Worker // Adding `capacity - 1` to the numerator rounds the extra delay caused by
37*d9f75844SAndroid Build Coastguard Worker // capacity constraints up to an integral microsecond. Sending 0 bits takes 0
38*d9f75844SAndroid Build Coastguard Worker // extra time, while sending 1 bit gets rounded up to 1 (the multiplication by
39*d9f75844SAndroid Build Coastguard Worker // 1000 is because capacity is in kbps).
40*d9f75844SAndroid Build Coastguard Worker // The factor 1000 comes from 10^6 / 10^3, where 10^6 is due to the time unit
41*d9f75844SAndroid Build Coastguard Worker // being us and 10^3 is due to the rate unit being kbps.
42*d9f75844SAndroid Build Coastguard Worker return start_time_us + ((1000 * bits + capacity_kbps - 1) / capacity_kbps);
43*d9f75844SAndroid Build Coastguard Worker }
44*d9f75844SAndroid Build Coastguard Worker
45*d9f75844SAndroid Build Coastguard Worker } // namespace
46*d9f75844SAndroid Build Coastguard Worker
SimulatedNetwork(Config config,uint64_t random_seed)47*d9f75844SAndroid Build Coastguard Worker SimulatedNetwork::SimulatedNetwork(Config config, uint64_t random_seed)
48*d9f75844SAndroid Build Coastguard Worker : random_(random_seed),
49*d9f75844SAndroid Build Coastguard Worker bursting_(false),
50*d9f75844SAndroid Build Coastguard Worker last_enqueue_time_us_(0),
51*d9f75844SAndroid Build Coastguard Worker last_capacity_link_exit_time_(0) {
52*d9f75844SAndroid Build Coastguard Worker SetConfig(config);
53*d9f75844SAndroid Build Coastguard Worker }
54*d9f75844SAndroid Build Coastguard Worker
55*d9f75844SAndroid Build Coastguard Worker SimulatedNetwork::~SimulatedNetwork() = default;
56*d9f75844SAndroid Build Coastguard Worker
SetConfig(const Config & config)57*d9f75844SAndroid Build Coastguard Worker void SimulatedNetwork::SetConfig(const Config& config) {
58*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&config_lock_);
59*d9f75844SAndroid Build Coastguard Worker config_state_.config = config; // Shallow copy of the struct.
60*d9f75844SAndroid Build Coastguard Worker double prob_loss = config.loss_percent / 100.0;
61*d9f75844SAndroid Build Coastguard Worker if (config_state_.config.avg_burst_loss_length == -1) {
62*d9f75844SAndroid Build Coastguard Worker // Uniform loss
63*d9f75844SAndroid Build Coastguard Worker config_state_.prob_loss_bursting = prob_loss;
64*d9f75844SAndroid Build Coastguard Worker config_state_.prob_start_bursting = prob_loss;
65*d9f75844SAndroid Build Coastguard Worker } else {
66*d9f75844SAndroid Build Coastguard Worker // Lose packets according to a gilbert-elliot model.
67*d9f75844SAndroid Build Coastguard Worker int avg_burst_loss_length = config.avg_burst_loss_length;
68*d9f75844SAndroid Build Coastguard Worker int min_avg_burst_loss_length = std::ceil(prob_loss / (1 - prob_loss));
69*d9f75844SAndroid Build Coastguard Worker
70*d9f75844SAndroid Build Coastguard Worker RTC_CHECK_GT(avg_burst_loss_length, min_avg_burst_loss_length)
71*d9f75844SAndroid Build Coastguard Worker << "For a total packet loss of " << config.loss_percent
72*d9f75844SAndroid Build Coastguard Worker << "%% then"
73*d9f75844SAndroid Build Coastguard Worker " avg_burst_loss_length must be "
74*d9f75844SAndroid Build Coastguard Worker << min_avg_burst_loss_length + 1 << " or higher.";
75*d9f75844SAndroid Build Coastguard Worker
76*d9f75844SAndroid Build Coastguard Worker config_state_.prob_loss_bursting = (1.0 - 1.0 / avg_burst_loss_length);
77*d9f75844SAndroid Build Coastguard Worker config_state_.prob_start_bursting =
78*d9f75844SAndroid Build Coastguard Worker prob_loss / (1 - prob_loss) / avg_burst_loss_length;
79*d9f75844SAndroid Build Coastguard Worker }
80*d9f75844SAndroid Build Coastguard Worker }
81*d9f75844SAndroid Build Coastguard Worker
UpdateConfig(std::function<void (BuiltInNetworkBehaviorConfig *)> config_modifier)82*d9f75844SAndroid Build Coastguard Worker void SimulatedNetwork::UpdateConfig(
83*d9f75844SAndroid Build Coastguard Worker std::function<void(BuiltInNetworkBehaviorConfig*)> config_modifier) {
84*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&config_lock_);
85*d9f75844SAndroid Build Coastguard Worker config_modifier(&config_state_.config);
86*d9f75844SAndroid Build Coastguard Worker }
87*d9f75844SAndroid Build Coastguard Worker
PauseTransmissionUntil(int64_t until_us)88*d9f75844SAndroid Build Coastguard Worker void SimulatedNetwork::PauseTransmissionUntil(int64_t until_us) {
89*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&config_lock_);
90*d9f75844SAndroid Build Coastguard Worker config_state_.pause_transmission_until_us = until_us;
91*d9f75844SAndroid Build Coastguard Worker }
92*d9f75844SAndroid Build Coastguard Worker
EnqueuePacket(PacketInFlightInfo packet)93*d9f75844SAndroid Build Coastguard Worker bool SimulatedNetwork::EnqueuePacket(PacketInFlightInfo packet) {
94*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
95*d9f75844SAndroid Build Coastguard Worker
96*d9f75844SAndroid Build Coastguard Worker // Check that old packets don't get enqueued, the SimulatedNetwork expect that
97*d9f75844SAndroid Build Coastguard Worker // the packets' send time is monotonically increasing. The tolerance for
98*d9f75844SAndroid Build Coastguard Worker // non-monotonic enqueue events is 0.5 ms because on multi core systems
99*d9f75844SAndroid Build Coastguard Worker // clock_gettime(CLOCK_MONOTONIC) can show non-monotonic behaviour between
100*d9f75844SAndroid Build Coastguard Worker // theads running on different cores.
101*d9f75844SAndroid Build Coastguard Worker // TODO(bugs.webrtc.org/14525): Open a bug on this with the goal to re-enable
102*d9f75844SAndroid Build Coastguard Worker // the DCHECK.
103*d9f75844SAndroid Build Coastguard Worker // At the moment, we see more than 130ms between non-monotonic events, which
104*d9f75844SAndroid Build Coastguard Worker // is more than expected.
105*d9f75844SAndroid Build Coastguard Worker // RTC_DCHECK_GE(packet.send_time_us - last_enqueue_time_us_, -2000);
106*d9f75844SAndroid Build Coastguard Worker
107*d9f75844SAndroid Build Coastguard Worker ConfigState state = GetConfigState();
108*d9f75844SAndroid Build Coastguard Worker
109*d9f75844SAndroid Build Coastguard Worker // If the network config requires packet overhead, let's apply it as early as
110*d9f75844SAndroid Build Coastguard Worker // possible.
111*d9f75844SAndroid Build Coastguard Worker packet.size += state.config.packet_overhead;
112*d9f75844SAndroid Build Coastguard Worker
113*d9f75844SAndroid Build Coastguard Worker // If `queue_length_packets` is 0, the queue size is infinite.
114*d9f75844SAndroid Build Coastguard Worker if (state.config.queue_length_packets > 0 &&
115*d9f75844SAndroid Build Coastguard Worker capacity_link_.size() >= state.config.queue_length_packets) {
116*d9f75844SAndroid Build Coastguard Worker // Too many packet on the link, drop this one.
117*d9f75844SAndroid Build Coastguard Worker return false;
118*d9f75844SAndroid Build Coastguard Worker }
119*d9f75844SAndroid Build Coastguard Worker
120*d9f75844SAndroid Build Coastguard Worker // If the packet has been sent before the previous packet in the network left
121*d9f75844SAndroid Build Coastguard Worker // the capacity queue, let's ensure the new packet will start its trip in the
122*d9f75844SAndroid Build Coastguard Worker // network after the last bit of the previous packet has left it.
123*d9f75844SAndroid Build Coastguard Worker int64_t packet_send_time_us = packet.send_time_us;
124*d9f75844SAndroid Build Coastguard Worker if (!capacity_link_.empty()) {
125*d9f75844SAndroid Build Coastguard Worker packet_send_time_us =
126*d9f75844SAndroid Build Coastguard Worker std::max(packet_send_time_us, capacity_link_.back().arrival_time_us);
127*d9f75844SAndroid Build Coastguard Worker }
128*d9f75844SAndroid Build Coastguard Worker capacity_link_.push({.packet = packet,
129*d9f75844SAndroid Build Coastguard Worker .arrival_time_us = CalculateArrivalTimeUs(
130*d9f75844SAndroid Build Coastguard Worker packet_send_time_us, packet.size * 8,
131*d9f75844SAndroid Build Coastguard Worker state.config.link_capacity_kbps)});
132*d9f75844SAndroid Build Coastguard Worker
133*d9f75844SAndroid Build Coastguard Worker // Only update `next_process_time_us_` if not already set (if set, there is no
134*d9f75844SAndroid Build Coastguard Worker // way that a new packet will make the `next_process_time_us_` change).
135*d9f75844SAndroid Build Coastguard Worker if (!next_process_time_us_) {
136*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_EQ(capacity_link_.size(), 1);
137*d9f75844SAndroid Build Coastguard Worker next_process_time_us_ = capacity_link_.front().arrival_time_us;
138*d9f75844SAndroid Build Coastguard Worker }
139*d9f75844SAndroid Build Coastguard Worker
140*d9f75844SAndroid Build Coastguard Worker last_enqueue_time_us_ = packet.send_time_us;
141*d9f75844SAndroid Build Coastguard Worker return true;
142*d9f75844SAndroid Build Coastguard Worker }
143*d9f75844SAndroid Build Coastguard Worker
NextDeliveryTimeUs() const144*d9f75844SAndroid Build Coastguard Worker absl::optional<int64_t> SimulatedNetwork::NextDeliveryTimeUs() const {
145*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
146*d9f75844SAndroid Build Coastguard Worker return next_process_time_us_;
147*d9f75844SAndroid Build Coastguard Worker }
148*d9f75844SAndroid Build Coastguard Worker
UpdateCapacityQueue(ConfigState state,int64_t time_now_us)149*d9f75844SAndroid Build Coastguard Worker void SimulatedNetwork::UpdateCapacityQueue(ConfigState state,
150*d9f75844SAndroid Build Coastguard Worker int64_t time_now_us) {
151*d9f75844SAndroid Build Coastguard Worker // If there is at least one packet in the `capacity_link_`, let's update its
152*d9f75844SAndroid Build Coastguard Worker // arrival time to take into account changes in the network configuration
153*d9f75844SAndroid Build Coastguard Worker // since the last call to UpdateCapacityQueue.
154*d9f75844SAndroid Build Coastguard Worker if (!capacity_link_.empty()) {
155*d9f75844SAndroid Build Coastguard Worker capacity_link_.front().arrival_time_us = CalculateArrivalTimeUs(
156*d9f75844SAndroid Build Coastguard Worker std::max(capacity_link_.front().packet.send_time_us,
157*d9f75844SAndroid Build Coastguard Worker last_capacity_link_exit_time_),
158*d9f75844SAndroid Build Coastguard Worker capacity_link_.front().packet.size * 8,
159*d9f75844SAndroid Build Coastguard Worker state.config.link_capacity_kbps);
160*d9f75844SAndroid Build Coastguard Worker }
161*d9f75844SAndroid Build Coastguard Worker
162*d9f75844SAndroid Build Coastguard Worker // The capacity link is empty or the first packet is not expected to exit yet.
163*d9f75844SAndroid Build Coastguard Worker if (capacity_link_.empty() ||
164*d9f75844SAndroid Build Coastguard Worker time_now_us < capacity_link_.front().arrival_time_us) {
165*d9f75844SAndroid Build Coastguard Worker return;
166*d9f75844SAndroid Build Coastguard Worker }
167*d9f75844SAndroid Build Coastguard Worker bool reorder_packets = false;
168*d9f75844SAndroid Build Coastguard Worker
169*d9f75844SAndroid Build Coastguard Worker do {
170*d9f75844SAndroid Build Coastguard Worker // Time to get this packet (the original or just updated arrival_time_us is
171*d9f75844SAndroid Build Coastguard Worker // smaller or equal to time_now_us).
172*d9f75844SAndroid Build Coastguard Worker PacketInfo packet = capacity_link_.front();
173*d9f75844SAndroid Build Coastguard Worker capacity_link_.pop();
174*d9f75844SAndroid Build Coastguard Worker
175*d9f75844SAndroid Build Coastguard Worker // If the network is paused, the pause will be implemented as an extra delay
176*d9f75844SAndroid Build Coastguard Worker // to be spent in the `delay_link_` queue.
177*d9f75844SAndroid Build Coastguard Worker if (state.pause_transmission_until_us > packet.arrival_time_us) {
178*d9f75844SAndroid Build Coastguard Worker packet.arrival_time_us = state.pause_transmission_until_us;
179*d9f75844SAndroid Build Coastguard Worker }
180*d9f75844SAndroid Build Coastguard Worker
181*d9f75844SAndroid Build Coastguard Worker // Store the original arrival time, before applying packet loss or extra
182*d9f75844SAndroid Build Coastguard Worker // delay. This is needed to know when it is the first available time the
183*d9f75844SAndroid Build Coastguard Worker // next packet in the `capacity_link_` queue can start transmitting.
184*d9f75844SAndroid Build Coastguard Worker last_capacity_link_exit_time_ = packet.arrival_time_us;
185*d9f75844SAndroid Build Coastguard Worker
186*d9f75844SAndroid Build Coastguard Worker // Drop packets at an average rate of `state.config.loss_percent` with
187*d9f75844SAndroid Build Coastguard Worker // and average loss burst length of `state.config.avg_burst_loss_length`.
188*d9f75844SAndroid Build Coastguard Worker if ((bursting_ && random_.Rand<double>() < state.prob_loss_bursting) ||
189*d9f75844SAndroid Build Coastguard Worker (!bursting_ && random_.Rand<double>() < state.prob_start_bursting)) {
190*d9f75844SAndroid Build Coastguard Worker bursting_ = true;
191*d9f75844SAndroid Build Coastguard Worker packet.arrival_time_us = PacketDeliveryInfo::kNotReceived;
192*d9f75844SAndroid Build Coastguard Worker } else {
193*d9f75844SAndroid Build Coastguard Worker // If packets are not dropped, apply extra delay as configured.
194*d9f75844SAndroid Build Coastguard Worker bursting_ = false;
195*d9f75844SAndroid Build Coastguard Worker int64_t arrival_time_jitter_us = std::max(
196*d9f75844SAndroid Build Coastguard Worker random_.Gaussian(state.config.queue_delay_ms * 1000,
197*d9f75844SAndroid Build Coastguard Worker state.config.delay_standard_deviation_ms * 1000),
198*d9f75844SAndroid Build Coastguard Worker 0.0);
199*d9f75844SAndroid Build Coastguard Worker
200*d9f75844SAndroid Build Coastguard Worker // If reordering is not allowed then adjust arrival_time_jitter
201*d9f75844SAndroid Build Coastguard Worker // to make sure all packets are sent in order.
202*d9f75844SAndroid Build Coastguard Worker int64_t last_arrival_time_us =
203*d9f75844SAndroid Build Coastguard Worker delay_link_.empty() ? -1 : delay_link_.back().arrival_time_us;
204*d9f75844SAndroid Build Coastguard Worker if (!state.config.allow_reordering && !delay_link_.empty() &&
205*d9f75844SAndroid Build Coastguard Worker packet.arrival_time_us + arrival_time_jitter_us <
206*d9f75844SAndroid Build Coastguard Worker last_arrival_time_us) {
207*d9f75844SAndroid Build Coastguard Worker arrival_time_jitter_us = last_arrival_time_us - packet.arrival_time_us;
208*d9f75844SAndroid Build Coastguard Worker }
209*d9f75844SAndroid Build Coastguard Worker packet.arrival_time_us += arrival_time_jitter_us;
210*d9f75844SAndroid Build Coastguard Worker
211*d9f75844SAndroid Build Coastguard Worker // Optimization: Schedule a reorder only when a packet will exit before
212*d9f75844SAndroid Build Coastguard Worker // the one in front.
213*d9f75844SAndroid Build Coastguard Worker if (last_arrival_time_us > packet.arrival_time_us) {
214*d9f75844SAndroid Build Coastguard Worker reorder_packets = true;
215*d9f75844SAndroid Build Coastguard Worker }
216*d9f75844SAndroid Build Coastguard Worker }
217*d9f75844SAndroid Build Coastguard Worker delay_link_.emplace_back(packet);
218*d9f75844SAndroid Build Coastguard Worker
219*d9f75844SAndroid Build Coastguard Worker // If there are no packets in the queue, there is nothing else to do.
220*d9f75844SAndroid Build Coastguard Worker if (capacity_link_.empty()) {
221*d9f75844SAndroid Build Coastguard Worker break;
222*d9f75844SAndroid Build Coastguard Worker }
223*d9f75844SAndroid Build Coastguard Worker // If instead there is another packet in the `capacity_link_` queue, let's
224*d9f75844SAndroid Build Coastguard Worker // calculate its arrival_time_us based on the latest config (which might
225*d9f75844SAndroid Build Coastguard Worker // have been changed since it was enqueued).
226*d9f75844SAndroid Build Coastguard Worker int64_t next_start = std::max(last_capacity_link_exit_time_,
227*d9f75844SAndroid Build Coastguard Worker capacity_link_.front().packet.send_time_us);
228*d9f75844SAndroid Build Coastguard Worker capacity_link_.front().arrival_time_us = CalculateArrivalTimeUs(
229*d9f75844SAndroid Build Coastguard Worker next_start, capacity_link_.front().packet.size * 8,
230*d9f75844SAndroid Build Coastguard Worker state.config.link_capacity_kbps);
231*d9f75844SAndroid Build Coastguard Worker // And if the next packet in the queue needs to exit, let's dequeue it.
232*d9f75844SAndroid Build Coastguard Worker } while (capacity_link_.front().arrival_time_us <= time_now_us);
233*d9f75844SAndroid Build Coastguard Worker
234*d9f75844SAndroid Build Coastguard Worker if (state.config.allow_reordering && reorder_packets) {
235*d9f75844SAndroid Build Coastguard Worker // Packets arrived out of order and since the network config allows
236*d9f75844SAndroid Build Coastguard Worker // reordering, let's sort them per arrival_time_us to make so they will also
237*d9f75844SAndroid Build Coastguard Worker // be delivered out of order.
238*d9f75844SAndroid Build Coastguard Worker std::stable_sort(delay_link_.begin(), delay_link_.end(),
239*d9f75844SAndroid Build Coastguard Worker [](const PacketInfo& p1, const PacketInfo& p2) {
240*d9f75844SAndroid Build Coastguard Worker return p1.arrival_time_us < p2.arrival_time_us;
241*d9f75844SAndroid Build Coastguard Worker });
242*d9f75844SAndroid Build Coastguard Worker }
243*d9f75844SAndroid Build Coastguard Worker }
244*d9f75844SAndroid Build Coastguard Worker
GetConfigState() const245*d9f75844SAndroid Build Coastguard Worker SimulatedNetwork::ConfigState SimulatedNetwork::GetConfigState() const {
246*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&config_lock_);
247*d9f75844SAndroid Build Coastguard Worker return config_state_;
248*d9f75844SAndroid Build Coastguard Worker }
249*d9f75844SAndroid Build Coastguard Worker
DequeueDeliverablePackets(int64_t receive_time_us)250*d9f75844SAndroid Build Coastguard Worker std::vector<PacketDeliveryInfo> SimulatedNetwork::DequeueDeliverablePackets(
251*d9f75844SAndroid Build Coastguard Worker int64_t receive_time_us) {
252*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_RUNS_SERIALIZED(&process_checker_);
253*d9f75844SAndroid Build Coastguard Worker
254*d9f75844SAndroid Build Coastguard Worker UpdateCapacityQueue(GetConfigState(), receive_time_us);
255*d9f75844SAndroid Build Coastguard Worker std::vector<PacketDeliveryInfo> packets_to_deliver;
256*d9f75844SAndroid Build Coastguard Worker
257*d9f75844SAndroid Build Coastguard Worker // Check the extra delay queue.
258*d9f75844SAndroid Build Coastguard Worker while (!delay_link_.empty() &&
259*d9f75844SAndroid Build Coastguard Worker receive_time_us >= delay_link_.front().arrival_time_us) {
260*d9f75844SAndroid Build Coastguard Worker PacketInfo packet_info = delay_link_.front();
261*d9f75844SAndroid Build Coastguard Worker packets_to_deliver.emplace_back(
262*d9f75844SAndroid Build Coastguard Worker PacketDeliveryInfo(packet_info.packet, packet_info.arrival_time_us));
263*d9f75844SAndroid Build Coastguard Worker delay_link_.pop_front();
264*d9f75844SAndroid Build Coastguard Worker }
265*d9f75844SAndroid Build Coastguard Worker
266*d9f75844SAndroid Build Coastguard Worker if (!delay_link_.empty()) {
267*d9f75844SAndroid Build Coastguard Worker next_process_time_us_ = delay_link_.front().arrival_time_us;
268*d9f75844SAndroid Build Coastguard Worker } else if (!capacity_link_.empty()) {
269*d9f75844SAndroid Build Coastguard Worker next_process_time_us_ = capacity_link_.front().arrival_time_us;
270*d9f75844SAndroid Build Coastguard Worker } else {
271*d9f75844SAndroid Build Coastguard Worker next_process_time_us_.reset();
272*d9f75844SAndroid Build Coastguard Worker }
273*d9f75844SAndroid Build Coastguard Worker return packets_to_deliver;
274*d9f75844SAndroid Build Coastguard Worker }
275*d9f75844SAndroid Build Coastguard Worker
276*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
277