1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5var USER_MEDIA_TAB_ID = 'user-media-tab-id'; 6 7var tabView = null; 8var ssrcInfoManager = null; 9var peerConnectionUpdateTable = null; 10var statsTable = null; 11var dumpCreator = null; 12/** A map from peer connection id to the PeerConnectionRecord. */ 13var peerConnectionDataStore = {}; 14/** A list of getUserMedia requests. */ 15var userMediaRequests = []; 16 17/** A simple class to store the updates and stats data for a peer connection. */ 18var PeerConnectionRecord = (function() { 19 /** @constructor */ 20 function PeerConnectionRecord() { 21 /** @private */ 22 this.record_ = { 23 constraints: {}, 24 rtcConfiguration: [], 25 stats: {}, 26 updateLog: [], 27 url: '', 28 }; 29 }; 30 31 PeerConnectionRecord.prototype = { 32 /** @override */ 33 toJSON: function() { 34 return this.record_; 35 }, 36 37 /** 38 * Adds the initilization info of the peer connection. 39 * @param {string} url The URL of the web page owning the peer connection. 40 * @param {Array} rtcConfiguration 41 * @param {!Object} constraints Media constraints. 42 */ 43 initialize: function(url, rtcConfiguration, constraints) { 44 this.record_.url = url; 45 this.record_.rtcConfiguration = rtcConfiguration; 46 this.record_.constraints = constraints; 47 }, 48 49 /** 50 * @param {string} dataSeriesId The TimelineDataSeries identifier. 51 * @return {!TimelineDataSeries} 52 */ 53 getDataSeries: function(dataSeriesId) { 54 return this.record_.stats[dataSeriesId]; 55 }, 56 57 /** 58 * @param {string} dataSeriesId The TimelineDataSeries identifier. 59 * @param {!TimelineDataSeries} dataSeries The TimelineDataSeries to set to. 60 */ 61 setDataSeries: function(dataSeriesId, dataSeries) { 62 this.record_.stats[dataSeriesId] = dataSeries; 63 }, 64 65 /** 66 * @param {!Object} update The object contains keys "time", "type", and 67 * "value". 68 */ 69 addUpdate: function(update) { 70 var time = new Date(parseFloat(update.time)); 71 this.record_.updateLog.push({ 72 time: time.toLocaleString(), 73 type: update.type, 74 value: update.value, 75 }); 76 }, 77 }; 78 79 return PeerConnectionRecord; 80})(); 81 82// The maximum number of data points bufferred for each stats. Old data points 83// will be shifted out when the buffer is full. 84var MAX_STATS_DATA_POINT_BUFFER_SIZE = 1000; 85 86<include src="tab_view.js"/> 87<include src="data_series.js"/> 88<include src="ssrc_info_manager.js"/> 89<include src="stats_graph_helper.js"/> 90<include src="stats_table.js"/> 91<include src="peer_connection_update_table.js"/> 92<include src="dump_creator.js"/> 93 94 95function initialize() { 96 dumpCreator = new DumpCreator($('content-root')); 97 tabView = new TabView($('content-root')); 98 ssrcInfoManager = new SsrcInfoManager(); 99 peerConnectionUpdateTable = new PeerConnectionUpdateTable(); 100 statsTable = new StatsTable(ssrcInfoManager); 101 102 chrome.send('finishedDOMLoad'); 103 104 // Requests stats from all peer connections every second. 105 window.setInterval(requestStats, 1000); 106} 107document.addEventListener('DOMContentLoaded', initialize); 108 109 110/** Sends a request to the browser to get peer connection statistics. */ 111function requestStats() { 112 if (Object.keys(peerConnectionDataStore).length > 0) 113 chrome.send('getAllStats'); 114} 115 116 117/** 118 * A helper function for getting a peer connection element id. 119 * 120 * @param {!Object.<string, number>} data The object containing the pid and lid 121 * of the peer connection. 122 * @return {string} The peer connection element id. 123 */ 124function getPeerConnectionId(data) { 125 return data.pid + '-' + data.lid; 126} 127 128 129/** 130 * Extracts ssrc info from a setLocal/setRemoteDescription update. 131 * 132 * @param {!PeerConnectionUpdateEntry} data The peer connection update data. 133 */ 134function extractSsrcInfo(data) { 135 if (data.type == 'setLocalDescription' || 136 data.type == 'setRemoteDescription') { 137 ssrcInfoManager.addSsrcStreamInfo(data.value); 138 } 139} 140 141 142/** 143 * A helper function for appending a child element to |parent|. 144 * 145 * @param {!Element} parent The parent element. 146 * @param {string} tag The child element tag. 147 * @param {string} text The textContent of the new DIV. 148 * @return {!Element} the new DIV element. 149 */ 150function appendChildWithText(parent, tag, text) { 151 var child = document.createElement(tag); 152 child.textContent = text; 153 parent.appendChild(child); 154 return child; 155} 156 157/** 158 * Helper for adding a peer connection update. 159 * 160 * @param {Element} peerConnectionElement 161 * @param {!PeerConnectionUpdateEntry} update The peer connection update data. 162 */ 163function addPeerConnectionUpdate(peerConnectionElement, update) { 164 peerConnectionUpdateTable.addPeerConnectionUpdate(peerConnectionElement, 165 update); 166 extractSsrcInfo(update); 167 peerConnectionDataStore[peerConnectionElement.id].addUpdate(update); 168} 169 170 171/** Browser message handlers. */ 172 173 174/** 175 * Removes all information about a peer connection. 176 * 177 * @param {!Object.<string, number>} data The object containing the pid and lid 178 * of a peer connection. 179 */ 180function removePeerConnection(data) { 181 var element = $(getPeerConnectionId(data)); 182 if (element) { 183 delete peerConnectionDataStore[element.id]; 184 tabView.removeTab(element.id); 185 } 186} 187 188 189/** 190 * Adds a peer connection. 191 * 192 * @param {!Object} data The object containing the pid, lid, url, 193 * rtcConfiguration, and constraints of a peer connection. 194 */ 195function addPeerConnection(data) { 196 var id = getPeerConnectionId(data); 197 198 if (!peerConnectionDataStore[id]) { 199 peerConnectionDataStore[id] = new PeerConnectionRecord(); 200 } 201 peerConnectionDataStore[id].initialize( 202 data.url, data.rtcConfiguration, data.constraints); 203 204 var peerConnectionElement = $(id); 205 if (!peerConnectionElement) { 206 peerConnectionElement = tabView.addTab(id, data.url + ' [' + id + ']'); 207 } 208 peerConnectionElement.innerHTML = 209 '<p>' + data.url + ' ' + data.rtcConfiguration + ' ' + data.constraints + 210 '</p>'; 211 212 return peerConnectionElement; 213} 214 215 216/** 217 * Adds a peer connection update. 218 * 219 * @param {!PeerConnectionUpdateEntry} data The peer connection update data. 220 */ 221function updatePeerConnection(data) { 222 var peerConnectionElement = $(getPeerConnectionId(data)); 223 addPeerConnectionUpdate(peerConnectionElement, data); 224} 225 226 227/** 228 * Adds the information of all peer connections created so far. 229 * 230 * @param {Array.<!Object>} data An array of the information of all peer 231 * connections. Each array item contains pid, lid, url, rtcConfiguration, 232 * constraints, and an array of updates as the log. 233 */ 234function updateAllPeerConnections(data) { 235 for (var i = 0; i < data.length; ++i) { 236 var peerConnection = addPeerConnection(data[i]); 237 238 var log = data[i].log; 239 if (!log) 240 continue; 241 for (var j = 0; j < log.length; ++j) { 242 addPeerConnectionUpdate(peerConnection, log[j]); 243 } 244 } 245 requestStats(); 246} 247 248 249/** 250 * Handles the report of stats. 251 * 252 * @param {!Object} data The object containing pid, lid, and reports, where 253 * reports is an array of stats reports. Each report contains id, type, 254 * and stats, where stats is the object containing timestamp and values, 255 * which is an array of strings, whose even index entry is the name of the 256 * stat, and the odd index entry is the value. 257 */ 258function addStats(data) { 259 var peerConnectionElement = $(getPeerConnectionId(data)); 260 if (!peerConnectionElement) 261 return; 262 263 for (var i = 0; i < data.reports.length; ++i) { 264 var report = data.reports[i]; 265 statsTable.addStatsReport(peerConnectionElement, report); 266 drawSingleReport(peerConnectionElement, report); 267 } 268} 269 270 271/** 272 * Adds a getUserMedia request. 273 * 274 * @param {!Object} data The object containing rid {number}, pid {number}, 275 * origin {string}, audio {string}, video {string}. 276 */ 277function addGetUserMedia(data) { 278 userMediaRequests.push(data); 279 280 if (!$(USER_MEDIA_TAB_ID)) { 281 tabView.addTab(USER_MEDIA_TAB_ID, 'GetUserMedia Requests'); 282 } 283 284 var requestDiv = document.createElement('div'); 285 requestDiv.className = 'user-media-request-div-class'; 286 requestDiv.rid = data.rid; 287 $(USER_MEDIA_TAB_ID).appendChild(requestDiv); 288 289 appendChildWithText(requestDiv, 'div', 'Caller origin: ' + data.origin); 290 appendChildWithText(requestDiv, 'div', 'Caller process id: ' + data.pid); 291 appendChildWithText(requestDiv, 'span', 'Audio Constraints').style.fontWeight 292 = 'bold'; 293 appendChildWithText(requestDiv, 'div', data.audio); 294 295 appendChildWithText(requestDiv, 'span', 'Video Constraints').style.fontWeight 296 = 'bold'; 297 appendChildWithText(requestDiv, 'div', data.video); 298} 299 300 301/** 302 * Removes the getUserMedia requests from the specified |rid|. 303 * 304 * @param {!Object} data The object containing rid {number}, the render id. 305 */ 306function removeGetUserMediaForRenderer(data) { 307 for (var i = userMediaRequests.length - 1; i >= 0; --i) { 308 if (userMediaRequests[i].rid == data.rid) 309 userMediaRequests.splice(i, 1); 310 } 311 312 var requests = $(USER_MEDIA_TAB_ID).childNodes; 313 for (var i = 0; i < requests.length; ++i) { 314 if (requests[i].rid == data.rid) 315 $(USER_MEDIA_TAB_ID).removeChild(requests[i]); 316 317 } 318 if ($(USER_MEDIA_TAB_ID).childNodes.length == 0) 319 tabView.removeTab(USER_MEDIA_TAB_ID); 320} 321 322 323/** 324 * Notification that the AEC recording file selection dialog was cancelled, 325 * i.e. AEC has not been enabled. 326 */ 327function aecRecordingFileSelectionCancelled() { 328 dumpCreator.disableAecRecording(); 329} 330 331 332/** 333 * Set 334 */ 335function enableAecRecording() { 336 dumpCreator.enableAecRecording(); 337} 338