chromium/content/test/data/media/peerconnection-call-audio.html

<html>
<head>
  <script type="text/javascript" src="webrtc_test_utilities.js"></script>
  <script type="text/javascript" src="webrtc_test_common.js"></script>
  <script type="text/javascript" src="webrtc_test_audio.js"></script>
  <script type="text/javascript">
  $ = function(id) {
    return document.getElementById(id);
  };

  var gFirstConnection = null;
  var gSecondConnection = null;
  var gLocalStream = null;

  var gRemoteStreams = {};

  // The second set of constraints should request audio (e.g. audio:true) since
  // we expect audio to be playing after the second renegotiation.
  function callAndRenegotiateToAudio(constraints, renegotiationConstraints) {
    createConnections(null);
    return Promise.all([
      navigator.mediaDevices.getUserMedia(constraints)
          .then(addStreamToBothConnectionsAndNegotiate),

      waitForConnectionToStabilize(gFirstConnection).then(() => {
        gFirstConnection.removeStream(gLocalStream);
        gSecondConnection.removeStream(gLocalStream);
        return navigator.mediaDevices.getUserMedia(renegotiationConstraints)
            .then(addStreamToTheFirstConnectionAndNegotiate);
        }).then(() => {
          return waitForConnectionToStabilize(gFirstConnection)
        }).then(() => {
          return ensureAudioPlaying(gSecondConnection);
        }),
    ])
    .then(logSuccess);
  }

  function setupCallAndPromiseAudioPlaying(constraints) {
    createConnections(null);

    // Add the local stream to gFirstConnection to play one-way audio.
    return Promise.all([
      navigator.mediaDevices.getUserMedia(constraints)
          .then(addStreamToTheFirstConnectionAndNegotiate),
      waitForConnectionToStabilize(gFirstConnection)
          .then(() => { return ensureAudioPlaying(gSecondConnection); }),
    ]);
  }

  function callAndEnsureAudioIsPlaying(constraints) {
    return setupCallAndPromiseAudioPlaying(constraints)
        .then(logSuccess);
  }

  function enableRemoteVideo(peerConnection, enabled) {
    remoteStream = peerConnection.getRemoteStreams()[0];
    remoteStream.getVideoTracks()[0].enabled = enabled;
  }

  function enableRemoteAudio(peerConnection, enabled) {
    remoteStream = peerConnection.getRemoteStreams()[0];
    remoteStream.getAudioTracks()[0].enabled = enabled;
  }

  function enableLocalVideo(peerConnection, enabled) {
    localStream = peerConnection.getLocalStreams()[0];
    localStream.getVideoTracks()[0].enabled = enabled;
  }

  function enableLocalAudio(peerConnection, enabled) {
    localStream = peerConnection.getLocalStreams()[0];
    localStream.getAudioTracks()[0].enabled = enabled;
  }

  function callAndEnsureRemoteAudioTrackMutingWorks(constraints) {
    return setupCallAndPromiseAudioPlaying(constraints).then(() => {
      // Call is up, now mute the remote track and check we stop playing out
      // audio (after a small delay, we don't expect it to happen instantly).
      enableRemoteAudio(gSecondConnection, false);

      return new Promise(resolve => {
        setTimeout(resolve, 250);
      })
      .then(() => ensureSilence(gSecondConnection));
    })
    .then(logSuccess);
  }

  function callAndEnsureLocalAudioTrackMutingWorks(constraints) {
    return setupCallAndPromiseAudioPlaying(constraints).then(() => {
      // Call is up, now mute the local track of the sending side and ensure
      // the receiving side stops receiving audio.
      enableLocalAudio(gFirstConnection, false);

      return new Promise(resolve => {
        setTimeout(resolve, 250);
      })
      .then(() => ensureSilence(gSecondConnection));
    })
    .then(logSuccess);
  }

  function callAndEnsureAudioTrackUnmutingWorks(constraints) {
    return setupCallAndPromiseAudioPlaying(constraints).then(() => {
      // Mute, wait a while, unmute, verify audio gets back up.
      // (Also, ensure video muting doesn't affect audio).
      enableRemoteAudio(gSecondConnection, false);
      enableRemoteVideo(gSecondConnection, false);

      setTimeout(function() {
        enableRemoteAudio(gSecondConnection, true);
      }, 500);

      return new Promise(resolve => {
        setTimeout(resolve, 1500);
      })
      .then(() => ensureAudioPlaying(gSecondConnection));
    })
    .then(logSuccess);
  }

  function callAndEnsureLocalVideoMutingDoesntMuteAudio(constraints) {
    return setupCallAndPromiseAudioPlaying(constraints).then(() => {
      enableLocalVideo(gFirstConnection, false);
      return ensureAudioPlaying(gSecondConnection)
          .then(logSuccess);
    });
  }

  function callAndEnsureRemoteVideoMutingDoesntMuteAudio(constraints) {
    return setupCallAndPromiseAudioPlaying(constraints).then(() => {
      enableRemoteVideo(gSecondConnection, false);
      return ensureAudioPlaying(gSecondConnection)
          .then(logSuccess);
    });
  }

  // TODO(crbug.com/40637961): This test is a temporary replacement for:
  // external/wpt/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html
  async function testEstablishAudioOnlyCallAndVerifyGetSynchronizationSourcesWorks() {
    const startTime = performance.timeOrigin + performance.now();

    await setupCallAndPromiseAudioPlaying({audio: true});

    const peerConnection = gSecondConnection;

    const receivers = peerConnection.getReceivers();
    assertEquals(receivers.length, 1);
    const receiver = receivers[0];
    assertEquals(receiver.track.kind, 'audio');

    const results = receiver.getSynchronizationSources();
    assertEquals(results.length, 1);
    const result = results[0];

    const endTime = performance.timeOrigin + performance.now();

    console.log('getSynchronizationSources() = ' + JSON.stringify(result));

    // timestamp
    assertEquals(typeof result.timestamp, 'number');
    assertTrue(result.timestamp >= startTime);
    assertTrue(result.timestamp <= endTime);

    // source
    assertEquals(typeof result.source, 'number');
    assertTrue(result.source >= 0);
    assertTrue(result.source <= 0xffffffff);

    // rtpTimestamp
    assertEquals(typeof result.rtpTimestamp, 'number');
    assertTrue(result.rtpTimestamp >= 0);
    assertTrue(result.rtpTimestamp <= 0xffffffff);

    // audioLevel
    assertEquals(typeof result.audioLevel, 'number');
    assertTrue(result.audioLevel >= 0);
    assertTrue(result.audioLevel <= 1);

    // voiceActivityFlag
    if (result.voiceActivityFlag != undefined) {
      assertEquals(typeof result.voiceActivityFlag, 'boolean');
    }

    return logSuccess();
  }

  function createConnections(constraints) {
    gFirstConnection = createConnection(constraints, 'remote-view-1');
    assertEquals('stable', gFirstConnection.signalingState);

    gSecondConnection = createConnection(constraints, 'remote-view-2');
    assertEquals('stable', gSecondConnection.signalingState);
  }

  function createConnection(constraints, remoteView) {
    var pc = new RTCPeerConnection(null, constraints);
    pc.onaddstream = function(event) {
      onRemoteStream(event, remoteView);
    }
    return pc;
  }

  function displayAndRemember(localStream) {
    $('local-view').srcObject = localStream;

    gLocalStream = localStream;
  }

  // Called if getUserMedia succeeds and we want to send from both connections.
  function addStreamToBothConnectionsAndNegotiate(localStream) {
    displayAndRemember(localStream);
    gFirstConnection.addStream(localStream);
    gSecondConnection.addStream(localStream);
    negotiate();
  }

  // Called if getUserMedia succeeds when we want to send from one connection.
  function addStreamToTheFirstConnectionAndNegotiate(localStream) {
    displayAndRemember(localStream);
    gFirstConnection.addStream(localStream);
    negotiate();
  }

  function negotiate() {
    negotiateBetween(gFirstConnection, gSecondConnection);
  }

  function onRemoteStream(e, target) {
    console.log("Receiving remote stream...");
    gRemoteStreams[target] = e.stream;
    var remoteVideo = $(target);
    remoteVideo.srcObject = e.stream;
  }

  </script>
</head>
<body>
  <table border="0">
    <tr>
      <td><video width="320" height="240" id="local-view" style="display:none"
          autoplay muted></video></td>
      <td><video width="320" height="240" id="remote-view-1"
          style="display:none" autoplay></video></td>
      <td><video width="320" height="240" id="remote-view-2"
          style="display:none" autoplay></video></td>
      <!-- Canvases are named after their corresponding video elements. -->
      <td><canvas width="320" height="240" id="remote-view-1-canvas"
          style="display:none"></canvas></td>
      <td><canvas width="320" height="240" id="remote-view-2-canvas"
          style="display:none"></canvas></td>
    </tr>
  </table>
</body>
</html>