This class measures the allocated cumulative byte budget (as specified by one or more rate updates) and the actual cumulative number of bytes produced over a sliding window. A utilization factor (produced bytes / budgeted bytes) is calculated seen from the first data point timestamp until the last data point timestamp plus the amount time needed to send that last data point given no further updates to the rate. Wireup to EncoderBitrateAdjuster will happen in a follow-up CL. Bug: b/349561566 Change-Id: Id0dc183b07a96366531007be9ff1c1ec6574e9ff Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/356200 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Auto-Submit: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42591}
141 lines
5.1 KiB
C++
141 lines
5.1 KiB
C++
/*
|
|
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "video/rate_utilization_tracker.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace webrtc {
|
|
|
|
RateUtilizationTracker::RateUtilizationTracker(
|
|
size_t max_num_encoded_data_points,
|
|
TimeDelta max_duration)
|
|
: max_data_points_(max_num_encoded_data_points),
|
|
max_duration_(max_duration),
|
|
current_rate_(DataRate::Zero()) {
|
|
RTC_CHECK_GE(max_num_encoded_data_points, 0);
|
|
RTC_CHECK_GT(max_duration, TimeDelta::Zero());
|
|
}
|
|
|
|
void RateUtilizationTracker::OnDataRateChanged(DataRate rate, Timestamp time) {
|
|
current_rate_ = rate;
|
|
if (data_points_.empty()) {
|
|
// First entry should be contain first produced data, so just return after
|
|
// setting `current_rate_`.
|
|
return;
|
|
} else {
|
|
RateUsageUpdate& last_data_point = data_points_.back();
|
|
RTC_CHECK_GE(time, last_data_point.time);
|
|
if (last_data_point.time == time) {
|
|
last_data_point.target_rate = rate;
|
|
} else {
|
|
data_points_.push_back({.time = time,
|
|
.target_rate = rate,
|
|
.produced_data = DataSize::Zero()});
|
|
}
|
|
}
|
|
|
|
CullOldData(time);
|
|
}
|
|
|
|
void RateUtilizationTracker::OnDataProduced(DataSize size, Timestamp time) {
|
|
if (data_points_.empty()) {
|
|
data_points_.push_back(
|
|
{.time = time, .target_rate = current_rate_, .produced_data = size});
|
|
} else {
|
|
RateUsageUpdate& last_data_point = data_points_.back();
|
|
RTC_CHECK_GE(time, last_data_point.time);
|
|
if (last_data_point.time == time) {
|
|
last_data_point.produced_data += size;
|
|
} else {
|
|
data_points_.push_back(
|
|
{.time = time, .target_rate = current_rate_, .produced_data = size});
|
|
}
|
|
}
|
|
|
|
CullOldData(time);
|
|
}
|
|
|
|
absl::optional<double> RateUtilizationTracker::GetRateUtilizationFactor(
|
|
Timestamp time) const {
|
|
if (data_points_.empty()) {
|
|
return absl::nullopt;
|
|
}
|
|
|
|
RTC_CHECK_GE(time, data_points_.back().time);
|
|
DataSize allocated_send_data_size = DataSize::Zero();
|
|
DataSize total_produced_data = DataSize::Zero();
|
|
|
|
// Keep track of the last time data was produced - how much it was and how
|
|
// much rate budget has been allocated since then.
|
|
DataSize data_allocated_for_last_data = DataSize::Zero();
|
|
DataSize size_of_last_data = DataSize::Zero();
|
|
|
|
RTC_DCHECK(!data_points_.front().produced_data.IsZero());
|
|
for (size_t i = 0; i < data_points_.size(); ++i) {
|
|
const RateUsageUpdate& update = data_points_[i];
|
|
total_produced_data += update.produced_data;
|
|
|
|
DataSize allocated_since_previous_data_point =
|
|
i == 0 ? DataSize::Zero()
|
|
: (update.time - data_points_[i - 1].time) *
|
|
data_points_[i - 1].target_rate;
|
|
allocated_send_data_size += allocated_since_previous_data_point;
|
|
|
|
if (update.produced_data.IsZero()) {
|
|
// Just a rate update past the last seen produced data.
|
|
data_allocated_for_last_data += allocated_since_previous_data_point;
|
|
} else {
|
|
// A newer data point with produced data, reset accumulator for rate
|
|
// allocated past the last data point.
|
|
size_of_last_data = update.produced_data;
|
|
data_allocated_for_last_data = DataSize::Zero();
|
|
}
|
|
}
|
|
|
|
if (allocated_send_data_size.IsZero() && current_rate_.IsZero()) {
|
|
// No allocated rate across all of the data points, ignore.
|
|
return absl::nullopt;
|
|
}
|
|
|
|
// Calculate the rate past the very last data point until the polling time.
|
|
const RateUsageUpdate& last_update = data_points_.back();
|
|
DataSize allocated_since_last_data_point =
|
|
(time - last_update.time) * last_update.target_rate;
|
|
|
|
// If the last produced data packet is larger than the accumulated rate
|
|
// allocation window since then, use that data point size instead (minus any
|
|
// data rate accumulated in rate updates after that data point was produced).
|
|
allocated_send_data_size +=
|
|
std::max(allocated_since_last_data_point,
|
|
size_of_last_data - data_allocated_for_last_data);
|
|
|
|
return total_produced_data.bytes<double>() / allocated_send_data_size.bytes();
|
|
}
|
|
|
|
void RateUtilizationTracker::CullOldData(Timestamp time) {
|
|
// Remove data points that are either too old, exceed the limit of number of
|
|
// data points - and make sure the first entry in the list contains actual
|
|
// data produced since we calculate send usage since that time.
|
|
|
|
// We don't allow negative times so always start window at absolute time >= 0.
|
|
const Timestamp oldest_included_time =
|
|
time.ms() > max_duration_.ms() ? time - max_duration_ : Timestamp::Zero();
|
|
|
|
while (!data_points_.empty() &&
|
|
(data_points_.front().time < oldest_included_time ||
|
|
data_points_.size() > max_data_points_ ||
|
|
data_points_.front().produced_data.IsZero())) {
|
|
data_points_.pop_front();
|
|
}
|
|
}
|
|
|
|
} // namespace webrtc
|