From dc35dcd4dce9d6e3737434aed57ec3b78b0feba0 Mon Sep 17 00:00:00 2001 From: terelius Date: Mon, 1 Aug 2016 12:03:27 -0700 Subject: [PATCH] Convenience functions to set axis properties in visualization tool. Also makes the properties protected, as previously requested by Stefan. Review-Url: https://codereview.webrtc.org/2179223003 Cr-Commit-Position: refs/heads/master@{#13593} --- webrtc/tools/BUILD.gn | 1 + webrtc/tools/event_log_visualizer/analyzer.cc | 176 ++++++------------ webrtc/tools/event_log_visualizer/analyzer.h | 3 + .../generate_timeseries.cc | 24 +-- .../tools/event_log_visualizer/plot_base.cc | 75 ++++++++ webrtc/tools/event_log_visualizer/plot_base.h | 77 ++++++-- .../tools/event_log_visualizer/plot_python.cc | 59 +++--- .../tools/event_log_visualizer/plot_python.h | 6 +- webrtc/tools/tools.gyp | 1 + 9 files changed, 247 insertions(+), 175 deletions(-) create mode 100644 webrtc/tools/event_log_visualizer/plot_base.cc diff --git a/webrtc/tools/BUILD.gn b/webrtc/tools/BUILD.gn index 45d8051ee2..0bc32fcb93 100644 --- a/webrtc/tools/BUILD.gn +++ b/webrtc/tools/BUILD.gn @@ -178,6 +178,7 @@ if (rtc_include_tests) { "event_log_visualizer/analyzer.cc", "event_log_visualizer/analyzer.h", "event_log_visualizer/generate_timeseries.cc", + "event_log_visualizer/plot_base.cc", "event_log_visualizer/plot_base.h", "event_log_visualizer/plot_python.cc", "event_log_visualizer/plot_python.h", diff --git a/webrtc/tools/event_log_visualizer/analyzer.cc b/webrtc/tools/event_log_visualizer/analyzer.cc index ba92da7ad6..c97a30fc0e 100644 --- a/webrtc/tools/event_log_visualizer/analyzer.cc +++ b/webrtc/tools/event_log_visualizer/analyzer.cc @@ -32,6 +32,9 @@ #include "webrtc/video_receive_stream.h" #include "webrtc/video_send_stream.h" +namespace webrtc { +namespace plotting { + namespace { std::string SsrcToString(uint32_t ssrc) { @@ -90,17 +93,13 @@ void RegisterHeaderExtensions( } } -const double kXMargin = 1.02; -const double kYMargin = 1.1; -const double kDefaultXMin = -1; -const double kDefaultYMin = -1; +constexpr float kLeftMargin = 0.01f; +constexpr float kRightMargin = 0.02f; +constexpr float kBottomMargin = 0.02f; +constexpr float kTopMargin = 0.05f; } // namespace -namespace webrtc { -namespace plotting { - - bool EventLogAnalyzer::StreamId::operator<(const StreamId& other) const { if (ssrc_ < other.ssrc_) { return true; @@ -274,6 +273,7 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log) } begin_time_ = first_timestamp; end_time_ = last_timestamp; + call_duration_s_ = static_cast(end_time_ - begin_time_) / 1000000; } class BitrateObserver : public CongestionController::Observer, @@ -311,7 +311,6 @@ void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_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); @@ -328,7 +327,6 @@ void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction, uint64_t timestamp = parsed_log_.GetTimestamp(i); float x = static_cast(timestamp - begin_time_) / 1000000; float y = total_length; - max_y = std::max(max_y, y); time_series[parsed_header.ssrc].points.push_back( TimeSeriesPoint(x, y)); } @@ -340,19 +338,16 @@ void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction, 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->series_list_.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)"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Packet size (bytes)", kBottomMargin, + kTopMargin); if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->title = "Incoming RTP packets"; + plot->SetTitle("Incoming RTP packets"); } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->title = "Outgoing RTP packets"; + plot->SetTitle("Outgoing RTP packets"); } } @@ -362,7 +357,6 @@ void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { std::map 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); @@ -377,7 +371,6 @@ void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { // 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; } @@ -388,16 +381,13 @@ void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { 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->series_list_.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"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Time since last playout (ms)", kBottomMargin, + kTopMargin); + plot->SetTitle("Audio playout"); } // For each SSRC, plot the time between the consecutive playouts. @@ -410,9 +400,6 @@ void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { 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) { @@ -434,8 +421,6 @@ void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { // 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; @@ -448,22 +433,16 @@ void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { 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->series_list_.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"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Difference since last packet", kBottomMargin, + kTopMargin); + plot->SetTitle("Sequence number"); } void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) { - double max_y = 10; - double min_y = 0; - for (auto& kv : rtp_packets_) { StreamId stream_id = kv.first; // Filter on direction and SSRC. @@ -498,28 +477,20 @@ void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) { // 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.points.emplace_back(x, y); } } // Add the data set to the plot. - plot->series.push_back(std::move(time_series)); + plot->series_list_.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 = "Latency change (ms)"; - plot->title = "Network latency change between consecutive packets"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Latency change (ms)", kBottomMargin, + kTopMargin); + plot->SetTitle("Network latency change between consecutive packets"); } void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) { - double max_y = 10; - double min_y = 0; - for (auto& kv : rtp_packets_) { StreamId stream_id = kv.first; // Filter on direction and SSRC. @@ -554,22 +525,17 @@ void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) { // Generate a point, but place it on the x-axis. accumulated_delay_ms = 0; } - max_y = std::max(max_y, accumulated_delay_ms); - min_y = std::min(min_y, accumulated_delay_ms); time_series.points.emplace_back(x, accumulated_delay_ms); } } // Add the data set to the plot. - plot->series.push_back(std::move(time_series)); + plot->series_list_.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 = "Latency change (ms)"; - plot->title = "Accumulated network latency change"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Latency change (ms)", kBottomMargin, + kTopMargin); + plot->SetTitle("Accumulated network latency change"); } // Plot the total bandwidth used by all RTP streams. @@ -602,10 +568,9 @@ void EventLogAnalyzer::CreateTotalBitrateGraph( 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()); + plot->series_list_.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) { @@ -622,42 +587,36 @@ void EventLogAnalyzer::CreateTotalBitrateGraph( static_cast(window_duration_) / 1000000; float x = static_cast(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)); + plot->series_list_.back().points.push_back(TimeSeriesPoint(x, y)); } // Set labels. if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->series.back().label = "Incoming bitrate"; + plot->series_list_.back().label = "Incoming bitrate"; } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->series.back().label = "Outgoing bitrate"; + plot->series_list_.back().label = "Outgoing bitrate"; } - plot->series.back().style = LINE_GRAPH; + plot->series_list_.back().style = LINE_GRAPH; // Overlay the send-side bandwidth estimate over the outgoing bitrate. if (desired_direction == kOutgoingPacket) { - plot->series.push_back(TimeSeries()); + plot->series_list_.push_back(TimeSeries()); for (auto& bwe_update : bwe_loss_updates_) { float x = static_cast(bwe_update.timestamp - begin_time_) / 1000000; float y = static_cast(bwe_update.new_bitrate) / 1000; - max_y = std::max(max_y, y); - plot->series.back().points.emplace_back(x, y); + plot->series_list_.back().points.emplace_back(x, y); } - plot->series.back().label = "Loss-based estimate"; - plot->series.back().style = LINE_GRAPH; + plot->series_list_.back().label = "Loss-based estimate"; + plot->series_list_.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)"; + plot->series_list_.back().style = LINE_GRAPH; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->title = "Incoming RTP bitrate"; + plot->SetTitle("Incoming RTP bitrate"); } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->title = "Outgoing RTP bitrate"; + plot->SetTitle("Outgoing RTP bitrate"); } } @@ -698,15 +657,13 @@ void EventLogAnalyzer::CreateStreamBitrateGraph( } } - 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()); + plot->series_list_.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) { @@ -724,25 +681,20 @@ void EventLogAnalyzer::CreateStreamBitrateGraph( static_cast(window_duration_) / 1000000; float x = static_cast(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)); + plot->series_list_.back().points.push_back(TimeSeriesPoint(x, y)); } // Set labels. - plot->series.back().label = SsrcToString(kv.first); - plot->series.back().style = LINE_GRAPH; + plot->series_list_.back().label = SsrcToString(kv.first); + plot->series_list_.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)"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { - plot->title = "Incoming bitrate per stream"; + plot->SetTitle("Incoming bitrate per stream"); } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { - plot->title = "Outgoing bitrate per stream"; + plot->SetTitle("Outgoing bitrate per stream"); } } @@ -776,8 +728,6 @@ void EventLogAnalyzer::CreateBweGraph(Plot* plot) { 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(); @@ -834,8 +784,6 @@ void EventLogAnalyzer::CreateBweGraph(Plot* plot) { 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(clock.TimeInMicroseconds() - begin_time_) / 1000000; time_series.points.emplace_back(x, y); @@ -843,15 +791,11 @@ void EventLogAnalyzer::CreateBweGraph(Plot* plot) { time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()}); } // Add the data set to the plot. - plot->series.push_back(std::move(time_series)); + plot->series_list_.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"; + plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin); + plot->SetTitle("Simulated BWE behavior"); } } // namespace plotting diff --git a/webrtc/tools/event_log_visualizer/analyzer.h b/webrtc/tools/event_log_visualizer/analyzer.h index da3b206e10..bb4db2b824 100644 --- a/webrtc/tools/event_log_visualizer/analyzer.h +++ b/webrtc/tools/event_log_visualizer/analyzer.h @@ -115,6 +115,9 @@ class EventLogAnalyzer { // First and last events of the log. uint64_t begin_time_; uint64_t end_time_; + + // Duration (in seconds) of log file. + float call_duration_s_; }; } // namespace plotting diff --git a/webrtc/tools/event_log_visualizer/generate_timeseries.cc b/webrtc/tools/event_log_visualizer/generate_timeseries.cc index 02e9ad3727..20d369620b 100644 --- a/webrtc/tools/event_log_visualizer/generate_timeseries.cc +++ b/webrtc/tools/event_log_visualizer/generate_timeseries.cc @@ -82,44 +82,44 @@ int main(int argc, char* argv[]) { if (FLAGS_plot_all || FLAGS_plot_packets) { if (FLAGS_incoming) { analyzer.CreatePacketGraph(webrtc::PacketDirection::kIncomingPacket, - collection->append_new_plot()); + collection->AppendNewPlot()); } if (FLAGS_outgoing) { analyzer.CreatePacketGraph(webrtc::PacketDirection::kOutgoingPacket, - collection->append_new_plot()); + collection->AppendNewPlot()); } } if (FLAGS_plot_all || FLAGS_plot_audio_playout) { - analyzer.CreatePlayoutGraph(collection->append_new_plot()); + analyzer.CreatePlayoutGraph(collection->AppendNewPlot()); } if (FLAGS_plot_all || FLAGS_plot_sequence_number) { if (FLAGS_incoming) { - analyzer.CreateSequenceNumberGraph(collection->append_new_plot()); + analyzer.CreateSequenceNumberGraph(collection->AppendNewPlot()); } } if (FLAGS_plot_all || FLAGS_plot_delay_change) { if (FLAGS_incoming) { - analyzer.CreateDelayChangeGraph(collection->append_new_plot()); + analyzer.CreateDelayChangeGraph(collection->AppendNewPlot()); } } if (FLAGS_plot_all || FLAGS_plot_accumulated_delay_change) { if (FLAGS_incoming) { - analyzer.CreateAccumulatedDelayChangeGraph(collection->append_new_plot()); + analyzer.CreateAccumulatedDelayChangeGraph(collection->AppendNewPlot()); } } if (FLAGS_plot_all || FLAGS_plot_total_bitrate) { if (FLAGS_incoming) { analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kIncomingPacket, - collection->append_new_plot()); + collection->AppendNewPlot()); } if (FLAGS_outgoing) { analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kOutgoingPacket, - collection->append_new_plot()); + collection->AppendNewPlot()); } } @@ -127,20 +127,20 @@ int main(int argc, char* argv[]) { if (FLAGS_incoming) { analyzer.CreateStreamBitrateGraph( webrtc::PacketDirection::kIncomingPacket, - collection->append_new_plot()); + collection->AppendNewPlot()); } if (FLAGS_outgoing) { analyzer.CreateStreamBitrateGraph( webrtc::PacketDirection::kOutgoingPacket, - collection->append_new_plot()); + collection->AppendNewPlot()); } } if (FLAGS_plot_all || FLAGS_plot_bwe) { - analyzer.CreateBweGraph(collection->append_new_plot()); + analyzer.CreateBweGraph(collection->AppendNewPlot()); } - collection->draw(); + collection->Draw(); return 0; } diff --git a/webrtc/tools/event_log_visualizer/plot_base.cc b/webrtc/tools/event_log_visualizer/plot_base.cc new file mode 100644 index 0000000000..f27fcd6b84 --- /dev/null +++ b/webrtc/tools/event_log_visualizer/plot_base.cc @@ -0,0 +1,75 @@ +/* + * 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/plot_base.h" + +#include + +#include "webrtc/base/checks.h" + +namespace webrtc { +namespace plotting { + +void Plot::SetXAxis(float min_value, + float max_value, + std::string label, + float left_margin, + float right_margin) { + RTC_DCHECK_LE(min_value, max_value); + xaxis_min_ = min_value - left_margin * (max_value - min_value); + xaxis_max_ = max_value + right_margin * (max_value - min_value); + xaxis_label_ = label; +} + +void Plot::SetSuggestedXAxis(float min_value, + float max_value, + std::string label, + float left_margin, + float right_margin) { + for (const auto& series : series_list_) { + for (const auto& point : series.points) { + min_value = std::min(min_value, point.x); + max_value = std::max(max_value, point.x); + } + } + SetXAxis(min_value, max_value, label, left_margin, right_margin); +} + +void Plot::SetYAxis(float min_value, + float max_value, + std::string label, + float bottom_margin, + float top_margin) { + RTC_DCHECK_LE(min_value, max_value); + yaxis_min_ = min_value - bottom_margin * (max_value - min_value); + yaxis_max_ = max_value + top_margin * (max_value - min_value); + yaxis_label_ = label; +} + +void Plot::SetSuggestedYAxis(float min_value, + float max_value, + std::string label, + float bottom_margin, + float top_margin) { + for (const auto& series : series_list_) { + for (const auto& point : series.points) { + min_value = std::min(min_value, point.y); + max_value = std::max(max_value, point.y); + } + } + SetYAxis(min_value, max_value, label, bottom_margin, top_margin); +} + +void Plot::SetTitle(std::string title) { + title_ = title; +} + +} // namespace plotting +} // namespace webrtc diff --git a/webrtc/tools/event_log_visualizer/plot_base.h b/webrtc/tools/event_log_visualizer/plot_base.h index 0bfdf8d323..5546271132 100644 --- a/webrtc/tools/event_log_visualizer/plot_base.h +++ b/webrtc/tools/event_log_visualizer/plot_base.h @@ -44,32 +44,79 @@ struct TimeSeries { std::vector points; }; -// This is basically a struct that represents of a general graph, with axes, -// title and one or more data series. We make it a class only to document that -// it also specifies an interface for the draw()ing objects. +// A container that represents a general graph, with axes, title and one or +// more data series. A subclass should define the output format by overriding +// the Draw() method. class Plot { public: virtual ~Plot() {} - virtual void draw() = 0; - float xaxis_min; - float xaxis_max; - std::string xaxis_label; - float yaxis_min; - float yaxis_max; - std::string yaxis_label; - std::vector series; - std::string title; + // Overloaded to draw the plot. + virtual void Draw() = 0; + + // Sets the lower x-axis limit to min_value (if left_margin == 0). + // Sets the upper x-axis limit to max_value (if right_margin == 0). + // The margins are measured as fractions of the interval + // (max_value - min_value) and are added to either side of the plot. + void SetXAxis(float min_value, + float max_value, + std::string label, + float left_margin = 0, + float right_margin = 0); + + // Sets the lower and upper x-axis limits based on min_value and max_value, + // but modified such that all points in the data series can be represented + // on the x-axis. The margins are measured as fractions of the range of + // x-values and are added to either side of the plot. + void SetSuggestedXAxis(float min_value, + float max_value, + std::string label, + float left_margin = 0, + float right_margin = 0); + + // Sets the lower y-axis limit to min_value (if bottom_margin == 0). + // Sets the upper y-axis limit to max_value (if top_margin == 0). + // The margins are measured as fractions of the interval + // (max_value - min_value) and are added to either side of the plot. + void SetYAxis(float min_value, + float max_value, + std::string label, + float bottom_margin = 0, + float top_margin = 0); + + // Sets the lower and upper y-axis limits based on min_value and max_value, + // but modified such that all points in the data series can be represented + // on the y-axis. The margins are measured as fractions of the range of + // y-values and are added to either side of the plot. + void SetSuggestedYAxis(float min_value, + float max_value, + std::string label, + float bottom_margin = 0, + float top_margin = 0); + + // Sets the title of the plot. + void SetTitle(std::string title); + + std::vector series_list_; + + protected: + float xaxis_min_; + float xaxis_max_; + std::string xaxis_label_; + float yaxis_min_; + float yaxis_max_; + std::string yaxis_label_; + std::string title_; }; class PlotCollection { public: virtual ~PlotCollection() {} - virtual void draw() = 0; - virtual Plot* append_new_plot() = 0; + virtual void Draw() = 0; + virtual Plot* AppendNewPlot() = 0; protected: - std::vector > plots; + std::vector > plots_; }; } // namespace plotting diff --git a/webrtc/tools/event_log_visualizer/plot_python.cc b/webrtc/tools/event_log_visualizer/plot_python.cc index 211d33679c..d4583444ef 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 + #include namespace webrtc { @@ -20,62 +21,62 @@ PythonPlot::PythonPlot() {} PythonPlot::~PythonPlot() {} -void PythonPlot::draw() { +void PythonPlot::Draw() { // Write python commands to stdout. Intended program usage is // ./event_log_visualizer event_log160330.dump | python - if (!series.empty()) { - printf("color_count = %zu\n", series.size()); + if (!series_list_.empty()) { + printf("color_count = %zu\n", series_list_.size()); printf( "hls_colors = [(i*1.0/color_count, 0.25+i*0.5/color_count, 0.8) for i " "in range(color_count)]\n"); printf("rgb_colors = [colorsys.hls_to_rgb(*hls) for hls in hls_colors]\n"); - for (size_t i = 0; i < series.size(); i++) { + for (size_t i = 0; i < series_list_.size(); i++) { // List x coordinates printf("x%zu = [", i); - if (series[i].points.size() > 0) - printf("%G", series[i].points[0].x); - for (size_t j = 1; j < series[i].points.size(); j++) - printf(", %G", series[i].points[j].x); + if (series_list_[i].points.size() > 0) + printf("%G", series_list_[i].points[0].x); + for (size_t j = 1; j < series_list_[i].points.size(); j++) + printf(", %G", series_list_[i].points[j].x); printf("]\n"); // List y coordinates printf("y%zu = [", i); - if (series[i].points.size() > 0) - printf("%G", series[i].points[0].y); - for (size_t j = 1; j < series[i].points.size(); j++) - printf(", %G", series[i].points[j].y); + if (series_list_[i].points.size() > 0) + printf("%G", series_list_[i].points[0].y); + for (size_t j = 1; j < series_list_[i].points.size(); j++) + printf(", %G", series_list_[i].points[j].y); printf("]\n"); - if (series[i].style == BAR_GRAPH) { + if (series_list_[i].style == BAR_GRAPH) { // There is a plt.bar function that draws bar plots, // but it is *way* too slow to be useful. printf( "plt.vlines(x%zu, map(lambda t: min(t,0), y%zu), map(lambda t: " "max(t,0), y%zu), color=rgb_colors[%zu], " "label=\'%s\')\n", - i, i, i, i, series[i].label.c_str()); - } else if (series[i].style == LINE_GRAPH) { + i, i, i, i, series_list_[i].label.c_str()); + } else if (series_list_[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) { + i, i, series_list_[i].label.c_str()); + } else if (series_list_[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()); + i, i, i, series_list_[i].label.c_str()); } else { printf("raise Exception(\"Unknown graph type\")\n"); } } } - printf("plt.xlim(%f, %f)\n", xaxis_min, xaxis_max); - printf("plt.ylim(%f, %f)\n", yaxis_min, yaxis_max); - printf("plt.xlabel(\'%s\')\n", xaxis_label.c_str()); - printf("plt.ylabel(\'%s\')\n", yaxis_label.c_str()); - printf("plt.title(\'%s\')\n", title.c_str()); - if (!series.empty()) { + printf("plt.xlim(%f, %f)\n", xaxis_min_, xaxis_max_); + printf("plt.ylim(%f, %f)\n", yaxis_min_, yaxis_max_); + printf("plt.xlabel(\'%s\')\n", xaxis_label_.c_str()); + printf("plt.ylabel(\'%s\')\n", yaxis_label_.c_str()); + printf("plt.title(\'%s\')\n", title_.c_str()); + if (!series_list_.empty()) { printf("plt.legend(loc=\'best\', fontsize=\'small\')\n"); } } @@ -84,19 +85,19 @@ PythonPlotCollection::PythonPlotCollection() {} PythonPlotCollection::~PythonPlotCollection() {} -void PythonPlotCollection::draw() { +void PythonPlotCollection::Draw() { printf("import matplotlib.pyplot as plt\n"); printf("import colorsys\n"); - for (size_t i = 0; i < plots.size(); i++) { + for (size_t i = 0; i < plots_.size(); i++) { printf("plt.figure(%zu)\n", i); - plots[i]->draw(); + plots_[i]->Draw(); } printf("plt.show()\n"); } -Plot* PythonPlotCollection::append_new_plot() { +Plot* PythonPlotCollection::AppendNewPlot() { Plot* plot = new PythonPlot(); - plots.push_back(std::unique_ptr(plot)); + plots_.push_back(std::unique_ptr(plot)); return plot; } diff --git a/webrtc/tools/event_log_visualizer/plot_python.h b/webrtc/tools/event_log_visualizer/plot_python.h index 8d63f952eb..2bf445a43e 100644 --- a/webrtc/tools/event_log_visualizer/plot_python.h +++ b/webrtc/tools/event_log_visualizer/plot_python.h @@ -19,15 +19,15 @@ class PythonPlot final : public Plot { public: PythonPlot(); ~PythonPlot() override; - void draw() override; + void Draw() override; }; class PythonPlotCollection final : public PlotCollection { public: PythonPlotCollection(); ~PythonPlotCollection() override; - void draw() override; - Plot* append_new_plot() override; + void Draw() override; + Plot* AppendNewPlot() override; }; } // namespace plotting diff --git a/webrtc/tools/tools.gyp b/webrtc/tools/tools.gyp index 46fb49a277..81a9b69c0f 100644 --- a/webrtc/tools/tools.gyp +++ b/webrtc/tools/tools.gyp @@ -121,6 +121,7 @@ 'event_log_visualizer/analyzer.cc', 'event_log_visualizer/analyzer.h', 'event_log_visualizer/generate_timeseries.cc', + 'event_log_visualizer/plot_base.cc', 'event_log_visualizer/plot_base.h', 'event_log_visualizer/plot_python.cc', 'event_log_visualizer/plot_python.h',