1/* 2 * Copyright 2017 The Chromium Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6/*jshint esversion: 6 */ 7 8'use strict'; 9 10const $ = document.getElementById.bind(document); 11 12function logError(err) { 13 console.error(err); 14} 15 16 17class FeedTable { 18 constructor() { 19 this.numCols = 5; 20 this.col = 0; 21 this.testTable = document.getElementById('test-table'); 22 this.row = this.testTable.insertRow(-1); 23 } 24 25 addNewAudioCell() { 26 if (this.col == this.numCols) { 27 this.row = this.testTable.insertRow(-1); 28 this.col = 0; 29 } 30 var newCell = this.row.insertCell(-1); 31 var audio = document.createElement('audio'); 32 audio.autoplay = false; 33 newCell.appendChild(audio); 34 this.col++; 35 return audio; 36 } 37} 38 39 40class PeerConnection { 41 constructor(audioElement) { 42 this.localConnection = null; 43 this.remoteConnection = null; 44 this.remoteAudio = audioElement; 45 } 46 47 start() { 48 const onGetUserMediaSuccess = this.onGetUserMediaSuccess.bind(this); 49 return navigator.mediaDevices 50 .getUserMedia({audio: true, video: true}) 51 .then(onGetUserMediaSuccess); 52 } 53 54 onGetUserMediaSuccess(stream) { 55 this.localConnection = new RTCPeerConnection(null); 56 this.localConnection.onicecandidate = (event) => { 57 this.onIceCandidate(this.remoteConnection, event); 58 }; 59 this.localConnection.addStream(stream); 60 61 this.remoteConnection = new RTCPeerConnection(null); 62 this.remoteConnection.onicecandidate = (event) => { 63 this.onIceCandidate(this.localConnection, event); 64 }; 65 this.remoteConnection.onaddstream = (e) => { 66 this.remoteAudio.srcObject = e.stream; 67 }; 68 69 var onCreateOfferSuccess = this.onCreateOfferSuccess.bind(this); 70 this.localConnection 71 .createOffer({offerToReceiveAudio: 1, offerToReceiveVideo: 1}) 72 .then(onCreateOfferSuccess, logError); 73 } 74 75 onCreateOfferSuccess(desc) { 76 this.localConnection.setLocalDescription(desc); 77 this.remoteConnection.setRemoteDescription(desc); 78 79 var onCreateAnswerSuccess = this.onCreateAnswerSuccess.bind(this); 80 this.remoteConnection.createAnswer().then(onCreateAnswerSuccess, logError); 81 } 82 83 onCreateAnswerSuccess(desc) { 84 this.remoteConnection.setLocalDescription(desc); 85 this.localConnection.setRemoteDescription(desc); 86 } 87 88 onIceCandidate(connection, event) { 89 if (event.candidate) { 90 connection.addIceCandidate(new RTCIceCandidate(event.candidate)); 91 } 92 } 93} 94 95 96class TestRunner { 97 constructor(runtimeSeconds) { 98 this.runtimeSeconds = runtimeSeconds; 99 this.audioElements = []; 100 this.peerConnections = []; 101 this.feedTable = new FeedTable(); 102 this.iteration = 0; 103 this.startTime; 104 this.lastIterationTime; 105 } 106 107 addPeerConnection() { 108 const audioElement = this.feedTable.addNewAudioCell(); 109 this.audioElements.push(audioElement); 110 this.peerConnections.push(new PeerConnection(audioElement)); 111 } 112 113 startTest() { 114 this.startTime = Date.now(); 115 let promises = testRunner.peerConnections.map((conn) => conn.start()); 116 Promise.all(promises) 117 .then(() => { 118 this.startTime = Date.now(); 119 this.audioElements.forEach((feed) => feed.play()); 120 this.pauseAndPlayLoop(); 121 }) 122 .catch((e) => {throw e}); 123 } 124 125 pauseAndPlayLoop() { 126 this.iteration++; 127 const status = this.getStatus(); 128 this.lastIterationTime = Date.now(); 129 $('status').textContent = status 130 if (status != 'ok-done') { 131 setTimeout(() => this.pauseAndPlayLoop()); 132 } else { 133 // Finished, pause the audio. 134 this.audioElements.forEach((feed) => feed.pause()); 135 } 136 } 137 138 getStatus() { 139 if (this.iteration == 0) { 140 return 'not-started'; 141 } 142 const timeSpent = Date.now() - this.startTime; 143 if (timeSpent >= this.runtimeSeconds * 1000) { 144 return 'ok-done'; 145 } else { 146 return `running, iteration: ${this.iteration}`; 147 } 148 } 149 150 getResults() { 151 const runTimeMillis = this.lastIterationTime - this.startTime; 152 return {'runTimeSeconds': runTimeMillis / 1000}; 153 } 154} 155 156 157let testRunner; 158 159function run(runtimeSeconds, numPeerConnections) { 160 testRunner = new TestRunner(runtimeSeconds); 161 for (let i = 0; i < numPeerConnections; i++) { 162 testRunner.addPeerConnection(); 163 } 164 testRunner.startTest(); 165} 166 167