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

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: Created 4 years, 7 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) {
stefan-webrtc 2016/05/31 18:53:39 Ssrc
terelius 2016/06/14 13:18:48 Done.
44 std::stringstream ss;
45 ss << "SSRC " << ssrc;
46 return ss.str();
47 }
48
49 // Checks whether the an SSRC is contained in the list of desired SSRCs.
stefan-webrtc 2016/05/31 18:53:40 -the
terelius 2016/06/14 13:18:48 Done.
50 // Note that an empty SSRC list counts matches every SSRC.
stefan-webrtc 2016/05/31 18:53:39 -counts?
terelius 2016/06/14 13:18:48 Done.
51 bool MatchingSSRC(uint32_t ssrc, const std::vector<uint32_t>& desired_ssrc) {
stefan-webrtc 2016/05/31 18:53:40 MatchingSsrc
terelius 2016/06/14 13:18:48 Done.
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 second and then multiply by 1000000 to convert to microseconds.
aleloi 2016/06/08 11:44:20 second -> seconds?
terelius 2016/06/14 13:18:48 Done.
62 static const double kTimestampToMicroSec =
aleloi 2016/06/08 11:44:20 constexpr?
terelius 2016/06/14 13:18:49 Done.
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) {
stefan-webrtc 2016/05/31 18:53:40 Can you use philipel's mod_ops.h for this? https:/
aleloi 2016/06/08 11:44:20 Is there a guarantee that this function is only gi
terelius 2016/06/14 13:18:48 Yes, the numbers should always be less than modulu
terelius 2016/06/14 13:18:48 No, at least not off the shelf. I want the signed
73 RTC_DCHECK_LE(1, modulus);
74 int64_t difference =
75 static_cast<int64_t>(later) - static_cast<int64_t>(earlier);
76 int64_t max_difference = modulus / 2;
77 int64_t min_difference = max_difference - modulus + 1;
78 if (difference > max_difference) {
79 difference -= modulus;
80 }
81 if (difference < min_difference) {
82 difference += modulus;
83 }
84 return difference;
85 }
86
87 // typedef StreamID uint64_t;
stefan-webrtc 2016/05/31 18:53:40 Remove?
terelius 2016/06/14 13:18:49 Done. I was considering whether a typedef would ma
88 uint64_t GetStreamID(uint32_t ssrc,
stefan-webrtc 2016/05/31 18:53:40 GetStreamId
aleloi 2016/06/08 11:44:20 I'd like a comment that explains what StreamID is.
terelius 2016/06/14 13:18:49 I've added a comment, but this isn't a defined con
terelius 2016/06/14 13:18:49 Done.
89 webrtc::PacketDirection direction,
90 webrtc::MediaType media_type) {
91 uint64_t stream = ssrc;
92 stream = (stream << 8) +
93 static_cast<uint64_t>(direction == webrtc::kIncomingPacket);
94 // stream_id = stream_id
stefan-webrtc 2016/05/31 18:53:40 Remove
terelius 2016/06/14 13:18:48 Done.
95 stream = (stream << 8) + static_cast<uint64_t>(media_type);
96 return stream;
97 }
98
99 uint32_t GetSsrcFromStreamID(uint64_t stream) {
stefan-webrtc 2016/05/31 18:53:40 GetSsrcFromStreamId
terelius 2016/06/14 13:18:48 Done.
100 return stream >> 16;
101 }
102
103 } // namespace
104
105 namespace webrtc {
106 namespace plotting {
107
108 EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log,
109 bool extra_info)
110 : parsed_log(log), extra_point_info(extra_info) {
111 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
112 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
113 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
114 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
115 if (event_type == ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT)
116 continue;
117 if (event_type == ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT)
118 continue;
119 if (event_type == ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT)
120 continue;
121 if (event_type == ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT)
122 continue;
123 uint64_t timestamp = parsed_log.GetTimestamp(i);
124 if (timestamp < first_timestamp)
aleloi 2016/06/08 11:44:20 You use if-statements here and the ternary ?: oper
terelius 2016/06/14 13:18:48 Good point. Done.
125 first_timestamp = timestamp;
126 if (timestamp > last_timestamp)
127 last_timestamp = timestamp;
128 }
129 if (last_timestamp < first_timestamp) {
130 // No useful events in the log
stefan-webrtc 2016/05/31 18:53:40 End with .
terelius 2016/06/14 13:18:48 Done.
131 first_timestamp = last_timestamp = 0;
132 }
133 begin_time = first_timestamp;
134 end_time = last_timestamp;
135 window_duration = 250000;
136 step = 10000;
137 }
138
139 void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
140 Plot* plot) {
141 std::map<uint32_t, TimeSeries> time_series;
142
143 std::string message;
aleloi 2016/06/08 11:44:20 It looks scary when message is defined outside of
terelius 2016/06/14 13:18:49 This is a rather artificial scenario. The code is
144 PacketDirection direction;
145 MediaType media_type;
146 uint8_t header[IP_PACKET_SIZE];
147 size_t header_length, total_length;
148 float max_y = 0;
149
150 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
151 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
152 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
153 parsed_log.GetRtpHeader(i, &direction, &media_type, header,
154 &header_length, &total_length);
155 if (direction == desired_direction) {
156 // Parse header to get SSRC
157 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
158 RTPHeader parsed_header;
159 rtp_parser.Parse(&parsed_header);
160 // Filter on SSRC
161 if (MatchingSSRC(parsed_header.ssrc, desired_ssrc)) {
162 uint64_t timestamp = parsed_log.GetTimestamp(i);
163 float x = static_cast<float>(timestamp - begin_time) / 1000000;
164 float y = total_length;
165 max_y = (y > max_y) ? y : max_y;
aleloi 2016/06/08 11:44:20 Make it consistent with the other min/max calculat
terelius 2016/06/14 13:18:48 Done.
166 if (extra_point_info) {
167 message = HeaderToString(parsed_header);
168 }
169 time_series[parsed_header.ssrc].points.push_back(
170 TimeSeriesPoint(x, y, message));
171 }
172 }
173 }
174 }
175
176 // Set labels and put in graph
stefan-webrtc 2016/05/31 18:53:40 End with .
terelius 2016/06/14 13:18:48 Done.
177 for (auto& kv : time_series) {
178 kv.second.label = SSRCToString(kv.first);
179 kv.second.style = BAR_GRAPH;
180 plot->series.push_back(TimeSeries());
181 plot->series.back().swap(kv.second);
182 }
183
184 plot->xaxis_min = -1;
185 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
186 plot->xaxis_label = "Time (s)";
187 plot->yaxis_min = -1;
188 plot->yaxis_max = max_y * 1.1;
aleloi 2016/06/08 11:44:20 Make 1.1, 1.02, -1 constants.
terelius 2016/06/14 13:18:49 Done.
189 plot->yaxis_label = "Packet size (bytes)";
190 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
191 plot->title = "Incoming RTP packets";
192 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
193 plot->title = "Outgoing RTP packets";
194 }
195 }
196
197 // For each SSRC, plot the time between the consecutive playouts.
198 void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
199 std::map<uint32_t, TimeSeries> time_series;
200 std::map<uint32_t, uint64_t> last_playout;
201
202 uint32_t ssrc;
203 float max_y = 0;
204
205 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
206 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
207 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
208 parsed_log.GetAudioPlayout(i, &ssrc);
209 uint64_t timestamp = parsed_log.GetTimestamp(i);
210 if (MatchingSSRC(ssrc, desired_ssrc)) {
211 float x = static_cast<float>(timestamp - begin_time) / 1000000;
212 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
213 if (time_series[ssrc].points.size() == 0) {
214 // There were no previusly logged playout for this SSRC.
215 // Generate a point, but place it on the x-axis.
216 y = 0;
217 }
218 max_y = (y > max_y) ? y : max_y;
219 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y, ""));
220 last_playout[ssrc] = timestamp;
221 }
222 }
223 }
224
225 // Set labels and put in graph
226 for (auto& kv : time_series) {
227 kv.second.label = SSRCToString(kv.first);
228 kv.second.style = BAR_GRAPH;
229 plot->series.push_back(TimeSeries());
230 plot->series.back().swap(kv.second);
231 }
232
233 plot->xaxis_min = -1;
234 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
stefan-webrtc 2016/05/31 18:53:40 Maybe name the constant 1.02 "kXMargin" or somethi
terelius 2016/06/14 13:18:48 Done.
235 plot->xaxis_label = "Time (s)";
236 plot->yaxis_min = -1;
237 plot->yaxis_max = max_y * 1.1;
stefan-webrtc 2016/05/31 18:53:40 kYMargin?
terelius 2016/06/14 13:18:49 Done.
238 plot->yaxis_label = "Time since last playout (ms)";
239 plot->title = "Audio playout";
240 }
241
242 // For each SSRC, plot the time between the consecutive playouts.
243 void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
244 std::map<uint32_t, TimeSeries> time_series;
245 std::map<uint32_t, uint16_t> last_seqno;
246
247 PacketDirection direction;
248 MediaType media_type;
249 uint8_t header[IP_PACKET_SIZE];
250 size_t header_length, total_length;
251
252 int max_y = 1;
253 int min_y = 0;
254
255 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
256 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
257 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
258 parsed_log.GetRtpHeader(i, &direction, &media_type, header,
259 &header_length, &total_length);
260 uint64_t timestamp = parsed_log.GetTimestamp(i);
261 if (direction == PacketDirection::kIncomingPacket) {
262 // Parse header to get SSRC.
263 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
264 RTPHeader parsed_header;
265 rtp_parser.Parse(&parsed_header);
266 // Filter on SSRC.
267 if (MatchingSSRC(parsed_header.ssrc, desired_ssrc)) {
268 float x = static_cast<float>(timestamp - begin_time) / 1000000;
269 int y = WrappingDifference(parsed_header.sequenceNumber,
270 last_seqno[parsed_header.ssrc], 1ul << 16);
271 if (time_series[parsed_header.ssrc].points.size() == 0) {
272 // There were no previusly logged playout for this SSRC.
273 // Generate a point, but place it on the x-axis.
274 y = 0;
275 }
276 max_y = (y > max_y) ? y : max_y;
277 min_y = (y < min_y) ? y : min_y;
278 time_series[parsed_header.ssrc].points.push_back(
279 TimeSeriesPoint(x, y, ""));
280 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
281 }
282 }
283 }
284 }
285
286 // Set labels and put in graph.
287 for (auto& kv : time_series) {
288 kv.second.label = SSRCToString(kv.first);
289 kv.second.style = BAR_GRAPH;
290 plot->series.push_back(TimeSeries());
291 plot->series.back().swap(kv.second);
292 }
293
294 plot->xaxis_min = -1;
295 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
296 plot->xaxis_label = "Time (s)";
297 plot->yaxis_min = min_y - 0.05 * (max_y - min_y);
298 plot->yaxis_max = max_y + 0.05 * (max_y - min_y);
299 plot->yaxis_label = "Difference since last packet";
300 plot->title = "Sequence number";
301 }
302
303 void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
304 // Maps a stream identifier consisting of ssrc, direction and MediaType
305 // to the header extensions used by that stream,
306 std::map<uint64_t, RtpHeaderExtensionMap> extension_maps;
307
308 struct SendReceiveTime {
309 SendReceiveTime() = default;
310 SendReceiveTime(uint32_t send_time, uint64_t recv_time)
311 : absoluteSendTime(send_time), receiveTimestamp(recv_time) {}
312 uint32_t absoluteSendTime; // 24-bit value in units of 2^-18 seconds
313 uint64_t receiveTimestamp; // In microseconds
314 };
315 std::map<uint64_t, SendReceiveTime> last_packet;
316 std::map<uint64_t, TimeSeries> time_series;
317
318 PacketDirection direction;
319 MediaType media_type;
320 uint8_t header[IP_PACKET_SIZE];
321 size_t header_length, total_length;
322
323 float max_y = 10;
324 float min_y = 0;
325
326 //
stefan-webrtc 2016/05/31 18:53:40 Remove?
terelius 2016/06/14 13:18:48 Done.
327 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
328 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
329 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
330 VideoReceiveStream::Config config(nullptr);
331 parsed_log.GetVideoReceiveConfig(i, &config);
332 uint64_t stream = GetStreamID(config.rtp.remote_ssrc, kIncomingPacket,
333 MediaType::VIDEO);
334 extension_maps[stream].Erase();
335 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
336 const std::string& extension = config.rtp.extensions[j].name;
aleloi 2016/06/03 08:51:11 RtpExtension.name was removed in 6f8d686d. It seem
terelius 2016/06/14 13:18:48 Done.
337 int id = config.rtp.extensions[j].id;
338 extension_maps[stream].Register(StringToRtpExtensionType(extension),
339 id);
340 }
341 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
342 VideoSendStream::Config config(nullptr);
343 parsed_log.GetVideoSendConfig(i, &config);
344 for (auto ssrc : config.rtp.ssrcs) {
345 uint64_t stream = GetStreamID(ssrc, kIncomingPacket, MediaType::VIDEO);
346 extension_maps[stream].Erase();
347 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
348 const std::string& extension = config.rtp.extensions[j].name;
349 int id = config.rtp.extensions[j].id;
350 extension_maps[stream].Register(StringToRtpExtensionType(extension),
351 id);
352 }
353 }
354 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
355 AudioReceiveStream::Config config;
356 // TODO(terelius): Parse the audio configs once we have them
357 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
358 AudioSendStream::Config config(nullptr);
359 // TODO(terelius): Parse the audio configs once we have them
360 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
361 parsed_log.GetRtpHeader(i, &direction, &media_type, header,
362 &header_length, &total_length);
363 if (direction == kIncomingPacket) {
364 // Parse header to get SSRC
365 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
366 RTPHeader parsed_header;
367 rtp_parser.Parse(&parsed_header);
368 // Filter on SSRC
369 if (MatchingSSRC(parsed_header.ssrc, desired_ssrc)) {
370 uint64_t stream =
371 GetStreamID(parsed_header.ssrc, direction, media_type);
372 // Look up the extension_map and parse it again to get the extensions.
373 if (extension_maps.count(stream) == 1) {
374 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
375 rtp_parser.Parse(&parsed_header, extension_map);
376 if (parsed_header.extension.hasAbsoluteSendTime) {
377 uint64_t timestamp = parsed_log.GetTimestamp(i);
378 int64_t send_time_diff = WrappingDifference(
379 parsed_header.extension.absoluteSendTime,
380 last_packet[stream].absoluteSendTime, 1ul << 24);
381 int64_t recv_time_diff =
382 timestamp - last_packet[stream].receiveTimestamp;
383
384 float x = static_cast<float>(timestamp - begin_time) / 1000000;
385 double y = static_cast<double>(
386 recv_time_diff -
387 AbsSendTimeToMicroseconds(send_time_diff)) /
388 1000;
389 if (time_series[stream].points.size() == 0) {
390 // There were no previusly logged playout for this SSRC.
391 // Generate a point, but place it on the x-axis.
392 y = 0;
393 }
394
395 max_y = (y > max_y) ? y : max_y;
396 min_y = (y < min_y) ? y : min_y;
397 time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
398 last_packet[stream] = SendReceiveTime(
399 parsed_header.extension.absoluteSendTime, timestamp);
400 }
401 }
402 }
403 }
404 }
405 }
406
407 // Set labels and put in graph
408 for (auto& kv : time_series) {
409 kv.second.label = SSRCToString(GetSsrcFromStreamID(kv.first));
410 kv.second.style = BAR_GRAPH;
411 plot->series.push_back(TimeSeries());
412 plot->series.back().swap(kv.second);
413 }
414
415 plot->xaxis_min = -1;
416 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
417 plot->xaxis_label = "Time (s)";
418 plot->yaxis_min = min_y - 0.05 * (max_y - min_y);
419 plot->yaxis_max = max_y + 0.05 * (max_y - min_y);
420 plot->yaxis_label = "Latency change (ms)";
421 plot->title = "Network latency change between consecutive packets";
422 }
423
424 void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
425 // TODO(terelius): Refactor
stefan-webrtc 2016/05/31 18:53:39 Is the plan to base this method on the previous on
terelius 2016/06/14 13:18:49 Not really base one on the other, but there is a l
426
427 // Maps a stream identifier consisting of ssrc, direction and MediaType
428 // to the header extensions used by that stream,
429 std::map<uint64_t, RtpHeaderExtensionMap> extension_maps;
430
431 struct SendReceiveTime {
432 SendReceiveTime() = default;
433 SendReceiveTime(uint32_t send_time, uint64_t recv_time, double accumulated)
434 : absoluteSendTime(send_time),
435 receiveTimestamp(recv_time),
436 accumulatedDelay(accumulated) {}
437 uint32_t absoluteSendTime; // 24-bit value in units of 2^-18 seconds
438 uint64_t receiveTimestamp; // In microseconds
439 double accumulatedDelay; // In milliseconds
stefan-webrtc 2016/05/31 18:53:40 No camel case
terelius 2016/06/14 13:18:48 Done. However, this means that I am no longer cons
440 };
441 std::map<uint64_t, SendReceiveTime> last_packet;
442 std::map<uint64_t, TimeSeries> time_series;
443
444 PacketDirection direction;
445 MediaType media_type;
446 uint8_t header[IP_PACKET_SIZE];
447 size_t header_length, total_length;
448
449 double max_y = 10;
450 double min_y = 0;
451
452 //
453 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
454 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
455 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) {
456 VideoReceiveStream::Config config(nullptr);
457 parsed_log.GetVideoReceiveConfig(i, &config);
458 uint64_t stream = GetStreamID(config.rtp.remote_ssrc, kIncomingPacket,
459 MediaType::VIDEO);
460 extension_maps[stream].Erase();
461 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
462 const std::string& extension = config.rtp.extensions[j].name;
463 int id = config.rtp.extensions[j].id;
464 extension_maps[stream].Register(StringToRtpExtensionType(extension),
465 id);
466 }
467 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) {
468 VideoSendStream::Config config(nullptr);
469 parsed_log.GetVideoSendConfig(i, &config);
470 for (auto ssrc : config.rtp.ssrcs) {
471 uint64_t stream = GetStreamID(ssrc, kIncomingPacket, MediaType::VIDEO);
472 extension_maps[stream].Erase();
473 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) {
474 const std::string& extension = config.rtp.extensions[j].name;
475 int id = config.rtp.extensions[j].id;
476 extension_maps[stream].Register(StringToRtpExtensionType(extension),
477 id);
478 }
479 }
480 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) {
481 AudioReceiveStream::Config config;
482 // TODO(terelius): Parse the audio configs once we have them
483 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) {
484 AudioSendStream::Config config(nullptr);
485 // TODO(terelius): Parse the audio configs once we have them
486 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) {
487 parsed_log.GetRtpHeader(i, &direction, &media_type, header,
488 &header_length, &total_length);
489 if (direction == kIncomingPacket) {
490 // Parse header to get SSRC
491 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
492 RTPHeader parsed_header;
493 rtp_parser.Parse(&parsed_header);
494 // Filter on SSRC
495 if (MatchingSSRC(parsed_header.ssrc, desired_ssrc)) {
496 uint64_t stream =
497 GetStreamID(parsed_header.ssrc, direction, media_type);
498 // Look up the extension_map and parse it again to get the extensions.
499 if (extension_maps.count(stream) == 1) {
500 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
501 rtp_parser.Parse(&parsed_header, extension_map);
502 if (parsed_header.extension.hasAbsoluteSendTime) {
503 uint64_t timestamp = parsed_log.GetTimestamp(i);
504 int64_t send_time_diff = WrappingDifference(
505 parsed_header.extension.absoluteSendTime,
506 last_packet[stream].absoluteSendTime, 1ul << 24);
507 int64_t recv_time_diff =
508 timestamp - last_packet[stream].receiveTimestamp;
509
510 float x = static_cast<float>(timestamp - begin_time) / 1000000;
511 double y = last_packet[stream].accumulatedDelay +
512 static_cast<double>(
513 recv_time_diff -
514 AbsSendTimeToMicroseconds(send_time_diff)) /
515 1000;
516 if (time_series[stream].points.size() == 0) {
517 // There were no previusly logged playout for this SSRC.
518 // Generate a point, but place it on the x-axis.
519 y = 0;
520 }
521
522 max_y = (y > max_y) ? y : max_y;
523 min_y = (y < min_y) ? y : min_y;
524 time_series[stream].points.push_back(TimeSeriesPoint(x, y, ""));
525 last_packet[stream] = SendReceiveTime(
526 parsed_header.extension.absoluteSendTime, timestamp, y);
527 }
528 }
529 }
530 }
531 }
532 }
533
534 // Set labels and put in graph
535 for (auto& kv : time_series) {
536 kv.second.label = SSRCToString(GetSsrcFromStreamID(kv.first));
537 kv.second.style = LINE_GRAPH;
538 plot->series.push_back(TimeSeries());
539 plot->series.back().swap(kv.second);
540 }
541
542 plot->xaxis_min = -1;
543 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
544 plot->xaxis_label = "Time (s)";
545 plot->yaxis_min = min_y - 0.05 * (max_y - min_y);
546 plot->yaxis_max = max_y + 0.05 * (max_y - min_y);
547 plot->yaxis_label = "Latency change (ms)";
548 plot->title = "Accumulated network latency change";
549 }
550
551 // Plot the total bandwitch used by all RTP streams.
552 void EventLogAnalyzer::CreateTotalBitrateGraph(
553 PacketDirection desired_direction,
554 Plot* plot) {
555 struct TimestampSize {
556 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
557 uint64_t timestamp;
558 size_t size;
559 };
560 std::vector<TimestampSize> packets;
561
562 PacketDirection direction;
563 size_t total_length;
564
565 // Extract timestamps and sizes for the relevant packets.
566 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
567 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
568 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
569 parsed_log.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
570 &total_length);
571 if (direction == desired_direction) {
572 uint64_t timestamp = parsed_log.GetTimestamp(i);
573 packets.push_back(TimestampSize(timestamp, total_length));
574 }
575 }
576 }
577
578 size_t window_index_begin = 0;
579 size_t window_index_end = 0;
580 size_t bytes_in_window = 0;
581 float max_y = 0;
582
583 // Calculate a moving average of the bitrate and store in a TimeSeries.
584 plot->series.push_back(TimeSeries());
585 for (uint64_t time = begin_time; time < end_time + step; time += step) {
586 while (window_index_end < packets.size() &&
587 packets[window_index_end].timestamp < time) {
588 bytes_in_window += packets[window_index_end].size;
589 window_index_end++;
590 }
591 while (window_index_begin < packets.size() &&
592 packets[window_index_begin].timestamp < time - window_duration) {
593 bytes_in_window -= packets[window_index_begin].size;
594 window_index_begin++;
595 }
596 RTC_DCHECK_LE(0ul, bytes_in_window);
597 float window_duration_in_seconds =
598 static_cast<float>(window_duration) / 1000000;
599 float x = static_cast<float>(time - begin_time) / 1000000;
600 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
601 max_y = (y > max_y) ? y : max_y;
602 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
603 }
604
605 // Set labels
606 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
607 plot->series.back().label = "Incoming bitrate";
608 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
609 plot->series.back().label = "Outgoing bitrate";
610 }
611 plot->series.back().style = LINE_GRAPH;
612
613 plot->xaxis_min = -1;
614 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
615 plot->xaxis_label = "Time (s)";
616 plot->yaxis_min = -1;
617 plot->yaxis_max = max_y * 1.1;
618 plot->yaxis_label = "Bitrate (kbps)";
619 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
620 plot->title = "Incoming RTP bitrate";
621 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
622 plot->title = "Outgoing RTP bitrate";
623 }
624 }
625
626 // For each SSRC, plot the bandwitch used by that stream.
627 void EventLogAnalyzer::CreateStreamBitrateGraph(
628 PacketDirection desired_direction,
629 Plot* plot) {
630 struct TimestampSize {
631 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
632 uint64_t timestamp;
633 size_t size;
634 };
635 std::map<uint32_t, std::vector<TimestampSize> > packets;
636
637 PacketDirection direction;
638 MediaType media_type;
639 uint8_t header[IP_PACKET_SIZE];
640 size_t header_length, total_length;
641
642 // Extract timestamps and sizes for the relevant packets.
643 for (size_t i = 0; i < parsed_log.GetNumberOfEvents(); i++) {
644 ParsedRtcEventLog::EventType event_type = parsed_log.GetEventType(i);
645 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
646 parsed_log.GetRtpHeader(i, &direction, &media_type, header,
647 &header_length, &total_length);
648 if (direction == desired_direction) {
649 // Parse header to get SSRC
650 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
651 RTPHeader parsed_header;
652 rtp_parser.Parse(&parsed_header);
653 // Filter on SSRC
654 if (MatchingSSRC(parsed_header.ssrc, desired_ssrc)) {
655 uint64_t timestamp = parsed_log.GetTimestamp(i);
656 packets[parsed_header.ssrc].push_back(
657 TimestampSize(timestamp, total_length));
658 }
659 }
660 }
661 }
662
663 float max_y = 0;
664
665 for (auto& kv : packets) {
666 size_t window_index_begin = 0;
667 size_t window_index_end = 0;
668 size_t bytes_in_window = 0;
669
670 // Calculate a moving average of the bitrate and store in a TimeSeries.
671 plot->series.push_back(TimeSeries());
672 for (uint64_t time = begin_time; time < end_time + step; time += step) {
673 while (window_index_end < kv.second.size() &&
674 kv.second[window_index_end].timestamp < time) {
675 bytes_in_window += kv.second[window_index_end].size;
676 window_index_end++;
677 }
678 while (window_index_begin < kv.second.size() &&
679 kv.second[window_index_begin].timestamp < time - window_duration) {
680 bytes_in_window -= kv.second[window_index_begin].size;
681 window_index_begin++;
682 }
683 RTC_DCHECK_LE(0ul, bytes_in_window);
684 float window_duration_in_seconds =
685 static_cast<float>(window_duration) / 1000000;
686 float x = static_cast<float>(time - begin_time) / 1000000;
687 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
688 max_y = (y > max_y) ? y : max_y;
689 plot->series.back().points.push_back(TimeSeriesPoint(x, y));
690 }
691
692 // Set labels
693 plot->series.back().label = SSRCToString(kv.first);
694 plot->series.back().style = LINE_GRAPH;
695 }
696
697 plot->xaxis_min = -1;
698 plot->xaxis_max = (end_time - begin_time) / 1000000 * 1.02;
699 plot->xaxis_label = "Time (s)";
700 plot->yaxis_min = -1;
701 plot->yaxis_max = max_y * 1.1;
702 plot->yaxis_label = "Bitrate (kbps)";
703 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
704 plot->title = "Incoming bitrate per stream";
705 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
706 plot->title = "Outgoing bitrate per stream";
707 }
708 }
709
710 } // namespace plotting
711 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698