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/2188033004 .

Cr-Commit-Position: refs/heads/master@{#13558}
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");
       }