Chromium Code Reviews

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: Review comments Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
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) {
stefan-webrtc 2016/06/29 11:13:05 Should we make this a method on RTPHeader instead?
terelius 2016/07/01 16:54:45 I have no opinion on this, but this function does
stefan-webrtc 2016/07/05 08:58:11 I think it's fine as is for now.
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 // There could conceivably be e.g. an incoming and an outgoing stream with
90 // the same SSRC. To get a unique identifier for each stream we append some
91 // other values to the SSRC.
92 uint64_t GetStreamId(uint32_t ssrc,
stefan-webrtc 2016/06/29 11:13:04 Wouldn't it be nicer to make stream id a comparabl
terelius 2016/07/01 16:54:45 Done. However, the advantage of precomputing a 64-
93 webrtc::PacketDirection direction,
94 webrtc::MediaType media_type) {
95 uint64_t stream = ssrc;
96 stream = (stream << 8) +
97 static_cast<uint64_t>(direction == webrtc::kIncomingPacket);
98 stream = (stream << 8) + static_cast<uint64_t>(media_type);
99 return stream;
100 }
101
102 uint32_t GetSsrcFromStreamId(uint64_t stream) {
103 return stream >> 16;
104 }
105
106 const double kXMargin = 1.02;
107 const double kYMargin = 1.1;
108 const double kDefaultXMin = -1;
109 const double kDefaultYMin = -1;
110
111 } // namespace
112
113 namespace webrtc {
114 namespace plotting {
115
116 EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log,
117 bool extra_info)
118 : parsed_log_(log), extra_point_info_(extra_info) {
119 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
120 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
121 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
122 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
123 if (event_type == ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT)
124 continue;
125 if (event_type == ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT)
126 continue;
127 if (event_type == ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT)
128 continue;
129 if (event_type == ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT)
130 continue;
131 uint64_t timestamp = parsed_log_.GetTimestamp(i);
132 first_timestamp = std::min(first_timestamp, timestamp);
133 last_timestamp = std::max(last_timestamp, timestamp);
134 }
135 if (last_timestamp < first_timestamp) {
136 // No useful events in the log.
137 first_timestamp = last_timestamp = 0;
138 }
139 begin_time_ = first_timestamp;
140 end_time_ = last_timestamp;
141 window_duration_ = 250000;
142 step_ = 10000;
stefan-webrtc 2016/06/29 11:13:04 These two members should be set in the initializer
terelius 2016/07/01 16:54:45 Done.
143 }
144
145 void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
146 Plot* plot) {
147 std::map<uint32_t, TimeSeries> time_series;
148
149 PacketDirection direction;
150 MediaType media_type;
151 uint8_t header[IP_PACKET_SIZE];
152 size_t header_length, total_length;
153 float max_y = 0;
154
155 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
156 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
157 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
158 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
159 &header_length, &total_length);
160 if (direction == desired_direction) {
161 // Parse header to get SSRC.
162 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
163 RTPHeader parsed_header;
164 rtp_parser.Parse(&parsed_header);
165 // Filter on SSRC.
166 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
167 uint64_t timestamp = parsed_log_.GetTimestamp(i);
168 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
169 float y = total_length;
170 max_y = std::max(max_y, y);
171 std::string message;
172 if (extra_point_info_) {
173 message = HeaderToString(parsed_header);
174 }
175 time_series[parsed_header.ssrc].points.push_back(
176 TimeSeriesPoint(x, y, message));
177 }
178 }
179 }
180 }
181
182 // Set labels and put in graph.
183 for (auto& kv : time_series) {
184 kv.second.label = SsrcToString(kv.first);
185 kv.second.style = BAR_GRAPH;
186 plot->series.push_back(TimeSeries());
187 plot->series.back().swap(kv.second);
stefan-webrtc 2016/06/29 11:13:04 Why not simply push_back(kv.second)?
terelius 2016/07/01 16:54:45 Because I don't want to copy all the data in the T
188 }
189
190 plot->xaxis_min = kDefaultXMin;
191 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
192 plot->xaxis_label = "Time (s)";
193 plot->yaxis_min = kDefaultYMin;
194 plot->yaxis_max = max_y * kYMargin;
195 plot->yaxis_label = "Packet size (bytes)";
196 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
197 plot->title = "Incoming RTP packets";
198 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
199 plot->title = "Outgoing RTP packets";
200 }
201 }
202
203 // For each SSRC, plot the time between the consecutive playouts.
204 void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
205 std::map<uint32_t, TimeSeries> time_series;
206 std::map<uint32_t, uint64_t> last_playout;
207
208 uint32_t ssrc;
209 float max_y = 0;
210
211 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
212 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
213 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
214 parsed_log_.GetAudioPlayout(i, &ssrc);
215 uint64_t timestamp = parsed_log_.GetTimestamp(i);
216 if (MatchingSsrc(ssrc, desired_ssrc_)) {
217 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
218 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
219 if (time_series[ssrc].points.size() == 0) {
220 // There were no previusly logged playout for this SSRC.
221 // Generate a point, but place it on the x-axis.
222 y = 0;
223 }
224 max_y = std::max(max_y, y);
225 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y, ""));
226 last_playout[ssrc] = timestamp;
227 }
228 }
229 }
230
231 // Set labels and put in graph.
232 for (auto& kv : time_series) {
233 kv.second.label = SsrcToString(kv.first);
234 kv.second.style = BAR_GRAPH;
235 plot->series.push_back(TimeSeries());
236 plot->series.back().swap(kv.second);
237 }
238
239 plot->xaxis_min = kDefaultXMin;
240 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
241 plot->xaxis_label = "Time (s)";
242 plot->yaxis_min = kDefaultYMin;
243 plot->yaxis_max = max_y * kYMargin;
244 plot->yaxis_label = "Time since last playout (ms)";
245 plot->title = "Audio playout";
246 }
247
248 // For each SSRC, plot the time between the consecutive playouts.
249 void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
250 std::map<uint32_t, TimeSeries> time_series;
251 std::map<uint32_t, uint16_t> last_seqno;
252
253 PacketDirection direction;
254 MediaType media_type;
255 uint8_t header[IP_PACKET_SIZE];
256 size_t header_length, total_length;
257
258 int max_y = 1;
259 int min_y = 0;
260
261 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
262 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
263 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
264 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
265 &header_length, &total_length);
266 uint64_t timestamp = parsed_log_.GetTimestamp(i);
267 if (direction == PacketDirection::kIncomingPacket) {
268 // Parse header to get SSRC.
269 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
270 RTPHeader parsed_header;
271 rtp_parser.Parse(&parsed_header);
272 // Filter on SSRC.
273 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
274 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
275 int y = WrappingDifference(parsed_header.sequenceNumber,
276 last_seqno[parsed_header.ssrc], 1ul << 16);
277 if (time_series[parsed_header.ssrc].points.size() == 0) {
278 // There were no previusly logged playout for this SSRC.
279 // Generate a point, but place it on the x-axis.
280 y = 0;
281 }
282 max_y = std::max(max_y, y);
283 min_y = std::min(min_y, y);
284 time_series[parsed_header.ssrc].points.push_back(
285 TimeSeriesPoint(x, y, ""));
286 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
287 }
288 }
289 }
290 }
291
292 // Set labels and put in graph.
293 for (auto& kv : time_series) {
294 kv.second.label = SsrcToString(kv.first);
295 kv.second.style = BAR_GRAPH;
296 plot->series.push_back(TimeSeries());
297 plot->series.back().swap(kv.second);
298 }
299
300 plot->xaxis_min = kDefaultXMin;
301 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
302 plot->xaxis_label = "Time (s)";
303 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
304 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
305 plot->yaxis_label = "Difference since last packet";
306 plot->title = "Sequence number";
307 }
308
309 void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
310 // Maps a stream identifier consisting of ssrc, direction and MediaType
311 // to the header extensions used by that stream,
312 std::map<uint64_t, RtpHeaderExtensionMap> extension_maps;
313
314 struct SendReceiveTime {
315 SendReceiveTime() = default;
316 SendReceiveTime(uint32_t send_time, uint64_t recv_time)
317 : absolute_send_time(send_time), receive_timestamp(recv_time) {}
318 uint32_t absolute_send_time; // 24-bit value in units of 2^-18 seconds.
319 uint64_t receive_timestamp; // In microseconds.
320 };
321 std::map<uint64_t, SendReceiveTime> last_packet;
322 std::map<uint64_t, TimeSeries> time_series;
323
324 PacketDirection direction;
325 MediaType media_type;
326 uint8_t header[IP_PACKET_SIZE];
327 size_t header_length, total_length;
328
329 double max_y = 10;
330 double min_y = 0;
331
332 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
333 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
334 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
stefan-webrtc 2016/06/29 11:13:04 Handling of config events seems to happen in many
terelius 2016/07/01 16:54:45 Config events are used in CreateDelayChangeGraph a
stefan-webrtc 2016/07/05 08:58:11 If it becomes better in a follow up I think I'm ok
335 VideoReceiveStream::Config config(nullptr);
336 parsed_log_.GetVideoReceiveConfig(i, &config);
337 uint64_t stream = GetStreamId(config.rtp.remote_ssrc, kIncomingPacket,
338 MediaType::VIDEO);
339 extension_maps[stream].Erase();
340 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
341 const std::string& extension = config.rtp.extensions[j].uri;
342 int id = config.rtp.extensions[j].id;
343 extension_maps[stream].Register(StringToRtpExtensionType(extension),
344 id);
345 }
346 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
347 VideoSendStream::Config config(nullptr);
348 parsed_log_.GetVideoSendConfig(i, &config);
349 for (auto ssrc : config.rtp.ssrcs) {
350 uint64_t stream = GetStreamId(ssrc, kIncomingPacket, MediaType::VIDEO);
351 extension_maps[stream].Erase();
352 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
353 const std::string& extension = config.rtp.extensions[j].uri;
354 int id = config.rtp.extensions[j].id;
355 extension_maps[stream].Register(StringToRtpExtensionType(extension),
356 id);
357 }
358 }
359 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
360 AudioReceiveStream::Config config;
361 // TODO(terelius): Parse the audio configs once we have them
362 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
363 AudioSendStream::Config config(nullptr);
364 // TODO(terelius): Parse the audio configs once we have them
365 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
366 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
367 &header_length, &total_length);
368 if (direction == kIncomingPacket) {
369 // Parse header to get SSRC.
370 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
371 RTPHeader parsed_header;
372 rtp_parser.Parse(&parsed_header);
373 // Filter on SSRC.
374 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
stefan-webrtc 2016/06/29 11:13:05 Maybe we could have a method similar to: GetEvents
terelius 2016/07/01 16:54:45 The follow up CL that caches a map from streams to
stefan-webrtc 2016/07/05 08:58:11 Acknowledged.
375 uint64_t stream =
376 GetStreamId(parsed_header.ssrc, direction, media_type);
377 // Look up the extension_map and parse it again to get the extensions.
378 if (extension_maps.count(stream) == 1) {
379 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
380 rtp_parser.Parse(&parsed_header, extension_map);
381 if (parsed_header.extension.hasAbsoluteSendTime) {
382 uint64_t timestamp = parsed_log_.GetTimestamp(i);
383 int64_t send_time_diff = WrappingDifference(
384 parsed_header.extension.absoluteSendTime,
385 last_packet[stream].absolute_send_time, 1ul << 24);
386 int64_t recv_time_diff =
387 timestamp - last_packet[stream].receive_timestamp;
388
389 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
390 double y = static_cast<double>(
391 recv_time_diff -
392 AbsSendTimeToMicroseconds(send_time_diff)) /
393 1000;
394 if (time_series[stream].points.size() == 0) {
395 // There were no previusly logged playout for this SSRC.
396 // Generate a point, but place it on the x-axis.
397 y = 0;
398 }
399 max_y = std::max(max_y, y);
400 min_y = std::min(min_y, y);
401 time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
402 last_packet[stream] = SendReceiveTime(
403 parsed_header.extension.absoluteSendTime, timestamp);
404 }
405 }
406 }
407 }
408 }
409 }
410
411 // Set labels and put in graph.
412 for (auto& kv : time_series) {
413 kv.second.label = SsrcToString(GetSsrcFromStreamId(kv.first));
414 kv.second.style = BAR_GRAPH;
415 plot->series.push_back(TimeSeries());
416 plot->series.back().swap(kv.second);
417 }
418
419 plot->xaxis_min = kDefaultXMin;
420 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
421 plot->xaxis_label = "Time (s)";
422 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
423 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
424 plot->yaxis_label = "Latency change (ms)";
425 plot->title = "Network latency change between consecutive packets";
426 }
427
428 void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
429 // TODO(terelius): Refactor
430
431 // Maps a stream identifier consisting of ssrc, direction and MediaType
432 // to the header extensions used by that stream.
433 std::map<uint64_t, RtpHeaderExtensionMap> extension_maps;
434
435 struct SendReceiveTime {
436 SendReceiveTime() = default;
437 SendReceiveTime(uint32_t send_time, uint64_t recv_time, double accumulated)
438 : absolute_send_time(send_time),
439 receive_timestamp(recv_time),
440 accumulated_delay(accumulated) {}
441 uint32_t absolute_send_time; // 24-bit value in units of 2^-18 seconds.
442 uint64_t receive_timestamp; // In microseconds.
443 double accumulated_delay; // In milliseconds.
444 };
445 std::map<uint64_t, SendReceiveTime> last_packet;
446 std::map<uint64_t, TimeSeries> time_series;
447
448 PacketDirection direction;
449 MediaType media_type;
450 uint8_t header[IP_PACKET_SIZE];
451 size_t header_length, total_length;
452
453 double max_y = 10;
454 double min_y = 0;
455
456 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
457 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
458 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
459 VideoReceiveStream::Config config(nullptr);
460 parsed_log_.GetVideoReceiveConfig(i, &config);
461 uint64_t stream = GetStreamId(config.rtp.remote_ssrc, kIncomingPacket,
462 MediaType::VIDEO);
463 extension_maps[stream].Erase();
464 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
465 const std::string& extension = config.rtp.extensions[j].uri;
466 int id = config.rtp.extensions[j].id;
467 extension_maps[stream].Register(StringToRtpExtensionType(extension),
468 id);
469 }
470 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
471 VideoSendStream::Config config(nullptr);
472 parsed_log_.GetVideoSendConfig(i, &config);
473 for (auto ssrc : config.rtp.ssrcs) {
474 uint64_t stream = GetStreamId(ssrc, kIncomingPacket, MediaType::VIDEO);
475 extension_maps[stream].Erase();
476 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
477 const std::string& extension = config.rtp.extensions[j].uri;
478 int id = config.rtp.extensions[j].id;
479 extension_maps[stream].Register(StringToRtpExtensionType(extension),
480 id);
481 }
482 }
483 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
484 AudioReceiveStream::Config config;
485 // TODO(terelius): Parse the audio configs once we have them
486 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
487 AudioSendStream::Config config(nullptr);
488 // TODO(terelius): Parse the audio configs once we have them
489 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
490 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
491 &header_length, &total_length);
492 if (direction == kIncomingPacket) {
493 // Parse header to get SSRC.
494 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
495 RTPHeader parsed_header;
496 rtp_parser.Parse(&parsed_header);
497 // Filter on SSRC.
498 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
499 uint64_t stream =
500 GetStreamId(parsed_header.ssrc, direction, media_type);
501 // Look up the extension_map and parse it again to get the extensions.
502 if (extension_maps.count(stream) == 1) {
503 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
504 rtp_parser.Parse(&parsed_header, extension_map);
505 if (parsed_header.extension.hasAbsoluteSendTime) {
506 uint64_t timestamp = parsed_log_.GetTimestamp(i);
507 int64_t send_time_diff = WrappingDifference(
508 parsed_header.extension.absoluteSendTime,
509 last_packet[stream].absolute_send_time, 1ul << 24);
510 int64_t recv_time_diff =
511 timestamp - last_packet[stream].receive_timestamp;
512
513 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
514 double y = last_packet[stream].accumulated_delay +
515 static_cast<double>(
516 recv_time_diff -
517 AbsSendTimeToMicroseconds(send_time_diff)) /
518 1000;
519 if (time_series[stream].points.size() == 0) {
520 // There were no previusly logged playout for this SSRC.
521 // Generate a point, but place it on the x-axis.
522 y = 0;
523 }
524 max_y = std::max(max_y, y);
525 min_y = std::min(min_y, y);
526 time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
527 last_packet[stream] = SendReceiveTime(
528 parsed_header.extension.absoluteSendTime, timestamp, y);
529 }
530 }
531 }
532 }
533 }
534 }
535
536 // Set labels and put in graph.
537 for (auto& kv : time_series) {
538 kv.second.label = SsrcToString(GetSsrcFromStreamId(kv.first));
539 kv.second.style = LINE_GRAPH;
540 plot->series.push_back(TimeSeries());
541 plot->series.back().swap(kv.second);
542 }
543
544 plot->xaxis_min = kDefaultXMin;
545 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
546 plot->xaxis_label = "Time (s)";
547 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y);
548 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y);
549 plot->yaxis_label = "Latency change (ms)";
550 plot->title = "Accumulated network latency change";
551 }
552
553 // Plot the total bandwitch used by all RTP streams.
554 void EventLogAnalyzer::CreateTotalBitrateGraph(
555 PacketDirection desired_direction,
556 Plot* plot) {
557 struct TimestampSize {
558 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
559 uint64_t timestamp;
560 size_t size;
561 };
562 std::vector<TimestampSize> packets;
563
564 PacketDirection direction;
565 size_t total_length;
566
567 // Extract timestamps and sizes for the relevant packets.
568 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
569 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
570 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
571 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
572 &total_length);
573 if (direction == desired_direction) {
574 uint64_t timestamp = parsed_log_.GetTimestamp(i);
575 packets.push_back(TimestampSize(timestamp, total_length));
576 }
577 }
578 }
579
580 size_t window_index_begin = 0;
581 size_t window_index_end = 0;
582 size_t bytes_in_window = 0;
583 float max_y = 0;
584
585 // Calculate a moving average of the bitrate and store in a TimeSeries.
586 plot->series.push_back(TimeSeries());
587 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
588 while (window_index_end < packets.size() &&
589 packets[window_index_end].timestamp < time) {
590 bytes_in_window += packets[window_index_end].size;
591 window_index_end++;
592 }
593 while (window_index_begin < packets.size() &&
594 packets[window_index_begin].timestamp < time - window_duration_) {
595 bytes_in_window -= packets[window_index_begin].size;
596 window_index_begin++;
597 }
598 RTC_DCHECK_LE(0ul, bytes_in_window);
599 float window_duration_in_seconds =
600 static_cast<float>(window_duration_) / 1000000;
601 float x = static_cast<float>(time - begin_time_) / 1000000;
602 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
603 max_y = std::max(max_y, y);
604 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
605 }
606
607 // Set labels.
608 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
609 plot->series.back().label = "Incoming bitrate";
610 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
611 plot->series.back().label = "Outgoing bitrate";
612 }
613 plot->series.back().style = LINE_GRAPH;
614
615 plot->xaxis_min = kDefaultXMin;
616 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
617 plot->xaxis_label = "Time (s)";
618 plot->yaxis_min = kDefaultYMin;
619 plot->yaxis_max = max_y * kYMargin;
620 plot->yaxis_label = "Bitrate (kbps)";
621 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
622 plot->title = "Incoming RTP bitrate";
623 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
624 plot->title = "Outgoing RTP bitrate";
625 }
626 }
627
628 // For each SSRC, plot the bandwitch used by that stream.
stefan-webrtc 2016/06/29 11:13:04 bandwidth
terelius 2016/07/01 16:54:45 Done. :)
stefan-webrtc 2016/07/05 08:58:11 Nope, still there... :)
terelius 2016/07/06 15:15:12 Done.
629 void EventLogAnalyzer::CreateStreamBitrateGraph(
630 PacketDirection desired_direction,
631 Plot* plot) {
632 struct TimestampSize {
633 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
634 uint64_t timestamp;
635 size_t size;
636 };
637 std::map<uint32_t, std::vector<TimestampSize> > packets;
638
639 PacketDirection direction;
640 MediaType media_type;
641 uint8_t header[IP_PACKET_SIZE];
642 size_t header_length, total_length;
643
644 // Extract timestamps and sizes for the relevant packets.
645 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
646 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
647 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
648 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
649 &header_length, &total_length);
650 if (direction == desired_direction) {
651 // Parse header to get SSRC.
652 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
653 RTPHeader parsed_header;
654 rtp_parser.Parse(&parsed_header);
655 // Filter on SSRC.
656 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
657 uint64_t timestamp = parsed_log_.GetTimestamp(i);
658 packets[parsed_header.ssrc].push_back(
659 TimestampSize(timestamp, total_length));
660 }
661 }
662 }
663 }
664
665 float max_y = 0;
666
667 for (auto& kv : packets) {
668 size_t window_index_begin = 0;
669 size_t window_index_end = 0;
670 size_t bytes_in_window = 0;
671
672 // Calculate a moving average of the bitrate and store in a TimeSeries.
673 plot->series.push_back(TimeSeries());
674 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
675 while (window_index_end < kv.second.size() &&
676 kv.second[window_index_end].timestamp < time) {
677 bytes_in_window += kv.second[window_index_end].size;
678 window_index_end++;
679 }
680 while (window_index_begin < kv.second.size() &&
681 kv.second[window_index_begin].timestamp <
682 time - window_duration_) {
683 bytes_in_window -= kv.second[window_index_begin].size;
684 window_index_begin++;
685 }
686 RTC_DCHECK_LE(0ul, bytes_in_window);
687 float window_duration_in_seconds =
688 static_cast<float>(window_duration_) / 1000000;
689 float x = static_cast<float>(time - begin_time_) / 1000000;
690 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
691 max_y = std::max(max_y, y);
692 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
693 }
694
695 // Set labels.
696 plot->series.back().label = SsrcToString(kv.first);
697 plot->series.back().style = LINE_GRAPH;
698 }
699
700 plot->xaxis_min = kDefaultXMin;
701 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin;
702 plot->xaxis_label = "Time (s)";
703 plot->yaxis_min = kDefaultYMin;
704 plot->yaxis_max = max_y * kYMargin;
705 plot->yaxis_label = "Bitrate (kbps)";
706 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
707 plot->title = "Incoming bitrate per stream";
708 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
709 plot->title = "Outgoing bitrate per stream";
710 }
711 }
712
713 } // namespace plotting
714 } // namespace webrtc
OLDNEW

Powered by Google App Engine