Revert "Create new API for RtcEventLogParser."
This reverts commit 9e336ec0b8a77c3461d13677cff3563c11c88daa.
Reason for revert: Code can accidentally include the deprecated parser but link with the new one, or vice versa. Reverting to fix naming.
Original change's description:
> Create new API for RtcEventLogParser.
>
> The new API stores events gathered by event type. For example, it is
> possible to ask fo a list of all incoming RTCP messages or all audio
> playout events.
>
> The new API is experimental and may change over next few weeks. Once
> it has stabilized and all unit tests and existing tools have been
> ported to the new API, the old one will be removed.
>
> This CL also updates the event_log_visualizer tool to use the new
> parser API. This is not a funcional change except for:
> - Incoming and outgoing audio level are now drawn in two separate plots.
> - Incoming and outgoing timstamps are now drawn in two separate plots.
> - RTCP count is no longer split into Video and Audio. It also counts
> all RTCP packets rather than only specific message types.
> - Slight timing difference in sendside BWE simulation due to only
> iterating over transport feedbacks and not over all RTCP packets.
> This timing changes are not visible in the plots.
>
>
> Media type for RTCP messages might not be identified correctly by
> rtc_event_log2text anymore. On the other hand, assigning a specific
> media type to an RTCP packet was a bit hacky to begin with.
>
> Bug: webrtc:8111
> Change-Id: I8e7168302beb69b2e163a097a2a142b86dd4a26b
> Reviewed-on: https://webrtc-review.googlesource.com/60865
> Reviewed-by: Minyue Li <minyue@webrtc.org>
> Reviewed-by: Sebastian Jansson <srte@webrtc.org>
> Commit-Queue: Björn Terelius <terelius@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#23015}
TBR=terelius@webrtc.org,srte@webrtc.org,minyue@webrtc.org
Change-Id: Ib4bbcf0563423675a3cc1dce59ebf665e0c5dae9
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: webrtc:8111
Reviewed-on: https://webrtc-review.googlesource.com/72500
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Commit-Queue: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23026}
diff --git a/rtc_tools/event_log_visualizer/analyzer.cc b/rtc_tools/event_log_visualizer/analyzer.cc
index c780632..1a7309c 100644
--- a/rtc_tools/event_log_visualizer/analyzer.cc
+++ b/rtc_tools/event_log_visualizer/analyzer.cc
@@ -14,6 +14,7 @@
#include <cmath>
#include <limits>
#include <map>
+#include <sstream>
#include <string>
#include <utility>
@@ -24,7 +25,6 @@
#include "call/video_send_stream.h"
#include "common_types.h" // NOLINT(build/include)
#include "logging/rtc_event_log/rtc_stream_config.h"
-#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
#include "modules/audio_coding/neteq/tools/audio_sink.h"
#include "modules/audio_coding/neteq/tools/fake_decode_from_file.h"
#include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h"
@@ -40,7 +40,6 @@
#include "modules/pacing/packet_router.h"
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
-#include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/remb.h"
@@ -50,7 +49,6 @@
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "rtc_base/checks.h"
#include "rtc_base/format_macros.h"
-#include "rtc_base/function_view.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/sequence_number_util.h"
#include "rtc_base/ptr_util.h"
@@ -61,6 +59,7 @@
#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE
namespace webrtc {
+namespace plotting {
namespace {
@@ -128,45 +127,29 @@
return difference;
}
-// This is much more reliable for outgoing streams than for incoming streams.
-template <typename RtpPacketContainer>
-rtc::Optional<uint32_t> EstimateRtpClockFrequency(
- const RtpPacketContainer& packets,
- int64_t end_time_us) {
- RTC_CHECK(packets.size() >= 2);
- SeqNumUnwrapper<uint32_t> unwrapper;
- uint64_t first_rtp_timestamp =
- unwrapper.Unwrap(packets[0].rtp.header.timestamp);
- int64_t first_log_timestamp = packets[0].log_time_us();
- uint64_t last_rtp_timestamp = first_rtp_timestamp;
- int64_t last_log_timestamp = first_log_timestamp;
- for (size_t i = 1; i < packets.size(); i++) {
- if (packets[i].log_time_us() > end_time_us)
- break;
- last_rtp_timestamp = unwrapper.Unwrap(packets[i].rtp.header.timestamp);
- last_log_timestamp = packets[i].log_time_us();
- }
- if (last_log_timestamp - first_log_timestamp < kNumMicrosecsPerSec) {
- RTC_LOG(LS_WARNING)
- << "Failed to estimate RTP clock frequency: Stream too short. ("
- << packets.size() << " packets, "
- << last_log_timestamp - first_log_timestamp << " us)";
- return rtc::nullopt;
- }
- double duration =
- static_cast<double>(last_log_timestamp - first_log_timestamp) /
- kNumMicrosecsPerSec;
- double estimated_frequency =
- (last_rtp_timestamp - first_rtp_timestamp) / duration;
- for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) {
- if (std::fabs(estimated_frequency - f) < 0.05 * f) {
- return f;
- }
- }
- RTC_LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate "
- << estimated_frequency
- << "not close to any stardard RTP frequency.";
- return rtc::nullopt;
+// Return default values for header extensions, to use on streams without stored
+// mapping data. Currently this only applies to audio streams, since the mapping
+// is not stored in the event log.
+// TODO(ivoc): Remove this once this mapping is stored in the event log for
+// audio streams. Tracking bug: webrtc:6399
+webrtc::RtpHeaderExtensionMap GetDefaultHeaderExtensionMap() {
+ webrtc::RtpHeaderExtensionMap default_map;
+ default_map.Register<AudioLevel>(webrtc::RtpExtension::kAudioLevelDefaultId);
+ default_map.Register<TransmissionOffset>(
+ webrtc::RtpExtension::kTimestampOffsetDefaultId);
+ default_map.Register<AbsoluteSendTime>(
+ webrtc::RtpExtension::kAbsSendTimeDefaultId);
+ default_map.Register<VideoOrientation>(
+ webrtc::RtpExtension::kVideoRotationDefaultId);
+ default_map.Register<VideoContentTypeExtension>(
+ webrtc::RtpExtension::kVideoContentTypeDefaultId);
+ default_map.Register<VideoTimingExtension>(
+ webrtc::RtpExtension::kVideoTimingDefaultId);
+ default_map.Register<TransportSequenceNumber>(
+ webrtc::RtpExtension::kTransportSequenceNumberDefaultId);
+ default_map.Register<PlayoutDelayLimits>(
+ webrtc::RtpExtension::kPlayoutDelayDefaultId);
+ return default_map;
}
constexpr float kLeftMargin = 0.01f;
@@ -182,8 +165,7 @@
int64_t send_time_diff = WrappingDifference(
new_packet.header.extension.absoluteSendTime,
old_packet.header.extension.absoluteSendTime, 1ul << 24);
- int64_t recv_time_diff =
- new_packet.log_time_us() - old_packet.log_time_us();
+ int64_t recv_time_diff = new_packet.timestamp - old_packet.timestamp;
double delay_change_us =
recv_time_diff - AbsSendTimeToMicroseconds(send_time_diff);
return delay_change_us / 1000;
@@ -197,7 +179,7 @@
const LoggedRtpPacket& new_packet) {
int64_t send_time_diff = WrappingDifference(
new_packet.header.timestamp, old_packet.header.timestamp, 1ull << 32);
- int64_t recv_time_diff = new_packet.log_time_us() - old_packet.log_time_us();
+ int64_t recv_time_diff = new_packet.timestamp - old_packet.timestamp;
const double kVideoSampleRate = 90000;
// TODO(terelius): We treat all streams as video for now, even though
@@ -211,9 +193,9 @@
if (delay_change < -10000 || 10000 < delay_change) {
RTC_LOG(LS_WARNING) << "Very large delay change. Timestamps correct?";
RTC_LOG(LS_WARNING) << "Old capture time " << old_packet.header.timestamp
- << ", received time " << old_packet.log_time_us();
+ << ", received time " << old_packet.timestamp;
RTC_LOG(LS_WARNING) << "New capture time " << new_packet.header.timestamp
- << ", received time " << new_packet.log_time_us();
+ << ", received time " << new_packet.timestamp;
RTC_LOG(LS_WARNING) << "Receive time difference " << recv_time_diff << " = "
<< static_cast<double>(recv_time_diff) /
kNumMicrosecsPerSec
@@ -226,55 +208,55 @@
return delay_change;
}
-// For each element in data_view, use |f()| to extract a y-coordinate and
+// For each element in data, use |get_y()| to extract a y-coordinate and
// store the result in a TimeSeries.
-template <typename DataType, typename IterableType>
-void ProcessPoints(rtc::FunctionView<rtc::Optional<float>(const DataType&)> f,
- const IterableType& data_view,
- int64_t begin_time,
- TimeSeries* result) {
- for (size_t i = 0; i < data_view.size(); i++) {
- const DataType& elem = data_view[i];
- float x = static_cast<float>(elem.log_time_us() - begin_time) /
+template <typename DataType>
+void ProcessPoints(
+ rtc::FunctionView<rtc::Optional<float>(const DataType&)> get_y,
+ const std::vector<DataType>& data,
+ uint64_t begin_time,
+ TimeSeries* result) {
+ for (size_t i = 0; i < data.size(); i++) {
+ float x = static_cast<float>(data[i].timestamp - begin_time) /
kNumMicrosecsPerSec;
- rtc::Optional<float> y = f(elem);
+ rtc::Optional<float> y = get_y(data[i]);
if (y)
result->points.emplace_back(x, *y);
}
}
-// For each pair of adjacent elements in |data|, use |f()| to extract a
+// For each pair of adjacent elements in |data|, use |get_y| to extract a
// y-coordinate and store the result in a TimeSeries. Note that the x-coordinate
// will be the time of the second element in the pair.
-template <typename DataType, typename ResultType, typename IterableType>
+template <typename DataType, typename ResultType>
void ProcessPairs(
rtc::FunctionView<rtc::Optional<ResultType>(const DataType&,
- const DataType&)> f,
- const IterableType& data,
- int64_t begin_time,
+ const DataType&)> get_y,
+ const std::vector<DataType>& data,
+ uint64_t begin_time,
TimeSeries* result) {
for (size_t i = 1; i < data.size(); i++) {
- float x = static_cast<float>(data[i].log_time_us() - begin_time) /
+ float x = static_cast<float>(data[i].timestamp - begin_time) /
kNumMicrosecsPerSec;
- rtc::Optional<ResultType> y = f(data[i - 1], data[i]);
+ rtc::Optional<ResultType> y = get_y(data[i - 1], data[i]);
if (y)
result->points.emplace_back(x, static_cast<float>(*y));
}
}
-// For each element in data, use |f()| to extract a y-coordinate and
+// For each element in data, use |extract()| to extract a y-coordinate and
// store the result in a TimeSeries.
-template <typename DataType, typename ResultType, typename IterableType>
+template <typename DataType, typename ResultType>
void AccumulatePoints(
- rtc::FunctionView<rtc::Optional<ResultType>(const DataType&)> f,
- const IterableType& data,
- int64_t begin_time,
+ rtc::FunctionView<rtc::Optional<ResultType>(const DataType&)> extract,
+ const std::vector<DataType>& data,
+ uint64_t begin_time,
TimeSeries* result) {
ResultType sum = 0;
for (size_t i = 0; i < data.size(); i++) {
- float x = static_cast<float>(data[i].log_time_us() - begin_time) /
+ float x = static_cast<float>(data[i].timestamp - begin_time) /
kNumMicrosecsPerSec;
- rtc::Optional<ResultType> y = f(data[i]);
+ rtc::Optional<ResultType> y = extract(data[i]);
if (y) {
sum += *y;
result->points.emplace_back(x, static_cast<float>(sum));
@@ -282,21 +264,21 @@
}
}
-// For each pair of adjacent elements in |data|, use |f()| to extract a
+// For each pair of adjacent elements in |data|, use |extract()| to extract a
// y-coordinate and store the result in a TimeSeries. Note that the x-coordinate
// will be the time of the second element in the pair.
-template <typename DataType, typename ResultType, typename IterableType>
+template <typename DataType, typename ResultType>
void AccumulatePairs(
rtc::FunctionView<rtc::Optional<ResultType>(const DataType&,
- const DataType&)> f,
- const IterableType& data,
- int64_t begin_time,
+ const DataType&)> extract,
+ const std::vector<DataType>& data,
+ uint64_t begin_time,
TimeSeries* result) {
ResultType sum = 0;
for (size_t i = 1; i < data.size(); i++) {
- float x = static_cast<float>(data[i].log_time_us() - begin_time) /
+ float x = static_cast<float>(data[i].timestamp - begin_time) /
kNumMicrosecsPerSec;
- rtc::Optional<ResultType> y = f(data[i - 1], data[i]);
+ rtc::Optional<ResultType> y = extract(data[i - 1], data[i]);
if (y)
sum += *y;
result->points.emplace_back(x, static_cast<float>(sum));
@@ -307,31 +289,30 @@
// A data point is generated every |step| microseconds from |begin_time|
// to |end_time|. The value of each data point is the average of the data
// during the preceeding |window_duration_us| microseconds.
-template <typename DataType, typename ResultType, typename IterableType>
+template <typename DataType, typename ResultType>
void MovingAverage(
- rtc::FunctionView<rtc::Optional<ResultType>(const DataType&)> f,
- const IterableType& data_view,
- int64_t begin_time,
- int64_t end_time,
- int64_t window_duration_us,
- int64_t step,
- TimeSeries* result) {
+ rtc::FunctionView<rtc::Optional<ResultType>(const DataType&)> extract,
+ const std::vector<DataType>& data,
+ uint64_t begin_time,
+ uint64_t end_time,
+ uint64_t window_duration_us,
+ uint64_t step,
+ webrtc::plotting::TimeSeries* result) {
size_t window_index_begin = 0;
size_t window_index_end = 0;
ResultType sum_in_window = 0;
- for (int64_t t = begin_time; t < end_time + step; t += step) {
- while (window_index_end < data_view.size() &&
- data_view[window_index_end].log_time_us() < t) {
- rtc::Optional<ResultType> value = f(data_view[window_index_end]);
+ for (uint64_t t = begin_time; t < end_time + step; t += step) {
+ while (window_index_end < data.size() &&
+ data[window_index_end].timestamp < t) {
+ rtc::Optional<ResultType> value = extract(data[window_index_end]);
if (value)
sum_in_window += *value;
++window_index_end;
}
- while (window_index_begin < data_view.size() &&
- data_view[window_index_begin].log_time_us() <
- t - window_duration_us) {
- rtc::Optional<ResultType> value = f(data_view[window_index_begin]);
+ while (window_index_begin < data.size() &&
+ data[window_index_begin].timestamp < t - window_duration_us) {
+ rtc::Optional<ResultType> value = extract(data[window_index_begin]);
if (value)
sum_in_window -= *value;
++window_index_begin;
@@ -425,7 +406,7 @@
}
std::string GetCandidatePairLogDescriptionAsString(
- const LoggedIceCandidatePairConfig& config) {
+ const ParsedRtcEventLog::IceCandidatePairConfig& config) {
// Example: stun:wifi->relay(tcp):cellular@udp:ipv4
// represents a pair of a local server-reflexive candidate on a WiFi network
// and a remote relay candidate using TCP as the relay protocol on a cell
@@ -448,57 +429,244 @@
return ss.str();
}
-std::string GetDirectionAsString(PacketDirection direction) {
- if (direction == kIncomingPacket) {
- return "Incoming";
- } else {
- return "Outgoing";
- }
-}
-
-std::string GetDirectionAsShortString(PacketDirection direction) {
- if (direction == kIncomingPacket) {
- return "In";
- } else {
- return "Out";
- }
-}
-
} // namespace
EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
: parsed_log_(log), window_duration_(250000), step_(10000) {
- begin_time_ = parsed_log_.first_timestamp();
- end_time_ = parsed_log_.last_timestamp();
- if (end_time_ < begin_time_) {
- RTC_LOG(LS_WARNING) << "No useful events in the log.";
- begin_time_ = end_time_ = 0;
- }
- call_duration_s_ = ToCallTime(end_time_);
+ uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
+ uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
- const auto& log_start_events = parsed_log_.start_log_events();
- const auto& log_end_events = parsed_log_.stop_log_events();
- auto start_iter = log_start_events.begin();
- auto end_iter = log_end_events.begin();
- while (start_iter != log_start_events.end()) {
- int64_t start = start_iter->log_time_us();
- ++start_iter;
- rtc::Optional<int64_t> next_start;
- if (start_iter != log_start_events.end())
- next_start.emplace(start_iter->log_time_us());
- if (end_iter != log_end_events.end() &&
- end_iter->log_time_us() <=
- next_start.value_or(std::numeric_limits<int64_t>::max())) {
- int64_t end = end_iter->log_time_us();
- RTC_DCHECK_LE(start, end);
- log_segments_.push_back(std::make_pair(start, end));
- ++end_iter;
- } else {
- // we're missing an end event. Assume that it occurred just before the
- // next start.
- log_segments_.push_back(
- std::make_pair(start, next_start.value_or(end_time_)));
+ PacketDirection direction;
+ uint8_t header[IP_PACKET_SIZE];
+ size_t header_length;
+ size_t total_length;
+
+ uint8_t last_incoming_rtcp_packet[IP_PACKET_SIZE];
+ uint8_t last_incoming_rtcp_packet_length = 0;
+
+ // Make a default extension map for streams without configuration information.
+ // TODO(ivoc): Once configuration of audio streams is stored in the event log,
+ // this can be removed. Tracking bug: webrtc:6399
+ RtpHeaderExtensionMap default_extension_map = GetDefaultHeaderExtensionMap();
+
+ rtc::Optional<uint64_t> last_log_start;
+
+ 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 &&
+ event_type != ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT &&
+ event_type != ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT &&
+ event_type != ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT &&
+ event_type != ParsedRtcEventLog::LOG_START &&
+ event_type != ParsedRtcEventLog::LOG_END) {
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ first_timestamp = std::min(first_timestamp, timestamp);
+ last_timestamp = std::max(last_timestamp, timestamp);
}
+
+ switch (parsed_log_.GetEventType(i)) {
+ case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
+ rtclog::StreamConfig config = parsed_log_.GetVideoReceiveConfig(i);
+ StreamId stream(config.remote_ssrc, kIncomingPacket);
+ video_ssrcs_.insert(stream);
+ StreamId rtx_stream(config.rtx_ssrc, kIncomingPacket);
+ video_ssrcs_.insert(rtx_stream);
+ rtx_ssrcs_.insert(rtx_stream);
+ break;
+ }
+ case ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT: {
+ std::vector<rtclog::StreamConfig> configs =
+ parsed_log_.GetVideoSendConfig(i);
+ for (const auto& config : configs) {
+ StreamId stream(config.local_ssrc, kOutgoingPacket);
+ video_ssrcs_.insert(stream);
+ StreamId rtx_stream(config.rtx_ssrc, kOutgoingPacket);
+ video_ssrcs_.insert(rtx_stream);
+ rtx_ssrcs_.insert(rtx_stream);
+ }
+ break;
+ }
+ case ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT: {
+ rtclog::StreamConfig config = parsed_log_.GetAudioReceiveConfig(i);
+ StreamId stream(config.remote_ssrc, kIncomingPacket);
+ audio_ssrcs_.insert(stream);
+ break;
+ }
+ case ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT: {
+ rtclog::StreamConfig config = parsed_log_.GetAudioSendConfig(i);
+ StreamId stream(config.local_ssrc, kOutgoingPacket);
+ audio_ssrcs_.insert(stream);
+ break;
+ }
+ case ParsedRtcEventLog::RTP_EVENT: {
+ RtpHeaderExtensionMap* extension_map = parsed_log_.GetRtpHeader(
+ i, &direction, header, &header_length, &total_length, nullptr);
+ RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
+ RTPHeader parsed_header;
+ if (extension_map != nullptr) {
+ rtp_parser.Parse(&parsed_header, extension_map);
+ } else {
+ // Use the default extension map.
+ // TODO(ivoc): Once configuration of audio streams is stored in the
+ // event log, this can be removed.
+ // Tracking bug: webrtc:6399
+ rtp_parser.Parse(&parsed_header, &default_extension_map);
+ }
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ StreamId stream(parsed_header.ssrc, direction);
+ rtp_packets_[stream].push_back(LoggedRtpPacket(
+ timestamp, parsed_header, header_length, total_length));
+ break;
+ }
+ case ParsedRtcEventLog::RTCP_EVENT: {
+ uint8_t packet[IP_PACKET_SIZE];
+ parsed_log_.GetRtcpPacket(i, &direction, packet, &total_length);
+ // Currently incoming RTCP packets are logged twice, both for audio and
+ // video. Only act on one of them. Compare against the previous parsed
+ // incoming RTCP packet.
+ if (direction == webrtc::kIncomingPacket) {
+ RTC_CHECK_LE(total_length, IP_PACKET_SIZE);
+ if (total_length == last_incoming_rtcp_packet_length &&
+ memcmp(last_incoming_rtcp_packet, packet, total_length) == 0) {
+ continue;
+ } else {
+ memcpy(last_incoming_rtcp_packet, packet, total_length);
+ last_incoming_rtcp_packet_length = total_length;
+ }
+ }
+ rtcp::CommonHeader header;
+ const uint8_t* packet_end = packet + total_length;
+ for (const uint8_t* block = packet; block < packet_end;
+ block = header.NextPacket()) {
+ RTC_CHECK(header.Parse(block, packet_end - block));
+ if (header.type() == rtcp::TransportFeedback::kPacketType &&
+ header.fmt() == rtcp::TransportFeedback::kFeedbackMessageType) {
+ std::unique_ptr<rtcp::TransportFeedback> rtcp_packet(
+ rtc::MakeUnique<rtcp::TransportFeedback>());
+ if (rtcp_packet->Parse(header)) {
+ uint32_t ssrc = rtcp_packet->sender_ssrc();
+ StreamId stream(ssrc, direction);
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ rtcp_packets_[stream].push_back(LoggedRtcpPacket(
+ timestamp, kRtcpTransportFeedback, std::move(rtcp_packet)));
+ }
+ } else if (header.type() == rtcp::SenderReport::kPacketType) {
+ std::unique_ptr<rtcp::SenderReport> rtcp_packet(
+ rtc::MakeUnique<rtcp::SenderReport>());
+ if (rtcp_packet->Parse(header)) {
+ uint32_t ssrc = rtcp_packet->sender_ssrc();
+ StreamId stream(ssrc, direction);
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ rtcp_packets_[stream].push_back(
+ LoggedRtcpPacket(timestamp, kRtcpSr, std::move(rtcp_packet)));
+ }
+ } else if (header.type() == rtcp::ReceiverReport::kPacketType) {
+ std::unique_ptr<rtcp::ReceiverReport> rtcp_packet(
+ rtc::MakeUnique<rtcp::ReceiverReport>());
+ if (rtcp_packet->Parse(header)) {
+ uint32_t ssrc = rtcp_packet->sender_ssrc();
+ StreamId stream(ssrc, direction);
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ rtcp_packets_[stream].push_back(
+ LoggedRtcpPacket(timestamp, kRtcpRr, std::move(rtcp_packet)));
+ }
+ } else if (header.type() == rtcp::Remb::kPacketType &&
+ header.fmt() == rtcp::Remb::kFeedbackMessageType) {
+ std::unique_ptr<rtcp::Remb> rtcp_packet(
+ rtc::MakeUnique<rtcp::Remb>());
+ if (rtcp_packet->Parse(header)) {
+ uint32_t ssrc = rtcp_packet->sender_ssrc();
+ StreamId stream(ssrc, direction);
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ rtcp_packets_[stream].push_back(LoggedRtcpPacket(
+ timestamp, kRtcpRemb, std::move(rtcp_packet)));
+ }
+ }
+ }
+ break;
+ }
+ case ParsedRtcEventLog::LOG_START: {
+ if (last_log_start) {
+ // A LOG_END event was missing. Use last_timestamp.
+ RTC_DCHECK_GE(last_timestamp, *last_log_start);
+ log_segments_.push_back(
+ std::make_pair(*last_log_start, last_timestamp));
+ }
+ last_log_start = parsed_log_.GetTimestamp(i);
+ break;
+ }
+ case ParsedRtcEventLog::LOG_END: {
+ RTC_DCHECK(last_log_start);
+ log_segments_.push_back(
+ std::make_pair(*last_log_start, parsed_log_.GetTimestamp(i)));
+ last_log_start.reset();
+ break;
+ }
+ case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: {
+ uint32_t this_ssrc;
+ parsed_log_.GetAudioPlayout(i, &this_ssrc);
+ audio_playout_events_[this_ssrc].push_back(parsed_log_.GetTimestamp(i));
+ break;
+ }
+ case ParsedRtcEventLog::LOSS_BASED_BWE_UPDATE: {
+ LossBasedBweUpdate bwe_update;
+ bwe_update.timestamp = parsed_log_.GetTimestamp(i);
+ parsed_log_.GetLossBasedBweUpdate(i, &bwe_update.new_bitrate,
+ &bwe_update.fraction_loss,
+ &bwe_update.expected_packets);
+ bwe_loss_updates_.push_back(bwe_update);
+ break;
+ }
+ case ParsedRtcEventLog::DELAY_BASED_BWE_UPDATE: {
+ bwe_delay_updates_.push_back(parsed_log_.GetDelayBasedBweUpdate(i));
+ break;
+ }
+ case ParsedRtcEventLog::AUDIO_NETWORK_ADAPTATION_EVENT: {
+ AudioNetworkAdaptationEvent ana_event;
+ ana_event.timestamp = parsed_log_.GetTimestamp(i);
+ parsed_log_.GetAudioNetworkAdaptation(i, &ana_event.config);
+ audio_network_adaptation_events_.push_back(ana_event);
+ break;
+ }
+ case ParsedRtcEventLog::BWE_PROBE_CLUSTER_CREATED_EVENT: {
+ bwe_probe_cluster_created_events_.push_back(
+ parsed_log_.GetBweProbeClusterCreated(i));
+ break;
+ }
+ case ParsedRtcEventLog::BWE_PROBE_RESULT_EVENT: {
+ bwe_probe_result_events_.push_back(parsed_log_.GetBweProbeResult(i));
+ break;
+ }
+ case ParsedRtcEventLog::ALR_STATE_EVENT: {
+ alr_state_events_.push_back(parsed_log_.GetAlrState(i));
+ break;
+ }
+ case ParsedRtcEventLog::ICE_CANDIDATE_PAIR_CONFIG: {
+ ice_candidate_pair_configs_.push_back(
+ parsed_log_.GetIceCandidatePairConfig(i));
+ break;
+ }
+ case ParsedRtcEventLog::ICE_CANDIDATE_PAIR_EVENT: {
+ ice_candidate_pair_events_.push_back(
+ parsed_log_.GetIceCandidatePairEvent(i));
+ break;
+ }
+ case ParsedRtcEventLog::UNKNOWN_EVENT: {
+ break;
+ }
+ }
+ }
+
+ if (last_timestamp < first_timestamp) {
+ // No useful events in the log.
+ first_timestamp = last_timestamp = 0;
+ }
+ begin_time_ = first_timestamp;
+ end_time_ = last_timestamp;
+ call_duration_s_ = ToCallTime(end_time_);
+ if (last_log_start) {
+ // The log was missing the last LOG_END event. Fake it.
+ log_segments_.push_back(std::make_pair(*last_log_start, end_time_));
}
RTC_LOG(LS_INFO) << "Found " << log_segments_.size()
<< " (LOG_START, LOG_END) segments in log.";
@@ -510,7 +678,7 @@
BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {}
void OnNetworkChanged(uint32_t bitrate_bps,
- uint8_t fraction_lost,
+ uint8_t fraction_loss,
int64_t rtt_ms,
int64_t probing_interval_ms) override {
last_bitrate_bps_ = bitrate_bps;
@@ -532,90 +700,187 @@
bool bitrate_updated_;
};
+bool EventLogAnalyzer::IsRtxSsrc(StreamId stream_id) const {
+ return rtx_ssrcs_.count(stream_id) == 1;
+}
+
+bool EventLogAnalyzer::IsVideoSsrc(StreamId stream_id) const {
+ return video_ssrcs_.count(stream_id) == 1;
+}
+
+bool EventLogAnalyzer::IsAudioSsrc(StreamId stream_id) const {
+ return audio_ssrcs_.count(stream_id) == 1;
+}
+
+std::string EventLogAnalyzer::GetStreamName(StreamId stream_id) const {
+ std::stringstream name;
+ if (IsAudioSsrc(stream_id)) {
+ name << "Audio ";
+ } else if (IsVideoSsrc(stream_id)) {
+ name << "Video ";
+ } else {
+ name << "Unknown ";
+ }
+ if (IsRtxSsrc(stream_id))
+ name << "RTX ";
+ if (stream_id.GetDirection() == kIncomingPacket) {
+ name << "(In) ";
+ } else {
+ name << "(Out) ";
+ }
+ name << SsrcToString(stream_id.GetSsrc());
+ return name.str();
+}
+
+// This is much more reliable for outgoing streams than for incoming streams.
+rtc::Optional<uint32_t> EventLogAnalyzer::EstimateRtpClockFrequency(
+ const std::vector<LoggedRtpPacket>& packets) const {
+ RTC_CHECK(packets.size() >= 2);
+ uint64_t end_time_us = log_segments_.empty()
+ ? std::numeric_limits<uint64_t>::max()
+ : log_segments_.front().second;
+ SeqNumUnwrapper<uint32_t> unwrapper;
+ uint64_t first_rtp_timestamp = unwrapper.Unwrap(packets[0].header.timestamp);
+ uint64_t first_log_timestamp = packets[0].timestamp;
+ uint64_t last_rtp_timestamp = first_rtp_timestamp;
+ uint64_t last_log_timestamp = first_log_timestamp;
+ for (size_t i = 1; i < packets.size(); i++) {
+ if (packets[i].timestamp > end_time_us)
+ break;
+ last_rtp_timestamp = unwrapper.Unwrap(packets[i].header.timestamp);
+ last_log_timestamp = packets[i].timestamp;
+ }
+ if (last_log_timestamp - first_log_timestamp < kNumMicrosecsPerSec) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to estimate RTP clock frequency: Stream too short. ("
+ << packets.size() << " packets, "
+ << last_log_timestamp - first_log_timestamp << " us)";
+ return rtc::nullopt;
+ }
+ double duration =
+ static_cast<double>(last_log_timestamp - first_log_timestamp) /
+ kNumMicrosecsPerSec;
+ double estimated_frequency =
+ (last_rtp_timestamp - first_rtp_timestamp) / duration;
+ for (uint32_t f : {8000, 16000, 32000, 48000, 90000}) {
+ if (std::fabs(estimated_frequency - f) < 0.05 * f) {
+ return f;
+ }
+ }
+ RTC_LOG(LS_WARNING) << "Failed to estimate RTP clock frequency: Estimate "
+ << estimated_frequency
+ << "not close to any stardard RTP frequency.";
+ return rtc::nullopt;
+}
+
float EventLogAnalyzer::ToCallTime(int64_t timestamp) const {
return static_cast<float>(timestamp - begin_time_) / kNumMicrosecsPerSec;
}
-void EventLogAnalyzer::CreatePacketGraph(PacketDirection direction,
+void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- // Filter on SSRC.
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) {
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != desired_direction ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
continue;
}
- TimeSeries time_series(GetStreamName(direction, stream.ssrc),
- LineStyle::kBar);
- auto GetPacketSize = [](const LoggedRtpPacket& packet) {
- return rtc::Optional<float>(packet.total_length);
- };
- ProcessPoints<LoggedRtpPacket>(GetPacketSize, stream.packet_view,
- begin_time_, &time_series);
+ TimeSeries time_series(GetStreamName(stream_id), LineStyle::kBar);
+ ProcessPoints<LoggedRtpPacket>(
+ [](const LoggedRtpPacket& packet) {
+ return rtc::Optional<float>(packet.total_length);
+ },
+ packet_stream, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
}
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Packet size (bytes)", kBottomMargin,
kTopMargin);
- plot->SetTitle(GetDirectionAsString(direction) + " RTP packets");
+ if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
+ plot->SetTitle("Incoming RTP packets");
+ } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
+ plot->SetTitle("Outgoing RTP packets");
+ }
}
-template <typename IterableType>
+template <typename T>
void EventLogAnalyzer::CreateAccumulatedPacketsTimeSeries(
+ PacketDirection desired_direction,
Plot* plot,
- const IterableType& packets,
- const std::string& label) {
- TimeSeries time_series(label, LineStyle::kStep);
- for (size_t i = 0; i < packets.size(); i++) {
- float x = ToCallTime(packets[i].log_time_us());
- time_series.points.emplace_back(x, i + 1);
+ const std::map<StreamId, std::vector<T>>& packets,
+ const std::string& label_prefix) {
+ for (auto& kv : packets) {
+ StreamId stream_id = kv.first;
+ const std::vector<T>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != desired_direction ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
+ continue;
+ }
+
+ std::string label = label_prefix + " " + GetStreamName(stream_id);
+ TimeSeries time_series(label, LineStyle::kStep);
+ for (size_t i = 0; i < packet_stream.size(); i++) {
+ float x = ToCallTime(packet_stream[i].timestamp);
+ time_series.points.emplace_back(x, i + 1);
+ }
+
+ plot->AppendTimeSeries(std::move(time_series));
}
- plot->AppendTimeSeries(std::move(time_series));
}
-void EventLogAnalyzer::CreateAccumulatedPacketsGraph(PacketDirection direction,
- Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_))
- continue;
- std::string label =
- std::string("RTP ") + GetStreamName(direction, stream.ssrc);
- CreateAccumulatedPacketsTimeSeries(plot, stream.packet_view, label);
- }
- std::string label =
- std::string("RTCP ") + "(" + GetDirectionAsShortString(direction) + ")";
- if (direction == kIncomingPacket) {
- CreateAccumulatedPacketsTimeSeries(
- plot, parsed_log_.incoming_rtcp_packets(), label);
- } else {
- CreateAccumulatedPacketsTimeSeries(
- plot, parsed_log_.outgoing_rtcp_packets(), label);
- }
+void EventLogAnalyzer::CreateAccumulatedPacketsGraph(
+ PacketDirection desired_direction,
+ Plot* plot) {
+ CreateAccumulatedPacketsTimeSeries(desired_direction, plot, rtp_packets_,
+ "RTP");
+ CreateAccumulatedPacketsTimeSeries(desired_direction, plot, rtcp_packets_,
+ "RTCP");
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Received Packets", kBottomMargin, kTopMargin);
- plot->SetTitle(std::string("Accumulated ") + GetDirectionAsString(direction) +
- " RTP/RTCP packets");
+ if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
+ plot->SetTitle("Accumulated Incoming RTP/RTCP packets");
+ } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
+ plot->SetTitle("Accumulated Outgoing RTP/RTCP packets");
+ }
}
// For each SSRC, plot the time between the consecutive playouts.
void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
- for (const auto& playout_stream : parsed_log_.audio_playout_events()) {
- uint32_t ssrc = playout_stream.first;
- if (!MatchingSsrc(ssrc, desired_ssrc_))
- continue;
- rtc::Optional<int64_t> last_playout;
- TimeSeries time_series(SsrcToString(ssrc), LineStyle::kBar);
- for (const auto& playout_time : playout_stream.second) {
- float x = ToCallTime(playout_time);
- // If there were no previous playouts, place the point on the x-axis.
- float y = static_cast<float>(playout_time -
- last_playout.value_or(playout_time)) /
- 1000;
- time_series.points.push_back(TimeSeriesPoint(x, y));
- last_playout.emplace(playout_time);
+ std::map<uint32_t, TimeSeries> time_series;
+ std::map<uint32_t, uint64_t> last_playout;
+
+ uint32_t ssrc;
+
+ 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 = ToCallTime(timestamp);
+ 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;
+ }
+ time_series[ssrc].points.push_back(TimeSeriesPoint(x, y));
+ last_playout[ssrc] = timestamp;
+ }
}
- plot->AppendTimeSeries(std::move(time_series));
+ }
+
+ // Set labels and put in graph.
+ for (auto& kv : time_series) {
+ kv.second.label = SsrcToString(kv.first);
+ kv.second.line_style = LineStyle::kBar;
+ plot->AppendTimeSeries(std::move(kv.second));
}
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
@@ -625,51 +890,59 @@
}
// For audio SSRCs, plot the audio level.
-void EventLogAnalyzer::CreateAudioLevelGraph(PacketDirection direction,
- Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- if (!IsAudioSsrc(direction, stream.ssrc))
- continue;
- TimeSeries time_series(GetStreamName(direction, stream.ssrc),
- LineStyle::kLine);
- for (auto& packet : stream.packet_view) {
+void EventLogAnalyzer::CreateAudioLevelGraph(Plot* plot) {
+ std::map<StreamId, TimeSeries> time_series;
+
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // TODO(ivoc): When audio send/receive configs are stored in the event
+ // log, a check should be added here to only process audio
+ // streams. Tracking bug: webrtc:6399
+ for (auto& packet : packet_stream) {
if (packet.header.extension.hasAudioLevel) {
- float x = ToCallTime(packet.log_time_us());
+ float x = ToCallTime(packet.timestamp);
// The audio level is stored in -dBov (so e.g. -10 dBov is stored as 10)
// Here we convert it to dBov.
float y = static_cast<float>(-packet.header.extension.audioLevel);
- time_series.points.emplace_back(TimeSeriesPoint(x, y));
+ time_series[stream_id].points.emplace_back(TimeSeriesPoint(x, y));
}
}
- plot->AppendTimeSeries(std::move(time_series));
+ }
+
+ for (auto& series : time_series) {
+ series.second.label = GetStreamName(series.first);
+ series.second.line_style = LineStyle::kLine;
+ plot->AppendTimeSeries(std::move(series.second));
}
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetYAxis(-127, 0, "Audio level (dBov)", kBottomMargin,
kTopMargin);
- plot->SetTitle(GetDirectionAsString(direction) + " audio level");
+ plot->SetTitle("Audio level");
}
// For each SSRC, plot the time between the consecutive playouts.
void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
- for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) {
- // Filter on SSRC.
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) {
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != kIncomingPacket ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
continue;
}
- TimeSeries time_series(GetStreamName(kIncomingPacket, stream.ssrc),
- LineStyle::kBar);
- auto GetSequenceNumberDiff = [](const LoggedRtpPacketIncoming& old_packet,
- const LoggedRtpPacketIncoming& new_packet) {
- int64_t diff =
- WrappingDifference(new_packet.rtp.header.sequenceNumber,
- old_packet.rtp.header.sequenceNumber, 1ul << 16);
- return diff;
- };
- ProcessPairs<LoggedRtpPacketIncoming, float>(GetSequenceNumberDiff,
- stream.incoming_packets,
- begin_time_, &time_series);
+ TimeSeries time_series(GetStreamName(stream_id), LineStyle::kBar);
+ ProcessPairs<LoggedRtpPacket, float>(
+ [](const LoggedRtpPacket& old_packet,
+ const LoggedRtpPacket& new_packet) {
+ int64_t diff =
+ WrappingDifference(new_packet.header.sequenceNumber,
+ old_packet.header.sequenceNumber, 1ul << 16);
+ return diff;
+ },
+ packet_stream, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
}
@@ -680,47 +953,47 @@
}
void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) {
- for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) {
- const std::vector<LoggedRtpPacketIncoming>& packets =
- stream.incoming_packets;
- // Filter on SSRC.
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_) || packets.size() == 0) {
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != kIncomingPacket ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_) ||
+ packet_stream.size() == 0) {
continue;
}
- TimeSeries time_series(GetStreamName(kIncomingPacket, stream.ssrc),
- LineStyle::kLine, PointStyle::kHighlight);
- // TODO(terelius): Should the window and step size be read from the class
- // instead?
- const int64_t kWindowUs = 1000000;
- const int64_t kStep = 1000000;
+ TimeSeries time_series(GetStreamName(stream_id), LineStyle::kLine,
+ PointStyle::kHighlight);
+ const uint64_t kWindowUs = 1000000;
+ const uint64_t kStep = 1000000;
SeqNumUnwrapper<uint16_t> unwrapper_;
SeqNumUnwrapper<uint16_t> prior_unwrapper_;
size_t window_index_begin = 0;
size_t window_index_end = 0;
- uint64_t highest_seq_number =
- unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1;
- uint64_t highest_prior_seq_number =
- prior_unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1;
+ int64_t highest_seq_number =
+ unwrapper_.Unwrap(packet_stream[0].header.sequenceNumber) - 1;
+ int64_t highest_prior_seq_number =
+ prior_unwrapper_.Unwrap(packet_stream[0].header.sequenceNumber) - 1;
- for (int64_t t = begin_time_; t < end_time_ + kStep; t += kStep) {
- while (window_index_end < packets.size() &&
- packets[window_index_end].rtp.log_time_us() < t) {
- uint64_t sequence_number = unwrapper_.Unwrap(
- packets[window_index_end].rtp.header.sequenceNumber);
+ for (uint64_t t = begin_time_; t < end_time_ + kStep; t += kStep) {
+ while (window_index_end < packet_stream.size() &&
+ packet_stream[window_index_end].timestamp < t) {
+ int64_t sequence_number = unwrapper_.Unwrap(
+ packet_stream[window_index_end].header.sequenceNumber);
highest_seq_number = std::max(highest_seq_number, sequence_number);
++window_index_end;
}
- while (window_index_begin < packets.size() &&
- packets[window_index_begin].rtp.log_time_us() < t - kWindowUs) {
- uint64_t sequence_number = prior_unwrapper_.Unwrap(
- packets[window_index_begin].rtp.header.sequenceNumber);
+ while (window_index_begin < packet_stream.size() &&
+ packet_stream[window_index_begin].timestamp < t - kWindowUs) {
+ int64_t sequence_number = prior_unwrapper_.Unwrap(
+ packet_stream[window_index_begin].header.sequenceNumber);
highest_prior_seq_number =
std::max(highest_prior_seq_number, sequence_number);
++window_index_begin;
}
float x = ToCallTime(t);
- uint64_t expected_packets = highest_seq_number - highest_prior_seq_number;
+ int64_t expected_packets = highest_seq_number - highest_prior_seq_number;
if (expected_packets > 0) {
int64_t received_packets = window_index_end - window_index_begin;
int64_t lost_packets = expected_packets - received_packets;
@@ -738,28 +1011,28 @@
}
void EventLogAnalyzer::CreateIncomingDelayDeltaGraph(Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(kIncomingPacket)) {
- // Filter on SSRC.
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_) ||
- IsAudioSsrc(kIncomingPacket, stream.ssrc) ||
- !IsVideoSsrc(kIncomingPacket, stream.ssrc) ||
- IsRtxSsrc(kIncomingPacket, stream.ssrc)) {
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != kIncomingPacket ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_) ||
+ IsAudioSsrc(stream_id) || !IsVideoSsrc(stream_id) ||
+ IsRtxSsrc(stream_id)) {
continue;
}
- TimeSeries capture_time_data(
- GetStreamName(kIncomingPacket, stream.ssrc) + " capture-time",
- LineStyle::kBar);
+ TimeSeries capture_time_data(GetStreamName(stream_id) + " capture-time",
+ LineStyle::kBar);
ProcessPairs<LoggedRtpPacket, double>(NetworkDelayDiff_CaptureTime,
- stream.packet_view, begin_time_,
+ packet_stream, begin_time_,
&capture_time_data);
plot->AppendTimeSeries(std::move(capture_time_data));
- TimeSeries send_time_data(
- GetStreamName(kIncomingPacket, stream.ssrc) + " abs-send-time",
- LineStyle::kBar);
+ TimeSeries send_time_data(GetStreamName(stream_id) + " abs-send-time",
+ LineStyle::kBar);
ProcessPairs<LoggedRtpPacket, double>(NetworkDelayDiff_AbsSendTime,
- stream.packet_view, begin_time_,
+ packet_stream, begin_time_,
&send_time_data);
plot->AppendTimeSeries(std::move(send_time_data));
}
@@ -771,28 +1044,28 @@
}
void EventLogAnalyzer::CreateIncomingDelayGraph(Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(kIncomingPacket)) {
- // Filter on SSRC.
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_) ||
- IsAudioSsrc(kIncomingPacket, stream.ssrc) ||
- !IsVideoSsrc(kIncomingPacket, stream.ssrc) ||
- IsRtxSsrc(kIncomingPacket, stream.ssrc)) {
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != kIncomingPacket ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_) ||
+ IsAudioSsrc(stream_id) || !IsVideoSsrc(stream_id) ||
+ IsRtxSsrc(stream_id)) {
continue;
}
- TimeSeries capture_time_data(
- GetStreamName(kIncomingPacket, stream.ssrc) + " capture-time",
- LineStyle::kLine);
+ TimeSeries capture_time_data(GetStreamName(stream_id) + " capture-time",
+ LineStyle::kLine);
AccumulatePairs<LoggedRtpPacket, double>(NetworkDelayDiff_CaptureTime,
- stream.packet_view, begin_time_,
+ packet_stream, begin_time_,
&capture_time_data);
plot->AppendTimeSeries(std::move(capture_time_data));
- TimeSeries send_time_data(
- GetStreamName(kIncomingPacket, stream.ssrc) + " abs-send-time",
- LineStyle::kLine);
+ TimeSeries send_time_data(GetStreamName(stream_id) + " abs-send-time",
+ LineStyle::kLine);
AccumulatePairs<LoggedRtpPacket, double>(NetworkDelayDiff_AbsSendTime,
- stream.packet_view, begin_time_,
+ packet_stream, begin_time_,
&send_time_data);
plot->AppendTimeSeries(std::move(send_time_data));
}
@@ -807,9 +1080,9 @@
void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) {
TimeSeries time_series("Fraction lost", LineStyle::kLine,
PointStyle::kHighlight);
- for (auto& bwe_update : parsed_log_.bwe_loss_updates()) {
- float x = ToCallTime(bwe_update.log_time_us());
- float y = static_cast<float>(bwe_update.fraction_lost) / 255 * 100;
+ for (auto& bwe_update : bwe_loss_updates_) {
+ float x = ToCallTime(bwe_update.timestamp);
+ float y = static_cast<float>(bwe_update.fraction_loss) / 255 * 100;
time_series.points.emplace_back(x, y);
}
@@ -821,82 +1094,51 @@
}
// Plot the total bandwidth used by all RTP streams.
-void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) {
- // TODO(terelius): This could be provided by the parser.
- std::multimap<int64_t, size_t> packets_in_order;
- for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) {
- for (const LoggedRtpPacketIncoming& packet : stream.incoming_packets)
- packets_in_order.insert(
- std::make_pair(packet.rtp.log_time_us(), packet.rtp.total_length));
+void EventLogAnalyzer::CreateTotalBitrateGraph(
+ PacketDirection desired_direction,
+ Plot* plot,
+ bool show_detector_state,
+ bool show_alr_state) {
+ 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, &total_length,
+ nullptr);
+ if (direction == desired_direction) {
+ uint64_t timestamp = parsed_log_.GetTimestamp(i);
+ packets.push_back(TimestampSize(timestamp, total_length));
+ }
+ }
}
- auto window_begin = packets_in_order.begin();
- auto window_end = packets_in_order.begin();
+ 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.
TimeSeries bitrate_series("Bitrate", LineStyle::kLine);
- for (int64_t time = begin_time_; time < end_time_ + step_; time += step_) {
- while (window_end != packets_in_order.end() && window_end->first < time) {
- bytes_in_window += window_end->second;
- ++window_end;
+ 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_begin != packets_in_order.end() &&
- window_begin->first < time - window_duration_) {
- RTC_DCHECK_LE(window_begin->second, bytes_in_window);
- bytes_in_window -= window_begin->second;
- ++window_begin;
- }
- float window_duration_in_seconds =
- static_cast<float>(window_duration_) / kNumMicrosecsPerSec;
- float x = ToCallTime(time);
- float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
- bitrate_series.points.emplace_back(x, y);
- }
- plot->AppendTimeSeries(std::move(bitrate_series));
-
- // Overlay the outgoing REMB over incoming bitrate.
- TimeSeries remb_series("Remb", LineStyle::kStep);
- for (const auto& rtcp : parsed_log_.rembs(kOutgoingPacket)) {
- float x = ToCallTime(rtcp.log_time_us());
- float y = static_cast<float>(rtcp.remb.bitrate_bps()) / 1000;
- remb_series.points.emplace_back(x, y);
- }
- plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series));
-
- plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
- plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin);
- plot->SetTitle("Incoming RTP bitrate");
-}
-
-// Plot the total bandwidth used by all RTP streams.
-void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot,
- bool show_detector_state,
- bool show_alr_state) {
- // TODO(terelius): This could be provided by the parser.
- std::multimap<int64_t, size_t> packets_in_order;
- for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) {
- for (const LoggedRtpPacketOutgoing& packet : stream.outgoing_packets)
- packets_in_order.insert(
- std::make_pair(packet.rtp.log_time_us(), packet.rtp.total_length));
- }
-
- auto window_begin = packets_in_order.begin();
- auto window_end = packets_in_order.begin();
- size_t bytes_in_window = 0;
-
- // Calculate a moving average of the bitrate and store in a TimeSeries.
- TimeSeries bitrate_series("Bitrate", LineStyle::kLine);
- for (int64_t time = begin_time_; time < end_time_ + step_; time += step_) {
- while (window_end != packets_in_order.end() && window_end->first < time) {
- bytes_in_window += window_end->second;
- ++window_end;
- }
- while (window_begin != packets_in_order.end() &&
- window_begin->first < time - window_duration_) {
- RTC_DCHECK_LE(window_begin->second, bytes_in_window);
- bytes_in_window -= window_begin->second;
- ++window_begin;
+ 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_) / kNumMicrosecsPerSec;
@@ -907,161 +1149,195 @@
plot->AppendTimeSeries(std::move(bitrate_series));
// Overlay the send-side bandwidth estimate over the outgoing bitrate.
- TimeSeries loss_series("Loss-based estimate", LineStyle::kStep);
- for (auto& loss_update : parsed_log_.bwe_loss_updates()) {
- float x = ToCallTime(loss_update.log_time_us());
- float y = static_cast<float>(loss_update.bitrate_bps) / 1000;
- loss_series.points.emplace_back(x, y);
- }
+ if (desired_direction == kOutgoingPacket) {
+ TimeSeries loss_series("Loss-based estimate", LineStyle::kStep);
+ for (auto& loss_update : bwe_loss_updates_) {
+ float x = ToCallTime(loss_update.timestamp);
+ float y = static_cast<float>(loss_update.new_bitrate) / 1000;
+ loss_series.points.emplace_back(x, y);
+ }
- TimeSeries delay_series("Delay-based estimate", LineStyle::kStep);
- 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;
+ TimeSeries delay_series("Delay-based estimate", LineStyle::kStep);
+ 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;
+ BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal;
- for (auto& delay_update : parsed_log_.bwe_delay_updates()) {
- float x = ToCallTime(delay_update.log_time_us());
- float y = static_cast<float>(delay_update.bitrate_bps) / 1000;
+ for (auto& delay_update : bwe_delay_updates_) {
+ float x = ToCallTime(delay_update.timestamp);
+ 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;
+ 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;
- case BandwidthUsage::kLast:
- RTC_NOTREACHED();
+ 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;
+ case BandwidthUsage::kLast:
+ RTC_NOTREACHED();
+ }
+ }
+
+ 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.", LineStyle::kNone,
+ PointStyle::kHighlight);
+ for (auto& cluster : bwe_probe_cluster_created_events_) {
+ float x = ToCallTime(cluster.timestamp);
+ float y = static_cast<float>(cluster.bitrate_bps) / 1000;
+ created_series.points.emplace_back(x, y);
+ }
+
+ TimeSeries result_series("Probing results.", LineStyle::kNone,
+ PointStyle::kHighlight);
+ for (auto& result : bwe_probe_result_events_) {
+ if (result.bitrate_bps) {
+ float x = ToCallTime(result.timestamp);
+ float y = static_cast<float>(*result.bitrate_bps) / 1000;
+ result_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.", LineStyle::kNone,
- PointStyle::kHighlight);
- for (auto& cluster : parsed_log_.bwe_probe_cluster_created_events()) {
- float x = ToCallTime(cluster.log_time_us());
- float y = static_cast<float>(cluster.bitrate_bps) / 1000;
- created_series.points.emplace_back(x, y);
- }
-
- TimeSeries result_series("Probing results.", LineStyle::kNone,
- PointStyle::kHighlight);
- for (auto& result : parsed_log_.bwe_probe_result_events()) {
- if (result.bitrate_bps) {
- float x = ToCallTime(result.log_time_us());
- float y = static_cast<float>(*result.bitrate_bps) / 1000;
- result_series.points.emplace_back(x, y);
+ IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal);
+ bool previously_in_alr = false;
+ int64_t alr_start = 0;
+ for (auto& alr : alr_state_events_) {
+ float y = ToCallTime(alr.timestamp);
+ if (!previously_in_alr && alr.in_alr) {
+ alr_start = alr.timestamp;
+ previously_in_alr = true;
+ } else if (previously_in_alr && !alr.in_alr) {
+ float x = ToCallTime(alr_start);
+ alr_state.intervals.emplace_back(x, y);
+ previously_in_alr = false;
+ }
}
- }
- IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal);
- bool previously_in_alr = false;
- int64_t alr_start = 0;
- for (auto& alr : parsed_log_.alr_state_events()) {
- float y = ToCallTime(alr.log_time_us());
- if (!previously_in_alr && alr.in_alr) {
- alr_start = alr.log_time_us();
- previously_in_alr = true;
- } else if (previously_in_alr && !alr.in_alr) {
+ if (previously_in_alr) {
float x = ToCallTime(alr_start);
+ float y = ToCallTime(end_time_);
alr_state.intervals.emplace_back(x, y);
- previously_in_alr = false;
+ }
+
+ if (show_detector_state) {
+ plot->AppendIntervalSeries(std::move(overusing_series));
+ plot->AppendIntervalSeries(std::move(underusing_series));
+ plot->AppendIntervalSeries(std::move(normal_series));
+ }
+
+ if (show_alr_state) {
+ plot->AppendIntervalSeries(std::move(alr_state));
+ }
+ plot->AppendTimeSeries(std::move(loss_series));
+ plot->AppendTimeSeries(std::move(delay_series));
+ plot->AppendTimeSeries(std::move(created_series));
+ plot->AppendTimeSeries(std::move(result_series));
+ }
+
+ // Overlay the incoming REMB over the outgoing bitrate
+ // and outgoing REMB over incoming bitrate.
+ PacketDirection remb_direction =
+ desired_direction == kOutgoingPacket ? kIncomingPacket : kOutgoingPacket;
+ TimeSeries remb_series("Remb", LineStyle::kStep);
+ std::multimap<uint64_t, const LoggedRtcpPacket*> remb_packets;
+ for (const auto& kv : rtcp_packets_) {
+ if (kv.first.GetDirection() == remb_direction) {
+ for (const LoggedRtcpPacket& rtcp_packet : kv.second) {
+ if (rtcp_packet.type == kRtcpRemb) {
+ remb_packets.insert(
+ std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
+ }
+ }
}
}
- if (previously_in_alr) {
- float x = ToCallTime(alr_start);
- float y = ToCallTime(end_time_);
- alr_state.intervals.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));
- }
-
- if (show_alr_state) {
- plot->AppendIntervalSeries(std::move(alr_state));
- }
- plot->AppendTimeSeries(std::move(loss_series));
- plot->AppendTimeSeries(std::move(delay_series));
- plot->AppendTimeSeries(std::move(created_series));
- plot->AppendTimeSeries(std::move(result_series));
-
- // Overlay the incoming REMB over the outgoing bitrate.
- TimeSeries remb_series("Remb", LineStyle::kStep);
- for (const auto& rtcp : parsed_log_.rembs(kIncomingPacket)) {
- float x = ToCallTime(rtcp.log_time_us());
- float y = static_cast<float>(rtcp.remb.bitrate_bps()) / 1000;
+ for (const auto& kv : remb_packets) {
+ const LoggedRtcpPacket* const rtcp = kv.second;
+ const rtcp::Remb* const remb = static_cast<rtcp::Remb*>(rtcp->packet.get());
+ float x = ToCallTime(rtcp->timestamp);
+ float y = static_cast<float>(remb->bitrate_bps()) / 1000;
remb_series.points.emplace_back(x, y);
}
plot->AppendTimeSeriesIfNotEmpty(std::move(remb_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin);
- plot->SetTitle("Outgoing RTP bitrate");
+ if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
+ plot->SetTitle("Incoming RTP bitrate");
+ } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
+ plot->SetTitle("Outgoing RTP bitrate");
+ }
}
// For each SSRC, plot the bandwidth used by that stream.
-void EventLogAnalyzer::CreateStreamBitrateGraph(PacketDirection direction,
- Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- // Filter on SSRC.
- if (!MatchingSsrc(stream.ssrc, desired_ssrc_)) {
+void EventLogAnalyzer::CreateStreamBitrateGraph(
+ PacketDirection desired_direction,
+ Plot* plot) {
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
+ // Filter on direction and SSRC.
+ if (stream_id.GetDirection() != desired_direction ||
+ !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
continue;
}
- TimeSeries time_series(GetStreamName(direction, stream.ssrc),
- LineStyle::kLine);
- auto GetPacketSizeKilobits = [](const LoggedRtpPacket& packet) {
- return packet.total_length * 8.0 / 1000.0;
- };
+ TimeSeries time_series(GetStreamName(stream_id), LineStyle::kLine);
MovingAverage<LoggedRtpPacket, double>(
- GetPacketSizeKilobits, stream.packet_view, begin_time_, end_time_,
- window_duration_, step_, &time_series);
+ [](const LoggedRtpPacket& packet) {
+ return packet.total_length * 8.0 / 1000.0;
+ },
+ packet_stream, begin_time_, end_time_, window_duration_, step_,
+ &time_series);
plot->AppendTimeSeries(std::move(time_series));
}
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin);
- plot->SetTitle(GetDirectionAsString(direction) + " bitrate per stream");
+ if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
+ plot->SetTitle("Incoming bitrate per stream");
+ } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
+ plot->SetTitle("Outgoing bitrate per stream");
+ }
}
void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) {
- using RtpPacketType = LoggedRtpPacketOutgoing;
- using TransportFeedbackType = LoggedRtcpPacketTransportFeedback;
+ std::multimap<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
+ std::multimap<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
- // TODO(terelius): This could be provided by the parser.
- std::multimap<int64_t, const RtpPacketType*> outgoing_rtp;
- for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) {
- for (const RtpPacketType& rtp_packet : stream.outgoing_packets)
- outgoing_rtp.insert(
- std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet));
+ for (const auto& kv : rtp_packets_) {
+ if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
+ for (const LoggedRtpPacket& rtp_packet : kv.second)
+ outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
+ }
}
- const std::vector<TransportFeedbackType>& incoming_rtcp =
- parsed_log_.transport_feedbacks(kIncomingPacket);
+ for (const auto& kv : rtcp_packets_) {
+ if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
+ for (const LoggedRtcpPacket& rtcp_packet : kv.second)
+ incoming_rtcp.insert(
+ std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
+ }
+ }
SimulatedClock clock(0);
BitrateObserver observer;
@@ -1091,7 +1367,7 @@
auto NextRtcpTime = [&]() {
if (rtcp_iterator != incoming_rtcp.end())
- return static_cast<int64_t>(rtcp_iterator->log_time_us());
+ return static_cast<int64_t>(rtcp_iterator->first);
return std::numeric_limits<int64_t>::max();
};
@@ -1122,38 +1398,41 @@
clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime());
- cc.OnTransportFeedback(rtcp_iterator->transport_feedback);
- std::vector<PacketFeedback> feedback = cc.GetTransportFeedbackVector();
- SortPacketFeedbackVector(&feedback);
- rtc::Optional<uint32_t> bitrate_bps;
- if (!feedback.empty()) {
+ const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
+ if (rtcp.type == kRtcpTransportFeedback) {
+ cc.OnTransportFeedback(
+ *static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
+ std::vector<PacketFeedback> feedback = cc.GetTransportFeedbackVector();
+ SortPacketFeedbackVector(&feedback);
+ rtc::Optional<uint32_t> bitrate_bps;
+ if (!feedback.empty()) {
#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
- acknowledged_bitrate_estimator.IncomingPacketFeedbackVector(feedback);
+ acknowledged_bitrate_estimator.IncomingPacketFeedbackVector(feedback);
#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
- for (const PacketFeedback& packet : feedback)
- acked_bitrate.Update(packet.payload_size, packet.arrival_time_ms);
- bitrate_bps = acked_bitrate.Rate(feedback.back().arrival_time_ms);
+ for (const PacketFeedback& packet : feedback)
+ acked_bitrate.Update(packet.payload_size, packet.arrival_time_ms);
+ bitrate_bps = acked_bitrate.Rate(feedback.back().arrival_time_ms);
+ }
+ float x = ToCallTime(clock.TimeInMicroseconds());
+ float y = bitrate_bps.value_or(0) / 1000;
+ acked_time_series.points.emplace_back(x, y);
+#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
+ y = acknowledged_bitrate_estimator.bitrate_bps().value_or(0) / 1000;
+ acked_estimate_time_series.points.emplace_back(x, y);
+#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
}
- float x = ToCallTime(clock.TimeInMicroseconds());
- float y = bitrate_bps.value_or(0) / 1000;
- acked_time_series.points.emplace_back(x, y);
-#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
- y = acknowledged_bitrate_estimator.bitrate_bps().value_or(0) / 1000;
- acked_estimate_time_series.points.emplace_back(x, y);
-#endif // !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
++rtcp_iterator;
}
if (clock.TimeInMicroseconds() >= NextRtpTime()) {
RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime());
- const RtpPacketType& rtp_packet = *rtp_iterator->second;
- if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) {
- RTC_DCHECK(rtp_packet.rtp.header.extension.hasTransportSequenceNumber);
- cc.AddPacket(rtp_packet.rtp.header.ssrc,
- rtp_packet.rtp.header.extension.transportSequenceNumber,
- rtp_packet.rtp.total_length, PacedPacketInfo());
+ const LoggedRtpPacket& rtp = *rtp_iterator->second;
+ if (rtp.header.extension.hasTransportSequenceNumber) {
+ RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
+ cc.AddPacket(rtp.header.ssrc,
+ rtp.header.extension.transportSequenceNumber,
+ rtp.total_length, PacedPacketInfo());
rtc::SentPacket sent_packet(
- rtp_packet.rtp.header.extension.transportSequenceNumber,
- rtp_packet.rtp.log_time_us() / 1000);
+ rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
cc.OnSentPacket(sent_packet);
}
++rtp_iterator;
@@ -1182,7 +1461,6 @@
}
void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) {
- using RtpPacketType = LoggedRtpPacketIncoming;
class RembInterceptingPacketRouter : public PacketRouter {
public:
void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
@@ -1203,19 +1481,19 @@
bool bitrate_updated_;
};
- std::multimap<int64_t, const RtpPacketType*> incoming_rtp;
+ std::multimap<uint64_t, const LoggedRtpPacket*> incoming_rtp;
- for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) {
- if (IsVideoSsrc(kIncomingPacket, stream.ssrc)) {
- for (const auto& rtp_packet : stream.incoming_packets)
- incoming_rtp.insert(
- std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet));
+ for (const auto& kv : rtp_packets_) {
+ if (kv.first.GetDirection() == PacketDirection::kIncomingPacket &&
+ IsVideoSsrc(kv.first)) {
+ for (const LoggedRtpPacket& rtp_packet : kv.second)
+ incoming_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
}
}
SimulatedClock clock(0);
RembInterceptingPacketRouter packet_router;
- // TODO(terelius): The PacketRouter is used as the RemoteBitrateObserver.
+ // TODO(terelius): The PacketRrouter is the used as the RemoteBitrateObserver.
// Is this intentional?
ReceiveSideCongestionController rscc(&clock, &packet_router);
// TODO(holmer): Log the call config and use that here instead.
@@ -1229,12 +1507,12 @@
RateStatistics acked_bitrate(250, 8000);
int64_t last_update_us = 0;
for (const auto& kv : incoming_rtp) {
- const RtpPacketType& packet = *kv.second;
- int64_t arrival_time_ms = packet.rtp.log_time_us() / 1000;
- size_t payload = packet.rtp.total_length; /*Should subtract header?*/
- clock.AdvanceTimeMicroseconds(packet.rtp.log_time_us() -
+ const LoggedRtpPacket& packet = *kv.second;
+ int64_t arrival_time_ms = packet.timestamp / 1000;
+ size_t payload = packet.total_length; /*Should subtract header?*/
+ clock.AdvanceTimeMicroseconds(packet.timestamp -
clock.TimeInMicroseconds());
- rscc.OnReceivedPacket(arrival_time_ms, payload, packet.rtp.header);
+ rscc.OnReceivedPacket(arrival_time_ms, payload, packet.header);
acked_bitrate.Update(payload, arrival_time_ms);
rtc::Optional<uint32_t> bitrate_bps = acked_bitrate.Rate(arrival_time_ms);
if (bitrate_bps) {
@@ -1260,19 +1538,23 @@
}
void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) {
- using RtpPacketType = LoggedRtpPacketOutgoing;
- using TransportFeedbackType = LoggedRtcpPacketTransportFeedback;
+ std::multimap<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
+ std::multimap<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
- // TODO(terelius): This could be provided by the parser.
- std::multimap<int64_t, const RtpPacketType*> outgoing_rtp;
- for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) {
- for (const RtpPacketType& rtp_packet : stream.outgoing_packets)
- outgoing_rtp.insert(
- std::make_pair(rtp_packet.rtp.log_time_us(), &rtp_packet));
+ for (const auto& kv : rtp_packets_) {
+ if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
+ for (const LoggedRtpPacket& rtp_packet : kv.second)
+ outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
+ }
}
- const std::vector<TransportFeedbackType>& incoming_rtcp =
- parsed_log_.transport_feedbacks(kIncomingPacket);
+ for (const auto& kv : rtcp_packets_) {
+ if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
+ for (const LoggedRtcpPacket& rtcp_packet : kv.second)
+ incoming_rtcp.insert(
+ std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
+ }
+ }
SimulatedClock clock(0);
TransportFeedbackAdapter feedback_adapter(&clock);
@@ -1294,7 +1576,7 @@
auto NextRtcpTime = [&]() {
if (rtcp_iterator != incoming_rtcp.end())
- return static_cast<int64_t>(rtcp_iterator->log_time_us());
+ return static_cast<int64_t>(rtcp_iterator->first);
return std::numeric_limits<int64_t>::max();
};
@@ -1304,34 +1586,37 @@
clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime());
- feedback_adapter.OnTransportFeedback(rtcp_iterator->transport_feedback);
- std::vector<PacketFeedback> feedback =
- feedback_adapter.GetTransportFeedbackVector();
- SortPacketFeedbackVector(&feedback);
- for (const PacketFeedback& packet : feedback) {
- float x = ToCallTime(clock.TimeInMicroseconds());
- if (packet.send_time_ms == PacketFeedback::kNoSendTime) {
- late_feedback_series.points.emplace_back(x, prev_y);
- continue;
+ const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
+ if (rtcp.type == kRtcpTransportFeedback) {
+ feedback_adapter.OnTransportFeedback(
+ *static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
+ std::vector<PacketFeedback> feedback =
+ feedback_adapter.GetTransportFeedbackVector();
+ SortPacketFeedbackVector(&feedback);
+ for (const PacketFeedback& packet : feedback) {
+ float x = ToCallTime(clock.TimeInMicroseconds());
+ if (packet.send_time_ms == PacketFeedback::kNoSendTime) {
+ late_feedback_series.points.emplace_back(x, prev_y);
+ continue;
+ }
+ int64_t y = packet.arrival_time_ms - packet.send_time_ms;
+ prev_y = y;
+ estimated_base_delay_ms = std::min(y, estimated_base_delay_ms);
+ time_series.points.emplace_back(x, y);
}
- int64_t y = packet.arrival_time_ms - packet.send_time_ms;
- prev_y = y;
- estimated_base_delay_ms = std::min(y, estimated_base_delay_ms);
- time_series.points.emplace_back(x, y);
}
++rtcp_iterator;
}
if (clock.TimeInMicroseconds() >= NextRtpTime()) {
RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime());
- const RtpPacketType& rtp_packet = *rtp_iterator->second;
- if (rtp_packet.rtp.header.extension.hasTransportSequenceNumber) {
- feedback_adapter.AddPacket(
- rtp_packet.rtp.header.ssrc,
- rtp_packet.rtp.header.extension.transportSequenceNumber,
- rtp_packet.rtp.total_length, PacedPacketInfo());
+ const LoggedRtpPacket& rtp = *rtp_iterator->second;
+ if (rtp.header.extension.hasTransportSequenceNumber) {
+ RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
+ feedback_adapter.AddPacket(rtp.header.ssrc,
+ rtp.header.extension.transportSequenceNumber,
+ rtp.total_length, PacedPacketInfo());
feedback_adapter.OnSentPacket(
- rtp_packet.rtp.header.extension.transportSequenceNumber,
- rtp_packet.rtp.log_time_us() / 1000);
+ rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
}
++rtp_iterator;
}
@@ -1352,10 +1637,40 @@
plot->SetTitle("Network Delay Change.");
}
+std::vector<std::pair<int64_t, int64_t>> EventLogAnalyzer::GetFrameTimestamps()
+ const {
+ std::vector<std::pair<int64_t, int64_t>> timestamps;
+ size_t largest_stream_size = 0;
+ const std::vector<LoggedRtpPacket>* largest_video_stream = nullptr;
+ // Find the incoming video stream with the most number of packets that is
+ // not rtx.
+ for (const auto& kv : rtp_packets_) {
+ if (kv.first.GetDirection() == kIncomingPacket &&
+ video_ssrcs_.find(kv.first) != video_ssrcs_.end() &&
+ rtx_ssrcs_.find(kv.first) == rtx_ssrcs_.end() &&
+ kv.second.size() > largest_stream_size) {
+ largest_stream_size = kv.second.size();
+ largest_video_stream = &kv.second;
+ }
+ }
+ if (largest_video_stream == nullptr) {
+ for (auto& packet : *largest_video_stream) {
+ if (packet.header.markerBit) {
+ int64_t capture_ms = packet.header.timestamp / 90.0;
+ int64_t arrival_ms = packet.timestamp / 1000.0;
+ timestamps.push_back(std::make_pair(capture_ms, arrival_ms));
+ }
+ }
+ }
+ return timestamps;
+}
+
void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) {
- for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) {
- const std::vector<LoggedRtpPacketOutgoing>& packets =
- stream.outgoing_packets;
+ for (const auto& kv : rtp_packets_) {
+ const std::vector<LoggedRtpPacket>& packets = kv.second;
+ StreamId stream_id = kv.first;
+ if (stream_id.GetDirection() == kIncomingPacket)
+ continue;
if (packets.size() < 2) {
RTC_LOG(LS_WARNING)
@@ -1363,15 +1678,11 @@
"pacer delay with less than 2 packets in the stream";
continue;
}
- int64_t end_time_us = log_segments_.empty()
- ? std::numeric_limits<int64_t>::max()
- : log_segments_.front().second;
rtc::Optional<uint32_t> estimated_frequency =
- EstimateRtpClockFrequency(packets, end_time_us);
+ EstimateRtpClockFrequency(packets);
if (!estimated_frequency)
continue;
- if (IsVideoSsrc(kOutgoingPacket, stream.ssrc) &&
- *estimated_frequency != 90000) {
+ if (IsVideoSsrc(stream_id) && *estimated_frequency != 90000) {
RTC_LOG(LS_WARNING)
<< "Video stream should use a 90 kHz clock but appears to use "
<< *estimated_frequency / 1000 << ". Discarding.";
@@ -1379,22 +1690,21 @@
}
TimeSeries pacer_delay_series(
- GetStreamName(kOutgoingPacket, stream.ssrc) + "(" +
+ GetStreamName(stream_id) + "(" +
std::to_string(*estimated_frequency / 1000) + " kHz)",
LineStyle::kLine, PointStyle::kHighlight);
SeqNumUnwrapper<uint32_t> timestamp_unwrapper;
uint64_t first_capture_timestamp =
- timestamp_unwrapper.Unwrap(packets.front().rtp.header.timestamp);
- uint64_t first_send_timestamp = packets.front().rtp.log_time_us();
- for (const auto& packet : packets) {
+ timestamp_unwrapper.Unwrap(packets.front().header.timestamp);
+ uint64_t first_send_timestamp = packets.front().timestamp;
+ for (LoggedRtpPacket packet : packets) {
double capture_time_ms = (static_cast<double>(timestamp_unwrapper.Unwrap(
- packet.rtp.header.timestamp)) -
+ packet.header.timestamp)) -
first_capture_timestamp) /
*estimated_frequency * 1000;
double send_time_ms =
- static_cast<double>(packet.rtp.log_time_us() - first_send_timestamp) /
- 1000;
- float x = ToCallTime(packet.rtp.log_time_us());
+ static_cast<double>(packet.timestamp - first_send_timestamp) / 1000;
+ float x = ToCallTime(packet.timestamp);
float y = send_time_ms - capture_time_ms;
pacer_delay_series.points.emplace_back(x, y);
}
@@ -1407,52 +1717,58 @@
"Delay from capture to send time. (First packet normalized to 0.)");
}
-void EventLogAnalyzer::CreateTimestampGraph(PacketDirection direction,
- Plot* plot) {
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- TimeSeries rtp_timestamps(
- GetStreamName(direction, stream.ssrc) + " capture-time",
- LineStyle::kLine, PointStyle::kHighlight);
- for (const auto& packet : stream.packet_view) {
- float x = ToCallTime(packet.log_time_us());
- float y = packet.header.timestamp;
- rtp_timestamps.points.emplace_back(x, y);
- }
- plot->AppendTimeSeries(std::move(rtp_timestamps));
+void EventLogAnalyzer::CreateTimestampGraph(Plot* plot) {
+ for (const auto& kv : rtp_packets_) {
+ const std::vector<LoggedRtpPacket>& rtp_packets = kv.second;
+ StreamId stream_id = kv.first;
- TimeSeries rtcp_timestamps(
- GetStreamName(direction, stream.ssrc) + " rtcp capture-time",
- LineStyle::kLine, PointStyle::kHighlight);
- // TODO(terelius): Why only sender reports?
- const auto& sender_reports = parsed_log_.sender_reports(direction);
- for (const auto& rtcp : sender_reports) {
- if (rtcp.sr.sender_ssrc() != stream.ssrc)
- continue;
- float x = ToCallTime(rtcp.log_time_us());
- float y = rtcp.sr.rtp_timestamp();
- rtcp_timestamps.points.emplace_back(x, y);
+ {
+ TimeSeries timestamp_data(GetStreamName(stream_id) + " capture-time",
+ LineStyle::kLine, PointStyle::kHighlight);
+ for (LoggedRtpPacket packet : rtp_packets) {
+ float x = ToCallTime(packet.timestamp);
+ float y = packet.header.timestamp;
+ timestamp_data.points.emplace_back(x, y);
+ }
+ plot->AppendTimeSeries(std::move(timestamp_data));
}
- plot->AppendTimeSeriesIfNotEmpty(std::move(rtcp_timestamps));
+
+ {
+ auto kv = rtcp_packets_.find(stream_id);
+ if (kv != rtcp_packets_.end()) {
+ const auto& packets = kv->second;
+ TimeSeries timestamp_data(
+ GetStreamName(stream_id) + " rtcp capture-time", LineStyle::kLine,
+ PointStyle::kHighlight);
+ for (const LoggedRtcpPacket& rtcp : packets) {
+ if (rtcp.type != kRtcpSr)
+ continue;
+ rtcp::SenderReport* sr;
+ sr = static_cast<rtcp::SenderReport*>(rtcp.packet.get());
+ float x = ToCallTime(rtcp.timestamp);
+ float y = sr->rtp_timestamp();
+ timestamp_data.points.emplace_back(x, y);
+ }
+ plot->AppendTimeSeries(std::move(timestamp_data));
+ }
+ }
}
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
- plot->SetSuggestedYAxis(0, 1, "RTP timestamp", kBottomMargin, kTopMargin);
- plot->SetTitle(GetDirectionAsString(direction) + " timestamps");
+ plot->SetSuggestedYAxis(0, 1, "Timestamp (90khz)", kBottomMargin, kTopMargin);
+ plot->SetTitle("Timestamps");
}
void EventLogAnalyzer::CreateAudioEncoderTargetBitrateGraph(Plot* plot) {
TimeSeries time_series("Audio encoder target bitrate", LineStyle::kLine,
PointStyle::kHighlight);
- auto GetAnaBitrateBps = [](const LoggedAudioNetworkAdaptationEvent& ana_event)
- -> rtc::Optional<float> {
- if (ana_event.config.bitrate_bps)
- return rtc::Optional<float>(
- static_cast<float>(*ana_event.config.bitrate_bps));
- return rtc::nullopt;
- };
- ProcessPoints<LoggedAudioNetworkAdaptationEvent>(
- GetAnaBitrateBps, parsed_log_.audio_network_adaptation_events(),
- begin_time_, &time_series);
+ ProcessPoints<AudioNetworkAdaptationEvent>(
+ [](const AudioNetworkAdaptationEvent& ana_event) -> rtc::Optional<float> {
+ if (ana_event.config.bitrate_bps)
+ return static_cast<float>(*ana_event.config.bitrate_bps);
+ return rtc::nullopt;
+ },
+ audio_network_adaptation_events_, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Bitrate (bps)", kBottomMargin, kTopMargin);
@@ -1462,16 +1778,14 @@
void EventLogAnalyzer::CreateAudioEncoderFrameLengthGraph(Plot* plot) {
TimeSeries time_series("Audio encoder frame length", LineStyle::kLine,
PointStyle::kHighlight);
- auto GetAnaFrameLengthMs =
- [](const LoggedAudioNetworkAdaptationEvent& ana_event) {
+ ProcessPoints<AudioNetworkAdaptationEvent>(
+ [](const AudioNetworkAdaptationEvent& ana_event) {
if (ana_event.config.frame_length_ms)
return rtc::Optional<float>(
static_cast<float>(*ana_event.config.frame_length_ms));
return rtc::Optional<float>();
- };
- ProcessPoints<LoggedAudioNetworkAdaptationEvent>(
- GetAnaFrameLengthMs, parsed_log_.audio_network_adaptation_events(),
- begin_time_, &time_series);
+ },
+ audio_network_adaptation_events_, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Frame length (ms)", kBottomMargin, kTopMargin);
@@ -1481,16 +1795,14 @@
void EventLogAnalyzer::CreateAudioEncoderPacketLossGraph(Plot* plot) {
TimeSeries time_series("Audio encoder uplink packet loss fraction",
LineStyle::kLine, PointStyle::kHighlight);
- auto GetAnaPacketLoss =
- [](const LoggedAudioNetworkAdaptationEvent& ana_event) {
+ ProcessPoints<AudioNetworkAdaptationEvent>(
+ [](const AudioNetworkAdaptationEvent& ana_event) {
if (ana_event.config.uplink_packet_loss_fraction)
return rtc::Optional<float>(static_cast<float>(
*ana_event.config.uplink_packet_loss_fraction));
return rtc::Optional<float>();
- };
- ProcessPoints<LoggedAudioNetworkAdaptationEvent>(
- GetAnaPacketLoss, parsed_log_.audio_network_adaptation_events(),
- begin_time_, &time_series);
+ },
+ audio_network_adaptation_events_, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 10, "Percent lost packets", kBottomMargin,
@@ -1501,16 +1813,14 @@
void EventLogAnalyzer::CreateAudioEncoderEnableFecGraph(Plot* plot) {
TimeSeries time_series("Audio encoder FEC", LineStyle::kLine,
PointStyle::kHighlight);
- auto GetAnaFecEnabled =
- [](const LoggedAudioNetworkAdaptationEvent& ana_event) {
+ ProcessPoints<AudioNetworkAdaptationEvent>(
+ [](const AudioNetworkAdaptationEvent& ana_event) {
if (ana_event.config.enable_fec)
return rtc::Optional<float>(
static_cast<float>(*ana_event.config.enable_fec));
return rtc::Optional<float>();
- };
- ProcessPoints<LoggedAudioNetworkAdaptationEvent>(
- GetAnaFecEnabled, parsed_log_.audio_network_adaptation_events(),
- begin_time_, &time_series);
+ },
+ audio_network_adaptation_events_, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "FEC (false/true)", kBottomMargin, kTopMargin);
@@ -1520,16 +1830,14 @@
void EventLogAnalyzer::CreateAudioEncoderEnableDtxGraph(Plot* plot) {
TimeSeries time_series("Audio encoder DTX", LineStyle::kLine,
PointStyle::kHighlight);
- auto GetAnaDtxEnabled =
- [](const LoggedAudioNetworkAdaptationEvent& ana_event) {
+ ProcessPoints<AudioNetworkAdaptationEvent>(
+ [](const AudioNetworkAdaptationEvent& ana_event) {
if (ana_event.config.enable_dtx)
return rtc::Optional<float>(
static_cast<float>(*ana_event.config.enable_dtx));
return rtc::Optional<float>();
- };
- ProcessPoints<LoggedAudioNetworkAdaptationEvent>(
- GetAnaDtxEnabled, parsed_log_.audio_network_adaptation_events(),
- begin_time_, &time_series);
+ },
+ audio_network_adaptation_events_, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "DTX (false/true)", kBottomMargin, kTopMargin);
@@ -1539,16 +1847,14 @@
void EventLogAnalyzer::CreateAudioEncoderNumChannelsGraph(Plot* plot) {
TimeSeries time_series("Audio encoder number of channels", LineStyle::kLine,
PointStyle::kHighlight);
- auto GetAnaNumChannels =
- [](const LoggedAudioNetworkAdaptationEvent& ana_event) {
+ ProcessPoints<AudioNetworkAdaptationEvent>(
+ [](const AudioNetworkAdaptationEvent& ana_event) {
if (ana_event.config.num_channels)
return rtc::Optional<float>(
static_cast<float>(*ana_event.config.num_channels));
return rtc::Optional<float>();
- };
- ProcessPoints<LoggedAudioNetworkAdaptationEvent>(
- GetAnaNumChannels, parsed_log_.audio_network_adaptation_events(),
- begin_time_, &time_series);
+ },
+ audio_network_adaptation_events_, begin_time_, &time_series);
plot->AppendTimeSeries(std::move(time_series));
plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
plot->SetSuggestedYAxis(0, 1, "Number of channels (1 (mono)/2 (stereo))",
@@ -1560,9 +1866,9 @@
public:
// Does not take any ownership, and all pointers must refer to valid objects
// that outlive the one constructed.
- NetEqStreamInput(const std::vector<LoggedRtpPacketIncoming>* packet_stream,
- const std::vector<int64_t>* output_events_us,
- rtc::Optional<int64_t> end_time_us)
+ NetEqStreamInput(const std::vector<LoggedRtpPacket>* packet_stream,
+ const std::vector<uint64_t>* output_events_us,
+ rtc::Optional<uint64_t> end_time_us)
: packet_stream_(*packet_stream),
packet_stream_it_(packet_stream_.begin()),
output_events_us_it_(output_events_us->begin()),
@@ -1576,11 +1882,11 @@
if (packet_stream_it_ == packet_stream_.end()) {
return rtc::nullopt;
}
- if (end_time_us_ && packet_stream_it_->rtp.log_time_us() > *end_time_us_) {
+ if (end_time_us_ && packet_stream_it_->timestamp > *end_time_us_) {
return rtc::nullopt;
}
// Convert from us to ms.
- return packet_stream_it_->rtp.log_time_us() / 1000;
+ return packet_stream_it_->timestamp / 1000;
}
rtc::Optional<int64_t> NextOutputEventTime() const override {
@@ -1599,14 +1905,14 @@
return std::unique_ptr<PacketData>();
}
std::unique_ptr<PacketData> packet_data(new PacketData());
- packet_data->header = packet_stream_it_->rtp.header;
+ packet_data->header = packet_stream_it_->header;
// Convert from us to ms.
- packet_data->time_ms = packet_stream_it_->rtp.log_time_us() / 1000.0;
+ packet_data->time_ms = packet_stream_it_->timestamp / 1000.0;
// This is a header-only "dummy" packet. Set the payload to all zeros, with
// length according to the virtual length.
- packet_data->payload.SetSize(packet_stream_it_->rtp.total_length -
- packet_stream_it_->rtp.header_length);
+ packet_data->payload.SetSize(packet_stream_it_->total_length -
+ packet_stream_it_->header_length);
std::fill_n(packet_data->payload.data(), packet_data->payload.size(), 0);
++packet_stream_it_;
@@ -1625,15 +1931,15 @@
if (packet_stream_it_ == packet_stream_.end()) {
return rtc::nullopt;
}
- return packet_stream_it_->rtp.header;
+ return packet_stream_it_->header;
}
private:
- const std::vector<LoggedRtpPacketIncoming>& packet_stream_;
- std::vector<LoggedRtpPacketIncoming>::const_iterator packet_stream_it_;
- std::vector<int64_t>::const_iterator output_events_us_it_;
- const std::vector<int64_t>::const_iterator output_events_us_end_;
- const rtc::Optional<int64_t> end_time_us_;
+ const std::vector<LoggedRtpPacket>& packet_stream_;
+ std::vector<LoggedRtpPacket>::const_iterator packet_stream_it_;
+ std::vector<uint64_t>::const_iterator output_events_us_it_;
+ const std::vector<uint64_t>::const_iterator output_events_us_end_;
+ const rtc::Optional<uint64_t> end_time_us_;
};
namespace {
@@ -1641,9 +1947,9 @@
// the test and returns the NetEqDelayAnalyzer object that was used to
// instrument the test.
std::unique_ptr<test::NetEqDelayAnalyzer> CreateNetEqTestAndRun(
- const std::vector<LoggedRtpPacketIncoming>* packet_stream,
- const std::vector<int64_t>* output_events_us,
- rtc::Optional<int64_t> end_time_us,
+ const std::vector<LoggedRtpPacket>* packet_stream,
+ const std::vector<uint64_t>* output_events_us,
+ rtc::Optional<uint64_t> end_time_us,
const std::string& replacement_file_name,
int file_sample_rate_hz) {
std::unique_ptr<test::NetEqInput> input(
@@ -1698,35 +2004,34 @@
const std::string& replacement_file_name,
int file_sample_rate_hz,
Plot* plot) {
- const std::vector<LoggedRtpPacketIncoming>* audio_packets = nullptr;
- uint32_t ssrc;
- for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) {
- if (IsAudioSsrc(kIncomingPacket, stream.ssrc)) {
- audio_packets = &stream.incoming_packets;
- ssrc = stream.ssrc;
- break;
- }
- }
- if (audio_packets == nullptr) {
+ const auto& incoming_audio_kv = std::find_if(
+ rtp_packets_.begin(), rtp_packets_.end(),
+ [this](std::pair<StreamId, std::vector<LoggedRtpPacket>> kv) {
+ return kv.first.GetDirection() == kIncomingPacket &&
+ this->IsAudioSsrc(kv.first);
+ });
+ if (incoming_audio_kv == rtp_packets_.end()) {
// No incoming audio stream found.
return;
}
- std::map<uint32_t, std::vector<int64_t>>::const_iterator output_events_it =
- parsed_log_.audio_playout_events().find(ssrc);
- if (output_events_it == parsed_log_.audio_playout_events().end()) {
+ const uint32_t ssrc = incoming_audio_kv->first.GetSsrc();
+
+ std::map<uint32_t, std::vector<uint64_t>>::const_iterator output_events_it =
+ audio_playout_events_.find(ssrc);
+ if (output_events_it == audio_playout_events_.end()) {
// Could not find output events with SSRC matching the input audio stream.
// Using the first available stream of output events.
- output_events_it = parsed_log_.audio_playout_events().cbegin();
+ output_events_it = audio_playout_events_.cbegin();
}
- rtc::Optional<int64_t> end_time_us =
+ rtc::Optional<uint64_t> end_time_us =
log_segments_.empty()
? rtc::nullopt
- : rtc::Optional<int64_t>(log_segments_.front().second);
+ : rtc::Optional<uint64_t>(log_segments_.front().second);
auto delay_cb = CreateNetEqTestAndRun(
- audio_packets, &output_events_it->second, end_time_us,
+ &incoming_audio_kv->second, &output_events_it->second, end_time_us,
replacement_file_name, file_sample_rate_hz);
std::vector<float> send_times_s;
@@ -1742,27 +2047,28 @@
RTC_DCHECK_EQ(send_times_s.size(), playout_delay_ms.size());
RTC_DCHECK_EQ(send_times_s.size(), target_delay_ms.size());
- std::map<uint32_t, TimeSeries> time_series_packet_arrival;
- std::map<uint32_t, TimeSeries> time_series_relative_packet_arrival;
- std::map<uint32_t, TimeSeries> time_series_play_time;
- std::map<uint32_t, TimeSeries> time_series_target_time;
+ std::map<StreamId, TimeSeries> time_series_packet_arrival;
+ std::map<StreamId, TimeSeries> time_series_relative_packet_arrival;
+ std::map<StreamId, TimeSeries> time_series_play_time;
+ std::map<StreamId, TimeSeries> time_series_target_time;
float min_y_axis = 0.f;
float max_y_axis = 0.f;
+ const StreamId stream_id = incoming_audio_kv->first;
for (size_t i = 0; i < send_times_s.size(); ++i) {
- time_series_packet_arrival[ssrc].points.emplace_back(
+ time_series_packet_arrival[stream_id].points.emplace_back(
TimeSeriesPoint(send_times_s[i], arrival_delay_ms[i]));
- time_series_relative_packet_arrival[ssrc].points.emplace_back(
+ time_series_relative_packet_arrival[stream_id].points.emplace_back(
TimeSeriesPoint(send_times_s[i], corrected_arrival_delay_ms[i]));
min_y_axis = std::min(min_y_axis, corrected_arrival_delay_ms[i]);
max_y_axis = std::max(max_y_axis, corrected_arrival_delay_ms[i]);
if (playout_delay_ms[i]) {
- time_series_play_time[ssrc].points.emplace_back(
+ time_series_play_time[stream_id].points.emplace_back(
TimeSeriesPoint(send_times_s[i], *playout_delay_ms[i]));
min_y_axis = std::min(min_y_axis, *playout_delay_ms[i]);
max_y_axis = std::max(max_y_axis, *playout_delay_ms[i]);
}
if (target_delay_ms[i]) {
- time_series_target_time[ssrc].points.emplace_back(
+ time_series_target_time[stream_id].points.emplace_back(
TimeSeriesPoint(send_times_s[i], *target_delay_ms[i]));
min_y_axis = std::min(min_y_axis, *target_delay_ms[i]);
max_y_axis = std::max(max_y_axis, *target_delay_ms[i]);
@@ -1800,7 +2106,7 @@
void EventLogAnalyzer::CreateIceCandidatePairConfigGraph(Plot* plot) {
std::map<uint32_t, TimeSeries> configs_by_cp_id;
- for (const auto& config : parsed_log_.ice_candidate_pair_configs()) {
+ for (const auto& config : ice_candidate_pair_configs_) {
if (configs_by_cp_id.find(config.candidate_pair_id) ==
configs_by_cp_id.end()) {
const std::string candidate_pair_desc =
@@ -1812,7 +2118,7 @@
candidate_pair_desc_by_id_[config.candidate_pair_id] =
candidate_pair_desc;
}
- float x = ToCallTime(config.log_time_us());
+ float x = ToCallTime(config.timestamp);
float y = static_cast<float>(config.type);
configs_by_cp_id[config.candidate_pair_id].points.emplace_back(x, y);
}
@@ -1836,7 +2142,7 @@
candidate_pair_desc_by_id_.end()) {
return candidate_pair_desc_by_id_[candidate_pair_id];
}
- for (const auto& config : parsed_log_.ice_candidate_pair_configs()) {
+ for (const auto& config : ice_candidate_pair_configs_) {
// TODO(qingsi): Add the handling of the "Updated" config event after the
// visualization of property change for candidate pairs is introduced.
if (candidate_pair_desc_by_id_.find(config.candidate_pair_id) ==
@@ -1852,7 +2158,7 @@
void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) {
std::map<uint32_t, TimeSeries> checks_by_cp_id;
- for (const auto& event : parsed_log_.ice_candidate_pair_events()) {
+ for (const auto& event : ice_candidate_pair_events_) {
if (checks_by_cp_id.find(event.candidate_pair_id) ==
checks_by_cp_id.end()) {
checks_by_cp_id[event.candidate_pair_id] = TimeSeries(
@@ -1860,7 +2166,7 @@
GetCandidatePairLogDescriptionFromId(event.candidate_pair_id),
LineStyle::kNone, PointStyle::kHighlight);
}
- float x = ToCallTime(event.log_time_us());
+ float x = ToCallTime(event.timestamp);
float y = static_cast<float>(event.type);
checks_by_cp_id[event.candidate_pair_id].points.emplace_back(x, y);
}
@@ -1876,176 +2182,163 @@
plot->SetTitle("[IceEventLog] ICE connectivity checks");
}
+void EventLogAnalyzer::Notification(
+ std::unique_ptr<TriageNotification> notification) {
+ notifications_.push_back(std::move(notification));
+}
+
void EventLogAnalyzer::PrintNotifications(FILE* file) {
+ if (notifications_.size() == 0)
+ return;
fprintf(file, "========== TRIAGE NOTIFICATIONS ==========\n");
- for (const auto& alert : incoming_rtp_recv_time_gaps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : incoming_rtcp_recv_time_gaps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : outgoing_rtp_send_time_gaps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : outgoing_rtcp_send_time_gaps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : incoming_seq_num_jumps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : incoming_capture_time_jumps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : outgoing_seq_num_jumps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : outgoing_capture_time_jumps_) {
- fprintf(file, "%3.3lf s : %s\n", alert.Time(), alert.ToString().c_str());
- }
- for (const auto& alert : outgoing_high_loss_alerts_) {
- fprintf(file, " : %s\n", alert.ToString().c_str());
+ for (const auto& notification : notifications_) {
+ rtc::Optional<float> call_timestamp = notification->Time();
+ if (call_timestamp.has_value()) {
+ fprintf(file, "%3.3lf s : %s\n", call_timestamp.value(),
+ notification->ToString().c_str());
+ } else {
+ fprintf(file, " : %s\n", notification->ToString().c_str());
+ }
}
fprintf(file, "========== END TRIAGE NOTIFICATIONS ==========\n");
}
-void EventLogAnalyzer::CreateStreamGapAlerts(PacketDirection direction) {
- // With 100 packets/s (~800kbps), false positives would require 10 s without
- // data.
- constexpr int64_t kMaxSeqNumJump = 1000;
- // With a 90 kHz clock, false positives would require 10 s without data.
- constexpr int64_t kMaxCaptureTimeJump = 900000;
-
- int64_t end_time_us = log_segments_.empty()
- ? std::numeric_limits<int64_t>::max()
- : log_segments_.front().second;
-
- SeqNumUnwrapper<uint16_t> seq_num_unwrapper;
- rtc::Optional<int64_t> last_seq_num;
- SeqNumUnwrapper<uint32_t> capture_time_unwrapper;
- rtc::Optional<int64_t> last_capture_time;
- // Check for gaps in sequence numbers and capture timestamps.
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- for (const auto& packet : stream.packet_view) {
- if (packet.log_time_us() > end_time_us) {
- // Only process the first (LOG_START, LOG_END) segment.
- break;
- }
-
- int64_t seq_num = seq_num_unwrapper.Unwrap(packet.header.sequenceNumber);
- if (last_seq_num.has_value() &&
- std::abs(seq_num - last_seq_num.value()) > kMaxSeqNumJump) {
- Alert_SeqNumJump(direction, ToCallTime(packet.log_time_us()),
- packet.header.ssrc);
- }
- last_seq_num.emplace(seq_num);
-
- int64_t capture_time =
- capture_time_unwrapper.Unwrap(packet.header.timestamp);
- if (last_capture_time.has_value() &&
- std::abs(capture_time - last_capture_time.value()) >
- kMaxCaptureTimeJump) {
- Alert_CaptureTimeJump(direction, ToCallTime(packet.log_time_us()),
- packet.header.ssrc);
- }
- last_capture_time.emplace(capture_time);
- }
- }
-}
-
-void EventLogAnalyzer::CreateTransmissionGapAlerts(PacketDirection direction) {
- constexpr int64_t kMaxRtpTransmissionGap = 500000;
- constexpr int64_t kMaxRtcpTransmissionGap = 2000000;
- int64_t end_time_us = log_segments_.empty()
- ? std::numeric_limits<int64_t>::max()
- : log_segments_.front().second;
-
- // TODO(terelius): The parser could provide a list of all packets, ordered
- // by time, for each direction.
- std::multimap<int64_t, const LoggedRtpPacket*> rtp_in_direction;
- for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) {
- for (const LoggedRtpPacket& rtp_packet : stream.packet_view)
- rtp_in_direction.emplace(rtp_packet.log_time_us(), &rtp_packet);
- }
- rtc::Optional<int64_t> last_rtp_time;
- for (const auto& kv : rtp_in_direction) {
- int64_t timestamp = kv.first;
- if (timestamp > end_time_us) {
- // Only process the first (LOG_START, LOG_END) segment.
- break;
- }
- int64_t duration = timestamp - last_rtp_time.value_or(0);
- if (last_rtp_time.has_value() && duration > kMaxRtpTransmissionGap) {
- // No packet sent/received for more than 500 ms.
- Alert_RtpLogTimeGap(direction, ToCallTime(timestamp), duration / 1000);
- }
- last_rtp_time.emplace(timestamp);
- }
-
- rtc::Optional<int64_t> last_rtcp_time;
- if (direction == kIncomingPacket) {
- for (const auto& rtcp : parsed_log_.incoming_rtcp_packets()) {
- if (rtcp.log_time_us() > end_time_us) {
- // Only process the first (LOG_START, LOG_END) segment.
- break;
- }
- int64_t duration = rtcp.log_time_us() - last_rtcp_time.value_or(0);
- if (last_rtcp_time.has_value() && duration > kMaxRtcpTransmissionGap) {
- // No feedback sent/received for more than 2000 ms.
- Alert_RtcpLogTimeGap(direction, ToCallTime(rtcp.log_time_us()),
- duration / 1000);
- }
- last_rtcp_time.emplace(rtcp.log_time_us());
- }
- } else {
- for (const auto& rtcp : parsed_log_.outgoing_rtcp_packets()) {
- if (rtcp.log_time_us() > end_time_us) {
- // Only process the first (LOG_START, LOG_END) segment.
- break;
- }
- int64_t duration = rtcp.log_time_us() - last_rtcp_time.value_or(0);
- if (last_rtcp_time.has_value() && duration > kMaxRtcpTransmissionGap) {
- // No feedback sent/received for more than 2000 ms.
- Alert_RtcpLogTimeGap(direction, ToCallTime(rtcp.log_time_us()),
- duration / 1000);
- }
- last_rtcp_time.emplace(rtcp.log_time_us());
- }
- }
-}
-
// TODO(terelius): Notifications could possibly be generated by the same code
// that produces the graphs. There is some code duplication that could be
// avoided, but that might be solved anyway when we move functionality from the
// analyzer to the parser.
void EventLogAnalyzer::CreateTriageNotifications() {
- CreateStreamGapAlerts(kIncomingPacket);
- CreateStreamGapAlerts(kOutgoingPacket);
- CreateTransmissionGapAlerts(kIncomingPacket);
- CreateTransmissionGapAlerts(kOutgoingPacket);
+ uint64_t end_time_us = log_segments_.empty()
+ ? std::numeric_limits<uint64_t>::max()
+ : log_segments_.front().second;
+ // Check for gaps in sequence numbers and capture timestamps.
+ for (auto& kv : rtp_packets_) {
+ StreamId stream_id = kv.first;
+ const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
- int64_t end_time_us = log_segments_.empty()
- ? std::numeric_limits<int64_t>::max()
- : log_segments_.front().second;
+ SeqNumUnwrapper<uint16_t> seq_no_unwrapper;
+ rtc::Optional<int64_t> last_seq_no;
+ SeqNumUnwrapper<uint32_t> timestamp_unwrapper;
+ rtc::Optional<int64_t> last_timestamp;
+ for (const auto& packet : packet_stream) {
+ if (packet.timestamp > end_time_us) {
+ // Only process the first (LOG_START, LOG_END) segment.
+ break;
+ }
+ int64_t seq_no = seq_no_unwrapper.Unwrap(packet.header.sequenceNumber);
+ if (last_seq_no.has_value() &&
+ std::abs(seq_no - last_seq_no.value()) > 1000) {
+ // With roughly 100 packets per second (~800kbps), this would require 10
+ // seconds without data to trigger incorrectly.
+ if (stream_id.GetDirection() == kIncomingPacket) {
+ Notification(rtc::MakeUnique<IncomingSeqNoJump>(
+ ToCallTime(packet.timestamp), packet.header.ssrc));
+ } else {
+ Notification(rtc::MakeUnique<OutgoingSeqNoJump>(
+ ToCallTime(packet.timestamp), packet.header.ssrc));
+ }
+ }
+ last_seq_no.emplace(seq_no);
+ int64_t timestamp = timestamp_unwrapper.Unwrap(packet.header.timestamp);
+ if (last_timestamp.has_value() &&
+ std::abs(timestamp - last_timestamp.value()) > 900000) {
+ // With a 90 kHz clock, this would require 10 seconds without data to
+ // trigger incorrectly.
+ if (stream_id.GetDirection() == kIncomingPacket) {
+ Notification(rtc::MakeUnique<IncomingCaptureTimeJump>(
+ ToCallTime(packet.timestamp), packet.header.ssrc));
+ } else {
+ Notification(rtc::MakeUnique<OutgoingCaptureTimeJump>(
+ ToCallTime(packet.timestamp), packet.header.ssrc));
+ }
+ }
+ last_timestamp.emplace(timestamp);
+ }
+ }
- constexpr double kMaxLossFraction = 0.05;
+ // Check for gaps in RTP and RTCP streams
+ for (const auto direction :
+ {PacketDirection::kIncomingPacket, PacketDirection::kOutgoingPacket}) {
+ // TODO(terelius): The parser could provide a list of all packets, ordered
+ // by time, for each direction.
+ std::multimap<uint64_t, const LoggedRtpPacket*> rtp_in_direction;
+ for (const auto& kv : rtp_packets_) {
+ if (kv.first.GetDirection() == direction) {
+ for (const LoggedRtpPacket& rtp_packet : kv.second)
+ rtp_in_direction.emplace(rtp_packet.timestamp, &rtp_packet);
+ }
+ }
+ rtc::Optional<uint64_t> last_rtp_packet;
+ for (const auto& kv : rtp_in_direction) {
+ uint64_t timestamp = kv.first;
+ if (timestamp > end_time_us) {
+ // Only process the first (LOG_START, LOG_END) segment.
+ break;
+ }
+ int64_t duration = timestamp - last_rtp_packet.value_or(0);
+ if (last_rtp_packet.has_value() && duration > 500000) {
+ // No incoming packet for more than 500 ms.
+ if (direction == kIncomingPacket) {
+ Notification(rtc::MakeUnique<IncomingRtpReceiveTimeGap>(
+ ToCallTime(timestamp), duration / 1000));
+ } else {
+ Notification(rtc::MakeUnique<OutgoingRtpSendTimeGap>(
+ ToCallTime(timestamp), duration / 1000));
+ }
+ }
+ last_rtp_packet.emplace(timestamp);
+ }
+
+ // TODO(terelius): The parser could provide a list of all packets, ordered
+ // by time, for each direction.
+ std::multimap<uint64_t, const LoggedRtcpPacket*> rtcp_in_direction;
+ for (const auto& kv : rtcp_packets_) {
+ if (kv.first.GetDirection() == direction) {
+ for (const LoggedRtcpPacket& rtcp_packet : kv.second)
+ rtcp_in_direction.emplace(rtcp_packet.timestamp, &rtcp_packet);
+ }
+ }
+ rtc::Optional<uint64_t> last_incoming_rtcp_packet;
+ for (const auto& kv : rtcp_in_direction) {
+ uint64_t timestamp = kv.first;
+ if (timestamp > end_time_us) {
+ // Only process the first (LOG_START, LOG_END) segment.
+ break;
+ }
+ int64_t duration = timestamp - last_incoming_rtcp_packet.value_or(0);
+ if (last_incoming_rtcp_packet.has_value() && duration > 2000000) {
+ // No incoming feedback for more than 2000 ms.
+ if (direction == kIncomingPacket) {
+ Notification(rtc::MakeUnique<IncomingRtcpReceiveTimeGap>(
+ ToCallTime(timestamp), duration / 1000));
+ } else {
+ Notification(rtc::MakeUnique<OutgoingRtcpSendTimeGap>(
+ ToCallTime(timestamp), duration / 1000));
+ }
+ }
+ last_incoming_rtcp_packet.emplace(timestamp);
+ }
+ }
+
// Loss feedback
int64_t total_lost_packets = 0;
int64_t total_expected_packets = 0;
- for (auto& bwe_update : parsed_log_.bwe_loss_updates()) {
- if (bwe_update.log_time_us() > end_time_us) {
+ for (auto& bwe_update : bwe_loss_updates_) {
+ if (bwe_update.timestamp > end_time_us) {
// Only process the first (LOG_START, LOG_END) segment.
break;
}
- int64_t lost_packets = static_cast<double>(bwe_update.fraction_lost) / 255 *
+ int64_t lost_packets = static_cast<double>(bwe_update.fraction_loss) / 255 *
bwe_update.expected_packets;
total_lost_packets += lost_packets;
total_expected_packets += bwe_update.expected_packets;
}
double avg_outgoing_loss =
static_cast<double>(total_lost_packets) / total_expected_packets;
- if (avg_outgoing_loss > kMaxLossFraction) {
- Alert_OutgoingHighLoss(avg_outgoing_loss);
+ if (avg_outgoing_loss > 0.05) {
+ Notification(rtc::MakeUnique<OutgoingHighLoss>(avg_outgoing_loss));
}
}
+} // namespace plotting
} // namespace webrtc
diff --git a/rtc_tools/event_log_visualizer/analyzer.h b/rtc_tools/event_log_visualizer/analyzer.h
index b37de21..a8fedb8 100644
--- a/rtc_tools/event_log_visualizer/analyzer.h
+++ b/rtc_tools/event_log_visualizer/analyzer.h
@@ -18,12 +18,54 @@
#include <utility>
#include <vector>
-#include "logging/rtc_event_log/rtc_event_log_parser2.h"
-#include "rtc_base/strings/string_builder.h"
+#include "logging/rtc_event_log/rtc_event_log_parser.h"
+#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+#include "rtc_base/function_view.h"
#include "rtc_tools/event_log_visualizer/plot_base.h"
#include "rtc_tools/event_log_visualizer/triage_notifications.h"
namespace webrtc {
+namespace plotting {
+
+struct LoggedRtpPacket {
+ LoggedRtpPacket(uint64_t timestamp,
+ RTPHeader header,
+ size_t header_length,
+ size_t total_length)
+ : timestamp(timestamp),
+ header(header),
+ header_length(header_length),
+ total_length(total_length) {}
+ uint64_t timestamp;
+ // TODO(terelius): This allocates space for 15 CSRCs even if none are used.
+ RTPHeader header;
+ size_t header_length;
+ size_t total_length;
+};
+
+struct LoggedRtcpPacket {
+ LoggedRtcpPacket(uint64_t timestamp,
+ RTCPPacketType rtcp_type,
+ std::unique_ptr<rtcp::RtcpPacket> rtcp_packet)
+ : timestamp(timestamp), type(rtcp_type), packet(std::move(rtcp_packet)) {}
+ uint64_t timestamp;
+ RTCPPacketType type;
+ std::unique_ptr<rtcp::RtcpPacket> packet;
+};
+
+struct LossBasedBweUpdate {
+ uint64_t timestamp;
+ int32_t new_bitrate;
+ uint8_t fraction_loss;
+ int32_t expected_packets;
+};
+
+struct AudioNetworkAdaptationEvent {
+ uint64_t timestamp;
+ AudioEncoderRuntimeConfig config;
+};
class EventLogAnalyzer {
public:
@@ -32,13 +74,14 @@
// modified while the EventLogAnalyzer is being used.
explicit EventLogAnalyzer(const ParsedRtcEventLog& log);
- void CreatePacketGraph(PacketDirection direction, Plot* plot);
+ void CreatePacketGraph(PacketDirection desired_direction, Plot* plot);
- void CreateAccumulatedPacketsGraph(PacketDirection direction, Plot* plot);
+ void CreateAccumulatedPacketsGraph(PacketDirection desired_direction,
+ Plot* plot);
void CreatePlayoutGraph(Plot* plot);
- void CreateAudioLevelGraph(PacketDirection direction, Plot* plot);
+ void CreateAudioLevelGraph(Plot* plot);
void CreateSequenceNumberGraph(Plot* plot);
@@ -49,20 +92,19 @@
void CreateFractionLossGraph(Plot* plot);
- void CreateTotalIncomingBitrateGraph(Plot* plot);
- void CreateTotalOutgoingBitrateGraph(Plot* plot,
- bool show_detector_state = false,
- bool show_alr_state = false);
+ void CreateTotalBitrateGraph(PacketDirection desired_direction,
+ Plot* plot,
+ bool show_detector_state = false,
+ bool show_alr_state = false);
- void CreateStreamBitrateGraph(PacketDirection direction, Plot* plot);
+ void CreateStreamBitrateGraph(PacketDirection desired_direction, Plot* plot);
void CreateSendSideBweSimulationGraph(Plot* plot);
void CreateReceiveSideBweSimulationGraph(Plot* plot);
void CreateNetworkDelayFeedbackGraph(Plot* plot);
void CreatePacerDelayGraph(Plot* plot);
-
- void CreateTimestampGraph(PacketDirection direction, Plot* plot);
+ void CreateTimestampGraph(Plot* plot);
void CreateAudioEncoderTargetBitrateGraph(Plot* plot);
void CreateAudioEncoderFrameLengthGraph(Plot* plot);
@@ -77,114 +119,55 @@
void CreateIceCandidatePairConfigGraph(Plot* plot);
void CreateIceConnectivityCheckGraph(Plot* plot);
+ // Returns a vector of capture and arrival timestamps for the video frames
+ // of the stream with the most number of frames.
+ std::vector<std::pair<int64_t, int64_t>> GetFrameTimestamps() const;
+
void CreateTriageNotifications();
void PrintNotifications(FILE* file);
private:
- bool IsRtxSsrc(PacketDirection direction, uint32_t ssrc) const {
- if (direction == kIncomingPacket) {
- return parsed_log_.incoming_rtx_ssrcs().find(ssrc) !=
- parsed_log_.incoming_rtx_ssrcs().end();
- } else {
- return parsed_log_.outgoing_rtx_ssrcs().find(ssrc) !=
- parsed_log_.outgoing_rtx_ssrcs().end();
+ class StreamId {
+ public:
+ StreamId(uint32_t ssrc, webrtc::PacketDirection direction)
+ : ssrc_(ssrc), direction_(direction) {}
+ bool operator<(const StreamId& other) const {
+ return std::tie(ssrc_, direction_) <
+ std::tie(other.ssrc_, other.direction_);
}
- }
-
- bool IsVideoSsrc(PacketDirection direction, uint32_t ssrc) const {
- if (direction == kIncomingPacket) {
- return parsed_log_.incoming_video_ssrcs().find(ssrc) !=
- parsed_log_.incoming_video_ssrcs().end();
- } else {
- return parsed_log_.outgoing_video_ssrcs().find(ssrc) !=
- parsed_log_.outgoing_video_ssrcs().end();
+ bool operator==(const StreamId& other) const {
+ return std::tie(ssrc_, direction_) ==
+ std::tie(other.ssrc_, other.direction_);
}
- }
+ uint32_t GetSsrc() const { return ssrc_; }
+ webrtc::PacketDirection GetDirection() const { return direction_; }
- bool IsAudioSsrc(PacketDirection direction, uint32_t ssrc) const {
- if (direction == kIncomingPacket) {
- return parsed_log_.incoming_audio_ssrcs().find(ssrc) !=
- parsed_log_.incoming_audio_ssrcs().end();
- } else {
- return parsed_log_.outgoing_audio_ssrcs().find(ssrc) !=
- parsed_log_.outgoing_audio_ssrcs().end();
- }
- }
+ private:
+ uint32_t ssrc_;
+ webrtc::PacketDirection direction_;
+ };
- template <typename IterableType>
- void CreateAccumulatedPacketsTimeSeries(Plot* plot,
- const IterableType& packets,
- const std::string& label);
+ template <typename T>
+ void CreateAccumulatedPacketsTimeSeries(
+ PacketDirection desired_direction,
+ Plot* plot,
+ const std::map<StreamId, std::vector<T>>& packets,
+ const std::string& label_prefix);
- void CreateStreamGapAlerts(PacketDirection direction);
- void CreateTransmissionGapAlerts(PacketDirection direction);
+ bool IsRtxSsrc(StreamId stream_id) const;
- std::string GetStreamName(PacketDirection direction, uint32_t ssrc) const {
- char buffer[200];
- rtc::SimpleStringBuilder name(buffer);
- if (IsAudioSsrc(direction, ssrc)) {
- name << "Audio ";
- } else if (IsVideoSsrc(direction, ssrc)) {
- name << "Video ";
- } else {
- name << "Unknown ";
- }
- if (IsRtxSsrc(direction, ssrc)) {
- name << "RTX ";
- }
- if (direction == kIncomingPacket)
- name << "(In) ";
- else
- name << "(Out) ";
- name << "SSRC " << ssrc;
- return name.str();
- }
+ bool IsVideoSsrc(StreamId stream_id) const;
+
+ bool IsAudioSsrc(StreamId stream_id) const;
+
+ std::string GetStreamName(StreamId stream_id) const;
+
+ rtc::Optional<uint32_t> EstimateRtpClockFrequency(
+ const std::vector<LoggedRtpPacket>& packets) const;
float ToCallTime(int64_t timestamp) const;
- void Alert_RtpLogTimeGap(PacketDirection direction,
- float time_seconds,
- int64_t duration) {
- if (direction == kIncomingPacket) {
- incoming_rtp_recv_time_gaps_.emplace_back(time_seconds, duration);
- } else {
- outgoing_rtp_send_time_gaps_.emplace_back(time_seconds, duration);
- }
- }
-
- void Alert_RtcpLogTimeGap(PacketDirection direction,
- float time_seconds,
- int64_t duration) {
- if (direction == kIncomingPacket) {
- incoming_rtcp_recv_time_gaps_.emplace_back(time_seconds, duration);
- } else {
- outgoing_rtcp_send_time_gaps_.emplace_back(time_seconds, duration);
- }
- }
-
- void Alert_SeqNumJump(PacketDirection direction,
- float time_seconds,
- uint32_t ssrc) {
- if (direction == kIncomingPacket) {
- incoming_seq_num_jumps_.emplace_back(time_seconds, ssrc);
- } else {
- outgoing_seq_num_jumps_.emplace_back(time_seconds, ssrc);
- }
- }
-
- void Alert_CaptureTimeJump(PacketDirection direction,
- float time_seconds,
- uint32_t ssrc) {
- if (direction == kIncomingPacket) {
- incoming_capture_time_jumps_.emplace_back(time_seconds, ssrc);
- } else {
- outgoing_capture_time_jumps_.emplace_back(time_seconds, ssrc);
- }
- }
-
- void Alert_OutgoingHighLoss(double avg_loss_fraction) {
- outgoing_high_loss_alerts_.emplace_back(avg_loss_fraction);
- }
+ void Notification(std::unique_ptr<TriageNotification> notification);
std::string GetCandidatePairLogDescriptionFromId(uint32_t candidate_pair_id);
@@ -194,19 +177,50 @@
// If left empty, all SSRCs will be considered relevant.
std::vector<uint32_t> desired_ssrc_;
+ // Tracks what each stream is configured for. Note that a single SSRC can be
+ // in several sets. For example, the SSRC used for sending video over RTX
+ // will appear in both video_ssrcs_ and rtx_ssrcs_. In the unlikely case that
+ // an SSRC is reconfigured to a different media type mid-call, it will also
+ // appear in multiple sets.
+ std::set<StreamId> rtx_ssrcs_;
+ std::set<StreamId> video_ssrcs_;
+ std::set<StreamId> audio_ssrcs_;
+
+ // Maps a stream identifier consisting of ssrc and direction to the parsed
+ // RTP headers in that stream. Header extensions are parsed if the stream
+ // has been configured.
+ std::map<StreamId, std::vector<LoggedRtpPacket>> rtp_packets_;
+
+ std::map<StreamId, std::vector<LoggedRtcpPacket>> rtcp_packets_;
+
+ // Maps an SSRC to the timestamps of parsed audio playout events.
+ std::map<uint32_t, std::vector<uint64_t>> audio_playout_events_;
+
// Stores the timestamps for all log segments, in the form of associated start
// and end events.
- std::vector<std::pair<int64_t, int64_t>> log_segments_;
+ std::vector<std::pair<uint64_t, uint64_t>> log_segments_;
- std::vector<IncomingRtpReceiveTimeGap> incoming_rtp_recv_time_gaps_;
- std::vector<IncomingRtcpReceiveTimeGap> incoming_rtcp_recv_time_gaps_;
- std::vector<OutgoingRtpSendTimeGap> outgoing_rtp_send_time_gaps_;
- std::vector<OutgoingRtcpSendTimeGap> outgoing_rtcp_send_time_gaps_;
- std::vector<IncomingSeqNumJump> incoming_seq_num_jumps_;
- std::vector<IncomingCaptureTimeJump> incoming_capture_time_jumps_;
- std::vector<OutgoingSeqNoJump> outgoing_seq_num_jumps_;
- std::vector<OutgoingCaptureTimeJump> outgoing_capture_time_jumps_;
- std::vector<OutgoingHighLoss> outgoing_high_loss_alerts_;
+ // A list of all updates from the send-side loss-based bandwidth estimator.
+ std::vector<LossBasedBweUpdate> bwe_loss_updates_;
+
+ std::vector<AudioNetworkAdaptationEvent> audio_network_adaptation_events_;
+
+ std::vector<ParsedRtcEventLog::BweProbeClusterCreatedEvent>
+ bwe_probe_cluster_created_events_;
+
+ std::vector<ParsedRtcEventLog::BweProbeResultEvent> bwe_probe_result_events_;
+
+ std::vector<ParsedRtcEventLog::BweDelayBasedUpdate> bwe_delay_updates_;
+
+ std::vector<std::unique_ptr<TriageNotification>> notifications_;
+
+ std::vector<ParsedRtcEventLog::AlrStateEvent> alr_state_events_;
+
+ std::vector<ParsedRtcEventLog::IceCandidatePairConfig>
+ ice_candidate_pair_configs_;
+
+ std::vector<ParsedRtcEventLog::IceCandidatePairEvent>
+ ice_candidate_pair_events_;
std::map<uint32_t, std::string> candidate_pair_desc_by_id_;
@@ -214,17 +228,18 @@
// 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.
- int64_t window_duration_;
- int64_t step_;
+ uint64_t window_duration_;
+ uint64_t step_;
// First and last events of the log.
- int64_t begin_time_;
- int64_t end_time_;
+ uint64_t begin_time_;
+ uint64_t end_time_;
// Duration (in seconds) of log file.
float call_duration_s_;
};
+} // namespace plotting
} // namespace webrtc
#endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_ANALYZER_H_
diff --git a/rtc_tools/event_log_visualizer/main.cc b/rtc_tools/event_log_visualizer/main.cc
index 3dce290..2e7a79e 100644
--- a/rtc_tools/event_log_visualizer/main.cc
+++ b/rtc_tools/event_log_visualizer/main.cc
@@ -10,7 +10,7 @@
#include <iostream>
-#include "logging/rtc_event_log/rtc_event_log_parser2.h"
+#include "logging/rtc_event_log/rtc_event_log_parser.h"
#include "rtc_base/flags.h"
#include "rtc_tools/event_log_visualizer/analyzer.h"
#include "rtc_tools/event_log_visualizer/plot_base.h"
@@ -143,15 +143,10 @@
false,
"Show the state ALR state on the total bitrate graph");
-DEFINE_bool(parse_unconfigured_header_extensions,
- true,
- "Attempt to parse unconfigured header extensions using the default "
- "WebRTC mapping. This can give very misleading results if the "
- "application negotiates a different mapping.");
-
-DEFINE_bool(print_triage_alerts,
- false,
- "Print triage alerts, i.e. a list of potential problems.");
+DEFINE_bool(
+ print_triage_notifications,
+ false,
+ "Print triage notifications, i.e. a list of suspicious looking events.");
void SetAllPlotFlags(bool setting);
@@ -214,13 +209,7 @@
std::string filename = argv[1];
- webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions header_extensions =
- webrtc::ParsedRtcEventLog::UnconfiguredHeaderExtensions::kDontParse;
- if (FLAG_parse_unconfigured_header_extensions) {
- header_extensions = webrtc::ParsedRtcEventLog::
- UnconfiguredHeaderExtensions::kAttemptWebrtcDefaultConfig;
- }
- webrtc::ParsedRtcEventLog parsed_log(header_extensions);
+ webrtc::ParsedRtcEventLog parsed_log;
if (!parsed_log.ParseFile(filename)) {
std::cerr << "Could not parse the entire log file." << std::endl;
@@ -229,34 +218,31 @@
<< std::endl;
}
- webrtc::EventLogAnalyzer analyzer(parsed_log);
- std::unique_ptr<webrtc::PlotCollection> collection(
- new webrtc::PythonPlotCollection());
+ webrtc::plotting::EventLogAnalyzer analyzer(parsed_log);
+ std::unique_ptr<webrtc::plotting::PlotCollection> collection(
+ new webrtc::plotting::PythonPlotCollection());
if (FLAG_plot_incoming_packet_sizes) {
- analyzer.CreatePacketGraph(webrtc::kIncomingPacket,
+ analyzer.CreatePacketGraph(webrtc::PacketDirection::kIncomingPacket,
collection->AppendNewPlot());
}
if (FLAG_plot_outgoing_packet_sizes) {
- analyzer.CreatePacketGraph(webrtc::kOutgoingPacket,
+ analyzer.CreatePacketGraph(webrtc::PacketDirection::kOutgoingPacket,
collection->AppendNewPlot());
}
if (FLAG_plot_incoming_packet_count) {
- analyzer.CreateAccumulatedPacketsGraph(webrtc::kIncomingPacket,
- collection->AppendNewPlot());
+ analyzer.CreateAccumulatedPacketsGraph(
+ webrtc::PacketDirection::kIncomingPacket, collection->AppendNewPlot());
}
if (FLAG_plot_outgoing_packet_count) {
- analyzer.CreateAccumulatedPacketsGraph(webrtc::kOutgoingPacket,
- collection->AppendNewPlot());
+ analyzer.CreateAccumulatedPacketsGraph(
+ webrtc::PacketDirection::kOutgoingPacket, collection->AppendNewPlot());
}
if (FLAG_plot_audio_playout) {
analyzer.CreatePlayoutGraph(collection->AppendNewPlot());
}
if (FLAG_plot_audio_level) {
- analyzer.CreateAudioLevelGraph(webrtc::kIncomingPacket,
- collection->AppendNewPlot());
- analyzer.CreateAudioLevelGraph(webrtc::kOutgoingPacket,
- collection->AppendNewPlot());
+ analyzer.CreateAudioLevelGraph(collection->AppendNewPlot());
}
if (FLAG_plot_incoming_sequence_number_delta) {
analyzer.CreateSequenceNumberGraph(collection->AppendNewPlot());
@@ -271,19 +257,23 @@
analyzer.CreateIncomingPacketLossGraph(collection->AppendNewPlot());
}
if (FLAG_plot_incoming_bitrate) {
- analyzer.CreateTotalIncomingBitrateGraph(collection->AppendNewPlot());
+ analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kIncomingPacket,
+ collection->AppendNewPlot(),
+ FLAG_show_detector_state,
+ FLAG_show_alr_state);
}
if (FLAG_plot_outgoing_bitrate) {
- analyzer.CreateTotalOutgoingBitrateGraph(collection->AppendNewPlot(),
- FLAG_show_detector_state,
- FLAG_show_alr_state);
+ analyzer.CreateTotalBitrateGraph(webrtc::PacketDirection::kOutgoingPacket,
+ collection->AppendNewPlot(),
+ FLAG_show_detector_state,
+ FLAG_show_alr_state);
}
if (FLAG_plot_incoming_stream_bitrate) {
- analyzer.CreateStreamBitrateGraph(webrtc::kIncomingPacket,
+ analyzer.CreateStreamBitrateGraph(webrtc::PacketDirection::kIncomingPacket,
collection->AppendNewPlot());
}
if (FLAG_plot_outgoing_stream_bitrate) {
- analyzer.CreateStreamBitrateGraph(webrtc::kOutgoingPacket,
+ analyzer.CreateStreamBitrateGraph(webrtc::PacketDirection::kOutgoingPacket,
collection->AppendNewPlot());
}
if (FLAG_plot_simulated_receiveside_bwe) {
@@ -299,10 +289,7 @@
analyzer.CreateFractionLossGraph(collection->AppendNewPlot());
}
if (FLAG_plot_timestamps) {
- analyzer.CreateTimestampGraph(webrtc::kIncomingPacket,
- collection->AppendNewPlot());
- analyzer.CreateTimestampGraph(webrtc::kOutgoingPacket,
- collection->AppendNewPlot());
+ analyzer.CreateTimestampGraph(collection->AppendNewPlot());
}
if (FLAG_plot_pacer_delay) {
analyzer.CreatePacerDelayGraph(collection->AppendNewPlot());
@@ -346,7 +333,7 @@
collection->Draw();
- if (FLAG_print_triage_alerts) {
+ if (FLAG_print_triage_notifications) {
analyzer.CreateTriageNotifications();
analyzer.PrintNotifications(stderr);
}
diff --git a/rtc_tools/event_log_visualizer/plot_base.cc b/rtc_tools/event_log_visualizer/plot_base.cc
index 9a21393..7ff4ef9 100644
--- a/rtc_tools/event_log_visualizer/plot_base.cc
+++ b/rtc_tools/event_log_visualizer/plot_base.cc
@@ -15,6 +15,7 @@
#include "rtc_base/checks.h"
namespace webrtc {
+namespace plotting {
void Plot::SetXAxis(float min_value,
float max_value,
@@ -84,4 +85,5 @@
}
}
+} // namespace plotting
} // namespace webrtc
diff --git a/rtc_tools/event_log_visualizer/plot_base.h b/rtc_tools/event_log_visualizer/plot_base.h
index e73f004..700ffbf 100644
--- a/rtc_tools/event_log_visualizer/plot_base.h
+++ b/rtc_tools/event_log_visualizer/plot_base.h
@@ -16,6 +16,7 @@
#include <vector>
namespace webrtc {
+namespace plotting {
enum class LineStyle {
kNone, // No line connecting the points. Used to create scatter plots.
@@ -172,6 +173,7 @@
std::vector<std::unique_ptr<Plot> > plots_;
};
+} // namespace plotting
} // namespace webrtc
#endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_BASE_H_
diff --git a/rtc_tools/event_log_visualizer/plot_protobuf.cc b/rtc_tools/event_log_visualizer/plot_protobuf.cc
index e986a74..e5e0a8b 100644
--- a/rtc_tools/event_log_visualizer/plot_protobuf.cc
+++ b/rtc_tools/event_log_visualizer/plot_protobuf.cc
@@ -13,6 +13,7 @@
#include <memory>
namespace webrtc {
+namespace plotting {
ProtobufPlot::ProtobufPlot() {}
@@ -82,4 +83,5 @@
return plot;
}
+} // namespace plotting
} // namespace webrtc
diff --git a/rtc_tools/event_log_visualizer/plot_protobuf.h b/rtc_tools/event_log_visualizer/plot_protobuf.h
index f59d303..5c5cce1 100644
--- a/rtc_tools/event_log_visualizer/plot_protobuf.h
+++ b/rtc_tools/event_log_visualizer/plot_protobuf.h
@@ -17,6 +17,7 @@
#include "rtc_tools/event_log_visualizer/plot_base.h"
namespace webrtc {
+namespace plotting {
class ProtobufPlot final : public Plot {
public:
@@ -35,6 +36,7 @@
void ExportProtobuf(webrtc::analytics::ChartCollection* collection);
};
+} // namespace plotting
} // namespace webrtc
#endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PROTOBUF_H_
diff --git a/rtc_tools/event_log_visualizer/plot_python.cc b/rtc_tools/event_log_visualizer/plot_python.cc
index 37c4d84..8f406e2 100644
--- a/rtc_tools/event_log_visualizer/plot_python.cc
+++ b/rtc_tools/event_log_visualizer/plot_python.cc
@@ -17,6 +17,7 @@
#include "rtc_base/checks.h"
namespace webrtc {
+namespace plotting {
PythonPlot::PythonPlot() {}
@@ -179,4 +180,5 @@
return plot;
}
+} // namespace plotting
} // namespace webrtc
diff --git a/rtc_tools/event_log_visualizer/plot_python.h b/rtc_tools/event_log_visualizer/plot_python.h
index 61d17a0..2a5a66c 100644
--- a/rtc_tools/event_log_visualizer/plot_python.h
+++ b/rtc_tools/event_log_visualizer/plot_python.h
@@ -13,6 +13,7 @@
#include "rtc_tools/event_log_visualizer/plot_base.h"
namespace webrtc {
+namespace plotting {
class PythonPlot final : public Plot {
public:
@@ -29,6 +30,7 @@
Plot* AppendNewPlot() override;
};
+} // namespace plotting
} // namespace webrtc
#endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_PLOT_PYTHON_H_
diff --git a/rtc_tools/event_log_visualizer/triage_notifications.h b/rtc_tools/event_log_visualizer/triage_notifications.h
index 49e0620..641e2ae 100644
--- a/rtc_tools/event_log_visualizer/triage_notifications.h
+++ b/rtc_tools/event_log_visualizer/triage_notifications.h
@@ -14,136 +14,130 @@
#include <string>
namespace webrtc {
+namespace plotting {
-class IncomingRtpReceiveTimeGap {
+class TriageNotification {
+ public:
+ TriageNotification() : time_seconds_() {}
+ explicit TriageNotification(float time_seconds)
+ : time_seconds_(time_seconds) {}
+ virtual ~TriageNotification() = default;
+ virtual std::string ToString() = 0;
+ rtc::Optional<float> Time() { return time_seconds_; }
+
+ private:
+ rtc::Optional<float> time_seconds_;
+};
+
+class IncomingRtpReceiveTimeGap : public TriageNotification {
public:
IncomingRtpReceiveTimeGap(float time_seconds, int64_t duration)
- : time_seconds_(time_seconds), duration_(duration) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), duration_(duration) {}
+ std::string ToString() {
return std::string("No RTP packets received for ") +
std::to_string(duration_) + std::string(" ms");
}
private:
- float time_seconds_;
int64_t duration_;
};
-class IncomingRtcpReceiveTimeGap {
+class IncomingRtcpReceiveTimeGap : public TriageNotification {
public:
IncomingRtcpReceiveTimeGap(float time_seconds, int64_t duration)
- : time_seconds_(time_seconds), duration_(duration) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), duration_(duration) {}
+ std::string ToString() {
return std::string("No RTCP packets received for ") +
std::to_string(duration_) + std::string(" ms");
}
private:
- float time_seconds_;
int64_t duration_;
};
-class OutgoingRtpSendTimeGap {
+class OutgoingRtpSendTimeGap : public TriageNotification {
public:
OutgoingRtpSendTimeGap(float time_seconds, int64_t duration)
- : time_seconds_(time_seconds), duration_(duration) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), duration_(duration) {}
+ std::string ToString() {
return std::string("No RTP packets sent for ") + std::to_string(duration_) +
std::string(" ms");
}
private:
- float time_seconds_;
int64_t duration_;
};
-class OutgoingRtcpSendTimeGap {
+class OutgoingRtcpSendTimeGap : public TriageNotification {
public:
OutgoingRtcpSendTimeGap(float time_seconds, int64_t duration)
- : time_seconds_(time_seconds), duration_(duration) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), duration_(duration) {}
+ std::string ToString() {
return std::string("No RTCP packets sent for ") +
std::to_string(duration_) + std::string(" ms");
}
private:
- float time_seconds_;
int64_t duration_;
};
-class IncomingSeqNumJump {
+class IncomingSeqNoJump : public TriageNotification {
public:
- IncomingSeqNumJump(float time_seconds, uint32_t ssrc)
- : time_seconds_(time_seconds), ssrc_(ssrc) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ IncomingSeqNoJump(float time_seconds, uint32_t ssrc)
+ : TriageNotification(time_seconds), ssrc_(ssrc) {}
+ std::string ToString() {
return std::string("Sequence number jumps on incoming SSRC ") +
std::to_string(ssrc_);
}
private:
- float time_seconds_;
-
uint32_t ssrc_;
};
-class IncomingCaptureTimeJump {
+class IncomingCaptureTimeJump : public TriageNotification {
public:
IncomingCaptureTimeJump(float time_seconds, uint32_t ssrc)
- : time_seconds_(time_seconds), ssrc_(ssrc) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), ssrc_(ssrc) {}
+ std::string ToString() {
return std::string("Capture timestamp jumps on incoming SSRC ") +
std::to_string(ssrc_);
}
private:
- float time_seconds_;
-
uint32_t ssrc_;
};
-class OutgoingSeqNoJump {
+class OutgoingSeqNoJump : public TriageNotification {
public:
OutgoingSeqNoJump(float time_seconds, uint32_t ssrc)
- : time_seconds_(time_seconds), ssrc_(ssrc) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), ssrc_(ssrc) {}
+ std::string ToString() {
return std::string("Sequence number jumps on outgoing SSRC ") +
std::to_string(ssrc_);
}
private:
- float time_seconds_;
-
uint32_t ssrc_;
};
-class OutgoingCaptureTimeJump {
+class OutgoingCaptureTimeJump : public TriageNotification {
public:
OutgoingCaptureTimeJump(float time_seconds, uint32_t ssrc)
- : time_seconds_(time_seconds), ssrc_(ssrc) {}
- float Time() const { return time_seconds_; }
- std::string ToString() const {
+ : TriageNotification(time_seconds), ssrc_(ssrc) {}
+ std::string ToString() {
return std::string("Capture timestamp jumps on outgoing SSRC ") +
std::to_string(ssrc_);
}
private:
- float time_seconds_;
-
uint32_t ssrc_;
};
-class OutgoingHighLoss {
+class OutgoingHighLoss : public TriageNotification {
public:
explicit OutgoingHighLoss(double avg_loss_fraction)
: avg_loss_fraction_(avg_loss_fraction) {}
- std::string ToString() const {
+ std::string ToString() {
return std::string("High average loss (") +
std::to_string(avg_loss_fraction_ * 100) +
std::string("%) across the call.");
@@ -153,6 +147,7 @@
double avg_loss_fraction_;
};
+} // namespace plotting
} // namespace webrtc
#endif // RTC_TOOLS_EVENT_LOG_VISUALIZER_TRIAGE_NOTIFICATIONS_H_