Tune flow control windows in MOQT to be higher. Currently, the starting point per stream is 16KiB, meaning that we are bound to hit a flow control block for every I-frame at a sufficiently high bandwidth. This changes that to 128KiB. PiperOrigin-RevId: 825158931
diff --git a/build/source_list.bzl b/build/source_list.bzl index cb84ae0..9fd77f0 100644 --- a/build/source_list.bzl +++ b/build/source_list.bzl
@@ -1565,6 +1565,7 @@ "quic/moqt/moqt_priority.h", "quic/moqt/moqt_probe_manager.h", "quic/moqt/moqt_publisher.h", + "quic/moqt/moqt_quic_config.h", "quic/moqt/moqt_relay_publisher.h", "quic/moqt/moqt_relay_track_publisher.h", "quic/moqt/moqt_session.h", @@ -1591,6 +1592,7 @@ "quic/moqt/moqt_parser.cc", "quic/moqt/moqt_priority.cc", "quic/moqt/moqt_probe_manager.cc", + "quic/moqt/moqt_quic_config.cc", "quic/moqt/moqt_relay_publisher.cc", "quic/moqt/moqt_relay_track_publisher.cc", "quic/moqt/moqt_session.cc",
diff --git a/build/source_list.gni b/build/source_list.gni index 5f30475..be8a3b4 100644 --- a/build/source_list.gni +++ b/build/source_list.gni
@@ -1569,6 +1569,7 @@ "src/quiche/quic/moqt/moqt_priority.h", "src/quiche/quic/moqt/moqt_probe_manager.h", "src/quiche/quic/moqt/moqt_publisher.h", + "src/quiche/quic/moqt/moqt_quic_config.h", "src/quiche/quic/moqt/moqt_relay_publisher.h", "src/quiche/quic/moqt/moqt_relay_track_publisher.h", "src/quiche/quic/moqt/moqt_session.h", @@ -1595,6 +1596,7 @@ "src/quiche/quic/moqt/moqt_parser.cc", "src/quiche/quic/moqt/moqt_priority.cc", "src/quiche/quic/moqt/moqt_probe_manager.cc", + "src/quiche/quic/moqt/moqt_quic_config.cc", "src/quiche/quic/moqt/moqt_relay_publisher.cc", "src/quiche/quic/moqt/moqt_relay_track_publisher.cc", "src/quiche/quic/moqt/moqt_session.cc",
diff --git a/build/source_list.json b/build/source_list.json index 3d3f419..41abf16 100644 --- a/build/source_list.json +++ b/build/source_list.json
@@ -1568,6 +1568,7 @@ "quiche/quic/moqt/moqt_priority.h", "quiche/quic/moqt/moqt_probe_manager.h", "quiche/quic/moqt/moqt_publisher.h", + "quiche/quic/moqt/moqt_quic_config.h", "quiche/quic/moqt/moqt_relay_publisher.h", "quiche/quic/moqt/moqt_relay_track_publisher.h", "quiche/quic/moqt/moqt_session.h", @@ -1594,6 +1595,7 @@ "quiche/quic/moqt/moqt_parser.cc", "quiche/quic/moqt/moqt_priority.cc", "quiche/quic/moqt/moqt_probe_manager.cc", + "quiche/quic/moqt/moqt_quic_config.cc", "quiche/quic/moqt/moqt_relay_publisher.cc", "quiche/quic/moqt/moqt_relay_track_publisher.cc", "quiche/quic/moqt/moqt_session.cc",
diff --git a/quiche/quic/moqt/moqt_quic_config.cc b/quiche/quic/moqt/moqt_quic_config.cc new file mode 100644 index 0000000..fd8b68f --- /dev/null +++ b/quiche/quic/moqt/moqt_quic_config.cc
@@ -0,0 +1,48 @@ +// Copyright 2025 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_quic_config.h" + +#include "quiche/quic/core/quic_config.h" +#include "quiche/quic/core/quic_types.h" + +namespace moqt { + +namespace { + +using ::quic::QuicByteCount; + +// Since MoQT creates a lot of short-lived streams, the tuning of the initial +// stream flow control window is critical to avoid incurring a latency penalty +// from ramping up the window on every individual stream. +// +// A typical I-frame in a high-bitrate FHD video tends to be in the low 100 KiB +// range. Even for a higher-latency connection such as 100ms, that would imply +// an instantaneous bitrate of 10 Mbps. +constexpr QuicByteCount kDefaultInitialStreamWindow = 128 * 1024; + +// The flow control window autotuning does work with connection-level flow +// control, but we still can make the startup smoother by setting a more +// reasonable value than the default 16 KiB. It does not have to accomodate for +// much more than a single data stream at a time, since for most of the MOQT +// users the bandwidth usage would be dominated by a single track. +constexpr QuicByteCount kDefaultInitialConnectionWindow = + 2 * kDefaultInitialStreamWindow; + +} // namespace + +void TuneQuicConfig(quic::QuicConfig& config) { + config.SetInitialMaxStreamDataBytesUnidirectionalToSend( + kDefaultInitialStreamWindow); + config.SetInitialSessionFlowControlWindowToSend( + kDefaultInitialConnectionWindow); +} + +quic::QuicConfig GenerateQuicConfig() { + quic::QuicConfig config; + TuneQuicConfig(config); + return config; +} + +} // namespace moqt
diff --git a/quiche/quic/moqt/moqt_quic_config.h b/quiche/quic/moqt/moqt_quic_config.h new file mode 100644 index 0000000..4d160ac --- /dev/null +++ b/quiche/quic/moqt/moqt_quic_config.h
@@ -0,0 +1,21 @@ +// Copyright 2025 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_QUIC_CONFIG_H_ +#define QUICHE_QUIC_MOQT_MOQT_QUIC_CONFIG_H_ + +#include "quiche/quic/core/quic_config.h" + +namespace moqt { + +// Adjusts QuicConfig to work better with MOQT by setting parameters such as +// initial flow control windows appropriately. +void TuneQuicConfig(quic::QuicConfig& config); + +// Convenience method that returns the result of tuning a default QuicConfig. +quic::QuicConfig GenerateQuicConfig(); + +} // namespace moqt + +#endif // QUICHE_QUIC_MOQT_MOQT_QUIC_CONFIG_H_
diff --git a/quiche/quic/moqt/test_tools/moqt_simulator.h b/quiche/quic/moqt/test_tools/moqt_simulator.h index 160a23f..b0fbffd 100644 --- a/quiche/quic/moqt/test_tools/moqt_simulator.h +++ b/quiche/quic/moqt/test_tools/moqt_simulator.h
@@ -17,6 +17,7 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "quiche/quic/core/quic_bandwidth.h" +#include "quiche/quic/core/quic_session.h" #include "quiche/quic/core/quic_time.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/moqt/moqt_bitrate_adjuster.h" @@ -206,6 +207,9 @@ MoqtSession* client_session() { return client_endpoint_.session(); } MoqtSession* server_session() { return server_endpoint_.session(); } + quic::QuicSession* client_quic_session() { + return client_endpoint_.quic_session(); + } std::string GetClientSessionCongestionControl();
diff --git a/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc b/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc index b124720..43aad64 100644 --- a/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc +++ b/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc
@@ -18,6 +18,7 @@ #include "quiche/quic/core/quic_time.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/moqt/moqt_messages.h" +#include "quiche/quic/moqt/moqt_quic_config.h" #include "quiche/quic/moqt/moqt_session.h" #include "quiche/quic/moqt/moqt_session_callbacks.h" #include "quiche/quic/test_tools/crypto_test_utils.h" @@ -52,7 +53,7 @@ quic::Perspective::IS_CLIENT, quic::GetQuicVersionsForGenericSession()), crypto_config_(quic::test::crypto_test_utils::ProofVerifierForTesting()), - quic_session_(connection_.get(), false, nullptr, quic::QuicConfig(), + quic_session_(connection_.get(), false, nullptr, GenerateQuicConfig(), "test.example.com", 443, "moqt", &session_, /*visitor_owned=*/false, nullptr, &crypto_config_), session_(&quic_session_, @@ -76,7 +77,7 @@ quic::QuicRandom::GetInstance(), quic::test::crypto_test_utils::ProofSourceForTesting(), quic::KeyExchangeSource::Default()), - quic_session_(connection_.get(), false, nullptr, quic::QuicConfig(), + quic_session_(connection_.get(), false, nullptr, GenerateQuicConfig(), "moqt", &session_, /*visitor_owned=*/false, nullptr, &crypto_config_, &compressed_certs_cache_),
diff --git a/quiche/quic/moqt/test_tools/moqt_simulator_test.cc b/quiche/quic/moqt/test_tools/moqt_simulator_test.cc index 4ead7fc..f491515 100644 --- a/quiche/quic/moqt/test_tools/moqt_simulator_test.cc +++ b/quiche/quic/moqt/test_tools/moqt_simulator_test.cc
@@ -5,6 +5,7 @@ #include "quiche/quic/moqt/test_tools/moqt_simulator.h" #include "quiche/quic/core/quic_bandwidth.h" +#include "quiche/quic/core/quic_connection_stats.h" #include "quiche/quic/core/quic_time.h" #include "quiche/common/platform/api/quiche_test.h" @@ -30,6 +31,10 @@ simulator.Run(); EXPECT_GE(simulator.received_on_time_fraction(), 0.8f); EXPECT_LT(simulator.received_on_time_fraction(), 0.99f); + + quic::QuicConnectionStats stats = + simulator.client_quic_session()->connection()->GetStats(); + EXPECT_LT(stats.blocked_frames_sent, 16); } } // namespace
diff --git a/quiche/quic/moqt/tools/moqt_client.cc b/quiche/quic/moqt/tools/moqt_client.cc index b27abca..2742431 100644 --- a/quiche/quic/moqt/tools/moqt_client.cc +++ b/quiche/quic/moqt/tools/moqt_client.cc
@@ -17,6 +17,7 @@ #include "quiche/quic/core/quic_server_id.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/moqt/moqt_messages.h" +#include "quiche/quic/moqt/moqt_quic_config.h" #include "quiche/quic/moqt/moqt_session.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/quic/tools/quic_default_client.h" @@ -33,6 +34,7 @@ quic::QuicEventLoop* event_loop) : spdy_client_(peer_address, server_id, GetMoqtSupportedQuicVersions(), event_loop, std::move(proof_verifier)) { + TuneQuicConfig(*spdy_client_.config()); spdy_client_.set_enable_web_transport(true); }
diff --git a/quiche/quic/moqt/tools/moqt_client.h b/quiche/quic/moqt/tools/moqt_client.h index 853127b..796a751 100644 --- a/quiche/quic/moqt/tools/moqt_client.h +++ b/quiche/quic/moqt/tools/moqt_client.h
@@ -12,7 +12,9 @@ #include "quiche/quic/core/crypto/proof_verifier.h" #include "quiche/quic/core/http/quic_spdy_client_session.h" #include "quiche/quic/core/io/quic_event_loop.h" +#include "quiche/quic/core/quic_session.h" #include "quiche/quic/moqt/moqt_session.h" +#include "quiche/quic/moqt/moqt_session_callbacks.h" #include "quiche/quic/platform/api/quic_socket_address.h" #include "quiche/quic/tools/quic_default_client.h" #include "quiche/common/platform/api/quiche_export.h" @@ -32,6 +34,7 @@ void Connect(std::string path, MoqtSessionCallbacks callbacks); MoqtSession* session() { return session_; } + quic::QuicSession* quic_session() { return spdy_client_.session(); } private: absl::Status ConnectInner(std::string path, MoqtSessionCallbacks& callbacks);
diff --git a/quiche/quic/moqt/tools/moqt_end_to_end_test.cc b/quiche/quic/moqt/tools/moqt_end_to_end_test.cc index 6bf8c9a..718dd49 100644 --- a/quiche/quic/moqt/tools/moqt_end_to_end_test.cc +++ b/quiche/quic/moqt/tools/moqt_end_to_end_test.cc
@@ -97,6 +97,14 @@ callbacks.session_deleted_callback = [&] { deleted = true; }; std::unique_ptr<MoqtClient> client = CreateClient(); client->Connect("/test", std::move(callbacks)); + EXPECT_GT(client->quic_session() + ->config() + ->GetInitialMaxStreamDataBytesUnidirectionalToSend(), + 64 * 1024); + EXPECT_GT(client->quic_session() + ->config() + ->ReceivedInitialMaxStreamDataBytesUnidirectional(), + 64 * 1024); bool success = RunEventsUntil([&] { return established; }); EXPECT_TRUE(success); EXPECT_FALSE(deleted);
diff --git a/quiche/quic/moqt/tools/moqt_server.cc b/quiche/quic/moqt/tools/moqt_server.cc index b919cd4..daacabe 100644 --- a/quiche/quic/moqt/tools/moqt_server.cc +++ b/quiche/quic/moqt/tools/moqt_server.cc
@@ -10,8 +10,12 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "quiche/quic/core/crypto/proof_source.h" +#include "quiche/quic/core/crypto/quic_crypto_server_config.h" +#include "quiche/quic/core/quic_connection_id.h" #include "quiche/quic/core/quic_types.h" +#include "quiche/quic/core/quic_versions.h" #include "quiche/quic/moqt/moqt_messages.h" +#include "quiche/quic/moqt/moqt_quic_config.h" #include "quiche/quic/moqt/moqt_session.h" #include "quiche/quic/tools/quic_server.h" #include "quiche/quic/tools/web_transport_only_backend.h" @@ -41,6 +45,10 @@ MoqtServer::MoqtServer(std::unique_ptr<quic::ProofSource> proof_source, MoqtIncomingSessionCallback callback) : backend_(CreateWebTransportCallback(std::move(callback), &server_)), - server_(std::move(proof_source), /*proof_verifier=*/nullptr, &backend_) {} + server_(std::move(proof_source), /*proof_verifier=*/nullptr, + GenerateQuicConfig(), + quic::QuicCryptoServerConfig::ConfigOptions(), + quic::CurrentSupportedVersionsWithTls(), &backend_, + quic::kQuicDefaultConnectionIdLength) {} } // namespace moqt