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