xref: /aosp_15_r20/external/webrtc/rtc_tools/rtc_event_log_visualizer/main.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <stdio.h>
12 #include <string.h>
13 
14 #include <cstdio>
15 #include <fstream>
16 #include <iostream>
17 #include <map>
18 #include <memory>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/algorithm/container.h"
24 #include "absl/flags/flag.h"
25 #include "absl/flags/parse.h"
26 #include "absl/flags/usage.h"
27 #include "absl/flags/usage_config.h"
28 #include "absl/strings/match.h"
29 #include "api/neteq/neteq.h"
30 #include "api/rtc_event_log/rtc_event_log.h"
31 #include "logging/rtc_event_log/rtc_event_log_parser.h"
32 #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
33 #include "rtc_base/checks.h"
34 #include "rtc_base/logging.h"
35 #include "rtc_tools/rtc_event_log_visualizer/alerts.h"
36 #include "rtc_tools/rtc_event_log_visualizer/analyze_audio.h"
37 #include "rtc_tools/rtc_event_log_visualizer/analyzer.h"
38 #include "rtc_tools/rtc_event_log_visualizer/conversational_speech_en.h"
39 #include "rtc_tools/rtc_event_log_visualizer/plot_base.h"
40 #include "system_wrappers/include/field_trial.h"
41 
42 ABSL_FLAG(std::string,
43           plot,
44           "default",
45           "A comma separated list of plot names. See --list_plots for valid "
46           "options.");
47 
48 ABSL_FLAG(
49     std::string,
50     force_fieldtrials,
51     "",
52     "Field trials control experimental feature code which can be forced. "
53     "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enabled/"
54     " will assign the group Enabled to field trial WebRTC-FooFeature. Multiple "
55     "trials are separated by \"/\"");
56 ABSL_FLAG(std::string,
57           wav_filename,
58           "",
59           "Path to wav file used for simulation of jitter buffer");
60 
61 ABSL_FLAG(bool,
62           show_detector_state,
63           false,
64           "Show the state of the delay based BWE detector on the total "
65           "bitrate graph");
66 
67 ABSL_FLAG(bool,
68           show_alr_state,
69           false,
70           "Show the state ALR state on the total bitrate graph");
71 
72 ABSL_FLAG(bool,
73           show_link_capacity,
74           true,
75           "Show the lower and upper link capacity on the outgoing bitrate "
76           "graph");
77 
78 ABSL_FLAG(bool,
79           parse_unconfigured_header_extensions,
80           true,
81           "Attempt to parse unconfigured header extensions using the default "
82           "WebRTC mapping. This can give very misleading results if the "
83           "application negotiates a different mapping.");
84 
85 ABSL_FLAG(bool,
86           print_triage_alerts,
87           true,
88           "Print triage alerts, i.e. a list of potential problems.");
89 
90 ABSL_FLAG(bool,
91           normalize_time,
92           true,
93           "Normalize the log timestamps so that the call starts at time 0.");
94 
95 ABSL_FLAG(bool,
96           shared_xaxis,
97           false,
98           "Share x-axis between all plots so that zooming in one plot "
99           "updates all the others too. A downside is that certain "
100           "operations like panning become much slower.");
101 
102 ABSL_FLAG(bool,
103           protobuf_output,
104           false,
105           "Output charts as protobuf instead of python code.");
106 
107 ABSL_FLAG(bool,
108           list_plots,
109           false,
110           "List of registered plots (for use with the --plot flag)");
111 
112 using webrtc::Plot;
113 
114 namespace {
StrSplit(const std::string & s,const std::string & delimiter)115 std::vector<std::string> StrSplit(const std::string& s,
116                                   const std::string& delimiter) {
117   std::vector<std::string> v;
118   size_t pos = 0;
119   while (pos < s.length()) {
120     const std::string token = s.substr(pos, s.find(delimiter, pos) - pos);
121     pos += token.length() + delimiter.length();
122     v.push_back(token);
123   }
124   return v;
125 }
126 
127 struct PlotDeclaration {
PlotDeclaration__anon191a54050111::PlotDeclaration128   PlotDeclaration(const std::string& label, std::function<void(Plot*)> f)
129       : label(label), enabled(false), plot_func(f) {}
130   const std::string label;
131   bool enabled;
132   // TODO(terelius): Add a help text/explanation.
133   const std::function<void(Plot*)> plot_func;
134 };
135 
136 class PlotMap {
137  public:
RegisterPlot(const std::string & label,std::function<void (Plot *)> f)138   void RegisterPlot(const std::string& label, std::function<void(Plot*)> f) {
139     for (const auto& plot : plots_) {
140       RTC_DCHECK(plot.label != label)
141           << "Can't use the same label for multiple plots";
142     }
143     plots_.push_back({label, f});
144   }
145 
EnablePlotsByFlags(const std::vector<std::string> & flags,const std::map<std::string,std::vector<std::string>> & flag_aliases)146   bool EnablePlotsByFlags(
147       const std::vector<std::string>& flags,
148       const std::map<std::string, std::vector<std::string>>& flag_aliases) {
149     bool status = true;
150     for (const std::string& flag : flags) {
151       auto alias_it = flag_aliases.find(flag);
152       if (alias_it != flag_aliases.end()) {
153         const auto& replacements = alias_it->second;
154         for (const auto& replacement : replacements) {
155           status &= EnablePlotByFlag(replacement);
156         }
157       } else {
158         status &= EnablePlotByFlag(flag);
159       }
160     }
161     return status;
162   }
163 
EnableAllPlots()164   void EnableAllPlots() {
165     for (auto& plot : plots_) {
166       plot.enabled = true;
167     }
168   }
169 
begin()170   std::vector<PlotDeclaration>::iterator begin() { return plots_.begin(); }
end()171   std::vector<PlotDeclaration>::iterator end() { return plots_.end(); }
172 
173  private:
EnablePlotByFlag(const std::string & flag)174   bool EnablePlotByFlag(const std::string& flag) {
175     for (auto& plot : plots_) {
176       if (plot.label == flag) {
177         plot.enabled = true;
178         return true;
179       }
180     }
181     if (flag == "simulated_neteq_jitter_buffer_delay") {
182       // This flag is handled separately.
183       return true;
184     }
185     std::cerr << "Unrecognized plot name \'" << flag << "\'. Aborting."
186               << std::endl;
187     return false;
188   }
189 
190   std::vector<PlotDeclaration> plots_;
191 };
192 
ContainsHelppackageFlags(absl::string_view filename)193 bool ContainsHelppackageFlags(absl::string_view filename) {
194   return absl::EndsWith(filename, "main.cc");
195 }
196 
197 }  // namespace
198 
main(int argc,char * argv[])199 int main(int argc, char* argv[]) {
200   absl::SetProgramUsageMessage(
201       "A tool for visualizing WebRTC event logs.\n"
202       "Example usage:\n"
203       "./event_log_visualizer <logfile> | python\n");
204   absl::FlagsUsageConfig flag_config;
205   flag_config.contains_help_flags = &ContainsHelppackageFlags;
206   absl::SetFlagsUsageConfig(flag_config);
207   std::vector<char*> args = absl::ParseCommandLine(argc, argv);
208 
209   // Print RTC_LOG warnings and errors even in release builds.
210   if (rtc::LogMessage::GetLogToDebug() > rtc::LS_WARNING) {
211     rtc::LogMessage::LogToDebug(rtc::LS_WARNING);
212   }
213   rtc::LogMessage::SetLogToStderr(true);
214 
215   // Flag replacements
216   std::map<std::string, std::vector<std::string>> flag_aliases = {
217       {"default",
218        {"incoming_delay", "incoming_loss_rate", "incoming_bitrate",
219         "outgoing_bitrate", "incoming_stream_bitrate",
220         "outgoing_stream_bitrate", "network_delay_feedback",
221         "fraction_loss_feedback"}},
222       {"sendside_bwe",
223        {"outgoing_packet_sizes", "outgoing_bitrate", "outgoing_stream_bitrate",
224         "simulated_sendside_bwe", "network_delay_feedback",
225         "fraction_loss_feedback"}},
226       {"receiveside_bwe",
227        {"incoming_packet_sizes", "incoming_delay", "incoming_loss_rate",
228         "incoming_bitrate", "incoming_stream_bitrate",
229         "simulated_receiveside_bwe"}},
230       {"rtcp_details",
231        {"incoming_rtcp_fraction_lost", "outgoing_rtcp_fraction_lost",
232         "incoming_rtcp_cumulative_lost", "outgoing_rtcp_cumulative_lost",
233         "incoming_rtcp_highest_seq_number", "outgoing_rtcp_highest_seq_number",
234         "incoming_rtcp_delay_since_last_sr",
235         "outgoing_rtcp_delay_since_last_sr"}},
236       {"simulated_neteq_stats",
237        {"simulated_neteq_jitter_buffer_delay",
238         "simulated_neteq_preferred_buffer_size",
239         "simulated_neteq_concealment_events", "simulated_neteq_preemptive_rate",
240         "simulated_neteq_accelerate_rate", "simulated_neteq_speech_expand_rate",
241         "simulated_neteq_expand_rate"}}};
242 
243   std::vector<std::string> plot_flags =
244       StrSplit(absl::GetFlag(FLAGS_plot), ",");
245 
246   // InitFieldTrialsFromString stores the char*, so the char array must outlive
247   // the application.
248   const std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
249   webrtc::field_trial::InitFieldTrialsFromString(field_trials.c_str());
250 
251   webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions header_extensions =
252       webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions::kDontParse;
253   if (absl::GetFlag(FLAGS_parse_unconfigured_header_extensions)) {
254     header_extensions = webrtc::ParsedRtcEventLog::
255         UnconfiguredHeaderExtensions::kAttemptWebrtcDefaultConfig;
256   }
257   webrtc::ParsedRtcEventLog parsed_log(header_extensions,
258                                        /*allow_incomplete_logs*/ true);
259 
260   if (args.size() == 2) {
261     std::string filename = args[1];
262     auto status = parsed_log.ParseFile(filename);
263     if (!status.ok()) {
264       std::cerr << "Failed to parse " << filename << ": " << status.message()
265                 << std::endl;
266       return -1;
267     }
268   }
269 
270   webrtc::AnalyzerConfig config;
271   config.window_duration_ = webrtc::TimeDelta::Millis(250);
272   config.step_ = webrtc::TimeDelta::Millis(10);
273   if (!parsed_log.start_log_events().empty()) {
274     config.rtc_to_utc_offset_ = parsed_log.start_log_events()[0].utc_time() -
275                                 parsed_log.start_log_events()[0].log_time();
276   }
277   config.normalize_time_ = absl::GetFlag(FLAGS_normalize_time);
278   config.begin_time_ = parsed_log.first_timestamp();
279   config.end_time_ = parsed_log.last_timestamp();
280   if (config.end_time_ < config.begin_time_) {
281     RTC_LOG(LS_WARNING) << "Log end time " << config.end_time_
282                         << " not after begin time " << config.begin_time_
283                         << ". Nothing to analyze. Is the log broken?";
284     return -1;
285   }
286 
287   webrtc::EventLogAnalyzer analyzer(parsed_log, config);
288   webrtc::PlotCollection collection;
289   collection.SetCallTimeToUtcOffsetMs(config.CallTimeToUtcOffsetMs());
290 
291   PlotMap plots;
292   plots.RegisterPlot("incoming_packet_sizes", [&](Plot* plot) {
293     analyzer.CreatePacketGraph(webrtc::kIncomingPacket, plot);
294   });
295 
296   plots.RegisterPlot("outgoing_packet_sizes", [&](Plot* plot) {
297     analyzer.CreatePacketGraph(webrtc::kOutgoingPacket, plot);
298   });
299   plots.RegisterPlot("incoming_rtcp_types", [&](Plot* plot) {
300     analyzer.CreateRtcpTypeGraph(webrtc::kIncomingPacket, plot);
301   });
302   plots.RegisterPlot("outgoing_rtcp_types", [&](Plot* plot) {
303     analyzer.CreateRtcpTypeGraph(webrtc::kOutgoingPacket, plot);
304   });
305   plots.RegisterPlot("incoming_packet_count", [&](Plot* plot) {
306     analyzer.CreateAccumulatedPacketsGraph(webrtc::kIncomingPacket, plot);
307   });
308   plots.RegisterPlot("outgoing_packet_count", [&](Plot* plot) {
309     analyzer.CreateAccumulatedPacketsGraph(webrtc::kOutgoingPacket, plot);
310   });
311   plots.RegisterPlot("incoming_packet_rate", [&](Plot* plot) {
312     analyzer.CreatePacketRateGraph(webrtc::kIncomingPacket, plot);
313   });
314   plots.RegisterPlot("outgoing_packet_rate", [&](Plot* plot) {
315     analyzer.CreatePacketRateGraph(webrtc::kOutgoingPacket, plot);
316   });
317   plots.RegisterPlot("total_incoming_packet_rate", [&](Plot* plot) {
318     analyzer.CreateTotalPacketRateGraph(webrtc::kIncomingPacket, plot);
319   });
320   plots.RegisterPlot("total_outgoing_packet_rate", [&](Plot* plot) {
321     analyzer.CreateTotalPacketRateGraph(webrtc::kOutgoingPacket, plot);
322   });
323   plots.RegisterPlot("audio_playout",
324                      [&](Plot* plot) { analyzer.CreatePlayoutGraph(plot); });
325   plots.RegisterPlot("incoming_audio_level", [&](Plot* plot) {
326     analyzer.CreateAudioLevelGraph(webrtc::kIncomingPacket, plot);
327   });
328   plots.RegisterPlot("outgoing_audio_level", [&](Plot* plot) {
329     analyzer.CreateAudioLevelGraph(webrtc::kOutgoingPacket, plot);
330   });
331   plots.RegisterPlot("incoming_sequence_number_delta", [&](Plot* plot) {
332     analyzer.CreateSequenceNumberGraph(plot);
333   });
334   plots.RegisterPlot("incoming_delay", [&](Plot* plot) {
335     analyzer.CreateIncomingDelayGraph(plot);
336   });
337   plots.RegisterPlot("incoming_loss_rate", [&](Plot* plot) {
338     analyzer.CreateIncomingPacketLossGraph(plot);
339   });
340   plots.RegisterPlot("incoming_bitrate", [&](Plot* plot) {
341     analyzer.CreateTotalIncomingBitrateGraph(plot);
342   });
343   plots.RegisterPlot("outgoing_bitrate", [&](Plot* plot) {
344     analyzer.CreateTotalOutgoingBitrateGraph(
345         plot, absl::GetFlag(FLAGS_show_detector_state),
346         absl::GetFlag(FLAGS_show_alr_state),
347         absl::GetFlag(FLAGS_show_link_capacity));
348   });
349   plots.RegisterPlot("incoming_stream_bitrate", [&](Plot* plot) {
350     analyzer.CreateStreamBitrateGraph(webrtc::kIncomingPacket, plot);
351   });
352   plots.RegisterPlot("outgoing_stream_bitrate", [&](Plot* plot) {
353     analyzer.CreateStreamBitrateGraph(webrtc::kOutgoingPacket, plot);
354   });
355   plots.RegisterPlot("incoming_layer_bitrate_allocation", [&](Plot* plot) {
356     analyzer.CreateBitrateAllocationGraph(webrtc::kIncomingPacket, plot);
357   });
358   plots.RegisterPlot("outgoing_layer_bitrate_allocation", [&](Plot* plot) {
359     analyzer.CreateBitrateAllocationGraph(webrtc::kOutgoingPacket, plot);
360   });
361   plots.RegisterPlot("simulated_receiveside_bwe", [&](Plot* plot) {
362     analyzer.CreateReceiveSideBweSimulationGraph(plot);
363   });
364   plots.RegisterPlot("simulated_sendside_bwe", [&](Plot* plot) {
365     analyzer.CreateSendSideBweSimulationGraph(plot);
366   });
367   plots.RegisterPlot("simulated_goog_cc", [&](Plot* plot) {
368     analyzer.CreateGoogCcSimulationGraph(plot);
369   });
370   plots.RegisterPlot("network_delay_feedback", [&](Plot* plot) {
371     analyzer.CreateNetworkDelayFeedbackGraph(plot);
372   });
373   plots.RegisterPlot("fraction_loss_feedback", [&](Plot* plot) {
374     analyzer.CreateFractionLossGraph(plot);
375   });
376   plots.RegisterPlot("incoming_timestamps", [&](Plot* plot) {
377     analyzer.CreateTimestampGraph(webrtc::kIncomingPacket, plot);
378   });
379   plots.RegisterPlot("outgoing_timestamps", [&](Plot* plot) {
380     analyzer.CreateTimestampGraph(webrtc::kOutgoingPacket, plot);
381   });
382 
383   auto GetFractionLost = [](const webrtc::rtcp::ReportBlock& block) -> float {
384     return static_cast<double>(block.fraction_lost()) / 256 * 100;
385   };
386   plots.RegisterPlot("incoming_rtcp_fraction_lost", [&](Plot* plot) {
387     analyzer.CreateSenderAndReceiverReportPlot(
388         webrtc::kIncomingPacket, GetFractionLost,
389         "Fraction lost (incoming RTCP)", "Loss rate (percent)", plot);
390   });
391   plots.RegisterPlot("outgoing_rtcp_fraction_lost", [&](Plot* plot) {
392     analyzer.CreateSenderAndReceiverReportPlot(
393         webrtc::kOutgoingPacket, GetFractionLost,
394         "Fraction lost (outgoing RTCP)", "Loss rate (percent)", plot);
395   });
396   auto GetCumulativeLost = [](const webrtc::rtcp::ReportBlock& block) -> float {
397     return block.cumulative_lost_signed();
398   };
399   plots.RegisterPlot("incoming_rtcp_cumulative_lost", [&](Plot* plot) {
400     analyzer.CreateSenderAndReceiverReportPlot(
401         webrtc::kIncomingPacket, GetCumulativeLost,
402         "Cumulative lost packets (incoming RTCP)", "Packets", plot);
403   });
404   plots.RegisterPlot("outgoing_rtcp_cumulative_lost", [&](Plot* plot) {
405     analyzer.CreateSenderAndReceiverReportPlot(
406         webrtc::kOutgoingPacket, GetCumulativeLost,
407         "Cumulative lost packets (outgoing RTCP)", "Packets", plot);
408   });
409 
410   auto GetHighestSeqNumber =
411       [](const webrtc::rtcp::ReportBlock& block) -> float {
412     return block.extended_high_seq_num();
413   };
414   plots.RegisterPlot("incoming_rtcp_highest_seq_number", [&](Plot* plot) {
415     analyzer.CreateSenderAndReceiverReportPlot(
416         webrtc::kIncomingPacket, GetHighestSeqNumber,
417         "Highest sequence number (incoming RTCP)", "Sequence number", plot);
418   });
419   plots.RegisterPlot("outgoing_rtcp_highest_seq_number", [&](Plot* plot) {
420     analyzer.CreateSenderAndReceiverReportPlot(
421         webrtc::kOutgoingPacket, GetHighestSeqNumber,
422         "Highest sequence number (outgoing RTCP)", "Sequence number", plot);
423   });
424 
425   auto DelaySinceLastSr = [](const webrtc::rtcp::ReportBlock& block) -> float {
426     return static_cast<double>(block.delay_since_last_sr()) / 65536;
427   };
428   plots.RegisterPlot("incoming_rtcp_delay_since_last_sr", [&](Plot* plot) {
429     analyzer.CreateSenderAndReceiverReportPlot(
430         webrtc::kIncomingPacket, DelaySinceLastSr,
431         "Delay since last received sender report (incoming RTCP)", "Time (s)",
432         plot);
433   });
434   plots.RegisterPlot("outgoing_rtcp_delay_since_last_sr", [&](Plot* plot) {
435     analyzer.CreateSenderAndReceiverReportPlot(
436         webrtc::kOutgoingPacket, DelaySinceLastSr,
437         "Delay since last received sender report (outgoing RTCP)", "Time (s)",
438         plot);
439   });
440 
441   plots.RegisterPlot("pacer_delay",
442                      [&](Plot* plot) { analyzer.CreatePacerDelayGraph(plot); });
443   plots.RegisterPlot("audio_encoder_bitrate", [&](Plot* plot) {
444     CreateAudioEncoderTargetBitrateGraph(parsed_log, config, plot);
445   });
446   plots.RegisterPlot("audio_encoder_frame_length", [&](Plot* plot) {
447     CreateAudioEncoderFrameLengthGraph(parsed_log, config, plot);
448   });
449   plots.RegisterPlot("audio_encoder_packet_loss", [&](Plot* plot) {
450     CreateAudioEncoderPacketLossGraph(parsed_log, config, plot);
451   });
452   plots.RegisterPlot("audio_encoder_fec", [&](Plot* plot) {
453     CreateAudioEncoderEnableFecGraph(parsed_log, config, plot);
454   });
455   plots.RegisterPlot("audio_encoder_dtx", [&](Plot* plot) {
456     CreateAudioEncoderEnableDtxGraph(parsed_log, config, plot);
457   });
458   plots.RegisterPlot("audio_encoder_num_channels", [&](Plot* plot) {
459     CreateAudioEncoderNumChannelsGraph(parsed_log, config, plot);
460   });
461 
462   plots.RegisterPlot("ice_candidate_pair_config", [&](Plot* plot) {
463     analyzer.CreateIceCandidatePairConfigGraph(plot);
464   });
465   plots.RegisterPlot("ice_connectivity_check", [&](Plot* plot) {
466     analyzer.CreateIceConnectivityCheckGraph(plot);
467   });
468   plots.RegisterPlot("dtls_transport_state", [&](Plot* plot) {
469     analyzer.CreateDtlsTransportStateGraph(plot);
470   });
471   plots.RegisterPlot("dtls_writable_state", [&](Plot* plot) {
472     analyzer.CreateDtlsWritableStateGraph(plot);
473   });
474 
475   std::string wav_path;
476   bool has_generated_wav_file = false;
477   if (!absl::GetFlag(FLAGS_wav_filename).empty()) {
478     wav_path = absl::GetFlag(FLAGS_wav_filename);
479   } else {
480     // TODO(bugs.webrtc.org/14248): Remove the need to generate a file
481     // and read the file directly from memory.
482     wav_path = std::tmpnam(nullptr);
483     std::ofstream out_wav_file(wav_path);
484     out_wav_file.write(
485         reinterpret_cast<char*>(&webrtc::conversational_speech_en_wav[0]),
486         webrtc::conversational_speech_en_wav_len);
487     has_generated_wav_file = true;
488   }
489   absl::optional<webrtc::NetEqStatsGetterMap> neteq_stats;
490 
491   plots.RegisterPlot("simulated_neteq_expand_rate", [&](Plot* plot) {
492     if (!neteq_stats) {
493       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
494     }
495     webrtc::CreateNetEqNetworkStatsGraph(
496         parsed_log, config, *neteq_stats,
497         [](const webrtc::NetEqNetworkStatistics& stats) {
498           return stats.expand_rate / 16384.f;
499         },
500         "Expand rate", plot);
501   });
502 
503   plots.RegisterPlot("simulated_neteq_speech_expand_rate", [&](Plot* plot) {
504     if (!neteq_stats) {
505       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
506     }
507     webrtc::CreateNetEqNetworkStatsGraph(
508         parsed_log, config, *neteq_stats,
509         [](const webrtc::NetEqNetworkStatistics& stats) {
510           return stats.speech_expand_rate / 16384.f;
511         },
512         "Speech expand rate", plot);
513   });
514 
515   plots.RegisterPlot("simulated_neteq_accelerate_rate", [&](Plot* plot) {
516     if (!neteq_stats) {
517       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
518     }
519     webrtc::CreateNetEqNetworkStatsGraph(
520         parsed_log, config, *neteq_stats,
521         [](const webrtc::NetEqNetworkStatistics& stats) {
522           return stats.accelerate_rate / 16384.f;
523         },
524         "Accelerate rate", plot);
525   });
526 
527   plots.RegisterPlot("simulated_neteq_preemptive_rate", [&](Plot* plot) {
528     if (!neteq_stats) {
529       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
530     }
531     webrtc::CreateNetEqNetworkStatsGraph(
532         parsed_log, config, *neteq_stats,
533         [](const webrtc::NetEqNetworkStatistics& stats) {
534           return stats.preemptive_rate / 16384.f;
535         },
536         "Preemptive rate", plot);
537   });
538 
539   plots.RegisterPlot("simulated_neteq_concealment_events", [&](Plot* plot) {
540     if (!neteq_stats) {
541       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
542     }
543     webrtc::CreateNetEqLifetimeStatsGraph(
544         parsed_log, config, *neteq_stats,
545         [](const webrtc::NetEqLifetimeStatistics& stats) {
546           return static_cast<float>(stats.concealment_events);
547         },
548         "Concealment events", plot);
549   });
550 
551   plots.RegisterPlot("simulated_neteq_preferred_buffer_size", [&](Plot* plot) {
552     if (!neteq_stats) {
553       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
554     }
555     webrtc::CreateNetEqNetworkStatsGraph(
556         parsed_log, config, *neteq_stats,
557         [](const webrtc::NetEqNetworkStatistics& stats) {
558           return stats.preferred_buffer_size_ms;
559         },
560         "Preferred buffer size (ms)", plot);
561   });
562 
563   if (absl::c_find(plot_flags, "all") != plot_flags.end()) {
564     plots.EnableAllPlots();
565     // Treated separately since it isn't registered like the other plots.
566     plot_flags.push_back("simulated_neteq_jitter_buffer_delay");
567   } else {
568     bool success = plots.EnablePlotsByFlags(plot_flags, flag_aliases);
569     if (!success) {
570       return 1;
571     }
572   }
573 
574   if (absl::GetFlag(FLAGS_list_plots)) {
575     std::cerr << "List of registered plots (for use with the --plot flag):"
576               << std::endl;
577     for (const auto& plot : plots) {
578       // TODO(terelius): Also print a help text.
579       std::cerr << "  " << plot.label << std::endl;
580     }
581     // The following flag doesn't fit the model used for the other plots.
582     std::cerr << "simulated_neteq_jitter_buffer_delay" << std::endl;
583     std::cerr << "List of plot aliases (for use with the --plot flag):"
584               << std::endl;
585     std::cerr << "  all = every registered plot" << std::endl;
586     for (const auto& alias : flag_aliases) {
587       std::cerr << "  " << alias.first << " = ";
588       for (const auto& replacement : alias.second) {
589         std::cerr << replacement << ",";
590       }
591       std::cerr << std::endl;
592     }
593     return 0;
594   }
595   if (args.size() != 2) {
596     // Print usage information.
597     std::cerr << absl::ProgramUsageMessage();
598     return 1;
599   }
600 
601   for (const auto& plot : plots) {
602     if (plot.enabled) {
603       Plot* output = collection.AppendNewPlot();
604       plot.plot_func(output);
605       output->SetId(plot.label);
606     }
607   }
608 
609   // The model we use for registering plots assumes that the each plot label
610   // can be mapped to a lambda that will produce exactly one plot. The
611   // simulated_neteq_jitter_buffer_delay plot doesn't fit this model since it
612   // creates multiple plots, and would need some state kept between the lambda
613   // calls.
614   if (absl::c_find(plot_flags, "simulated_neteq_jitter_buffer_delay") !=
615       plot_flags.end()) {
616     if (!neteq_stats) {
617       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
618     }
619     for (webrtc::NetEqStatsGetterMap::const_iterator it = neteq_stats->cbegin();
620          it != neteq_stats->cend(); ++it) {
621       webrtc::CreateAudioJitterBufferGraph(parsed_log, config, it->first,
622                                            it->second.get(),
623                                            collection.AppendNewPlot());
624     }
625   }
626 
627   if (absl::GetFlag(FLAGS_protobuf_output)) {
628     webrtc::analytics::ChartCollection proto_charts;
629     collection.ExportProtobuf(&proto_charts);
630     std::cout << proto_charts.SerializeAsString();
631   } else {
632     collection.PrintPythonCode(absl::GetFlag(FLAGS_shared_xaxis));
633   }
634 
635   if (absl::GetFlag(FLAGS_print_triage_alerts)) {
636     webrtc::TriageHelper triage_alerts(config);
637     triage_alerts.AnalyzeLog(parsed_log);
638     triage_alerts.Print(stderr);
639   }
640 
641   // TODO(bugs.webrtc.org/14248): Remove the need to generate a file
642   // and read the file directly from memory.
643   if (has_generated_wav_file) {
644     RTC_CHECK_EQ(std::remove(wav_path.c_str()), 0)
645         << "Failed to remove " << wav_path;
646   }
647   return 0;
648 }
649