Refactor map of RTC event log chart names

This moves the mapping from names to charts into the Analyzer for the
"normal" charts. (Neteq simulations require special treatment and are
kept outside)

Also fixes 2 minor bugs:
 - simulated_neteq_stats alias did not generate simulated_neteq_jitter_buffer_delay
 - simulated_neteq_jitter_buffer_delay did not populate the `id` /
   window title

Bug: None
Change-Id: I1c93e5fbc535fd1f2af9eaeef37d9d646d54419e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/347862
Reviewed-by: Jeremy Leconte <jleconte@google.com>
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42123}
diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.cc b/rtc_tools/rtc_event_log_visualizer/analyzer.cc
index 7b8586a..899cc5b7 100644
--- a/rtc_tools/rtc_event_log_visualizer/analyzer.cc
+++ b/rtc_tools/rtc_event_log_visualizer/analyzer.cc
@@ -66,6 +66,7 @@
 #include "rtc_base/numerics/sequence_number_unwrapper.h"
 #include "rtc_base/rate_statistics.h"
 #include "rtc_base/strings/string_builder.h"
+#include "rtc_tools/rtc_event_log_visualizer/analyze_audio.h"
 #include "rtc_tools/rtc_event_log_visualizer/analyzer_common.h"
 #include "rtc_tools/rtc_event_log_visualizer/log_simulation.h"
 #include "rtc_tools/rtc_event_log_visualizer/plot_base.h"
@@ -428,6 +429,22 @@
   Timestamp base_time = Timestamp::MinusInfinity();
 };
 
+float GetHighestSeqNumber(const webrtc::rtcp::ReportBlock& block) {
+  return block.extended_high_seq_num();
+}
+
+float GetFractionLost(const webrtc::rtcp::ReportBlock& block) {
+  return static_cast<double>(block.fraction_lost()) / 256 * 100;
+}
+
+float GetCumulativeLost(const webrtc::rtcp::ReportBlock& block) {
+  return block.cumulative_lost();
+}
+
+float DelaySinceLastSr(const webrtc::rtcp::ReportBlock& block) {
+  return static_cast<double>(block.delay_since_last_sr()) / 65536;
+}
+
 }  // namespace
 
 EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log,
@@ -490,6 +507,201 @@
   bool bitrate_updated_;
 };
 
+void EventLogAnalyzer::InitializeMapOfNamedGraphs(bool show_detector_state,
+                                                  bool show_alr_state,
+                                                  bool show_link_capacity) {
+  plots_.RegisterPlot("incoming_packet_sizes", [this](Plot* plot) {
+    this->CreatePacketGraph(webrtc::kIncomingPacket, plot);
+  });
+
+  plots_.RegisterPlot("outgoing_packet_sizes", [this](Plot* plot) {
+    this->CreatePacketGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("incoming_rtcp_types", [this](Plot* plot) {
+    this->CreateRtcpTypeGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_rtcp_types", [this](Plot* plot) {
+    this->CreateRtcpTypeGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("incoming_packet_count", [this](Plot* plot) {
+    this->CreateAccumulatedPacketsGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_packet_count", [this](Plot* plot) {
+    this->CreateAccumulatedPacketsGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("incoming_packet_rate", [this](Plot* plot) {
+    this->CreatePacketRateGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_packet_rate", [this](Plot* plot) {
+    this->CreatePacketRateGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("total_incoming_packet_rate", [this](Plot* plot) {
+    this->CreateTotalPacketRateGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("total_outgoing_packet_rate", [this](Plot* plot) {
+    this->CreateTotalPacketRateGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("audio_playout",
+                      [this](Plot* plot) { this->CreatePlayoutGraph(plot); });
+
+  plots_.RegisterPlot("neteq_set_minimum_delay", [this](Plot* plot) {
+    this->CreateNetEqSetMinimumDelay(plot);
+  });
+
+  plots_.RegisterPlot("incoming_audio_level", [this](Plot* plot) {
+    this->CreateAudioLevelGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_audio_level", [this](Plot* plot) {
+    this->CreateAudioLevelGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("incoming_sequence_number_delta", [this](Plot* plot) {
+    this->CreateSequenceNumberGraph(plot);
+  });
+  plots_.RegisterPlot("incoming_delay", [this](Plot* plot) {
+    this->CreateIncomingDelayGraph(plot);
+  });
+  plots_.RegisterPlot("incoming_loss_rate", [this](Plot* plot) {
+    this->CreateIncomingPacketLossGraph(plot);
+  });
+  plots_.RegisterPlot("incoming_bitrate", [this](Plot* plot) {
+    this->CreateTotalIncomingBitrateGraph(plot);
+  });
+  plots_.RegisterPlot(
+      "outgoing_bitrate", [this, show_detector_state, show_alr_state,
+                           show_link_capacity](Plot* plot) {
+        this->CreateTotalOutgoingBitrateGraph(
+            plot, show_detector_state, show_alr_state, show_link_capacity);
+      });
+  plots_.RegisterPlot("incoming_stream_bitrate", [this](Plot* plot) {
+    this->CreateStreamBitrateGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_stream_bitrate", [this](Plot* plot) {
+    this->CreateStreamBitrateGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("incoming_layer_bitrate_allocation", [this](Plot* plot) {
+    this->CreateBitrateAllocationGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_layer_bitrate_allocation", [this](Plot* plot) {
+    this->CreateBitrateAllocationGraph(webrtc::kOutgoingPacket, plot);
+  });
+  plots_.RegisterPlot("simulated_receiveside_bwe", [this](Plot* plot) {
+    this->CreateReceiveSideBweSimulationGraph(plot);
+  });
+  plots_.RegisterPlot("simulated_sendside_bwe", [this](Plot* plot) {
+    this->CreateSendSideBweSimulationGraph(plot);
+  });
+  plots_.RegisterPlot("simulated_goog_cc", [this](Plot* plot) {
+    this->CreateGoogCcSimulationGraph(plot);
+  });
+  plots_.RegisterPlot("outgoing_twcc_loss", [this](Plot* plot) {
+    this->CreateOutgoingTWCCLossRateGraph(plot);
+  });
+  plots_.RegisterPlot("network_delay_feedback", [this](Plot* plot) {
+    this->CreateNetworkDelayFeedbackGraph(plot);
+  });
+  plots_.RegisterPlot("fraction_loss_feedback", [this](Plot* plot) {
+    this->CreateFractionLossGraph(plot);
+  });
+  plots_.RegisterPlot("incoming_timestamps", [this](Plot* plot) {
+    this->CreateTimestampGraph(webrtc::kIncomingPacket, plot);
+  });
+  plots_.RegisterPlot("outgoing_timestamps", [this](Plot* plot) {
+    this->CreateTimestampGraph(webrtc::kOutgoingPacket, plot);
+  });
+
+  plots_.RegisterPlot("incoming_rtcp_fraction_lost", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kIncomingPacket, GetFractionLost,
+        "Fraction lost (incoming RTCP)", "Loss rate (percent)", plot);
+  });
+  plots_.RegisterPlot("outgoing_rtcp_fraction_lost", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kOutgoingPacket, GetFractionLost,
+        "Fraction lost (outgoing RTCP)", "Loss rate (percent)", plot);
+  });
+
+  plots_.RegisterPlot("incoming_rtcp_cumulative_lost", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kIncomingPacket, GetCumulativeLost,
+        "Cumulative lost packets (incoming RTCP)", "Packets", plot);
+  });
+  plots_.RegisterPlot("outgoing_rtcp_cumulative_lost", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kOutgoingPacket, GetCumulativeLost,
+        "Cumulative lost packets (outgoing RTCP)", "Packets", plot);
+  });
+
+  plots_.RegisterPlot("incoming_rtcp_highest_seq_number", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kIncomingPacket, GetHighestSeqNumber,
+        "Highest sequence number (incoming RTCP)", "Sequence number", plot);
+  });
+  plots_.RegisterPlot("outgoing_rtcp_highest_seq_number", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kOutgoingPacket, GetHighestSeqNumber,
+        "Highest sequence number (outgoing RTCP)", "Sequence number", plot);
+  });
+
+  plots_.RegisterPlot("incoming_rtcp_delay_since_last_sr", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kIncomingPacket, DelaySinceLastSr,
+        "Delay since last received sender report (incoming RTCP)", "Time (s)",
+        plot);
+  });
+  plots_.RegisterPlot("outgoing_rtcp_delay_since_last_sr", [this](Plot* plot) {
+    this->CreateSenderAndReceiverReportPlot(
+        webrtc::kOutgoingPacket, DelaySinceLastSr,
+        "Delay since last received sender report (outgoing RTCP)", "Time (s)",
+        plot);
+  });
+
+  plots_.RegisterPlot(
+      "pacer_delay", [this](Plot* plot) { this->CreatePacerDelayGraph(plot); });
+
+  plots_.RegisterPlot("audio_encoder_bitrate", [this](Plot* plot) {
+    CreateAudioEncoderTargetBitrateGraph(this->parsed_log_, this->config_,
+                                         plot);
+  });
+  plots_.RegisterPlot("audio_encoder_frame_length", [this](Plot* plot) {
+    CreateAudioEncoderFrameLengthGraph(this->parsed_log_, this->config_, plot);
+  });
+  plots_.RegisterPlot("audio_encoder_packet_loss", [this](Plot* plot) {
+    CreateAudioEncoderPacketLossGraph(this->parsed_log_, this->config_, plot);
+  });
+  plots_.RegisterPlot("audio_encoder_fec", [this](Plot* plot) {
+    CreateAudioEncoderEnableFecGraph(this->parsed_log_, this->config_, plot);
+  });
+  plots_.RegisterPlot("audio_encoder_dtx", [this](Plot* plot) {
+    CreateAudioEncoderEnableDtxGraph(this->parsed_log_, this->config_, plot);
+  });
+  plots_.RegisterPlot("audio_encoder_num_channels", [this](Plot* plot) {
+    CreateAudioEncoderNumChannelsGraph(this->parsed_log_, this->config_, plot);
+  });
+
+  plots_.RegisterPlot("ice_candidate_pair_config", [this](Plot* plot) {
+    this->CreateIceCandidatePairConfigGraph(plot);
+  });
+  plots_.RegisterPlot("ice_connectivity_check", [this](Plot* plot) {
+    this->CreateIceConnectivityCheckGraph(plot);
+  });
+  plots_.RegisterPlot("dtls_transport_state", [this](Plot* plot) {
+    this->CreateDtlsTransportStateGraph(plot);
+  });
+  plots_.RegisterPlot("dtls_writable_state", [this](Plot* plot) {
+    this->CreateDtlsWritableStateGraph(plot);
+  });
+}
+
+void EventLogAnalyzer::CreateGraphsByName(const std::vector<std::string>& names,
+                                          PlotCollection* collection) {
+  for (const auto& plot : plots_) {
+    if (absl::c_find(names, plot.label) != names.end()) {
+      Plot* output = collection->AppendNewPlot(plot.label);
+      plot.plot_func(output);
+    }
+  }
+}
+
 void EventLogAnalyzer::CreatePacketGraph(PacketDirection direction,
                                          Plot* plot) {
   for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.h b/rtc_tools/rtc_event_log_visualizer/analyzer.h
index 3dfd5de..a29454e 100644
--- a/rtc_tools/rtc_event_log_visualizer/analyzer.h
+++ b/rtc_tools/rtc_event_log_visualizer/analyzer.h
@@ -26,6 +26,31 @@
 namespace webrtc {
 
 class EventLogAnalyzer {
+  struct PlotDeclaration {
+    PlotDeclaration(const std::string& label, std::function<void(Plot*)> f)
+        : label(label), plot_func(f) {}
+    const std::string label;
+    // TODO(terelius): Add a help text/explanation.
+    const std::function<void(Plot*)> plot_func;
+  };
+
+  class PlotMap {
+   public:
+    void RegisterPlot(const std::string& label, std::function<void(Plot*)> 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});
+    }
+
+    std::vector<PlotDeclaration>::iterator begin() { return plots_.begin(); }
+    std::vector<PlotDeclaration>::iterator end() { return plots_.end(); }
+
+   private:
+    std::vector<PlotDeclaration> plots_;
+  };
+
  public:
   // The EventLogAnalyzer keeps a reference to the ParsedRtcEventLogNew for the
   // duration of its lifetime. The ParsedRtcEventLogNew must not be destroyed or
@@ -33,6 +58,21 @@
   EventLogAnalyzer(const ParsedRtcEventLog& log, bool normalize_time);
   EventLogAnalyzer(const ParsedRtcEventLog& log, const AnalyzerConfig& config);
 
+  void CreateGraphsByName(const std::vector<std::string>& names,
+                          PlotCollection* collection);
+
+  void InitializeMapOfNamedGraphs(bool show_detector_state,
+                                  bool show_alr_state,
+                                  bool show_link_capacity);
+
+  std::vector<std::string> GetGraphNames() {
+    std::vector<std::string> plot_names;
+    for (const auto& plot : plots_) {
+      plot_names.push_back(plot.label);
+    }
+    return plot_names;
+  }
+
   void CreatePacketGraph(PacketDirection direction, Plot* plot);
 
   void CreateRtcpTypeGraph(PacketDirection direction, Plot* plot);
@@ -108,6 +148,8 @@
   std::map<uint32_t, std::string> candidate_pair_desc_by_id_;
 
   AnalyzerConfig config_;
+
+  PlotMap plots_;
 };
 
 }  // namespace webrtc
diff --git a/rtc_tools/rtc_event_log_visualizer/main.cc b/rtc_tools/rtc_event_log_visualizer/main.cc
index e9a747c..8cc4496 100644
--- a/rtc_tools/rtc_event_log_visualizer/main.cc
+++ b/rtc_tools/rtc_event_log_visualizer/main.cc
@@ -129,71 +129,10 @@
   return v;
 }
 
-struct PlotDeclaration {
-  PlotDeclaration(const std::string& label, std::function<void(Plot*)> f)
-      : label(label), enabled(false), plot_func(f) {}
-  const std::string label;
-  bool enabled;
-  // TODO(terelius): Add a help text/explanation.
-  const std::function<void(Plot*)> plot_func;
-};
-
-class PlotMap {
- public:
-  void RegisterPlot(const std::string& label, std::function<void(Plot*)> 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<std::string>& flags,
-      const std::map<std::string, std::vector<std::string>>& 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<PlotDeclaration>::iterator begin() { return plots_.begin(); }
-  std::vector<PlotDeclaration>::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<PlotDeclaration> plots_;
-};
+bool KnownPlotName(absl::string_view name,
+                   const std::vector<std::string>& known_plots) {
+  return absl::c_find(known_plots, name) != known_plots.end();
+}
 
 bool ContainsHelppackageFlags(absl::string_view filename) {
   return absl::EndsWith(filename, "main.cc");
@@ -217,37 +156,6 @@
   }
   rtc::LogMessage::SetLogToStderr(true);
 
-  // Flag replacements
-  std::map<std::string, std::vector<std::string>> 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", "outgoing_twcc_loss"}},
-      {"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_preemptive_rate",
-        "simulated_neteq_accelerate_rate", "simulated_neteq_speech_expand_rate",
-        "simulated_neteq_expand_rate"}}};
-
-  std::vector<std::string> plot_flags =
-      StrSplit(absl::GetFlag(FLAGS_plot), ",");
-
   // InitFieldTrialsFromString stores the char*, so the char array must outlive
   // the application.
   const std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
@@ -289,202 +197,6 @@
     return -1;
   }
 
-  webrtc::EventLogAnalyzer analyzer(parsed_log, config);
-  webrtc::PlotCollection collection;
-  collection.SetCallTimeToUtcOffsetMs(config.CallTimeToUtcOffsetMs());
-
-  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("incoming_packet_rate", [&](Plot* plot) {
-    analyzer.CreatePacketRateGraph(webrtc::kIncomingPacket, plot);
-  });
-  plots.RegisterPlot("outgoing_packet_rate", [&](Plot* plot) {
-    analyzer.CreatePacketRateGraph(webrtc::kOutgoingPacket, plot);
-  });
-  plots.RegisterPlot("total_incoming_packet_rate", [&](Plot* plot) {
-    analyzer.CreateTotalPacketRateGraph(webrtc::kIncomingPacket, plot);
-  });
-  plots.RegisterPlot("total_outgoing_packet_rate", [&](Plot* plot) {
-    analyzer.CreateTotalPacketRateGraph(webrtc::kOutgoingPacket, plot);
-  });
-  plots.RegisterPlot("audio_playout",
-                     [&](Plot* plot) { analyzer.CreatePlayoutGraph(plot); });
-
-  plots.RegisterPlot("neteq_set_minimum_delay", [&](Plot* plot) {
-    analyzer.CreateNetEqSetMinimumDelay(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, absl::GetFlag(FLAGS_show_detector_state),
-        absl::GetFlag(FLAGS_show_alr_state),
-        absl::GetFlag(FLAGS_show_link_capacity));
-  });
-  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("outgoing_twcc_loss", [&](Plot* plot) {
-    analyzer.CreateOutgoingTWCCLossRateGraph(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<double>(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)", plot);
-  });
-  plots.RegisterPlot("outgoing_rtcp_fraction_lost", [&](Plot* plot) {
-    analyzer.CreateSenderAndReceiverReportPlot(
-        webrtc::kOutgoingPacket, GetFractionLost,
-        "Fraction lost (outgoing RTCP)", "Loss rate (percent)", plot);
-  });
-  auto GetCumulativeLost = [](const webrtc::rtcp::ReportBlock& block) -> float {
-    return block.cumulative_lost();
-  };
-  plots.RegisterPlot("incoming_rtcp_cumulative_lost", [&](Plot* plot) {
-    analyzer.CreateSenderAndReceiverReportPlot(
-        webrtc::kIncomingPacket, GetCumulativeLost,
-        "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", plot);
-  });
-
-  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", plot);
-  });
-  plots.RegisterPlot("outgoing_rtcp_highest_seq_number", [&](Plot* plot) {
-    analyzer.CreateSenderAndReceiverReportPlot(
-        webrtc::kOutgoingPacket, GetHighestSeqNumber,
-        "Highest sequence number (outgoing RTCP)", "Sequence number", plot);
-  });
-
-  auto DelaySinceLastSr = [](const webrtc::rtcp::ReportBlock& block) -> float {
-    return static_cast<double>(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)",
-        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)",
-        plot);
-  });
-
-  plots.RegisterPlot("pacer_delay",
-                     [&](Plot* plot) { analyzer.CreatePacerDelayGraph(plot); });
-  plots.RegisterPlot("audio_encoder_bitrate", [&](Plot* plot) {
-    CreateAudioEncoderTargetBitrateGraph(parsed_log, config, plot);
-  });
-  plots.RegisterPlot("audio_encoder_frame_length", [&](Plot* plot) {
-    CreateAudioEncoderFrameLengthGraph(parsed_log, config, plot);
-  });
-  plots.RegisterPlot("audio_encoder_packet_loss", [&](Plot* plot) {
-    CreateAudioEncoderPacketLossGraph(parsed_log, config, plot);
-  });
-  plots.RegisterPlot("audio_encoder_fec", [&](Plot* plot) {
-    CreateAudioEncoderEnableFecGraph(parsed_log, config, plot);
-  });
-  plots.RegisterPlot("audio_encoder_dtx", [&](Plot* plot) {
-    CreateAudioEncoderEnableDtxGraph(parsed_log, config, plot);
-  });
-  plots.RegisterPlot("audio_encoder_num_channels", [&](Plot* plot) {
-    CreateAudioEncoderNumChannelsGraph(parsed_log, config, 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;
   bool has_generated_wav_file = false;
   if (!absl::GetFlag(FLAGS_wav_filename).empty()) {
@@ -499,100 +211,53 @@
         webrtc::conversational_speech_en_wav_len);
     has_generated_wav_file = true;
   }
-  absl::optional<webrtc::NetEqStatsGetterMap> neteq_stats;
 
-  plots.RegisterPlot("simulated_neteq_expand_rate", [&](Plot* plot) {
-    if (!neteq_stats) {
-      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
-    }
-    webrtc::CreateNetEqNetworkStatsGraph(
-        parsed_log, config, *neteq_stats,
-        [](const webrtc::NetEqNetworkStatistics& stats) {
-          return stats.expand_rate / 16384.f;
-        },
-        "Expand rate", plot);
-  });
+  webrtc::EventLogAnalyzer analyzer(parsed_log, config);
+  analyzer.InitializeMapOfNamedGraphs(absl::GetFlag(FLAGS_show_detector_state),
+                                      absl::GetFlag(FLAGS_show_alr_state),
+                                      absl::GetFlag(FLAGS_show_link_capacity));
 
-  plots.RegisterPlot("simulated_neteq_speech_expand_rate", [&](Plot* plot) {
-    if (!neteq_stats) {
-      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
-    }
-    webrtc::CreateNetEqNetworkStatsGraph(
-        parsed_log, config, *neteq_stats,
-        [](const webrtc::NetEqNetworkStatistics& stats) {
-          return stats.speech_expand_rate / 16384.f;
-        },
-        "Speech expand rate", plot);
-  });
-
-  plots.RegisterPlot("simulated_neteq_accelerate_rate", [&](Plot* plot) {
-    if (!neteq_stats) {
-      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
-    }
-    webrtc::CreateNetEqNetworkStatsGraph(
-        parsed_log, config, *neteq_stats,
-        [](const webrtc::NetEqNetworkStatistics& stats) {
-          return stats.accelerate_rate / 16384.f;
-        },
-        "Accelerate rate", plot);
-  });
-
-  plots.RegisterPlot("simulated_neteq_preemptive_rate", [&](Plot* plot) {
-    if (!neteq_stats) {
-      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
-    }
-    webrtc::CreateNetEqNetworkStatsGraph(
-        parsed_log, config, *neteq_stats,
-        [](const webrtc::NetEqNetworkStatistics& stats) {
-          return stats.preemptive_rate / 16384.f;
-        },
-        "Preemptive rate", plot);
-  });
-
-  plots.RegisterPlot("simulated_neteq_concealment_events", [&](Plot* plot) {
-    if (!neteq_stats) {
-      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
-    }
-    webrtc::CreateNetEqLifetimeStatsGraph(
-        parsed_log, config, *neteq_stats,
-        [](const webrtc::NetEqLifetimeStatistics& stats) {
-          return static_cast<float>(stats.concealment_events);
-        },
-        "Concealment events", plot);
-  });
-
-  plots.RegisterPlot("simulated_neteq_preferred_buffer_size", [&](Plot* plot) {
-    if (!neteq_stats) {
-      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
-    }
-    webrtc::CreateNetEqNetworkStatsGraph(
-        parsed_log, config, *neteq_stats,
-        [](const webrtc::NetEqNetworkStatistics& stats) {
-          return stats.preferred_buffer_size_ms;
-        },
-        "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;
-    }
-  }
+  // Flag replacements
+  std::map<std::string, std::vector<std::string>> 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", "outgoing_twcc_loss"}},
+      {"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_preemptive_rate",
+        "simulated_neteq_accelerate_rate", "simulated_neteq_speech_expand_rate",
+        "simulated_neteq_expand_rate"}}};
 
   if (absl::GetFlag(FLAGS_list_plots)) {
     std::cerr << "List of registered plots (for use with the --plot flag):"
               << std::endl;
-    for (const auto& plot : plots) {
+    for (const auto& plot_name : analyzer.GetGraphNames()) {
       // TODO(terelius): Also print a help text.
-      std::cerr << "  " << plot.label << std::endl;
+      std::cerr << "  " << plot_name;
     }
-    // The following flag doesn't fit the model used for the other plots.
-    std::cerr << "simulated_neteq_jitter_buffer_delay" << std::endl;
+    // The following flags don't fit the model used for the other plots.
+    for (const auto& plot_name : flag_aliases["simulated_neteq_stats"]) {
+      std::cerr << " " << plot_name;
+    }
+    std::cerr << std::endl;
+
     std::cerr << "List of plot aliases (for use with the --plot flag):"
               << std::endl;
     std::cerr << "  all = every registered plot" << std::endl;
@@ -605,17 +270,134 @@
     }
     return 0;
   }
+
   if (args.size() != 2) {
     // Print usage information.
     std::cerr << absl::ProgramUsageMessage();
     return 1;
   }
 
-  for (const auto& plot : plots) {
-    if (plot.enabled) {
-      Plot* output = collection.AppendNewPlot(plot.label);
-      plot.plot_func(output);
+  // Select which plots to output
+  std::vector<std::string> plot_flags =
+      StrSplit(absl::GetFlag(FLAGS_plot), ",");
+  std::vector<std::string> plot_names;
+  const std::vector<std::string> known_analyzer_plots =
+      analyzer.GetGraphNames();
+  const std::vector<std::string> known_neteq_plots =
+      flag_aliases["simulated_neteq_stats"];
+  std::vector<std::string> all_known_plots = known_analyzer_plots;
+  all_known_plots.insert(all_known_plots.end(), known_neteq_plots.begin(),
+                         known_neteq_plots.end());
+  for (const std::string& flag : plot_flags) {
+    if (flag == "all") {
+      plot_names = all_known_plots;
+      break;
     }
+    auto alias_it = flag_aliases.find(flag);
+    if (alias_it != flag_aliases.end()) {
+      for (std::string& replacement : alias_it->second) {
+        if (!KnownPlotName(replacement, all_known_plots)) {
+          std::cerr << "Unknown plot name \"" << replacement << "\""
+                    << std::endl;
+          return 1;
+        }
+        plot_names.push_back(replacement);
+      }
+    } else {
+      plot_names.push_back(flag);
+      if (!KnownPlotName(flag, all_known_plots)) {
+        std::cerr << "Unknown plot name \"" << flag << "\"" << std::endl;
+        return 1;
+      }
+    }
+  }
+
+  webrtc::PlotCollection collection;
+  analyzer.CreateGraphsByName(plot_names, &collection);
+
+  // The simulated neteq charts are treated separately because they have a
+  // different behavior compared to all other plots. In particular, the neteq
+  // plots
+  //  * cache the simulation results between different plots
+  //  * open and read files
+  //  * dont have a 1-to-1 mapping between IDs and charts.
+  absl::optional<webrtc::NetEqStatsGetterMap> neteq_stats;
+  if (absl::c_find(plot_names, "simulated_neteq_expand_rate") !=
+      plot_names.end()) {
+    if (!neteq_stats) {
+      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
+    }
+    webrtc::CreateNetEqNetworkStatsGraph(
+        parsed_log, config, *neteq_stats,
+        [](const webrtc::NetEqNetworkStatistics& stats) {
+          return stats.expand_rate / 16384.f;
+        },
+        "Expand rate", collection.AppendNewPlot("simulated_neteq_expand_rate"));
+  }
+  if (absl::c_find(plot_names, "simulated_neteq_speech_expand_rate") !=
+      plot_names.end()) {
+    if (!neteq_stats) {
+      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
+    }
+    webrtc::CreateNetEqNetworkStatsGraph(
+        parsed_log, config, *neteq_stats,
+        [](const webrtc::NetEqNetworkStatistics& stats) {
+          return stats.speech_expand_rate / 16384.f;
+        },
+        "Speech expand rate",
+        collection.AppendNewPlot("simulated_neteq_speech_expand_rate"));
+  }
+  if (absl::c_find(plot_names, "simulated_neteq_accelerate_rate") !=
+      plot_names.end()) {
+    if (!neteq_stats) {
+      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
+    }
+    webrtc::CreateNetEqNetworkStatsGraph(
+        parsed_log, config, *neteq_stats,
+        [](const webrtc::NetEqNetworkStatistics& stats) {
+          return stats.accelerate_rate / 16384.f;
+        },
+        "Accelerate rate",
+        collection.AppendNewPlot("simulated_neteq_accelerate_rate"));
+  }
+  if (absl::c_find(plot_names, "simulated_neteq_preemptive_rate") !=
+      plot_names.end()) {
+    if (!neteq_stats) {
+      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
+    }
+    webrtc::CreateNetEqNetworkStatsGraph(
+        parsed_log, config, *neteq_stats,
+        [](const webrtc::NetEqNetworkStatistics& stats) {
+          return stats.preemptive_rate / 16384.f;
+        },
+        "Preemptive rate",
+        collection.AppendNewPlot("simulated_neteq_preemptive_rate"));
+  }
+  if (absl::c_find(plot_names, "simulated_neteq_concealment_events") !=
+      plot_names.end()) {
+    if (!neteq_stats) {
+      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
+    }
+    webrtc::CreateNetEqLifetimeStatsGraph(
+        parsed_log, config, *neteq_stats,
+        [](const webrtc::NetEqLifetimeStatistics& stats) {
+          return static_cast<float>(stats.concealment_events);
+        },
+        "Concealment events",
+        collection.AppendNewPlot("simulated_neteq_concealment_events"));
+  }
+  if (absl::c_find(plot_names, "simulated_neteq_preferred_buffer_size") !=
+      plot_names.end()) {
+    if (!neteq_stats) {
+      neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
+    }
+    webrtc::CreateNetEqNetworkStatsGraph(
+        parsed_log, config, *neteq_stats,
+        [](const webrtc::NetEqNetworkStatistics& stats) {
+          return stats.preferred_buffer_size_ms;
+        },
+        "Preferred buffer size (ms)",
+        collection.AppendNewPlot("simulated_neteq_preferred_buffer_size"));
   }
 
   // The model we use for registering plots assumes that the each plot label
@@ -623,19 +405,20 @@
   // 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 (absl::c_find(plot_names, "simulated_neteq_jitter_buffer_delay") !=
+      plot_names.end()) {
     if (!neteq_stats) {
       neteq_stats = webrtc::SimulateNetEq(parsed_log, config, wav_path, 48000);
     }
-    for (webrtc::NetEqStatsGetterMap::const_iterator it = neteq_stats->cbegin();
-         it != neteq_stats->cend(); ++it) {
-      webrtc::CreateAudioJitterBufferGraph(parsed_log, config, it->first,
-                                           it->second.get(),
-                                           collection.AppendNewPlot());
+    for (auto it = neteq_stats->cbegin(); it != neteq_stats->cend(); ++it) {
+      webrtc::CreateAudioJitterBufferGraph(
+          parsed_log, config, it->first, it->second.get(),
+          collection.AppendNewPlot("simulated_neteq_jitter_buffer_delay"));
     }
   }
 
+  collection.SetCallTimeToUtcOffsetMs(config.CallTimeToUtcOffsetMs());
+
   if (absl::GetFlag(FLAGS_protobuf_output)) {
     webrtc::analytics::ChartCollection proto_charts;
     collection.ExportProtobuf(&proto_charts);