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