blob: 0326a4430ff2c25254e68dd56721f32bceb2d0a6 [file] [log] [blame] [edit]
// Copyright 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/moqt/moqt_bitrate_adjuster.h"
#include <algorithm>
#include <cstdint>
#include "quiche/quic/core/quic_bandwidth.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/web_transport/web_transport.h"
namespace moqt {
namespace {
using ::quic::QuicBandwidth;
using ::quic::QuicTime;
using ::quic::QuicTimeDelta;
// Whenever adjusting bitrate down, it is set to `kTargetBitrateMultiplier *
// bw`, where `bw` is typically windowed max bandwidth reported by BBR. The
// current value selected is a bit arbitrary; ideally, we would adjust down to
// the application data goodput (i.e. goodput excluding all of the framing
// overhead), but that would either require us knowing how to compute the
// framing overhead correctly, or implementing our own application-level goodput
// monitoring.
constexpr float kTargetBitrateMultiplier = 0.9f;
// Avoid re-adjusting bitrate within N RTTs after adjusting it. Here, on a
// typical 20ms connection, 40 RTTs is 800ms. Cap the limit at 3000ms.
constexpr float kMinTimeBetweenAdjustmentsInRtts = 40;
constexpr QuicTimeDelta kMaxTimeBetweenAdjustments =
QuicTimeDelta::FromSeconds(3);
} // namespace
void MoqtBitrateAdjuster::OnObjectAckReceived(
uint64_t /*group_id*/, uint64_t /*object_id*/,
QuicTimeDelta delta_from_deadline) {
if (delta_from_deadline < QuicTimeDelta::Zero()) {
// While adjusting down upon the first sign of packets getting late might
// seem aggressive, note that:
// - By the time user occurs, this is already a user-visible issue (so, in
// some sense, this isn't aggressive enough).
// - The adjustment won't happen if we're already bellow `k * max_bw`, so
// if the delays are due to other factors like bufferbloat, the measured
// bandwidth will likely not result in a downwards adjustment.
AttemptAdjustingDown();
}
}
void MoqtBitrateAdjuster::AttemptAdjustingDown() {
webtransport::SessionStats stats = session_->GetSessionStats();
// Wait for a while after doing an adjustment. There are non-trivial costs to
// switching, so we should rate limit adjustments.
QuicTimeDelta adjustment_delay =
QuicTimeDelta(stats.smoothed_rtt * kMinTimeBetweenAdjustmentsInRtts);
adjustment_delay = std::min(adjustment_delay, kMaxTimeBetweenAdjustments);
QuicTime now = clock_->ApproximateNow();
if (now - last_adjustment_time_ < adjustment_delay) {
return;
}
// Only adjust downwards.
QuicBandwidth target_bandwidth =
kTargetBitrateMultiplier *
QuicBandwidth::FromBitsPerSecond(stats.estimated_send_rate_bps);
QuicBandwidth current_bandwidth = adjustable_->GetCurrentBitrate();
if (current_bandwidth <= target_bandwidth) {
return;
}
QUICHE_DLOG(INFO) << "Adjusting the bitrate from " << current_bandwidth
<< " to " << target_bandwidth;
bool success = adjustable_->AdjustBitrate(target_bandwidth);
if (success) {
last_adjustment_time_ = now;
}
}
void MoqtBitrateAdjuster::OnObjectAckSupportKnown(bool supported) {
QUICHE_DLOG_IF(WARNING, !supported)
<< "OBJECT_ACK not supported; bitrate adjustments will not work.";
}
} // namespace moqt