Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(675)

Side by Side Diff: webrtc/tools/event_log_visualizer/analyzer.cc

Issue 1995523002: Visualization tool for WebrtcEventLogs (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: More review comments Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
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
31 namespace {
32
33 std::string HeaderToString(const webrtc::RTPHeader& parsed_header) {
34 std::stringstream ss;
35 ss << "Marker=" << parsed_header.markerBit
36 << ", PType=" << parsed_header.payloadType
37 << ", SeqNum=" << parsed_header.sequenceNumber
38 << ", CaptureTime=" << parsed_header.timestamp
39 << ", SSRC=" << parsed_header.ssrc;
40 return ss.str();
41 }
42
43 std::string SsrcToString(uint32_t ssrc) {
44 std::stringstream ss;
45 ss << "SSRC " << ssrc;
46 return ss.str();
47 }
48
49 // Checks whether an SSRC is contained in the list of desired SSRCs.
50 // Note that an empty SSRC list matches every SSRC.
51 bool MatchingSsrc(uint32_t ssrc, const std::vector<uint32_t>& desired_ssrc) {
52 if (desired_ssrc.size() == 0)
53 return true;
54 return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) !=
55 desired_ssrc.end();
56 }
57
58 double AbsSendTimeToMicroseconds(int64_t abs_send_time) {
59 // The timestamp is a fixed point representation with 6 bits for seconds
60 // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the
61 // time in seconds and then multiply by 1000000 to convert to microseconds.
62 static constexpr double kTimestampToMicroSec =
63 1000000.0 / static_cast<double>(1 << 18);
64 return abs_send_time * kTimestampToMicroSec;
65 }
66
67 // Computes the difference |later| - |earlier| where |later| and |earlier|
68 // are counters that wrap at |modulus|. The difference is chosen to have the
69 // least absolute value. For example if |modulus| is 8, then the difference will
70 // be chosen in the range [-3, 4]. If |modulus| is 9, then the difference will
71 // be in [-4, 4].
72 int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) {
73 RTC_DCHECK_LE(1, modulus);
74 RTC_DCHECK_LT(later, modulus);
75 RTC_DCHECK_LT(earlier, modulus);
76 int64_t difference =
77 static_cast<int64_t>(later) - static_cast<int64_t>(earlier);
78 int64_t max_difference = modulus / 2;
79 int64_t min_difference = max_difference - modulus + 1;
80 if (difference > max_difference) {
81 difference -= modulus;
82 }
83 if (difference < min_difference) {
84 difference += modulus;
85 }
86 return difference;
87 }
88
89 class StreamId {
90 public:
91 StreamId(uint32_t ssrc,
92 webrtc::PacketDirection direction,
93 webrtc::MediaType media_type)
94 : ssrc_(ssrc), direction_(direction), media_type_(media_type) {}
95
96 bool operator<(const StreamId& other) const {
97 if (ssrc_ < other.ssrc_) {
98 return true;
99 }
100 if (ssrc_ == other.ssrc_) {
101 if (media_type_ < other.media_type_) {
102 return true;
103 }
104 if (media_type_ == other.media_type_) {
105 if (direction_ < other.direction_) {
106 return true;
107 }
108 }
109 }
110 return false;
111 }
112
113 bool operator==(const StreamId& other) const {
114 return ssrc_ == other.ssrc_ && direction_ == other.direction_ &&
115 media_type_ == other.media_type_;
116 }
117
118 uint32_t GetSsrc() const { return ssrc_; }
119
120 private:
121 uint32_t ssrc_;
122 webrtc::PacketDirection direction_;
123 webrtc::MediaType media_type_;
124 };
125
126 const double kXMargin = 1.02;
127 const double kYMargin = 1.1;
128 const double kDefaultXMin = -1;
129 const double kDefaultYMin = -1;
130
131 } // namespace
132
133 namespace webrtc {
134 namespace plotting {
135
136 EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log,
137 bool extra_info)
138 : parsed_log_(log),
139 extra_point_info_(extra_info),
140 window_duration_(250000),
141 step_(10000) {
142 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
143 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
144 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
145 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
146 if (event_type == ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT)
147 continue;
148 if (event_type == ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT)
149 continue;
150 if (event_type == ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT)
151 continue;
152 if (event_type == ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT)
153 continue;
154 uint64_t timestamp = parsed_log_.GetTimestamp(i);
155 first_timestamp = std::min(first_timestamp, timestamp);
156 last_timestamp = std::max(last_timestamp, timestamp);
157 }
158 if (last_timestamp < first_timestamp) {
159 // No useful events in the log.
160 first_timestamp = last_timestamp = 0;
161 }
162 begin_time_ = first_timestamp;
163 end_time_ = last_timestamp;
164 }
165
166 void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
167 Plot* plot) {
168 std::map<uint32_t, TimeSeries> time_series;
169
170 PacketDirection direction;
171 MediaType media_type;
172 uint8_t header[IP_PACKET_SIZE];
173 size_t header_length, total_length;
174 float max_y = 0;
175
176 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
177 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
178 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
179 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
180 &header_length, &total_length);
181 if (direction == desired_direction) {
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 // Filter on SSRC.
187 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
188 uint64_t timestamp = parsed_log_.GetTimestamp(i);
189 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
190 float y = total_length;
191 max_y = std::max(max_y, y);
192 std::string message;
193 if (extra_point_info_) {
194 message = HeaderToString(parsed_header);
195 }
196 time_series[parsed_header.ssrc].points.push_back(
197 TimeSeriesPoint(x, y, message));
198 }
199 }
200 }
201 }
202
203 // Set labels and put in graph.
204 for (auto& kv : time_series) {
205 kv.second.label = SsrcToString(kv.first);
206 kv.second.style = BAR_GRAPH;
207 plot->series.push_back(std::move(kv.second));
208 }
209
210 plot->xaxis_min = kDefaultXMin;
211 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
212 plot->xaxis_label = "Time (s)";
213 plot->yaxis_min = kDefaultYMin;
214 plot->yaxis_max = max_y * kYMargin;
215 plot->yaxis_label = "Packet size (bytes)";
216 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
217 plot->title = "Incoming RTP packets";
218 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
219 plot->title = "Outgoing RTP packets";
220 }
221 }
222
223 // For each SSRC, plot the time between the consecutive playouts.
224 void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
225 std::map<uint32_t, TimeSeries> time_series;
226 std::map<uint32_t, uint64_t> last_playout;
227
228 uint32_t ssrc;
229 float max_y = 0;
230
231 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
232 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
233 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
234 parsed_log_.GetAudioPlayout(i, &ssrc);
235 uint64_t timestamp = parsed_log_.GetTimestamp(i);
236 if (MatchingSsrc(ssrc, desired_ssrc_)) {
237 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
238 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
239 if (time_series[ssrc].points.size() == 0) {
240 // There were no previusly logged playout for this SSRC.
241 // Generate a point, but place it on the x-axis.
242 y = 0;
243 }
244 max_y = std::max(max_y, y);
245 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y, ""));
246 last_playout[ssrc] = timestamp;
247 }
248 }
249 }
250
251 // Set labels and put in graph.
252 for (auto& kv : time_series) {
253 kv.second.label = SsrcToString(kv.first);
254 kv.second.style = BAR_GRAPH;
255 plot->series.push_back(std::move(kv.second));
256 }
257
258 plot->xaxis_min = kDefaultXMin;
259 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
260 plot->xaxis_label = "Time (s)";
261 plot->yaxis_min = kDefaultYMin;
262 plot->yaxis_max = max_y * kYMargin;
263 plot->yaxis_label = "Time since last playout (ms)";
264 plot->title = "Audio playout";
265 }
266
267 // For each SSRC, plot the time between the consecutive playouts.
268 void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
269 std::map<uint32_t, TimeSeries> time_series;
270 std::map<uint32_t, uint16_t> last_seqno;
271
272 PacketDirection direction;
273 MediaType media_type;
274 uint8_t header[IP_PACKET_SIZE];
275 size_t header_length, total_length;
276
277 int max_y = 1;
278 int min_y = 0;
279
280 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
281 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
282 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
283 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
284 &header_length, &total_length);
285 uint64_t timestamp = parsed_log_.GetTimestamp(i);
286 if (direction == PacketDirection::kIncomingPacket) {
287 // Parse header to get SSRC.
288 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
289 RTPHeader parsed_header;
290 rtp_parser.Parse(&parsed_header);
291 // Filter on SSRC.
292 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
293 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
294 int y = WrappingDifference(parsed_header.sequenceNumber,
295 last_seqno[parsed_header.ssrc], 1ul << 16);
296 if (time_series[parsed_header.ssrc].points.size() == 0) {
297 // There were no previusly logged playout for this SSRC.
298 // Generate a point, but place it on the x-axis.
299 y = 0;
300 }
301 max_y = std::max(max_y, y);
302 min_y = std::min(min_y, y);
303 time_series[parsed_header.ssrc].points.push_back(
304 TimeSeriesPoint(x, y, ""));
305 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
306 }
307 }
308 }
309 }
310
311 // Set labels and put in graph.
312 for (auto& kv : time_series) {
313 kv.second.label = SsrcToString(kv.first);
314 kv.second.style = BAR_GRAPH;
315 plot->series.push_back(std::move(kv.second));
316 }
317
318 plot->xaxis_min = kDefaultXMin;
319 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
320 plot->xaxis_label = "Time (s)";
321 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
322 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
323 plot->yaxis_label = "Difference since last packet";
324 plot->title = "Sequence number";
325 }
326
327 void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
328 // Maps a stream identifier consisting of ssrc, direction and MediaType
329 // to the header extensions used by that stream,
330 std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
331
332 struct SendReceiveTime {
333 SendReceiveTime() = default;
334 SendReceiveTime(uint32_t send_time, uint64_t recv_time)
335 : absolute_send_time(send_time), receive_timestamp(recv_time) {}
336 uint32_t absolute_send_time; // 24-bit value in units of 2^-18 seconds.
337 uint64_t receive_timestamp; // In microseconds.
338 };
339 std::map<StreamId, SendReceiveTime> last_packet;
340 std::map<StreamId, TimeSeries> time_series;
341
342 PacketDirection direction;
343 MediaType media_type;
344 uint8_t header[IP_PACKET_SIZE];
345 size_t header_length, total_length;
346
347 double max_y = 10;
348 double min_y = 0;
349
350 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
351 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
352 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
353 VideoReceiveStream::Config config(nullptr);
354 parsed_log_.GetVideoReceiveConfig(i, &config);
355 StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
356 MediaType::VIDEO);
357 extension_maps[stream].Erase();
358 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
359 const std::string& extension = config.rtp.extensions[j].uri;
360 int id = config.rtp.extensions[j].id;
361 extension_maps[stream].Register(StringToRtpExtensionType(extension),
362 id);
363 }
364 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
365 VideoSendStream::Config config(nullptr);
366 parsed_log_.GetVideoSendConfig(i, &config);
367 for (auto ssrc : config.rtp.ssrcs) {
368 StreamId stream(ssrc, kIncomingPacket, MediaType::VIDEO);
369 extension_maps[stream].Erase();
370 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
371 const std::string& extension = config.rtp.extensions[j].uri;
372 int id = config.rtp.extensions[j].id;
373 extension_maps[stream].Register(StringToRtpExtensionType(extension),
374 id);
375 }
376 }
377 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
378 AudioReceiveStream::Config config;
379 // TODO(terelius): Parse the audio configs once we have them
380 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
381 AudioSendStream::Config config(nullptr);
382 // TODO(terelius): Parse the audio configs once we have them
383 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
384 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
385 &header_length, &total_length);
386 if (direction == kIncomingPacket) {
387 // Parse header to get SSRC.
388 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
389 RTPHeader parsed_header;
390 rtp_parser.Parse(&parsed_header);
391 // Filter on SSRC.
392 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
393 StreamId stream(parsed_header.ssrc, direction, media_type);
394 // Look up the extension_map and parse it again to get the extensions.
395 if (extension_maps.count(stream) == 1) {
396 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
397 rtp_parser.Parse(&parsed_header, extension_map);
398 if (parsed_header.extension.hasAbsoluteSendTime) {
399 uint64_t timestamp = parsed_log_.GetTimestamp(i);
400 int64_t send_time_diff = WrappingDifference(
401 parsed_header.extension.absoluteSendTime,
402 last_packet[stream].absolute_send_time, 1ul << 24);
403 int64_t recv_time_diff =
404 timestamp - last_packet[stream].receive_timestamp;
405
406 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
407 double y = static_cast<double>(
408 recv_time_diff -
409 AbsSendTimeToMicroseconds(send_time_diff)) /
410 1000;
411 if (time_series[stream].points.size() == 0) {
412 // There were no previusly logged playout for this SSRC.
413 // Generate a point, but place it on the x-axis.
414 y = 0;
415 }
416 max_y = std::max(max_y, y);
417 min_y = std::min(min_y, y);
418 time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
419 last_packet[stream] = SendReceiveTime(
420 parsed_header.extension.absoluteSendTime, timestamp);
421 }
422 }
423 }
424 }
425 }
426 }
427
428 // Set labels and put in graph.
429 for (auto& kv : time_series) {
430 kv.second.label = SsrcToString(kv.first.GetSsrc());
431 kv.second.style = BAR_GRAPH;
432 plot->series.push_back(std::move(kv.second));
433 }
434
435 plot->xaxis_min = kDefaultXMin;
436 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
437 plot->xaxis_label = "Time (s)";
438 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
439 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
440 plot->yaxis_label = "Latency change (ms)";
441 plot->title = "Network latency change between consecutive packets";
442 }
443
444 void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
445 // TODO(terelius): Refactor
446
447 // Maps a stream identifier consisting of ssrc, direction and MediaType
448 // to the header extensions used by that stream.
449 std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
450
451 struct SendReceiveTime {
452 SendReceiveTime() = default;
453 SendReceiveTime(uint32_t send_time, uint64_t recv_time, double accumulated)
454 : absolute_send_time(send_time),
455 receive_timestamp(recv_time),
456 accumulated_delay(accumulated) {}
457 uint32_t absolute_send_time; // 24-bit value in units of 2^-18 seconds.
458 uint64_t receive_timestamp; // In microseconds.
459 double accumulated_delay; // In milliseconds.
460 };
461 std::map<StreamId, SendReceiveTime> last_packet;
462 std::map<StreamId, TimeSeries> time_series;
463
464 PacketDirection direction;
465 MediaType media_type;
466 uint8_t header[IP_PACKET_SIZE];
467 size_t header_length, total_length;
468
469 double max_y = 10;
470 double min_y = 0;
471
472 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
473 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
474 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
475 VideoReceiveStream::Config config(nullptr);
476 parsed_log_.GetVideoReceiveConfig(i, &config);
477 StreamId stream(config.rtp.remote_ssrc, kIncomingPacket,
478 MediaType::VIDEO);
479 extension_maps[stream].Erase();
480 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
481 const std::string& extension = config.rtp.extensions[j].uri;
482 int id = config.rtp.extensions[j].id;
483 extension_maps[stream].Register(StringToRtpExtensionType(extension),
484 id);
485 }
486 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
487 VideoSendStream::Config config(nullptr);
488 parsed_log_.GetVideoSendConfig(i, &config);
489 for (auto ssrc : config.rtp.ssrcs) {
490 StreamId stream(ssrc, kIncomingPacket, MediaType::VIDEO);
491 extension_maps[stream].Erase();
492 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
493 const std::string& extension = config.rtp.extensions[j].uri;
494 int id = config.rtp.extensions[j].id;
495 extension_maps[stream].Register(StringToRtpExtensionType(extension),
496 id);
497 }
498 }
499 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
500 AudioReceiveStream::Config config;
501 // TODO(terelius): Parse the audio configs once we have them
502 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
503 AudioSendStream::Config config(nullptr);
504 // TODO(terelius): Parse the audio configs once we have them
505 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
506 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
507 &header_length, &total_length);
508 if (direction == kIncomingPacket) {
509 // Parse header to get SSRC.
510 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
511 RTPHeader parsed_header;
512 rtp_parser.Parse(&parsed_header);
513 // Filter on SSRC.
514 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
515 StreamId stream(parsed_header.ssrc, direction, media_type);
516 // Look up the extension_map and parse it again to get the extensions.
517 if (extension_maps.count(stream) == 1) {
518 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
519 rtp_parser.Parse(&parsed_header, extension_map);
520 if (parsed_header.extension.hasAbsoluteSendTime) {
521 uint64_t timestamp = parsed_log_.GetTimestamp(i);
522 int64_t send_time_diff = WrappingDifference(
523 parsed_header.extension.absoluteSendTime,
524 last_packet[stream].absolute_send_time, 1ul << 24);
525 int64_t recv_time_diff =
526 timestamp - last_packet[stream].receive_timestamp;
527
528 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
529 double y = last_packet[stream].accumulated_delay +
530 static_cast<double>(
531 recv_time_diff -
532 AbsSendTimeToMicroseconds(send_time_diff)) /
533 1000;
534 if (time_series[stream].points.size() == 0) {
535 // There were no previusly logged playout for this SSRC.
536 // Generate a point, but place it on the x-axis.
537 y = 0;
538 }
539 max_y = std::max(max_y, y);
540 min_y = std::min(min_y, y);
541 time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
542 last_packet[stream] = SendReceiveTime(
543 parsed_header.extension.absoluteSendTime, timestamp, y);
544 }
545 }
546 }
547 }
548 }
549 }
550
551 // Set labels and put in graph.
552 for (auto& kv : time_series) {
553 kv.second.label = SsrcToString(kv.first.GetSsrc());
554 kv.second.style = LINE_GRAPH;
555 plot->series.push_back(std::move(kv.second));
556 }
557
558 plot->xaxis_min = kDefaultXMin;
559 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
560 plot->xaxis_label = "Time (s)";
561 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
562 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
563 plot->yaxis_label = "Latency change (ms)";
564 plot->title = "Accumulated network latency change";
565 }
566
567 // Plot the total bandwidth used by all RTP streams.
568 void EventLogAnalyzer::CreateTotalBitrateGraph(
569 PacketDirection desired_direction,
570 Plot* plot) {
571 struct TimestampSize {
572 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
573 uint64_t timestamp;
574 size_t size;
575 };
576 std::vector<TimestampSize> packets;
577
578 PacketDirection direction;
579 size_t total_length;
580
581 // Extract timestamps and sizes for the relevant packets.
582 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
583 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
584 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
585 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
586 &total_length);
587 if (direction == desired_direction) {
588 uint64_t timestamp = parsed_log_.GetTimestamp(i);
589 packets.push_back(TimestampSize(timestamp, total_length));
590 }
591 }
592 }
593
594 size_t window_index_begin = 0;
595 size_t window_index_end = 0;
596 size_t bytes_in_window = 0;
597 float max_y = 0;
598
599 // Calculate a moving average of the bitrate and store in a TimeSeries.
600 plot->series.push_back(TimeSeries());
601 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
602 while (window_index_end < packets.size() &&
603 packets[window_index_end].timestamp < time) {
604 bytes_in_window += packets[window_index_end].size;
605 window_index_end++;
606 }
607 while (window_index_begin < packets.size() &&
608 packets[window_index_begin].timestamp < time - window_duration_) {
609 bytes_in_window -= packets[window_index_begin].size;
610 window_index_begin++;
611 }
612 RTC_DCHECK_LE(0ul, bytes_in_window);
613 float window_duration_in_seconds =
614 static_cast<float>(window_duration_) / 1000000;
615 float x = static_cast<float>(time - begin_time_) / 1000000;
616 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
617 max_y = std::max(max_y, y);
618 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
619 }
620
621 // Set labels.
622 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
623 plot->series.back().label = "Incoming bitrate";
624 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
625 plot->series.back().label = "Outgoing bitrate";
626 }
627 plot->series.back().style = LINE_GRAPH;
628
629 plot->xaxis_min = kDefaultXMin;
630 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
631 plot->xaxis_label = "Time (s)";
632 plot->yaxis_min = kDefaultYMin;
633 plot->yaxis_max = max_y * kYMargin;
634 plot->yaxis_label = "Bitrate (kbps)";
635 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
636 plot->title = "Incoming RTP bitrate";
637 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
638 plot->title = "Outgoing RTP bitrate";
639 }
640 }
641
642 // For each SSRC, plot the bandwidth used by that stream.
643 void EventLogAnalyzer::CreateStreamBitrateGraph(
644 PacketDirection desired_direction,
645 Plot* plot) {
646 struct TimestampSize {
647 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
648 uint64_t timestamp;
649 size_t size;
650 };
651 std::map<uint32_t, std::vector<TimestampSize> > packets;
652
653 PacketDirection direction;
654 MediaType media_type;
655 uint8_t header[IP_PACKET_SIZE];
656 size_t header_length, total_length;
657
658 // Extract timestamps and sizes for the relevant packets.
659 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
660 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
661 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
662 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
663 &header_length, &total_length);
664 if (direction == desired_direction) {
665 // Parse header to get SSRC.
666 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
667 RTPHeader parsed_header;
668 rtp_parser.Parse(&parsed_header);
669 // Filter on SSRC.
670 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
671 uint64_t timestamp = parsed_log_.GetTimestamp(i);
672 packets[parsed_header.ssrc].push_back(
673 TimestampSize(timestamp, total_length));
674 }
675 }
676 }
677 }
678
679 float max_y = 0;
680
681 for (auto& kv : packets) {
682 size_t window_index_begin = 0;
683 size_t window_index_end = 0;
684 size_t bytes_in_window = 0;
685
686 // Calculate a moving average of the bitrate and store in a TimeSeries.
687 plot->series.push_back(TimeSeries());
688 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
689 while (window_index_end < kv.second.size() &&
690 kv.second[window_index_end].timestamp < time) {
691 bytes_in_window += kv.second[window_index_end].size;
692 window_index_end++;
693 }
694 while (window_index_begin < kv.second.size() &&
695 kv.second[window_index_begin].timestamp <
696 time - window_duration_) {
697 bytes_in_window -= kv.second[window_index_begin].size;
698 window_index_begin++;
699 }
700 RTC_DCHECK_LE(0ul, bytes_in_window);
701 float window_duration_in_seconds =
702 static_cast<float>(window_duration_) / 1000000;
703 float x = static_cast<float>(time - begin_time_) / 1000000;
704 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
705 max_y = std::max(max_y, y);
706 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
707 }
708
709 // Set labels.
710 plot->series.back().label = SsrcToString(kv.first);
711 plot->series.back().style = LINE_GRAPH;
712 }
713
714 plot->xaxis_min = kDefaultXMin;
715 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
716 plot->xaxis_label = "Time (s)";
717 plot->yaxis_min = kDefaultYMin;
718 plot->yaxis_max = max_y * kYMargin;
719 plot->yaxis_label = "Bitrate (kbps)";
720 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
721 plot->title = "Incoming bitrate per stream";
722 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
723 plot->title = "Outgoing bitrate per stream";
724 }
725 }
726
727 } // namespace plotting
728 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698