blob: e6dd35b6c6fa10e1d62f23cda08b2b36bf9ea792 [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"
25#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
26#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
27#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
28#include "webrtc/video_receive_stream.h"
29#include "webrtc/video_send_stream.h"
30
31namespace {
32
33std::string SsrcToString(uint32_t ssrc) {
34 std::stringstream ss;
35 ss << "SSRC " << ssrc;
36 return ss.str();
37}
38
39// Checks whether an SSRC is contained in the list of desired SSRCs.
40// Note that an empty SSRC list matches every SSRC.
41bool MatchingSsrc(uint32_t ssrc, const std::vector<uint32_t>& desired_ssrc) {
42 if (desired_ssrc.size() == 0)
43 return true;
44 return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) !=
45 desired_ssrc.end();
46}
47
48double AbsSendTimeToMicroseconds(int64_t abs_send_time) {
49 // The timestamp is a fixed point representation with 6 bits for seconds
50 // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the
51 // time in seconds and then multiply by 1000000 to convert to microseconds.
52 static constexpr double kTimestampToMicroSec =
53 1000000.0 / static_cast<double>(1 << 18);
54 return abs_send_time * kTimestampToMicroSec;
55}
56
57// Computes the difference |later| - |earlier| where |later| and |earlier|
58// are counters that wrap at |modulus|. The difference is chosen to have the
59// least absolute value. For example if |modulus| is 8, then the difference will
60// be chosen in the range [-3, 4]. If |modulus| is 9, then the difference will
61// be in [-4, 4].
62int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) {
63 RTC_DCHECK_LE(1, modulus);
64 RTC_DCHECK_LT(later, modulus);
65 RTC_DCHECK_LT(earlier, modulus);
66 int64_t difference =
67 static_cast<int64_t>(later) - static_cast<int64_t>(earlier);
68 int64_t max_difference = modulus / 2;
69 int64_t min_difference = max_difference - modulus + 1;
70 if (difference > max_difference) {
71 difference -= modulus;
72 }
73 if (difference < min_difference) {
74 difference += modulus;
75 }
76 return difference;
77}
78
terelius54ce6802016-07-13 06:44:41 -070079const double kXMargin = 1.02;
80const double kYMargin = 1.1;
81const double kDefaultXMin = -1;
82const double kDefaultYMin = -1;
83
84} // namespace
85
86namespace webrtc {
87namespace plotting {
88
terelius88e64e52016-07-19 01:51:06 -070089
90bool EventLogAnalyzer::StreamId::operator<(const StreamId& other) const {
91 if (ssrc_ < other.ssrc_) {
92 return true;
93 }
94 if (ssrc_ == other.ssrc_) {
95 if (media_type_ < other.media_type_) {
96 return true;
97 }
98 if (media_type_ == other.media_type_) {
99 if (direction_ < other.direction_) {
100 return true;
101 }
102 }
103 }
104 return false;
105}
106
107bool EventLogAnalyzer::StreamId::operator==(const StreamId& other) const {
108 return ssrc_ == other.ssrc_ && direction_ == other.direction_ &&
109 media_type_ == other.media_type_;
110}
111
112
terelius54ce6802016-07-13 06:44:41 -0700113EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
114 : parsed_log_(log), window_duration_(250000), step_(10000) {
115 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
116 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
terelius88e64e52016-07-19 01:51:06 -0700117
118 // Maps a stream identifier consisting of ssrc, direction and MediaType
119 // to the header extensions used by that stream,
120 std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
121
122 PacketDirection direction;
123 MediaType media_type;
124 uint8_t header[IP_PACKET_SIZE];
125 size_t header_length;
126 size_t total_length;
127
terelius54ce6802016-07-13 06:44:41 -0700128 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
129 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
terelius88e64e52016-07-19 01:51:06 -0700130 if (event_type != ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT &&
131 event_type != ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT &&
132 event_type != ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT &&
133 event_type != ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
134 uint64_t timestamp = parsed_log_.GetTimestamp(i);
135 first_timestamp = std::min(first_timestamp, timestamp);
136 last_timestamp = std::max(last_timestamp, timestamp);
137 }
138
139 switch (parsed_log_.GetEventType(i)) {
140 case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
141 VideoReceiveStream::Config config(nullptr);
142 parsed_log_.GetVideoReceiveConfig(i, &config);
143 StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
144 MediaType::VIDEO);
145 extension_maps[stream].Erase();
146 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
147 const std::string& extension = config.rtp.extensions[j].uri;
148 int id = config.rtp.extensions[j].id;
149 extension_maps[stream].Register(StringToRtpExtensionType(extension),
150 id);
151 }
152 break;
153 }
154 case ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT: {
155 VideoSendStream::Config config(nullptr);
156 parsed_log_.GetVideoSendConfig(i, &config);
157 for (auto ssrc : config.rtp.ssrcs) {
158 StreamId stream(ssrc, kOutgoingPacket, MediaType::VIDEO);
159 extension_maps[stream].Erase();
160 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
161 const std::string& extension = config.rtp.extensions[j].uri;
162 int id = config.rtp.extensions[j].id;
163 extension_maps[stream].Register(StringToRtpExtensionType(extension),
164 id);
165 }
166 }
167 break;
168 }
169 case ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT: {
170 AudioReceiveStream::Config config;
171 // TODO(terelius): Parse the audio configs once we have them.
172 break;
173 }
174 case ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT: {
175 AudioSendStream::Config config(nullptr);
176 // TODO(terelius): Parse the audio configs once we have them.
177 break;
178 }
179 case ParsedRtcEventLog::RTP_EVENT: {
180 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
181 &header_length, &total_length);
182 // Parse header to get SSRC.
183 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
184 RTPHeader parsed_header;
185 rtp_parser.Parse(&parsed_header);
186 StreamId stream(parsed_header.ssrc, direction, media_type);
187 // Look up the extension_map and parse it again to get the extensions.
188 if (extension_maps.count(stream) == 1) {
189 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
190 rtp_parser.Parse(&parsed_header, extension_map);
191 }
192 uint64_t timestamp = parsed_log_.GetTimestamp(i);
193 rtp_packets_[stream].push_back(
194 LoggedRtpPacket(timestamp, parsed_header));
195 break;
196 }
197 case ParsedRtcEventLog::RTCP_EVENT: {
198 break;
199 }
200 case ParsedRtcEventLog::LOG_START: {
201 break;
202 }
203 case ParsedRtcEventLog::LOG_END: {
204 break;
205 }
206 case ParsedRtcEventLog::BWE_PACKET_LOSS_EVENT: {
terelius8058e582016-07-25 01:32:41 -0700207 BwePacketLossEvent bwe_update;
208 bwe_update.timestamp = parsed_log_.GetTimestamp(i);
209 parsed_log_.GetBwePacketLossEvent(i, &bwe_update.new_bitrate,
210 &bwe_update.fraction_loss,
211 &bwe_update.expected_packets);
212 bwe_loss_updates_.push_back(bwe_update);
terelius88e64e52016-07-19 01:51:06 -0700213 break;
214 }
215 case ParsedRtcEventLog::BWE_PACKET_DELAY_EVENT: {
216 break;
217 }
218 case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: {
219 break;
220 }
221 case ParsedRtcEventLog::UNKNOWN_EVENT: {
222 break;
223 }
224 }
terelius54ce6802016-07-13 06:44:41 -0700225 }
terelius88e64e52016-07-19 01:51:06 -0700226
terelius54ce6802016-07-13 06:44:41 -0700227 if (last_timestamp < first_timestamp) {
228 // No useful events in the log.
229 first_timestamp = last_timestamp = 0;
230 }
231 begin_time_ = first_timestamp;
232 end_time_ = last_timestamp;
233}
234
235void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
236 Plot* plot) {
237 std::map<uint32_t, TimeSeries> time_series;
238
239 PacketDirection direction;
240 MediaType media_type;
241 uint8_t header[IP_PACKET_SIZE];
242 size_t header_length, total_length;
243 float max_y = 0;
244
245 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
246 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
247 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
248 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
249 &header_length, &total_length);
250 if (direction == desired_direction) {
251 // Parse header to get SSRC.
252 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
253 RTPHeader parsed_header;
254 rtp_parser.Parse(&parsed_header);
255 // Filter on SSRC.
256 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
257 uint64_t timestamp = parsed_log_.GetTimestamp(i);
258 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
259 float y = total_length;
260 max_y = std::max(max_y, y);
261 time_series[parsed_header.ssrc].points.push_back(
262 TimeSeriesPoint(x, y));
263 }
264 }
265 }
266 }
267
268 // Set labels and put in graph.
269 for (auto& kv : time_series) {
270 kv.second.label = SsrcToString(kv.first);
271 kv.second.style = BAR_GRAPH;
272 plot->series.push_back(std::move(kv.second));
273 }
274
275 plot->xaxis_min = kDefaultXMin;
276 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
277 plot->xaxis_label = "Time (s)";
278 plot->yaxis_min = kDefaultYMin;
279 plot->yaxis_max = max_y * kYMargin;
280 plot->yaxis_label = "Packet size (bytes)";
281 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
282 plot->title = "Incoming RTP packets";
283 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
284 plot->title = "Outgoing RTP packets";
285 }
286}
287
288// For each SSRC, plot the time between the consecutive playouts.
289void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
290 std::map<uint32_t, TimeSeries> time_series;
291 std::map<uint32_t, uint64_t> last_playout;
292
293 uint32_t ssrc;
294 float max_y = 0;
295
296 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
297 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
298 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
299 parsed_log_.GetAudioPlayout(i, &ssrc);
300 uint64_t timestamp = parsed_log_.GetTimestamp(i);
301 if (MatchingSsrc(ssrc, desired_ssrc_)) {
302 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
303 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
304 if (time_series[ssrc].points.size() == 0) {
305 // There were no previusly logged playout for this SSRC.
306 // Generate a point, but place it on the x-axis.
307 y = 0;
308 }
309 max_y = std::max(max_y, y);
310 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y));
311 last_playout[ssrc] = timestamp;
312 }
313 }
314 }
315
316 // Set labels and put in graph.
317 for (auto& kv : time_series) {
318 kv.second.label = SsrcToString(kv.first);
319 kv.second.style = BAR_GRAPH;
320 plot->series.push_back(std::move(kv.second));
321 }
322
323 plot->xaxis_min = kDefaultXMin;
324 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
325 plot->xaxis_label = "Time (s)";
326 plot->yaxis_min = kDefaultYMin;
327 plot->yaxis_max = max_y * kYMargin;
328 plot->yaxis_label = "Time since last playout (ms)";
329 plot->title = "Audio playout";
330}
331
332// For each SSRC, plot the time between the consecutive playouts.
333void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
334 std::map<uint32_t, TimeSeries> time_series;
335 std::map<uint32_t, uint16_t> last_seqno;
336
337 PacketDirection direction;
338 MediaType media_type;
339 uint8_t header[IP_PACKET_SIZE];
340 size_t header_length, total_length;
341
342 int max_y = 1;
343 int min_y = 0;
344
345 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
346 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
347 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
348 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
349 &header_length, &total_length);
350 uint64_t timestamp = parsed_log_.GetTimestamp(i);
351 if (direction == PacketDirection::kIncomingPacket) {
352 // Parse header to get SSRC.
353 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
354 RTPHeader parsed_header;
355 rtp_parser.Parse(&parsed_header);
356 // Filter on SSRC.
357 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
358 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
359 int y = WrappingDifference(parsed_header.sequenceNumber,
360 last_seqno[parsed_header.ssrc], 1ul << 16);
361 if (time_series[parsed_header.ssrc].points.size() == 0) {
362 // There were no previusly logged playout for this SSRC.
363 // Generate a point, but place it on the x-axis.
364 y = 0;
365 }
366 max_y = std::max(max_y, y);
367 min_y = std::min(min_y, y);
368 time_series[parsed_header.ssrc].points.push_back(
369 TimeSeriesPoint(x, y));
370 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
371 }
372 }
373 }
374 }
375
376 // Set labels and put in graph.
377 for (auto& kv : time_series) {
378 kv.second.label = SsrcToString(kv.first);
379 kv.second.style = BAR_GRAPH;
380 plot->series.push_back(std::move(kv.second));
381 }
382
383 plot->xaxis_min = kDefaultXMin;
384 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
385 plot->xaxis_label = "Time (s)";
386 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
387 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
388 plot->yaxis_label = "Difference since last packet";
389 plot->title = "Sequence number";
390}
391
392void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
terelius54ce6802016-07-13 06:44:41 -0700393 double max_y = 10;
394 double min_y = 0;
395
terelius88e64e52016-07-19 01:51:06 -0700396 for (auto& kv : rtp_packets_) {
397 StreamId stream_id = kv.first;
398 // Filter on direction and SSRC.
399 if (stream_id.GetDirection() != kIncomingPacket ||
400 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
401 continue;
402 }
terelius54ce6802016-07-13 06:44:41 -0700403
terelius88e64e52016-07-19 01:51:06 -0700404 TimeSeries time_series;
405 time_series.label = SsrcToString(stream_id.GetSsrc());
406 time_series.style = BAR_GRAPH;
407 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
408 int64_t last_abs_send_time = 0;
409 int64_t last_timestamp = 0;
410 for (const LoggedRtpPacket& packet : packet_stream) {
411 if (packet.header.extension.hasAbsoluteSendTime) {
412 int64_t send_time_diff =
413 WrappingDifference(packet.header.extension.absoluteSendTime,
414 last_abs_send_time, 1ul << 24);
415 int64_t recv_time_diff = packet.timestamp - last_timestamp;
416
417 last_abs_send_time = packet.header.extension.absoluteSendTime;
418 last_timestamp = packet.timestamp;
419
420 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
421 double y =
422 static_cast<double>(recv_time_diff -
423 AbsSendTimeToMicroseconds(send_time_diff)) /
424 1000;
425 if (time_series.points.size() == 0) {
426 // There were no previously logged packets for this SSRC.
427 // Generate a point, but place it on the x-axis.
428 y = 0;
terelius54ce6802016-07-13 06:44:41 -0700429 }
terelius88e64e52016-07-19 01:51:06 -0700430 max_y = std::max(max_y, y);
431 min_y = std::min(min_y, y);
432 time_series.points.emplace_back(x, y);
terelius54ce6802016-07-13 06:44:41 -0700433 }
434 }
terelius88e64e52016-07-19 01:51:06 -0700435 // Add the data set to the plot.
436 plot->series.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700437 }
438
439 plot->xaxis_min = kDefaultXMin;
440 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
441 plot->xaxis_label = "Time (s)";
442 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
443 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
444 plot->yaxis_label = "Latency change (ms)";
445 plot->title = "Network latency change between consecutive packets";
446}
447
448void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
terelius54ce6802016-07-13 06:44:41 -0700449 double max_y = 10;
450 double min_y = 0;
451
terelius88e64e52016-07-19 01:51:06 -0700452 for (auto& kv : rtp_packets_) {
453 StreamId stream_id = kv.first;
454 // Filter on direction and SSRC.
455 if (stream_id.GetDirection() != kIncomingPacket ||
456 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
457 continue;
458 }
459 TimeSeries time_series;
460 time_series.label = SsrcToString(stream_id.GetSsrc());
461 time_series.style = LINE_GRAPH;
462 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
463 int64_t last_abs_send_time = 0;
464 int64_t last_timestamp = 0;
465 double accumulated_delay_ms = 0;
466 for (const LoggedRtpPacket& packet : packet_stream) {
467 if (packet.header.extension.hasAbsoluteSendTime) {
468 int64_t send_time_diff =
469 WrappingDifference(packet.header.extension.absoluteSendTime,
470 last_abs_send_time, 1ul << 24);
471 int64_t recv_time_diff = packet.timestamp - last_timestamp;
terelius54ce6802016-07-13 06:44:41 -0700472
terelius88e64e52016-07-19 01:51:06 -0700473 last_abs_send_time = packet.header.extension.absoluteSendTime;
474 last_timestamp = packet.timestamp;
475
476 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
477 accumulated_delay_ms +=
478 static_cast<double>(recv_time_diff -
479 AbsSendTimeToMicroseconds(send_time_diff)) /
480 1000;
481 if (time_series.points.size() == 0) {
482 // There were no previously logged packets for this SSRC.
483 // Generate a point, but place it on the x-axis.
484 accumulated_delay_ms = 0;
terelius54ce6802016-07-13 06:44:41 -0700485 }
terelius88e64e52016-07-19 01:51:06 -0700486 max_y = std::max(max_y, accumulated_delay_ms);
487 min_y = std::min(min_y, accumulated_delay_ms);
488 time_series.points.emplace_back(x, accumulated_delay_ms);
terelius54ce6802016-07-13 06:44:41 -0700489 }
490 }
terelius88e64e52016-07-19 01:51:06 -0700491 // Add the data set to the plot.
492 plot->series.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700493 }
494
495 plot->xaxis_min = kDefaultXMin;
496 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
497 plot->xaxis_label = "Time (s)";
498 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
499 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
500 plot->yaxis_label = "Latency change (ms)";
501 plot->title = "Accumulated network latency change";
502}
503
504// Plot the total bandwidth used by all RTP streams.
505void EventLogAnalyzer::CreateTotalBitrateGraph(
506 PacketDirection desired_direction,
507 Plot* plot) {
508 struct TimestampSize {
509 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
510 uint64_t timestamp;
511 size_t size;
512 };
513 std::vector<TimestampSize> packets;
514
515 PacketDirection direction;
516 size_t total_length;
517
518 // Extract timestamps and sizes for the relevant packets.
519 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
520 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
521 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
522 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
523 &total_length);
524 if (direction == desired_direction) {
525 uint64_t timestamp = parsed_log_.GetTimestamp(i);
526 packets.push_back(TimestampSize(timestamp, total_length));
527 }
528 }
529 }
530
531 size_t window_index_begin = 0;
532 size_t window_index_end = 0;
533 size_t bytes_in_window = 0;
534 float max_y = 0;
535
536 // Calculate a moving average of the bitrate and store in a TimeSeries.
537 plot->series.push_back(TimeSeries());
538 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
539 while (window_index_end < packets.size() &&
540 packets[window_index_end].timestamp < time) {
541 bytes_in_window += packets[window_index_end].size;
542 window_index_end++;
543 }
544 while (window_index_begin < packets.size() &&
545 packets[window_index_begin].timestamp < time - window_duration_) {
546 RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window);
547 bytes_in_window -= packets[window_index_begin].size;
548 window_index_begin++;
549 }
550 float window_duration_in_seconds =
551 static_cast<float>(window_duration_) / 1000000;
552 float x = static_cast<float>(time - begin_time_) / 1000000;
553 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
554 max_y = std::max(max_y, y);
555 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
556 }
557
558 // Set labels.
559 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
560 plot->series.back().label = "Incoming bitrate";
561 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
562 plot->series.back().label = "Outgoing bitrate";
563 }
564 plot->series.back().style = LINE_GRAPH;
565
terelius8058e582016-07-25 01:32:41 -0700566 // Overlay the send-side bandwidth estimate over the outgoing bitrate.
567 if (desired_direction == kOutgoingPacket) {
568 plot->series.push_back(TimeSeries());
569 for (auto& bwe_update : bwe_loss_updates_) {
570 float x =
571 static_cast<float>(bwe_update.timestamp - begin_time_) / 1000000;
572 float y = static_cast<float>(bwe_update.new_bitrate) / 1000;
573 max_y = std::max(max_y, y);
574 plot->series.back().points.emplace_back(x, y);
575 }
576 plot->series.back().label = "Loss-based estimate";
577 plot->series.back().style = LINE_GRAPH;
578 }
579
terelius54ce6802016-07-13 06:44:41 -0700580 plot->xaxis_min = kDefaultXMin;
581 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
582 plot->xaxis_label = "Time (s)";
583 plot->yaxis_min = kDefaultYMin;
584 plot->yaxis_max = max_y * kYMargin;
585 plot->yaxis_label = "Bitrate (kbps)";
586 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
587 plot->title = "Incoming RTP bitrate";
588 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
589 plot->title = "Outgoing RTP bitrate";
590 }
591}
592
593// For each SSRC, plot the bandwidth used by that stream.
594void EventLogAnalyzer::CreateStreamBitrateGraph(
595 PacketDirection desired_direction,
596 Plot* plot) {
597 struct TimestampSize {
598 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
599 uint64_t timestamp;
600 size_t size;
601 };
terelius88e64e52016-07-19 01:51:06 -0700602 std::map<uint32_t, std::vector<TimestampSize>> packets;
terelius54ce6802016-07-13 06:44:41 -0700603
604 PacketDirection direction;
605 MediaType media_type;
606 uint8_t header[IP_PACKET_SIZE];
607 size_t header_length, total_length;
608
609 // Extract timestamps and sizes for the relevant packets.
610 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
611 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
612 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
613 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
614 &header_length, &total_length);
615 if (direction == desired_direction) {
616 // Parse header to get SSRC.
617 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
618 RTPHeader parsed_header;
619 rtp_parser.Parse(&parsed_header);
620 // Filter on SSRC.
621 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
622 uint64_t timestamp = parsed_log_.GetTimestamp(i);
623 packets[parsed_header.ssrc].push_back(
624 TimestampSize(timestamp, total_length));
625 }
626 }
627 }
628 }
629
630 float max_y = 0;
631
632 for (auto& kv : packets) {
633 size_t window_index_begin = 0;
634 size_t window_index_end = 0;
635 size_t bytes_in_window = 0;
636
637 // Calculate a moving average of the bitrate and store in a TimeSeries.
638 plot->series.push_back(TimeSeries());
639 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
640 while (window_index_end < kv.second.size() &&
641 kv.second[window_index_end].timestamp < time) {
642 bytes_in_window += kv.second[window_index_end].size;
643 window_index_end++;
644 }
645 while (window_index_begin < kv.second.size() &&
646 kv.second[window_index_begin].timestamp <
647 time - window_duration_) {
648 RTC_DCHECK_LE(kv.second[window_index_begin].size, bytes_in_window);
649 bytes_in_window -= kv.second[window_index_begin].size;
650 window_index_begin++;
651 }
652 float window_duration_in_seconds =
653 static_cast<float>(window_duration_) / 1000000;
654 float x = static_cast<float>(time - begin_time_) / 1000000;
655 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
656 max_y = std::max(max_y, y);
657 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
658 }
659
660 // Set labels.
661 plot->series.back().label = SsrcToString(kv.first);
662 plot->series.back().style = LINE_GRAPH;
663 }
664
665 plot->xaxis_min = kDefaultXMin;
666 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
667 plot->xaxis_label = "Time (s)";
668 plot->yaxis_min = kDefaultYMin;
669 plot->yaxis_max = max_y * kYMargin;
670 plot->yaxis_label = "Bitrate (kbps)";
671 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
672 plot->title = "Incoming bitrate per stream";
673 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
674 plot->title = "Outgoing bitrate per stream";
675 }
676}
677
678} // namespace plotting
679} // namespace webrtc