blob: ec569990224d2a9d7e951e3e06d24793e98c06e3 [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: {
207 break;
208 }
209 case ParsedRtcEventLog::BWE_PACKET_DELAY_EVENT: {
210 break;
211 }
212 case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: {
213 break;
214 }
215 case ParsedRtcEventLog::UNKNOWN_EVENT: {
216 break;
217 }
218 }
terelius54ce6802016-07-13 06:44:41 -0700219 }
terelius88e64e52016-07-19 01:51:06 -0700220
terelius54ce6802016-07-13 06:44:41 -0700221 if (last_timestamp < first_timestamp) {
222 // No useful events in the log.
223 first_timestamp = last_timestamp = 0;
224 }
225 begin_time_ = first_timestamp;
226 end_time_ = last_timestamp;
227}
228
229void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
230 Plot* plot) {
231 std::map<uint32_t, TimeSeries> time_series;
232
233 PacketDirection direction;
234 MediaType media_type;
235 uint8_t header[IP_PACKET_SIZE];
236 size_t header_length, total_length;
237 float max_y = 0;
238
239 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
240 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
241 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
242 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
243 &header_length, &total_length);
244 if (direction == desired_direction) {
245 // Parse header to get SSRC.
246 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
247 RTPHeader parsed_header;
248 rtp_parser.Parse(&parsed_header);
249 // Filter on SSRC.
250 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
251 uint64_t timestamp = parsed_log_.GetTimestamp(i);
252 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
253 float y = total_length;
254 max_y = std::max(max_y, y);
255 time_series[parsed_header.ssrc].points.push_back(
256 TimeSeriesPoint(x, y));
257 }
258 }
259 }
260 }
261
262 // Set labels and put in graph.
263 for (auto& kv : time_series) {
264 kv.second.label = SsrcToString(kv.first);
265 kv.second.style = BAR_GRAPH;
266 plot->series.push_back(std::move(kv.second));
267 }
268
269 plot->xaxis_min = kDefaultXMin;
270 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
271 plot->xaxis_label = "Time (s)";
272 plot->yaxis_min = kDefaultYMin;
273 plot->yaxis_max = max_y * kYMargin;
274 plot->yaxis_label = "Packet size (bytes)";
275 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
276 plot->title = "Incoming RTP packets";
277 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
278 plot->title = "Outgoing RTP packets";
279 }
280}
281
282// For each SSRC, plot the time between the consecutive playouts.
283void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
284 std::map<uint32_t, TimeSeries> time_series;
285 std::map<uint32_t, uint64_t> last_playout;
286
287 uint32_t ssrc;
288 float max_y = 0;
289
290 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
291 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
292 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
293 parsed_log_.GetAudioPlayout(i, &ssrc);
294 uint64_t timestamp = parsed_log_.GetTimestamp(i);
295 if (MatchingSsrc(ssrc, desired_ssrc_)) {
296 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
297 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
298 if (time_series[ssrc].points.size() == 0) {
299 // There were no previusly logged playout for this SSRC.
300 // Generate a point, but place it on the x-axis.
301 y = 0;
302 }
303 max_y = std::max(max_y, y);
304 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y));
305 last_playout[ssrc] = timestamp;
306 }
307 }
308 }
309
310 // Set labels and put in graph.
311 for (auto& kv : time_series) {
312 kv.second.label = SsrcToString(kv.first);
313 kv.second.style = BAR_GRAPH;
314 plot->series.push_back(std::move(kv.second));
315 }
316
317 plot->xaxis_min = kDefaultXMin;
318 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
319 plot->xaxis_label = "Time (s)";
320 plot->yaxis_min = kDefaultYMin;
321 plot->yaxis_max = max_y * kYMargin;
322 plot->yaxis_label = "Time since last playout (ms)";
323 plot->title = "Audio playout";
324}
325
326// For each SSRC, plot the time between the consecutive playouts.
327void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
328 std::map<uint32_t, TimeSeries> time_series;
329 std::map<uint32_t, uint16_t> last_seqno;
330
331 PacketDirection direction;
332 MediaType media_type;
333 uint8_t header[IP_PACKET_SIZE];
334 size_t header_length, total_length;
335
336 int max_y = 1;
337 int min_y = 0;
338
339 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
340 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
341 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
342 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
343 &header_length, &total_length);
344 uint64_t timestamp = parsed_log_.GetTimestamp(i);
345 if (direction == PacketDirection::kIncomingPacket) {
346 // Parse header to get SSRC.
347 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
348 RTPHeader parsed_header;
349 rtp_parser.Parse(&parsed_header);
350 // Filter on SSRC.
351 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
352 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
353 int y = WrappingDifference(parsed_header.sequenceNumber,
354 last_seqno[parsed_header.ssrc], 1ul << 16);
355 if (time_series[parsed_header.ssrc].points.size() == 0) {
356 // There were no previusly logged playout for this SSRC.
357 // Generate a point, but place it on the x-axis.
358 y = 0;
359 }
360 max_y = std::max(max_y, y);
361 min_y = std::min(min_y, y);
362 time_series[parsed_header.ssrc].points.push_back(
363 TimeSeriesPoint(x, y));
364 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
365 }
366 }
367 }
368 }
369
370 // Set labels and put in graph.
371 for (auto& kv : time_series) {
372 kv.second.label = SsrcToString(kv.first);
373 kv.second.style = BAR_GRAPH;
374 plot->series.push_back(std::move(kv.second));
375 }
376
377 plot->xaxis_min = kDefaultXMin;
378 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
379 plot->xaxis_label = "Time (s)";
380 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
381 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
382 plot->yaxis_label = "Difference since last packet";
383 plot->title = "Sequence number";
384}
385
386void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
terelius54ce6802016-07-13 06:44:41 -0700387 double max_y = 10;
388 double min_y = 0;
389
terelius88e64e52016-07-19 01:51:06 -0700390 for (auto& kv : rtp_packets_) {
391 StreamId stream_id = kv.first;
392 // Filter on direction and SSRC.
393 if (stream_id.GetDirection() != kIncomingPacket ||
394 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
395 continue;
396 }
terelius54ce6802016-07-13 06:44:41 -0700397
terelius88e64e52016-07-19 01:51:06 -0700398 TimeSeries time_series;
399 time_series.label = SsrcToString(stream_id.GetSsrc());
400 time_series.style = BAR_GRAPH;
401 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
402 int64_t last_abs_send_time = 0;
403 int64_t last_timestamp = 0;
404 for (const LoggedRtpPacket& packet : packet_stream) {
405 if (packet.header.extension.hasAbsoluteSendTime) {
406 int64_t send_time_diff =
407 WrappingDifference(packet.header.extension.absoluteSendTime,
408 last_abs_send_time, 1ul << 24);
409 int64_t recv_time_diff = packet.timestamp - last_timestamp;
410
411 last_abs_send_time = packet.header.extension.absoluteSendTime;
412 last_timestamp = packet.timestamp;
413
414 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
415 double y =
416 static_cast<double>(recv_time_diff -
417 AbsSendTimeToMicroseconds(send_time_diff)) /
418 1000;
419 if (time_series.points.size() == 0) {
420 // There were no previously logged packets for this SSRC.
421 // Generate a point, but place it on the x-axis.
422 y = 0;
terelius54ce6802016-07-13 06:44:41 -0700423 }
terelius88e64e52016-07-19 01:51:06 -0700424 max_y = std::max(max_y, y);
425 min_y = std::min(min_y, y);
426 time_series.points.emplace_back(x, y);
terelius54ce6802016-07-13 06:44:41 -0700427 }
428 }
terelius88e64e52016-07-19 01:51:06 -0700429 // Add the data set to the plot.
430 plot->series.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700431 }
432
433 plot->xaxis_min = kDefaultXMin;
434 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
435 plot->xaxis_label = "Time (s)";
436 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
437 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
438 plot->yaxis_label = "Latency change (ms)";
439 plot->title = "Network latency change between consecutive packets";
440}
441
442void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
terelius54ce6802016-07-13 06:44:41 -0700443 double max_y = 10;
444 double min_y = 0;
445
terelius88e64e52016-07-19 01:51:06 -0700446 for (auto& kv : rtp_packets_) {
447 StreamId stream_id = kv.first;
448 // Filter on direction and SSRC.
449 if (stream_id.GetDirection() != kIncomingPacket ||
450 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
451 continue;
452 }
453 TimeSeries time_series;
454 time_series.label = SsrcToString(stream_id.GetSsrc());
455 time_series.style = LINE_GRAPH;
456 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
457 int64_t last_abs_send_time = 0;
458 int64_t last_timestamp = 0;
459 double accumulated_delay_ms = 0;
460 for (const LoggedRtpPacket& packet : packet_stream) {
461 if (packet.header.extension.hasAbsoluteSendTime) {
462 int64_t send_time_diff =
463 WrappingDifference(packet.header.extension.absoluteSendTime,
464 last_abs_send_time, 1ul << 24);
465 int64_t recv_time_diff = packet.timestamp - last_timestamp;
terelius54ce6802016-07-13 06:44:41 -0700466
terelius88e64e52016-07-19 01:51:06 -0700467 last_abs_send_time = packet.header.extension.absoluteSendTime;
468 last_timestamp = packet.timestamp;
469
470 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
471 accumulated_delay_ms +=
472 static_cast<double>(recv_time_diff -
473 AbsSendTimeToMicroseconds(send_time_diff)) /
474 1000;
475 if (time_series.points.size() == 0) {
476 // There were no previously logged packets for this SSRC.
477 // Generate a point, but place it on the x-axis.
478 accumulated_delay_ms = 0;
terelius54ce6802016-07-13 06:44:41 -0700479 }
terelius88e64e52016-07-19 01:51:06 -0700480 max_y = std::max(max_y, accumulated_delay_ms);
481 min_y = std::min(min_y, accumulated_delay_ms);
482 time_series.points.emplace_back(x, accumulated_delay_ms);
terelius54ce6802016-07-13 06:44:41 -0700483 }
484 }
terelius88e64e52016-07-19 01:51:06 -0700485 // Add the data set to the plot.
486 plot->series.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700487 }
488
489 plot->xaxis_min = kDefaultXMin;
490 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
491 plot->xaxis_label = "Time (s)";
492 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
493 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
494 plot->yaxis_label = "Latency change (ms)";
495 plot->title = "Accumulated network latency change";
496}
497
498// Plot the total bandwidth used by all RTP streams.
499void EventLogAnalyzer::CreateTotalBitrateGraph(
500 PacketDirection desired_direction,
501 Plot* plot) {
502 struct TimestampSize {
503 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
504 uint64_t timestamp;
505 size_t size;
506 };
507 std::vector<TimestampSize> packets;
508
509 PacketDirection direction;
510 size_t total_length;
511
512 // Extract timestamps and sizes for the relevant packets.
513 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
514 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
515 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
516 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
517 &total_length);
518 if (direction == desired_direction) {
519 uint64_t timestamp = parsed_log_.GetTimestamp(i);
520 packets.push_back(TimestampSize(timestamp, total_length));
521 }
522 }
523 }
524
525 size_t window_index_begin = 0;
526 size_t window_index_end = 0;
527 size_t bytes_in_window = 0;
528 float max_y = 0;
529
530 // Calculate a moving average of the bitrate and store in a TimeSeries.
531 plot->series.push_back(TimeSeries());
532 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
533 while (window_index_end < packets.size() &&
534 packets[window_index_end].timestamp < time) {
535 bytes_in_window += packets[window_index_end].size;
536 window_index_end++;
537 }
538 while (window_index_begin < packets.size() &&
539 packets[window_index_begin].timestamp < time - window_duration_) {
540 RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window);
541 bytes_in_window -= packets[window_index_begin].size;
542 window_index_begin++;
543 }
544 float window_duration_in_seconds =
545 static_cast<float>(window_duration_) / 1000000;
546 float x = static_cast<float>(time - begin_time_) / 1000000;
547 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
548 max_y = std::max(max_y, y);
549 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
550 }
551
552 // Set labels.
553 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
554 plot->series.back().label = "Incoming bitrate";
555 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
556 plot->series.back().label = "Outgoing bitrate";
557 }
558 plot->series.back().style = LINE_GRAPH;
559
560 plot->xaxis_min = kDefaultXMin;
561 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
562 plot->xaxis_label = "Time (s)";
563 plot->yaxis_min = kDefaultYMin;
564 plot->yaxis_max = max_y * kYMargin;
565 plot->yaxis_label = "Bitrate (kbps)";
566 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
567 plot->title = "Incoming RTP bitrate";
568 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
569 plot->title = "Outgoing RTP bitrate";
570 }
571}
572
573// For each SSRC, plot the bandwidth used by that stream.
574void EventLogAnalyzer::CreateStreamBitrateGraph(
575 PacketDirection desired_direction,
576 Plot* plot) {
577 struct TimestampSize {
578 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
579 uint64_t timestamp;
580 size_t size;
581 };
terelius88e64e52016-07-19 01:51:06 -0700582 std::map<uint32_t, std::vector<TimestampSize>> packets;
terelius54ce6802016-07-13 06:44:41 -0700583
584 PacketDirection direction;
585 MediaType media_type;
586 uint8_t header[IP_PACKET_SIZE];
587 size_t header_length, total_length;
588
589 // Extract timestamps and sizes for the relevant packets.
590 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
591 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
592 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
593 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
594 &header_length, &total_length);
595 if (direction == desired_direction) {
596 // Parse header to get SSRC.
597 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
598 RTPHeader parsed_header;
599 rtp_parser.Parse(&parsed_header);
600 // Filter on SSRC.
601 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
602 uint64_t timestamp = parsed_log_.GetTimestamp(i);
603 packets[parsed_header.ssrc].push_back(
604 TimestampSize(timestamp, total_length));
605 }
606 }
607 }
608 }
609
610 float max_y = 0;
611
612 for (auto& kv : packets) {
613 size_t window_index_begin = 0;
614 size_t window_index_end = 0;
615 size_t bytes_in_window = 0;
616
617 // Calculate a moving average of the bitrate and store in a TimeSeries.
618 plot->series.push_back(TimeSeries());
619 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
620 while (window_index_end < kv.second.size() &&
621 kv.second[window_index_end].timestamp < time) {
622 bytes_in_window += kv.second[window_index_end].size;
623 window_index_end++;
624 }
625 while (window_index_begin < kv.second.size() &&
626 kv.second[window_index_begin].timestamp <
627 time - window_duration_) {
628 RTC_DCHECK_LE(kv.second[window_index_begin].size, bytes_in_window);
629 bytes_in_window -= kv.second[window_index_begin].size;
630 window_index_begin++;
631 }
632 float window_duration_in_seconds =
633 static_cast<float>(window_duration_) / 1000000;
634 float x = static_cast<float>(time - begin_time_) / 1000000;
635 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
636 max_y = std::max(max_y, y);
637 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
638 }
639
640 // Set labels.
641 plot->series.back().label = SsrcToString(kv.first);
642 plot->series.back().style = LINE_GRAPH;
643 }
644
645 plot->xaxis_min = kDefaultXMin;
646 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
647 plot->xaxis_label = "Time (s)";
648 plot->yaxis_min = kDefaultYMin;
649 plot->yaxis_max = max_y * kYMargin;
650 plot->yaxis_label = "Bitrate (kbps)";
651 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
652 plot->title = "Incoming bitrate per stream";
653 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
654 plot->title = "Outgoing bitrate per stream";
655 }
656}
657
658} // namespace plotting
659} // namespace webrtc