Javascript  |  167行  |  4.28 KB

/*
 * Copyright 2017 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
/*jshint esversion: 6 */

'use strict';

const $ = document.getElementById.bind(document);

function logError(err) {
  console.error(err);
}


class FeedTable {
  constructor() {
    this.numCols = 5;
    this.col = 0;
    this.testTable = document.getElementById('test-table');
    this.row = this.testTable.insertRow(-1);
  }

  addNewAudioCell() {
    if (this.col == this.numCols) {
      this.row = this.testTable.insertRow(-1);
      this.col = 0;
    }
    var newCell = this.row.insertCell(-1);
    var audio = document.createElement('audio');
    audio.autoplay = false;
    newCell.appendChild(audio);
    this.col++;
    return audio;
  }
}


class PeerConnection {
  constructor(audioElement) {
    this.localConnection = null;
    this.remoteConnection = null;
    this.remoteAudio = audioElement;
  }

  start() {
    const onGetUserMediaSuccess = this.onGetUserMediaSuccess.bind(this);
    return navigator.mediaDevices
        .getUserMedia({audio: true, video: true})
        .then(onGetUserMediaSuccess);
  }

  onGetUserMediaSuccess(stream) {
    this.localConnection = new RTCPeerConnection(null);
    this.localConnection.onicecandidate = (event) => {
      this.onIceCandidate(this.remoteConnection, event);
    };
    this.localConnection.addStream(stream);

    this.remoteConnection = new RTCPeerConnection(null);
    this.remoteConnection.onicecandidate = (event) => {
      this.onIceCandidate(this.localConnection, event);
    };
    this.remoteConnection.onaddstream = (e) => {
      this.remoteAudio.srcObject = e.stream;
    };

    var onCreateOfferSuccess = this.onCreateOfferSuccess.bind(this);
    this.localConnection
        .createOffer({offerToReceiveAudio: 1, offerToReceiveVideo: 1})
        .then(onCreateOfferSuccess, logError);
  }

  onCreateOfferSuccess(desc) {
    this.localConnection.setLocalDescription(desc);
    this.remoteConnection.setRemoteDescription(desc);

    var onCreateAnswerSuccess = this.onCreateAnswerSuccess.bind(this);
    this.remoteConnection.createAnswer().then(onCreateAnswerSuccess, logError);
  }

  onCreateAnswerSuccess(desc) {
    this.remoteConnection.setLocalDescription(desc);
    this.localConnection.setRemoteDescription(desc);
  }

  onIceCandidate(connection, event) {
    if (event.candidate) {
      connection.addIceCandidate(new RTCIceCandidate(event.candidate));
    }
  }
}


class TestRunner {
  constructor(runtimeSeconds) {
    this.runtimeSeconds = runtimeSeconds;
    this.audioElements = [];
    this.peerConnections = [];
    this.feedTable = new FeedTable();
    this.iteration = 0;
    this.startTime;
    this.lastIterationTime;
  }

  addPeerConnection() {
    const audioElement = this.feedTable.addNewAudioCell();
    this.audioElements.push(audioElement);
    this.peerConnections.push(new PeerConnection(audioElement));
  }

  startTest() {
    this.startTime = Date.now();
    let promises = testRunner.peerConnections.map((conn) => conn.start());
    Promise.all(promises)
        .then(() => {
          this.startTime = Date.now();
          this.audioElements.forEach((feed) => feed.play());
          this.pauseAndPlayLoop();
        })
        .catch((e) => {throw e});
  }

  pauseAndPlayLoop() {
    this.iteration++;
    const status = this.getStatus();
    this.lastIterationTime = Date.now();
    $('status').textContent = status
    if (status != 'ok-done') {
      setTimeout(() => this.pauseAndPlayLoop());
    } else {
      // Finished, pause the audio.
      this.audioElements.forEach((feed) => feed.pause());
    }
  }

  getStatus() {
    if (this.iteration == 0) {
      return 'not-started';
    }
    const timeSpent = Date.now() - this.startTime;
    if (timeSpent >= this.runtimeSeconds * 1000) {
      return 'ok-done';
    } else {
      return `running, iteration: ${this.iteration}`;
    }
  }

  getResults() {
    const runTimeMillis = this.lastIterationTime - this.startTime;
    return {'runTimeSeconds': runTimeMillis / 1000};
  }
}


let testRunner;

function run(runtimeSeconds, numPeerConnections) {
  testRunner = new TestRunner(runtimeSeconds);
  for (let i = 0; i < numPeerConnections; i++) {
    testRunner.addPeerConnection();
  }
  testRunner.startTest();
}