diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index 790fe66378..2cd4991e32 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -360,6 +360,7 @@ if (rtc_include_tests) { "../test:field_trial", "../test:fileutils", "../test:test_support", + "//third_party/abseil-cpp/absl/algorithm:container", ] } } diff --git a/rtc_tools/event_log_visualizer/main.cc b/rtc_tools/event_log_visualizer/main.cc index e7de720315..2fbd83aec0 100644 --- a/rtc_tools/event_log_visualizer/main.cc +++ b/rtc_tools/event_log_visualizer/main.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/algorithm/container.h" #include "logging/rtc_event_log/rtc_event_log.h" #include "logging/rtc_event_log/rtc_event_log_parser.h" #include "modules/audio_coding/neteq/include/neteq.h" @@ -32,150 +33,9 @@ #include "test/testsupport/file_utils.h" WEBRTC_DEFINE_string( - plot_profile, + plot, "default", - "A profile that selects a certain subset of the plots. Currently " - "defined profiles are \"all\", \"none\", \"sendside_bwe\"," - "\"receiveside_bwe\" and \"default\""); - -WEBRTC_DEFINE_bool(plot_incoming_packet_sizes, - false, - "Plot bar graph showing the size of each incoming packet."); -WEBRTC_DEFINE_bool(plot_outgoing_packet_sizes, - false, - "Plot bar graph showing the size of each outgoing packet."); -WEBRTC_DEFINE_bool(plot_incoming_rtcp_types, - false, - "Plot the RTCP block types for incoming RTCP packets."); -WEBRTC_DEFINE_bool(plot_outgoing_rtcp_types, - false, - "Plot the RTCP block types for outgoing RTCP packets."); -WEBRTC_DEFINE_bool( - plot_incoming_packet_count, - false, - "Plot the accumulated number of packets for each incoming stream."); -WEBRTC_DEFINE_bool( - plot_outgoing_packet_count, - false, - "Plot the accumulated number of packets for each outgoing stream."); -WEBRTC_DEFINE_bool( - plot_audio_playout, - false, - "Plot bar graph showing the time between each audio playout."); -WEBRTC_DEFINE_bool( - plot_audio_level, - false, - "Plot line graph showing the audio level of incoming audio."); -WEBRTC_DEFINE_bool( - plot_incoming_sequence_number_delta, - false, - "Plot the sequence number difference between consecutive incoming " - "packets."); -WEBRTC_DEFINE_bool( - plot_incoming_delay, - true, - "Plot the 1-way path delay for incoming packets, normalized so " - "that the first packet has delay 0."); -WEBRTC_DEFINE_bool( - plot_incoming_loss_rate, - true, - "Compute the loss rate for incoming packets using a method that's " - "similar to the one used for RTCP SR and RR fraction lost. Note " - "that the loss rate can be negative if packets are duplicated or " - "reordered."); -WEBRTC_DEFINE_bool(plot_incoming_bitrate, - true, - "Plot the total bitrate used by all incoming streams."); -WEBRTC_DEFINE_bool(plot_outgoing_bitrate, - true, - "Plot the total bitrate used by all outgoing streams."); -WEBRTC_DEFINE_bool(plot_incoming_stream_bitrate, - true, - "Plot the bitrate used by each incoming stream."); -WEBRTC_DEFINE_bool(plot_outgoing_stream_bitrate, - true, - "Plot the bitrate used by each outgoing stream."); -WEBRTC_DEFINE_bool(plot_incoming_layer_bitrate_allocation, - false, - "Plot the target bitrate for each incoming layer. Requires " - "incoming RTCP XR with target bitrate to be populated."); -WEBRTC_DEFINE_bool(plot_outgoing_layer_bitrate_allocation, - false, - "Plot the target bitrate for each outgoing layer. Requires " - "outgoing RTCP XR with target bitrate to be populated."); -WEBRTC_DEFINE_bool( - plot_simulated_receiveside_bwe, - false, - "Run the receive-side bandwidth estimator with the incoming rtp " - "packets and plot the resulting estimate."); -WEBRTC_DEFINE_bool( - plot_simulated_sendside_bwe, - false, - "Run the send-side bandwidth estimator with the outgoing rtp and " - "incoming rtcp and plot the resulting estimate."); -WEBRTC_DEFINE_bool(plot_simulated_goog_cc, - false, - "Run the GoogCC congestion controller based on the logged " - "events and plot the target bitrate."); -WEBRTC_DEFINE_bool( - plot_network_delay_feedback, - true, - "Compute network delay based on sent packets and the received " - "transport feedback."); -WEBRTC_DEFINE_bool( - plot_fraction_loss_feedback, - true, - "Plot packet loss in percent for outgoing packets (as perceived by " - "the send-side bandwidth estimator)."); -WEBRTC_DEFINE_bool( - plot_pacer_delay, - false, - "Plot the time each sent packet has spent in the pacer (based on " - "the difference between the RTP timestamp and the send " - "timestamp)."); -WEBRTC_DEFINE_bool( - plot_timestamps, - false, - "Plot the rtp timestamps of all rtp and rtcp packets over time."); -WEBRTC_DEFINE_bool( - plot_rtcp_details, - false, - "Plot the contents of all report blocks in all sender and receiver " - "reports. This includes fraction lost, cumulative number of lost " - "packets, extended highest sequence number and time since last " - "received SR."); -WEBRTC_DEFINE_bool(plot_audio_encoder_bitrate_bps, - false, - "Plot the audio encoder target bitrate."); -WEBRTC_DEFINE_bool(plot_audio_encoder_frame_length_ms, - false, - "Plot the audio encoder frame length."); -WEBRTC_DEFINE_bool( - plot_audio_encoder_packet_loss, - false, - "Plot the uplink packet loss fraction which is sent to the audio encoder."); -WEBRTC_DEFINE_bool(plot_audio_encoder_fec, - false, - "Plot the audio encoder FEC."); -WEBRTC_DEFINE_bool(plot_audio_encoder_dtx, - false, - "Plot the audio encoder DTX."); -WEBRTC_DEFINE_bool(plot_audio_encoder_num_channels, - false, - "Plot the audio encoder number of channels."); -WEBRTC_DEFINE_bool(plot_neteq_stats, false, "Plot the NetEq statistics."); -WEBRTC_DEFINE_bool(plot_ice_candidate_pair_config, - false, - "Plot the ICE candidate pair config events."); -WEBRTC_DEFINE_bool(plot_ice_connectivity_check, - false, - "Plot the ICE candidate pair connectivity checks."); -WEBRTC_DEFINE_bool(plot_dtls_transport_state, - false, - "Plot DTLS transport state changes."); -WEBRTC_DEFINE_bool(plot_dtls_writable_state, - false, - "Plot DTLS writable state changes."); + "A comma separated list of plot names. See below for valid options."); WEBRTC_DEFINE_string( force_fieldtrials, @@ -225,7 +85,87 @@ WEBRTC_DEFINE_bool(protobuf_output, false, "Output charts as protobuf instead of python code."); -void SetAllPlotFlags(bool setting); +using webrtc::Plot; + +namespace { +std::vector StrSplit(const std::string& s, + const std::string& delimiter) { + std::vector v; + size_t pos = 0; + while (pos < s.length()) { + const std::string token = s.substr(pos, s.find(delimiter, pos) - pos); + pos += token.length() + delimiter.length(); + v.push_back(token); + } + return v; +} + +struct PlotDeclaration { + PlotDeclaration(const std::string& label, std::function f) + : label(label), enabled(false), plot_func(f) {} + const std::string label; + bool enabled; + // TODO(terelius): Add a help text/explanation. + const std::function plot_func; +}; + +class PlotMap { + public: + void RegisterPlot(const std::string& label, std::function f) { + for (const auto& plot : plots_) { + RTC_DCHECK(plot.label != label) + << "Can't use the same label for multiple plots"; + } + plots_.push_back({label, f}); + } + + bool EnablePlotsByFlags( + const std::vector& flags, + const std::map>& flag_aliases) { + bool status = true; + for (const std::string& flag : flags) { + auto alias_it = flag_aliases.find(flag); + if (alias_it != flag_aliases.end()) { + const auto& replacements = alias_it->second; + for (const auto& replacement : replacements) { + status &= EnablePlotByFlag(replacement); + } + } else { + status &= EnablePlotByFlag(flag); + } + } + return status; + } + + void EnableAllPlots() { + for (auto& plot : plots_) { + plot.enabled = true; + } + } + + std::vector::iterator begin() { return plots_.begin(); } + std::vector::iterator end() { return plots_.end(); } + + private: + bool EnablePlotByFlag(const std::string& flag) { + for (auto& plot : plots_) { + if (plot.label == flag) { + plot.enabled = true; + return true; + } + } + if (flag == "simulated_neteq_jitter_buffer_delay") { + // This flag is handled separately. + return true; + } + std::cerr << "Unrecognized plot name \'" << flag << "\'. Aborting." + << std::endl; + return false; + } + + std::vector plots_; +}; +} // namespace int main(int argc, char* argv[]) { std::string program_name = argv[0]; @@ -235,53 +175,57 @@ int main(int argc, char* argv[]) { program_name + " | python\n" + "Run " + program_name + " --help for a list of command line options\n"; - // Parse command line flags without removing them. We're only interested in - // the |plot_profile| flag. - rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, false); - if (strcmp(FLAG_plot_profile, "all") == 0) { - SetAllPlotFlags(true); - } else if (strcmp(FLAG_plot_profile, "none") == 0) { - SetAllPlotFlags(false); - } else if (strcmp(FLAG_plot_profile, "sendside_bwe") == 0) { - SetAllPlotFlags(false); - FLAG_plot_outgoing_packet_sizes = true; - FLAG_plot_outgoing_bitrate = true; - FLAG_plot_outgoing_stream_bitrate = true; - FLAG_plot_simulated_sendside_bwe = true; - FLAG_plot_network_delay_feedback = true; - FLAG_plot_fraction_loss_feedback = true; - } else if (strcmp(FLAG_plot_profile, "receiveside_bwe") == 0) { - SetAllPlotFlags(false); - FLAG_plot_incoming_packet_sizes = true; - FLAG_plot_incoming_delay = true; - FLAG_plot_incoming_loss_rate = true; - FLAG_plot_incoming_bitrate = true; - FLAG_plot_incoming_stream_bitrate = true; - FLAG_plot_simulated_receiveside_bwe = true; - } else if (strcmp(FLAG_plot_profile, "default") == 0) { - // Do nothing. - } else { - rtc::Flag* plot_profile_flag = rtc::FlagList::Lookup("plot_profile"); - RTC_CHECK(plot_profile_flag); - plot_profile_flag->Print(false); - } - // Parse the remaining flags. They are applied relative to the chosen profile. rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true); - if (argc != 2 || FLAG_help) { - // Print usage information. - std::cout << usage; - if (FLAG_help) - rtc::FlagList::Print(nullptr, false); - return 0; - } + // Flag replacements + std::map> flag_aliases = { + {"default", + {"incoming_delay", "incoming_loss_rate", "incoming_bitrate", + "outgoing_bitrate," + "incoming_stream_bitrate", + "outgoing_stream_bitrate", "network_delay_feedback", + "fraction_loss_feedback"}}, + {"sendside_bwe", + {"outgoing_packet_sizes", "outgoing_bitrate", "outgoing_stream_bitrate", + "simulated_sendside_bwe", "network_delay_feedback", + "fraction_loss_feedback"}}, + {"receiveside_bwe", + {"incoming_packet_sizes", "incoming_delay", "incoming_loss_rate", + "incoming_" + "bitrate", + "incoming_stream_bitrate", "simulated_receiveside_bwe"}}, + {"rtcp_details", + {"incoming_rtcp_fraction_lost", "outgoing_rtcp_fraction_lost", + "incoming_rtcp_" + "cumulative_lost", + "outgoing_rtcp_cumulative_lost", + "incoming_rtcp_highest_" + "seq_number", + "outgoing_rtcp_highest_seq_number", + "incoming_rtcp_delay_since_" + "last_sr", + "outgoing_rtcp_delay_since_last_sr"}}, + {"simulated_neteq_stats", + {"simulated_neteq_jitter_buffer_delay", + "simulated_neteq_preferred_buffer_" + "size", + "simulated_neteq_" + "concealment_events", + "simulated_neteq_packet_loss_rate", + "simulated_neteq_" + "preemptive_rate", + "simulated_neteq_" + "accelerate_rate", + "simulated_neteq_speech_expand_rate", + "simulated_neteq_" + "expand_rate"}}}; + + std::vector plot_flags = StrSplit(FLAG_plot, ","); // InitFieldTrialsFromString stores the char*, so the char array must outlive // the application. webrtc::field_trial::InitFieldTrialsFromString(FLAG_force_fieldtrials); - std::string filename = argv[1]; - webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions header_extensions = webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions::kDontParse; if (FLAG_parse_unconfigured_header_extensions) { @@ -290,9 +234,12 @@ int main(int argc, char* argv[]) { } webrtc::ParsedRtcEventLog parsed_log(header_extensions); - if (!parsed_log.ParseFile(filename)) { - std::cerr << "Could not parse the entire log file." << std::endl; - std::cerr << "Only the parsable events will be analyzed." << std::endl; + if (argc == 2) { + std::string filename = argv[1]; + if (!parsed_log.ParseFile(filename)) { + std::cerr << "Could not parse the entire log file." << std::endl; + std::cerr << "Only the parsable events will be analyzed." << std::endl; + } } webrtc::EventLogAnalyzer analyzer(parsed_log, FLAG_normalize_time); @@ -303,238 +250,330 @@ int main(int argc, char* argv[]) { collection.reset(new webrtc::PythonPlotCollection(FLAG_shared_xaxis)); } - if (FLAG_plot_incoming_packet_sizes) { - analyzer.CreatePacketGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_outgoing_packet_sizes) { - analyzer.CreatePacketGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_rtcp_types) { - analyzer.CreateRtcpTypeGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_outgoing_rtcp_types) { - analyzer.CreateRtcpTypeGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_packet_count) { - analyzer.CreateAccumulatedPacketsGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_outgoing_packet_count) { - analyzer.CreateAccumulatedPacketsGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_audio_playout) { - analyzer.CreatePlayoutGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_audio_level) { - analyzer.CreateAudioLevelGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - analyzer.CreateAudioLevelGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_sequence_number_delta) { - analyzer.CreateSequenceNumberGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_delay) { - analyzer.CreateIncomingDelayGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_loss_rate) { - analyzer.CreateIncomingPacketLossGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_bitrate) { - analyzer.CreateTotalIncomingBitrateGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_outgoing_bitrate) { - analyzer.CreateTotalOutgoingBitrateGraph(collection->AppendNewPlot(), - FLAG_show_detector_state, + PlotMap plots; + plots.RegisterPlot("incoming_packet_sizes", [&](Plot* plot) { + analyzer.CreatePacketGraph(webrtc::kIncomingPacket, plot); + }); + + plots.RegisterPlot("outgoing_packet_sizes", [&](Plot* plot) { + analyzer.CreatePacketGraph(webrtc::kOutgoingPacket, plot); + }); + plots.RegisterPlot("incoming_rtcp_types", [&](Plot* plot) { + analyzer.CreateRtcpTypeGraph(webrtc::kIncomingPacket, plot); + }); + plots.RegisterPlot("outgoing_rtcp_types", [&](Plot* plot) { + analyzer.CreateRtcpTypeGraph(webrtc::kOutgoingPacket, plot); + }); + plots.RegisterPlot("incoming_packet_count", [&](Plot* plot) { + analyzer.CreateAccumulatedPacketsGraph(webrtc::kIncomingPacket, plot); + }); + plots.RegisterPlot("outgoing_packet_count", [&](Plot* plot) { + analyzer.CreateAccumulatedPacketsGraph(webrtc::kOutgoingPacket, plot); + }); + plots.RegisterPlot("audio_playout", + [&](Plot* plot) { analyzer.CreatePlayoutGraph(plot); }); + plots.RegisterPlot("incoming_audio_level", [&](Plot* plot) { + analyzer.CreateAudioLevelGraph(webrtc::kIncomingPacket, plot); + }); + plots.RegisterPlot("outgoing_audio_level", [&](Plot* plot) { + analyzer.CreateAudioLevelGraph(webrtc::kOutgoingPacket, plot); + }); + plots.RegisterPlot("incoming_sequence_number_delta", [&](Plot* plot) { + analyzer.CreateSequenceNumberGraph(plot); + }); + plots.RegisterPlot("incoming_delay", [&](Plot* plot) { + analyzer.CreateIncomingDelayGraph(plot); + }); + plots.RegisterPlot("incoming_loss_rate", [&](Plot* plot) { + analyzer.CreateIncomingPacketLossGraph(plot); + }); + plots.RegisterPlot("incoming_bitrate", [&](Plot* plot) { + analyzer.CreateTotalIncomingBitrateGraph(plot); + }); + plots.RegisterPlot("outgoing_bitrate", [&](Plot* plot) { + analyzer.CreateTotalOutgoingBitrateGraph(plot, FLAG_show_detector_state, FLAG_show_alr_state); - } - if (FLAG_plot_incoming_stream_bitrate) { - analyzer.CreateStreamBitrateGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_outgoing_stream_bitrate) { - analyzer.CreateStreamBitrateGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_incoming_layer_bitrate_allocation) { - analyzer.CreateBitrateAllocationGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_outgoing_layer_bitrate_allocation) { - analyzer.CreateBitrateAllocationGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_simulated_receiveside_bwe) { - analyzer.CreateReceiveSideBweSimulationGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_simulated_sendside_bwe) { - analyzer.CreateSendSideBweSimulationGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_simulated_goog_cc) { - analyzer.CreateGoogCcSimulationGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_network_delay_feedback) { - analyzer.CreateNetworkDelayFeedbackGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_fraction_loss_feedback) { - analyzer.CreateFractionLossGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_timestamps) { - analyzer.CreateTimestampGraph(webrtc::kIncomingPacket, - collection->AppendNewPlot()); - analyzer.CreateTimestampGraph(webrtc::kOutgoingPacket, - collection->AppendNewPlot()); - } - if (FLAG_plot_rtcp_details) { - auto GetFractionLost = [](const webrtc::rtcp::ReportBlock& block) -> float { - return static_cast(block.fraction_lost()) / 256 * 100; - }; + }); + plots.RegisterPlot("incoming_stream_bitrate", [&](Plot* plot) { + analyzer.CreateStreamBitrateGraph(webrtc::kIncomingPacket, plot); + }); + plots.RegisterPlot("outgoing_stream_bitrate", [&](Plot* plot) { + analyzer.CreateStreamBitrateGraph(webrtc::kOutgoingPacket, plot); + }); + plots.RegisterPlot("incoming_layer_bitrate_allocation", [&](Plot* plot) { + analyzer.CreateBitrateAllocationGraph(webrtc::kIncomingPacket, plot); + }); + plots.RegisterPlot("outgoing_layer_bitrate_allocation", [&](Plot* plot) { + analyzer.CreateBitrateAllocationGraph(webrtc::kOutgoingPacket, plot); + }); + plots.RegisterPlot("simulated_receiveside_bwe", [&](Plot* plot) { + analyzer.CreateReceiveSideBweSimulationGraph(plot); + }); + plots.RegisterPlot("simulated_sendside_bwe", [&](Plot* plot) { + analyzer.CreateSendSideBweSimulationGraph(plot); + }); + plots.RegisterPlot("simulated_goog_cc", [&](Plot* plot) { + analyzer.CreateGoogCcSimulationGraph(plot); + }); + plots.RegisterPlot("network_delay_feedback", [&](Plot* plot) { + analyzer.CreateNetworkDelayFeedbackGraph(plot); + }); + plots.RegisterPlot("fraction_loss_feedback", [&](Plot* plot) { + analyzer.CreateFractionLossGraph(plot); + }); + plots.RegisterPlot("incoming_timestamps", [&](Plot* plot) { + analyzer.CreateTimestampGraph(webrtc::kIncomingPacket, plot); + }); + plots.RegisterPlot("outgoing_timestamps", [&](Plot* plot) { + analyzer.CreateTimestampGraph(webrtc::kOutgoingPacket, plot); + }); + + auto GetFractionLost = [](const webrtc::rtcp::ReportBlock& block) -> float { + return static_cast(block.fraction_lost()) / 256 * 100; + }; + plots.RegisterPlot("incoming_rtcp_fraction_lost", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kIncomingPacket, GetFractionLost, - "Fraction lost (incoming RTCP)", "Loss rate (percent)", - collection->AppendNewPlot()); + "Fraction lost (incoming RTCP)", "Loss rate (percent)", plot); + }); + plots.RegisterPlot("outgoing_rtcp_fraction_lost", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kOutgoingPacket, GetFractionLost, - "Fraction lost (outgoing RTCP)", "Loss rate (percent)", - collection->AppendNewPlot()); - - auto GetCumulativeLost = - [](const webrtc::rtcp::ReportBlock& block) -> float { - return block.cumulative_lost_signed(); - }; + "Fraction lost (outgoing RTCP)", "Loss rate (percent)", plot); + }); + auto GetCumulativeLost = [](const webrtc::rtcp::ReportBlock& block) -> float { + return block.cumulative_lost_signed(); + }; + plots.RegisterPlot("incoming_rtcp_cumulative_lost", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kIncomingPacket, GetCumulativeLost, - "Cumulative lost packets (incoming RTCP)", "Packets", - collection->AppendNewPlot()); + "Cumulative lost packets (incoming RTCP)", "Packets", plot); + }); + plots.RegisterPlot("outgoing_rtcp_cumulative_lost", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kOutgoingPacket, GetCumulativeLost, - "Cumulative lost packets (outgoing RTCP)", "Packets", - collection->AppendNewPlot()); + "Cumulative lost packets (outgoing RTCP)", "Packets", plot); + }); - auto GetHighestSeqNumber = - [](const webrtc::rtcp::ReportBlock& block) -> float { - return block.extended_high_seq_num(); - }; + auto GetHighestSeqNumber = + [](const webrtc::rtcp::ReportBlock& block) -> float { + return block.extended_high_seq_num(); + }; + plots.RegisterPlot("incoming_rtcp_highest_seq_number", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kIncomingPacket, GetHighestSeqNumber, - "Highest sequence number (incoming RTCP)", "Sequence number", - collection->AppendNewPlot()); + "Highest sequence number (incoming RTCP)", "Sequence number", plot); + }); + plots.RegisterPlot("outgoing_rtcp_highest_seq_number", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kOutgoingPacket, GetHighestSeqNumber, - "Highest sequence number (outgoing RTCP)", "Sequence number", - collection->AppendNewPlot()); + "Highest sequence number (outgoing RTCP)", "Sequence number", plot); + }); - auto DelaySinceLastSr = - [](const webrtc::rtcp::ReportBlock& block) -> float { - return static_cast(block.delay_since_last_sr()) / 65536; - }; + auto DelaySinceLastSr = [](const webrtc::rtcp::ReportBlock& block) -> float { + return static_cast(block.delay_since_last_sr()) / 65536; + }; + plots.RegisterPlot("incoming_rtcp_delay_since_last_sr", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kIncomingPacket, DelaySinceLastSr, "Delay since last received sender report (incoming RTCP)", "Time (s)", - collection->AppendNewPlot()); + plot); + }); + plots.RegisterPlot("outgoing_rtcp_delay_since_last_sr", [&](Plot* plot) { analyzer.CreateSenderAndReceiverReportPlot( webrtc::kOutgoingPacket, DelaySinceLastSr, "Delay since last received sender report (outgoing RTCP)", "Time (s)", - collection->AppendNewPlot()); - } + plot); + }); - if (FLAG_plot_pacer_delay) { - analyzer.CreatePacerDelayGraph(collection->AppendNewPlot()); + plots.RegisterPlot("pacer_delay", + [&](Plot* plot) { analyzer.CreatePacerDelayGraph(plot); }); + plots.RegisterPlot("audio_encoder_bitrate", [&](Plot* plot) { + analyzer.CreateAudioEncoderTargetBitrateGraph(plot); + }); + plots.RegisterPlot("audio_encoder_frame_length", [&](Plot* plot) { + analyzer.CreateAudioEncoderFrameLengthGraph(plot); + }); + plots.RegisterPlot("audio_encoder_packet_loss", [&](Plot* plot) { + analyzer.CreateAudioEncoderPacketLossGraph(plot); + }); + plots.RegisterPlot("audio_encoder_fec", [&](Plot* plot) { + analyzer.CreateAudioEncoderEnableFecGraph(plot); + }); + plots.RegisterPlot("audio_encoder_dtx", [&](Plot* plot) { + analyzer.CreateAudioEncoderEnableDtxGraph(plot); + }); + plots.RegisterPlot("audio_encoder_num_channels", [&](Plot* plot) { + analyzer.CreateAudioEncoderNumChannelsGraph(plot); + }); + + plots.RegisterPlot("ice_candidate_pair_config", [&](Plot* plot) { + analyzer.CreateIceCandidatePairConfigGraph(plot); + }); + plots.RegisterPlot("ice_connectivity_check", [&](Plot* plot) { + analyzer.CreateIceConnectivityCheckGraph(plot); + }); + plots.RegisterPlot("dtls_transport_state", [&](Plot* plot) { + analyzer.CreateDtlsTransportStateGraph(plot); + }); + plots.RegisterPlot("dtls_writable_state", [&](Plot* plot) { + analyzer.CreateDtlsWritableStateGraph(plot); + }); + + std::string wav_path; + if (FLAG_wav_filename[0] != '\0') { + wav_path = FLAG_wav_filename; + } else { + wav_path = webrtc::test::ResourcePath( + "audio_processing/conversational_speech/EN_script2_F_sp2_B1", "wav"); } - if (FLAG_plot_audio_encoder_bitrate_bps) { - analyzer.CreateAudioEncoderTargetBitrateGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_audio_encoder_frame_length_ms) { - analyzer.CreateAudioEncoderFrameLengthGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_audio_encoder_packet_loss) { - analyzer.CreateAudioEncoderPacketLossGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_audio_encoder_fec) { - analyzer.CreateAudioEncoderEnableFecGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_audio_encoder_dtx) { - analyzer.CreateAudioEncoderEnableDtxGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_audio_encoder_num_channels) { - analyzer.CreateAudioEncoderNumChannelsGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_neteq_stats) { - std::string wav_path; - if (FLAG_wav_filename[0] != '\0') { - wav_path = FLAG_wav_filename; - } else { - wav_path = webrtc::test::ResourcePath( - "audio_processing/conversational_speech/EN_script2_F_sp2_B1", "wav"); - } - auto neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); - for (webrtc::EventLogAnalyzer::NetEqStatsGetterMap::const_iterator it = - neteq_stats.cbegin(); - it != neteq_stats.cend(); ++it) { - analyzer.CreateAudioJitterBufferGraph(it->first, it->second.get(), - collection->AppendNewPlot()); + absl::optional neteq_stats; + + plots.RegisterPlot("simulated_neteq_expand_rate", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); } analyzer.CreateNetEqNetworkStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqNetworkStatistics& stats) { return stats.expand_rate / 16384.f; }, - "Expand rate", collection->AppendNewPlot()); + "Expand rate", plot); + }); + + plots.RegisterPlot("simulated_neteq_speech_expand_rate", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } analyzer.CreateNetEqNetworkStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqNetworkStatistics& stats) { return stats.speech_expand_rate / 16384.f; }, - "Speech expand rate", collection->AppendNewPlot()); + "Speech expand rate", plot); + }); + + plots.RegisterPlot("simulated_neteq_accelerate_rate", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } analyzer.CreateNetEqNetworkStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqNetworkStatistics& stats) { return stats.accelerate_rate / 16384.f; }, - "Accelerate rate", collection->AppendNewPlot()); + "Accelerate rate", plot); + }); + + plots.RegisterPlot("simulated_neteq_preemptive_rate", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } analyzer.CreateNetEqNetworkStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqNetworkStatistics& stats) { return stats.preemptive_rate / 16384.f; }, - "Preemptive rate", collection->AppendNewPlot()); + "Preemptive rate", plot); + }); + + plots.RegisterPlot("simulated_neteq_packet_loss_rate", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } analyzer.CreateNetEqNetworkStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqNetworkStatistics& stats) { return stats.packet_loss_rate / 16384.f; }, - "Packet loss rate", collection->AppendNewPlot()); + "Packet loss rate", plot); + }); + + plots.RegisterPlot("simulated_neteq_concealment_events", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } analyzer.CreateNetEqLifetimeStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqLifetimeStatistics& stats) { return static_cast(stats.concealment_events); }, - "Concealment events", collection->AppendNewPlot()); + "Concealment events", plot); + }); + + plots.RegisterPlot("simulated_neteq_preferred_buffer_size", [&](Plot* plot) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } analyzer.CreateNetEqNetworkStatsGraph( - neteq_stats, + *neteq_stats, [](const webrtc::NetEqNetworkStatistics& stats) { return stats.preferred_buffer_size_ms; }, - "Preferred buffer size (ms)", collection->AppendNewPlot()); + "Preferred buffer size (ms)", plot); + }); + + if (absl::c_find(plot_flags, "all") != plot_flags.end()) { + plots.EnableAllPlots(); + // Treated separately since it isn't registered like the other plots. + plot_flags.push_back("simulated_neteq_jitter_buffer_delay"); + } else { + bool success = plots.EnablePlotsByFlags(plot_flags, flag_aliases); + if (!success) { + return 1; + } } - if (FLAG_plot_ice_candidate_pair_config) { - analyzer.CreateIceCandidatePairConfigGraph(collection->AppendNewPlot()); - } - if (FLAG_plot_ice_connectivity_check) { - analyzer.CreateIceConnectivityCheckGraph(collection->AppendNewPlot()); + if (argc != 2 || FLAG_help) { + // Print usage information. + std::cerr << usage; + if (FLAG_help) { + rtc::FlagList::Print(nullptr, false); + std::cerr << "List of registered plots (for use with the --plot flag):" + << std::endl; + for (const auto& plot : plots) { + // TODO(terelius): Also print a help text. + std::cerr << " " << plot.label << std::endl; + } + // The following flag doesn't fit the model used for the other plots. + std::cerr << "simulated_neteq_jitter_buffer_delay" << std::endl; + std::cerr << "List of plot aliases (for use with the --plot flag):" + << std::endl; + for (const auto& alias : flag_aliases) { + std::cerr << " " << alias.first << " = "; + for (const auto& replacement : alias.second) { + std::cerr << replacement << ","; + } + std::cerr << std::endl; + } + } + return 0; } - if (FLAG_plot_dtls_transport_state) { - analyzer.CreateDtlsTransportStateGraph(collection->AppendNewPlot()); + for (const auto& plot : plots) { + if (plot.enabled) { + Plot* output = collection->AppendNewPlot(); + plot.plot_func(output); + output->SetId(plot.label); + } } - if (FLAG_plot_dtls_writable_state) { - analyzer.CreateDtlsWritableStateGraph(collection->AppendNewPlot()); + + // The model we use for registering plots assumes that the each plot label + // can be mapped to a lambda that will produce exactly one plot. The + // simulated_neteq_jitter_buffer_delay plot doesn't fit this model since it + // creates multiple plots, and would need some state kept between the lambda + // calls. + if (absl::c_find(plot_flags, "simulated_neteq_jitter_buffer_delay") != + plot_flags.end()) { + if (!neteq_stats) { + neteq_stats = analyzer.SimulateNetEq(wav_path, 48000); + } + for (webrtc::EventLogAnalyzer::NetEqStatsGetterMap::const_iterator it = + neteq_stats->cbegin(); + it != neteq_stats->cend(); ++it) { + analyzer.CreateAudioJitterBufferGraph(it->first, it->second.get(), + collection->AppendNewPlot()); + } } collection->Draw(); @@ -547,39 +586,3 @@ int main(int argc, char* argv[]) { return 0; } -void SetAllPlotFlags(bool setting) { - FLAG_plot_incoming_packet_sizes = setting; - FLAG_plot_outgoing_packet_sizes = setting; - FLAG_plot_incoming_rtcp_types = setting; - FLAG_plot_outgoing_rtcp_types = setting; - FLAG_plot_incoming_packet_count = setting; - FLAG_plot_outgoing_packet_count = setting; - FLAG_plot_audio_playout = setting; - FLAG_plot_audio_level = setting; - FLAG_plot_incoming_sequence_number_delta = setting; - FLAG_plot_incoming_delay = setting; - FLAG_plot_incoming_loss_rate = setting; - FLAG_plot_incoming_bitrate = setting; - FLAG_plot_outgoing_bitrate = setting; - FLAG_plot_incoming_stream_bitrate = setting; - FLAG_plot_outgoing_stream_bitrate = setting; - FLAG_plot_incoming_layer_bitrate_allocation = setting; - FLAG_plot_outgoing_layer_bitrate_allocation = setting; - FLAG_plot_simulated_receiveside_bwe = setting; - FLAG_plot_simulated_sendside_bwe = setting; - FLAG_plot_simulated_goog_cc = setting; - FLAG_plot_network_delay_feedback = setting; - FLAG_plot_fraction_loss_feedback = setting; - FLAG_plot_timestamps = setting; - FLAG_plot_rtcp_details = setting; - FLAG_plot_audio_encoder_bitrate_bps = setting; - FLAG_plot_audio_encoder_frame_length_ms = setting; - FLAG_plot_audio_encoder_packet_loss = setting; - FLAG_plot_audio_encoder_fec = setting; - FLAG_plot_audio_encoder_dtx = setting; - FLAG_plot_audio_encoder_num_channels = setting; - FLAG_plot_neteq_stats = setting; - FLAG_plot_ice_candidate_pair_config = setting; - FLAG_plot_ice_connectivity_check = setting; - FLAG_plot_pacer_delay = setting; -} diff --git a/rtc_tools/event_log_visualizer/plot_base.cc b/rtc_tools/event_log_visualizer/plot_base.cc index 9a21393792..8313beb98d 100644 --- a/rtc_tools/event_log_visualizer/plot_base.cc +++ b/rtc_tools/event_log_visualizer/plot_base.cc @@ -66,10 +66,14 @@ void Plot::SetSuggestedYAxis(float min_value, SetYAxis(min_value, max_value, label, bottom_margin, top_margin); } -void Plot::SetTitle(std::string title) { +void Plot::SetTitle(const std::string& title) { title_ = title; } +void Plot::SetId(const std::string& id) { + id_ = id; +} + void Plot::AppendTimeSeries(TimeSeries&& time_series) { series_list_.emplace_back(std::move(time_series)); } diff --git a/rtc_tools/event_log_visualizer/plot_base.h b/rtc_tools/event_log_visualizer/plot_base.h index e73f004937..72fa575ea6 100644 --- a/rtc_tools/event_log_visualizer/plot_base.h +++ b/rtc_tools/event_log_visualizer/plot_base.h @@ -138,7 +138,12 @@ class Plot { float top_margin = 0); // Sets the title of the plot. - void SetTitle(std::string title); + void SetTitle(const std::string& title); + + // Sets an unique ID for the plot. The ID is similar to the title except that + // the title might change in future releases whereas the ID should be stable + // over time. + void SetId(const std::string& id); // Add a new TimeSeries to the plot. void AppendTimeSeries(TimeSeries&& time_series); @@ -158,6 +163,7 @@ class Plot { float yaxis_max_; std::string yaxis_label_; std::string title_; + std::string id_; std::vector series_list_; std::vector interval_list_; }; diff --git a/rtc_tools/event_log_visualizer/plot_protobuf.cc b/rtc_tools/event_log_visualizer/plot_protobuf.cc index 309214b2a9..9dc61f79a3 100644 --- a/rtc_tools/event_log_visualizer/plot_protobuf.cc +++ b/rtc_tools/event_log_visualizer/plot_protobuf.cc @@ -58,6 +58,7 @@ void ProtobufPlot::ExportProtobuf(webrtc::analytics::Chart* chart) { chart->set_xaxis_label(xaxis_label_); chart->set_yaxis_label(yaxis_label_); chart->set_title(title_); + chart->set_id(id_); } ProtobufPlotCollection::ProtobufPlotCollection() {} diff --git a/rtc_tools/event_log_visualizer/proto/chart.proto b/rtc_tools/event_log_visualizer/proto/chart.proto index 3a11d78671..aa518a767d 100644 --- a/rtc_tools/event_log_visualizer/proto/chart.proto +++ b/rtc_tools/event_log_visualizer/proto/chart.proto @@ -22,6 +22,7 @@ message Chart { float yaxis_max = 6; string yaxis_label = 7; string title = 8; + string id = 9; } message ChartCollection {