OLD | NEW |
| (Empty) |
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>Peer connection constraints: <input id="pc-constraints" type="text"></p> | |
44 <p>Force TURN: <input id="force-turn" type="checkbox" checked></p> | |
45 <p><input id="launcher-button" type="button" value="Run test"> | |
46 <div id="test-status" style="display:none"></div> | |
47 <div id="dashboard"> | |
48 <div id="control-category"></div> | |
49 <div id="chart"></div> | |
50 <div id="control-range"></div> | |
51 </div> | |
52 </div> | |
53 <script> | |
54 google.load('visualization', '1.0', {'packages':['controls']}); | |
55 | |
56 var durationInput = document.getElementById('duration'); | |
57 var maxVideoBitrateInput = document.getElementById('max-video-bitrate'); | |
58 var forceTurnInput = document.getElementById('force-turn'); | |
59 var launcherButton = document.getElementById('launcher-button'); | |
60 var autoModeInput = document.createElement('input'); | |
61 var testStatus = document.getElementById('test-status'); | |
62 var pcConstraintsInput = document.getElementById('pc-constraints'); | |
63 | |
64 launcherButton.onclick = start; | |
65 | |
66 // Load parameters from the url if present. This allows one to link to | |
67 // a specific test configuration and is used to automatically pass parameters | |
68 // for scripts such as record-test.sh | |
69 function getURLParameter(name, default_value) { | |
70 var search = | |
71 RegExp('(^\\?|&)' + name + '=' + '(.+?)(&|$)').exec(location.search); | |
72 if (search) | |
73 return decodeURI(search[2]); | |
74 else | |
75 return default_value; | |
76 } | |
77 | |
78 durationInput.value = getURLParameter('duration', 10); | |
79 maxVideoBitrateInput.value = getURLParameter('max-video-bitrate', 2000); | |
80 forceTurnInput.checked = (getURLParameter('force-turn', 'true') === 'true'); | |
81 autoModeInput.checked = (getURLParameter('auto-mode', 'false') === 'true'); | |
82 pcConstraintsInput.value = getURLParameter('pc-constraints', ''); | |
83 | |
84 if (autoModeInput.checked) start(); | |
85 | |
86 function start() { | |
87 var durationMs = parseInt(durationInput.value) * 1000; | |
88 var maxVideoBitrateKbps = parseInt(maxVideoBitrateInput.value); | |
89 var forceTurn = forceTurnInput.checked; | |
90 var autoClose = autoModeInput.checked; | |
91 var pcConstraints = pcConstraintsInput.value == "" ? | |
92 null : JSON.parse(pcConstraintsInput.value); | |
93 | |
94 var updateStatusInterval; | |
95 var testFinished = false; | |
96 function updateStatus() { | |
97 if (testFinished) { | |
98 testStatus.innerHTML = 'Test finished'; | |
99 if (updateStatusInterval) { | |
100 clearInterval(updateStatusInterval); | |
101 updateStatusInterval = null; | |
102 } | |
103 } else { | |
104 if (!updateStatusInterval) { | |
105 updateStatusInterval = setInterval(updateStatus, 1000); | |
106 testStatus.innerHTML = 'Running'; | |
107 } | |
108 testStatus.innerHTML += '.'; | |
109 } | |
110 } | |
111 | |
112 if (!(isFinite(maxVideoBitrateKbps) && maxVideoBitrateKbps > 0)) { | |
113 // TODO(andresp): Get a better way to show errors than alert. | |
114 alert("Invalid max video bitrate"); | |
115 return; | |
116 } | |
117 | |
118 if (!(isFinite(durationMs) && durationMs > 0)) { | |
119 alert("Invalid duration"); | |
120 return; | |
121 } | |
122 | |
123 durationInput.disabled = true; | |
124 forceTurnInput.disabled = true; | |
125 maxVideoBitrateInput.disabled = true; | |
126 launcherButton.style.display = 'none'; | |
127 testStatus.style.display = 'block'; | |
128 | |
129 getUserMedia({audio:true, video:true}, | |
130 gotStream, function() {}); | |
131 | |
132 function gotStream(stream) { | |
133 updateStatus(); | |
134 var test = new LoopbackTest(stream, durationMs, | |
135 forceTurn, | |
136 pcConstraints, | |
137 maxVideoBitrateKbps); | |
138 test.run(onTestFinished.bind(test)); | |
139 } | |
140 | |
141 function onTestFinished() { | |
142 testFinished = true; | |
143 updateStatus(); | |
144 if (autoClose) { | |
145 window.close(); | |
146 } else { | |
147 plotStats(this.getResults()); | |
148 } | |
149 } | |
150 } | |
151 | |
152 function plotStats(data) { | |
153 var dashboard = new google.visualization.Dashboard( | |
154 document.getElementById('dashboard')); | |
155 | |
156 var chart = new google.visualization.ChartWrapper({ | |
157 'containerId': 'chart', | |
158 'chartType': 'LineChart', | |
159 'options': { 'pointSize': 0, 'lineWidth': 1, 'interpolateNulls': true }, | |
160 }); | |
161 | |
162 var rangeFilter = new google.visualization.ControlWrapper({ | |
163 'controlType': 'ChartRangeFilter', | |
164 'containerId': 'control-range', | |
165 'options': { | |
166 'filterColumnIndex': 0, | |
167 'ui': { | |
168 'chartType': 'ScatterChart', | |
169 'chartOptions': { | |
170 'hAxis': {'baselineColor': 'none'} | |
171 }, | |
172 'chartView': { | |
173 'columns': [0, 1] | |
174 }, | |
175 'minRangeSize': 1000 // 1 second | |
176 } | |
177 }, | |
178 }); | |
179 | |
180 // Create a table with the columns of the dataset. | |
181 var columnsTable = new google.visualization.DataTable(); | |
182 columnsTable.addColumn('number', 'columnIndex'); | |
183 columnsTable.addColumn('string', 'columnLabel'); | |
184 var initState = {selectedValues: []}; | |
185 for (var i = 1; i < data.getNumberOfColumns(); i++) { | |
186 columnsTable.addRow([i, data.getColumnLabel(i)]); | |
187 initState.selectedValues.push(data.getColumnLabel(i)); | |
188 } | |
189 | |
190 var columnFilter = new google.visualization.ControlWrapper({ | |
191 controlType: 'CategoryFilter', | |
192 containerId: 'control-category', | |
193 dataTable: columnsTable, | |
194 options: { | |
195 filterColumnLabel: 'columnLabel', | |
196 ui: { | |
197 label: '', | |
198 allowNone: false, | |
199 selectedValuesLayout: 'aside' | |
200 } | |
201 }, | |
202 state: initState | |
203 }); | |
204 google.visualization.events.addListener(columnFilter, 'statechange', | |
205 function () { | |
206 var state = columnFilter.getState(); | |
207 var row; | |
208 var columnIndices = [0]; | |
209 for (var i = 0; i < state.selectedValues.length; i++) { | |
210 row = columnsTable.getFilteredRows([{ | |
211 column: 1, | |
212 value: state.selectedValues[i]}])[0]; | |
213 columnIndices.push(columnsTable.getValue(row, 0)); | |
214 } | |
215 // Sort the indices into their original order | |
216 columnIndices.sort(function (a, b) { return (a - b); }); | |
217 chart.setView({columns: columnIndices}); | |
218 chart.draw(); | |
219 }); | |
220 | |
221 columnFilter.draw(); | |
222 dashboard.bind([rangeFilter], [chart]); | |
223 dashboard.draw(data); | |
224 } | |
225 </script> | |
226 </body> | |
227 </html> | |
OLD | NEW |