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:
@ -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));
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user