| Index: webrtc/tools/event_log_visualizer/analyzer.cc
 | 
| diff --git a/webrtc/tools/event_log_visualizer/analyzer.cc b/webrtc/tools/event_log_visualizer/analyzer.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..44270b3922b20473c5ec578930bb424a5790299f
 | 
| --- /dev/null
 | 
| +++ b/webrtc/tools/event_log_visualizer/analyzer.cc
 | 
| @@ -0,0 +1,728 @@
 | 
| +/*
 | 
| + *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 | 
| + *
 | 
| + *  Use of this source code is governed by a BSD-style license
 | 
| + *  that can be found in the LICENSE file in the root of the source
 | 
| + *  tree. An additional intellectual property rights grant can be found
 | 
| + *  in the file PATENTS.  All contributing project authors may
 | 
| + *  be found in the AUTHORS file in the root of the source tree.
 | 
| + */
 | 
| +
 | 
| +#include "webrtc/tools/event_log_visualizer/analyzer.h"
 | 
| +
 | 
| +#include <algorithm>
 | 
| +#include <limits>
 | 
| +#include <map>
 | 
| +#include <sstream>
 | 
| +#include <string>
 | 
| +#include <utility>
 | 
| +
 | 
| +#include "webrtc/audio_receive_stream.h"
 | 
| +#include "webrtc/audio_send_stream.h"
 | 
| +#include "webrtc/base/checks.h"
 | 
| +#include "webrtc/call.h"
 | 
| +#include "webrtc/common_types.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/video_receive_stream.h"
 | 
| +#include "webrtc/video_send_stream.h"
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +std::string HeaderToString(const webrtc::RTPHeader& parsed_header) {
 | 
| +  std::stringstream ss;
 | 
| +  ss << "Marker=" << parsed_header.markerBit
 | 
| +     << ", PType=" << parsed_header.payloadType
 | 
| +     << ", SeqNum=" << parsed_header.sequenceNumber
 | 
| +     << ", CaptureTime=" << parsed_header.timestamp
 | 
| +     << ", SSRC=" << parsed_header.ssrc;
 | 
| +  return ss.str();
 | 
| +}
 | 
| +
 | 
| +std::string SsrcToString(uint32_t ssrc) {
 | 
| +  std::stringstream ss;
 | 
| +  ss << "SSRC " << ssrc;
 | 
| +  return ss.str();
 | 
| +}
 | 
| +
 | 
| +// Checks whether an SSRC is contained in the list of desired SSRCs.
 | 
| +// Note that an empty SSRC list matches every SSRC.
 | 
| +bool MatchingSsrc(uint32_t ssrc, const std::vector<uint32_t>& desired_ssrc) {
 | 
| +  if (desired_ssrc.size() == 0)
 | 
| +    return true;
 | 
| +  return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) !=
 | 
| +         desired_ssrc.end();
 | 
| +}
 | 
| +
 | 
| +double AbsSendTimeToMicroseconds(int64_t abs_send_time) {
 | 
| +  // The timestamp is a fixed point representation with 6 bits for seconds
 | 
| +  // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the
 | 
| +  // time in seconds and then multiply by 1000000 to convert to microseconds.
 | 
| +  static constexpr double kTimestampToMicroSec =
 | 
| +      1000000.0 / static_cast<double>(1 << 18);
 | 
| +  return abs_send_time * kTimestampToMicroSec;
 | 
| +}
 | 
| +
 | 
| +// Computes the difference |later| - |earlier| where |later| and |earlier|
 | 
| +// are counters that wrap at |modulus|. The difference is chosen to have the
 | 
| +// least absolute value. For example if |modulus| is 8, then the difference will
 | 
| +// be chosen in the range [-3, 4]. If |modulus| is 9, then the difference will
 | 
| +// be in [-4, 4].
 | 
| +int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) {
 | 
| +  RTC_DCHECK_LE(1, modulus);
 | 
| +  RTC_DCHECK_LT(later, modulus);
 | 
| +  RTC_DCHECK_LT(earlier, modulus);
 | 
| +  int64_t difference =
 | 
| +      static_cast<int64_t>(later) - static_cast<int64_t>(earlier);
 | 
| +  int64_t max_difference = modulus / 2;
 | 
| +  int64_t min_difference = max_difference - modulus + 1;
 | 
| +  if (difference > max_difference) {
 | 
| +    difference -= modulus;
 | 
| +  }
 | 
| +  if (difference < min_difference) {
 | 
| +    difference += modulus;
 | 
| +  }
 | 
| +  return difference;
 | 
| +}
 | 
| +
 | 
| +class StreamId {
 | 
| + public:
 | 
| +  StreamId(uint32_t ssrc,
 | 
| +           webrtc::PacketDirection direction,
 | 
| +           webrtc::MediaType media_type)
 | 
| +      : ssrc_(ssrc), direction_(direction), media_type_(media_type) {}
 | 
| +
 | 
| +  bool operator<(const StreamId& other) const {
 | 
| +    if (ssrc_ < other.ssrc_) {
 | 
| +      return true;
 | 
| +    }
 | 
| +    if (ssrc_ == other.ssrc_) {
 | 
| +      if (media_type_ < other.media_type_) {
 | 
| +        return true;
 | 
| +      }
 | 
| +      if (media_type_ == other.media_type_) {
 | 
| +        if (direction_ < other.direction_) {
 | 
| +          return true;
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  bool operator==(const StreamId& other) const {
 | 
| +    return ssrc_ == other.ssrc_ && direction_ == other.direction_ &&
 | 
| +           media_type_ == other.media_type_;
 | 
| +  }
 | 
| +
 | 
| +  uint32_t GetSsrc() const { return ssrc_; }
 | 
| +
 | 
| + private:
 | 
| +  uint32_t ssrc_;
 | 
| +  webrtc::PacketDirection direction_;
 | 
| +  webrtc::MediaType media_type_;
 | 
| +};
 | 
| +
 | 
| +const double kXMargin = 1.02;
 | 
| +const double kYMargin = 1.1;
 | 
| +const double kDefaultXMin = -1;
 | 
| +const double kDefaultYMin = -1;
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +namespace webrtc {
 | 
| +namespace plotting {
 | 
| +
 | 
| +EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log,
 | 
| +                                   bool extra_info)
 | 
| +    : parsed_log_(log),
 | 
| +      extra_point_info_(extra_info),
 | 
| +      window_duration_(250000),
 | 
| +      step_(10000) {
 | 
| +  uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
 | 
| +  uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT)
 | 
| +      continue;
 | 
| +    if (event_type == ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT)
 | 
| +      continue;
 | 
| +    if (event_type == ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT)
 | 
| +      continue;
 | 
| +    if (event_type == ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT)
 | 
| +      continue;
 | 
| +    uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +    first_timestamp = std::min(first_timestamp, timestamp);
 | 
| +    last_timestamp = std::max(last_timestamp, timestamp);
 | 
| +  }
 | 
| +  if (last_timestamp < first_timestamp) {
 | 
| +    // No useful events in the log.
 | 
| +    first_timestamp = last_timestamp = 0;
 | 
| +  }
 | 
| +  begin_time_ = first_timestamp;
 | 
| +  end_time_ = last_timestamp;
 | 
| +}
 | 
| +
 | 
| +void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
 | 
| +                                         Plot* plot) {
 | 
| +  std::map<uint32_t, TimeSeries> time_series;
 | 
| +
 | 
| +  PacketDirection direction;
 | 
| +  MediaType media_type;
 | 
| +  uint8_t header[IP_PACKET_SIZE];
 | 
| +  size_t header_length, total_length;
 | 
| +  float max_y = 0;
 | 
| +
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::RTP_EVENT) {
 | 
| +      parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
 | 
| +                               &header_length, &total_length);
 | 
| +      if (direction == desired_direction) {
 | 
| +        // Parse header to get SSRC.
 | 
| +        RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
 | 
| +        RTPHeader parsed_header;
 | 
| +        rtp_parser.Parse(&parsed_header);
 | 
| +        // Filter on SSRC.
 | 
| +        if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
 | 
| +          uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +          float x = static_cast<float>(timestamp - begin_time_) / 1000000;
 | 
| +          float y = total_length;
 | 
| +          max_y = std::max(max_y, y);
 | 
| +          std::string message;
 | 
| +          if (extra_point_info_) {
 | 
| +            message = HeaderToString(parsed_header);
 | 
| +          }
 | 
| +          time_series[parsed_header.ssrc].points.push_back(
 | 
| +              TimeSeriesPoint(x, y, message));
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Set labels and put in graph.
 | 
| +  for (auto& kv : time_series) {
 | 
| +    kv.second.label = SsrcToString(kv.first);
 | 
| +    kv.second.style = BAR_GRAPH;
 | 
| +    plot->series.push_back(std::move(kv.second));
 | 
| +  }
 | 
| +
 | 
| +  plot->xaxis_min = kDefaultXMin;
 | 
| +  plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
 | 
| +  plot->xaxis_label = "Time (s)";
 | 
| +  plot->yaxis_min = kDefaultYMin;
 | 
| +  plot->yaxis_max = max_y * kYMargin;
 | 
| +  plot->yaxis_label = "Packet size (bytes)";
 | 
| +  if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
 | 
| +    plot->title = "Incoming RTP packets";
 | 
| +  } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
 | 
| +    plot->title = "Outgoing RTP packets";
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// For each SSRC, plot the time between the consecutive playouts.
 | 
| +void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
 | 
| +  std::map<uint32_t, TimeSeries> time_series;
 | 
| +  std::map<uint32_t, uint64_t> last_playout;
 | 
| +
 | 
| +  uint32_t ssrc;
 | 
| +  float max_y = 0;
 | 
| +
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
 | 
| +      parsed_log_.GetAudioPlayout(i, &ssrc);
 | 
| +      uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +      if (MatchingSsrc(ssrc, desired_ssrc_)) {
 | 
| +        float x = static_cast<float>(timestamp - begin_time_) / 1000000;
 | 
| +        float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
 | 
| +        if (time_series[ssrc].points.size() == 0) {
 | 
| +          // There were no previusly logged playout for this SSRC.
 | 
| +          // Generate a point, but place it on the x-axis.
 | 
| +          y = 0;
 | 
| +        }
 | 
| +        max_y = std::max(max_y, y);
 | 
| +        time_series[ssrc].points.push_back(TimeSeriesPoint(x, y, ""));
 | 
| +        last_playout[ssrc] = timestamp;
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Set labels and put in graph.
 | 
| +  for (auto& kv : time_series) {
 | 
| +    kv.second.label = SsrcToString(kv.first);
 | 
| +    kv.second.style = BAR_GRAPH;
 | 
| +    plot->series.push_back(std::move(kv.second));
 | 
| +  }
 | 
| +
 | 
| +  plot->xaxis_min = kDefaultXMin;
 | 
| +  plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
 | 
| +  plot->xaxis_label = "Time (s)";
 | 
| +  plot->yaxis_min = kDefaultYMin;
 | 
| +  plot->yaxis_max = max_y * kYMargin;
 | 
| +  plot->yaxis_label = "Time since last playout (ms)";
 | 
| +  plot->title = "Audio playout";
 | 
| +}
 | 
| +
 | 
| +// For each SSRC, plot the time between the consecutive playouts.
 | 
| +void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
 | 
| +  std::map<uint32_t, TimeSeries> time_series;
 | 
| +  std::map<uint32_t, uint16_t> last_seqno;
 | 
| +
 | 
| +  PacketDirection direction;
 | 
| +  MediaType media_type;
 | 
| +  uint8_t header[IP_PACKET_SIZE];
 | 
| +  size_t header_length, total_length;
 | 
| +
 | 
| +  int max_y = 1;
 | 
| +  int min_y = 0;
 | 
| +
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::RTP_EVENT) {
 | 
| +      parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
 | 
| +                               &header_length, &total_length);
 | 
| +      uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +      if (direction == PacketDirection::kIncomingPacket) {
 | 
| +        // Parse header to get SSRC.
 | 
| +        RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
 | 
| +        RTPHeader parsed_header;
 | 
| +        rtp_parser.Parse(&parsed_header);
 | 
| +        // Filter on SSRC.
 | 
| +        if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
 | 
| +          float x = static_cast<float>(timestamp - begin_time_) / 1000000;
 | 
| +          int y = WrappingDifference(parsed_header.sequenceNumber,
 | 
| +                                     last_seqno[parsed_header.ssrc], 1ul << 16);
 | 
| +          if (time_series[parsed_header.ssrc].points.size() == 0) {
 | 
| +            // There were no previusly logged playout for this SSRC.
 | 
| +            // Generate a point, but place it on the x-axis.
 | 
| +            y = 0;
 | 
| +          }
 | 
| +          max_y = std::max(max_y, y);
 | 
| +          min_y = std::min(min_y, y);
 | 
| +          time_series[parsed_header.ssrc].points.push_back(
 | 
| +              TimeSeriesPoint(x, y, ""));
 | 
| +          last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Set labels and put in graph.
 | 
| +  for (auto& kv : time_series) {
 | 
| +    kv.second.label = SsrcToString(kv.first);
 | 
| +    kv.second.style = BAR_GRAPH;
 | 
| +    plot->series.push_back(std::move(kv.second));
 | 
| +  }
 | 
| +
 | 
| +  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 = "Difference since last packet";
 | 
| +  plot->title = "Sequence number";
 | 
| +}
 | 
| +
 | 
| +void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
 | 
| +  // Maps a stream identifier consisting of ssrc, direction and MediaType
 | 
| +  // to the header extensions used by that stream,
 | 
| +  std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
 | 
| +
 | 
| +  struct SendReceiveTime {
 | 
| +    SendReceiveTime() = default;
 | 
| +    SendReceiveTime(uint32_t send_time, uint64_t recv_time)
 | 
| +        : absolute_send_time(send_time), receive_timestamp(recv_time) {}
 | 
| +    uint32_t absolute_send_time;  // 24-bit value in units of 2^-18 seconds.
 | 
| +    uint64_t receive_timestamp;   // In microseconds.
 | 
| +  };
 | 
| +  std::map<StreamId, SendReceiveTime> last_packet;
 | 
| +  std::map<StreamId, TimeSeries> time_series;
 | 
| +
 | 
| +  PacketDirection direction;
 | 
| +  MediaType media_type;
 | 
| +  uint8_t header[IP_PACKET_SIZE];
 | 
| +  size_t header_length, total_length;
 | 
| +
 | 
| +  double max_y = 10;
 | 
| +  double min_y = 0;
 | 
| +
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
 | 
| +      VideoReceiveStream::Config config(nullptr);
 | 
| +      parsed_log_.GetVideoReceiveConfig(i, &config);
 | 
| +      StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
 | 
| +                      MediaType::VIDEO);
 | 
| +      extension_maps[stream].Erase();
 | 
| +      for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
 | 
| +        const std::string& extension = config.rtp.extensions[j].uri;
 | 
| +        int id = config.rtp.extensions[j].id;
 | 
| +        extension_maps[stream].Register(StringToRtpExtensionType(extension),
 | 
| +                                        id);
 | 
| +      }
 | 
| +    } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
 | 
| +      VideoSendStream::Config config(nullptr);
 | 
| +      parsed_log_.GetVideoSendConfig(i, &config);
 | 
| +      for (auto ssrc : config.rtp.ssrcs) {
 | 
| +        StreamId stream(ssrc, kIncomingPacket, MediaType::VIDEO);
 | 
| +        extension_maps[stream].Erase();
 | 
| +        for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
 | 
| +          const std::string& extension = config.rtp.extensions[j].uri;
 | 
| +          int id = config.rtp.extensions[j].id;
 | 
| +          extension_maps[stream].Register(StringToRtpExtensionType(extension),
 | 
| +                                          id);
 | 
| +        }
 | 
| +      }
 | 
| +    } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
 | 
| +      AudioReceiveStream::Config config;
 | 
| +      // TODO(terelius): Parse the audio configs once we have them
 | 
| +    } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
 | 
| +      AudioSendStream::Config config(nullptr);
 | 
| +      // TODO(terelius): Parse the audio configs once we have them
 | 
| +    } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
 | 
| +      parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
 | 
| +                               &header_length, &total_length);
 | 
| +      if (direction == kIncomingPacket) {
 | 
| +        // Parse header to get SSRC.
 | 
| +        RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
 | 
| +        RTPHeader parsed_header;
 | 
| +        rtp_parser.Parse(&parsed_header);
 | 
| +        // Filter on SSRC.
 | 
| +        if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
 | 
| +          StreamId stream(parsed_header.ssrc, direction, media_type);
 | 
| +          // 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];
 | 
| +            rtp_parser.Parse(&parsed_header, extension_map);
 | 
| +            if (parsed_header.extension.hasAbsoluteSendTime) {
 | 
| +              uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +              int64_t send_time_diff = WrappingDifference(
 | 
| +                  parsed_header.extension.absoluteSendTime,
 | 
| +                  last_packet[stream].absolute_send_time, 1ul << 24);
 | 
| +              int64_t recv_time_diff =
 | 
| +                  timestamp - last_packet[stream].receive_timestamp;
 | 
| +
 | 
| +              float x = static_cast<float>(timestamp - begin_time_) / 1000000;
 | 
| +              double y = static_cast<double>(
 | 
| +                             recv_time_diff -
 | 
| +                             AbsSendTimeToMicroseconds(send_time_diff)) /
 | 
| +                         1000;
 | 
| +              if (time_series[stream].points.size() == 0) {
 | 
| +                // There were no previusly logged playout for this SSRC.
 | 
| +                // Generate a point, but place it on the x-axis.
 | 
| +                y = 0;
 | 
| +              }
 | 
| +              max_y = std::max(max_y, y);
 | 
| +              min_y = std::min(min_y, y);
 | 
| +              time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
 | 
| +              last_packet[stream] = SendReceiveTime(
 | 
| +                  parsed_header.extension.absoluteSendTime, timestamp);
 | 
| +            }
 | 
| +          }
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Set labels and put in graph.
 | 
| +  for (auto& kv : time_series) {
 | 
| +    kv.second.label = SsrcToString(kv.first.GetSsrc());
 | 
| +    kv.second.style = BAR_GRAPH;
 | 
| +    plot->series.push_back(std::move(kv.second));
 | 
| +  }
 | 
| +
 | 
| +  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 = "Latency change (ms)";
 | 
| +  plot->title = "Network latency change between consecutive packets";
 | 
| +}
 | 
| +
 | 
| +void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
 | 
| +  // TODO(terelius): Refactor
 | 
| +
 | 
| +  // Maps a stream identifier consisting of ssrc, direction and MediaType
 | 
| +  // to the header extensions used by that stream.
 | 
| +  std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
 | 
| +
 | 
| +  struct SendReceiveTime {
 | 
| +    SendReceiveTime() = default;
 | 
| +    SendReceiveTime(uint32_t send_time, uint64_t recv_time, double accumulated)
 | 
| +        : absolute_send_time(send_time),
 | 
| +          receive_timestamp(recv_time),
 | 
| +          accumulated_delay(accumulated) {}
 | 
| +    uint32_t absolute_send_time;  // 24-bit value in units of 2^-18 seconds.
 | 
| +    uint64_t receive_timestamp;   // In microseconds.
 | 
| +    double accumulated_delay;     // In milliseconds.
 | 
| +  };
 | 
| +  std::map<StreamId, SendReceiveTime> last_packet;
 | 
| +  std::map<StreamId, TimeSeries> time_series;
 | 
| +
 | 
| +  PacketDirection direction;
 | 
| +  MediaType media_type;
 | 
| +  uint8_t header[IP_PACKET_SIZE];
 | 
| +  size_t header_length, total_length;
 | 
| +
 | 
| +  double max_y = 10;
 | 
| +  double min_y = 0;
 | 
| +
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
 | 
| +      VideoReceiveStream::Config config(nullptr);
 | 
| +      parsed_log_.GetVideoReceiveConfig(i, &config);
 | 
| +      StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
 | 
| +                      MediaType::VIDEO);
 | 
| +      extension_maps[stream].Erase();
 | 
| +      for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
 | 
| +        const std::string& extension = config.rtp.extensions[j].uri;
 | 
| +        int id = config.rtp.extensions[j].id;
 | 
| +        extension_maps[stream].Register(StringToRtpExtensionType(extension),
 | 
| +                                        id);
 | 
| +      }
 | 
| +    } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
 | 
| +      VideoSendStream::Config config(nullptr);
 | 
| +      parsed_log_.GetVideoSendConfig(i, &config);
 | 
| +      for (auto ssrc : config.rtp.ssrcs) {
 | 
| +        StreamId stream(ssrc, kIncomingPacket, MediaType::VIDEO);
 | 
| +        extension_maps[stream].Erase();
 | 
| +        for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
 | 
| +          const std::string& extension = config.rtp.extensions[j].uri;
 | 
| +          int id = config.rtp.extensions[j].id;
 | 
| +          extension_maps[stream].Register(StringToRtpExtensionType(extension),
 | 
| +                                          id);
 | 
| +        }
 | 
| +      }
 | 
| +    } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
 | 
| +      AudioReceiveStream::Config config;
 | 
| +      // TODO(terelius): Parse the audio configs once we have them
 | 
| +    } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
 | 
| +      AudioSendStream::Config config(nullptr);
 | 
| +      // TODO(terelius): Parse the audio configs once we have them
 | 
| +    } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
 | 
| +      parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
 | 
| +                               &header_length, &total_length);
 | 
| +      if (direction == kIncomingPacket) {
 | 
| +        // Parse header to get SSRC.
 | 
| +        RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
 | 
| +        RTPHeader parsed_header;
 | 
| +        rtp_parser.Parse(&parsed_header);
 | 
| +        // Filter on SSRC.
 | 
| +        if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
 | 
| +          StreamId stream(parsed_header.ssrc, direction, media_type);
 | 
| +          // 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];
 | 
| +            rtp_parser.Parse(&parsed_header, extension_map);
 | 
| +            if (parsed_header.extension.hasAbsoluteSendTime) {
 | 
| +              uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +              int64_t send_time_diff = WrappingDifference(
 | 
| +                  parsed_header.extension.absoluteSendTime,
 | 
| +                  last_packet[stream].absolute_send_time, 1ul << 24);
 | 
| +              int64_t recv_time_diff =
 | 
| +                  timestamp - last_packet[stream].receive_timestamp;
 | 
| +
 | 
| +              float x = static_cast<float>(timestamp - begin_time_) / 1000000;
 | 
| +              double y = last_packet[stream].accumulated_delay +
 | 
| +                         static_cast<double>(
 | 
| +                             recv_time_diff -
 | 
| +                             AbsSendTimeToMicroseconds(send_time_diff)) /
 | 
| +                             1000;
 | 
| +              if (time_series[stream].points.size() == 0) {
 | 
| +                // There were no previusly logged playout for this SSRC.
 | 
| +                // Generate a point, but place it on the x-axis.
 | 
| +                y = 0;
 | 
| +              }
 | 
| +              max_y = std::max(max_y, y);
 | 
| +              min_y = std::min(min_y, y);
 | 
| +              time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
 | 
| +              last_packet[stream] = SendReceiveTime(
 | 
| +                  parsed_header.extension.absoluteSendTime, timestamp, y);
 | 
| +            }
 | 
| +          }
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Set labels and put in graph.
 | 
| +  for (auto& kv : time_series) {
 | 
| +    kv.second.label = SsrcToString(kv.first.GetSsrc());
 | 
| +    kv.second.style = LINE_GRAPH;
 | 
| +    plot->series.push_back(std::move(kv.second));
 | 
| +  }
 | 
| +
 | 
| +  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 = "Latency change (ms)";
 | 
| +  plot->title = "Accumulated network latency change";
 | 
| +}
 | 
| +
 | 
| +// Plot the total bandwidth used by all RTP streams.
 | 
| +void EventLogAnalyzer::CreateTotalBitrateGraph(
 | 
| +    PacketDirection desired_direction,
 | 
| +    Plot* plot) {
 | 
| +  struct TimestampSize {
 | 
| +    TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
 | 
| +    uint64_t timestamp;
 | 
| +    size_t size;
 | 
| +  };
 | 
| +  std::vector<TimestampSize> packets;
 | 
| +
 | 
| +  PacketDirection direction;
 | 
| +  size_t total_length;
 | 
| +
 | 
| +  // Extract timestamps and sizes for the relevant packets.
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::RTP_EVENT) {
 | 
| +      parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
 | 
| +                               &total_length);
 | 
| +      if (direction == desired_direction) {
 | 
| +        uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +        packets.push_back(TimestampSize(timestamp, total_length));
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  size_t window_index_begin = 0;
 | 
| +  size_t window_index_end = 0;
 | 
| +  size_t bytes_in_window = 0;
 | 
| +  float max_y = 0;
 | 
| +
 | 
| +  // Calculate a moving average of the bitrate and store in a TimeSeries.
 | 
| +  plot->series.push_back(TimeSeries());
 | 
| +  for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
 | 
| +    while (window_index_end < packets.size() &&
 | 
| +           packets[window_index_end].timestamp < time) {
 | 
| +      bytes_in_window += packets[window_index_end].size;
 | 
| +      window_index_end++;
 | 
| +    }
 | 
| +    while (window_index_begin < packets.size() &&
 | 
| +           packets[window_index_begin].timestamp < time - window_duration_) {
 | 
| +      bytes_in_window -= packets[window_index_begin].size;
 | 
| +      window_index_begin++;
 | 
| +    }
 | 
| +    RTC_DCHECK_LE(0ul, bytes_in_window);
 | 
| +    float window_duration_in_seconds =
 | 
| +        static_cast<float>(window_duration_) / 1000000;
 | 
| +    float x = static_cast<float>(time - begin_time_) / 1000000;
 | 
| +    float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
 | 
| +    max_y = std::max(max_y, y);
 | 
| +    plot->series.back().points.push_back(TimeSeriesPoint(x, y));
 | 
| +  }
 | 
| +
 | 
| +  // Set labels.
 | 
| +  if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
 | 
| +    plot->series.back().label = "Incoming bitrate";
 | 
| +  } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
 | 
| +    plot->series.back().label = "Outgoing bitrate";
 | 
| +  }
 | 
| +  plot->series.back().style = LINE_GRAPH;
 | 
| +
 | 
| +  plot->xaxis_min = kDefaultXMin;
 | 
| +  plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
 | 
| +  plot->xaxis_label = "Time (s)";
 | 
| +  plot->yaxis_min = kDefaultYMin;
 | 
| +  plot->yaxis_max = max_y * kYMargin;
 | 
| +  plot->yaxis_label = "Bitrate (kbps)";
 | 
| +  if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
 | 
| +    plot->title = "Incoming RTP bitrate";
 | 
| +  } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
 | 
| +    plot->title = "Outgoing RTP bitrate";
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// For each SSRC, plot the bandwidth used by that stream.
 | 
| +void EventLogAnalyzer::CreateStreamBitrateGraph(
 | 
| +    PacketDirection desired_direction,
 | 
| +    Plot* plot) {
 | 
| +  struct TimestampSize {
 | 
| +    TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
 | 
| +    uint64_t timestamp;
 | 
| +    size_t size;
 | 
| +  };
 | 
| +  std::map<uint32_t, std::vector<TimestampSize> > packets;
 | 
| +
 | 
| +  PacketDirection direction;
 | 
| +  MediaType media_type;
 | 
| +  uint8_t header[IP_PACKET_SIZE];
 | 
| +  size_t header_length, total_length;
 | 
| +
 | 
| +  // Extract timestamps and sizes for the relevant packets.
 | 
| +  for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
 | 
| +    ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
 | 
| +    if (event_type == ParsedRtcEventLog::RTP_EVENT) {
 | 
| +      parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
 | 
| +                               &header_length, &total_length);
 | 
| +      if (direction == desired_direction) {
 | 
| +        // Parse header to get SSRC.
 | 
| +        RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
 | 
| +        RTPHeader parsed_header;
 | 
| +        rtp_parser.Parse(&parsed_header);
 | 
| +        // Filter on SSRC.
 | 
| +        if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
 | 
| +          uint64_t timestamp = parsed_log_.GetTimestamp(i);
 | 
| +          packets[parsed_header.ssrc].push_back(
 | 
| +              TimestampSize(timestamp, total_length));
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  float max_y = 0;
 | 
| +
 | 
| +  for (auto& kv : packets) {
 | 
| +    size_t window_index_begin = 0;
 | 
| +    size_t window_index_end = 0;
 | 
| +    size_t bytes_in_window = 0;
 | 
| +
 | 
| +    // Calculate a moving average of the bitrate and store in a TimeSeries.
 | 
| +    plot->series.push_back(TimeSeries());
 | 
| +    for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
 | 
| +      while (window_index_end < kv.second.size() &&
 | 
| +             kv.second[window_index_end].timestamp < time) {
 | 
| +        bytes_in_window += kv.second[window_index_end].size;
 | 
| +        window_index_end++;
 | 
| +      }
 | 
| +      while (window_index_begin < kv.second.size() &&
 | 
| +             kv.second[window_index_begin].timestamp <
 | 
| +                 time - window_duration_) {
 | 
| +        bytes_in_window -= kv.second[window_index_begin].size;
 | 
| +        window_index_begin++;
 | 
| +      }
 | 
| +      RTC_DCHECK_LE(0ul, bytes_in_window);
 | 
| +      float window_duration_in_seconds =
 | 
| +          static_cast<float>(window_duration_) / 1000000;
 | 
| +      float x = static_cast<float>(time - begin_time_) / 1000000;
 | 
| +      float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
 | 
| +      max_y = std::max(max_y, y);
 | 
| +      plot->series.back().points.push_back(TimeSeriesPoint(x, y));
 | 
| +    }
 | 
| +
 | 
| +    // Set labels.
 | 
| +    plot->series.back().label = SsrcToString(kv.first);
 | 
| +    plot->series.back().style = LINE_GRAPH;
 | 
| +  }
 | 
| +
 | 
| +  plot->xaxis_min = kDefaultXMin;
 | 
| +  plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
 | 
| +  plot->xaxis_label = "Time (s)";
 | 
| +  plot->yaxis_min = kDefaultYMin;
 | 
| +  plot->yaxis_max = max_y * kYMargin;
 | 
| +  plot->yaxis_label = "Bitrate (kbps)";
 | 
| +  if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
 | 
| +    plot->title = "Incoming bitrate per stream";
 | 
| +  } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
 | 
| +    plot->title = "Outgoing bitrate per stream";
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +}  // namespace plotting
 | 
| +}  // namespace webrtc
 | 
| 
 |