228 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
| <!DOCTYPE html>
 | |
| <!--
 | |
|   This page was created to help debug and study webrtc issues such as
 | |
|   bandwidth estimation problems. It allows one to easily launch a test
 | |
|   case that establishs a connection between 2 peer connections
 | |
| -->
 | |
| <html>
 | |
| <head>
 | |
| <title>Loopback test</title>
 | |
| 
 | |
| <!-- In order to plot graphs, this tools uses google visualization API which is
 | |
|      loaded via goog.load provided by google api. -->
 | |
| <script src="//www.google.com/jsapi"></script>
 | |
| 
 | |
| <!-- This file is included to allow loopback_test.js instantiate a
 | |
|      RTCPeerConnection on a browser and version agnostic way. -->
 | |
| <script src="adapter.js"></script>
 | |
| 
 | |
| <!-- Provides class StatTracker used by loopback_test.js to keep track of
 | |
|      RTCPeerConnection stats -->
 | |
| <script src="stat_tracker.js"></script>
 | |
| 
 | |
| <!-- Provides LoopbackTest class which has the core logic for the test itself.
 | |
|      Such as: create 2 peer connections, establish a call, filter turn
 | |
|      candidates, constraint video bitrate etc.
 | |
|   -->
 | |
| <script src="loopback_test.js"></script>
 | |
| 
 | |
| <style>
 | |
| #chart {
 | |
|   height: 400px;
 | |
| }
 | |
| 
 | |
| #control-range {
 | |
|   height: 100px;
 | |
| }
 | |
| </style>
 | |
| </head>
 | |
| <body>
 | |
| <div id="test-launcher">
 | |
|   <p>Duration (s): <input id="duration" type="text"></p>
 | |
|   <p>Max video bitrate (kbps): <input id="max-video-bitrate" type="text"></p>
 | |
|   <p>Peer connection constraints: <input id="pc-constraints" type="text"></p>
 | |
|   <p>Force TURN: <input id="force-turn" type="checkbox" checked></p>
 | |
|   <p><input id="launcher-button" type="button" value="Run test">
 | |
|   <div id="test-status" style="display:none"></div>
 | |
|   <div id="dashboard">
 | |
|     <div id="control-category"></div>
 | |
|     <div id="chart"></div>
 | |
|     <div id="control-range"></div>
 | |
|   </div>
 | |
| </div>
 | |
| <script>
 | |
| google.load('visualization', '1.0', {'packages':['controls']});
 | |
| 
 | |
| var durationInput = document.getElementById('duration');
 | |
| var maxVideoBitrateInput = document.getElementById('max-video-bitrate');
 | |
| var forceTurnInput = document.getElementById('force-turn');
 | |
| var launcherButton = document.getElementById('launcher-button');
 | |
| var autoModeInput = document.createElement('input');
 | |
| var testStatus = document.getElementById('test-status');
 | |
| var pcConstraintsInput = document.getElementById('pc-constraints');
 | |
| 
 | |
| launcherButton.onclick = start;
 | |
| 
 | |
| // Load parameters from the url if present. This allows one to link to
 | |
| // a specific test configuration and is used to automatically pass parameters
 | |
| // for scripts such as record-test.sh
 | |
| function getURLParameter(name, default_value) {
 | |
|   var search =
 | |
|       RegExp('(^\\?|&)' + name + '=' + '(.+?)(&|$)').exec(location.search);
 | |
|   if (search)
 | |
|     return decodeURI(search[2]);
 | |
|   else
 | |
|     return default_value;
 | |
| }
 | |
| 
 | |
| durationInput.value = getURLParameter('duration', 10);
 | |
| maxVideoBitrateInput.value = getURLParameter('max-video-bitrate', 2000);
 | |
| forceTurnInput.checked = (getURLParameter('force-turn', 'true') === 'true');
 | |
| autoModeInput.checked = (getURLParameter('auto-mode', 'false') === 'true');
 | |
| pcConstraintsInput.value = getURLParameter('pc-constraints', '');
 | |
| 
 | |
| if (autoModeInput.checked) start();
 | |
| 
 | |
| function start() {
 | |
|   var durationMs = parseInt(durationInput.value) * 1000;
 | |
|   var maxVideoBitrateKbps = parseInt(maxVideoBitrateInput.value);
 | |
|   var forceTurn = forceTurnInput.checked;
 | |
|   var autoClose = autoModeInput.checked;
 | |
|   var pcConstraints = pcConstraintsInput.value == "" ?
 | |
|                       null : JSON.parse(pcConstraintsInput.value);
 | |
| 
 | |
|   var updateStatusInterval;
 | |
|   var testFinished = false;
 | |
|   function updateStatus() {
 | |
|     if (testFinished) {
 | |
|       testStatus.innerHTML = 'Test finished';
 | |
|       if (updateStatusInterval) {
 | |
| 	clearInterval(updateStatusInterval);
 | |
|         updateStatusInterval = null;
 | |
|       }
 | |
|     } else {
 | |
|       if (!updateStatusInterval) {
 | |
|         updateStatusInterval = setInterval(updateStatus, 1000);
 | |
|         testStatus.innerHTML = 'Running';
 | |
|       }
 | |
|       testStatus.innerHTML += '.';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!(isFinite(maxVideoBitrateKbps) && maxVideoBitrateKbps > 0)) {
 | |
|     // TODO(andresp): Get a better way to show errors than alert.
 | |
|     alert("Invalid max video bitrate");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!(isFinite(durationMs) && durationMs > 0)) {
 | |
|     alert("Invalid duration");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   durationInput.disabled = true;
 | |
|   forceTurnInput.disabled = true;
 | |
|   maxVideoBitrateInput.disabled = true;
 | |
|   launcherButton.style.display = 'none';
 | |
|   testStatus.style.display = 'block';
 | |
| 
 | |
|   getUserMedia({audio:true, video:true},
 | |
|                 gotStream, function() {});
 | |
| 
 | |
|   function gotStream(stream) {
 | |
|     updateStatus();
 | |
|     var test = new LoopbackTest(stream, durationMs,
 | |
|                                 forceTurn,
 | |
|                                 pcConstraints,
 | |
|                                 maxVideoBitrateKbps);
 | |
|     test.run(onTestFinished.bind(test));
 | |
|   }
 | |
| 
 | |
|   function onTestFinished() {
 | |
|     testFinished = true;
 | |
|     updateStatus();
 | |
|     if (autoClose) {
 | |
|       window.close();
 | |
|     } else {
 | |
|       plotStats(this.getResults());
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| function plotStats(data) {
 | |
|   var dashboard = new google.visualization.Dashboard(
 | |
|       document.getElementById('dashboard'));
 | |
| 
 | |
|   var chart = new google.visualization.ChartWrapper({
 | |
|       'containerId': 'chart',
 | |
|       'chartType': 'LineChart',
 | |
|       'options': { 'pointSize': 0, 'lineWidth': 1, 'interpolateNulls': true },
 | |
|     });
 | |
| 
 | |
|   var rangeFilter = new google.visualization.ControlWrapper({
 | |
|      'controlType': 'ChartRangeFilter',
 | |
|      'containerId': 'control-range',
 | |
|      'options': {
 | |
|        'filterColumnIndex': 0,
 | |
|        'ui': {
 | |
|          'chartType': 'ScatterChart',
 | |
|          'chartOptions': {
 | |
|            'hAxis': {'baselineColor': 'none'}
 | |
|          },
 | |
|          'chartView': {
 | |
|            'columns': [0, 1]
 | |
|          },
 | |
|          'minRangeSize': 1000 // 1 second
 | |
|        }
 | |
|      },
 | |
|    });
 | |
| 
 | |
|   // Create a table with the columns of the dataset.
 | |
|   var columnsTable = new google.visualization.DataTable();
 | |
|   columnsTable.addColumn('number', 'columnIndex');
 | |
|   columnsTable.addColumn('string', 'columnLabel');
 | |
|   var initState = {selectedValues: []};
 | |
|   for (var i = 1; i < data.getNumberOfColumns(); i++) {
 | |
|     columnsTable.addRow([i, data.getColumnLabel(i)]);
 | |
|     initState.selectedValues.push(data.getColumnLabel(i));
 | |
|   }
 | |
| 
 | |
|   var columnFilter = new google.visualization.ControlWrapper({
 | |
|     controlType: 'CategoryFilter',
 | |
|     containerId: 'control-category',
 | |
|     dataTable: columnsTable,
 | |
|     options: {
 | |
|       filterColumnLabel: 'columnLabel',
 | |
|       ui: {
 | |
|         label: '',
 | |
|         allowNone: false,
 | |
|         selectedValuesLayout: 'aside'
 | |
|       }
 | |
|     },
 | |
|     state: initState
 | |
|   });
 | |
|   google.visualization.events.addListener(columnFilter, 'statechange',
 | |
|     function () {
 | |
|       var state = columnFilter.getState();
 | |
|       var row;
 | |
|       var columnIndices = [0];
 | |
|       for (var i = 0; i < state.selectedValues.length; i++) {
 | |
|         row = columnsTable.getFilteredRows([{
 | |
|             column: 1,
 | |
|             value: state.selectedValues[i]}])[0];
 | |
|         columnIndices.push(columnsTable.getValue(row, 0));
 | |
|       }
 | |
|       // Sort the indices into their original order
 | |
|       columnIndices.sort(function (a, b) { return (a - b); });
 | |
|       chart.setView({columns: columnIndices});
 | |
|       chart.draw();
 | |
|     });
 | |
| 
 | |
|   columnFilter.draw();
 | |
|   dashboard.bind([rangeFilter], [chart]);
 | |
|   dashboard.draw(data);
 | |
| }
 | |
| </script>
 | |
| </body>
 | |
| </html>
 |