Revert of Visualization tool for WebrtcEventLogs (patchset #9 id:160001 of https://codereview.webrtc.org/1995523002/ )

Reason for revert:
Reverting while investigating a downstream build failure.

Original issue's description:
> Initial version of the local visualization tool for WebrtcEventLogs.
>
> Plot graphs to python code. Generate packet, playout, sequence number and total bitrate graphs.
>
> Add bitrate and latency plots. Generate multiple figures, and control which ones are used through command line flags.
>
> Committed: https://crrev.com/a478786ef1513790194792010f766324a469db4d
> Cr-Commit-Position: refs/heads/master@{#13443}

TBR=stefan@webrtc.org,aleloi@webrtc.org,phoglund@webrtc.org,henrika@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Review-Url: https://codereview.webrtc.org/2147453002
Cr-Commit-Position: refs/heads/master@{#13445}
This commit is contained in:
terelius 2016-07-12 05:11:21 -07:00 committed by Commit bot
parent d658edeeed
commit a44f28da45
10 changed files with 1 additions and 1181 deletions

View File

@ -358,7 +358,7 @@ if (rtc_enable_protobuf) {
configs += [ ":common_config" ] configs += [ ":common_config" ]
public_configs = [ ":common_inherited_config" ] public_configs = [ ":common_inherited_config" ]
public_deps = [ deps = [
":rtc_event_log_proto", ":rtc_event_log_proto",
":webrtc_common", ":webrtc_common",
] ]

View File

@ -171,36 +171,6 @@ source_set("agc_test_utils") {
# Exclude tools depending on gflags since that's not available in Chromium. # Exclude tools depending on gflags since that's not available in Chromium.
if (rtc_include_tests) { if (rtc_include_tests) {
if (rtc_enable_protobuf) {
executable("event_log_visualizer") {
sources = [
"event_log_visualizer/analyzer.cc",
"event_log_visualizer/analyzer.h",
"event_log_visualizer/generate_timeseries.cc",
"event_log_visualizer/plot_base.h",
"event_log_visualizer/plot_python.cc",
"event_log_visualizer/plot_python.h",
]
configs += [ "..:common_config" ]
public_configs = [ "..:common_inherited_config" ]
if (is_clang && !is_nacl) {
# Suppress warnings from the Chromium Clang plugin.
# See http://code.google.com/p/webrtc/issues/detail?id=163 for details.
configs -= [ "//build/config/clang:find_bad_constructs" ]
}
defines = [ "ENABLE_RTC_EVENT_LOG" ]
deps = [
"../:rtc_event_log_parser",
"../modules/rtp_rtcp:rtp_rtcp",
"../system_wrappers:system_wrappers_default",
"//third_party/gflags",
]
}
}
executable("agc_harness") { executable("agc_harness") {
testonly = true testonly = true
sources = [ sources = [

View File

@ -1,9 +1,7 @@
include_rules = [ include_rules = [
"+webrtc/base", "+webrtc/base",
"+webrtc/call",
"+webrtc/common_video", "+webrtc/common_video",
"+webrtc/modules/audio_processing", "+webrtc/modules/audio_processing",
"+webrtc/modules/rtp_rtcp",
"+webrtc/system_wrappers", "+webrtc/system_wrappers",
"+webrtc/voice_engine", "+webrtc/voice_engine",
] ]

View File

@ -1,710 +0,0 @@
/*
* 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 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)
: parsed_log_(log), 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);
time_series[parsed_header.ssrc].points.push_back(
TimeSeriesPoint(x, y));
}
}
}
}
// 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_) {
RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window);
bytes_in_window -= packets[window_index_begin].size;
window_index_begin++;
}
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_) {
RTC_DCHECK_LE(kv.second[window_index_begin].size, bytes_in_window);
bytes_in_window -= kv.second[window_index_begin].size;
window_index_begin++;
}
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

View File

@ -1,65 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_ANALYZER_H_
#define WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_ANALYZER_H_
#include <vector>
#include "webrtc/call/rtc_event_log_parser.h"
#include "webrtc/tools/event_log_visualizer/plot_base.h"
namespace webrtc {
namespace plotting {
class EventLogAnalyzer {
public:
// The EventLogAnalyzer keeps a reference to the ParsedRtcEventLog for the
// duration of its lifetime. The ParsedRtcEventLog must not be destroyed or
// modified while the EventLogAnalyzer is being used.
explicit EventLogAnalyzer(const ParsedRtcEventLog& log);
void CreatePacketGraph(PacketDirection desired_direction, Plot* plot);
void CreatePlayoutGraph(Plot* plot);
void CreateSequenceNumberGraph(Plot* plot);
void CreateDelayChangeGraph(Plot* plot);
void CreateAccumulatedDelayChangeGraph(Plot* plot);
void CreateTotalBitrateGraph(PacketDirection desired_direction, Plot* plot);
void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot);
private:
const ParsedRtcEventLog& parsed_log_;
// A list of SSRCs we are interested in analysing.
// If left empty, all SSRCs will be considered relevant.
std::vector<uint32_t> desired_ssrc_;
// Window and step size used for calculating moving averages, e.g. bitrate.
// The generated data points will be |step_| microseconds apart.
// Only events occuring at most |window_duration_| microseconds before the
// current data point will be part of the average.
uint64_t window_duration_;
uint64_t step_;
// First and last events of the log.
uint64_t begin_time_;
uint64_t end_time_;
};
} // namespace plotting
} // namespace webrtc
#endif // WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_ANALYZER_H_

View File

@ -1,138 +0,0 @@
/*
* 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 <iostream>
#include "gflags/gflags.h"
#include "webrtc/call/rtc_event_log_parser.h"
#include "webrtc/tools/event_log_visualizer/analyzer.h"
#include "webrtc/tools/event_log_visualizer/plot_base.h"
#include "webrtc/tools/event_log_visualizer/plot_python.h"
DEFINE_bool(incoming, true, "Plot statistics for incoming packets.");
DEFINE_bool(outgoing, true, "Plot statistics for outgoing packets.");
DEFINE_bool(plot_all, true, "Plot all different data types.");
DEFINE_bool(plot_packets,
false,
"Plot bar graph showing the size of each packet.");
DEFINE_bool(plot_audio_playout,
false,
"Plot bar graph showing the time between each audio playout.");
DEFINE_bool(
plot_sequence_number,
false,
"Plot the difference in sequence number between consecutive packets.");
DEFINE_bool(
plot_delay_change,
false,
"Plot the difference in 1-way path delay between consecutive packets.");
DEFINE_bool(plot_accumulated_delay_change,
false,
"Plot the accumulated 1-way path delay change, or the path delay "
"change compared to the first packet.");
DEFINE_bool(plot_total_bitrate,
false,
"Plot the total bitrate used by all streams.");
DEFINE_bool(plot_stream_bitrate,
false,
"Plot the bitrate used by each stream.");
int main(int argc, char* argv[]) {
std::string program_name = argv[0];
std::string usage =
"A tool for visualizing WebRTC event logs.\n"
"Example usage:\n" +
program_name + " <logfile> | python\n" + "Run " + program_name +
" --help for a list of command line options\n";
google::SetUsageMessage(usage);
google::ParseCommandLineFlags(&argc, &argv, true);
if (argc != 2) {
// Print usage information.
std::cout << google::ProgramUsage();
return 0;
}
std::string filename = argv[1];
webrtc::ParsedRtcEventLog parsed_log;
if (!parsed_log.ParseFile(filename)) {
std::cerr << "Could not parse the entire log file." << std::endl;
std::cerr << "Proceeding to analyze the first "
<< parsed_log.GetNumberOfEvents() << " events in the file."
<< std::endl;
}
webrtc::plotting::EventLogAnalyzer analyzer(parsed_log);
std::unique_ptr<webrtc::plotting::PlotCollection> collection(
new webrtc::plotting::PythonPlotCollection());
if (FLAGS_plot_all || FLAGS_plot_packets) {
if (FLAGS_incoming) {
analyzer.CreatePacketGraph(webrtc::PacketDirection::kIncomingPacket,
collection->append_new_plot());
}
if (FLAGS_outgoing) {
analyzer.CreatePacketGraph(webrtc::PacketDirection::kOutgoingPacket,
collection->append_new_plot());
}
}
if (FLAGS_plot_all || FLAGS_plot_audio_playout) {
analyzer.CreatePlayoutGraph(collection->append_new_plot());
}
if (FLAGS_plot_all || FLAGS_plot_sequence_number) {
if (FLAGS_incoming) {
analyzer.CreateSequenceNumberGraph(collection->append_new_plot());
}
}
if (FLAGS_plot_all || FLAGS_plot_delay_change) {
if (FLAGS_incoming) {
analyzer.CreateDelayChangeGraph(collection->append_new_plot());
}
}
if (FLAGS_plot_all || FLAGS_plot_accumulated_delay_change) {
if (FLAGS_incoming) {
analyzer.CreateAccumulatedDelayChangeGraph(collection->append_new_plot());
}
}
if (FLAGS_plot_all || FLAGS_plot_total_bitrate) {
if (FLAGS_incoming) {
analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kIncomingPacket,
collection->append_new_plot());
}
if (FLAGS_outgoing) {
analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kOutgoingPacket,
collection->append_new_plot());
}
}
if (FLAGS_plot_all || FLAGS_plot_stream_bitrate) {
if (FLAGS_incoming) {
analyzer.CreateStreamBitrateGraph(
webrtc::PacketDirection::kIncomingPacket,
collection->append_new_plot());
}
if (FLAGS_outgoing) {
analyzer.CreateStreamBitrateGraph(
webrtc::PacketDirection::kOutgoingPacket,
collection->append_new_plot());
}
}
collection->draw();
return 0;
}

View File

@ -1,78 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_BASE_H_
#define WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_BASE_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace webrtc {
namespace plotting {
enum PlotStyle { LINE_GRAPH, BAR_GRAPH };
struct TimeSeriesPoint {
TimeSeriesPoint(float x, float y) : x(x), y(y) {}
float x;
float y;
};
struct TimeSeries {
TimeSeries() = default;
TimeSeries(TimeSeries&& other)
: label(std::move(other.label)),
style(other.style),
points(std::move(other.points)) {}
TimeSeries& operator=(TimeSeries&& other) {
label = std::move(other.label);
style = other.style;
points = std::move(other.points);
return *this;
}
std::string label;
PlotStyle style;
std::vector<TimeSeriesPoint> 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.
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<TimeSeries> series;
std::string title;
};
class PlotCollection {
public:
virtual ~PlotCollection() {}
virtual void draw() = 0;
virtual Plot* append_new_plot() = 0;
protected:
std::vector<std::unique_ptr<Plot> > plots;
};
} // namespace plotting
} // namespace webrtc
#endif // WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_BASE_H_

View File

@ -1,98 +0,0 @@
/*
* 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_python.h"
#include <stdio.h>
namespace webrtc {
namespace plotting {
PythonPlot::PythonPlot() {}
PythonPlot::~PythonPlot() {}
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());
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++) {
// 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);
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);
printf("]\n");
if (series[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) {
printf("plt.plot(x%zu, y%zu, color=rgb_colors[%zu], label=\'%s\')\n", i,
i, i, series[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.legend(loc=\'best\', fontsize=\'small\')\n");
}
}
PythonPlotCollection::PythonPlotCollection() {}
PythonPlotCollection::~PythonPlotCollection() {}
void PythonPlotCollection::draw() {
printf("import matplotlib.pyplot as plt\n");
printf("import colorsys\n");
for (size_t i = 0; i < plots.size(); i++) {
printf("plt.figure(%zu)\n", i);
plots[i]->draw();
}
printf("plt.show()\n");
}
Plot* PythonPlotCollection::append_new_plot() {
Plot* plot = new PythonPlot();
plots.push_back(std::unique_ptr<Plot>(plot));
return plot;
}
} // namespace plotting
} // namespace webrtc

View File

@ -1,36 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_
#define WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_
#include "webrtc/tools/event_log_visualizer/plot_base.h"
namespace webrtc {
namespace plotting {
class PythonPlot final : public Plot {
public:
PythonPlot();
~PythonPlot() override;
void draw() override;
};
class PythonPlotCollection final : public PlotCollection {
public:
PythonPlotCollection();
~PythonPlotCollection() override;
void draw() override;
Plot* append_new_plot() override;
};
} // namespace plotting
} // namespace webrtc
#endif // WEBRTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_

View File

@ -99,29 +99,6 @@
}, # force_mic_volume_max }, # force_mic_volume_max
], ],
'conditions': [ 'conditions': [
['enable_protobuf==1', {
'targets': [
{
# This target should only be built if enable_protobuf is set
'target_name': 'event_log_visualizer',
'type': 'executable',
'dependencies': [
'<(webrtc_root)/webrtc.gyp:rtc_event_log_parser',
'<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
'<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
],
'sources': [
'event_log_visualizer/analyzer.cc',
'event_log_visualizer/analyzer.h',
'event_log_visualizer/generate_timeseries.cc',
'event_log_visualizer/plot_base.h',
'event_log_visualizer/plot_python.cc',
'event_log_visualizer/plot_python.h',
],
},
],
}],
['include_tests==1', { ['include_tests==1', {
'targets' : [ 'targets' : [
{ {