blob: c15de6d7fbd9d59744afc3c57881185b263814d5 [file] [log] [blame]
terelius54ce6802016-07-13 06:44:41 -07001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/tools/event_log_visualizer/analyzer.h"
12
13#include <algorithm>
14#include <limits>
15#include <map>
16#include <sstream>
17#include <string>
18#include <utility>
19
20#include "webrtc/audio_receive_stream.h"
21#include "webrtc/audio_send_stream.h"
22#include "webrtc/base/checks.h"
23#include "webrtc/call.h"
24#include "webrtc/common_types.h"
Stefan Holmer2beea2a2016-07-28 17:42:15 +020025#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
terelius54ce6802016-07-13 06:44:41 -070026#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
27#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
28#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
Stefan Holmer2beea2a2016-07-28 17:42:15 +020029#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
30#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
terelius54ce6802016-07-13 06:44:41 -070031#include "webrtc/video_receive_stream.h"
32#include "webrtc/video_send_stream.h"
33
34namespace {
35
36std::string SsrcToString(uint32_t ssrc) {
37 std::stringstream ss;
38 ss << "SSRC " << ssrc;
39 return ss.str();
40}
41
42// Checks whether an SSRC is contained in the list of desired SSRCs.
43// Note that an empty SSRC list matches every SSRC.
44bool MatchingSsrc(uint32_t ssrc, const std::vector<uint32_t>& desired_ssrc) {
45 if (desired_ssrc.size() == 0)
46 return true;
47 return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) !=
48 desired_ssrc.end();
49}
50
51double AbsSendTimeToMicroseconds(int64_t abs_send_time) {
52 // The timestamp is a fixed point representation with 6 bits for seconds
53 // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the
54 // time in seconds and then multiply by 1000000 to convert to microseconds.
55 static constexpr double kTimestampToMicroSec =
56 1000000.0 / static_cast<double>(1 << 18);
57 return abs_send_time * kTimestampToMicroSec;
58}
59
60// Computes the difference |later| - |earlier| where |later| and |earlier|
61// are counters that wrap at |modulus|. The difference is chosen to have the
62// least absolute value. For example if |modulus| is 8, then the difference will
63// be chosen in the range [-3, 4]. If |modulus| is 9, then the difference will
64// be in [-4, 4].
65int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) {
66 RTC_DCHECK_LE(1, modulus);
67 RTC_DCHECK_LT(later, modulus);
68 RTC_DCHECK_LT(earlier, modulus);
69 int64_t difference =
70 static_cast<int64_t>(later) - static_cast<int64_t>(earlier);
71 int64_t max_difference = modulus / 2;
72 int64_t min_difference = max_difference - modulus + 1;
73 if (difference > max_difference) {
74 difference -= modulus;
75 }
76 if (difference < min_difference) {
77 difference += modulus;
78 }
79 return difference;
80}
81
terelius54ce6802016-07-13 06:44:41 -070082const double kXMargin = 1.02;
83const double kYMargin = 1.1;
84const double kDefaultXMin = -1;
85const double kDefaultYMin = -1;
86
87} // namespace
88
89namespace webrtc {
90namespace plotting {
91
terelius88e64e52016-07-19 01:51:06 -070092
93bool EventLogAnalyzer::StreamId::operator<(const StreamId& other) const {
94 if (ssrc_ < other.ssrc_) {
95 return true;
96 }
97 if (ssrc_ == other.ssrc_) {
Stefan Holmer2beea2a2016-07-28 17:42:15 +020098 if (direction_ < other.direction_) {
terelius88e64e52016-07-19 01:51:06 -070099 return true;
100 }
terelius88e64e52016-07-19 01:51:06 -0700101 }
102 return false;
103}
104
105bool EventLogAnalyzer::StreamId::operator==(const StreamId& other) const {
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200106 return ssrc_ == other.ssrc_ && direction_ == other.direction_;
terelius88e64e52016-07-19 01:51:06 -0700107}
108
109
terelius54ce6802016-07-13 06:44:41 -0700110EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
111 : parsed_log_(log), window_duration_(250000), step_(10000) {
112 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
113 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
terelius88e64e52016-07-19 01:51:06 -0700114
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200115 // Maps a stream identifier consisting of ssrc and direction
terelius88e64e52016-07-19 01:51:06 -0700116 // to the header extensions used by that stream,
117 std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
118
119 PacketDirection direction;
terelius88e64e52016-07-19 01:51:06 -0700120 uint8_t header[IP_PACKET_SIZE];
121 size_t header_length;
122 size_t total_length;
123
terelius54ce6802016-07-13 06:44:41 -0700124 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
125 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
terelius88e64e52016-07-19 01:51:06 -0700126 if (event_type != ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT &&
127 event_type != ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT &&
128 event_type != ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT &&
129 event_type != ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
130 uint64_t timestamp = parsed_log_.GetTimestamp(i);
131 first_timestamp = std::min(first_timestamp, timestamp);
132 last_timestamp = std::max(last_timestamp, timestamp);
133 }
134
135 switch (parsed_log_.GetEventType(i)) {
136 case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
137 VideoReceiveStream::Config config(nullptr);
138 parsed_log_.GetVideoReceiveConfig(i, &config);
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200139 StreamId stream(config.rtp.remote_ssrc, kIncomingPacket);
terelius88e64e52016-07-19 01:51:06 -0700140 extension_maps[stream].Erase();
141 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
142 const std::string& extension = config.rtp.extensions[j].uri;
143 int id = config.rtp.extensions[j].id;
144 extension_maps[stream].Register(StringToRtpExtensionType(extension),
145 id);
146 }
147 break;
148 }
149 case ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT: {
150 VideoSendStream::Config config(nullptr);
151 parsed_log_.GetVideoSendConfig(i, &config);
152 for (auto ssrc : config.rtp.ssrcs) {
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200153 StreamId stream(ssrc, kOutgoingPacket);
terelius88e64e52016-07-19 01:51:06 -0700154 extension_maps[stream].Erase();
155 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
156 const std::string& extension = config.rtp.extensions[j].uri;
157 int id = config.rtp.extensions[j].id;
158 extension_maps[stream].Register(StringToRtpExtensionType(extension),
159 id);
160 }
161 }
162 break;
163 }
164 case ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT: {
165 AudioReceiveStream::Config config;
166 // TODO(terelius): Parse the audio configs once we have them.
167 break;
168 }
169 case ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT: {
170 AudioSendStream::Config config(nullptr);
171 // TODO(terelius): Parse the audio configs once we have them.
172 break;
173 }
174 case ParsedRtcEventLog::RTP_EVENT: {
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200175 MediaType media_type;
terelius88e64e52016-07-19 01:51:06 -0700176 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
177 &header_length, &total_length);
178 // Parse header to get SSRC.
179 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
180 RTPHeader parsed_header;
181 rtp_parser.Parse(&parsed_header);
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200182 StreamId stream(parsed_header.ssrc, direction);
terelius88e64e52016-07-19 01:51:06 -0700183 // Look up the extension_map and parse it again to get the extensions.
184 if (extension_maps.count(stream) == 1) {
185 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
186 rtp_parser.Parse(&parsed_header, extension_map);
187 }
188 uint64_t timestamp = parsed_log_.GetTimestamp(i);
189 rtp_packets_[stream].push_back(
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200190 LoggedRtpPacket(timestamp, parsed_header, total_length));
terelius88e64e52016-07-19 01:51:06 -0700191 break;
192 }
193 case ParsedRtcEventLog::RTCP_EVENT: {
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200194 uint8_t packet[IP_PACKET_SIZE];
195 MediaType media_type;
196 parsed_log_.GetRtcpPacket(i, &direction, &media_type, packet,
197 &total_length);
198
199 RtpUtility::RtpHeaderParser rtp_parser(packet, total_length);
200 RTPHeader parsed_header;
201 RTC_CHECK(rtp_parser.ParseRtcp(&parsed_header));
202 uint32_t ssrc = parsed_header.ssrc;
203
204 RTCPUtility::RTCPParserV2 rtcp_parser(packet, total_length, true);
205 RTC_CHECK(rtcp_parser.IsValid());
206
207 RTCPUtility::RTCPPacketTypes packet_type = rtcp_parser.Begin();
208 while (packet_type != RTCPUtility::RTCPPacketTypes::kInvalid) {
209 switch (packet_type) {
210 case RTCPUtility::RTCPPacketTypes::kTransportFeedback: {
211 // Currently feedback is logged twice, both for audio and video.
212 // Only act on one of them.
213 if (media_type == MediaType::VIDEO) {
214 std::unique_ptr<rtcp::RtcpPacket> rtcp_packet(
215 rtcp_parser.ReleaseRtcpPacket());
216 StreamId stream(ssrc, direction);
217 uint64_t timestamp = parsed_log_.GetTimestamp(i);
218 rtcp_packets_[stream].push_back(LoggedRtcpPacket(
219 timestamp, kRtcpTransportFeedback, std::move(rtcp_packet)));
220 }
221 break;
222 }
223 default:
224 break;
225 }
226 rtcp_parser.Iterate();
227 packet_type = rtcp_parser.PacketType();
228 }
terelius88e64e52016-07-19 01:51:06 -0700229 break;
230 }
231 case ParsedRtcEventLog::LOG_START: {
232 break;
233 }
234 case ParsedRtcEventLog::LOG_END: {
235 break;
236 }
237 case ParsedRtcEventLog::BWE_PACKET_LOSS_EVENT: {
terelius8058e582016-07-25 01:32:41 -0700238 BwePacketLossEvent bwe_update;
239 bwe_update.timestamp = parsed_log_.GetTimestamp(i);
240 parsed_log_.GetBwePacketLossEvent(i, &bwe_update.new_bitrate,
241 &bwe_update.fraction_loss,
242 &bwe_update.expected_packets);
243 bwe_loss_updates_.push_back(bwe_update);
terelius88e64e52016-07-19 01:51:06 -0700244 break;
245 }
246 case ParsedRtcEventLog::BWE_PACKET_DELAY_EVENT: {
247 break;
248 }
249 case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: {
250 break;
251 }
252 case ParsedRtcEventLog::UNKNOWN_EVENT: {
253 break;
254 }
255 }
terelius54ce6802016-07-13 06:44:41 -0700256 }
terelius88e64e52016-07-19 01:51:06 -0700257
terelius54ce6802016-07-13 06:44:41 -0700258 if (last_timestamp < first_timestamp) {
259 // No useful events in the log.
260 first_timestamp = last_timestamp = 0;
261 }
262 begin_time_ = first_timestamp;
263 end_time_ = last_timestamp;
264}
265
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200266class BitrateObserver : public CongestionController::Observer,
267 public RemoteBitrateObserver {
268 public:
269 BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {}
270
271 void OnNetworkChanged(uint32_t bitrate_bps,
272 uint8_t fraction_loss,
273 int64_t rtt_ms) override {
274 last_bitrate_bps_ = bitrate_bps;
275 bitrate_updated_ = true;
276 }
277
278 void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
279 uint32_t bitrate) override {}
280
281 uint32_t last_bitrate_bps() const { return last_bitrate_bps_; }
282 bool GetAndResetBitrateUpdated() {
283 bool bitrate_updated = bitrate_updated_;
284 bitrate_updated_ = false;
285 return bitrate_updated;
286 }
287
288 private:
289 uint32_t last_bitrate_bps_;
290 bool bitrate_updated_;
291};
292
terelius54ce6802016-07-13 06:44:41 -0700293void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
294 Plot* plot) {
295 std::map<uint32_t, TimeSeries> time_series;
296
297 PacketDirection direction;
298 MediaType media_type;
299 uint8_t header[IP_PACKET_SIZE];
300 size_t header_length, total_length;
301 float max_y = 0;
302
303 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
304 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
305 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
306 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
307 &header_length, &total_length);
308 if (direction == desired_direction) {
309 // Parse header to get SSRC.
310 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
311 RTPHeader parsed_header;
312 rtp_parser.Parse(&parsed_header);
313 // Filter on SSRC.
314 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
315 uint64_t timestamp = parsed_log_.GetTimestamp(i);
316 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
317 float y = total_length;
318 max_y = std::max(max_y, y);
319 time_series[parsed_header.ssrc].points.push_back(
320 TimeSeriesPoint(x, y));
321 }
322 }
323 }
324 }
325
326 // Set labels and put in graph.
327 for (auto& kv : time_series) {
328 kv.second.label = SsrcToString(kv.first);
329 kv.second.style = BAR_GRAPH;
330 plot->series.push_back(std::move(kv.second));
331 }
332
333 plot->xaxis_min = kDefaultXMin;
334 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
335 plot->xaxis_label = "Time (s)";
336 plot->yaxis_min = kDefaultYMin;
337 plot->yaxis_max = max_y * kYMargin;
338 plot->yaxis_label = "Packet size (bytes)";
339 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
340 plot->title = "Incoming RTP packets";
341 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
342 plot->title = "Outgoing RTP packets";
343 }
344}
345
346// For each SSRC, plot the time between the consecutive playouts.
347void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
348 std::map<uint32_t, TimeSeries> time_series;
349 std::map<uint32_t, uint64_t> last_playout;
350
351 uint32_t ssrc;
352 float max_y = 0;
353
354 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
355 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
356 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
357 parsed_log_.GetAudioPlayout(i, &ssrc);
358 uint64_t timestamp = parsed_log_.GetTimestamp(i);
359 if (MatchingSsrc(ssrc, desired_ssrc_)) {
360 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
361 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
362 if (time_series[ssrc].points.size() == 0) {
363 // There were no previusly logged playout for this SSRC.
364 // Generate a point, but place it on the x-axis.
365 y = 0;
366 }
367 max_y = std::max(max_y, y);
368 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y));
369 last_playout[ssrc] = timestamp;
370 }
371 }
372 }
373
374 // Set labels and put in graph.
375 for (auto& kv : time_series) {
376 kv.second.label = SsrcToString(kv.first);
377 kv.second.style = BAR_GRAPH;
378 plot->series.push_back(std::move(kv.second));
379 }
380
381 plot->xaxis_min = kDefaultXMin;
382 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
383 plot->xaxis_label = "Time (s)";
384 plot->yaxis_min = kDefaultYMin;
385 plot->yaxis_max = max_y * kYMargin;
386 plot->yaxis_label = "Time since last playout (ms)";
387 plot->title = "Audio playout";
388}
389
390// For each SSRC, plot the time between the consecutive playouts.
391void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
392 std::map<uint32_t, TimeSeries> time_series;
393 std::map<uint32_t, uint16_t> last_seqno;
394
395 PacketDirection direction;
396 MediaType media_type;
397 uint8_t header[IP_PACKET_SIZE];
398 size_t header_length, total_length;
399
400 int max_y = 1;
401 int min_y = 0;
402
403 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
404 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
405 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
406 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
407 &header_length, &total_length);
408 uint64_t timestamp = parsed_log_.GetTimestamp(i);
409 if (direction == PacketDirection::kIncomingPacket) {
410 // Parse header to get SSRC.
411 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
412 RTPHeader parsed_header;
413 rtp_parser.Parse(&parsed_header);
414 // Filter on SSRC.
415 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
416 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
417 int y = WrappingDifference(parsed_header.sequenceNumber,
418 last_seqno[parsed_header.ssrc], 1ul << 16);
419 if (time_series[parsed_header.ssrc].points.size() == 0) {
420 // There were no previusly logged playout for this SSRC.
421 // Generate a point, but place it on the x-axis.
422 y = 0;
423 }
424 max_y = std::max(max_y, y);
425 min_y = std::min(min_y, y);
426 time_series[parsed_header.ssrc].points.push_back(
427 TimeSeriesPoint(x, y));
428 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
429 }
430 }
431 }
432 }
433
434 // Set labels and put in graph.
435 for (auto& kv : time_series) {
436 kv.second.label = SsrcToString(kv.first);
437 kv.second.style = BAR_GRAPH;
438 plot->series.push_back(std::move(kv.second));
439 }
440
441 plot->xaxis_min = kDefaultXMin;
442 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
443 plot->xaxis_label = "Time (s)";
444 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
445 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
446 plot->yaxis_label = "Difference since last packet";
447 plot->title = "Sequence number";
448}
449
450void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
terelius54ce6802016-07-13 06:44:41 -0700451 double max_y = 10;
452 double min_y = 0;
453
terelius88e64e52016-07-19 01:51:06 -0700454 for (auto& kv : rtp_packets_) {
455 StreamId stream_id = kv.first;
456 // Filter on direction and SSRC.
457 if (stream_id.GetDirection() != kIncomingPacket ||
458 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
459 continue;
460 }
terelius54ce6802016-07-13 06:44:41 -0700461
terelius88e64e52016-07-19 01:51:06 -0700462 TimeSeries time_series;
463 time_series.label = SsrcToString(stream_id.GetSsrc());
464 time_series.style = BAR_GRAPH;
465 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
466 int64_t last_abs_send_time = 0;
467 int64_t last_timestamp = 0;
468 for (const LoggedRtpPacket& packet : packet_stream) {
469 if (packet.header.extension.hasAbsoluteSendTime) {
470 int64_t send_time_diff =
471 WrappingDifference(packet.header.extension.absoluteSendTime,
472 last_abs_send_time, 1ul << 24);
473 int64_t recv_time_diff = packet.timestamp - last_timestamp;
474
475 last_abs_send_time = packet.header.extension.absoluteSendTime;
476 last_timestamp = packet.timestamp;
477
478 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
479 double y =
480 static_cast<double>(recv_time_diff -
481 AbsSendTimeToMicroseconds(send_time_diff)) /
482 1000;
483 if (time_series.points.size() == 0) {
484 // There were no previously logged packets for this SSRC.
485 // Generate a point, but place it on the x-axis.
486 y = 0;
terelius54ce6802016-07-13 06:44:41 -0700487 }
terelius88e64e52016-07-19 01:51:06 -0700488 max_y = std::max(max_y, y);
489 min_y = std::min(min_y, y);
490 time_series.points.emplace_back(x, y);
terelius54ce6802016-07-13 06:44:41 -0700491 }
492 }
terelius88e64e52016-07-19 01:51:06 -0700493 // Add the data set to the plot.
494 plot->series.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700495 }
496
497 plot->xaxis_min = kDefaultXMin;
498 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
499 plot->xaxis_label = "Time (s)";
500 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
501 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
502 plot->yaxis_label = "Latency change (ms)";
503 plot->title = "Network latency change between consecutive packets";
504}
505
506void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
terelius54ce6802016-07-13 06:44:41 -0700507 double max_y = 10;
508 double min_y = 0;
509
terelius88e64e52016-07-19 01:51:06 -0700510 for (auto& kv : rtp_packets_) {
511 StreamId stream_id = kv.first;
512 // Filter on direction and SSRC.
513 if (stream_id.GetDirection() != kIncomingPacket ||
514 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
515 continue;
516 }
517 TimeSeries time_series;
518 time_series.label = SsrcToString(stream_id.GetSsrc());
519 time_series.style = LINE_GRAPH;
520 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
521 int64_t last_abs_send_time = 0;
522 int64_t last_timestamp = 0;
523 double accumulated_delay_ms = 0;
524 for (const LoggedRtpPacket& packet : packet_stream) {
525 if (packet.header.extension.hasAbsoluteSendTime) {
526 int64_t send_time_diff =
527 WrappingDifference(packet.header.extension.absoluteSendTime,
528 last_abs_send_time, 1ul << 24);
529 int64_t recv_time_diff = packet.timestamp - last_timestamp;
terelius54ce6802016-07-13 06:44:41 -0700530
terelius88e64e52016-07-19 01:51:06 -0700531 last_abs_send_time = packet.header.extension.absoluteSendTime;
532 last_timestamp = packet.timestamp;
533
534 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
535 accumulated_delay_ms +=
536 static_cast<double>(recv_time_diff -
537 AbsSendTimeToMicroseconds(send_time_diff)) /
538 1000;
539 if (time_series.points.size() == 0) {
540 // There were no previously logged packets for this SSRC.
541 // Generate a point, but place it on the x-axis.
542 accumulated_delay_ms = 0;
terelius54ce6802016-07-13 06:44:41 -0700543 }
terelius88e64e52016-07-19 01:51:06 -0700544 max_y = std::max(max_y, accumulated_delay_ms);
545 min_y = std::min(min_y, accumulated_delay_ms);
546 time_series.points.emplace_back(x, accumulated_delay_ms);
terelius54ce6802016-07-13 06:44:41 -0700547 }
548 }
terelius88e64e52016-07-19 01:51:06 -0700549 // Add the data set to the plot.
550 plot->series.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700551 }
552
553 plot->xaxis_min = kDefaultXMin;
554 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
555 plot->xaxis_label = "Time (s)";
556 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
557 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
558 plot->yaxis_label = "Latency change (ms)";
559 plot->title = "Accumulated network latency change";
560}
561
562// Plot the total bandwidth used by all RTP streams.
563void EventLogAnalyzer::CreateTotalBitrateGraph(
564 PacketDirection desired_direction,
565 Plot* plot) {
566 struct TimestampSize {
567 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
568 uint64_t timestamp;
569 size_t size;
570 };
571 std::vector<TimestampSize> packets;
572
573 PacketDirection direction;
574 size_t total_length;
575
576 // Extract timestamps and sizes for the relevant packets.
577 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
578 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
579 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
580 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
581 &total_length);
582 if (direction == desired_direction) {
583 uint64_t timestamp = parsed_log_.GetTimestamp(i);
584 packets.push_back(TimestampSize(timestamp, total_length));
585 }
586 }
587 }
588
589 size_t window_index_begin = 0;
590 size_t window_index_end = 0;
591 size_t bytes_in_window = 0;
592 float max_y = 0;
593
594 // Calculate a moving average of the bitrate and store in a TimeSeries.
595 plot->series.push_back(TimeSeries());
596 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
597 while (window_index_end < packets.size() &&
598 packets[window_index_end].timestamp < time) {
599 bytes_in_window += packets[window_index_end].size;
600 window_index_end++;
601 }
602 while (window_index_begin < packets.size() &&
603 packets[window_index_begin].timestamp < time - window_duration_) {
604 RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window);
605 bytes_in_window -= packets[window_index_begin].size;
606 window_index_begin++;
607 }
608 float window_duration_in_seconds =
609 static_cast<float>(window_duration_) / 1000000;
610 float x = static_cast<float>(time - begin_time_) / 1000000;
611 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
612 max_y = std::max(max_y, y);
613 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
614 }
615
616 // Set labels.
617 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
618 plot->series.back().label = "Incoming bitrate";
619 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
620 plot->series.back().label = "Outgoing bitrate";
621 }
622 plot->series.back().style = LINE_GRAPH;
623
terelius8058e582016-07-25 01:32:41 -0700624 // Overlay the send-side bandwidth estimate over the outgoing bitrate.
625 if (desired_direction == kOutgoingPacket) {
626 plot->series.push_back(TimeSeries());
627 for (auto& bwe_update : bwe_loss_updates_) {
628 float x =
629 static_cast<float>(bwe_update.timestamp - begin_time_) / 1000000;
630 float y = static_cast<float>(bwe_update.new_bitrate) / 1000;
631 max_y = std::max(max_y, y);
632 plot->series.back().points.emplace_back(x, y);
633 }
634 plot->series.back().label = "Loss-based estimate";
635 plot->series.back().style = LINE_GRAPH;
636 }
637
terelius54ce6802016-07-13 06:44:41 -0700638 plot->xaxis_min = kDefaultXMin;
639 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
640 plot->xaxis_label = "Time (s)";
641 plot->yaxis_min = kDefaultYMin;
642 plot->yaxis_max = max_y * kYMargin;
643 plot->yaxis_label = "Bitrate (kbps)";
644 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
645 plot->title = "Incoming RTP bitrate";
646 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
647 plot->title = "Outgoing RTP bitrate";
648 }
649}
650
651// For each SSRC, plot the bandwidth used by that stream.
652void EventLogAnalyzer::CreateStreamBitrateGraph(
653 PacketDirection desired_direction,
654 Plot* plot) {
655 struct TimestampSize {
656 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
657 uint64_t timestamp;
658 size_t size;
659 };
terelius88e64e52016-07-19 01:51:06 -0700660 std::map<uint32_t, std::vector<TimestampSize>> packets;
terelius54ce6802016-07-13 06:44:41 -0700661
662 PacketDirection direction;
663 MediaType media_type;
664 uint8_t header[IP_PACKET_SIZE];
665 size_t header_length, total_length;
666
667 // Extract timestamps and sizes for the relevant packets.
668 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
669 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
670 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
671 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
672 &header_length, &total_length);
673 if (direction == desired_direction) {
674 // Parse header to get SSRC.
675 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
676 RTPHeader parsed_header;
677 rtp_parser.Parse(&parsed_header);
678 // Filter on SSRC.
679 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
680 uint64_t timestamp = parsed_log_.GetTimestamp(i);
681 packets[parsed_header.ssrc].push_back(
682 TimestampSize(timestamp, total_length));
683 }
684 }
685 }
686 }
687
688 float max_y = 0;
689
690 for (auto& kv : packets) {
691 size_t window_index_begin = 0;
692 size_t window_index_end = 0;
693 size_t bytes_in_window = 0;
694
695 // Calculate a moving average of the bitrate and store in a TimeSeries.
696 plot->series.push_back(TimeSeries());
697 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
698 while (window_index_end < kv.second.size() &&
699 kv.second[window_index_end].timestamp < time) {
700 bytes_in_window += kv.second[window_index_end].size;
701 window_index_end++;
702 }
703 while (window_index_begin < kv.second.size() &&
704 kv.second[window_index_begin].timestamp <
705 time - window_duration_) {
706 RTC_DCHECK_LE(kv.second[window_index_begin].size, bytes_in_window);
707 bytes_in_window -= kv.second[window_index_begin].size;
708 window_index_begin++;
709 }
710 float window_duration_in_seconds =
711 static_cast<float>(window_duration_) / 1000000;
712 float x = static_cast<float>(time - begin_time_) / 1000000;
713 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
714 max_y = std::max(max_y, y);
715 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
716 }
717
718 // Set labels.
719 plot->series.back().label = SsrcToString(kv.first);
720 plot->series.back().style = LINE_GRAPH;
721 }
722
723 plot->xaxis_min = kDefaultXMin;
724 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
725 plot->xaxis_label = "Time (s)";
726 plot->yaxis_min = kDefaultYMin;
727 plot->yaxis_max = max_y * kYMargin;
728 plot->yaxis_label = "Bitrate (kbps)";
729 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
730 plot->title = "Incoming bitrate per stream";
731 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
732 plot->title = "Outgoing bitrate per stream";
733 }
734}
735
Stefan Holmer2beea2a2016-07-28 17:42:15 +0200736void EventLogAnalyzer::CreateBweGraph(Plot* plot) {
737 std::map<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
738 std::map<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
739
740 for (const auto& kv : rtp_packets_) {
741 if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
742 for (const LoggedRtpPacket& rtp_packet : kv.second)
743 outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
744 }
745 }
746
747 for (const auto& kv : rtcp_packets_) {
748 if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
749 for (const LoggedRtcpPacket& rtcp_packet : kv.second)
750 incoming_rtcp.insert(
751 std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
752 }
753 }
754
755 SimulatedClock clock(0);
756 BitrateObserver observer;
757 RtcEventLogNullImpl null_event_log;
758 CongestionController cc(&clock, &observer, &observer, &null_event_log);
759 // TODO(holmer): Log the call config and use that here instead.
760 static const uint32_t kDefaultStartBitrateBps = 300000;
761 cc.SetBweBitrates(0, kDefaultStartBitrateBps, -1);
762
763 TimeSeries time_series;
764 time_series.label = "BWE";
765 time_series.style = LINE_DOT_GRAPH;
766 uint32_t max_y = 10;
767 uint32_t min_y = 0;
768
769 auto rtp_iterator = outgoing_rtp.begin();
770 auto rtcp_iterator = incoming_rtcp.begin();
771
772 auto NextRtpTime = [&]() {
773 if (rtp_iterator != outgoing_rtp.end())
774 return static_cast<int64_t>(rtp_iterator->first);
775 return std::numeric_limits<int64_t>::max();
776 };
777
778 auto NextRtcpTime = [&]() {
779 if (rtcp_iterator != incoming_rtcp.end())
780 return static_cast<int64_t>(rtcp_iterator->first);
781 return std::numeric_limits<int64_t>::max();
782 };
783
784 auto NextProcessTime = [&]() {
785 if (rtcp_iterator != incoming_rtcp.end() ||
786 rtp_iterator != outgoing_rtp.end()) {
787 return clock.TimeInMicroseconds() +
788 std::max<int64_t>(cc.TimeUntilNextProcess() * 1000, 0);
789 }
790 return std::numeric_limits<int64_t>::max();
791 };
792
793 int64_t time_us = std::min(NextRtpTime(), NextRtcpTime());
794 while (time_us != std::numeric_limits<int64_t>::max()) {
795 clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
796 if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
797 clock.AdvanceTimeMilliseconds(rtcp_iterator->first / 1000 -
798 clock.TimeInMilliseconds());
799 const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
800 if (rtcp.type == kRtcpTransportFeedback) {
801 cc.GetTransportFeedbackObserver()->OnTransportFeedback(
802 *static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
803 }
804 ++rtcp_iterator;
805 }
806 if (clock.TimeInMicroseconds() >= NextRtpTime()) {
807 clock.AdvanceTimeMilliseconds(rtp_iterator->first / 1000 -
808 clock.TimeInMilliseconds());
809 const LoggedRtpPacket& rtp = *rtp_iterator->second;
810 if (rtp.header.extension.hasTransportSequenceNumber) {
811 RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
812 cc.GetTransportFeedbackObserver()->AddPacket(
813 rtp.header.extension.transportSequenceNumber, rtp.total_length, 0);
814 rtc::SentPacket sent_packet(
815 rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
816 cc.OnSentPacket(sent_packet);
817 }
818 ++rtp_iterator;
819 }
820 if (clock.TimeInMicroseconds() >= NextProcessTime())
821 cc.Process();
822 if (observer.GetAndResetBitrateUpdated()) {
823 uint32_t y = observer.last_bitrate_bps() / 1000;
824 max_y = std::max(max_y, y);
825 min_y = std::min(min_y, y);
826 float x = static_cast<float>(clock.TimeInMicroseconds() - begin_time_) /
827 1000000;
828 time_series.points.emplace_back(x, y);
829 }
830 time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()});
831 }
832 // Add the data set to the plot.
833 plot->series.push_back(std::move(time_series));
834
835 plot->xaxis_min = kDefaultXMin;
836 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
837 plot->xaxis_label = "Time (s)";
838 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
839 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
840 plot->yaxis_label = "Bitrate (kbps)";
841 plot->title = "BWE";
842}
843
terelius54ce6802016-07-13 06:44:41 -0700844} // namespace plotting
845} // namespace webrtc