blob: 1a3aa89d354a11e6a912eb3cb30035289b475c13 [file]
// 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.
#ifndef QUICHE_QUIC_MOQT_MOQT_BITRATE_ADJUSTER_H_
#define QUICHE_QUIC_MOQT_MOQT_BITRATE_ADJUSTER_H_
#include <memory>
#include <optional>
#include <string>
#include "quiche/quic/core/quic_alarm_factory.h"
#include "quiche/quic/core/quic_bandwidth.h"
#include "quiche/quic/core/quic_clock.h"
#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/moqt/moqt_messages.h"
#include "quiche/quic/moqt/moqt_outstanding_objects.h"
#include "quiche/quic/moqt/moqt_probe_manager.h"
#include "quiche/quic/moqt/moqt_session.h"
#include "quiche/quic/moqt/moqt_trace_recorder.h"
#include "quiche/web_transport/web_transport.h"
namespace moqt {
// Indicates the type of new bitrate estimate.
enum class BitrateAdjustmentType {
// Indicates that the sender is sending too much data.
kDown,
// Indicates that the sender should attempt to increase the amount of data
// sent.
kUp,
};
// A sender that can potentially have its outgoing bitrate adjusted.
class BitrateAdjustable {
public:
virtual ~BitrateAdjustable() {}
// Returns the currently used bitrate.
// TODO(vasilvv): we should not depend on this value long-term, since the
// self-reported bitrate is not reliable in most real encoders.
virtual quic::QuicBandwidth GetCurrentBitrate() const = 0;
// Returns true if the sender could make use of more bandwidth than it is
// currently sending at.
virtual bool CouldUseExtraBandwidth() = 0;
// Notifies the sender that it should consider increasing or decreasing its
// bandwidth. `bandwidth` is the estimate of bandwidth available to the
// application.
virtual void ConsiderAdjustingBitrate(quic::QuicBandwidth bandwidth,
BitrateAdjustmentType type) = 0;
};
// Connection quality levels. Used by the bitrate adjuster to decide when to
// probe up or down.
enum class ConnectionQualityLevel {
// Lowest quality level. Reaching it will result in the bitrate adjuster
// attempting to immediately lower the bitrate.
kCritical,
// At this level, no new bandwidth probes will be started and all existing
// bandwidth probes are cancelled.
kVeryPrecarious,
// At this level, the adjuster will not start any new probes, but all of the
// previous probes will be allowed to continue. This level is separate from
// the previous one to avoid unstable behavior around the boundaries.
kPrecarious,
// Highest quality level. The adjuster will attempt to probe bandwidth if
// necessary.
kStable,
kNumLevels,
};
std::string ConnectionQualityLevelToString(ConnectionQualityLevel level);
template <typename Sink>
void AbslStringify(Sink& sink, ConnectionQualityLevel level) {
sink.Append(ConnectionQualityLevelToString(level));
}
inline constexpr int kNumQualityLevels =
static_cast<int>(ConnectionQualityLevel::kNumLevels);
// Parameters (mostly magic numbers) that determine behavior of
// MoqtBitrateAdjuster.
struct MoqtBitrateAdjusterParameters {
// When bitrate is adjusted down, multiply the congestion controller estimate
// by this factor. This should be less than 1, since congestion controller
// estimate tends to be overly optimistic in practice.
float target_bitrate_multiplier_down = 0.9f;
// Same, but applies for adjusting up. Similar considerations for selecting
// the value apply.
float target_bitrate_multiplier_up = 0.9f;
// Do not perform any updates within `initial_delay` after the connection
// start.
quic::QuicTimeDelta initial_delay = quic::QuicTimeDelta::FromSeconds(2);
// Quality level thresholds. If the object arrives with the
// time-before-deadline that is lower than the first number listed here, it
// will result in the connection being assigned the lowest quality level, and
// so on. The thresholds are expressed as a fraction of `time_window` (which
// typically would be equal to the size of the buffer in seconds).
float quality_level_time_thresholds[kNumQualityLevels - 1] = {0.2f, 0.4f,
0.6f};
// The maximum gap between the next object expected to be received, and the
// actually received object, expressed as a number of objects. If the
// reordering gap exceeds the configured threshold, the connection is marked
// as being in that quality level. The thresholds correspond to kCritical,
// kVeryPrecarious, and kPrecarious.
//
// The default for the worst-case reordering is 12, which corresponds to about
// 400ms for 30fps video.
int quality_level_reordering_thresholds[kNumQualityLevels - 1] = {12, 6, 3};
// Amount of time the connection has to spend in good state before attempting
// to probe for bandwidth.
quic::QuicTimeDelta time_before_probing = quic::QuicTimeDelta::FromSeconds(2);
// When probing, attempt to increase the bandwidth by the specified factor.
// Used when determining the probe size and timeout.
float probe_increase_target = 1.2;
// Expected duration of a probe, expressed in the number of round-trips
// necessary. Selecting values below 8 might interact negatively with BBR.
float round_trips_for_probe = 16;
// Probe results will be ignored if the probe was cancelled before lasting for
// the specified duration.
float min_probe_duration_in_rtts = 4;
};
// MoqtBitrateAdjuster monitors the progress of delivery for a single track, and
// adjusts the bitrate of the track in question accordingly.
class MoqtBitrateAdjuster : public MoqtPublishingMonitorInterface {
public:
MoqtBitrateAdjuster(const quic::QuicClock* clock,
webtransport::Session* session,
quic::QuicAlarmFactory* alarm_factory,
BitrateAdjustable* adjustable);
MoqtBitrateAdjuster(const quic::QuicClock* clock,
webtransport::Session* session,
std::unique_ptr<MoqtProbeManagerInterface> probe_manager,
BitrateAdjustable* adjustable);
// MoqtPublishingMonitorInterface implementation.
void OnObjectAckSupportKnown(
std::optional<quic::QuicTimeDelta> time_window) override;
void OnNewObjectEnqueued(Location location) override;
void OnObjectAckReceived(Location location,
quic::QuicTimeDelta delta_from_deadline) override;
MoqtTraceRecorder& trace_recorder() { return trace_recorder_; }
MoqtBitrateAdjusterParameters& parameters() { return parameters_; }
private:
void Start();
// Checks if the bitrate adjuster should react to an individual ack.
bool ShouldUseAckAsActionSignal(Location location);
// Checks if the bitrate should be adjusted down based on the result of
// processing an object ACK.
ConnectionQualityLevel GetQualityLevel(
int reordering_delta, quic::QuicTimeDelta delta_from_deadline) const;
// Starts a bandwidth probe if all the conditions are met.
void MaybeStartProbing(quic::QuicTime now);
// Callback called whenever the bandwidth probe is finished.
void OnProbeResult(const webtransport::SessionStats& old_stats,
const ProbeResult& result);
// Attempts adjusting the bitrate down.
void AttemptAdjustingDown();
void SuggestNewBitrate(quic::QuicBandwidth bitrate,
BitrateAdjustmentType type);
const quic::QuicClock* clock_; // Not owned.
webtransport::Session* session_; // Not owned.
BitrateAdjustable* adjustable_; // Not owned.
MoqtTraceRecorder trace_recorder_;
MoqtBitrateAdjusterParameters parameters_;
// The time at which Start() has been called.
quic::QuicTime start_time_ = quic::QuicTime::Zero();
// The window size received from the peer. This amount is used to establish
// the scale for incoming time deltas in the object ACKs.
quic::QuicTimeDelta time_window_ = quic::QuicTimeDelta::Zero();
// The earliest point at which the connection has been continuously in the
// stable state. Used to time bandwidth probes.
std::optional<quic::QuicTime> in_stable_state_since_;
std::optional<MoqtOutstandingObjects> outstanding_objects_;
std::unique_ptr<MoqtProbeManagerInterface> probe_manager_;
Location last_acked_object_;
};
// Given a suggestion to change bitrate `old_bitrate` to `new_bitrate` with the
// specified adjustment type, returns true if the change should be ignored.
// `min_change` is the threshold below which the change should be ignored,
// specified as a fraction of old bitrate.
bool ShouldIgnoreBitrateAdjustment(quic::QuicBandwidth new_bitrate,
BitrateAdjustmentType type,
quic::QuicBandwidth old_bitrate,
float min_change);
} // namespace moqt
#endif // QUICHE_QUIC_MOQT_MOQT_BITRATE_ADJUSTER_H_