Reland: Add BWE plot to event log analyzer.

The plot is constructed by actually running the congestion controller with
the logged rtp headers and rtcp feedback messages to reproduce the same behavior
as in the real call.

R=phoglund@webrtc.org, terelius@webrtc.org

Review URL: https://codereview.webrtc.org/2193763002 .

Cr-Commit-Position: refs/heads/master@{#13574}
diff --git a/webrtc/call/rtc_event_log.cc b/webrtc/call/rtc_event_log.cc
index 840b210..c022296 100644
--- a/webrtc/call/rtc_event_log.cc
+++ b/webrtc/call/rtc_event_log.cc
@@ -38,40 +38,6 @@
 
 namespace webrtc {
 
-// No-op implementation is used if flag is not set, or in tests.
-class RtcEventLogNullImpl final : public RtcEventLog {
- public:
-  bool StartLogging(const std::string& file_name,
-                    int64_t max_size_bytes) override {
-    return false;
-  }
-  bool StartLogging(rtc::PlatformFile platform_file,
-                    int64_t max_size_bytes) override {
-    // The platform_file is open and needs to be closed.
-    if (!rtc::ClosePlatformFile(platform_file)) {
-      LOG(LS_ERROR) << "Can't close file.";
-    }
-    return false;
-  }
-  void StopLogging() override {}
-  void LogVideoReceiveStreamConfig(
-      const VideoReceiveStream::Config& config) override {}
-  void LogVideoSendStreamConfig(
-      const VideoSendStream::Config& config) override {}
-  void LogRtpHeader(PacketDirection direction,
-                    MediaType media_type,
-                    const uint8_t* header,
-                    size_t packet_length) override {}
-  void LogRtcpPacket(PacketDirection direction,
-                     MediaType media_type,
-                     const uint8_t* packet,
-                     size_t length) override {}
-  void LogAudioPlayout(uint32_t ssrc) override {}
-  void LogBwePacketLossEvent(int32_t bitrate,
-                             uint8_t fraction_loss,
-                             int32_t total_packets) override {}
-};
-
 #ifdef ENABLE_RTC_EVENT_LOG
 
 class RtcEventLogImpl final : public RtcEventLog {
@@ -454,6 +420,15 @@
 
 #endif  // ENABLE_RTC_EVENT_LOG
 
+bool RtcEventLogNullImpl::StartLogging(rtc::PlatformFile platform_file,
+                                       int64_t max_size_bytes) {
+  // The platform_file is open and needs to be closed.
+  if (!rtc::ClosePlatformFile(platform_file)) {
+    LOG(LS_ERROR) << "Can't close file.";
+  }
+  return false;
+}
+
 // RtcEventLog member functions.
 std::unique_ptr<RtcEventLog> RtcEventLog::Create(const Clock* clock) {
 #ifdef ENABLE_RTC_EVENT_LOG
diff --git a/webrtc/call/rtc_event_log.h b/webrtc/call/rtc_event_log.h
index 7c72dd5..a335969 100644
--- a/webrtc/call/rtc_event_log.h
+++ b/webrtc/call/rtc_event_log.h
@@ -109,6 +109,34 @@
                                rtclog::EventStream* result);
 };
 
+// No-op implementation is used if flag is not set, or in tests.
+class RtcEventLogNullImpl final : public RtcEventLog {
+ public:
+  bool StartLogging(const std::string& file_name,
+                    int64_t max_size_bytes) override {
+    return false;
+  }
+  bool StartLogging(rtc::PlatformFile platform_file,
+                    int64_t max_size_bytes) override;
+  void StopLogging() override {}
+  void LogVideoReceiveStreamConfig(
+      const VideoReceiveStream::Config& config) override {}
+  void LogVideoSendStreamConfig(
+      const VideoSendStream::Config& config) override {}
+  void LogRtpHeader(PacketDirection direction,
+                    MediaType media_type,
+                    const uint8_t* header,
+                    size_t packet_length) override {}
+  void LogRtcpPacket(PacketDirection direction,
+                     MediaType media_type,
+                     const uint8_t* packet,
+                     size_t length) override {}
+  void LogAudioPlayout(uint32_t ssrc) override {}
+  void LogBwePacketLossEvent(int32_t bitrate,
+                             uint8_t fraction_loss,
+                             int32_t total_packets) override {}
+};
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_CALL_RTC_EVENT_LOG_H_
diff --git a/webrtc/modules/congestion_controller/congestion_controller.cc b/webrtc/modules/congestion_controller/congestion_controller.cc
index b4e5c14..3bb55cb 100644
--- a/webrtc/modules/congestion_controller/congestion_controller.cc
+++ b/webrtc/modules/congestion_controller/congestion_controller.cc
@@ -304,7 +304,7 @@
 
 void CongestionController::OnSentPacket(const rtc::SentPacket& sent_packet) {
   transport_feedback_adapter_.OnSentPacket(sent_packet.packet_id,
-                                            sent_packet.send_time_ms);
+                                           sent_packet.send_time_ms);
 }
 
 void CongestionController::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
diff --git a/webrtc/test/fuzzers/BUILD.gn b/webrtc/test/fuzzers/BUILD.gn
index d917f1a..6928062 100644
--- a/webrtc/test/fuzzers/BUILD.gn
+++ b/webrtc/test/fuzzers/BUILD.gn
@@ -114,6 +114,7 @@
     "congestion_controller_feedback_fuzzer.cc",
   ]
   deps = [
+    "../../:rtc_event_log",
     "../../modules/congestion_controller/",
   ]
 }
diff --git a/webrtc/test/fuzzers/congestion_controller_feedback_fuzzer.cc b/webrtc/test/fuzzers/congestion_controller_feedback_fuzzer.cc
index aa0d62e..6fb5f9b 100644
--- a/webrtc/test/fuzzers/congestion_controller_feedback_fuzzer.cc
+++ b/webrtc/test/fuzzers/congestion_controller_feedback_fuzzer.cc
@@ -26,42 +26,13 @@
                                uint32_t bitrate) override {}
 };
 
-class NullEventLog : public RtcEventLog {
- public:
-  ~NullEventLog() override {}
-  bool StartLogging(const std::string& file_name,
-                    int64_t max_size_bytes) override {
-    return true;
-  }
-  bool StartLogging(rtc::PlatformFile platform_file, int64_t max_size_bytes) {
-    return true;
-  }
-  void StopLogging() override{};
-  void LogVideoReceiveStreamConfig(
-      const webrtc::VideoReceiveStream::Config& config) override {}
-  void LogVideoSendStreamConfig(
-      const webrtc::VideoSendStream::Config& config) override {}
-  void LogRtpHeader(PacketDirection direction,
-                    MediaType media_type,
-                    const uint8_t* header,
-                    size_t packet_length) override {}
-  void LogRtcpPacket(PacketDirection direction,
-                     MediaType media_type,
-                     const uint8_t* packet,
-                     size_t length) override {}
-  void LogAudioPlayout(uint32_t ssrc) override {}
-  void LogBwePacketLossEvent(int32_t bitrate,
-                             uint8_t fraction_loss,
-                             int32_t total_packets) override {}
-};
-
 void FuzzOneInput(const uint8_t* data, size_t size) {
   size_t i = 0;
   if (size < sizeof(int64_t) + sizeof(uint8_t) + sizeof(uint32_t))
     return;
   SimulatedClock clock(data[i++]);
   NullBitrateObserver observer;
-  NullEventLog event_log;
+  RtcEventLogNullImpl event_log;
   CongestionController cc(&clock, &observer, &observer, &event_log);
   RemoteBitrateEstimator* rbe = cc.GetRemoteBitrateEstimator(true);
   RTPHeader header;
diff --git a/webrtc/tools/BUILD.gn b/webrtc/tools/BUILD.gn
index abf0ca0..45d8051 100644
--- a/webrtc/tools/BUILD.gn
+++ b/webrtc/tools/BUILD.gn
@@ -173,6 +173,7 @@
 if (rtc_include_tests) {
   if (rtc_enable_protobuf) {
     executable("event_log_visualizer") {
+      testonly = true
       sources = [
         "event_log_visualizer/analyzer.cc",
         "event_log_visualizer/analyzer.h",
@@ -193,9 +194,12 @@
 
       defines = [ "ENABLE_RTC_EVENT_LOG" ]
       deps = [
+        "../:rtc_event_log",
         "../:rtc_event_log_parser",
+        "../modules/congestion_controller:congestion_controller",
         "../modules/rtp_rtcp:rtp_rtcp",
         "../system_wrappers:system_wrappers_default",
+        "../test:field_trial",
         "//third_party/gflags",
       ]
     }
diff --git a/webrtc/tools/DEPS b/webrtc/tools/DEPS
index 33df89f..e4b85c8 100644
--- a/webrtc/tools/DEPS
+++ b/webrtc/tools/DEPS
@@ -3,6 +3,7 @@
   "+webrtc/call",
   "+webrtc/common_video",
   "+webrtc/modules/audio_processing",
+  "+webrtc/modules/congestion_controller",
   "+webrtc/modules/rtp_rtcp",
   "+webrtc/system_wrappers",
   "+webrtc/voice_engine",
diff --git a/webrtc/tools/event_log_visualizer/analyzer.cc b/webrtc/tools/event_log_visualizer/analyzer.cc
index e6dd35b..c15de6d 100644
--- a/webrtc/tools/event_log_visualizer/analyzer.cc
+++ b/webrtc/tools/event_log_visualizer/analyzer.cc
@@ -22,9 +22,12 @@
 #include "webrtc/base/checks.h"
 #include "webrtc/call.h"
 #include "webrtc/common_types.h"
+#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
 #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
 #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
+#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
+#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
 
@@ -92,21 +95,15 @@
     return true;
   }
   if (ssrc_ == other.ssrc_) {
-    if (media_type_ < other.media_type_) {
+    if (direction_ < other.direction_) {
       return true;
     }
-    if (media_type_ == other.media_type_) {
-      if (direction_ < other.direction_) {
-        return true;
-      }
-    }
   }
   return false;
 }
 
 bool EventLogAnalyzer::StreamId::operator==(const StreamId& other) const {
-  return ssrc_ == other.ssrc_ && direction_ == other.direction_ &&
-         media_type_ == other.media_type_;
+  return ssrc_ == other.ssrc_ && direction_ == other.direction_;
 }
 
 
@@ -115,12 +112,11 @@
   uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
   uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
 
-  // Maps a stream identifier consisting of ssrc, direction and MediaType
+  // Maps a stream identifier consisting of ssrc and direction
   // to the header extensions used by that stream,
   std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
 
   PacketDirection direction;
-  MediaType media_type;
   uint8_t header[IP_PACKET_SIZE];
   size_t header_length;
   size_t total_length;
@@ -140,8 +136,7 @@
       case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
         VideoReceiveStream::Config config(nullptr);
         parsed_log_.GetVideoReceiveConfig(i, &config);
-        StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
-                        MediaType::VIDEO);
+        StreamId stream(config.rtp.remote_ssrc, kIncomingPacket);
         extension_maps[stream].Erase();
         for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
           const std::string& extension = config.rtp.extensions[j].uri;
@@ -155,7 +150,7 @@
         VideoSendStream::Config config(nullptr);
         parsed_log_.GetVideoSendConfig(i, &config);
         for (auto ssrc : config.rtp.ssrcs) {
-          StreamId stream(ssrc, kOutgoingPacket, MediaType::VIDEO);
+          StreamId stream(ssrc, kOutgoingPacket);
           extension_maps[stream].Erase();
           for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
             const std::string& extension = config.rtp.extensions[j].uri;
@@ -177,13 +172,14 @@
         break;
       }
       case ParsedRtcEventLog::RTP_EVENT: {
+        MediaType media_type;
         parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
                                  &header_length, &total_length);
         // Parse header to get SSRC.
         RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
         RTPHeader parsed_header;
         rtp_parser.Parse(&parsed_header);
-        StreamId stream(parsed_header.ssrc, direction, media_type);
+        StreamId stream(parsed_header.ssrc, direction);
         // Look up the extension_map and parse it again to get the extensions.
         if (extension_maps.count(stream) == 1) {
           RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
@@ -191,10 +187,45 @@
         }
         uint64_t timestamp = parsed_log_.GetTimestamp(i);
         rtp_packets_[stream].push_back(
-            LoggedRtpPacket(timestamp, parsed_header));
+            LoggedRtpPacket(timestamp, parsed_header, total_length));
         break;
       }
       case ParsedRtcEventLog::RTCP_EVENT: {
+        uint8_t packet[IP_PACKET_SIZE];
+        MediaType media_type;
+        parsed_log_.GetRtcpPacket(i, &direction, &media_type, packet,
+                                  &total_length);
+
+        RtpUtility::RtpHeaderParser rtp_parser(packet, total_length);
+        RTPHeader parsed_header;
+        RTC_CHECK(rtp_parser.ParseRtcp(&parsed_header));
+        uint32_t ssrc = parsed_header.ssrc;
+
+        RTCPUtility::RTCPParserV2 rtcp_parser(packet, total_length, true);
+        RTC_CHECK(rtcp_parser.IsValid());
+
+        RTCPUtility::RTCPPacketTypes packet_type = rtcp_parser.Begin();
+        while (packet_type != RTCPUtility::RTCPPacketTypes::kInvalid) {
+          switch (packet_type) {
+            case RTCPUtility::RTCPPacketTypes::kTransportFeedback: {
+              // Currently feedback is logged twice, both for audio and video.
+              // Only act on one of them.
+              if (media_type == MediaType::VIDEO) {
+                std::unique_ptr<rtcp::RtcpPacket> rtcp_packet(
+                    rtcp_parser.ReleaseRtcpPacket());
+                StreamId stream(ssrc, direction);
+                uint64_t timestamp = parsed_log_.GetTimestamp(i);
+                rtcp_packets_[stream].push_back(LoggedRtcpPacket(
+                    timestamp, kRtcpTransportFeedback, std::move(rtcp_packet)));
+              }
+              break;
+            }
+            default:
+              break;
+          }
+          rtcp_parser.Iterate();
+          packet_type = rtcp_parser.PacketType();
+        }
         break;
       }
       case ParsedRtcEventLog::LOG_START: {
@@ -232,6 +263,33 @@
   end_time_ = last_timestamp;
 }
 
+class BitrateObserver : public CongestionController::Observer,
+                        public RemoteBitrateObserver {
+ public:
+  BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {}
+
+  void OnNetworkChanged(uint32_t bitrate_bps,
+                        uint8_t fraction_loss,
+                        int64_t rtt_ms) override {
+    last_bitrate_bps_ = bitrate_bps;
+    bitrate_updated_ = true;
+  }
+
+  void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
+                               uint32_t bitrate) override {}
+
+  uint32_t last_bitrate_bps() const { return last_bitrate_bps_; }
+  bool GetAndResetBitrateUpdated() {
+    bool bitrate_updated = bitrate_updated_;
+    bitrate_updated_ = false;
+    return bitrate_updated;
+  }
+
+ private:
+  uint32_t last_bitrate_bps_;
+  bool bitrate_updated_;
+};
+
 void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
                                          Plot* plot) {
   std::map<uint32_t, TimeSeries> time_series;
@@ -675,5 +733,113 @@
   }
 }
 
+void EventLogAnalyzer::CreateBweGraph(Plot* plot) {
+  std::map<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
+  std::map<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
+
+  for (const auto& kv : rtp_packets_) {
+    if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
+      for (const LoggedRtpPacket& rtp_packet : kv.second)
+        outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
+    }
+  }
+
+  for (const auto& kv : rtcp_packets_) {
+    if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
+      for (const LoggedRtcpPacket& rtcp_packet : kv.second)
+        incoming_rtcp.insert(
+            std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
+    }
+  }
+
+  SimulatedClock clock(0);
+  BitrateObserver observer;
+  RtcEventLogNullImpl null_event_log;
+  CongestionController cc(&clock, &observer, &observer, &null_event_log);
+  // TODO(holmer): Log the call config and use that here instead.
+  static const uint32_t kDefaultStartBitrateBps = 300000;
+  cc.SetBweBitrates(0, kDefaultStartBitrateBps, -1);
+
+  TimeSeries time_series;
+  time_series.label = "BWE";
+  time_series.style = LINE_DOT_GRAPH;
+  uint32_t max_y = 10;
+  uint32_t min_y = 0;
+
+  auto rtp_iterator = outgoing_rtp.begin();
+  auto rtcp_iterator = incoming_rtcp.begin();
+
+  auto NextRtpTime = [&]() {
+    if (rtp_iterator != outgoing_rtp.end())
+      return static_cast<int64_t>(rtp_iterator->first);
+    return std::numeric_limits<int64_t>::max();
+  };
+
+  auto NextRtcpTime = [&]() {
+    if (rtcp_iterator != incoming_rtcp.end())
+      return static_cast<int64_t>(rtcp_iterator->first);
+    return std::numeric_limits<int64_t>::max();
+  };
+
+  auto NextProcessTime = [&]() {
+    if (rtcp_iterator != incoming_rtcp.end() ||
+        rtp_iterator != outgoing_rtp.end()) {
+      return clock.TimeInMicroseconds() +
+             std::max<int64_t>(cc.TimeUntilNextProcess() * 1000, 0);
+    }
+    return std::numeric_limits<int64_t>::max();
+  };
+
+  int64_t time_us = std::min(NextRtpTime(), NextRtcpTime());
+  while (time_us != std::numeric_limits<int64_t>::max()) {
+    clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
+    if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
+      clock.AdvanceTimeMilliseconds(rtcp_iterator->first / 1000 -
+                                    clock.TimeInMilliseconds());
+      const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
+      if (rtcp.type == kRtcpTransportFeedback) {
+        cc.GetTransportFeedbackObserver()->OnTransportFeedback(
+            *static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
+      }
+      ++rtcp_iterator;
+    }
+    if (clock.TimeInMicroseconds() >= NextRtpTime()) {
+      clock.AdvanceTimeMilliseconds(rtp_iterator->first / 1000 -
+                                    clock.TimeInMilliseconds());
+      const LoggedRtpPacket& rtp = *rtp_iterator->second;
+      if (rtp.header.extension.hasTransportSequenceNumber) {
+        RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
+        cc.GetTransportFeedbackObserver()->AddPacket(
+            rtp.header.extension.transportSequenceNumber, rtp.total_length, 0);
+        rtc::SentPacket sent_packet(
+            rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
+        cc.OnSentPacket(sent_packet);
+      }
+      ++rtp_iterator;
+    }
+    if (clock.TimeInMicroseconds() >= NextProcessTime())
+      cc.Process();
+    if (observer.GetAndResetBitrateUpdated()) {
+      uint32_t y = observer.last_bitrate_bps() / 1000;
+      max_y = std::max(max_y, y);
+      min_y = std::min(min_y, y);
+      float x = static_cast<float>(clock.TimeInMicroseconds() - begin_time_) /
+                1000000;
+      time_series.points.emplace_back(x, y);
+    }
+    time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()});
+  }
+  // Add the data set to the plot.
+  plot->series.push_back(std::move(time_series));
+
+  plot->xaxis_min = kDefaultXMin;
+  plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
+  plot->xaxis_label = "Time (s)";
+  plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
+  plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
+  plot->yaxis_label = "Bitrate (kbps)";
+  plot->title = "BWE";
+}
+
 }  // namespace plotting
 }  // namespace webrtc
diff --git a/webrtc/tools/event_log_visualizer/analyzer.h b/webrtc/tools/event_log_visualizer/analyzer.h
index 0b92c10..da3b206 100644
--- a/webrtc/tools/event_log_visualizer/analyzer.h
+++ b/webrtc/tools/event_log_visualizer/analyzer.h
@@ -13,8 +13,12 @@
 
 #include <vector>
 #include <map>
+#include <memory>
+#include <utility>
 
 #include "webrtc/call/rtc_event_log_parser.h"
+#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h"
 #include "webrtc/tools/event_log_visualizer/plot_base.h"
 
 namespace webrtc {
@@ -41,30 +45,41 @@
 
   void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot);
 
+  void CreateBweGraph(Plot* plot);
+
  private:
   class StreamId {
    public:
-    StreamId(uint32_t ssrc,
-             webrtc::PacketDirection direction,
-             webrtc::MediaType media_type)
-        : ssrc_(ssrc), direction_(direction), media_type_(media_type) {}
+    StreamId(uint32_t ssrc, webrtc::PacketDirection direction)
+        : ssrc_(ssrc), direction_(direction) {}
     bool operator<(const StreamId& other) const;
     bool operator==(const StreamId& other) const;
     uint32_t GetSsrc() const { return ssrc_; }
     webrtc::PacketDirection GetDirection() const { return direction_; }
-    webrtc::MediaType GetMediaType() const { return media_type_; }
 
    private:
     uint32_t ssrc_;
     webrtc::PacketDirection direction_;
-    webrtc::MediaType media_type_;
   };
 
   struct LoggedRtpPacket {
-    LoggedRtpPacket(uint64_t timestamp, RTPHeader header)
-        : timestamp(timestamp), header(header) {}
+    LoggedRtpPacket(uint64_t timestamp, RTPHeader header, size_t total_length)
+        : timestamp(timestamp), header(header), total_length(total_length) {}
     uint64_t timestamp;
     RTPHeader header;
+    size_t total_length;
+  };
+
+  struct LoggedRtcpPacket {
+    LoggedRtcpPacket(uint64_t timestamp,
+                     RTCPPacketType rtcp_type,
+                     std::unique_ptr<rtcp::RtcpPacket> rtcp_packet)
+        : timestamp(timestamp),
+          type(rtcp_type),
+          packet(std::move(rtcp_packet)) {}
+    uint64_t timestamp;
+    RTCPPacketType type;
+    std::unique_ptr<rtcp::RtcpPacket> packet;
   };
 
   struct BwePacketLossEvent {
@@ -85,6 +100,8 @@
   // if the stream has been configured.
   std::map<StreamId, std::vector<LoggedRtpPacket>> rtp_packets_;
 
+  std::map<StreamId, std::vector<LoggedRtcpPacket>> rtcp_packets_;
+
   // A list of all updates from the send-side loss-based bandwidth estimator.
   std::vector<BwePacketLossEvent> bwe_loss_updates_;
 
diff --git a/webrtc/tools/event_log_visualizer/generate_timeseries.cc b/webrtc/tools/event_log_visualizer/generate_timeseries.cc
index d213947..02e9ad3 100644
--- a/webrtc/tools/event_log_visualizer/generate_timeseries.cc
+++ b/webrtc/tools/event_log_visualizer/generate_timeseries.cc
@@ -43,6 +43,10 @@
 DEFINE_bool(plot_stream_bitrate,
             false,
             "Plot the bitrate used by each stream.");
+DEFINE_bool(plot_bwe,
+            false,
+            "Run the bandwidth estimator with the logged rtp and rtcp and plot "
+            "the output.");
 
 int main(int argc, char* argv[]) {
   std::string program_name = argv[0];
@@ -132,6 +136,10 @@
     }
   }
 
+  if (FLAGS_plot_all || FLAGS_plot_bwe) {
+    analyzer.CreateBweGraph(collection->append_new_plot());
+  }
+
   collection->draw();
 
   return 0;
diff --git a/webrtc/tools/event_log_visualizer/plot_base.h b/webrtc/tools/event_log_visualizer/plot_base.h
index 925dcec..0bfdf8d 100644
--- a/webrtc/tools/event_log_visualizer/plot_base.h
+++ b/webrtc/tools/event_log_visualizer/plot_base.h
@@ -18,7 +18,7 @@
 namespace webrtc {
 namespace plotting {
 
-enum PlotStyle { LINE_GRAPH, BAR_GRAPH };
+enum PlotStyle { LINE_GRAPH, LINE_DOT_GRAPH, BAR_GRAPH };
 
 struct TimeSeriesPoint {
   TimeSeriesPoint(float x, float y) : x(x), y(y) {}
diff --git a/webrtc/tools/event_log_visualizer/plot_python.cc b/webrtc/tools/event_log_visualizer/plot_python.cc
index 916bc2e..211d336 100644
--- a/webrtc/tools/event_log_visualizer/plot_python.cc
+++ b/webrtc/tools/event_log_visualizer/plot_python.cc
@@ -11,6 +11,7 @@
 #include "webrtc/tools/event_log_visualizer/plot_python.h"
 
 #include <stdio.h>
+#include <memory>
 
 namespace webrtc {
 namespace plotting {
@@ -58,6 +59,11 @@
       } else if (series[i].style == LINE_GRAPH) {
         printf("plt.plot(x%zu, y%zu, color=rgb_colors[%zu], label=\'%s\')\n", i,
                i, i, series[i].label.c_str());
+      } else if (series[i].style == LINE_DOT_GRAPH) {
+        printf(
+            "plt.plot(x%zu, y%zu, color=rgb_colors[%zu], label=\'%s\', "
+            "marker='.')\n",
+            i, i, i, series[i].label.c_str());
       } else {
         printf("raise Exception(\"Unknown graph type\")\n");
       }
diff --git a/webrtc/tools/tools.gyp b/webrtc/tools/tools.gyp
index 5c11370..46fb49a 100644
--- a/webrtc/tools/tools.gyp
+++ b/webrtc/tools/tools.gyp
@@ -109,9 +109,12 @@
           'target_name': 'event_log_visualizer',
           'type': 'executable',
           'dependencies': [
+            '<(webrtc_root)/webrtc.gyp:rtc_event_log',
             '<(webrtc_root)/webrtc.gyp:rtc_event_log_parser',
+            '<(webrtc_root)/modules/modules.gyp:congestion_controller',
             '<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
             '<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
+            '<(webrtc_root)/test/test.gyp:field_trial',
             '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
           ],
           'sources': [