blob: 1f7973e376e78c31cb723a5807ddb677297eb6b6 [file] [log] [blame]
terelius54ce6802016-07-13 06:44:41 -07001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/tools/event_log_visualizer/analyzer.h"
12
13#include <algorithm>
14#include <limits>
15#include <map>
16#include <sstream>
17#include <string>
18#include <utility>
19
20#include "webrtc/audio_receive_stream.h"
21#include "webrtc/audio_send_stream.h"
22#include "webrtc/base/checks.h"
stefan6a850c32016-07-29 10:28:08 -070023#include "webrtc/base/logging.h"
terelius54ce6802016-07-13 06:44:41 -070024#include "webrtc/call.h"
25#include "webrtc/common_types.h"
Stefan Holmer13181032016-07-29 14:48:54 +020026#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
terelius54ce6802016-07-13 06:44:41 -070027#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
28#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
29#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
Stefan Holmer13181032016-07-29 14:48:54 +020030#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
31#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
terelius54ce6802016-07-13 06:44:41 -070032#include "webrtc/video_receive_stream.h"
33#include "webrtc/video_send_stream.h"
34
tereliusdc35dcd2016-08-01 12:03:27 -070035namespace webrtc {
36namespace plotting {
37
terelius54ce6802016-07-13 06:44:41 -070038namespace {
39
40std::string SsrcToString(uint32_t ssrc) {
41 std::stringstream ss;
42 ss << "SSRC " << ssrc;
43 return ss.str();
44}
45
46// Checks whether an SSRC is contained in the list of desired SSRCs.
47// Note that an empty SSRC list matches every SSRC.
48bool MatchingSsrc(uint32_t ssrc, const std::vector<uint32_t>& desired_ssrc) {
49 if (desired_ssrc.size() == 0)
50 return true;
51 return std::find(desired_ssrc.begin(), desired_ssrc.end(), ssrc) !=
52 desired_ssrc.end();
53}
54
55double AbsSendTimeToMicroseconds(int64_t abs_send_time) {
56 // The timestamp is a fixed point representation with 6 bits for seconds
57 // and 18 bits for fractions of a second. Thus, we divide by 2^18 to get the
58 // time in seconds and then multiply by 1000000 to convert to microseconds.
59 static constexpr double kTimestampToMicroSec =
60 1000000.0 / static_cast<double>(1 << 18);
61 return abs_send_time * kTimestampToMicroSec;
62}
63
64// Computes the difference |later| - |earlier| where |later| and |earlier|
65// are counters that wrap at |modulus|. The difference is chosen to have the
66// least absolute value. For example if |modulus| is 8, then the difference will
67// be chosen in the range [-3, 4]. If |modulus| is 9, then the difference will
68// be in [-4, 4].
69int64_t WrappingDifference(uint32_t later, uint32_t earlier, int64_t modulus) {
70 RTC_DCHECK_LE(1, modulus);
71 RTC_DCHECK_LT(later, modulus);
72 RTC_DCHECK_LT(earlier, modulus);
73 int64_t difference =
74 static_cast<int64_t>(later) - static_cast<int64_t>(earlier);
75 int64_t max_difference = modulus / 2;
76 int64_t min_difference = max_difference - modulus + 1;
77 if (difference > max_difference) {
78 difference -= modulus;
79 }
80 if (difference < min_difference) {
81 difference += modulus;
82 }
83 return difference;
84}
85
stefan6a850c32016-07-29 10:28:08 -070086void RegisterHeaderExtensions(
87 const std::vector<webrtc::RtpExtension>& extensions,
88 webrtc::RtpHeaderExtensionMap* extension_map) {
89 extension_map->Erase();
90 for (const webrtc::RtpExtension& extension : extensions) {
91 extension_map->Register(webrtc::StringToRtpExtensionType(extension.uri),
92 extension.id);
93 }
94}
95
tereliusdc35dcd2016-08-01 12:03:27 -070096constexpr float kLeftMargin = 0.01f;
97constexpr float kRightMargin = 0.02f;
98constexpr float kBottomMargin = 0.02f;
99constexpr float kTopMargin = 0.05f;
terelius54ce6802016-07-13 06:44:41 -0700100
101} // namespace
102
terelius54ce6802016-07-13 06:44:41 -0700103EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log)
104 : parsed_log_(log), window_duration_(250000), step_(10000) {
105 uint64_t first_timestamp = std::numeric_limits<uint64_t>::max();
106 uint64_t last_timestamp = std::numeric_limits<uint64_t>::min();
terelius88e64e52016-07-19 01:51:06 -0700107
Stefan Holmer13181032016-07-29 14:48:54 +0200108 // Maps a stream identifier consisting of ssrc and direction
terelius88e64e52016-07-19 01:51:06 -0700109 // to the header extensions used by that stream,
110 std::map<StreamId, RtpHeaderExtensionMap> extension_maps;
111
112 PacketDirection direction;
terelius88e64e52016-07-19 01:51:06 -0700113 uint8_t header[IP_PACKET_SIZE];
114 size_t header_length;
115 size_t total_length;
116
terelius54ce6802016-07-13 06:44:41 -0700117 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
118 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
terelius88e64e52016-07-19 01:51:06 -0700119 if (event_type != ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT &&
120 event_type != ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT &&
121 event_type != ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT &&
terelius88c1d2b2016-08-01 05:20:33 -0700122 event_type != ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT &&
123 event_type != ParsedRtcEventLog::LOG_START &&
124 event_type != ParsedRtcEventLog::LOG_END) {
terelius88e64e52016-07-19 01:51:06 -0700125 uint64_t timestamp = parsed_log_.GetTimestamp(i);
126 first_timestamp = std::min(first_timestamp, timestamp);
127 last_timestamp = std::max(last_timestamp, timestamp);
128 }
129
130 switch (parsed_log_.GetEventType(i)) {
131 case ParsedRtcEventLog::VIDEO_RECEIVER_CONFIG_EVENT: {
132 VideoReceiveStream::Config config(nullptr);
133 parsed_log_.GetVideoReceiveConfig(i, &config);
Stefan Holmer13181032016-07-29 14:48:54 +0200134 StreamId stream(config.rtp.remote_ssrc, kIncomingPacket);
stefan6a850c32016-07-29 10:28:08 -0700135 RegisterHeaderExtensions(config.rtp.extensions,
136 &extension_maps[stream]);
terelius0740a202016-08-08 10:21:04 -0700137 video_ssrcs_.insert(stream);
stefan6a850c32016-07-29 10:28:08 -0700138 for (auto kv : config.rtp.rtx) {
139 StreamId rtx_stream(kv.second.ssrc, kIncomingPacket);
140 RegisterHeaderExtensions(config.rtp.extensions,
141 &extension_maps[rtx_stream]);
terelius0740a202016-08-08 10:21:04 -0700142 video_ssrcs_.insert(rtx_stream);
143 rtx_ssrcs_.insert(rtx_stream);
terelius88e64e52016-07-19 01:51:06 -0700144 }
145 break;
146 }
147 case ParsedRtcEventLog::VIDEO_SENDER_CONFIG_EVENT: {
148 VideoSendStream::Config config(nullptr);
149 parsed_log_.GetVideoSendConfig(i, &config);
150 for (auto ssrc : config.rtp.ssrcs) {
Stefan Holmer13181032016-07-29 14:48:54 +0200151 StreamId stream(ssrc, kOutgoingPacket);
stefan6a850c32016-07-29 10:28:08 -0700152 RegisterHeaderExtensions(config.rtp.extensions,
153 &extension_maps[stream]);
terelius0740a202016-08-08 10:21:04 -0700154 video_ssrcs_.insert(stream);
stefan6a850c32016-07-29 10:28:08 -0700155 }
156 for (auto ssrc : config.rtp.rtx.ssrcs) {
terelius0740a202016-08-08 10:21:04 -0700157 StreamId rtx_stream(ssrc, kOutgoingPacket);
stefan6a850c32016-07-29 10:28:08 -0700158 RegisterHeaderExtensions(config.rtp.extensions,
terelius0740a202016-08-08 10:21:04 -0700159 &extension_maps[rtx_stream]);
160 video_ssrcs_.insert(rtx_stream);
161 rtx_ssrcs_.insert(rtx_stream);
terelius88e64e52016-07-19 01:51:06 -0700162 }
163 break;
164 }
165 case ParsedRtcEventLog::AUDIO_RECEIVER_CONFIG_EVENT: {
166 AudioReceiveStream::Config config;
167 // TODO(terelius): Parse the audio configs once we have them.
168 break;
169 }
170 case ParsedRtcEventLog::AUDIO_SENDER_CONFIG_EVENT: {
171 AudioSendStream::Config config(nullptr);
172 // TODO(terelius): Parse the audio configs once we have them.
173 break;
174 }
175 case ParsedRtcEventLog::RTP_EVENT: {
Stefan Holmer13181032016-07-29 14:48:54 +0200176 MediaType media_type;
terelius88e64e52016-07-19 01:51:06 -0700177 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
178 &header_length, &total_length);
179 // Parse header to get SSRC.
180 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
181 RTPHeader parsed_header;
182 rtp_parser.Parse(&parsed_header);
Stefan Holmer13181032016-07-29 14:48:54 +0200183 StreamId stream(parsed_header.ssrc, direction);
terelius88e64e52016-07-19 01:51:06 -0700184 // Look up the extension_map and parse it again to get the extensions.
185 if (extension_maps.count(stream) == 1) {
186 RtpHeaderExtensionMap* extension_map = &extension_maps[stream];
187 rtp_parser.Parse(&parsed_header, extension_map);
188 }
189 uint64_t timestamp = parsed_log_.GetTimestamp(i);
190 rtp_packets_[stream].push_back(
Stefan Holmer13181032016-07-29 14:48:54 +0200191 LoggedRtpPacket(timestamp, parsed_header, total_length));
terelius88e64e52016-07-19 01:51:06 -0700192 break;
193 }
194 case ParsedRtcEventLog::RTCP_EVENT: {
Stefan Holmer13181032016-07-29 14:48:54 +0200195 uint8_t packet[IP_PACKET_SIZE];
196 MediaType media_type;
197 parsed_log_.GetRtcpPacket(i, &direction, &media_type, packet,
198 &total_length);
199
200 RtpUtility::RtpHeaderParser rtp_parser(packet, total_length);
201 RTPHeader parsed_header;
202 RTC_CHECK(rtp_parser.ParseRtcp(&parsed_header));
203 uint32_t ssrc = parsed_header.ssrc;
204
205 RTCPUtility::RTCPParserV2 rtcp_parser(packet, total_length, true);
206 RTC_CHECK(rtcp_parser.IsValid());
207
208 RTCPUtility::RTCPPacketTypes packet_type = rtcp_parser.Begin();
209 while (packet_type != RTCPUtility::RTCPPacketTypes::kInvalid) {
210 switch (packet_type) {
211 case RTCPUtility::RTCPPacketTypes::kTransportFeedback: {
212 // Currently feedback is logged twice, both for audio and video.
213 // Only act on one of them.
214 if (media_type == MediaType::VIDEO) {
215 std::unique_ptr<rtcp::RtcpPacket> rtcp_packet(
216 rtcp_parser.ReleaseRtcpPacket());
217 StreamId stream(ssrc, direction);
218 uint64_t timestamp = parsed_log_.GetTimestamp(i);
219 rtcp_packets_[stream].push_back(LoggedRtcpPacket(
220 timestamp, kRtcpTransportFeedback, std::move(rtcp_packet)));
221 }
222 break;
223 }
224 default:
225 break;
226 }
227 rtcp_parser.Iterate();
228 packet_type = rtcp_parser.PacketType();
229 }
terelius88e64e52016-07-19 01:51:06 -0700230 break;
231 }
232 case ParsedRtcEventLog::LOG_START: {
233 break;
234 }
235 case ParsedRtcEventLog::LOG_END: {
236 break;
237 }
238 case ParsedRtcEventLog::BWE_PACKET_LOSS_EVENT: {
terelius8058e582016-07-25 01:32:41 -0700239 BwePacketLossEvent bwe_update;
240 bwe_update.timestamp = parsed_log_.GetTimestamp(i);
241 parsed_log_.GetBwePacketLossEvent(i, &bwe_update.new_bitrate,
242 &bwe_update.fraction_loss,
243 &bwe_update.expected_packets);
244 bwe_loss_updates_.push_back(bwe_update);
terelius88e64e52016-07-19 01:51:06 -0700245 break;
246 }
247 case ParsedRtcEventLog::BWE_PACKET_DELAY_EVENT: {
248 break;
249 }
250 case ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT: {
251 break;
252 }
253 case ParsedRtcEventLog::UNKNOWN_EVENT: {
254 break;
255 }
256 }
terelius54ce6802016-07-13 06:44:41 -0700257 }
terelius88e64e52016-07-19 01:51:06 -0700258
terelius54ce6802016-07-13 06:44:41 -0700259 if (last_timestamp < first_timestamp) {
260 // No useful events in the log.
261 first_timestamp = last_timestamp = 0;
262 }
263 begin_time_ = first_timestamp;
264 end_time_ = last_timestamp;
tereliusdc35dcd2016-08-01 12:03:27 -0700265 call_duration_s_ = static_cast<float>(end_time_ - begin_time_) / 1000000;
terelius54ce6802016-07-13 06:44:41 -0700266}
267
Stefan Holmer13181032016-07-29 14:48:54 +0200268class BitrateObserver : public CongestionController::Observer,
269 public RemoteBitrateObserver {
270 public:
271 BitrateObserver() : last_bitrate_bps_(0), bitrate_updated_(false) {}
272
273 void OnNetworkChanged(uint32_t bitrate_bps,
274 uint8_t fraction_loss,
275 int64_t rtt_ms) override {
276 last_bitrate_bps_ = bitrate_bps;
277 bitrate_updated_ = true;
278 }
279
280 void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs,
281 uint32_t bitrate) override {}
282
283 uint32_t last_bitrate_bps() const { return last_bitrate_bps_; }
284 bool GetAndResetBitrateUpdated() {
285 bool bitrate_updated = bitrate_updated_;
286 bitrate_updated_ = false;
287 return bitrate_updated;
288 }
289
290 private:
291 uint32_t last_bitrate_bps_;
292 bool bitrate_updated_;
293};
294
terelius0740a202016-08-08 10:21:04 -0700295bool EventLogAnalyzer::IsRtxSsrc(StreamId stream_id) {
296 return rtx_ssrcs_.count(stream_id) == 1;
297}
298
299bool EventLogAnalyzer::IsVideoSsrc(StreamId stream_id) {
300 return video_ssrcs_.count(stream_id) == 1;
301}
302
303bool EventLogAnalyzer::IsAudioSsrc(StreamId stream_id) {
304 return audio_ssrcs_.count(stream_id) == 1;
305}
306
terelius54ce6802016-07-13 06:44:41 -0700307void EventLogAnalyzer::CreatePacketGraph(PacketDirection desired_direction,
308 Plot* plot) {
309 std::map<uint32_t, TimeSeries> time_series;
310
311 PacketDirection direction;
312 MediaType media_type;
313 uint8_t header[IP_PACKET_SIZE];
314 size_t header_length, total_length;
terelius54ce6802016-07-13 06:44:41 -0700315
316 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
317 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
318 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
319 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
320 &header_length, &total_length);
321 if (direction == desired_direction) {
322 // Parse header to get SSRC.
323 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
324 RTPHeader parsed_header;
325 rtp_parser.Parse(&parsed_header);
326 // Filter on SSRC.
327 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
328 uint64_t timestamp = parsed_log_.GetTimestamp(i);
329 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
330 float y = total_length;
terelius54ce6802016-07-13 06:44:41 -0700331 time_series[parsed_header.ssrc].points.push_back(
332 TimeSeriesPoint(x, y));
333 }
334 }
335 }
336 }
337
338 // Set labels and put in graph.
339 for (auto& kv : time_series) {
340 kv.second.label = SsrcToString(kv.first);
341 kv.second.style = BAR_GRAPH;
tereliusdc35dcd2016-08-01 12:03:27 -0700342 plot->series_list_.push_back(std::move(kv.second));
terelius54ce6802016-07-13 06:44:41 -0700343 }
344
tereliusdc35dcd2016-08-01 12:03:27 -0700345 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
346 plot->SetSuggestedYAxis(0, 1, "Packet size (bytes)", kBottomMargin,
347 kTopMargin);
terelius54ce6802016-07-13 06:44:41 -0700348 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700349 plot->SetTitle("Incoming RTP packets");
terelius54ce6802016-07-13 06:44:41 -0700350 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700351 plot->SetTitle("Outgoing RTP packets");
terelius54ce6802016-07-13 06:44:41 -0700352 }
353}
354
355// For each SSRC, plot the time between the consecutive playouts.
356void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) {
357 std::map<uint32_t, TimeSeries> time_series;
358 std::map<uint32_t, uint64_t> last_playout;
359
360 uint32_t ssrc;
terelius54ce6802016-07-13 06:44:41 -0700361
362 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
363 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
364 if (event_type == ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
365 parsed_log_.GetAudioPlayout(i, &ssrc);
366 uint64_t timestamp = parsed_log_.GetTimestamp(i);
367 if (MatchingSsrc(ssrc, desired_ssrc_)) {
368 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
369 float y = static_cast<float>(timestamp - last_playout[ssrc]) / 1000;
370 if (time_series[ssrc].points.size() == 0) {
371 // There were no previusly logged playout for this SSRC.
372 // Generate a point, but place it on the x-axis.
373 y = 0;
374 }
terelius54ce6802016-07-13 06:44:41 -0700375 time_series[ssrc].points.push_back(TimeSeriesPoint(x, y));
376 last_playout[ssrc] = timestamp;
377 }
378 }
379 }
380
381 // Set labels and put in graph.
382 for (auto& kv : time_series) {
383 kv.second.label = SsrcToString(kv.first);
384 kv.second.style = BAR_GRAPH;
tereliusdc35dcd2016-08-01 12:03:27 -0700385 plot->series_list_.push_back(std::move(kv.second));
terelius54ce6802016-07-13 06:44:41 -0700386 }
387
tereliusdc35dcd2016-08-01 12:03:27 -0700388 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
389 plot->SetSuggestedYAxis(0, 1, "Time since last playout (ms)", kBottomMargin,
390 kTopMargin);
391 plot->SetTitle("Audio playout");
terelius54ce6802016-07-13 06:44:41 -0700392}
393
394// For each SSRC, plot the time between the consecutive playouts.
395void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) {
396 std::map<uint32_t, TimeSeries> time_series;
397 std::map<uint32_t, uint16_t> last_seqno;
398
399 PacketDirection direction;
400 MediaType media_type;
401 uint8_t header[IP_PACKET_SIZE];
402 size_t header_length, total_length;
403
terelius54ce6802016-07-13 06:44:41 -0700404 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
405 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
406 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
407 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
408 &header_length, &total_length);
409 uint64_t timestamp = parsed_log_.GetTimestamp(i);
410 if (direction == PacketDirection::kIncomingPacket) {
411 // Parse header to get SSRC.
412 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
413 RTPHeader parsed_header;
414 rtp_parser.Parse(&parsed_header);
415 // Filter on SSRC.
416 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
417 float x = static_cast<float>(timestamp - begin_time_) / 1000000;
418 int y = WrappingDifference(parsed_header.sequenceNumber,
419 last_seqno[parsed_header.ssrc], 1ul << 16);
420 if (time_series[parsed_header.ssrc].points.size() == 0) {
421 // There were no previusly logged playout for this SSRC.
422 // Generate a point, but place it on the x-axis.
423 y = 0;
424 }
terelius54ce6802016-07-13 06:44:41 -0700425 time_series[parsed_header.ssrc].points.push_back(
426 TimeSeriesPoint(x, y));
427 last_seqno[parsed_header.ssrc] = parsed_header.sequenceNumber;
428 }
429 }
430 }
431 }
432
433 // Set labels and put in graph.
434 for (auto& kv : time_series) {
435 kv.second.label = SsrcToString(kv.first);
436 kv.second.style = BAR_GRAPH;
tereliusdc35dcd2016-08-01 12:03:27 -0700437 plot->series_list_.push_back(std::move(kv.second));
terelius54ce6802016-07-13 06:44:41 -0700438 }
439
tereliusdc35dcd2016-08-01 12:03:27 -0700440 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
441 plot->SetSuggestedYAxis(0, 1, "Difference since last packet", kBottomMargin,
442 kTopMargin);
443 plot->SetTitle("Sequence number");
terelius54ce6802016-07-13 06:44:41 -0700444}
445
446void EventLogAnalyzer::CreateDelayChangeGraph(Plot* plot) {
terelius88e64e52016-07-19 01:51:06 -0700447 for (auto& kv : rtp_packets_) {
448 StreamId stream_id = kv.first;
449 // Filter on direction and SSRC.
450 if (stream_id.GetDirection() != kIncomingPacket ||
451 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
452 continue;
453 }
terelius54ce6802016-07-13 06:44:41 -0700454
terelius88e64e52016-07-19 01:51:06 -0700455 TimeSeries time_series;
456 time_series.label = SsrcToString(stream_id.GetSsrc());
457 time_series.style = BAR_GRAPH;
458 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
459 int64_t last_abs_send_time = 0;
460 int64_t last_timestamp = 0;
461 for (const LoggedRtpPacket& packet : packet_stream) {
462 if (packet.header.extension.hasAbsoluteSendTime) {
463 int64_t send_time_diff =
464 WrappingDifference(packet.header.extension.absoluteSendTime,
465 last_abs_send_time, 1ul << 24);
466 int64_t recv_time_diff = packet.timestamp - last_timestamp;
467
468 last_abs_send_time = packet.header.extension.absoluteSendTime;
469 last_timestamp = packet.timestamp;
470
471 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
472 double y =
473 static_cast<double>(recv_time_diff -
474 AbsSendTimeToMicroseconds(send_time_diff)) /
475 1000;
476 if (time_series.points.size() == 0) {
477 // There were no previously logged packets for this SSRC.
478 // Generate a point, but place it on the x-axis.
479 y = 0;
terelius54ce6802016-07-13 06:44:41 -0700480 }
terelius88e64e52016-07-19 01:51:06 -0700481 time_series.points.emplace_back(x, y);
terelius54ce6802016-07-13 06:44:41 -0700482 }
483 }
terelius88e64e52016-07-19 01:51:06 -0700484 // Add the data set to the plot.
tereliusdc35dcd2016-08-01 12:03:27 -0700485 plot->series_list_.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700486 }
487
tereliusdc35dcd2016-08-01 12:03:27 -0700488 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
489 plot->SetSuggestedYAxis(0, 1, "Latency change (ms)", kBottomMargin,
490 kTopMargin);
491 plot->SetTitle("Network latency change between consecutive packets");
terelius54ce6802016-07-13 06:44:41 -0700492}
493
494void EventLogAnalyzer::CreateAccumulatedDelayChangeGraph(Plot* plot) {
terelius88e64e52016-07-19 01:51:06 -0700495 for (auto& kv : rtp_packets_) {
496 StreamId stream_id = kv.first;
497 // Filter on direction and SSRC.
498 if (stream_id.GetDirection() != kIncomingPacket ||
499 !MatchingSsrc(stream_id.GetSsrc(), desired_ssrc_)) {
500 continue;
501 }
502 TimeSeries time_series;
503 time_series.label = SsrcToString(stream_id.GetSsrc());
504 time_series.style = LINE_GRAPH;
505 const std::vector<LoggedRtpPacket>& packet_stream = kv.second;
506 int64_t last_abs_send_time = 0;
507 int64_t last_timestamp = 0;
508 double accumulated_delay_ms = 0;
509 for (const LoggedRtpPacket& packet : packet_stream) {
510 if (packet.header.extension.hasAbsoluteSendTime) {
511 int64_t send_time_diff =
512 WrappingDifference(packet.header.extension.absoluteSendTime,
513 last_abs_send_time, 1ul << 24);
514 int64_t recv_time_diff = packet.timestamp - last_timestamp;
terelius54ce6802016-07-13 06:44:41 -0700515
terelius88e64e52016-07-19 01:51:06 -0700516 last_abs_send_time = packet.header.extension.absoluteSendTime;
517 last_timestamp = packet.timestamp;
518
519 float x = static_cast<float>(packet.timestamp - begin_time_) / 1000000;
520 accumulated_delay_ms +=
521 static_cast<double>(recv_time_diff -
522 AbsSendTimeToMicroseconds(send_time_diff)) /
523 1000;
524 if (time_series.points.size() == 0) {
525 // There were no previously logged packets for this SSRC.
526 // Generate a point, but place it on the x-axis.
527 accumulated_delay_ms = 0;
terelius54ce6802016-07-13 06:44:41 -0700528 }
terelius88e64e52016-07-19 01:51:06 -0700529 time_series.points.emplace_back(x, accumulated_delay_ms);
terelius54ce6802016-07-13 06:44:41 -0700530 }
531 }
terelius88e64e52016-07-19 01:51:06 -0700532 // Add the data set to the plot.
tereliusdc35dcd2016-08-01 12:03:27 -0700533 plot->series_list_.push_back(std::move(time_series));
terelius54ce6802016-07-13 06:44:41 -0700534 }
535
tereliusdc35dcd2016-08-01 12:03:27 -0700536 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
537 plot->SetSuggestedYAxis(0, 1, "Latency change (ms)", kBottomMargin,
538 kTopMargin);
539 plot->SetTitle("Accumulated network latency change");
terelius54ce6802016-07-13 06:44:41 -0700540}
541
tereliusf736d232016-08-04 10:00:11 -0700542// Plot the fraction of packets lost (as perceived by the loss-based BWE).
543void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) {
544 plot->series_list_.push_back(TimeSeries());
545 for (auto& bwe_update : bwe_loss_updates_) {
546 float x = static_cast<float>(bwe_update.timestamp - begin_time_) / 1000000;
547 float y = static_cast<float>(bwe_update.fraction_loss) / 255 * 100;
548 plot->series_list_.back().points.emplace_back(x, y);
549 }
550 plot->series_list_.back().label = "Fraction lost";
551 plot->series_list_.back().style = LINE_DOT_GRAPH;
552
553 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
554 plot->SetSuggestedYAxis(0, 10, "Percent lost packets", kBottomMargin,
555 kTopMargin);
556 plot->SetTitle("Reported packet loss");
557}
558
terelius54ce6802016-07-13 06:44:41 -0700559// Plot the total bandwidth used by all RTP streams.
560void EventLogAnalyzer::CreateTotalBitrateGraph(
561 PacketDirection desired_direction,
562 Plot* plot) {
563 struct TimestampSize {
564 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
565 uint64_t timestamp;
566 size_t size;
567 };
568 std::vector<TimestampSize> packets;
569
570 PacketDirection direction;
571 size_t total_length;
572
573 // Extract timestamps and sizes for the relevant packets.
574 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
575 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
576 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
577 parsed_log_.GetRtpHeader(i, &direction, nullptr, nullptr, nullptr,
578 &total_length);
579 if (direction == desired_direction) {
580 uint64_t timestamp = parsed_log_.GetTimestamp(i);
581 packets.push_back(TimestampSize(timestamp, total_length));
582 }
583 }
584 }
585
586 size_t window_index_begin = 0;
587 size_t window_index_end = 0;
588 size_t bytes_in_window = 0;
terelius54ce6802016-07-13 06:44:41 -0700589
590 // Calculate a moving average of the bitrate and store in a TimeSeries.
tereliusdc35dcd2016-08-01 12:03:27 -0700591 plot->series_list_.push_back(TimeSeries());
terelius54ce6802016-07-13 06:44:41 -0700592 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
593 while (window_index_end < packets.size() &&
594 packets[window_index_end].timestamp < time) {
595 bytes_in_window += packets[window_index_end].size;
596 window_index_end++;
597 }
598 while (window_index_begin < packets.size() &&
599 packets[window_index_begin].timestamp < time - window_duration_) {
600 RTC_DCHECK_LE(packets[window_index_begin].size, bytes_in_window);
601 bytes_in_window -= packets[window_index_begin].size;
602 window_index_begin++;
603 }
604 float window_duration_in_seconds =
605 static_cast<float>(window_duration_) / 1000000;
606 float x = static_cast<float>(time - begin_time_) / 1000000;
607 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
tereliusdc35dcd2016-08-01 12:03:27 -0700608 plot->series_list_.back().points.push_back(TimeSeriesPoint(x, y));
terelius54ce6802016-07-13 06:44:41 -0700609 }
610
611 // Set labels.
612 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700613 plot->series_list_.back().label = "Incoming bitrate";
terelius54ce6802016-07-13 06:44:41 -0700614 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700615 plot->series_list_.back().label = "Outgoing bitrate";
terelius54ce6802016-07-13 06:44:41 -0700616 }
tereliusdc35dcd2016-08-01 12:03:27 -0700617 plot->series_list_.back().style = LINE_GRAPH;
terelius54ce6802016-07-13 06:44:41 -0700618
terelius8058e582016-07-25 01:32:41 -0700619 // Overlay the send-side bandwidth estimate over the outgoing bitrate.
620 if (desired_direction == kOutgoingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700621 plot->series_list_.push_back(TimeSeries());
terelius8058e582016-07-25 01:32:41 -0700622 for (auto& bwe_update : bwe_loss_updates_) {
623 float x =
624 static_cast<float>(bwe_update.timestamp - begin_time_) / 1000000;
625 float y = static_cast<float>(bwe_update.new_bitrate) / 1000;
tereliusdc35dcd2016-08-01 12:03:27 -0700626 plot->series_list_.back().points.emplace_back(x, y);
terelius8058e582016-07-25 01:32:41 -0700627 }
tereliusdc35dcd2016-08-01 12:03:27 -0700628 plot->series_list_.back().label = "Loss-based estimate";
629 plot->series_list_.back().style = LINE_GRAPH;
terelius8058e582016-07-25 01:32:41 -0700630 }
tereliusdc35dcd2016-08-01 12:03:27 -0700631 plot->series_list_.back().style = LINE_GRAPH;
632 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
633 plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin);
terelius54ce6802016-07-13 06:44:41 -0700634 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700635 plot->SetTitle("Incoming RTP bitrate");
terelius54ce6802016-07-13 06:44:41 -0700636 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700637 plot->SetTitle("Outgoing RTP bitrate");
terelius54ce6802016-07-13 06:44:41 -0700638 }
639}
640
641// For each SSRC, plot the bandwidth used by that stream.
642void EventLogAnalyzer::CreateStreamBitrateGraph(
643 PacketDirection desired_direction,
644 Plot* plot) {
645 struct TimestampSize {
646 TimestampSize(uint64_t t, size_t s) : timestamp(t), size(s) {}
647 uint64_t timestamp;
648 size_t size;
649 };
terelius88e64e52016-07-19 01:51:06 -0700650 std::map<uint32_t, std::vector<TimestampSize>> packets;
terelius54ce6802016-07-13 06:44:41 -0700651
652 PacketDirection direction;
653 MediaType media_type;
654 uint8_t header[IP_PACKET_SIZE];
655 size_t header_length, total_length;
656
657 // Extract timestamps and sizes for the relevant packets.
658 for (size_t i = 0; i < parsed_log_.GetNumberOfEvents(); i++) {
659 ParsedRtcEventLog::EventType event_type = parsed_log_.GetEventType(i);
660 if (event_type == ParsedRtcEventLog::RTP_EVENT) {
661 parsed_log_.GetRtpHeader(i, &direction, &media_type, header,
662 &header_length, &total_length);
663 if (direction == desired_direction) {
664 // Parse header to get SSRC.
665 RtpUtility::RtpHeaderParser rtp_parser(header, header_length);
666 RTPHeader parsed_header;
667 rtp_parser.Parse(&parsed_header);
668 // Filter on SSRC.
669 if (MatchingSsrc(parsed_header.ssrc, desired_ssrc_)) {
670 uint64_t timestamp = parsed_log_.GetTimestamp(i);
671 packets[parsed_header.ssrc].push_back(
672 TimestampSize(timestamp, total_length));
673 }
674 }
675 }
676 }
677
terelius54ce6802016-07-13 06:44:41 -0700678 for (auto& kv : packets) {
679 size_t window_index_begin = 0;
680 size_t window_index_end = 0;
681 size_t bytes_in_window = 0;
682
683 // Calculate a moving average of the bitrate and store in a TimeSeries.
tereliusdc35dcd2016-08-01 12:03:27 -0700684 plot->series_list_.push_back(TimeSeries());
terelius54ce6802016-07-13 06:44:41 -0700685 for (uint64_t time = begin_time_; time < end_time_ + step_; time += step_) {
686 while (window_index_end < kv.second.size() &&
687 kv.second[window_index_end].timestamp < time) {
688 bytes_in_window += kv.second[window_index_end].size;
689 window_index_end++;
690 }
691 while (window_index_begin < kv.second.size() &&
692 kv.second[window_index_begin].timestamp <
693 time - window_duration_) {
694 RTC_DCHECK_LE(kv.second[window_index_begin].size, bytes_in_window);
695 bytes_in_window -= kv.second[window_index_begin].size;
696 window_index_begin++;
697 }
698 float window_duration_in_seconds =
699 static_cast<float>(window_duration_) / 1000000;
700 float x = static_cast<float>(time - begin_time_) / 1000000;
701 float y = bytes_in_window * 8 / window_duration_in_seconds / 1000;
tereliusdc35dcd2016-08-01 12:03:27 -0700702 plot->series_list_.back().points.push_back(TimeSeriesPoint(x, y));
terelius54ce6802016-07-13 06:44:41 -0700703 }
704
705 // Set labels.
tereliusdc35dcd2016-08-01 12:03:27 -0700706 plot->series_list_.back().label = SsrcToString(kv.first);
707 plot->series_list_.back().style = LINE_GRAPH;
terelius54ce6802016-07-13 06:44:41 -0700708 }
709
tereliusdc35dcd2016-08-01 12:03:27 -0700710 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
711 plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin);
terelius54ce6802016-07-13 06:44:41 -0700712 if (desired_direction == webrtc::PacketDirection::kIncomingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700713 plot->SetTitle("Incoming bitrate per stream");
terelius54ce6802016-07-13 06:44:41 -0700714 } else if (desired_direction == webrtc::PacketDirection::kOutgoingPacket) {
tereliusdc35dcd2016-08-01 12:03:27 -0700715 plot->SetTitle("Outgoing bitrate per stream");
terelius54ce6802016-07-13 06:44:41 -0700716 }
717}
718
Stefan Holmer13181032016-07-29 14:48:54 +0200719void EventLogAnalyzer::CreateBweGraph(Plot* plot) {
720 std::map<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
721 std::map<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
722
723 for (const auto& kv : rtp_packets_) {
724 if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
725 for (const LoggedRtpPacket& rtp_packet : kv.second)
726 outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
727 }
728 }
729
730 for (const auto& kv : rtcp_packets_) {
731 if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
732 for (const LoggedRtcpPacket& rtcp_packet : kv.second)
733 incoming_rtcp.insert(
734 std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
735 }
736 }
737
738 SimulatedClock clock(0);
739 BitrateObserver observer;
740 RtcEventLogNullImpl null_event_log;
741 CongestionController cc(&clock, &observer, &observer, &null_event_log);
742 // TODO(holmer): Log the call config and use that here instead.
743 static const uint32_t kDefaultStartBitrateBps = 300000;
744 cc.SetBweBitrates(0, kDefaultStartBitrateBps, -1);
745
746 TimeSeries time_series;
747 time_series.label = "BWE";
748 time_series.style = LINE_DOT_GRAPH;
Stefan Holmer13181032016-07-29 14:48:54 +0200749
750 auto rtp_iterator = outgoing_rtp.begin();
751 auto rtcp_iterator = incoming_rtcp.begin();
752
753 auto NextRtpTime = [&]() {
754 if (rtp_iterator != outgoing_rtp.end())
755 return static_cast<int64_t>(rtp_iterator->first);
756 return std::numeric_limits<int64_t>::max();
757 };
758
759 auto NextRtcpTime = [&]() {
760 if (rtcp_iterator != incoming_rtcp.end())
761 return static_cast<int64_t>(rtcp_iterator->first);
762 return std::numeric_limits<int64_t>::max();
763 };
764
765 auto NextProcessTime = [&]() {
766 if (rtcp_iterator != incoming_rtcp.end() ||
767 rtp_iterator != outgoing_rtp.end()) {
768 return clock.TimeInMicroseconds() +
769 std::max<int64_t>(cc.TimeUntilNextProcess() * 1000, 0);
770 }
771 return std::numeric_limits<int64_t>::max();
772 };
773
774 int64_t time_us = std::min(NextRtpTime(), NextRtcpTime());
775 while (time_us != std::numeric_limits<int64_t>::max()) {
776 clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
777 if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
stefanc3de0332016-08-02 07:22:17 -0700778 RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime());
Stefan Holmer13181032016-07-29 14:48:54 +0200779 const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
780 if (rtcp.type == kRtcpTransportFeedback) {
781 cc.GetTransportFeedbackObserver()->OnTransportFeedback(
782 *static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
783 }
784 ++rtcp_iterator;
785 }
786 if (clock.TimeInMicroseconds() >= NextRtpTime()) {
stefanc3de0332016-08-02 07:22:17 -0700787 RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime());
Stefan Holmer13181032016-07-29 14:48:54 +0200788 const LoggedRtpPacket& rtp = *rtp_iterator->second;
789 if (rtp.header.extension.hasTransportSequenceNumber) {
790 RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
791 cc.GetTransportFeedbackObserver()->AddPacket(
792 rtp.header.extension.transportSequenceNumber, rtp.total_length, 0);
793 rtc::SentPacket sent_packet(
794 rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
795 cc.OnSentPacket(sent_packet);
796 }
797 ++rtp_iterator;
798 }
stefanc3de0332016-08-02 07:22:17 -0700799 if (clock.TimeInMicroseconds() >= NextProcessTime()) {
800 RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextProcessTime());
Stefan Holmer13181032016-07-29 14:48:54 +0200801 cc.Process();
stefanc3de0332016-08-02 07:22:17 -0700802 }
Stefan Holmer13181032016-07-29 14:48:54 +0200803 if (observer.GetAndResetBitrateUpdated()) {
804 uint32_t y = observer.last_bitrate_bps() / 1000;
Stefan Holmer13181032016-07-29 14:48:54 +0200805 float x = static_cast<float>(clock.TimeInMicroseconds() - begin_time_) /
806 1000000;
807 time_series.points.emplace_back(x, y);
808 }
809 time_us = std::min({NextRtpTime(), NextRtcpTime(), NextProcessTime()});
810 }
811 // Add the data set to the plot.
tereliusdc35dcd2016-08-01 12:03:27 -0700812 plot->series_list_.push_back(std::move(time_series));
Stefan Holmer13181032016-07-29 14:48:54 +0200813
tereliusdc35dcd2016-08-01 12:03:27 -0700814 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
815 plot->SetSuggestedYAxis(0, 10, "Bitrate (kbps)", kBottomMargin, kTopMargin);
816 plot->SetTitle("Simulated BWE behavior");
Stefan Holmer13181032016-07-29 14:48:54 +0200817}
818
stefanc3de0332016-08-02 07:22:17 -0700819void EventLogAnalyzer::CreateNetworkDelayFeebackGraph(Plot* plot) {
820 std::map<uint64_t, const LoggedRtpPacket*> outgoing_rtp;
821 std::map<uint64_t, const LoggedRtcpPacket*> incoming_rtcp;
822
823 for (const auto& kv : rtp_packets_) {
824 if (kv.first.GetDirection() == PacketDirection::kOutgoingPacket) {
825 for (const LoggedRtpPacket& rtp_packet : kv.second)
826 outgoing_rtp.insert(std::make_pair(rtp_packet.timestamp, &rtp_packet));
827 }
828 }
829
830 for (const auto& kv : rtcp_packets_) {
831 if (kv.first.GetDirection() == PacketDirection::kIncomingPacket) {
832 for (const LoggedRtcpPacket& rtcp_packet : kv.second)
833 incoming_rtcp.insert(
834 std::make_pair(rtcp_packet.timestamp, &rtcp_packet));
835 }
836 }
837
838 SimulatedClock clock(0);
839 TransportFeedbackAdapter feedback_adapter(nullptr, &clock);
840
841 TimeSeries time_series;
842 time_series.label = "Network Delay Change";
843 time_series.style = LINE_DOT_GRAPH;
844 int64_t estimated_base_delay_ms = std::numeric_limits<int64_t>::max();
845
846 auto rtp_iterator = outgoing_rtp.begin();
847 auto rtcp_iterator = incoming_rtcp.begin();
848
849 auto NextRtpTime = [&]() {
850 if (rtp_iterator != outgoing_rtp.end())
851 return static_cast<int64_t>(rtp_iterator->first);
852 return std::numeric_limits<int64_t>::max();
853 };
854
855 auto NextRtcpTime = [&]() {
856 if (rtcp_iterator != incoming_rtcp.end())
857 return static_cast<int64_t>(rtcp_iterator->first);
858 return std::numeric_limits<int64_t>::max();
859 };
860
861 int64_t time_us = std::min(NextRtpTime(), NextRtcpTime());
862 while (time_us != std::numeric_limits<int64_t>::max()) {
863 clock.AdvanceTimeMicroseconds(time_us - clock.TimeInMicroseconds());
864 if (clock.TimeInMicroseconds() >= NextRtcpTime()) {
865 RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtcpTime());
866 const LoggedRtcpPacket& rtcp = *rtcp_iterator->second;
867 if (rtcp.type == kRtcpTransportFeedback) {
868 std::vector<PacketInfo> feedback =
869 feedback_adapter.GetPacketFeedbackVector(
870 *static_cast<rtcp::TransportFeedback*>(rtcp.packet.get()));
871 for (const PacketInfo& packet : feedback) {
872 int64_t y = packet.arrival_time_ms - packet.send_time_ms;
873 float x =
874 static_cast<float>(clock.TimeInMicroseconds() - begin_time_) /
875 1000000;
876 estimated_base_delay_ms = std::min(y, estimated_base_delay_ms);
877 time_series.points.emplace_back(x, y);
878 }
879 }
880 ++rtcp_iterator;
881 }
882 if (clock.TimeInMicroseconds() >= NextRtpTime()) {
883 RTC_DCHECK_EQ(clock.TimeInMicroseconds(), NextRtpTime());
884 const LoggedRtpPacket& rtp = *rtp_iterator->second;
885 if (rtp.header.extension.hasTransportSequenceNumber) {
886 RTC_DCHECK(rtp.header.extension.hasTransportSequenceNumber);
887 feedback_adapter.AddPacket(rtp.header.extension.transportSequenceNumber,
888 rtp.total_length, 0);
889 feedback_adapter.OnSentPacket(
890 rtp.header.extension.transportSequenceNumber, rtp.timestamp / 1000);
891 }
892 ++rtp_iterator;
893 }
894 time_us = std::min(NextRtpTime(), NextRtcpTime());
895 }
896 // We assume that the base network delay (w/o queues) is the min delay
897 // observed during the call.
898 for (TimeSeriesPoint& point : time_series.points)
899 point.y -= estimated_base_delay_ms;
900 // Add the data set to the plot.
901 plot->series_list_.push_back(std::move(time_series));
902
903 plot->SetXAxis(0, call_duration_s_, "Time (s)", kLeftMargin, kRightMargin);
904 plot->SetSuggestedYAxis(0, 10, "Delay (ms)", kBottomMargin, kTopMargin);
905 plot->SetTitle("Network Delay Change.");
906}
terelius54ce6802016-07-13 06:44:41 -0700907} // namespace plotting
908} // namespace webrtc