| // 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. | |
| // moqt_simulator simulates the behavior of MoQ Transport under various network | |
| // conditions and application settings. | |
| #include <cstdint> | |
| #include <iostream> | |
| #include <string> | |
| #include "absl/strings/ascii.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/time/time.h" | |
| #include "quiche/quic/core/quic_bandwidth.h" | |
| #include "quiche/quic/core/quic_time.h" | |
| #include "quiche/quic/core/quic_types.h" | |
| #include "quiche/quic/moqt/moqt_priority.h" | |
| #include "quiche/quic/moqt/test_tools/moqt_simulator.h" | |
| #include "quiche/common/platform/api/quiche_command_line_flags.h" | |
| namespace { | |
| using ::quic::QuicBandwidth; | |
| using ::quic::QuicByteCount; | |
| using ::quic::QuicTimeDelta; | |
| } // namespace | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| uint64_t, bandwidth, | |
| moqt::test::SimulationParameters().bandwidth.ToKBitsPerSecond(), | |
| "Bandwidth of the simulated link, in kilobits per second."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| uint64_t, bitrate, | |
| moqt::test::SimulationParameters().bitrate.ToKBitsPerSecond(), | |
| "Initial bitrate of the simulated media."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| absl::Duration, deadline, | |
| moqt::test::SimulationParameters().deadline.ToAbsl(), | |
| "Frame delivery deadline (used for measurement only)."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| absl::Duration, duration, | |
| moqt::test::SimulationParameters().duration.ToAbsl(), | |
| "Duration of the simulation"); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| std::string, group_order, "desc", | |
| "Delivery order used for the MoQT track simulated ('asc' or 'desc')."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| bool, bitrate_adaptation, true, | |
| "Whether track payload's bitrate can be adjusted."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG(absl::Duration, delivery_timeout, | |
| absl::InfiniteDuration(), | |
| "Delivery timeout for the subscription."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG(bool, alternative_timeout, false, | |
| "Use alternative delivery timeout design."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| float, packet_loss_rate, | |
| moqt::test::SimulationParameters().packet_loss_rate, | |
| "Adds additional packet loss at the publisher-to-subscriber direction, " | |
| "specified as a fraction."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| absl::Duration, blackhole_duration, | |
| moqt::test::SimulationParameters().blackhole_duration.ToAbsl(), | |
| "If non-zero, makes the traffic disappear in the middle of the connection " | |
| "for the specified duration."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| quic::QuicByteCount, aggregation_threshold, | |
| moqt::test::SimulationParameters().aggregation_threshold, | |
| "If non-zero, enables packet aggregation with the specified threshold (the " | |
| "packets sent by publisher will be delayed until the specified number is " | |
| "present)."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| absl::Duration, aggregation_timeout, | |
| moqt::test::SimulationParameters().aggregation_timeout.ToAbsl(), | |
| "Sets the timeout for packet aggregation; if zero, this will be set to the " | |
| "quarter of min RTT."); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| absl::Duration, group_duration, absl::ZeroDuration(), | |
| "If non-zero, sets the group size to match the requested duration"); | |
| DEFINE_QUICHE_COMMAND_LINE_FLAG( | |
| std::string, output_format, "", | |
| R"(If non-empty, instead of the usual human-readable format, | |
| the tool will output the raw numbers from the simulation, formatted as | |
| descrbied by the parameter. | |
| Supported format keys: | |
| * {on_time_fraction} -- fraction of objects that arrived on time | |
| * {late_fraction} -- fraction of objects that arrived late | |
| * {missing_fraction} -- fraction of objects that never arrived)"); | |
| int main(int argc, char** argv) { | |
| moqt::test::SimulationParameters parameters; | |
| quiche::QuicheParseCommandLineFlags("moqt_simulator", argc, argv); | |
| parameters.bandwidth = quic::QuicBandwidth::FromKBitsPerSecond( | |
| quiche::GetQuicheCommandLineFlag(FLAGS_bandwidth)); | |
| parameters.bitrate = quic::QuicBandwidth::FromKBitsPerSecond( | |
| quiche::GetQuicheCommandLineFlag(FLAGS_bitrate)); | |
| parameters.deadline = | |
| quic::QuicTimeDelta(quiche::GetQuicheCommandLineFlag(FLAGS_deadline)); | |
| parameters.duration = | |
| quic::QuicTimeDelta(quiche::GetQuicheCommandLineFlag(FLAGS_duration)); | |
| parameters.bitrate_adaptation = | |
| quiche::GetQuicheCommandLineFlag(FLAGS_bitrate_adaptation); | |
| parameters.delivery_timeout = quic::QuicTimeDelta( | |
| quiche::GetQuicheCommandLineFlag(FLAGS_delivery_timeout)); | |
| parameters.packet_loss_rate = | |
| quiche::GetQuicheCommandLineFlag(FLAGS_packet_loss_rate); | |
| parameters.alternative_timeout = | |
| quiche::GetQuicheCommandLineFlag(FLAGS_alternative_timeout); | |
| parameters.blackhole_duration = quic::QuicTimeDelta( | |
| quiche::GetQuicheCommandLineFlag(FLAGS_blackhole_duration)); | |
| parameters.aggregation_threshold = | |
| quiche::GetQuicheCommandLineFlag(FLAGS_aggregation_threshold); | |
| parameters.aggregation_timeout = quic::QuicTimeDelta( | |
| quiche::GetQuicheCommandLineFlag(FLAGS_aggregation_timeout)); | |
| absl::Duration group_duration = | |
| quiche::GetQuicheCommandLineFlag(FLAGS_group_duration); | |
| if (group_duration > absl::ZeroDuration()) { | |
| parameters.keyframe_interval = | |
| absl::ToDoubleSeconds(group_duration) * parameters.fps; | |
| } | |
| std::string raw_group_order = absl::AsciiStrToLower( | |
| quiche::GetQuicheCommandLineFlag(FLAGS_group_order)); | |
| if (raw_group_order == "asc") { | |
| parameters.group_order = moqt::MoqtGroupOrder::kAscending; | |
| } else if (raw_group_order == "desc") { | |
| parameters.group_order = moqt::MoqtGroupOrder::kDescending; | |
| } else { | |
| std::cerr << "--group_order must be 'asc' or 'desc'." << std::endl; | |
| return 1; | |
| } | |
| moqt::test::MoqtSimulator simulator(parameters); | |
| simulator.Run(); | |
| std::string output_format = | |
| quiche::GetQuicheCommandLineFlag(FLAGS_output_format); | |
| if (output_format.empty()) { | |
| simulator.HumanReadableOutput(); | |
| } else { | |
| simulator.CustomOutput(output_format); | |
| } | |
| return 0; | |
| } |