12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This file contains helper methods to draw the stats timeline graphs. 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Each graph represents a series of stats report for a PeerConnection, 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// e.g. 1234-0-ssrc-abcd123-bytesSent is the graph for the series of bytesSent 92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// for ssrc-abcd123 of PeerConnection 0 in process 1234. 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The graphs are drawn as CANVAS, grouped per report type per PeerConnection. 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Each group has an expand/collapse button and is collapsed initially. 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)<include src="timeline_graph_view.js"/> 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var STATS_GRAPH_CONTAINER_HEADING_CLASS = 'stats-graph-container-heading'; 17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)var RECEIVED_PROPAGATION_DELTA_LABEL = 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 'googReceivedPacketGroupPropagationDeltaDebug'; 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)var RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL = 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 'googReceivedPacketGroupArrivalTimeDebug'; 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Specifies which stats should be drawn on the 'bweCompound' graph and how. 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var bweCompoundGraphConfig = { 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) googAvailableSendBandwidth: {color: 'red'}, 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) googTargetEncBitrateCorrected: {color: 'purple'}, 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) googActualEncBitrate: {color: 'orange'}, 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) googRetransmitBitrate: {color: 'blue'}, 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) googTransmitBitrate: {color: 'green'}, 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Converts the last entry of |srcDataSeries| from the total amount to the 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// amount per second. 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var totalToPerSecond = function(srcDataSeries) { 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var length = srcDataSeries.dataPoints_.length; 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (length >= 2) { 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var lastDataPoint = srcDataSeries.dataPoints_[length - 1]; 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var secondLastDataPoint = srcDataSeries.dataPoints_[length - 2]; 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return (lastDataPoint.value - secondLastDataPoint.value) * 1000 / 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) (lastDataPoint.time - secondLastDataPoint.time); 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 0; 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Converts the value of total bytes to bits per second. 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var totalBytesToBitsPerSecond = function(srcDataSeries) { 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return totalToPerSecond(srcDataSeries) * 8; 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Specifies which stats should be converted before drawn and how. 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |convertedName| is the name of the converted value, |convertFunction| 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// is the function used to calculate the new converted value based on the 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// original dataSeries. 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var dataConversionConfig = { 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) packetsSent: { 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertedName: 'packetsSentPerSecond', 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertFunction: totalToPerSecond, 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }, 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bytesSent: { 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertedName: 'bitsSentPerSecond', 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertFunction: totalBytesToBitsPerSecond, 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }, 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) packetsReceived: { 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertedName: 'packetsReceivedPerSecond', 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertFunction: totalToPerSecond, 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }, 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bytesReceived: { 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertedName: 'bitsReceivedPerSecond', 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertFunction: totalBytesToBitsPerSecond, 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }, 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // This is due to a bug of wrong units reported for googTargetEncBitrate. 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // TODO (jiayl): remove this when the unit bug is fixed. 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) googTargetEncBitrate: { 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertedName: 'googTargetEncBitrateCorrected', 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) convertFunction: function (srcDataSeries) { 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var length = srcDataSeries.dataPoints_.length; 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var lastDataPoint = srcDataSeries.dataPoints_[length - 1]; 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (lastDataPoint.value < 5000) 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return lastDataPoint.value * 1000; 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return lastDataPoint.value; 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 86a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 87a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)// The object contains the stats names that should not be added to the graph, 88a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)// even if they are numbers. 89a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)var statsNameBlackList = { 90a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 'ssrc': true, 91a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 'googTrackId': true, 92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 'googComponent': true, 93868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 'googLocalAddress': true, 94868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 'googRemoteAddress': true, 95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 'googFingerprint': true, 96a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}; 97a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var graphViews = {}; 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Returns number parsed from |value|, or NaN if the stats name is black-listed. 101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)function getNumberFromValue(name, value) { 102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (statsNameBlackList[name]) 103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return NaN; 104868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return parseFloat(value); 105868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 106868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 107b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Adds the stats report |report| to the timeline graph for the given 108b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// |peerConnectionElement|. 109b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)function drawSingleReport(peerConnectionElement, report) { 110b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) var reportType = report.type; 111b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) var reportId = report.id; 112b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) var stats = report.stats; 113b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!stats || !stats.values) 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return; 115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 116b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for (var i = 0; i < stats.values.length - 1; i = i + 2) { 117b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) var rawLabel = stats.values[i]; 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Propagation deltas are handled separately. 1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (rawLabel == RECEIVED_PROPAGATION_DELTA_LABEL) { 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) drawReceivedPropagationDelta( 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) peerConnectionElement, report, stats.values[i + 1]); 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) continue; 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var rawDataSeriesId = reportId + '-' + rawLabel; 125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) var rawValue = getNumberFromValue(rawLabel, stats.values[i + 1]); 126868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (isNaN(rawValue)) { 127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // We do not draw non-numerical values, but still want to record it in the 128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // data series. 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) addDataSeriesPoints(peerConnectionElement, 1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) rawDataSeriesId, 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) rawLabel, 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) [stats.timestamp], 1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) [stats.values[i + 1]]); 134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) continue; 135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var finalDataSeriesId = rawDataSeriesId; 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var finalLabel = rawLabel; 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var finalValue = rawValue; 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // We need to convert the value if dataConversionConfig[rawLabel] exists. 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (dataConversionConfig[rawLabel]) { 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Updates the original dataSeries before the conversion. 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) addDataSeriesPoints(peerConnectionElement, 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) rawDataSeriesId, 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) rawLabel, 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) [stats.timestamp], 1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) [rawValue]); 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Convert to another value to draw on graph, using the original 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // dataSeries as input. 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) finalValue = dataConversionConfig[rawLabel].convertFunction( 15290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionDataStore[peerConnectionElement.id].getDataSeries( 15390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) rawDataSeriesId)); 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) finalLabel = dataConversionConfig[rawLabel].convertedName; 15590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) finalDataSeriesId = reportId + '-' + finalLabel; 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Updates the final dataSeries to draw. 1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) addDataSeriesPoints(peerConnectionElement, 1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) finalDataSeriesId, 1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) finalLabel, 1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) [stats.timestamp], 1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) [finalValue]); 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Updates the graph. 1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var graphType = bweCompoundGraphConfig[finalLabel] ? 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'bweCompound' : finalLabel; 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var graphViewId = 169a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) peerConnectionElement.id + '-' + reportId + '-' + graphType; 1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!graphViews[graphViewId]) { 1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) graphViews[graphViewId] = createStatsGraphView(peerConnectionElement, 173b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) report, 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) graphType); 175b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) var date = new Date(stats.timestamp); 1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) graphViews[graphViewId].setDateRange(date, date); 1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Adds the new dataSeries to the graphView. We have to do it here to cover 1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // both the simple and compound graph cases. 18090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var dataSeries = 18190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionDataStore[peerConnectionElement.id].getDataSeries( 18290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) finalDataSeriesId); 18390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!graphViews[graphViewId].hasDataSeries(dataSeries)) 18490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) graphViews[graphViewId].addDataSeries(dataSeries); 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) graphViews[graphViewId].updateEndDate(); 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Makes sure the TimelineDataSeries with id |dataSeriesId| is created, 1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// and adds the new data points to it. |times| is the list of timestamps for 1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// each data point, and |values| is the list of the data point values. 1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function addDataSeriesPoints( 1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) peerConnectionElement, dataSeriesId, label, times, values) { 19490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var dataSeries = 19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionDataStore[peerConnectionElement.id].getDataSeries( 19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dataSeriesId); 19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!dataSeries) { 19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dataSeries = new TimelineDataSeries(); 19990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionDataStore[peerConnectionElement.id].setDataSeries( 20090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dataSeriesId, dataSeries); 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (bweCompoundGraphConfig[label]) { 20290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dataSeries.setColor(bweCompoundGraphConfig[label].color); 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (var i = 0; i < times.length; ++i) 2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dataSeries.addPoint(times[i], values[i]); 2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Draws the received propagation deltas using the packet group arrival time as 2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// the x-axis. For example, |report.stats.values| should be like 2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// ['googReceivedPacketGroupArrivalTimeDebug', '[123456, 234455, 344566]', 2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// 'googReceivedPacketGroupPropagationDeltaDebug', '[23, 45, 56]', ...]. 2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function drawReceivedPropagationDelta(peerConnectionElement, report, deltas) { 2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var reportId = report.id; 2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var stats = report.stats; 2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var times = null; 2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Find the packet group arrival times. 2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (var i = 0; i < stats.values.length - 1; i = i + 2) { 2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (stats.values[i] == RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL) { 2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) times = stats.values[i + 1]; 2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) break; 2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Unexpected. 2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (times == null) 2265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Convert |deltas| and |times| from strings to arrays of numbers. 2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) try { 2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) deltas = JSON.parse(deltas); 2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) times = JSON.parse(times); 2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } catch (e) { 2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) console.log(e); 2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return; 2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Update the data series. 2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var dataSeriesId = reportId + '-' + RECEIVED_PROPAGATION_DELTA_LABEL; 2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) addDataSeriesPoints( 2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) peerConnectionElement, 2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) dataSeriesId, 2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RECEIVED_PROPAGATION_DELTA_LABEL, 2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) times, 2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) deltas); 2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Update the graph. 2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var graphViewId = peerConnectionElement.id + '-' + reportId + '-' + 2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RECEIVED_PROPAGATION_DELTA_LABEL; 2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var date = new Date(times[times.length - 1]); 2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (!graphViews[graphViewId]) { 2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) graphViews[graphViewId] = createStatsGraphView( 2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) peerConnectionElement, 2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) report, 2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) RECEIVED_PROPAGATION_DELTA_LABEL); 2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) graphViews[graphViewId].setScale(10); 2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) graphViews[graphViewId].setDateRange(date, date); 2565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) var dataSeries = peerConnectionDataStore[peerConnectionElement.id] 2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) .getDataSeries(dataSeriesId); 2585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) graphViews[graphViewId].addDataSeries(dataSeries); 2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) graphViews[graphViewId].updateEndDate(date); 2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Ensures a div container to hold all stats graphs for one track is created as 264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// a child of |peerConnectionElement|. 265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)function ensureStatsGraphTopContainer(peerConnectionElement, report) { 2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var containerId = peerConnectionElement.id + '-' + 267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) report.type + '-' + report.id + '-graph-container'; 2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var container = $(containerId); 2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!container) { 270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container = document.createElement('details'); 2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) container.id = containerId; 272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container.className = 'stats-graph-container'; 2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) peerConnectionElement.appendChild(container); 275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container.innerHTML ='<summary><span></span></summary>'; 276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container.firstChild.firstChild.className = 277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) STATS_GRAPH_CONTAINER_HEADING_CLASS; 278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container.firstChild.firstChild.textContent = 279a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles) 'Stats graphs for ' + report.id; 280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 281b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (report.type == 'ssrc') { 282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) var ssrcInfoElement = document.createElement('div'); 283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container.firstChild.appendChild(ssrcInfoElement); 284b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ssrcInfoManager.populateSsrcInfo(ssrcInfoElement, 285b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) GetSsrcFromReport(report)); 286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return container; 2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Creates the container elements holding a timeline graph 2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// and the TimelineGraphView object. 293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function createStatsGraphView( 294b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) peerConnectionElement, report, statsName) { 2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var topContainer = ensureStatsGraphTopContainer(peerConnectionElement, 296b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) report); 2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 29890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var graphViewId = 29990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionElement.id + '-' + report.id + '-' + statsName; 3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var divId = graphViewId + '-div'; 3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var canvasId = graphViewId + '-canvas'; 3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var container = document.createElement("div"); 3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) container.className = 'stats-graph-sub-container'; 3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) topContainer.appendChild(container); 3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) container.innerHTML = '<div>' + statsName + '</div>' + 3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) '<div id=' + divId + '><canvas id=' + canvasId + '></canvas></div>'; 3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (statsName == 'bweCompound') { 309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) container.insertBefore( 31090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) createBweCompoundLegend(peerConnectionElement, report.id), 311c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) $(divId)); 3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return new TimelineGraphView(divId, canvasId); 3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Creates the legend section for the bweCompound graph. 3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns the legend element. 31890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)function createBweCompoundLegend(peerConnectionElement, reportId) { 3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var legend = document.createElement('div'); 3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (var prop in bweCompoundGraphConfig) { 3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var div = document.createElement('div'); 3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) legend.appendChild(div); 3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) div.innerHTML = '<input type=checkbox checked></input>' + prop; 3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) div.style.color = bweCompoundGraphConfig[prop].color; 32590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) div.dataSeriesId = reportId + '-' + prop; 3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) div.graphViewId = 32790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionElement.id + '-' + reportId + '-bweCompound'; 3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) div.firstChild.addEventListener('click', function(event) { 32990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var target = 33090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) peerConnectionDataStore[peerConnectionElement.id].getDataSeries( 33190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) event.target.parentNode.dataSeriesId); 3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) target.show(event.target.checked); 3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) graphViews[event.target.parentNode.graphViewId].repaint(); 3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }); 3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return legend; 3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 338