| <!doctype html> | |
| <meta charset=utf-8> | |
| <meta name="timeout" content="long"> | |
| <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'; | |
| async function waitForTimeout(t, timeout_ms = 5000) { | |
| return new Promise(r => t.step_timeout(r, timeout_ms)); | |
| } | |
| // 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); | |
| let muteWatcher; | |
| let unmutePromise; | |
| // 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 => { | |
| assert_true(e.track.muted, 'track is muted in ontrack'); | |
| muteWatcher = new EventWatcher(t, e.track, ['unmute'], | |
| () => waitForTimeout(t)); | |
| unmutePromise = muteWatcher.wait_for('unmute'); | |
| pc2.ontrack = t.step_func(e => { | |
| assert_unreached('ontrack fired unexpectedly'); | |
| }); | |
| }); | |
| await exchangeOfferAnswer(pc1, pc2); | |
| await unmutePromise; | |
| }, '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); | |
| let muteWatcher; | |
| let unmutePromise; | |
| pc2.ontrack = t.step_func(e => { | |
| muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute'], | |
| () => waitForTimeout(t)); | |
| unmutePromise = muteWatcher.wait_for('unmute'); | |
| }); | |
| await exchangeOfferAnswer(pc1, pc2); | |
| // Need to wait for the initial unmute event before renegotiating, otherwise | |
| // there will be no transition from unmuted->muted. | |
| await unmutePromise; | |
| 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); | |
| let muteWatcher; | |
| let unmutePromise; | |
| pc2.ontrack = t.step_func(e => { | |
| muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute'], | |
| () => waitForTimeout(t)); | |
| unmutePromise = muteWatcher.wait_for('unmute'); | |
| }); | |
| await exchangeOfferAnswer(pc1, pc2); | |
| await unmutePromise; | |
| const mutePromise = muteWatcher.wait_for('mute'); | |
| localTransceiver.direction = 'inactive'; | |
| await exchangeOfferAnswer(pc1, pc2); | |
| await mutePromise; | |
| unmutePromise = muteWatcher.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); | |
| let muteWatcher; | |
| let unmutePromise; | |
| pc2.ontrack = t.step_func(e => { | |
| muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute'], | |
| () => waitForTimeout(t)); | |
| unmutePromise = muteWatcher.wait_for('unmute'); | |
| }); | |
| await exchangeOfferAnswer(pc1, pc2); | |
| // Need to wait for the initial unmute event before closing, otherwise | |
| // there will be no transition from unmuted->muted. | |
| await unmutePromise; | |
| // This will send an RTCP BYE which causes the remote side to mute. | |
| const mutePromise = muteWatcher.wait_for('mute'); | |
| localTransceiver.stop(); | |
| await mutePromise; | |
| }, 'transceiver.stop() on one side (without renegotiation) causes mute events on the other'); | |
| 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); | |
| let muteWatcher; | |
| let unmutePromise; | |
| pc2.ontrack = t.step_func(e => { | |
| muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute'], | |
| () => waitForTimeout(t)); | |
| unmutePromise = muteWatcher.wait_for('unmute'); | |
| }); | |
| await exchangeOfferAnswer(pc1, pc2); | |
| // Need to wait for the initial unmute event before closing, otherwise | |
| // there will be no transition from unmuted->muted. | |
| await unmutePromise; | |
| const mutePromise = muteWatcher.wait_for('mute'); | |
| pc1.close(); | |
| await mutePromise; | |
| }, 'pc.close() on one side causes mute events on the other'); | |
| 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); | |
| let pc2Track; | |
| let pc2Transceiver; | |
| let unmutePromise; | |
| pc2.ontrack = t.step_func(e => { | |
| pc2Track = e.track; | |
| pc2Transceiver = pc2.getTransceivers()[0]; | |
| const watcher = new EventWatcher(t, e.track, ['unmute'], () => waitForTimeout(t)); | |
| unmutePromise = watcher.wait_for('unmute'); | |
| }); | |
| await exchangeOfferAnswer(pc1, pc2); | |
| await unmutePromise; | |
| // Stop the transceiver on the receiving side. Per spec the receiver track | |
| // must end when a transceiver is stopped. | |
| pc2Transceiver.stop(); | |
| await new Promise(r => { pc2Track.onended = r; }); | |
| // A 'mute' event must not fire on an already-ended track when the remote | |
| // side renegotiates away from sending. | |
| pc2Track.onmute = t.step_func(() => { | |
| assert_unreached('mute event must not fire on an ended track'); | |
| }); | |
| localTransceiver.direction = 'recvonly'; | |
| await exchangeOfferAnswer(pc1, pc2); | |
| assert_equals(pc2Track.readyState, 'ended', | |
| 'receiver track remains ended after renegotiation'); | |
| }, 'No mute event fires on a stopped transceiver\'s track when remote renegotiates to recvonly'); | |
| </script> |