Home | History | Annotate | Download | only in webrtc_PausePlayPeerConnections
      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  * FeedTable stores all elements.
     18  */
     19 class FeedTable {
     20   constructor() {
     21     this.numCols = 5;
     22     this.col = 0;
     23     this.testTable = $('test-table');
     24     this.row = this.testTable.insertRow(-1);
     25   }
     26 
     27   addNewCell(elementType) {
     28     if (this.col == this.numCols) {
     29       this.row = this.testTable.insertRow(-1);
     30       this.col = 0;
     31     }
     32     var newCell = this.row.insertCell(-1);
     33     var element = document.createElement(elementType);
     34     element.autoplay = false;
     35     newCell.appendChild(element);
     36     this.col++;
     37     return element;
     38   }
     39 }
     40 
     41 /**
     42  * A simple loopback connection;
     43  * - localConnection is fed video/audio from source
     44  * - localConnection is linked to remoteConnection
     45  * - remoteConnection is displayed in the given videoElement
     46  */
     47 class PeerConnection {
     48 
     49   /**
     50    * @param {!HTMLMediaElement} element - And 'audio' or 'video' element.
     51    * @param {!Object} constraints - The constraints for the peer connection.
     52    */
     53   constructor(element, constraints) {
     54     this.localConnection = null;
     55     this.remoteConnection = null;
     56     this.remoteElement = element;
     57     this.constraints = constraints;
     58   }
     59 
     60   start() {
     61     return navigator.mediaDevices
     62         .getUserMedia(this.constraints)
     63         .then((stream) => {
     64             this.onGetUserMediaSuccess(stream)
     65         });
     66   };
     67 
     68   onGetUserMediaSuccess(stream) {
     69     this.localConnection = new RTCPeerConnection(null);
     70     this.localConnection.onicecandidate = (event) => {
     71       this.onIceCandidate(this.remoteConnection, event);
     72     };
     73     this.localConnection.addStream(stream);
     74 
     75     this.remoteConnection = new RTCPeerConnection(null);
     76     this.remoteConnection.onicecandidate = (event) => {
     77       this.onIceCandidate(this.localConnection, event);
     78     };
     79     this.remoteConnection.onaddstream = (e) => {
     80       this.remoteElement.srcObject = e.stream;
     81     };
     82 
     83     this.localConnection
     84         .createOffer({offerToReceiveAudio: 1, offerToReceiveVideo: 1})
     85         .then((desc) => {this.onCreateOfferSuccess(desc)}, logError);
     86   };
     87 
     88   onCreateOfferSuccess(desc) {
     89     this.localConnection.setLocalDescription(desc);
     90     this.remoteConnection.setRemoteDescription(desc);
     91 
     92     this.remoteConnection.createAnswer().then(
     93         (desc) => {this.onCreateAnswerSuccess(desc)}, logError);
     94   };
     95 
     96   onCreateAnswerSuccess(desc) {
     97     this.remoteConnection.setLocalDescription(desc);
     98     this.localConnection.setRemoteDescription(desc);
     99   };
    100 
    101   onIceCandidate(connection, event) {
    102     if (event.candidate) {
    103       connection.addIceCandidate(new RTCIceCandidate(event.candidate));
    104     }
    105   };
    106 }
    107 
    108 
    109 class TestRunner {
    110   constructor(runtimeSeconds, pausePlayIterationDelayMillis) {
    111     this.runtimeSeconds = runtimeSeconds;
    112     this.pausePlayIterationDelayMillis = pausePlayIterationDelayMillis;
    113     this.elements = [];
    114     this.peerConnections = [];
    115     this.feedTable = new FeedTable();
    116     this.iteration = 0;
    117     this.startTime;
    118     this.lastIterationTime;
    119   }
    120 
    121   addPeerConnection(elementType) {
    122     const element = this.feedTable.addNewCell(elementType);
    123     const constraints = {audio: true};
    124     if (elementType === 'video') {
    125       constraints.video = {
    126         width: {exact: 300}
    127       };
    128     } else if (elementType === 'audio') {
    129       constraints.video = false;
    130     } else {
    131       throw new Error('elementType must be one of "audio" or "video"');
    132     }
    133     this.elements.push(element);
    134     this.peerConnections.push(new PeerConnection(element, constraints));
    135   }
    136 
    137   startTest() {
    138     this.startTime = Date.now();
    139     let promises = testRunner.peerConnections.map((conn) => conn.start());
    140     Promise.all(promises)
    141         .then(() => {
    142           this.startTime = Date.now();
    143           this.pauseAndPlayLoop();
    144         })
    145         .catch((e) => {throw e});
    146   }
    147 
    148   pauseAndPlayLoop() {
    149     this.iteration++;
    150     this.elements.forEach((feed) => {
    151       if (Math.random() >= 0.5) {
    152         feed.play();
    153       } else {
    154         feed.pause();
    155       }
    156     });
    157     const status = this.getStatus();
    158     this.lastIterationTime = Date.now();
    159     $('status').textContent = status
    160     if (status != 'ok-done') {
    161       setTimeout(
    162           () => {this.pauseAndPlayLoop()}, this.pausePlayIterationDelayMillis);
    163     } else {  // We're done. Pause all feeds.
    164       this.elements.forEach((feed) => {
    165         feed.pause();
    166       });
    167     }
    168   }
    169 
    170   getStatus() {
    171     if (this.iteration == 0) {
    172       return 'not-started';
    173     }
    174     const timeSpent = Date.now() - this.startTime;
    175     if (timeSpent >= this.runtimeSeconds * 1000) {
    176       return 'ok-done';
    177     } else {
    178       return `running, iteration: ${this.iteration}`;
    179     }
    180   }
    181 
    182   getResults() {
    183     const runTimeMillis = this.lastIterationTime - this.startTime;
    184     return {'runTimeSeconds': runTimeMillis / 1000};
    185   }
    186 }
    187 
    188 let testRunner;
    189 
    190 function startTest(
    191     runtimeSeconds, numPeerConnections, pausePlayIterationDelayMillis,
    192     elementType) {
    193   testRunner = new TestRunner(
    194       runtimeSeconds, pausePlayIterationDelayMillis);
    195   for (let i = 0; i < numPeerConnections; i++) {
    196     testRunner.addPeerConnection(elementType);
    197   }
    198   testRunner.startTest();
    199 }
    200