Added -show_detector_state which show the detector state in the total bitrate graph.

BUG=none

Review-Url: https://codereview.webrtc.org/2826313004
Cr-Commit-Position: refs/heads/master@{#19020}
This commit is contained in:
philipel
2017-07-14 06:30:03 -07:00
committed by Commit Bot
parent 9b1367f233
commit 23c7f25266
6 changed files with 139 additions and 6 deletions

View File

@ -898,7 +898,8 @@ void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) {
// Plot the total bandwidth used by all RTP streams. // Plot the total bandwidth used by all RTP streams.
void EventLogAnalyzer::CreateTotalBitrateGraph( void EventLogAnalyzer::CreateTotalBitrateGraph(
PacketDirection desired_direction, PacketDirection desired_direction,
Plot* plot) { Plot* plot,
bool show_detector_state) {
struct TimestampSize { struct TimestampSize {
TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {} TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
uint64_t timestamp; uint64_t timestamp;
@ -958,13 +959,46 @@ void EventLogAnalyzer::CreateTotalBitrateGraph(
} }
TimeSeries delay_series("Delay-based estimate", LINE_STEP_GRAPH); TimeSeries delay_series("Delay-based estimate", LINE_STEP_GRAPH);
IntervalSeries overusing_series("Overusing", "#ff8e82",
IntervalSeries::kHorizontal);
IntervalSeries underusing_series("Underusing", "#5092fc",
IntervalSeries::kHorizontal);
IntervalSeries normal_series("Normal", "#c4ffc4",
IntervalSeries::kHorizontal);
IntervalSeries* last_series = &normal_series;
double last_detector_switch = 0.0;
BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal;
for (auto& delay_update : bwe_delay_updates_) { for (auto& delay_update : bwe_delay_updates_) {
float x = float x =
static_cast<float>(delay_update.timestamp - begin_time_) / 1000000; static_cast<float>(delay_update.timestamp - begin_time_) / 1000000;
float y = static_cast<float>(delay_update.bitrate_bps) / 1000; float y = static_cast<float>(delay_update.bitrate_bps) / 1000;
if (last_detector_state != delay_update.detector_state) {
last_series->intervals.emplace_back(last_detector_switch, x);
last_detector_state = delay_update.detector_state;
last_detector_switch = x;
switch (delay_update.detector_state) {
case BandwidthUsage::kBwNormal:
last_series = &normal_series;
break;
case BandwidthUsage::kBwUnderusing:
last_series = &underusing_series;
break;
case BandwidthUsage::kBwOverusing:
last_series = &overusing_series;
break;
}
}
delay_series.points.emplace_back(x, y); delay_series.points.emplace_back(x, y);
} }
RTC_CHECK(last_series);
last_series->intervals.emplace_back(last_detector_switch, end_time_);
TimeSeries created_series("Probe cluster created.", DOT_GRAPH); TimeSeries created_series("Probe cluster created.", DOT_GRAPH);
for (auto& cluster : bwe_probe_cluster_created_events_) { for (auto& cluster : bwe_probe_cluster_created_events_) {
float x = static_cast<float>(cluster.timestamp - begin_time_) / 1000000; float x = static_cast<float>(cluster.timestamp - begin_time_) / 1000000;
@ -980,6 +1014,14 @@ void EventLogAnalyzer::CreateTotalBitrateGraph(
result_series.points.emplace_back(x, y); result_series.points.emplace_back(x, y);
} }
} }
if (show_detector_state) {
plot->AppendIntervalSeries(std::move(overusing_series));
plot->AppendIntervalSeries(std::move(underusing_series));
plot->AppendIntervalSeries(std::move(normal_series));
}
plot->AppendTimeSeries(std::move(bitrate_series));
plot->AppendTimeSeries(std::move(loss_series)); plot->AppendTimeSeries(std::move(loss_series));
plot->AppendTimeSeries(std::move(delay_series)); plot->AppendTimeSeries(std::move(delay_series));
plot->AppendTimeSeries(std::move(created_series)); plot->AppendTimeSeries(std::move(created_series));

View File

@ -85,7 +85,9 @@ class EventLogAnalyzer {
void CreateFractionLossGraph(Plot* plot); void CreateFractionLossGraph(Plot* plot);
void CreateTotalBitrateGraph(PacketDirection desired_direction, Plot* plot); void CreateTotalBitrateGraph(PacketDirection desired_direction,
Plot* plot,
bool show_detector_state = false);
void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot); void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot);

View File

@ -90,6 +90,11 @@ DEFINE_string(
"trials are separated by \"/\""); "trials are separated by \"/\"");
DEFINE_bool(help, false, "prints this message"); DEFINE_bool(help, false, "prints this message");
DEFINE_bool(
show_detector_state,
false,
"Mark the delay based bwe detector state on the total bitrate graph");
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::string program_name = argv[0]; std::string program_name = argv[0];
std::string usage = std::string usage =
@ -178,11 +183,13 @@ int main(int argc, char* argv[]) {
if (FLAG_plot_all || FLAG_plot_total_bitrate) { if (FLAG_plot_all || FLAG_plot_total_bitrate) {
if (FLAG_incoming) { if (FLAG_incoming) {
analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kIncomingPacket, analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kIncomingPacket,
collection->AppendNewPlot()); collection->AppendNewPlot(),
FLAG_show_detector_state);
} }
if (FLAG_outgoing) { if (FLAG_outgoing) {
analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kOutgoingPacket, analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kOutgoingPacket,
collection->AppendNewPlot()); collection->AppendNewPlot(),
FLAG_show_detector_state);
} }
} }

View File

@ -75,6 +75,10 @@ void Plot::AppendTimeSeries(TimeSeries&& time_series) {
series_list_.emplace_back(std::move(time_series)); series_list_.emplace_back(std::move(time_series));
} }
void Plot::AppendIntervalSeries(IntervalSeries&& interval_series) {
interval_list_.emplace_back(std::move(interval_series));
}
void Plot::AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series) { void Plot::AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series) {
if (time_series.points.size() > 0) { if (time_series.points.size() > 0) {
series_list_.emplace_back(std::move(time_series)); series_list_.emplace_back(std::move(time_series));

View File

@ -53,6 +53,29 @@ struct TimeSeries {
std::vector<TimeSeriesPoint> points; std::vector<TimeSeriesPoint> points;
}; };
struct Interval {
Interval() = default;
Interval(double begin, double end) : begin(begin), end(end) {}
double begin;
double end;
};
struct IntervalSeries {
enum Orientation { kHorizontal, kVertical };
IntervalSeries() = default;
IntervalSeries(const std::string& label,
const std::string& color,
IntervalSeries::Orientation orientation)
: label(label), color(color), orientation(orientation) {}
std::string label;
std::string color;
Orientation orientation;
std::vector<Interval> intervals;
};
// A container that represents a general graph, with axes, title and one or // 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 // more data series. A subclass should define the output format by overriding
// the Draw() method. // the Draw() method.
@ -109,6 +132,9 @@ class Plot {
// Add a new TimeSeries to the plot. // Add a new TimeSeries to the plot.
void AppendTimeSeries(TimeSeries&& time_series); void AppendTimeSeries(TimeSeries&& time_series);
// Add a new IntervalSeries to the plot.
void AppendIntervalSeries(IntervalSeries&& interval_series);
// Add a new TimeSeries to the plot if the series contains contains data. // Add a new TimeSeries to the plot if the series contains contains data.
// Otherwise, the call has no effect and the timeseries is destroyed. // Otherwise, the call has no effect and the timeseries is destroyed.
void AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series); void AppendTimeSeriesIfNotEmpty(TimeSeries&& time_series);
@ -122,6 +148,7 @@ class Plot {
std::string yaxis_label_; std::string yaxis_label_;
std::string title_; std::string title_;
std::vector<TimeSeries> series_list_; std::vector<TimeSeries> series_list_;
std::vector<IntervalSeries> interval_list_;
}; };
class PlotCollection { class PlotCollection {

View File

@ -14,6 +14,8 @@
#include <memory> #include <memory>
#include "webrtc/rtc_base/checks.h"
namespace webrtc { namespace webrtc {
namespace plotting { namespace plotting {
@ -74,6 +76,8 @@ void PythonPlot::Draw() {
printf("y%zu = [v for dup in y%zu for v in [dup, dup]]\n", i, i); printf("y%zu = [v for dup in y%zu for v in [dup, dup]]\n", i, i);
printf( printf(
"plt.plot(x%zu[1:], y%zu[:-1], color=rgb_colors[%zu], " "plt.plot(x%zu[1:], y%zu[:-1], color=rgb_colors[%zu], "
"path_effects=[pe.Stroke(linewidth=2, foreground='black'), "
"pe.Normal()], "
"label=\'%s\')\n", "label=\'%s\')\n",
i, i, i, series_list_[i].label.c_str()); i, i, i, series_list_[i].label.c_str());
} else if (series_list_[i].style == DOT_GRAPH) { } else if (series_list_[i].style == DOT_GRAPH) {
@ -85,6 +89,47 @@ void PythonPlot::Draw() {
printf("raise Exception(\"Unknown graph type\")\n"); printf("raise Exception(\"Unknown graph type\")\n");
} }
} }
// IntervalSeries
printf("interval_colors = ['#ff8e82','#5092fc','#c4ffc4']\n");
RTC_CHECK_LE(interval_list_.size(), 3);
// To get the intervals to show up in the legend we have to created patches
// for them.
printf("legend_patches = []\n");
for (size_t i = 0; i < interval_list_.size(); i++) {
// List intervals
printf("\n# === IntervalSeries: %s ===\n",
interval_list_[i].label.c_str());
printf("ival%zu = [", i);
if (interval_list_[i].intervals.size() > 0) {
printf("(%G, %G)", interval_list_[i].intervals[0].begin,
interval_list_[i].intervals[0].end);
}
for (size_t j = 1; j < interval_list_[i].intervals.size(); j++) {
printf(", (%G, %G)", interval_list_[i].intervals[j].begin,
interval_list_[i].intervals[j].end);
}
printf("]\n");
printf("for i in range(0, %zu):\n", interval_list_[i].intervals.size());
if (interval_list_[i].orientation == IntervalSeries::kVertical) {
printf(
" plt.axhspan(ival%zu[i][0], ival%zu[i][1], "
"facecolor=interval_colors[%zu], "
"alpha=0.3)\n",
i, i, i);
} else {
printf(
" plt.axvspan(ival%zu[i][0], ival%zu[i][1], "
"facecolor=interval_colors[%zu], "
"alpha=0.3)\n",
i, i, i);
}
printf(
"legend_patches.append(mpatches.Patch(ec=\'black\', "
"fc=interval_colors[%zu], label='%s'))\n",
i, interval_list_[i].label.c_str());
}
} }
printf("plt.xlim(%f, %f)\n", xaxis_min_, xaxis_max_); printf("plt.xlim(%f, %f)\n", xaxis_min_, xaxis_max_);
@ -92,8 +137,12 @@ void PythonPlot::Draw() {
printf("plt.xlabel(\'%s\')\n", xaxis_label_.c_str()); printf("plt.xlabel(\'%s\')\n", xaxis_label_.c_str());
printf("plt.ylabel(\'%s\')\n", yaxis_label_.c_str()); printf("plt.ylabel(\'%s\')\n", yaxis_label_.c_str());
printf("plt.title(\'%s\')\n", title_.c_str()); printf("plt.title(\'%s\')\n", title_.c_str());
if (!series_list_.empty()) { if (!series_list_.empty() || !interval_list_.empty()) {
printf("plt.legend(loc=\'best\', fontsize=\'small\')\n"); printf("handles, labels = plt.gca().get_legend_handles_labels()\n");
printf("for lp in legend_patches:\n");
printf(" handles.append(lp)\n");
printf(" labels.append(lp.get_label())\n");
printf("plt.legend(handles, labels, loc=\'best\', fontsize=\'small\')\n");
} }
} }
@ -103,6 +152,8 @@ PythonPlotCollection::~PythonPlotCollection() {}
void PythonPlotCollection::Draw() { void PythonPlotCollection::Draw() {
printf("import matplotlib.pyplot as plt\n"); printf("import matplotlib.pyplot as plt\n");
printf("import matplotlib.patches as mpatches\n");
printf("import matplotlib.patheffects as pe\n");
printf("import colorsys\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); printf("plt.figure(%zu)\n", i);