Bug 1494648 [wpt PR 13241] - [Unified Plan] Remote MediaStreamTracks should be muted by default., a=testonly
authorHenrik Boström <hbos@chromium.org>
Thu, 11 Oct 2018 09:30:30 +0000
changeset 496745 cb1d335ae21b8eb8d711d0fedaafdf93a4bea0db
parent 496744 bf9667abba51109254ad238200f9ad5ae013e7b4
child 496746 7f9995d90affed02070d111f906f7fa676f285cc
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1494648, 13241, 884023, 788558, 777619, 1249066, 595405
milestone64.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 1494648 [wpt PR 13241] - [Unified Plan] Remote MediaStreamTracks should be muted by default., a=testonly Automatic update from web-platform-tests[Unified Plan] Remote MediaStreamTracks should be muted by default. Per-spec, tracks that are created with a receiver (e.g. addTrack or setRemoteDescription) are muted by default. Prior to this CL they were unmuted by default, whether or not they were receiving any packets. A correct implementation should unmute the tracks when RTP packets arrive. We are not quite there yet, this CL assumes that if the receiver becomes active through renegotiation it will unmute. We are careful to make sure that the track is muted on the "ontrack" event so that the application has time to wire up the "onunmute" event. By unmuting as part of processing SDP we fix the Unified Plan bug where a remote track that had previously been muted was not unmuted when becoming active again (transciever.currentDirection == 'sendrecv' or 'recvonly'), https://crbug.com/884023. This CL also makes "ontrack" fire synchronously per-spec, https://crbug.com/788558. Note that some stream events still fire asynchronously, which means they now fire after the "ontrack" event in the Unified Plan case. This is remaining work on that bug. A new file is created to test muting related behaviors, and some helper functions used this and another file are moved to RTCPeerConnection-helper.js. Bug: 884023, 777619, 788558 Change-Id: I8dc3e2adf04e72282f085779639edc73bacfc86b Reviewed-on: https://chromium-review.googlesource.com/1249066 Commit-Queue: Henrik Boström <hbos@chromium.org> Reviewed-by: Guido Urdaneta <guidou@chromium.org> Cr-Commit-Position: refs/heads/master@{#595405} -- wpt-commits: 30c6ad632c481c22e8e35dcc8021c15a8da28338 wpt-pr: 13241
testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js
testing/web-platform/tests/webrtc/RTCPeerConnection-remote-track-mute.https.html
testing/web-platform/tests/webrtc/RTCPeerConnection-transceivers.https.html
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js
@@ -458,31 +458,44 @@ function getUserMediaTracksAndStreams(co
       stream.removeTrack(track);
       tracks.push(track);
       streams.push(stream);
       return [tracks, streams];
     });
   });
 }
 
+// Performs an offer exchange caller -> callee.
 async function exchangeOffer(caller, callee) {
   const offer = await caller.createOffer();
   await caller.setLocalDescription(offer);
   return callee.setRemoteDescription(offer);
 }
+// Performs an answer exchange caller -> callee.
 async function exchangeAnswer(caller, callee) {
   const answer = await callee.createAnswer();
   await callee.setLocalDescription(answer);
   return caller.setRemoteDescription(answer);
 }
 async function exchangeOfferAnswer(caller, callee) {
   await exchangeOffer(caller, callee);
   return exchangeAnswer(caller, callee);
 }
-
+// The returned promise is resolved with caller's ontrack event.
+async function exchangeAnswerAndListenToOntrack(t, caller, callee) {
+  const ontrackPromise = addEventListenerPromise(t, caller, 'track');
+  await exchangeAnswer(caller, callee);
+  return ontrackPromise;
+}
+// The returned promise is resolved with callee's ontrack event.
+async function exchangeOfferAndListenToOntrack(t, caller, callee) {
+  const ontrackPromise = addEventListenerPromise(t, callee, 'track');
+  await exchangeOffer(caller, callee);
+  return ontrackPromise;
+}
 
 // The resolver has a |promise| that can be resolved or rejected using |resolve|
 // or |reject|.
 class Resolver {
   constructor() {
     let promiseResolve;
     let promiseReject;
     this.promise = new Promise(function(resolve, reject) {
@@ -498,8 +511,32 @@ function addEventListenerPromise(t, targ
   return new Promise((resolve, reject) => {
     target.addEventListener(type, t.step_func(e => {
       if (listener != undefined)
         e = listener(e);
       resolve(e);
     }));
   });
 }
+
+function createPeerConnectionWithCleanup(t) {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  return pc;
+}
+
+async function createTrackAndStreamWithCleanup(t, kind = 'audio') {
+  let constraints = {};
+  constraints[kind] = true;
+  const stream = await navigator.mediaDevices.getUserMedia(constraints);
+  const [track] = stream.getTracks();
+  t.add_cleanup(() => track.stop());
+  return [track, stream];
+}
+
+function findTransceiverForSender(pc, sender) {
+  const transceivers = pc.getTransceivers();
+  for (let i = 0; i < transceivers.length; ++i) {
+    if (transceivers[i].sender == sender)
+      return transceivers[i];
+  }
+  return null;
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-remote-track-mute.https.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCPeerConnection-transceivers.https.html</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="RTCPeerConnection-helper.js"></script>
+<script>
+'use strict';
+
+// The following helper functions are called from RTCPeerConnection-helper.js:
+//   exchangeOffer
+//   exchangeOfferAndListenToOntrack
+//   exchangeAnswer
+//   exchangeAnswerAndListenToOntrack
+//   addEventListenerPromise
+//   createPeerConnectionWithCleanup
+//   createTrackAndStreamWithCleanup
+//   findTransceiverForSender
+
+promise_test(async t => {
+  const pc1 = createPeerConnectionWithCleanup(t);
+  pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
+  const pc2 = createPeerConnectionWithCleanup(t);
+  exchangeIceCandidates(pc1, pc2);
+
+  const unmuteResolver = new Resolver();
+  let remoteTrack = null;
+  // The unmuting it timing sensitive so we hook up to the event directly
+  // instead of wrapping it in an EventWatcher which uses promises.
+  pc2.ontrack = t.step_func(e => {
+    remoteTrack = e.track;
+    assert_true(remoteTrack.muted, 'track is muted in ontrack');
+    remoteTrack.onunmute = t.step_func(e => {
+      assert_false(remoteTrack.muted, 'track is unmuted in onunmute');
+      unmuteResolver.resolve();
+    });
+    pc2.ontrack = t.step_func(e => {
+      assert_unreached('ontrack fired unexpectedly');
+    });
+  });
+  await exchangeOfferAnswer(pc1, pc2);
+  await unmuteResolver.promise;
+}, 'ontrack: track goes from muted to unmuted');
+
+promise_test(async t => {
+  const pc1 = createPeerConnectionWithCleanup(t);
+  const pc1Sender = pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
+  const localTransceiver = findTransceiverForSender(pc1, pc1Sender);
+  const pc2 = createPeerConnectionWithCleanup(t);
+  exchangeIceCandidates(pc1, pc2);
+
+  const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
+  await exchangeAnswer(pc1, pc2);
+
+  const muteWatcher = new EventWatcher(t, e.track, ['mute']);
+  const mutePromise = muteWatcher.wait_for('mute');
+  localTransceiver.direction = 'inactive';
+  await exchangeOfferAnswer(pc1, pc2);
+
+  await mutePromise;
+}, 'Changing transceiver direction to \'inactive\' mutes the remote track');
+
+promise_test(async t => {
+  const pc1 = createPeerConnectionWithCleanup(t);
+  const pc1Sender = pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
+  const localTransceiver = findTransceiverForSender(pc1, pc1Sender);
+  const pc2 = createPeerConnectionWithCleanup(t);
+  exchangeIceCandidates(pc1, pc2);
+
+  const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
+  await exchangeAnswer(pc1, pc2);
+  localTransceiver.direction = 'inactive';
+  await exchangeOfferAnswer(pc1, pc2);
+
+  const unmuteWatcher = new EventWatcher(t, e.track, ['unmute']);
+  const unmutePromise = unmuteWatcher.wait_for('unmute');
+  localTransceiver.direction = 'sendrecv';
+  await exchangeOfferAnswer(pc1, pc2);
+
+  await unmutePromise;
+}, 'Changing transceiver direction to \'sendrecv\' unmutes the remote track');
+
+promise_test(async t => {
+  const pc1 = createPeerConnectionWithCleanup(t);
+  const pc1Sender = pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
+  const localTransceiver = findTransceiverForSender(pc1, pc1Sender);
+  const pc2 = createPeerConnectionWithCleanup(t);
+  exchangeIceCandidates(pc1, pc2);
+
+  const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
+  await exchangeAnswer(pc1, pc2);
+  const muteWatcher = new EventWatcher(t, e.track, ['mute']);
+  const mutePromise = muteWatcher.wait_for('mute');
+  pc2.close();
+  await mutePromise;
+}, 'pc.close() mutes remote tracks');
+
+</script>
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-transceivers.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-transceivers.https.html
@@ -3,69 +3,24 @@
 <title>RTCPeerConnection-transceivers.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="RTCPeerConnection-helper.js"></script>
 <script>
 'use strict';
 
 // The following helper functions are called from RTCPeerConnection-helper.js:
+//   exchangeOffer
+//   exchangeOfferAndListenToOntrack
+//   exchangeAnswer
+//   exchangeAnswerAndListenToOntrack
 //   addEventListenerPromise
-
-function createPeerConnectionWithCleanup(t) {
-  const pc = new RTCPeerConnection();
-  t.add_cleanup(() => pc.close());
-  return pc;
-}
-
-async function createTrackAndStreamWithCleanup(t, kind = 'audio') {
-  let constraints = {};
-  constraints[kind] = true;
-  const stream = await navigator.mediaDevices.getUserMedia(constraints);
-  const [track] = stream.getTracks();
-  t.add_cleanup(() => track.stop());
-  return [track, stream];
-}
-
-function findTransceiverForSender(pc, sender) {
-  const transceivers = pc.getTransceivers();
-  for (let i = 0; i < transceivers.length; ++i) {
-    if (transceivers[i].sender == sender)
-      return transceivers[i];
-  }
-  return null;
-}
-
-// Performs an offer exchange pc1 -> pc2.
-async function exchangeOffer(pc1, pc2) {
-  const offer = await pc1.createOffer();
-  await pc1.setLocalDescription(offer);
-  await pc2.setRemoteDescription(offer);
-}
-
-// The returned promise is resolved with pc2's ontrack event.
-async function exchangeOfferAndListenToOntrack(t, pc1, pc2) {
-  const ontrackPromise = addEventListenerPromise(t, pc2, 'track');
-  await exchangeOffer(pc1, pc2);
-  return ontrackPromise;
-}
-
-// Performs an answer exchange pc2 -> pc1.
-async function exchangeAnswer(pc1, pc2) {
-  const answer = await pc2.createAnswer();
-  await pc2.setLocalDescription(answer);
-  await pc1.setRemoteDescription(answer);
-}
-
-// The returned promise is resolved with pc1's ontrack event.
-async function exchangeAnswerAndListenToOntrack(t, pc1, pc2) {
-  const ontrackPromise = addEventListenerPromise(t, pc1, 'track');
-  await exchangeAnswer(pc1, pc2);
-  return ontrackPromise;
-}
+//   createPeerConnectionWithCleanup
+//   createTrackAndStreamWithCleanup
+//   findTransceiverForSender
 
 promise_test(async t => {
   const pc = createPeerConnectionWithCleanup(t);
   const [track, stream] = await createTrackAndStreamWithCleanup(t);
   const sender = pc.addTrack(track, stream);
   const transceiver = findTransceiverForSender(pc, sender);
   assert_true(transceiver instanceof RTCRtpTransceiver);
   assert_true(transceiver.sender instanceof RTCRtpSender);
@@ -488,9 +443,25 @@ promise_test(async t => {
   await exchangeAnswer(pc1, pc2);
   assert_equals(transceiver.currentDirection, 'sendonly');
   assert_false(transceiver.stopped);
   pc1.close();
   assert_equals(transceiver.currentDirection, null);
   assert_true(transceiver.stopped);
 }, 'Closing the PC stops the transceivers');
 
+promise_test(async t => {
+  const pc1 = createPeerConnectionWithCleanup(t);
+  const pc1Sender = pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
+  const localTransceiver = findTransceiverForSender(pc1, pc1Sender);
+  const pc2 = createPeerConnectionWithCleanup(t);
+  exchangeIceCandidates(pc1, pc2);
+
+  const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
+  await exchangeAnswer(pc1, pc2);
+  localTransceiver.direction = 'inactive';
+  await exchangeOfferAnswer(pc1, pc2);
+
+  localTransceiver.direction = 'sendrecv';
+  await exchangeOfferAndListenToOntrack(t, pc1, pc2);
+}, 'Changing transceiver direction to \'sendrecv\' makes ontrack fire');
+
 </script>