Home | History | Annotate | Download | only in audio_WebRtcAudioLoopback
      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 
     10 const $ = document.getElementById.bind(document);
     11 
     12 function logError(err) {
     13   console.error(err);
     14 }
     15 
     16 
     17 class 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 
     40 class 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 
     96 class 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 
    157 let testRunner;
    158 
    159 function 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