Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(207)

Unified Diff: webrtc/tools/event_log_visualizer/analyzer.cc

Issue 1995523002: Visualization tool for WebrtcEventLogs (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Review comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..1625b7454d46493599aacfc63f208701250a4068
--- /dev/null
+++ b/webrtc/tools/event_log_visualizer/analyzer.cc
@@ -0,0 +1,714 @@
+/*
+ * 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) {
stefan-webrtc 2016/06/29 11:13:05 Should we make this a method on RTPHeader instead?
terelius 2016/07/01 16:54:45 I have no opinion on this, but this function does
stefan-webrtc 2016/07/05 08:58:11 I think it's fine as is for now.
+ 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;
+}
+
+// There could conceivably be e.g. an incoming and an outgoing stream with
+// the same SSRC. To get a unique identifier for each stream we append some
+// other values to the SSRC.
+uint64_t GetStreamId(uint32_t ssrc,
stefan-webrtc 2016/06/29 11:13:04 Wouldn't it be nicer to make stream id a comparabl
terelius 2016/07/01 16:54:45 Done. However, the advantage of precomputing a 64-
+ webrtc::PacketDirection direction,
+ webrtc::MediaType media_type) {
+ uint64_t stream = ssrc;
+ stream = (stream << 8) +
+ static_cast<uint64_t>(direction == webrtc::kIncomingPacket);
+ stream = (stream << 8) + static_cast<uint64_t>(media_type);
+ return stream;
+}
+
+uint32_t GetSsrcFromStreamId(uint64_t stream) {
+ return stream >> 16;
+}
+
+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) {
+ 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;
+ window_duration_ = 250000;
+ step_ = 10000;
stefan-webrtc 2016/06/29 11:13:04 These two members should be set in the initializer
terelius 2016/07/01 16:54:45 Done.
+}
+
+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(TimeSeries());
+ plot->series.back().swap(kv.second);
stefan-webrtc 2016/06/29 11:13:04 Why not simply push_back(kv.second)?
terelius 2016/07/01 16:54:45 Because I don't want to copy all the data in the T
+ }
+
+ 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(TimeSeries());
+ plot->series.back().swap(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(TimeSeries());
+ plot->series.back().swap(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<uint64_t, 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<uint64_t, SendReceiveTime> last_packet;
+ std::map<uint64_t, 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) {
stefan-webrtc 2016/06/29 11:13:04 Handling of config events seems to happen in many
terelius 2016/07/01 16:54:45 Config events are used in CreateDelayChangeGraph a
stefan-webrtc 2016/07/05 08:58:11 If it becomes better in a follow up I think I'm ok
+ VideoReceiveStream::Config config(nullptr);
+ parsed_log_.GetVideoReceiveConfig(i, &config);
+ uint64_t stream = GetStreamId(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) {
+ uint64_t stream = GetStreamId(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_)) {
stefan-webrtc 2016/06/29 11:13:05 Maybe we could have a method similar to: GetEvents
terelius 2016/07/01 16:54:45 The follow up CL that caches a map from streams to
stefan-webrtc 2016/07/05 08:58:11 Acknowledged.
+ uint64_t stream =
+ GetStreamId(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(GetSsrcFromStreamId(kv.first));
+ kv.second.style = BAR_GRAPH;
+ plot->series.push_back(TimeSeries());
+ plot->series.back().swap(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<uint64_t, 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<uint64_t, SendReceiveTime> last_packet;
+ std::map<uint64_t, 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);
+ uint64_t stream = GetStreamId(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) {
+ uint64_t stream = GetStreamId(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_)) {
+ uint64_t stream =
+ GetStreamId(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(GetSsrcFromStreamId(kv.first));
+ kv.second.style = LINE_GRAPH;
+ plot->series.push_back(TimeSeries());
+ plot->series.back().swap(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 bandwitch 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 bandwitch used by that stream.
stefan-webrtc 2016/06/29 11:13:04 bandwidth
terelius 2016/07/01 16:54:45 Done. :)
stefan-webrtc 2016/07/05 08:58:11 Nope, still there... :)
terelius 2016/07/06 15:15:12 Done.
+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

Powered by Google App Engine
This is Rietveld 408576698