1/**
2 * Copyright (c) 2014 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// StatTracker is a helper class to keep track of stats on a RTCPeerConnection
12// object. It uses google visualization datatables to keep the recorded samples
13// and simplify plugging them into graphs later.
14//
15// Usage example:
16//   var tracker = new StatTracker(pc, pollInterval);
17//   tracker.recordStat("EstimatedSendBitrate",
18//                      "bweforvideo", "googAvailableSendBandwidth");
19//   ...
20//   tracker.stop();
21//   tracker.dataTable(); // returns the recorded values. In this case
22//   a table with 2 columns { Time, EstimatedSendBitrate } and a row for each
23//   sample taken until stop() was called.
24//
25function StatTracker(pc, pollInterval) {
26  pollInterval = pollInterval || 250;
27
28  var dataTable = new google.visualization.DataTable();
29  var timeColumnIndex = dataTable.addColumn('datetime', 'Time');
30  var recording = true;
31
32  // Set of sampling functions. Functions registered here are called
33  // once per getStats with the given report and a rowIndex for the
34  // sample period so they can extract and record the tracked variables.
35  var samplingFunctions = {};
36
37  // Accessor to the current recorded stats.
38  this.dataTable = function() { return dataTable; }
39
40  // recordStat(varName, recordName, statName) adds a samplingFunction that
41  // records namedItem(recordName).stat(statName) from RTCStatsReport for each
42  // sample into a column named varName in the dataTable.
43  this.recordStat = function (varName, recordName, statName) {
44    var columnIndex = dataTable.addColumn('number', varName);
45    samplingFunctions[varName] = function (report, rowIndex) {
46      var sample;
47      var record = report.namedItem(recordName);
48      if (record) sample = record.stat(statName);
49      dataTable.setCell(rowIndex, columnIndex, sample);
50    }
51  }
52
53  // Stops the polling of stats from the peer connection.
54  this.stop = function() {
55    recording = false;
56  }
57
58  // RTCPeerConnection.getStats is asynchronous. In order to avoid having
59  // too many pending getStats requests going, this code only queues the
60  // next getStats with setTimeout after the previous one returns, instead
61  // of using setInterval.
62  function poll() {
63    pc.getStats(function (report) {
64      if (!recording) return;
65      setTimeout(poll, pollInterval);
66      var result = report.result();
67      if (result.length < 1) return;
68
69      var rowIndex = dataTable.addRow();
70      dataTable.setCell(rowIndex, timeColumnIndex, result[0].timestamp);
71      for (var v in samplingFunctions)
72        samplingFunctions[v](report, rowIndex);
73    });
74  }
75  setTimeout(poll, pollInterval);
76}
77
78/**
79 * Utility method to perform a full join between data tables from StatTracker.
80 */
81function mergeDataTable(dataTable1, dataTable2) {
82  function allColumns(cols) {
83    var a = [];
84    for (var i = 1; i < cols; ++i) a.push(i);
85    return a;
86  }
87  return google.visualization.data.join(
88      dataTable1,
89      dataTable2,
90      'full',
91      [[0, 0]],
92      allColumns(dataTable1.getNumberOfColumns()),
93      allColumns(dataTable2.getNumberOfColumns()));
94}
95