andresp@webrtc.org | 44eb87e | 2014-03-17 14:23:22 +0000 | [diff] [blame^] | 1 | <!DOCTYPE html> |
| 2 | <!-- |
| 3 | This page was created to help debug and study webrtc issues such as |
| 4 | bandwidth estimation problems. It allows one to easily launch a test |
| 5 | case that establishs a connection between 2 peer connections |
| 6 | --> |
| 7 | <html> |
| 8 | <head> |
| 9 | <title>Loopback test</title> |
| 10 | |
| 11 | <!-- In order to plot graphs, this tools uses google visualization API which is |
| 12 | loaded via goog.load provided by google api. --> |
| 13 | <script src="//www.google.com/jsapi"></script> |
| 14 | |
| 15 | <!-- This file is included to allow loopback_test.js instantiate a |
| 16 | RTCPeerConnection on a browser and version agnostic way. --> |
| 17 | <script src="adapter.js"></script> |
| 18 | |
| 19 | <!-- Provides class StatTracker used by loopback_test.js to keep track of |
| 20 | RTCPeerConnection stats --> |
| 21 | <script src="stat_tracker.js"></script> |
| 22 | |
| 23 | <!-- Provides LoopbackTest class which has the core logic for the test itself. |
| 24 | Such as: create 2 peer connections, establish a call, filter turn |
| 25 | candidates, constraint video bitrate etc. |
| 26 | --> |
| 27 | <script src="loopback_test.js"></script> |
| 28 | |
| 29 | <style> |
| 30 | #chart { |
| 31 | height: 400px; |
| 32 | } |
| 33 | |
| 34 | #control-range { |
| 35 | height: 100px; |
| 36 | } |
| 37 | </style> |
| 38 | </head> |
| 39 | <body> |
| 40 | <div id="test-launcher"> |
| 41 | <p>Duration (s): <input id="duration" type="text"></p> |
| 42 | <p>Max video bitrate (kbps): <input id="max-video-bitrate" type="text"></p> |
| 43 | <p>Force TURN: <input id="force-turn" type="checkbox" checked></p> |
| 44 | <p><input id="launcher-button" type="button" value="Run test"> |
| 45 | <div id="test-status" style="display:none"></div> |
| 46 | <div id="dashboard"> |
| 47 | <div id="control-category"></div> |
| 48 | <div id="chart"></div> |
| 49 | <div id="control-range"></div> |
| 50 | </div> |
| 51 | </div> |
| 52 | <script> |
| 53 | google.load('visualization', '1.0', {'packages':['controls']}); |
| 54 | |
| 55 | var durationInput = document.getElementById('duration'); |
| 56 | var maxVideoBitrateInput = document.getElementById('max-video-bitrate'); |
| 57 | var forceTurnInput = document.getElementById('force-turn'); |
| 58 | var launcherButton = document.getElementById('launcher-button'); |
| 59 | var autoModeInput = document.createElement('input'); |
| 60 | var testStatus = document.getElementById('test-status'); |
| 61 | |
| 62 | launcherButton.onclick = start; |
| 63 | |
| 64 | // Load parameters from the url if present. This allows one to link to |
| 65 | // a specific test configuration and is used to automatically pass parameters |
| 66 | // for scripts such as record-test.sh |
| 67 | function getURLParameter(name, default_value) { |
| 68 | var search = |
| 69 | RegExp('(^\\?|&)' + name + '=' + '(.+?)(&|$)').exec(location.search); |
| 70 | if (search) |
| 71 | return decodeURI(search[2]); |
| 72 | else |
| 73 | return default_value; |
| 74 | } |
| 75 | |
| 76 | durationInput.value = getURLParameter('duration', 10); |
| 77 | maxVideoBitrateInput.value = getURLParameter('max-video-bitrate', 2000); |
| 78 | forceTurnInput.checked = (getURLParameter('force-turn', 'true') === 'true'); |
| 79 | autoModeInput.checked = (getURLParameter('auto-mode', 'false') === 'true'); |
| 80 | |
| 81 | if (autoModeInput.checked) start(); |
| 82 | |
| 83 | function start() { |
| 84 | var durationMs = parseInt(durationInput.value) * 1000; |
| 85 | var maxVideoBitrateKbps = parseInt(maxVideoBitrateInput.value); |
| 86 | var forceTurn = forceTurnInput.checked; |
| 87 | var autoClose = autoModeInput.checked; |
| 88 | |
| 89 | var updateStatusInterval; |
| 90 | var testFinished = false; |
| 91 | function updateStatus() { |
| 92 | if (testFinished) { |
| 93 | testStatus.innerHTML = 'Test finished'; |
| 94 | if (updateStatusInterval) { |
| 95 | clearInterval(updateStatusInterval); |
| 96 | updateStatusInterval = null; |
| 97 | } |
| 98 | } else { |
| 99 | if (!updateStatusInterval) { |
| 100 | updateStatusInterval = setInterval(updateStatus, 1000); |
| 101 | testStatus.innerHTML = 'Running'; |
| 102 | } |
| 103 | testStatus.innerHTML += '.'; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | if (!(isFinite(maxVideoBitrateKbps) && maxVideoBitrateKbps > 0)) { |
| 108 | // TODO(andresp): Get a better way to show errors than alert. |
| 109 | alert("Invalid max video bitrate"); |
| 110 | return; |
| 111 | } |
| 112 | |
| 113 | if (!(isFinite(durationMs) && durationMs > 0)) { |
| 114 | alert("Invalid duration"); |
| 115 | return; |
| 116 | } |
| 117 | |
| 118 | durationInput.disabled = true; |
| 119 | forceTurnInput.disabled = true; |
| 120 | maxVideoBitrateInput.disabled = true; |
| 121 | launcherButton.style.display = 'none'; |
| 122 | testStatus.style.display = 'block'; |
| 123 | |
| 124 | getUserMedia({audio:true, video:true}, |
| 125 | gotStream, function() {}); |
| 126 | |
| 127 | function gotStream(stream) { |
| 128 | updateStatus(); |
| 129 | var test = new LoopbackTest(stream, durationMs, forceTurn, |
| 130 | maxVideoBitrateKbps); |
| 131 | test.run(onTestFinished.bind(test)); |
| 132 | } |
| 133 | |
| 134 | function onTestFinished() { |
| 135 | testFinished = true; |
| 136 | updateStatus(); |
| 137 | if (autoClose) { |
| 138 | window.close(); |
| 139 | } else { |
| 140 | plotStats(this.getResults()); |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | function plotStats(data) { |
| 146 | var dashboard = new google.visualization.Dashboard( |
| 147 | document.getElementById('dashboard')); |
| 148 | |
| 149 | var chart = new google.visualization.ChartWrapper({ |
| 150 | 'containerId': 'chart', |
| 151 | 'chartType': 'LineChart', |
| 152 | 'options': { 'pointSize': 0, 'lineWidth': 1, 'interpolateNulls': true }, |
| 153 | }); |
| 154 | |
| 155 | var rangeFilter = new google.visualization.ControlWrapper({ |
| 156 | 'controlType': 'ChartRangeFilter', |
| 157 | 'containerId': 'control-range', |
| 158 | 'options': { |
| 159 | 'filterColumnIndex': 0, |
| 160 | 'ui': { |
| 161 | 'chartType': 'ScatterChart', |
| 162 | 'chartOptions': { |
| 163 | 'hAxis': {'baselineColor': 'none'} |
| 164 | }, |
| 165 | 'chartView': { |
| 166 | 'columns': [0, 1] |
| 167 | }, |
| 168 | 'minRangeSize': 1000 // 1 second |
| 169 | } |
| 170 | }, |
| 171 | }); |
| 172 | |
| 173 | // Create a table with the columns of the dataset. |
| 174 | var columnsTable = new google.visualization.DataTable(); |
| 175 | columnsTable.addColumn('number', 'columnIndex'); |
| 176 | columnsTable.addColumn('string', 'columnLabel'); |
| 177 | var initState = {selectedValues: []}; |
| 178 | for (var i = 1; i < data.getNumberOfColumns(); i++) { |
| 179 | columnsTable.addRow([i, data.getColumnLabel(i)]); |
| 180 | initState.selectedValues.push(data.getColumnLabel(i)); |
| 181 | } |
| 182 | |
| 183 | var columnFilter = new google.visualization.ControlWrapper({ |
| 184 | controlType: 'CategoryFilter', |
| 185 | containerId: 'control-category', |
| 186 | dataTable: columnsTable, |
| 187 | options: { |
| 188 | filterColumnLabel: 'columnLabel', |
| 189 | ui: { |
| 190 | label: '', |
| 191 | allowNone: false, |
| 192 | selectedValuesLayout: 'aside' |
| 193 | } |
| 194 | }, |
| 195 | state: initState |
| 196 | }); |
| 197 | google.visualization.events.addListener(columnFilter, 'statechange', |
| 198 | function () { |
| 199 | var state = columnFilter.getState(); |
| 200 | var row; |
| 201 | var columnIndices = [0]; |
| 202 | for (var i = 0; i < state.selectedValues.length; i++) { |
| 203 | row = columnsTable.getFilteredRows([{ |
| 204 | column: 1, |
| 205 | value: state.selectedValues[i]}])[0]; |
| 206 | columnIndices.push(columnsTable.getValue(row, 0)); |
| 207 | } |
| 208 | // Sort the indices into their original order |
| 209 | columnIndices.sort(function (a, b) { return (a - b); }); |
| 210 | chart.setView({columns: columnIndices}); |
| 211 | chart.draw(); |
| 212 | }); |
| 213 | |
| 214 | columnFilter.draw(); |
| 215 | dashboard.bind([rangeFilter], [chart]); |
| 216 | dashboard.draw(data); |
| 217 | } |
| 218 | </script> |
| 219 | </body> |
| 220 | </html> |