fune/testing/web-platform/tests/webrtc/RTCPeerConnection-transceivers.https.html
Henrik Boström 0cdf8369fe Bug 1766830 [wpt PR 33842] - Make sure removed/stopped transceivers are reflected as such in JS., a=testonly
Automatic update from web-platform-tests
Make sure removed/stopped transceivers are reflected as such in JS.

RTCRtpTransceiver::UpdateMembers() makes sure transceivers are
up-to-date with the lower layers. But when fully stopped transceivers
are removed their states are never surfaced. While we correctly removed
these transceivers, the JS app could still keep references to these
objects and upon inspection they look as if they have not been stopped.

This CL fixes issues relating to stop by...
- Making sure the states reflects being stopped using
  RTCRtpTransceiver::OnTransceiverStopped().
- Making sure "direction" is surfaced if when becoming kStopped.

This CL also gets rid of boolean stopped since this has been removed
from the spec (it's equivalent to direction == "stopped") and updates
some old WPTs that to expect currentDirection to be "stopped" when
stopped rather than null and that stopped transceivers are removed from
getTransceivers().

Bug: 1319911, 1319913
Change-Id: I437e15db3291ffe9121b5e8c6afa02901fd68663
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3609972
Reviewed-by: Harald Alvestrand <hta@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#997200}

--

wpt-commits: 00195af1427294304855fb8a40e73918924b13e0
wpt-pr: 33842
2022-05-15 20:20:51 +00:00

509 lines
24 KiB
HTML

<!doctype html>
<meta name="timeout" content="long"/>
<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 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);
assert_equals(transceiver.sender, sender);
}, 'addTrack: creates a transceiver for the sender');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_array_equals(pc.getTransceivers(), [transceiver],
'pc.getTransceivers() equals [transceiver]');
assert_array_equals(pc.getSenders(), [transceiver.sender],
'pc.getSenders() equals [transceiver.sender]');
assert_array_equals(pc.getReceivers(), [transceiver.receiver],
'pc.getReceivers() equals [transceiver.receiver]');
}, 'addTrack: "transceiver == {sender,receiver}"');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_true(transceiver.sender.track instanceof MediaStreamTrack,
'transceiver.sender.track instanceof MediaStreamTrack');
assert_equals(transceiver.sender.track, track,
'transceiver.sender.track == track');
}, 'addTrack: transceiver.sender is associated with the track');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_true(transceiver.receiver instanceof RTCRtpReceiver,
'transceiver.receiver instanceof RTCRtpReceiver');
assert_true(transceiver.receiver.track instanceof MediaStreamTrack,
'transceiver.receiver.track instanceof MediaStreamTrack');
assert_not_equals(transceiver.receiver.track, track,
'transceiver.receiver.track != track');
}, 'addTrack: transceiver.receiver has its own track');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_true(transceiver.receiver.track.muted);
}, 'addTrack: transceiver.receiver\'s track is muted');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_equals(transceiver.mid, null);
}, 'addTrack: transceiver is not associated with an m-section');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
// `stopped` is non-standard. Move to external/wpt/webrtc/legacy/?
assert_false(transceiver.stopped);
}, 'addTrack: transceiver is not stopped');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_equals(transceiver.direction, 'sendrecv');
}, 'addTrack: transceiver\'s direction is sendrecv');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
assert_equals(transceiver.currentDirection, null);
}, 'addTrack: transceiver\'s currentDirection is null');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
await pc.setLocalDescription(await pc.createOffer());
assert_not_equals(transceiver.mid, null, 'transceiver.mid != null');
}, 'setLocalDescription(offer): transceiver gets associated with an m-section');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc, pc.addTrack(track, stream));
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
let sdp = offer.sdp;
let sdpMidLineStart = sdp.indexOf('a=mid:');
let sdpMidLineEnd = sdp.indexOf('\r\n', sdpMidLineStart);
assert_true(sdpMidLineStart != -1 && sdpMidLineEnd != -1,
'Failed to parse offer SDP for a=mid');
let parsedMid = sdp.substring(sdpMidLineStart + 6, sdpMidLineEnd);
assert_equals(transceiver.mid, parsedMid, 'transceiver.mid == parsedMid');
}, 'setLocalDescription(offer): transceiver.mid matches the offer SDP');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_true(trackEvent instanceof RTCTrackEvent,
'trackEvent instanceof RTCTrackEvent');
assert_true(trackEvent.track instanceof MediaStreamTrack,
'trackEvent.track instanceof MediaStreamTrack');
}, 'setRemoteDescription(offer): ontrack fires with a track');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
pc1.addTrack(track, stream);
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_true(trackEvent.track instanceof MediaStreamTrack,
'trackEvent.track instanceof MediaStreamTrack');
assert_equals(trackEvent.streams.length, 1,
'trackEvent contains a single stream');
assert_true(trackEvent.streams[0] instanceof MediaStream,
'trackEvent has a MediaStream');
assert_equals(trackEvent.streams[0].id, stream.id,
'trackEvent.streams[0].id == stream.id');
}, 'setRemoteDescription(offer): ontrack\'s stream.id is the same as stream.id');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_true(trackEvent.transceiver instanceof RTCRtpTransceiver,
'trackEvent.transceiver instanceof RTCRtpTransceiver');
}, 'setRemoteDescription(offer): ontrack fires with a transceiver.');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc1, pc1.addTrack(track, stream));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(transceiver.mid, trackEvent.transceiver.mid);
}, 'setRemoteDescription(offer): transceiver.mid is the same on both ends');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
const transceiver = trackEvent.transceiver;
assert_array_equals(pc2.getTransceivers(), [transceiver],
'pc2.getTransceivers() equals [transceiver]');
assert_array_equals(pc2.getSenders(), [transceiver.sender],
'pc2.getSenders() equals [transceiver.sender]');
assert_array_equals(pc2.getReceivers(), [transceiver.receiver],
'pc2.getReceivers() equals [transceiver.receiver]');
}, 'setRemoteDescription(offer): "transceiver == {sender,receiver}"');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.transceiver.direction, 'recvonly');
}, 'setRemoteDescription(offer): transceiver.direction is recvonly');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.transceiver.currentDirection, null);
}, 'setRemoteDescription(offer): transceiver.currentDirection is null');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
// `stopped` is non-standard. Move to external/wpt/webrtc/legacy/?
assert_false(trackEvent.transceiver.stopped);
}, 'setRemoteDescription(offer): transceiver.stopped is false');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
pc1.addTrack(... await createTrackAndStreamWithCleanup(t));
const pc2 = createPeerConnectionWithCleanup(t);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
const transceiver = trackEvent.transceiver;
assert_equals(transceiver.currentDirection, null,
'SRD(offer): transceiver.currentDirection is null');
await pc2.setLocalDescription(await pc2.createAnswer());
assert_equals(transceiver.currentDirection, 'recvonly',
'SLD(answer): transceiver.currentDirection is recvonly');
}, 'setLocalDescription(answer): transceiver.currentDirection is recvonly');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = findTransceiverForSender(pc1, pc1.addTrack(track, stream));
const pc2 = createPeerConnectionWithCleanup(t);
await exchangeOffer(pc1, pc2);
assert_equals(transceiver.currentDirection, null,
'SLD(offer): transceiver.currentDirection is null');
await exchangeAnswer(pc1, pc2);
assert_equals(transceiver.currentDirection, 'sendonly',
'SRD(answer): transceiver.currentDirection is sendonly');
}, 'setLocalDescription(answer): transceiver.currentDirection is sendonly');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver(track);
assert_true(transceiver instanceof RTCRtpTransceiver);
assert_true(transceiver.sender instanceof RTCRtpSender);
assert_true(transceiver.receiver instanceof RTCRtpReceiver);
assert_equals(transceiver.sender.track, track);
}, 'addTransceiver(track): creates a transceiver for the track');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver(track);
assert_array_equals(pc.getTransceivers(), [transceiver],
'pc.getTransceivers() equals [transceiver]');
assert_array_equals(pc.getSenders(), [transceiver.sender],
'pc.getSenders() equals [transceiver.sender]');
assert_array_equals(pc.getReceivers(), [transceiver.receiver],
'pc.getReceivers() equals [transceiver.receiver]');
}, 'addTransceiver(track): "transceiver == {sender,receiver}"');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver(track, {direction:'inactive'});
assert_equals(transceiver.direction, 'inactive');
}, 'addTransceiver(track, init): initialize direction to inactive');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const otherPc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver(track, {
sendEncodings: [{active:false}]
});
// Negotiate parameters.
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
await otherPc.setRemoteDescription(offer);
const answer = await otherPc.createAnswer();
await otherPc.setLocalDescription(answer);
await pc.setRemoteDescription(answer);
const params = transceiver.sender.getParameters();
assert_false(params.encodings[0].active);
}, 'addTransceiver(track, init): initialize sendEncodings[0].active to false');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track] = await createTrackAndStreamWithCleanup(t);
pc1.addTransceiver(track, {streams:[]});
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.streams.length, 0, 'trackEvent.streams.length == 0');
}, 'addTransceiver(0 streams): ontrack fires with no stream');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track] = await createTrackAndStreamWithCleanup(t);
const stream = new MediaStream();
pc1.addTransceiver(track, {streams:[stream]});
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.streams.length, 1, 'trackEvent.streams.length == 1');
assert_equals(trackEvent.streams[0].id, stream.id,
'trackEvent.streams[0].id == stream.id');
}, 'addTransceiver(1 stream): ontrack fires with corresponding stream');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track] = await createTrackAndStreamWithCleanup(t);
const stream0 = new MediaStream();
const stream1 = new MediaStream();
pc1.addTransceiver(track, {streams:[stream0, stream1]});
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.streams.length, 2, 'trackEvent.streams.length == 2');
assert_equals(trackEvent.streams[0].id, stream0.id,
'trackEvent.streams[0].id == stream0.id');
assert_equals(trackEvent.streams[1].id, stream1.id,
'trackEvent.streams[1].id == stream1.id');
}, 'addTransceiver(2 streams): ontrack fires with corresponding two streams');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track] = await createTrackAndStreamWithCleanup(t);
pc1.addTrack(track);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.streams.length, 0, 'trackEvent.streams.length == 0');
}, 'addTrack(0 streams): ontrack fires with no stream');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track] = await createTrackAndStreamWithCleanup(t);
const stream = new MediaStream();
pc1.addTrack(track, stream);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.streams.length, 1, 'trackEvent.streams.length == 1');
assert_equals(trackEvent.streams[0].id, stream.id,
'trackEvent.streams[0].id == stream.id');
}, 'addTrack(1 stream): ontrack fires with corresponding stream');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track] = await createTrackAndStreamWithCleanup(t);
const stream0 = new MediaStream();
const stream1 = new MediaStream();
pc1.addTrack(track, stream0, stream1);
const trackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
assert_equals(trackEvent.streams.length, 2, 'trackEvent.streams.length == 2');
assert_equals(trackEvent.streams[0].id, stream0.id,
'trackEvent.streams[0].id == stream0.id');
assert_equals(trackEvent.streams[1].id, stream1.id,
'trackEvent.streams[1].id == stream1.id');
}, 'addTrack(2 streams): ontrack fires with corresponding two streams');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver('audio');
assert_equals(transceiver.direction, 'sendrecv');
}, 'addTransceiver(\'audio\'): creates a transceiver with direction sendrecv');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver('audio');
assert_equals(transceiver.receiver.track.kind, 'audio');
}, 'addTransceiver(\'audio\'): transceiver.receiver.track.kind == \'audio\'');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver('video');
assert_equals(transceiver.receiver.track.kind, 'video');
}, 'addTransceiver(\'video\'): transceiver.receiver.track.kind == \'video\'');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver('audio');
assert_equals(transceiver.sender.track, null);
}, 'addTransceiver(\'audio\'): transceiver.sender.track == null');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver('audio');
assert_equals(transceiver.currentDirection, null);
}, 'addTransceiver(\'audio\'): transceiver.currentDirection is null');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const transceiver = pc.addTransceiver('audio');
// `stopped` is non-standard. Move to external/wpt/webrtc/legacy/?
assert_false(transceiver.stopped);
}, 'addTransceiver(\'audio\'): transceiver.stopped is false');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t, 'audio');
const transceiver = pc.addTransceiver('audio');
const sender = pc.addTrack(track, stream);
assert_equals(sender, transceiver.sender, 'sender == transceiver.sender');
assert_equals(sender.track, track, 'sender.track == track');
}, 'addTrack reuses reusable transceivers');
promise_test(async t => {
const pc = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t, 'audio');
const t1 = pc.addTransceiver('audio');
const t2 = pc.addTransceiver(track);
assert_not_equals(t2, t1, 't2 != t1');
assert_equals(t2.sender.track, track, 't2.sender.track == track');
}, 'addTransceiver does not reuse reusable transceivers');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t);
const pc1Transceiver = findTransceiverForSender(pc1, pc1.addTrack(track, stream));
const pc2TrackEvent = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
const pc2Transceiver = pc2TrackEvent.transceiver;
assert_equals(pc2Transceiver.direction, 'recvonly',
'pc2Transceiver.direction is recvonly after SRD(offer)');
const pc2Sender = pc2.addTrack(track, stream);
assert_equals(pc2Transceiver.sender, pc2Sender,
'pc2Transceiver.sender == sender');
assert_equals(pc2Transceiver.direction, 'sendrecv',
'pc2Transceiver.direction is sendrecv after addTrack()');
assert_equals(pc2Transceiver.currentDirection, null,
'pc2Transceiver.currentDirection is null before answer');
const pc1TrackEvent = await exchangeAnswerAndListenToOntrack(t, pc1, pc2);
assert_equals(pc2Transceiver.currentDirection, 'sendrecv',
'pc2Transceiver.currentDirection is sendrecv after SLD(answer)');
assert_equals(pc1TrackEvent.transceiver, pc1Transceiver,
'Answer: pc1.ontrack fires with the existing transceiver.');
assert_equals(pc1Transceiver.currentDirection, 'sendrecv',
'pc1Transceiver.currentDirection is sendrecv');
assert_equals(pc2.getTransceivers().length, 1,
'pc2.getTransceivers().length == 1');
assert_equals(pc1.getTransceivers().length, 1,
'pc1.getTransceivers().length == 1');
}, 'Can setup two-way call using a single transceiver');
promise_test(async t => {
const pc1 = createPeerConnectionWithCleanup(t);
const pc2 = createPeerConnectionWithCleanup(t);
const [track, stream] = await createTrackAndStreamWithCleanup(t, 'audio');
const transceiver = pc1.addTransceiver(track);
await exchangeOffer(pc1, pc2);
await exchangeAnswer(pc1, pc2);
assert_equals(transceiver.currentDirection, 'sendonly');
// `stopped` is non-standard. Move to external/wpt/webrtc/legacy/?
assert_false(transceiver.stopped);
pc1.close();
assert_equals(transceiver.currentDirection, 'stopped');
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');
// Regression test coverage for https://crbug.com/950280.
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const pc2Promise = pc2.createOffer()
.then((offer) => { return pc1.setRemoteDescription(offer); })
.then(() => { return pc1.createAnswer(); })
.then((answer) => { return pc1.setLocalDescription(answer); });
pc1.addTransceiver('audio', { direction: 'recvonly' });
const pc1Promise = pc1.createOffer()
.then(() => { pc1.addTrack(pc1.getReceivers()[0].track); });
await Promise.all([pc1Promise, pc2Promise]);
assert_equals(pc1.getSenders()[0].track, pc1.getReceivers()[0].track);
}, 'transceiver.sender.track does not revert to an old state');
// Regression test coverage for https://crbug.com/950280.
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const pc2Promise = pc2.createOffer()
.then((offer) => { return pc1.setRemoteDescription(offer); })
.then(() => { return pc1.createAnswer(); });
pc1.addTransceiver('audio', { direction: 'recvonly' });
const pc1Promise = pc1.createOffer()
.then(() => { pc1.getTransceivers()[0].direction = 'inactive'; });
await Promise.all([pc1Promise, pc2Promise]);
assert_equals(pc1.getTransceivers()[0].direction, 'inactive');
}, 'transceiver.direction does not revert to an old state');
</script>