Bug 1458407 [wpt PR 10746] - [webrtc] Use procedurally-generated media streams, a=testonly
authorSoares Chen <soares.chen@gmail.com>
Wed, 18 Jul 2018 09:42:49 +0000
changeset 427518 d81593e1a5950c35d9dc3f40d4b1afe88752501a
parent 427517 87cab050b41b989f43e3dcaf9e8bd50f3203778d
child 427519 75a6753d9b703be831b1fbbf3438ba7e4b445576
push id34307
push usercsabou@mozilla.com
push dateFri, 20 Jul 2018 21:42:49 +0000
treeherdermozilla-central@6eec814dea78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1458407, 10746
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1458407 [wpt PR 10746] - [webrtc] Use procedurally-generated media streams, a=testonly Automatic update from web-platform-testsMerge pull request #10746 from bocoup/webrtc-media [webrtc] Use procedurally-generated media streams -- wpt-commits: eee513ca541588299fafa71a9337eceae6b680cf wpt-pr: 10746
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webrtc/RTCPeerConnection-add-track-no-deadlock.https.html
testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html
testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js
testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html
testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html
testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html
testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html
testing/web-platform/tests/webrtc/interfaces.https.html
testing/web-platform/tests/webrtc/simplecall.https.html
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -502680,17 +502680,17 @@
    "75e949c1edef367f29d23a1c4921fe7748c78a3f",
    "testharness"
   ],
   "css/css-animations/pending-style-changes-001.html": [
    "5f2bf4b6712dd230109be62407cd31800451a271",
    "testharness"
   ],
   "css/css-animations/support/testcommon.js": [
-   "3e2b733b29fca0963c95c0d069b7a518db266004",
+   "3e0f25eacf2de8cac7f81b26bcd171b281bde112",
    "support"
   ],
   "css/css-backgrounds/META.yml": [
    "6579dbf88b7cdf0a3720b569c1aec90f4315b23f",
    "support"
   ],
   "css/css-backgrounds/background-331.html": [
    "28185e9f9710a676579fa8de6cc39e1febc9e16f",
@@ -627596,25 +627596,25 @@
    "6938c88a0167e418aa9e93416865c857cc3489c5",
    "testharness"
   ],
   "webrtc/RTCIceTransport.html": [
    "1b593c8ed167fd998195a2e2051bc489473f1bf4",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-add-track-no-deadlock.https.html": [
-   "d08414aefa6b0f082a0fcb7f5d05933636c012bb",
+   "e6c4c1e922e47bfb065d65bcf0954610162b8839",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addIceCandidate.html": [
    "6d6351d16a2ce4f3e5b91757498c1a7c4caa3289",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addTrack.https.html": [
-   "eddb5aedbfea3636ae03c0920c4450afc8e70658",
+   "c924fe69d43bdef8ff1bd9e645c5cecc6b4fe34f",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addTransceiver.html": [
    "c2d5766daa3ea4050ccb2777d7c08af1a1bd176f",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-canTrickleIceCandidates.html": [
    "b19badd054a12905d636e1c125293b5c9157906f",
@@ -627652,25 +627652,25 @@
    "208bb45887440df3bf1e45dd63f09d2d5b70857d",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-getIdentityAssertion.sub.html": [
    "1008ce2bf98829460c2de29d59558ca10e0e81e9",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-getStats.https.html": [
-   "f703ed5bf1fc434afd72f525e1639c7e4621e1b7",
+   "dd972db6f5b3ef771ff817fbeb18fb65de01710a",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-getTransceivers.html": [
    "b4c97af4f907a3d02fe1ebd24f00ab110b387575",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-helper.js": [
-   "8f8de7bbc399d3598f425c865b59e3d68830b2f8",
+   "e92d33cfb3fdacc6a2f172ecfe3444a1ced5680e",
    "support"
   ],
   "webrtc/RTCPeerConnection-iceConnectionState.html": [
    "718973199ab2bbdba519500e0e508ab51e2d8ddb",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-iceGatheringState.html": [
    "cfa693af6b6fb3a4ba6ff04e9e2fa1845016f6da",
@@ -627688,17 +627688,17 @@
    "7c5857534be80343928cacbe89a01d34ed3f0972",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-peerIdentity.html": [
    "d1e2441af750d0c698ecdfc1423d32d37dbb5f5f",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-removeTrack.https.html": [
-   "f4251f373bce768c30c5da9253029319921f3cf0",
+   "a5c919e396c7f8a916a9e8fe6c766f9dc263a6f8",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setDescription-transceiver.html": [
    "47d2f81b1e5f0c81eb249261829fded2f4b00a95",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-answer.html": [
    "beefd1c58a3edcc1d11a41e6ee591955b336fb3d",
@@ -627736,17 +627736,17 @@
    "f898128a0de6b757bc48f93707e13663e72c2410",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-rollback.html": [
    "2f7a15a6600893f786d48d300cac03f474ba7e6b",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html": [
-   "b3d025c0a50886293e340afc27081212b0bead95",
+   "3d1cfd0db67de255f7b1845d1be7fda6847eeb90",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setRemoteDescription.html": [
    "8a3e2f1e157e1ceed18ac66e57040a941b658f24",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-track-stats.https.html": [
    "d40ed4a64f9e769c21060b0ba9b593938d8f8403",
@@ -627796,29 +627796,29 @@
    "ce5367315aad59d4e0c847643b05318cea1cd31c",
    "testharness"
   ],
   "webrtc/RTCRtpReceiver-getParameters.html": [
    "ed910cbe15534cae43b79cc008395bd62fbd0637",
    "testharness"
   ],
   "webrtc/RTCRtpReceiver-getStats.https.html": [
-   "ebbca119f90320469cb311cac234c82d230f1191",
+   "6dbb42fd71efcfbf8f637090f0326c834326c618",
    "testharness"
   ],
   "webrtc/RTCRtpReceiver-getSynchronizationSources.https.html": [
    "621d3a3fc636d4c4a065998333405977c38f87b1",
    "testharness"
   ],
   "webrtc/RTCRtpSender-getCapabilities.html": [
    "27f083617973770f0d42efb93813f0112fc68ad2",
    "testharness"
   ],
   "webrtc/RTCRtpSender-getStats.https.html": [
-   "7cea6806ce165a32f5f28d6df215a2af07da7bd2",
+   "d00d8893b7f4f0e150d3e6281eff0c2e19fd03c3",
    "testharness"
   ],
   "webrtc/RTCRtpSender-replaceTrack.html": [
    "ea6ff719d0939e22fd5af2c72af009a1c9602c93",
    "testharness"
   ],
   "webrtc/RTCRtpSender-setParameters.html": [
    "b01436d8c7b71b84b4bdc1b58ea329967889bc32",
@@ -627876,17 +627876,17 @@
    "d0579ad82d492ba5cd957561ca70b5b8a344452c",
    "testharness"
   ],
   "webrtc/identity-helper.sub.js": [
    "8286dcf59e5eae0320a569c2097423d9589b0be2",
    "support"
   ],
   "webrtc/interfaces.https.html": [
-   "e66dc8812ea38e216c4483dbb3c7814574c39653",
+   "a5656af33767d909ae61760773f715d1cb44ac42",
    "testharness"
   ],
   "webrtc/no-media-call.html": [
    "e9f056be5a865f1bb73d60ec72c38f8d5f16d747",
    "testharness"
   ],
   "webrtc/promises-call.html": [
    "f0292ca52ee86920d0fdb8bccae2bc40a8ef99af",
@@ -627896,17 +627896,17 @@
    "af7066f4e223c39ff45a095c78acf4ca59a211ba",
    "support"
   ],
   "webrtc/protocol/video-codecs.https.html": [
    "c187072754be4f6392e98db645f1b1e8b5d930e2",
    "testharness"
   ],
   "webrtc/simplecall.https.html": [
-   "146432ca56e487a035df8ad9d5a7fa4a495b5405",
+   "01b15f6f42ca762c5e2949370b64575d45ec9941",
    "testharness"
   ],
   "webrtc/tools/.eslintrc.js": [
    "3ae35a91ae46bfba2c2ef959338ba1b61d1b3761",
    "support"
   ],
   "webrtc/tools/README.md": [
    "4a7dfd32e2129c362e153a83e4645fcc77b74247",
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-add-track-no-deadlock.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-add-track-no-deadlock.https.html
@@ -1,23 +1,24 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCPeerConnection addTrack does not deadlock</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCPeerConnection-helper.js"></script>
 <script>
   'use strict';
 
   // This test sets up two peer connections using a sequence of operations
   // that triggered a deadlock in Chrome. See https://crbug.com/736725.
   // If a deadlock is introduced again, this test times out.
   promise_test(async t => {
     const pc1 = new RTCPeerConnection();
     t.add_cleanup(() => pc1.close());
-    const stream = await navigator.mediaDevices.getUserMedia(
+    const stream = await getNoiseStream(
       {audio: false, video: true});
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const videoTrack = stream.getVideoTracks()[0];
     pc1.addTrack(videoTrack, stream);
     const offer = await pc1.createOffer();
     await pc1.setLocalDescription(offer);
     const pc2 = new RTCPeerConnection();
     t.add_cleanup(() => pc2.close());
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-addTrack.https.html
@@ -36,17 +36,17 @@
   /*
     5.1.  addTrack
       4.  If connection's [[isClosed]] slot is true, throw an InvalidStateError.
    */
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
 
     pc.close();
     assert_throws('InvalidStateError', () => pc.addTrack(track, stream))
   }, 'addTrack when pc is closed should throw InvalidStateError');
 
   /*
@@ -59,17 +59,17 @@
           3.  Create an RTCRtpTransceiver with sender and receiver and let
               transceiver be the result.
           4.  Add transceiver to connection's set of transceivers.
    */
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
 
     const sender = pc.addTrack(track);
 
     assert_true(sender instanceof RTCRtpSender,
       'Expect sender to be instance of RTCRtpSender');
 
@@ -91,34 +91,34 @@
     assert_array_equals([transceiver.receiver], pc.getReceivers(),
       'Expect only one receiver associated with transceiver added');
   }, 'addTrack with single track argument and no stream should succeed');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
 
     const sender = pc.addTrack(track, stream);
 
     assert_true(sender instanceof RTCRtpSender,
       'Expect sender to be instance of RTCRtpSender');
 
     assert_equals(sender.track, track,
       `Expect sender's track to be the added track`);
   }, 'addTrack with single track argument and single stream should succeed');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
 
     const stream2 = new MediaStream([track]);
     const sender = pc.addTrack(track, stream, stream2);
 
     assert_true(sender instanceof RTCRtpSender,
       'Expect sender to be instance of RTCRtpSender');
@@ -132,17 +132,17 @@
       5.  Let senders be the result of executing the CollectSenders algorithm.
           If an RTCRtpSender for track already exists in senders, throw an
           InvalidAccessError.
    */
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
 
     pc.addTrack(track, stream);
     assert_throws('InvalidAccessError', () => pc.addTrack(track, stream));
   }, 'Adding the same track multiple times should throw InvalidAccessError');
 
   /*
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-getStats.https.html
@@ -233,17 +233,17 @@
   async_test(t => {
     const pc1 = new RTCPeerConnection();
     t.add_cleanup(() => pc1.close());
     const pc2 = new RTCPeerConnection();
     t.add_cleanup(() => pc2.close());
 
     const dataChannel = pc1.createDataChannel('test-channel');
 
-    return navigator.mediaDevices.getUserMedia({
+    return getNoiseStream({
       audio: true,
       video: true
     })
     .then(t.step_func(mediaStream => {
       const tracks = mediaStream.getTracks();
       const [audioTrack] = mediaStream.getAudioTracks();
       const [videoTrack] = mediaStream.getVideoTracks();
 
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js
@@ -345,22 +345,120 @@ function generateMediaStreamTrack(kind) 
   const { track } = receiver;
 
   assert_true(track instanceof MediaStreamTrack,
     'Expect receiver track to be instance of MediaStreamTrack');
 
   return track;
 }
 
-// Obtain a MediaStreamTrack of kind using getUserMedia.
+// These media tracks will be continually updated with deterministic "noise" in
+// order to ensure UAs do not cease transmission in response to apparent
+// silence.
+//
+// > Many codecs and systems are capable of detecting "silence" and changing
+// > their behavior in this case by doing things such as not transmitting any
+// > media.
+//
+// Source: https://w3c.github.io/webrtc-pc/#offer-answer-options
+const trackFactories = {
+  // Share a single context between tests to avoid exceeding resource limits
+  // without requiring explicit destruction.
+  audioContext: null,
+
+  /**
+   * Given a set of requested media types, determine if the user agent is
+   * capable of procedurally generating a suitable media stream.
+   *
+   * @param {object} requested
+   * @param {boolean} [requested.audio] - flag indicating whether the desired
+   *                                      stream should include an audio track
+   * @param {boolean} [requested.video] - flag indicating whether the desired
+   *                                      stream should include a video track
+   *
+   * @returns {boolean}
+   */
+  canCreate(requested) {
+    const supported = {
+      audio: !!window.MediaStreamAudioDestinationNode,
+      video: !!HTMLCanvasElement.prototype.captureStream
+    };
+
+    return (!requested.audio || supported.audio) &&
+      (!requested.video || supported.video);
+  },
+
+  audio() {
+    const ctx = trackFactories.audioContext = trackFactories.audioContext ||
+      new AudioContext();
+    const oscillator = ctx.createOscillator();
+    const dst = oscillator.connect(ctx.createMediaStreamDestination());
+    oscillator.start();
+    return dst.stream.getAudioTracks()[0];
+  },
+
+  video({width = 640, height = 480} = {}) {
+    const canvas = Object.assign(
+      document.createElement("canvas"), {width, height}
+    );
+    const ctx = canvas.getContext('2d');
+    const stream = canvas.captureStream();
+
+    let count = 0;
+    setInterval(() => {
+      ctx.fillStyle = `rgb(${count%255}, ${count*count%255}, ${count%255})`;
+      count += 1;
+
+      ctx.fillRect(0, 0, width, height);
+    }, 100);
+
+    if (document.body) {
+      document.body.appendChild(canvas);
+    } else {
+      document.addEventListener('DOMContentLoaded', () => {
+        document.body.appendChild(canvas);
+      });
+    }
+
+    return stream.getVideoTracks()[0];
+  }
+};
+
+// Generate a MediaStream bearing the specified tracks.
+//
+// @param {object} [caps]
+// @param {boolean} [caps.audio] - flag indicating whether the generated stream
+//                                 should include an audio track
+// @param {boolean} [caps.video] - flag indicating whether the generated stream
+//                                 should include a video track
+async function getNoiseStream(caps = {}) {
+  if (!trackFactories.canCreate(caps)) {
+    return navigator.mediaDevices.getUserMedia(caps);
+  }
+  const tracks = [];
+
+  if (caps.audio) {
+    tracks.push(trackFactories.audio());
+  }
+
+  if (caps.video) {
+    tracks.push(trackFactories.video());
+  }
+
+  return new MediaStream(tracks);
+}
+
+// Obtain a MediaStreamTrack of kind using procedurally-generated streams (and
+// falling back to `getUserMedia` when the user agent cannot generate the
+// requested streams).
 // Return Promise of pair of track and associated mediaStream.
 // Assumes that there is at least one available device
 // to generate the track.
 function getTrackFromUserMedia(kind) {
-  return navigator.mediaDevices.getUserMedia({ [kind]: true })
+  return getNoiseStream({ [kind]: true })
   .then(mediaStream => {
     const [track] = mediaStream.getTracks();
     return [track, mediaStream];
   });
 }
 
 // Obtain |count| MediaStreamTracks of type |kind| and MediaStreams. The tracks
 // do not belong to any stream and the streams are empty. Returns a Promise
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-removeTrack.https.html
@@ -45,17 +45,17 @@
     assert_throws('InvalidStateError', () => pc.removeTrack(sender));
 
   }, 'addTransceiver - Calling removeTrack when connection is closed should throw InvalidStateError');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     const sender = pc.addTrack(track, stream);
 
     pc.close();
     assert_throws('InvalidStateError', () => pc.removeTrack(sender));
   }, 'addTrack - Calling removeTrack when connection is closed should throw InvalidStateError');
 
@@ -70,17 +70,17 @@
     assert_throws('InvalidStateError', () => pc2.removeTrack(sender));
 
   }, 'addTransceiver - Calling removeTrack on different connection that is closed should throw InvalidStateError');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     const sender = pc.addTrack(track, stream);
 
     const pc2 = new RTCPeerConnection();
     pc2.close();
     assert_throws('InvalidStateError', () => pc2.removeTrack(sender));
   }, 'addTrack - Calling removeTrack on different connection that is closed should throw InvalidStateError');
@@ -99,17 +99,17 @@
     assert_throws('InvalidAccessError', () => pc2.removeTrack(sender));
 
   }, 'addTransceiver - Calling removeTrack on different connection should throw InvalidAccessError');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     const sender = pc.addTrack(track, stream);
 
     const pc2 = new RTCPeerConnection();
     assert_throws('InvalidAccessError', () => pc2.removeTrack(sender));
   }, 'addTrack - Calling removeTrack on different connection should throw InvalidAccessError')
 
@@ -132,17 +132,17 @@
     assert_equals(transceiver.direction, 'recvonly');
 
   }, 'addTransceiver - Calling removeTrack with valid sender should set sender.track to null');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
 
-    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+    const stream = await getNoiseStream({ audio: true });
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     const sender = pc.addTrack(track, stream);
 
     assert_equals(sender.track, track);
 
     pc.removeTrack(sender);
     assert_equals(sender.track, null);
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html
@@ -18,17 +18,17 @@
   // states of RTCPeerConnection, MediaStream and MediaStreamTrack.
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     caller.addTrack(localStream.getTracks()[0]);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.track.id, localStream.getTracks()[0].id,
                     'Local and remote track IDs match.');
       assert_equals(e.streams.length, 0, 'No remote stream created.');
     });
     await exchangeOffer(caller, callee);
@@ -36,17 +36,17 @@
   }, 'addTrack() with a track and no stream makes ontrack fire with a track and no stream.');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     caller.addTrack(localStream.getTracks()[0], localStream);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.track.id, localStream.getTracks()[0].id,
                     'Local and remote track IDs match.');
       assert_equals(e.streams.length, 1, 'Created a single remote stream.');
       assert_equals(e.streams[0].id, localStream.id,
                     'Local and remote stream IDs match.');
@@ -59,17 +59,17 @@
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     let eventSequence = '';
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     caller.addTrack(localStream.getTracks()[0], localStream);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       eventSequence += 'ontrack;';
     });
     await exchangeOffer(caller, callee);
     eventSequence += 'setRemoteDescription;';
     await ontrackPromise;
@@ -77,18 +77,18 @@
   }, 'ontrack fires before setRemoteDescription resolves.');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStreams = await Promise.all([
-      navigator.mediaDevices.getUserMedia({audio: true}),
-      navigator.mediaDevices.getUserMedia({audio: true}),
+      getNoiseStream({audio: true}),
+      getNoiseStream({audio: true}),
     ]);
     caller.addTrack(localStreams[0].getTracks()[0], localStreams[0]);
     caller.addTrack(localStreams[1].getTracks()[0], localStreams[0]);
     let ontrackEventsFired = 0;
     const ontrackEventResolvers = [ new Resolver(), new Resolver() ];
     callee.ontrack = t.step_func(e => {
       ontrackEventResolvers[ontrackEventsFired++].resolve(e);
     });
@@ -121,18 +121,18 @@
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     let eventSequence = '';
     const localStreams = await Promise.all([
-      navigator.mediaDevices.getUserMedia({audio: true}),
-      navigator.mediaDevices.getUserMedia({audio: true}),
+      getNoiseStream({audio: true}),
+      getNoiseStream({audio: true}),
     ]);
     caller.addTrack(localStreams[0].getTracks()[0], localStreams[0]);
     const remoteStreams = [];
     callee.ontrack = e => {
       if (!remoteStreams.includes(e.streams[0]))
         remoteStreams.push(e.streams[0]);
     };
     await exchangeOfferAnswer(caller, callee);
@@ -153,18 +153,18 @@
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     let eventSequence = '';
     const localStreams = await Promise.all([
-      navigator.mediaDevices.getUserMedia({audio: true}),
-      navigator.mediaDevices.getUserMedia({audio: true}),
+      getNoiseStream({audio: true}),
+      getNoiseStream({audio: true}),
     ]);
     caller.addTrack(localStreams[0].getTracks()[0], localStreams[0]);
     const remoteStreams = [];
     callee.ontrack = e => {
       if (!remoteStreams.includes(e.streams[0]))
         remoteStreams.push(e.streams[0]);
     };
     await exchangeOfferAnswer(caller, callee);
@@ -182,18 +182,18 @@
   }, 'stream.onaddtrack fires before setRemoteDescription resolves.');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStreams = await Promise.all([
-      navigator.mediaDevices.getUserMedia({audio: true}),
-      navigator.mediaDevices.getUserMedia({audio: true}),
+      getNoiseStream({audio: true}),
+      getNoiseStream({audio: true}),
     ]);
     caller.addTrack(localStreams[0].getTracks()[0],
                     localStreams[0], localStreams[1]);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.track.id, localStreams[0].getTracks()[0].id,
                     'Local and remote track IDs match.');
       assert_equals(e.streams.length, 2, 'Two remote stream created.');
       assert_array_equals(e.streams[0].getTracks(), [e.track],
@@ -210,34 +210,34 @@
   }, 'addTrack() with a track and two streams makes ontrack fire with a track and two streams.');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     caller.addTrack(localStream.getTracks()[0]);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_array_equals(callee.getReceivers(), [e.receiver],
                           'getReceivers() == [e.receiver].');
     });
     await exchangeOffer(caller, callee);
     await ontrackPromise;
   }, 'ontrack\'s receiver matches getReceivers().');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     const sender = caller.addTrack(localStream.getTracks()[0]);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track');
     await exchangeOfferAnswer(caller, callee);
     await ontrackPromise;
     assert_equals(callee.getReceivers().length, 1, 'One receiver created.');
     caller.removeTrack(sender);
     await exchangeOffer(caller, callee);
@@ -245,17 +245,17 @@
   }, 'removeTrack() does not remove the receiver.');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     const [track] = localStream.getTracks();
     const sender = caller.addTrack(track, localStream);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.streams.length, 1);
       return e.streams[0];
     });
     await exchangeOfferAnswer(caller, callee);
@@ -274,17 +274,17 @@
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     let eventSequence = '';
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     const sender = caller.addTrack(localStream.getTracks()[0], localStream);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.streams.length, 1);
       return e.streams[0];
     });
     await exchangeOfferAnswer(caller, callee);
     const remoteStream = await ontrackPromise;
@@ -301,17 +301,17 @@
   }, 'stream.onremovetrack fires before setRemoteDescription resolves.');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     const sender = caller.addTrack(localStream.getTracks()[0], localStream);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.streams.length, 1);
       return e.streams[0];
     });
     await exchangeOfferAnswer(caller, callee);
     const remoteStream = await ontrackPromise;
@@ -327,17 +327,17 @@
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
     let eventSequence = '';
     const localStream =
-        await navigator.mediaDevices.getUserMedia({audio: true});
+        await getNoiseStream({audio: true});
     t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
     const sender = caller.addTrack(localStream.getTracks()[0], localStream);
     const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
       assert_equals(e.streams.length, 1);
       return e.streams[0];
     });
     await exchangeOfferAnswer(caller, callee);
     const remoteStream = await ontrackPromise;
@@ -351,15 +351,15 @@
     eventSequence += 'setRemoteDescription;';
     await onmutePromise;
     assert_equals(eventSequence, 'track.onmute;setRemoteDescription;');
   }, 'track.onmute fires before setRemoteDescription resolves.');
 
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
-    const stream = await navigator.mediaDevices.getUserMedia({audio: true});
+    const stream = await getNoiseStream({audio: true});
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const sender = pc.addTrack(stream.getTracks()[0]);
     pc.removeTrack(sender);
     pc.removeTrack(sender);
   }, 'removeTrack() twice is safe.');
 </script>
--- a/testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html
+++ b/testing/web-platform/tests/webrtc/RTCRtpReceiver-getStats.https.html
@@ -57,17 +57,17 @@
     assert_stats_report_has_stats(statsReport, ['inbound-rtp']);
   }, 'receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
-    const stream = await navigator.mediaDevices.getUserMedia({audio:true});
+    const stream = await getNoiseStream({audio:true});
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     caller.addTrack(track, stream);
 
     await doSignalingHandshake(caller, callee);
     const receiver = callee.getReceivers()[0];
     const statsReport = await receiver.getStats();
     validateStatsReport(statsReport);
--- a/testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html
+++ b/testing/web-platform/tests/webrtc/RTCRtpSender-getStats.https.html
@@ -52,17 +52,17 @@
     assert_stats_report_has_stats(statsReport, ['outbound-rtp']);
   }, 'sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats');
 
   promise_test(async t => {
     const caller = new RTCPeerConnection();
     t.add_cleanup(() => caller.close());
     const callee = new RTCPeerConnection();
     t.add_cleanup(() => callee.close());
-    const stream = await navigator.mediaDevices.getUserMedia({audio:true});
+    const stream = await getNoiseStream({audio:true});
     t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
     const [track] = stream.getTracks();
     const sender = caller.addTrack(track, stream);
 
     await doSignalingHandshake(caller, callee);
     const statsReport = await sender.getStats();
     validateStatsReport(statsReport);
     assert_stats_report_has_stats(statsReport, ['outbound-rtp']);
--- a/testing/web-platform/tests/webrtc/interfaces.https.html
+++ b/testing/web-platform/tests/webrtc/interfaces.https.html
@@ -74,17 +74,17 @@
 
       const iceTransport = dtlsTransport.transport;
       idlTestObjects.iceTransport = iceTransport;
     });
   }
 
   // Asynchoronously generate MediaStreamTrack from getUserMedia
   function asyncInitMediaStreamTrack() {
-    return navigator.mediaDevices.getUserMedia({ audio: true })
+    return getNoiseStream({ audio: true })
     .then(mediaStream => {
       idlTestObjects.mediaStreamTrack = mediaStream.getTracks()[0];
     });
   }
 
   // Run all async test drivers, report and swallow any error
   // thrown/rejected. Proper test for correct initialization
   // of the objects are done in their respective test files.
--- a/testing/web-platform/tests/webrtc/simplecall.https.html
+++ b/testing/web-platform/tests/webrtc/simplecall.https.html
@@ -1,28 +1,20 @@
 <!doctype html>
-<!--
-To run this test, you must have a webcam and a microphone or use
-fake devices by specifying
-  --use-fake-device-for-media-stream --use-fake-ui-for-media-stream
-for Chrome or by setting the
-  media.navigator.streams.fake
-property to true in Firefox.
--->
-
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>RTCPeerConnection Connection Test</title>
+  <script src="RTCPeerConnection-helper.js"></script>
 </head>
 <body>
   <div id="log"></div>
   <div>
-    <video id="local-view" autoplay="autoplay"></video>
-    <video id="remote-view" autoplay="autoplay"/>
+    <video id="local-view" muted autoplay="autoplay"></video>
+    <video id="remote-view" muted autoplay="autoplay"/>
     </video>
   </div>
 
   <!-- These files are in place when executing on W3C. -->
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script type="text/javascript">
   var test = async_test('Can set up a basic WebRTC call.', {timeout: 5000});
@@ -33,17 +25,17 @@ property to true in Firefox.
   // if the remote video gets video data that implies the negotiation
   // as well as the ICE and DTLS connection are up.
   document.getElementById('remote-view')
       .addEventListener('loadedmetadata', function() {
     // Call negotiated: done.
     test.done();
   });
 
-  function getUserMediaOkCallback(localStream) {
+  function getNoiseStreamOkCallback(localStream) {
     gFirstConnection = new RTCPeerConnection(null);
     gFirstConnection.onicecandidate = onIceCandidateToFirst;
     localStream.getTracks().forEach(function(track) {
       gFirstConnection.addTrack(track, localStream);
     });
     gFirstConnection.createOffer(onOfferCreated, failed('createOffer'));
 
     var videoTag = document.getElementById('local-view');
@@ -106,15 +98,15 @@ property to true in Firefox.
 
   // Returns a suitable error callback.
   function failed(function_name) {
     return test.unreached_func('WebRTC called error callback for ' + function_name);
   }
 
   // This function starts the test.
   test.step(function() {
-    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
-      .then(test.step_func(getUserMediaOkCallback), failed('getUserMedia'));
+    getNoiseStream({ video: true, audio: true })
+      .then(test.step_func(getNoiseStreamOkCallback), failed('getNoiseStream'));
   });
 </script>
 
 </body>
 </html>