OLD | NEW |
---|---|
1 # Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 1 # Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
2 # | 2 # |
3 # Use of this source code is governed by a BSD-style license | 3 # Use of this source code is governed by a BSD-style license |
4 # that can be found in the LICENSE file in the root of the source | 4 # that can be found in the LICENSE file in the root of the source |
5 # tree. An additional intellectual property rights grant can be found | 5 # tree. An additional intellectual property rights grant can be found |
6 # in the file PATENTS. All contributing project authors may | 6 # in the file PATENTS. All contributing project authors may |
7 # be found in the AUTHORS file in the root of the source tree. | 7 # be found in the AUTHORS file in the root of the source tree. |
8 | 8 |
9 """Displays statistics and plots graphs from RTC protobuf dump.""" | 9 """Displays statistics and plots graphs from RTC protobuf dump.""" |
10 | 10 |
11 from __future__ import division | 11 from __future__ import division |
12 from __future__ import print_function | 12 from __future__ import print_function |
13 | 13 |
14 import collections | 14 import collections |
15 import optparse | 15 import optparse |
16 import sys | 16 import sys |
17 | 17 |
18 import matplotlib.pyplot as plt | 18 import matplotlib.pyplot as plt |
19 import numpy | 19 import numpy |
20 | 20 |
21 import misc | 21 import misc |
22 import pb_parse | 22 import pb_parse |
23 | 23 |
24 | 24 |
25 class RTPStatistics(object): | 25 class RTPStatistics(object): |
26 """Has methods for calculating and plotting RTP stream statistics.""" | 26 """Has methods for calculating and plotting RTP stream statistics.""" |
27 | 27 |
28 BANDWIDTH_SMOOTHING_WINDOW_SIZE = 10 | 28 BANDWIDTH_SMOOTHING_WINDOW_SIZE = 10 |
29 PLOT_RESOLUTION_MS = 50 | |
29 | 30 |
30 def __init__(self, data_points): | 31 def __init__(self, data_points): |
31 """Initializes object with data_points and computes simple statistics. | 32 """Initializes object with data_points and computes simple statistics. |
32 | 33 |
33 Computes percentages of number of packets and packet sizes by | 34 Computes percentages of number of packets and packet sizes by |
34 SSRC. | 35 SSRC. |
35 | 36 |
36 Args: | 37 Args: |
37 data_points: list of pb_parse.DataPoints on which statistics are | 38 data_points: list of pb_parse.DataPoints on which statistics are |
38 calculated. | 39 calculated. |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
163 if freq is None or always_query_sample_rate: | 164 if freq is None or always_query_sample_rate: |
164 if not always_query_sample_rate: | 165 if not always_query_sample_rate: |
165 print ("Frequency could not be guessed.", end=" ") | 166 print ("Frequency could not be guessed.", end=" ") |
166 freq = int(misc.get_input("Input frequency (in kHz)> ")) | 167 freq = int(misc.get_input("Input frequency (in kHz)> ")) |
167 else: | 168 else: |
168 print("Guessed frequency: {}kHz".format(freq)) | 169 print("Guessed frequency: {}kHz".format(freq)) |
169 | 170 |
170 for point in self.data_points: | 171 for point in self.data_points: |
171 point.real_send_time_ms = (point.timestamp - | 172 point.real_send_time_ms = (point.timestamp - |
172 self.data_points[0].timestamp) / freq | 173 self.data_points[0].timestamp) / freq |
173 point.delay = point.arrival_timestamp_ms -point.real_send_time_ms | 174 point.delay = point.arrival_timestamp_ms - point.real_send_time_ms |
174 | 175 |
175 def print_duration_statistics(self): | 176 def print_duration_statistics(self): |
176 """Prints delay, clock drift and bitrate statistics.""" | 177 """Prints delay, clock drift and bitrate statistics.""" |
177 | 178 |
178 min_delay = min(point.delay for point in self.data_points) | 179 min_delay = min(point.delay for point in self.data_points) |
179 | 180 |
180 for point in self.data_points: | 181 for point in self.data_points: |
181 point.absdelay = point.delay - min_delay | 182 point.absdelay = point.delay - min_delay |
182 | 183 |
183 stream_duration_sender = self.data_points[-1].real_send_time_ms / 1000 | 184 stream_duration_sender = self.data_points[-1].real_send_time_ms / 1000 |
(...skipping 30 matching lines...) Expand all Loading... | |
214 last = point | 215 last = point |
215 self.data_points = data_points_ordered | 216 self.data_points = data_points_ordered |
216 | 217 |
217 def compute_bandwidth(self): | 218 def compute_bandwidth(self): |
218 """Computes bandwidth averaged over several consecutive packets. | 219 """Computes bandwidth averaged over several consecutive packets. |
219 | 220 |
220 The number of consecutive packets used in the average is | 221 The number of consecutive packets used in the average is |
221 BANDWIDTH_SMOOTHING_WINDOW_SIZE. Averaging is done with | 222 BANDWIDTH_SMOOTHING_WINDOW_SIZE. Averaging is done with |
222 numpy.correlate. | 223 numpy.correlate. |
223 """ | 224 """ |
224 self.bandwidth_kbps = [] | 225 start_ms = self.data_points[0].real_send_time_ms |
225 for i in range(len(self.data_points) - 1): | 226 stop_ms = self.data_points[-1].real_send_time_ms |
226 self.bandwidth_kbps.append(self.data_points[i].size * 8 / | 227 (self.bandwidth_kbps, _) = numpy.histogram( |
227 (self.data_points[i + | 228 [point.real_send_time_ms for point in self.data_points], |
228 1].real_send_time_ms - | 229 bins=numpy.arange(start_ms, stop_ms, |
229 self.data_points[i].real_send_time_ms) | 230 RTPStatistics.PLOT_RESOLUTION_MS), |
230 ) | 231 weights=[point.size * 8 / RTPStatistics.PLOT_RESOLUTION_MS |
232 for point in self.data_points] | |
233 ) | |
231 correlate_filter = (numpy.ones( | 234 correlate_filter = (numpy.ones( |
232 RTPStatistics.BANDWIDTH_SMOOTHING_WINDOW_SIZE) / | 235 RTPStatistics.BANDWIDTH_SMOOTHING_WINDOW_SIZE) / |
233 RTPStatistics.BANDWIDTH_SMOOTHING_WINDOW_SIZE) | 236 RTPStatistics.BANDWIDTH_SMOOTHING_WINDOW_SIZE) |
234 self.smooth_bw_kbps = numpy.correlate(self.bandwidth_kbps, correlate_filter) | 237 self.smooth_bw_kbps = numpy.correlate(self.bandwidth_kbps, correlate_filter) |
235 | 238 |
236 def plot_statistics(self): | 239 def plot_statistics(self): |
237 """Plots changes in delay and average bandwidth.""" | 240 """Plots changes in delay and average bandwidth.""" |
241 | |
242 start_ms = self.data_points[0].real_send_time_ms | |
243 stop_ms = self.data_points[-1].real_send_time_ms | |
244 time_axis = numpy.arange(start_ms / 1000, stop_ms / 1000, | |
245 RTPStatistics.PLOT_RESOLUTION_MS / 1000) | |
246 | |
247 delay = calculate_delay(start_ms, stop_ms, | |
248 RTPStatistics.PLOT_RESOLUTION_MS, | |
phoglund
2016/07/28 06:28:55
Nit: indent
aleloi
2016/07/28 08:41:46
Done.
| |
249 self.data_points) | |
250 | |
238 plt.figure(1) | 251 plt.figure(1) |
239 plt.plot([f.real_send_time_ms / 1000 for f in self.data_points], | 252 plt.plot(time_axis, delay) |
240 [f.absdelay for f in self.data_points]) | |
241 plt.xlabel("Send time [s]") | 253 plt.xlabel("Send time [s]") |
242 plt.ylabel("Relative transport delay [ms]") | 254 plt.ylabel("Relative transport delay [ms]") |
243 | 255 |
244 plt.figure(2) | 256 plt.figure(2) |
245 plt.plot([f.real_send_time_ms / 1000 for f in | 257 plt.plot(time_axis[:len(self.smooth_bw_kbps)], self.smooth_bw_kbps) |
246 self.data_points][:len(self.smooth_bw_kbps)], | |
247 self.smooth_bw_kbps[:len(self.data_points)]) | |
248 plt.xlabel("Send time [s]") | 258 plt.xlabel("Send time [s]") |
249 plt.ylabel("Bandwidth [kbps]") | 259 plt.ylabel("Bandwidth [kbps]") |
250 | 260 |
251 plt.show() | 261 plt.show() |
252 | 262 |
253 | 263 |
264 def calculate_delay(start, stop, step, points): | |
265 """Regularizes the time coordinates for the delay. | |
266 | |
267 Regularizes points by rounding the timestamps to the nearest point | |
phoglund
2016/07/28 06:28:55
Isn't this "quantizing"?
stefan-webrtc
2016/07/28 07:13:36
Perhaps you can use numpy's digitize instead?
http
aleloi
2016/07/28 08:41:46
I'd rather not use that, I think. Based on the des
aleloi
2016/07/28 08:41:46
Wikipedia confirms that "quantizing" is the correc
| |
268 in the time sequence start, start+step, start+2*step... Takes the | |
269 average of the delays of points rounded to the same. Returns | |
270 masked array, in which time points with no value are masked. | |
271 """ | |
272 grouped_delays = [[] for _ in numpy.arange(start, stop, step)] | |
273 rounded_value_index = lambda x: int((x - start) / step) | |
274 for point in points: | |
275 grouped_delays[rounded_value_index(point.real_send_time_ms) | |
276 ].append(point.absdelay) | |
277 regularized_delays = [numpy.average(arr) if arr else None for arr in | |
278 grouped_delays] | |
279 return numpy.ma.masked_values(regularized_delays, None) | |
280 | |
281 | |
254 def main(): | 282 def main(): |
255 usage = "Usage: %prog [options] <filename of rtc event log>" | 283 usage = "Usage: %prog [options] <filename of rtc event log>" |
256 parser = optparse.OptionParser(usage=usage) | 284 parser = optparse.OptionParser(usage=usage) |
257 parser.add_option("--dump_header_to_stdout", | 285 parser.add_option("--dump_header_to_stdout", |
258 default=False, action="store_true", | 286 default=False, action="store_true", |
259 help="print header info to stdout; similar to rtp_analyze") | 287 help="print header info to stdout; similar to rtp_analyze") |
260 parser.add_option("--query_sample_rate", | 288 parser.add_option("--query_sample_rate", |
261 default=False, action="store_true", | 289 default=False, action="store_true", |
262 help="always query user for real sample rate") | 290 help="always query user for real sample rate") |
263 | 291 |
(...skipping 19 matching lines...) Expand all Loading... | |
283 print("Statistics:") | 311 print("Statistics:") |
284 rtp_stats.print_sequence_number_statistics() | 312 rtp_stats.print_sequence_number_statistics() |
285 rtp_stats.estimate_frequency(options.query_sample_rate) | 313 rtp_stats.estimate_frequency(options.query_sample_rate) |
286 rtp_stats.print_duration_statistics() | 314 rtp_stats.print_duration_statistics() |
287 rtp_stats.remove_reordered() | 315 rtp_stats.remove_reordered() |
288 rtp_stats.compute_bandwidth() | 316 rtp_stats.compute_bandwidth() |
289 rtp_stats.plot_statistics() | 317 rtp_stats.plot_statistics() |
290 | 318 |
291 if __name__ == "__main__": | 319 if __name__ == "__main__": |
292 main() | 320 main() |
OLD | NEW |