OLD | NEW |
(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 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. |
| 41 bool 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 |
| 48 double 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]. |
| 62 int64_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 |
| 79 class StreamId { |
| 80 public: |
| 81 StreamId(uint32_t ssrc, |
| 82 webrtc::PacketDirection direction, |
| 83 webrtc::MediaType media_type) |
| 84 : ssrc_(ssrc), direction_(direction), media_type_(media_type) {} |
| 85 |
| 86 bool operator<(const StreamId& other) const { |
| 87 if (ssrc_ < other.ssrc_) { |
| 88 return true; |
| 89 } |
| 90 if (ssrc_ == other.ssrc_) { |
| 91 if (media_type_ < other.media_type_) { |
| 92 return true; |
| 93 } |
| 94 if (media_type_ == other.media_type_) { |
| 95 if (direction_ < other.direction_) { |
| 96 return true; |
| 97 } |
| 98 } |
| 99 } |
| 100 return false; |
| 101 } |
| 102 |
| 103 bool operator==(const StreamId& other) const { |
| 104 return ssrc_ == other.ssrc_ && direction_ == other.direction_ && |
| 105 media_type_ == other.media_type_; |
| 106 } |
| 107 |
| 108 uint32_t GetSsrc() const { return ssrc_; } |
| 109 |
| 110 private: |
| 111 uint32_t ssrc_; |
| 112 webrtc::PacketDirection direction_; |
| 113 webrtc::MediaType media_type_; |
| 114 }; |
| 115 |
| 116 const double kXMargin = 1.02; |
| 117 const double kYMargin = 1.1; |
| 118 const double kDefaultXMin = -1; |
| 119 const double kDefaultYMin = -1; |
| 120 |
| 121 } // namespace |
| 122 |
| 123 namespace webrtc { |
| 124 namespace plotting { |
| 125 |
| 126 EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log) |
| 127 : parsed_log_(log), window_duration_(250000), step_(10000) { |
| 128 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max(); |
| 129 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min(); |
| 130 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 131 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 132 if (event_type == ParsedRtcEventLog::EventType::VIDEO_RECEIVER_CONFIG_EVENT) |
| 133 continue; |
| 134 if (event_type == ParsedRtcEventLog::EventType::VIDEO_SENDER_CONFIG_EVENT) |
| 135 continue; |
| 136 if (event_type == ParsedRtcEventLog::EventType::AUDIO_RECEIVER_CONFIG_EVENT) |
| 137 continue; |
| 138 if (event_type == ParsedRtcEventLog::EventType::AUDIO_SENDER_CONFIG_EVENT) |
| 139 continue; |
| 140 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 141 first_timestamp = std::min(first_timestamp, timestamp); |
| 142 last_timestamp = std::max(last_timestamp, timestamp); |
| 143 } |
| 144 if (last_timestamp < first_timestamp) { |
| 145 // No useful events in the log. |
| 146 first_timestamp = last_timestamp = 0; |
| 147 } |
| 148 begin_time_ = first_timestamp; |
| 149 end_time_ = last_timestamp; |
| 150 } |
| 151 |
| 152 void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction, |
| 153 Plot* plot) { |
| 154 std::map<uint32_t, TimeSeries> time_series; |
| 155 |
| 156 PacketDirection direction; |
| 157 MediaType media_type; |
| 158 uint8_t header[IP_PACKET_SIZE]; |
| 159 size_t header_length, total_length; |
| 160 float max_y = 0; |
| 161 |
| 162 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 163 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 164 if (event_type == ParsedRtcEventLog::RTP_EVENT) { |
| 165 parsed_log_.GetRtpHeader(i, &direction, &media_type, header, |
| 166 &header_length, &total_length); |
| 167 if (direction == desired_direction) { |
| 168 // Parse header to get SSRC. |
| 169 RtpUtility::RtpHeaderParser rtp_parser(header, header_length); |
| 170 RTPHeader parsed_header; |
| 171 rtp_parser.Parse(&parsed_header); |
| 172 // Filter on SSRC. |
| 173 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) { |
| 174 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 175 float x = static_cast<float>(timestamp - begin_time_) / 1000000; |
| 176 float y = total_length; |
| 177 max_y = std::max(max_y, y); |
| 178 time_series[parsed_header.ssrc].points.push_back( |
| 179 TimeSeriesPoint(x, y)); |
| 180 } |
| 181 } |
| 182 } |
| 183 } |
| 184 |
| 185 // Set labels and put in graph. |
| 186 for (auto& kv : time_series) { |
| 187 kv.second.label = SsrcToString(kv.first); |
| 188 kv.second.style = BAR_GRAPH; |
| 189 plot->series.push_back(std::move(kv.second)); |
| 190 } |
| 191 |
| 192 plot->xaxis_min = kDefaultXMin; |
| 193 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin; |
| 194 plot->xaxis_label = "Time (s)"; |
| 195 plot->yaxis_min = kDefaultYMin; |
| 196 plot->yaxis_max = max_y * kYMargin; |
| 197 plot->yaxis_label = "Packet size (bytes)"; |
| 198 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { |
| 199 plot->title = "Incoming RTP packets"; |
| 200 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { |
| 201 plot->title = "Outgoing RTP packets"; |
| 202 } |
| 203 } |
| 204 |
| 205 // For each SSRC, plot the time between the consecutive playouts. |
| 206 void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { |
| 207 std::map<uint32_t, TimeSeries> time_series; |
| 208 std::map<uint32_t, uint64_t> last_playout; |
| 209 |
| 210 uint32_t ssrc; |
| 211 float max_y = 0; |
| 212 |
| 213 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 214 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 215 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) { |
| 216 parsed_log_.GetAudioPlayout(i, &ssrc); |
| 217 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 218 if (MatchingSsrc(ssrc, desired_ssrc_)) { |
| 219 float x = static_cast<float>(timestamp - begin_time_) / 1000000; |
| 220 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000; |
| 221 if (time_series[ssrc].points.size() == 0) { |
| 222 // There were no previusly logged playout for this SSRC. |
| 223 // Generate a point, but place it on the x-axis. |
| 224 y = 0; |
| 225 } |
| 226 max_y = std::max(max_y, y); |
| 227 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y)); |
| 228 last_playout[ssrc] = timestamp; |
| 229 } |
| 230 } |
| 231 } |
| 232 |
| 233 // Set labels and put in graph. |
| 234 for (auto& kv : time_series) { |
| 235 kv.second.label = SsrcToString(kv.first); |
| 236 kv.second.style = BAR_GRAPH; |
| 237 plot->series.push_back(std::move(kv.second)); |
| 238 } |
| 239 |
| 240 plot->xaxis_min = kDefaultXMin; |
| 241 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin; |
| 242 plot->xaxis_label = "Time (s)"; |
| 243 plot->yaxis_min = kDefaultYMin; |
| 244 plot->yaxis_max = max_y * kYMargin; |
| 245 plot->yaxis_label = "Time since last playout (ms)"; |
| 246 plot->title = "Audio playout"; |
| 247 } |
| 248 |
| 249 // For each SSRC, plot the time between the consecutive playouts. |
| 250 void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { |
| 251 std::map<uint32_t, TimeSeries> time_series; |
| 252 std::map<uint32_t, uint16_t> last_seqno; |
| 253 |
| 254 PacketDirection direction; |
| 255 MediaType media_type; |
| 256 uint8_t header[IP_PACKET_SIZE]; |
| 257 size_t header_length, total_length; |
| 258 |
| 259 int max_y = 1; |
| 260 int min_y = 0; |
| 261 |
| 262 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 263 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 264 if (event_type == ParsedRtcEventLog::RTP_EVENT) { |
| 265 parsed_log_.GetRtpHeader(i, &direction, &media_type, header, |
| 266 &header_length, &total_length); |
| 267 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 268 if (direction == PacketDirection::kIncomingPacket) { |
| 269 // Parse header to get SSRC. |
| 270 RtpUtility::RtpHeaderParser rtp_parser(header, header_length); |
| 271 RTPHeader parsed_header; |
| 272 rtp_parser.Parse(&parsed_header); |
| 273 // Filter on SSRC. |
| 274 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) { |
| 275 float x = static_cast<float>(timestamp - begin_time_) / 1000000; |
| 276 int y = WrappingDifference(parsed_header.sequenceNumber, |
| 277 last_seqno[parsed_header.ssrc], 1ul << 16); |
| 278 if (time_series[parsed_header.ssrc].points.size() == 0) { |
| 279 // There were no previusly logged playout for this SSRC. |
| 280 // Generate a point, but place it on the x-axis. |
| 281 y = 0; |
| 282 } |
| 283 max_y = std::max(max_y, y); |
| 284 min_y = std::min(min_y, y); |
| 285 time_series[parsed_header.ssrc].points.push_back( |
| 286 TimeSeriesPoint(x, y)); |
| 287 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber; |
| 288 } |
| 289 } |
| 290 } |
| 291 } |
| 292 |
| 293 // Set labels and put in graph. |
| 294 for (auto& kv : time_series) { |
| 295 kv.second.label = SsrcToString(kv.first); |
| 296 kv.second.style = BAR_GRAPH; |
| 297 plot->series.push_back(std::move(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<StreamId, 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<StreamId, SendReceiveTime> last_packet; |
| 322 std::map<StreamId, 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) { |
| 335 VideoReceiveStream::Config config(nullptr); |
| 336 parsed_log_.GetVideoReceiveConfig(i, &config); |
| 337 StreamId stream(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 StreamId stream(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_)) { |
| 375 StreamId stream(parsed_header.ssrc, direction, media_type); |
| 376 // Look up the extension_map and parse it again to get the extensions. |
| 377 if (extension_maps.count(stream) == 1) { |
| 378 RtpHeaderExtensionMap* extension_map = &extension_maps[stream]; |
| 379 rtp_parser.Parse(&parsed_header, extension_map); |
| 380 if (parsed_header.extension.hasAbsoluteSendTime) { |
| 381 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 382 int64_t send_time_diff = WrappingDifference( |
| 383 parsed_header.extension.absoluteSendTime, |
| 384 last_packet[stream].absolute_send_time, 1ul << 24); |
| 385 int64_t recv_time_diff = |
| 386 timestamp - last_packet[stream].receive_timestamp; |
| 387 |
| 388 float x = static_cast<float>(timestamp - begin_time_) / 1000000; |
| 389 double y = static_cast<double>( |
| 390 recv_time_diff - |
| 391 AbsSendTimeToMicroseconds(send_time_diff)) / |
| 392 1000; |
| 393 if (time_series[stream].points.size() == 0) { |
| 394 // There were no previusly logged playout for this SSRC. |
| 395 // Generate a point, but place it on the x-axis. |
| 396 y = 0; |
| 397 } |
| 398 max_y = std::max(max_y, y); |
| 399 min_y = std::min(min_y, y); |
| 400 time_series[stream].points.push_back(TimeSeriesPoint(x, y)); |
| 401 last_packet[stream] = SendReceiveTime( |
| 402 parsed_header.extension.absoluteSendTime, timestamp); |
| 403 } |
| 404 } |
| 405 } |
| 406 } |
| 407 } |
| 408 } |
| 409 |
| 410 // Set labels and put in graph. |
| 411 for (auto& kv : time_series) { |
| 412 kv.second.label = SsrcToString(kv.first.GetSsrc()); |
| 413 kv.second.style = BAR_GRAPH; |
| 414 plot->series.push_back(std::move(kv.second)); |
| 415 } |
| 416 |
| 417 plot->xaxis_min = kDefaultXMin; |
| 418 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin; |
| 419 plot->xaxis_label = "Time (s)"; |
| 420 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y); |
| 421 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y); |
| 422 plot->yaxis_label = "Latency change (ms)"; |
| 423 plot->title = "Network latency change between consecutive packets"; |
| 424 } |
| 425 |
| 426 void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) { |
| 427 // TODO(terelius): Refactor |
| 428 |
| 429 // Maps a stream identifier consisting of ssrc, direction and MediaType |
| 430 // to the header extensions used by that stream. |
| 431 std::map<StreamId, RtpHeaderExtensionMap> extension_maps; |
| 432 |
| 433 struct SendReceiveTime { |
| 434 SendReceiveTime() = default; |
| 435 SendReceiveTime(uint32_t send_time, uint64_t recv_time, double accumulated) |
| 436 : absolute_send_time(send_time), |
| 437 receive_timestamp(recv_time), |
| 438 accumulated_delay(accumulated) {} |
| 439 uint32_t absolute_send_time; // 24-bit value in units of 2^-18 seconds. |
| 440 uint64_t receive_timestamp; // In microseconds. |
| 441 double accumulated_delay; // In milliseconds. |
| 442 }; |
| 443 std::map<StreamId, SendReceiveTime> last_packet; |
| 444 std::map<StreamId, TimeSeries> time_series; |
| 445 |
| 446 PacketDirection direction; |
| 447 MediaType media_type; |
| 448 uint8_t header[IP_PACKET_SIZE]; |
| 449 size_t header_length, total_length; |
| 450 |
| 451 double max_y = 10; |
| 452 double min_y = 0; |
| 453 |
| 454 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 455 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 456 if (event_type == ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT) { |
| 457 VideoReceiveStream::Config config(nullptr); |
| 458 parsed_log_.GetVideoReceiveConfig(i, &config); |
| 459 StreamId stream(config.rtp.remote_ssrc, kIncomingPacket, |
| 460 MediaType::VIDEO); |
| 461 extension_maps[stream].Erase(); |
| 462 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) { |
| 463 const std::string& extension = config.rtp.extensions[j].uri; |
| 464 int id = config.rtp.extensions[j].id; |
| 465 extension_maps[stream].Register(StringToRtpExtensionType(extension), |
| 466 id); |
| 467 } |
| 468 } else if (event_type == ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT) { |
| 469 VideoSendStream::Config config(nullptr); |
| 470 parsed_log_.GetVideoSendConfig(i, &config); |
| 471 for (auto ssrc : config.rtp.ssrcs) { |
| 472 StreamId stream(ssrc, kIncomingPacket, MediaType::VIDEO); |
| 473 extension_maps[stream].Erase(); |
| 474 for (size_t j = 0; j < config.rtp.extensions.size(); ++j) { |
| 475 const std::string& extension = config.rtp.extensions[j].uri; |
| 476 int id = config.rtp.extensions[j].id; |
| 477 extension_maps[stream].Register(StringToRtpExtensionType(extension), |
| 478 id); |
| 479 } |
| 480 } |
| 481 } else if (event_type == ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT) { |
| 482 AudioReceiveStream::Config config; |
| 483 // TODO(terelius): Parse the audio configs once we have them |
| 484 } else if (event_type == ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT) { |
| 485 AudioSendStream::Config config(nullptr); |
| 486 // TODO(terelius): Parse the audio configs once we have them |
| 487 } else if (event_type == ParsedRtcEventLog::RTP_EVENT) { |
| 488 parsed_log_.GetRtpHeader(i, &direction, &media_type, header, |
| 489 &header_length, &total_length); |
| 490 if (direction == kIncomingPacket) { |
| 491 // Parse header to get SSRC. |
| 492 RtpUtility::RtpHeaderParser rtp_parser(header, header_length); |
| 493 RTPHeader parsed_header; |
| 494 rtp_parser.Parse(&parsed_header); |
| 495 // Filter on SSRC. |
| 496 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) { |
| 497 StreamId stream(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].absolute_send_time, 1ul << 24); |
| 507 int64_t recv_time_diff = |
| 508 timestamp - last_packet[stream].receive_timestamp; |
| 509 |
| 510 float x = static_cast<float>(timestamp - begin_time_) / 1000000; |
| 511 double y = last_packet[stream].accumulated_delay + |
| 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 max_y = std::max(max_y, y); |
| 522 min_y = std::min(min_y, y); |
| 523 time_series[stream].points.push_back(TimeSeriesPoint(x, y)); |
| 524 last_packet[stream] = SendReceiveTime( |
| 525 parsed_header.extension.absoluteSendTime, timestamp, y); |
| 526 } |
| 527 } |
| 528 } |
| 529 } |
| 530 } |
| 531 } |
| 532 |
| 533 // Set labels and put in graph. |
| 534 for (auto& kv : time_series) { |
| 535 kv.second.label = SsrcToString(kv.first.GetSsrc()); |
| 536 kv.second.style = LINE_GRAPH; |
| 537 plot->series.push_back(std::move(kv.second)); |
| 538 } |
| 539 |
| 540 plot->xaxis_min = kDefaultXMin; |
| 541 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin; |
| 542 plot->xaxis_label = "Time (s)"; |
| 543 plot->yaxis_min = min_y - (kYMargin - 1) / 2 * (max_y - min_y); |
| 544 plot->yaxis_max = max_y + (kYMargin - 1) / 2 * (max_y - min_y); |
| 545 plot->yaxis_label = "Latency change (ms)"; |
| 546 plot->title = "Accumulated network latency change"; |
| 547 } |
| 548 |
| 549 // Plot the total bandwidth used by all RTP streams. |
| 550 void EventLogAnalyzer::CreateTotalBitrateGraph( |
| 551 PacketDirection desired_direction, |
| 552 Plot* plot) { |
| 553 struct TimestampSize { |
| 554 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {} |
| 555 uint64_t timestamp; |
| 556 size_t size; |
| 557 }; |
| 558 std::vector<TimestampSize> packets; |
| 559 |
| 560 PacketDirection direction; |
| 561 size_t total_length; |
| 562 |
| 563 // Extract timestamps and sizes for the relevant packets. |
| 564 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 565 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 566 if (event_type == ParsedRtcEventLog::RTP_EVENT) { |
| 567 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr, |
| 568 &total_length); |
| 569 if (direction == desired_direction) { |
| 570 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 571 packets.push_back(TimestampSize(timestamp, total_length)); |
| 572 } |
| 573 } |
| 574 } |
| 575 |
| 576 size_t window_index_begin = 0; |
| 577 size_t window_index_end = 0; |
| 578 size_t bytes_in_window = 0; |
| 579 float max_y = 0; |
| 580 |
| 581 // Calculate a moving average of the bitrate and store in a TimeSeries. |
| 582 plot->series.push_back(TimeSeries()); |
| 583 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) { |
| 584 while (window_index_end < packets.size() && |
| 585 packets[window_index_end].timestamp < time) { |
| 586 bytes_in_window += packets[window_index_end].size; |
| 587 window_index_end++; |
| 588 } |
| 589 while (window_index_begin < packets.size() && |
| 590 packets[window_index_begin].timestamp < time - window_duration_) { |
| 591 RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window); |
| 592 bytes_in_window -= packets[window_index_begin].size; |
| 593 window_index_begin++; |
| 594 } |
| 595 float window_duration_in_seconds = |
| 596 static_cast<float>(window_duration_) / 1000000; |
| 597 float x = static_cast<float>(time - begin_time_) / 1000000; |
| 598 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; |
| 599 max_y = std::max(max_y, y); |
| 600 plot->series.back().points.push_back(TimeSeriesPoint(x, y)); |
| 601 } |
| 602 |
| 603 // Set labels. |
| 604 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { |
| 605 plot->series.back().label = "Incoming bitrate"; |
| 606 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { |
| 607 plot->series.back().label = "Outgoing bitrate"; |
| 608 } |
| 609 plot->series.back().style = LINE_GRAPH; |
| 610 |
| 611 plot->xaxis_min = kDefaultXMin; |
| 612 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin; |
| 613 plot->xaxis_label = "Time (s)"; |
| 614 plot->yaxis_min = kDefaultYMin; |
| 615 plot->yaxis_max = max_y * kYMargin; |
| 616 plot->yaxis_label = "Bitrate (kbps)"; |
| 617 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { |
| 618 plot->title = "Incoming RTP bitrate"; |
| 619 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { |
| 620 plot->title = "Outgoing RTP bitrate"; |
| 621 } |
| 622 } |
| 623 |
| 624 // For each SSRC, plot the bandwidth used by that stream. |
| 625 void EventLogAnalyzer::CreateStreamBitrateGraph( |
| 626 PacketDirection desired_direction, |
| 627 Plot* plot) { |
| 628 struct TimestampSize { |
| 629 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {} |
| 630 uint64_t timestamp; |
| 631 size_t size; |
| 632 }; |
| 633 std::map<uint32_t, std::vector<TimestampSize> > packets; |
| 634 |
| 635 PacketDirection direction; |
| 636 MediaType media_type; |
| 637 uint8_t header[IP_PACKET_SIZE]; |
| 638 size_t header_length, total_length; |
| 639 |
| 640 // Extract timestamps and sizes for the relevant packets. |
| 641 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) { |
| 642 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i); |
| 643 if (event_type == ParsedRtcEventLog::RTP_EVENT) { |
| 644 parsed_log_.GetRtpHeader(i, &direction, &media_type, header, |
| 645 &header_length, &total_length); |
| 646 if (direction == desired_direction) { |
| 647 // Parse header to get SSRC. |
| 648 RtpUtility::RtpHeaderParser rtp_parser(header, header_length); |
| 649 RTPHeader parsed_header; |
| 650 rtp_parser.Parse(&parsed_header); |
| 651 // Filter on SSRC. |
| 652 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) { |
| 653 uint64_t timestamp = parsed_log_.GetTimestamp(i); |
| 654 packets[parsed_header.ssrc].push_back( |
| 655 TimestampSize(timestamp, total_length)); |
| 656 } |
| 657 } |
| 658 } |
| 659 } |
| 660 |
| 661 float max_y = 0; |
| 662 |
| 663 for (auto& kv : packets) { |
| 664 size_t window_index_begin = 0; |
| 665 size_t window_index_end = 0; |
| 666 size_t bytes_in_window = 0; |
| 667 |
| 668 // Calculate a moving average of the bitrate and store in a TimeSeries. |
| 669 plot->series.push_back(TimeSeries()); |
| 670 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) { |
| 671 while (window_index_end < kv.second.size() && |
| 672 kv.second[window_index_end].timestamp < time) { |
| 673 bytes_in_window += kv.second[window_index_end].size; |
| 674 window_index_end++; |
| 675 } |
| 676 while (window_index_begin < kv.second.size() && |
| 677 kv.second[window_index_begin].timestamp < |
| 678 time - window_duration_) { |
| 679 RTC_DCHECK_LE(kv.second[window_index_begin].size, bytes_in_window); |
| 680 bytes_in_window -= kv.second[window_index_begin].size; |
| 681 window_index_begin++; |
| 682 } |
| 683 float window_duration_in_seconds = |
| 684 static_cast<float>(window_duration_) / 1000000; |
| 685 float x = static_cast<float>(time - begin_time_) / 1000000; |
| 686 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; |
| 687 max_y = std::max(max_y, y); |
| 688 plot->series.back().points.push_back(TimeSeriesPoint(x, y)); |
| 689 } |
| 690 |
| 691 // Set labels. |
| 692 plot->series.back().label = SsrcToString(kv.first); |
| 693 plot->series.back().style = LINE_GRAPH; |
| 694 } |
| 695 |
| 696 plot->xaxis_min = kDefaultXMin; |
| 697 plot->xaxis_max = (end_time_ - begin_time_) / 1000000 * kXMargin; |
| 698 plot->xaxis_label = "Time (s)"; |
| 699 plot->yaxis_min = kDefaultYMin; |
| 700 plot->yaxis_max = max_y * kYMargin; |
| 701 plot->yaxis_label = "Bitrate (kbps)"; |
| 702 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) { |
| 703 plot->title = "Incoming bitrate per stream"; |
| 704 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) { |
| 705 plot->title = "Outgoing bitrate per stream"; |
| 706 } |
| 707 } |
| 708 |
| 709 } // namespace plotting |
| 710 } // namespace webrtc |
OLD | NEW |