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 #ifndef CAST_STREAMING_BANDWIDTH_ESTIMATOR_H_ 6*3f982cf4SFabien Sanglard #define CAST_STREAMING_BANDWIDTH_ESTIMATOR_H_ 7*3f982cf4SFabien Sanglard 8*3f982cf4SFabien Sanglard #include <stdint.h> 9*3f982cf4SFabien Sanglard 10*3f982cf4SFabien Sanglard #include <limits> 11*3f982cf4SFabien Sanglard 12*3f982cf4SFabien Sanglard #include "platform/api/time.h" 13*3f982cf4SFabien Sanglard 14*3f982cf4SFabien Sanglard namespace openscreen { 15*3f982cf4SFabien Sanglard namespace cast { 16*3f982cf4SFabien Sanglard 17*3f982cf4SFabien Sanglard // Tracks send attempts and successful receives, and then computes a total 18*3f982cf4SFabien Sanglard // network bandwith estimate. 19*3f982cf4SFabien Sanglard // 20*3f982cf4SFabien Sanglard // Two metrics are tracked by the BandwidthEstimator, over a "recent history" 21*3f982cf4SFabien Sanglard // time window: 22*3f982cf4SFabien Sanglard // 23*3f982cf4SFabien Sanglard // 1. The number of packets sent during bursts (see SenderPacketRouter for 24*3f982cf4SFabien Sanglard // explanation of what a "burst" is). These track when the network was 25*3f982cf4SFabien Sanglard // actually in-use for transmission and the magnitude of each burst. When 26*3f982cf4SFabien Sanglard // computing bandwidth, the estimator assumes the timeslices where the 27*3f982cf4SFabien Sanglard // network was not in-use could have been used to send even more bytes at 28*3f982cf4SFabien Sanglard // the same rate. 29*3f982cf4SFabien Sanglard // 30*3f982cf4SFabien Sanglard // 2. Successful receipt of payload bytes over time, or a lack thereof. 31*3f982cf4SFabien Sanglard // Packets that include acknowledgements from the Receivers are providing 32*3f982cf4SFabien Sanglard // proof of the successful receipt of payload bytes. All other packets 33*3f982cf4SFabien Sanglard // provide proof of network connectivity over time, and are used to 34*3f982cf4SFabien Sanglard // identify periods of time where nothing was received. 35*3f982cf4SFabien Sanglard // 36*3f982cf4SFabien Sanglard // The BandwidthEstimator assumes a simplified model for streaming over the 37*3f982cf4SFabien Sanglard // network. The model does not include any detailed knowledge about things like 38*3f982cf4SFabien Sanglard // protocol overhead, packet re-transmits, parasitic bufferring, network 39*3f982cf4SFabien Sanglard // reliability, etc. Instead, it automatically accounts for all such things by 40*3f982cf4SFabien Sanglard // looking at what's actually leaving the Senders and what's actually making it 41*3f982cf4SFabien Sanglard // to the Receivers. 42*3f982cf4SFabien Sanglard // 43*3f982cf4SFabien Sanglard // This simplified model does produce some known inaccuracies in the resulting 44*3f982cf4SFabien Sanglard // estimations. If no data has recently been transmitted (or been received), 45*3f982cf4SFabien Sanglard // estimations cannot be provided. If the transmission rate is near (or 46*3f982cf4SFabien Sanglard // exceeding) the network's capacity, the estimations will be very accurate. In 47*3f982cf4SFabien Sanglard // between those two extremes, the logic will tend to under-estimate the 48*3f982cf4SFabien Sanglard // network's capacity. However, those under-estimates will still be far larger 49*3f982cf4SFabien Sanglard // than the current transmission rate. 50*3f982cf4SFabien Sanglard // 51*3f982cf4SFabien Sanglard // Thus, these estimates can be used effectively as a control signal for 52*3f982cf4SFabien Sanglard // congestion control in upstream code modules. The logic computing the media's 53*3f982cf4SFabien Sanglard // encoding target bitrate should be adjusted in realtime using a TCP-like 54*3f982cf4SFabien Sanglard // congestion control algorithm: 55*3f982cf4SFabien Sanglard // 56*3f982cf4SFabien Sanglard // 1. When the estimated bitrate is less than the current encoding target 57*3f982cf4SFabien Sanglard // bitrate, aggressively and immediately decrease the encoding bitrate. 58*3f982cf4SFabien Sanglard // 59*3f982cf4SFabien Sanglard // 2. When the estimated bitrate is more than the current encoding target 60*3f982cf4SFabien Sanglard // bitrate, gradually increase the encoding bitrate (up to the maximum 61*3f982cf4SFabien Sanglard // that is reasonable for the application). 62*3f982cf4SFabien Sanglard class BandwidthEstimator { 63*3f982cf4SFabien Sanglard public: 64*3f982cf4SFabien Sanglard // |max_packets_per_timeslice| and |timeslice_duration| should match the burst 65*3f982cf4SFabien Sanglard // configuration in SenderPacketRouter. |start_time| should be a recent 66*3f982cf4SFabien Sanglard // point-in-time before the first packet is sent. 67*3f982cf4SFabien Sanglard BandwidthEstimator(int max_packets_per_timeslice, 68*3f982cf4SFabien Sanglard Clock::duration timeslice_duration, 69*3f982cf4SFabien Sanglard Clock::time_point start_time); 70*3f982cf4SFabien Sanglard 71*3f982cf4SFabien Sanglard ~BandwidthEstimator(); 72*3f982cf4SFabien Sanglard 73*3f982cf4SFabien Sanglard // Returns the duration of the fixed, recent-history time window over which 74*3f982cf4SFabien Sanglard // data flows are being tracked. history_window()75*3f982cf4SFabien Sanglard Clock::duration history_window() const { return history_window_; } 76*3f982cf4SFabien Sanglard 77*3f982cf4SFabien Sanglard // Records |when| burst-sending was active or inactive. For the active case, 78*3f982cf4SFabien Sanglard // |num_packets_sent| should include all network packets sent, including 79*3f982cf4SFabien Sanglard // non-payload packets (since both affect the modeled utilization/capacity). 80*3f982cf4SFabien Sanglard // For the inactive case, this method should be called with zero for 81*3f982cf4SFabien Sanglard // |num_packets_sent|. 82*3f982cf4SFabien Sanglard void OnBurstComplete(int num_packets_sent, Clock::time_point when); 83*3f982cf4SFabien Sanglard 84*3f982cf4SFabien Sanglard // Records when a RTCP packet was received. It's important for Senders to call 85*3f982cf4SFabien Sanglard // this any time a packet comes in from the Receivers, even if no payload is 86*3f982cf4SFabien Sanglard // being acknowledged, since the time windows of "nothing successfully 87*3f982cf4SFabien Sanglard // received" is also important information to track. 88*3f982cf4SFabien Sanglard void OnRtcpReceived(Clock::time_point arrival_time, 89*3f982cf4SFabien Sanglard Clock::duration estimated_round_trip_time); 90*3f982cf4SFabien Sanglard 91*3f982cf4SFabien Sanglard // Records that some number of payload bytes has been acknowledged (i.e., 92*3f982cf4SFabien Sanglard // successfully received). 93*3f982cf4SFabien Sanglard void OnPayloadReceived(int payload_bytes_acknowledged, 94*3f982cf4SFabien Sanglard Clock::time_point ack_arrival_time, 95*3f982cf4SFabien Sanglard Clock::duration estimated_round_trip_time); 96*3f982cf4SFabien Sanglard 97*3f982cf4SFabien Sanglard // Computes the current network bandwith estimate. Returns 0 if this cannot be 98*3f982cf4SFabien Sanglard // determined due to a lack of sufficiently-recent data. 99*3f982cf4SFabien Sanglard int ComputeNetworkBandwidth() const; 100*3f982cf4SFabien Sanglard 101*3f982cf4SFabien Sanglard private: 102*3f982cf4SFabien Sanglard // FlowTracker (below) manages a ring buffer of size 256. It simplifies the 103*3f982cf4SFabien Sanglard // index calculations to use an integer data type where all arithmetic is mod 104*3f982cf4SFabien Sanglard // 256. 105*3f982cf4SFabien Sanglard using index_mod_256_t = uint8_t; 106*3f982cf4SFabien Sanglard static constexpr int kNumTimeslices = 107*3f982cf4SFabien Sanglard static_cast<int>(std::numeric_limits<index_mod_256_t>::max()) + 1; 108*3f982cf4SFabien Sanglard 109*3f982cf4SFabien Sanglard // Tracks volume (e.g., the total number of payload bytes) over a fixed 110*3f982cf4SFabien Sanglard // recent-history time window. The time window is divided up into a number of 111*3f982cf4SFabien Sanglard // identical timeslices, each of which represents the total number of bytes 112*3f982cf4SFabien Sanglard // that flowed during a certain period of time. The data is accumulated in 113*3f982cf4SFabien Sanglard // ring buffer elements so that old data points drop-off as newer ones (that 114*3f982cf4SFabien Sanglard // move the history window forward) are added. 115*3f982cf4SFabien Sanglard class FlowTracker { 116*3f982cf4SFabien Sanglard public: 117*3f982cf4SFabien Sanglard FlowTracker(Clock::duration timeslice_duration, 118*3f982cf4SFabien Sanglard Clock::time_point begin_time); 119*3f982cf4SFabien Sanglard ~FlowTracker(); 120*3f982cf4SFabien Sanglard begin_time()121*3f982cf4SFabien Sanglard Clock::time_point begin_time() const { return begin_time_; } end_time()122*3f982cf4SFabien Sanglard Clock::time_point end_time() const { 123*3f982cf4SFabien Sanglard return begin_time_ + timeslice_duration_ * kNumTimeslices; 124*3f982cf4SFabien Sanglard } 125*3f982cf4SFabien Sanglard 126*3f982cf4SFabien Sanglard // Advance the end of the time window being tracked such that the 127*3f982cf4SFabien Sanglard // most-recent timeslice includes |until|. Too-old timeslices are dropped 128*3f982cf4SFabien Sanglard // and new ones are initialized to a zero amount. 129*3f982cf4SFabien Sanglard void AdvanceToIncludeTime(Clock::time_point until); 130*3f982cf4SFabien Sanglard 131*3f982cf4SFabien Sanglard // Accumulate the given |amount| into the timeslice that includes |when|. 132*3f982cf4SFabien Sanglard void Accumulate(int32_t amount, Clock::time_point when); 133*3f982cf4SFabien Sanglard 134*3f982cf4SFabien Sanglard // Return the sum of all the amounts in recent history. This clamps to the 135*3f982cf4SFabien Sanglard // valid range of int32_t, if necessary. 136*3f982cf4SFabien Sanglard int32_t Sum() const; 137*3f982cf4SFabien Sanglard 138*3f982cf4SFabien Sanglard private: 139*3f982cf4SFabien Sanglard const Clock::duration timeslice_duration_; 140*3f982cf4SFabien Sanglard 141*3f982cf4SFabien Sanglard // The beginning of the oldest timeslice in the recent-history time window, 142*3f982cf4SFabien Sanglard // the one pointed to by |tail_|. 143*3f982cf4SFabien Sanglard Clock::time_point begin_time_; 144*3f982cf4SFabien Sanglard 145*3f982cf4SFabien Sanglard // A ring buffer tracking the accumulated amount for each timeslice. 146*3f982cf4SFabien Sanglard int32_t history_ring_[kNumTimeslices]{}; 147*3f982cf4SFabien Sanglard 148*3f982cf4SFabien Sanglard // The index of the oldest timeslice in the |history_ring_|. This can also 149*3f982cf4SFabien Sanglard // be thought of, equivalently, as the index just after the most-recent 150*3f982cf4SFabien Sanglard // timeslice. 151*3f982cf4SFabien Sanglard index_mod_256_t tail_ = 0; 152*3f982cf4SFabien Sanglard }; 153*3f982cf4SFabien Sanglard 154*3f982cf4SFabien Sanglard // The maximum number of packet sends that could possibly be attempted during 155*3f982cf4SFabien Sanglard // the recent-history time window. 156*3f982cf4SFabien Sanglard const int max_packets_per_history_window_; 157*3f982cf4SFabien Sanglard 158*3f982cf4SFabien Sanglard // The range of time being tracked. 159*3f982cf4SFabien Sanglard const Clock::duration history_window_; 160*3f982cf4SFabien Sanglard 161*3f982cf4SFabien Sanglard // History tracking for send attempts, and success feeback. These timeseries 162*3f982cf4SFabien Sanglard // are in terms of when packets have left the Senders. 163*3f982cf4SFabien Sanglard FlowTracker burst_history_; 164*3f982cf4SFabien Sanglard FlowTracker feedback_history_; 165*3f982cf4SFabien Sanglard }; 166*3f982cf4SFabien Sanglard 167*3f982cf4SFabien Sanglard } // namespace cast 168*3f982cf4SFabien Sanglard } // namespace openscreen 169*3f982cf4SFabien Sanglard 170*3f982cf4SFabien Sanglard #endif // CAST_STREAMING_BANDWIDTH_ESTIMATOR_H_ 171