• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/bandwidth_estimator.h"
6 
7 #include <algorithm>
8 
9 #include "util/osp_logging.h"
10 #include "util/saturate_cast.h"
11 
12 namespace openscreen {
13 namespace cast {
14 
15 using openscreen::operator<<;  // For std::chrono::duration logging.
16 
17 namespace {
18 
19 // Converts units from |bytes| per |time_window| number of Clock ticks into
20 // bits-per-second.
ToClampedBitsPerSecond(int32_t bytes,Clock::duration time_window)21 int ToClampedBitsPerSecond(int32_t bytes, Clock::duration time_window) {
22   OSP_DCHECK_GT(time_window, Clock::duration::zero());
23 
24   // Divide |bytes| by |time_window| and scale the units to bits per second.
25   constexpr int64_t kBitsPerByte = 8;
26   constexpr int64_t kClockTicksPerSecond =
27       Clock::to_duration(std::chrono::seconds(1)).count();
28   const int64_t bits = bytes * kBitsPerByte;
29   const int64_t bits_per_second =
30       (bits * kClockTicksPerSecond) / time_window.count();
31   return saturate_cast<int>(bits_per_second);
32 }
33 
34 }  // namespace
35 
BandwidthEstimator(int max_packets_per_timeslice,Clock::duration timeslice_duration,Clock::time_point start_time)36 BandwidthEstimator::BandwidthEstimator(int max_packets_per_timeslice,
37                                        Clock::duration timeslice_duration,
38                                        Clock::time_point start_time)
39     : max_packets_per_history_window_(max_packets_per_timeslice *
40                                       kNumTimeslices),
41       history_window_(timeslice_duration * kNumTimeslices),
42       burst_history_(timeslice_duration, start_time),
43       feedback_history_(timeslice_duration, start_time) {
44   OSP_DCHECK_GT(max_packets_per_timeslice, 0);
45   OSP_DCHECK_GT(timeslice_duration, Clock::duration::zero());
46 }
47 
48 BandwidthEstimator::~BandwidthEstimator() = default;
49 
OnBurstComplete(int num_packets_sent,Clock::time_point when)50 void BandwidthEstimator::OnBurstComplete(int num_packets_sent,
51                                          Clock::time_point when) {
52   OSP_DCHECK_GE(num_packets_sent, 0);
53   burst_history_.Accumulate(num_packets_sent, when);
54 }
55 
OnRtcpReceived(Clock::time_point arrival_time,Clock::duration estimated_round_trip_time)56 void BandwidthEstimator::OnRtcpReceived(
57     Clock::time_point arrival_time,
58     Clock::duration estimated_round_trip_time) {
59   OSP_DCHECK_GE(estimated_round_trip_time, Clock::duration::zero());
60   // Move forward the feedback history tracking timeline to include the latest
61   // moment a packet could have left the Sender.
62   feedback_history_.AdvanceToIncludeTime(arrival_time -
63                                          estimated_round_trip_time);
64 }
65 
OnPayloadReceived(int payload_bytes_acknowledged,Clock::time_point ack_arrival_time,Clock::duration estimated_round_trip_time)66 void BandwidthEstimator::OnPayloadReceived(
67     int payload_bytes_acknowledged,
68     Clock::time_point ack_arrival_time,
69     Clock::duration estimated_round_trip_time) {
70   OSP_DCHECK_GE(payload_bytes_acknowledged, 0);
71   OSP_DCHECK_GE(estimated_round_trip_time, Clock::duration::zero());
72   // Track the bytes in terms of when the last packet was sent.
73   feedback_history_.Accumulate(payload_bytes_acknowledged,
74                                ack_arrival_time - estimated_round_trip_time);
75 }
76 
ComputeNetworkBandwidth() const77 int BandwidthEstimator::ComputeNetworkBandwidth() const {
78   // Determine whether the |burst_history_| time window overlaps with the
79   // |feedback_history_| time window by at least half. The time windows don't
80   // have to overlap entirely because the calculations are averaging all the
81   // measurements (i.e., recent typical behavior). Though, they should overlap
82   // by "enough" so that the measurements correlate "enough."
83   const Clock::time_point overlap_begin =
84       std::max(burst_history_.begin_time(), feedback_history_.begin_time());
85   const Clock::time_point overlap_end =
86       std::min(burst_history_.end_time(), feedback_history_.end_time());
87   if ((overlap_end - overlap_begin) < (history_window_ / 2)) {
88     return 0;
89   }
90 
91   const int32_t num_packets_transmitted = burst_history_.Sum();
92   if (num_packets_transmitted <= 0) {
93     // Cannot estimate because there have been no transmissions recently.
94     return 0;
95   }
96   const Clock::duration transmit_duration = history_window_ *
97                                             num_packets_transmitted /
98                                             max_packets_per_history_window_;
99   const int32_t num_bytes_received = feedback_history_.Sum();
100   return ToClampedBitsPerSecond(num_bytes_received, transmit_duration);
101 }
102 
103 // static
104 constexpr int BandwidthEstimator::kNumTimeslices;
105 
FlowTracker(Clock::duration timeslice_duration,Clock::time_point begin_time)106 BandwidthEstimator::FlowTracker::FlowTracker(Clock::duration timeslice_duration,
107                                              Clock::time_point begin_time)
108     : timeslice_duration_(timeslice_duration), begin_time_(begin_time) {}
109 
110 BandwidthEstimator::FlowTracker::~FlowTracker() = default;
111 
AdvanceToIncludeTime(Clock::time_point until)112 void BandwidthEstimator::FlowTracker::AdvanceToIncludeTime(
113     Clock::time_point until) {
114   if (until < end_time()) {
115     return;  // Not advancing.
116   }
117 
118   // Step forward in time, at timeslice granularity.
119   const int64_t num_periods = 1 + (until - end_time()) / timeslice_duration_;
120   begin_time_ += num_periods * timeslice_duration_;
121 
122   // Shift the ring elements, discarding N oldest timeslices, and creating N new
123   // ones initialized to zero.
124   const int shift_count = std::min<int64_t>(num_periods, kNumTimeslices);
125   for (int i = 0; i < shift_count; ++i) {
126     history_ring_[tail_++] = 0;
127   }
128 }
129 
Accumulate(int32_t amount,Clock::time_point when)130 void BandwidthEstimator::FlowTracker::Accumulate(int32_t amount,
131                                                  Clock::time_point when) {
132   if (when < begin_time_) {
133     return;  // Ignore a data point that is already too old.
134   }
135 
136   AdvanceToIncludeTime(when);
137 
138   // Because of the AdvanceToIncludeTime() call just made, the offset/index
139   // calculations here are guaranteed to point to a valid element in the
140   // |history_ring_|.
141   const int64_t offset_from_first = (when - begin_time_) / timeslice_duration_;
142   const index_mod_256_t ring_index = tail_ + offset_from_first;
143   int32_t& timeslice = history_ring_[ring_index];
144   timeslice = saturate_cast<int32_t>(int64_t{timeslice} + amount);
145 }
146 
Sum() const147 int32_t BandwidthEstimator::FlowTracker::Sum() const {
148   int64_t result = 0;
149   for (int32_t amount : history_ring_) {
150     result += amount;
151   }
152   return saturate_cast<int32_t>(result);
153 }
154 
155 }  // namespace cast
156 }  // namespace openscreen
157