mirror of
https://github.com/servo/servo.git
synced 2025-06-25 17:44:33 +01:00
338 lines
13 KiB
HTML
338 lines
13 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>RTCPeerConnection.prototype.removeTrack</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="RTCPeerConnection-helper.js"></script>
|
|
<script>
|
|
'use strict';
|
|
|
|
// Test is based on the following editor draft:
|
|
// https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html
|
|
|
|
// The following helper functions are called from RTCPeerConnection-helper.js:
|
|
// generateAnswer
|
|
|
|
/*
|
|
5.1. RTCPeerConnection Interface Extensions
|
|
partial interface RTCPeerConnection {
|
|
...
|
|
void removeTrack(RTCRtpSender sender);
|
|
RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind,
|
|
optional RTCRtpTransceiverInit init);
|
|
};
|
|
*/
|
|
|
|
// Before calling removeTrack can be tested, one needs to add MediaStreamTracks to
|
|
// a peer connection. There are two ways for adding MediaStreamTrack: addTrack and
|
|
// addTransceiver. addTransceiver is a newer API while addTrack has been implemented
|
|
// in current browsers for some time. As a result some of the removeTrack tests have
|
|
// two versions so that removeTrack can be partially tested without addTransceiver
|
|
// and the transceiver APIs being implemented.
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
3. If connection's [[isClosed]] slot is true, throw an InvalidStateError.
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
const stream = await getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = pc.addTransceiver(track);
|
|
const { sender } = transceiver;
|
|
|
|
pc.close();
|
|
assert_throws_dom('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 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_dom('InvalidStateError', () => pc.removeTrack(sender));
|
|
}, 'addTrack - 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 getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = pc.addTransceiver(track);
|
|
const { sender } = transceiver;
|
|
|
|
const pc2 = new RTCPeerConnection();
|
|
pc2.close();
|
|
assert_throws_dom('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 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_dom('InvalidStateError', () => pc2.removeTrack(sender));
|
|
}, 'addTrack - Calling removeTrack on different connection that is closed should throw InvalidStateError');
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
4. If sender was not created by connection, throw an InvalidAccessError.
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const stream = await getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = pc.addTransceiver(track);
|
|
const { sender } = transceiver;
|
|
|
|
const pc2 = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc2.close());
|
|
assert_throws_dom('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 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();
|
|
t.add_cleanup(() => pc2.close());
|
|
assert_throws_dom('InvalidAccessError', () => pc2.removeTrack(sender));
|
|
}, 'addTrack - Calling removeTrack on different connection should throw InvalidAccessError')
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
7. Set sender.track to null.
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const stream = await getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = pc.addTransceiver(track);
|
|
const { sender } = transceiver;
|
|
|
|
assert_equals(sender.track, track);
|
|
assert_equals(transceiver.direction, 'sendrecv');
|
|
assert_equals(transceiver.currentDirection, null);
|
|
|
|
pc.removeTrack(sender);
|
|
assert_equals(sender.track, null);
|
|
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 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);
|
|
}, 'addTrack - Calling removeTrack with valid sender should set sender.track to null');
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
7. Set sender.track to null.
|
|
10. If transceiver.currentDirection is sendrecv set transceiver.direction
|
|
to recvonly.
|
|
*/
|
|
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 getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = caller.addTransceiver(track);
|
|
const { sender } = transceiver;
|
|
|
|
assert_equals(sender.track, track);
|
|
assert_equals(transceiver.direction, 'sendrecv');
|
|
assert_equals(transceiver.currentDirection, null);
|
|
|
|
const offer = await caller.createOffer();
|
|
await caller.setLocalDescription(offer);
|
|
await callee.setRemoteDescription(offer);
|
|
callee.addTrack(track, stream);
|
|
const answer = await callee.createAnswer();
|
|
await callee.setLocalDescription(answer);
|
|
await caller.setRemoteDescription(answer);
|
|
assert_equals(transceiver.currentDirection, 'sendrecv');
|
|
|
|
caller.removeTrack(sender);
|
|
assert_equals(sender.track, null);
|
|
assert_equals(transceiver.direction, 'recvonly');
|
|
assert_equals(transceiver.currentDirection, 'sendrecv',
|
|
'Expect currentDirection to not change');
|
|
}, 'Calling removeTrack with currentDirection sendrecv should set direction to recvonly');
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
7. Set sender.track to null.
|
|
11. If transceiver.currentDirection is sendonly set transceiver.direction
|
|
to inactive.
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const stream = await getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = pc.addTransceiver(track, { direction: 'sendonly' });
|
|
const { sender } = transceiver;
|
|
|
|
assert_equals(sender.track, track);
|
|
assert_equals(transceiver.direction, 'sendonly');
|
|
assert_equals(transceiver.currentDirection, null);
|
|
|
|
const offer = await pc.createOffer();
|
|
await pc.setLocalDescription(offer);
|
|
const answer = await generateAnswer(offer);
|
|
await pc.setRemoteDescription(answer);
|
|
assert_equals(transceiver.currentDirection, 'sendonly');
|
|
|
|
pc.removeTrack(sender);
|
|
assert_equals(sender.track, null);
|
|
assert_equals(transceiver.direction, 'inactive');
|
|
assert_equals(transceiver.currentDirection, 'sendonly',
|
|
'Expect currentDirection to not change');
|
|
}, 'Calling removeTrack with currentDirection sendonly should set direction to inactive');
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
7. Set sender.track to null.
|
|
9. If transceiver.currentDirection is recvonly or inactive,
|
|
then abort these steps.
|
|
*/
|
|
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 getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = caller.addTransceiver(track, { direction: 'recvonly' });
|
|
const { sender } = transceiver;
|
|
|
|
assert_equals(sender.track, track);
|
|
assert_equals(transceiver.direction, 'recvonly');
|
|
assert_equals(transceiver.currentDirection, null);
|
|
|
|
const offer = await caller.createOffer();
|
|
await caller.setLocalDescription(offer);
|
|
await callee.setRemoteDescription(offer);
|
|
callee.addTrack(track, stream);
|
|
const answer = await callee.createAnswer();
|
|
await callee.setLocalDescription(answer);
|
|
await caller.setRemoteDescription(answer);
|
|
assert_equals(transceiver.currentDirection, 'recvonly');
|
|
|
|
caller.removeTrack(sender);
|
|
assert_equals(sender.track, null);
|
|
assert_equals(transceiver.direction, 'recvonly');
|
|
assert_equals(transceiver.currentDirection, 'recvonly');
|
|
}, 'Calling removeTrack with currentDirection recvonly should not change direction');
|
|
|
|
/*
|
|
5.1. removeTrack
|
|
7. Set sender.track to null.
|
|
9. If transceiver.currentDirection is recvonly or inactive,
|
|
then abort these steps.
|
|
*/
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
const stream = await getNoiseStream({audio: true});
|
|
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
|
|
const [track] = stream.getTracks();
|
|
const transceiver = pc.addTransceiver(track, { direction: 'inactive' });
|
|
const { sender } = transceiver;
|
|
|
|
assert_equals(sender.track, track);
|
|
assert_equals(transceiver.direction, 'inactive');
|
|
assert_equals(transceiver.currentDirection, null);
|
|
|
|
const offer = await pc.createOffer();
|
|
await pc.setLocalDescription(offer);
|
|
const answer = await generateAnswer(offer);
|
|
await pc.setRemoteDescription(answer);
|
|
assert_equals(transceiver.currentDirection, 'inactive');
|
|
|
|
pc.removeTrack(sender);
|
|
assert_equals(sender.track, null);
|
|
assert_equals(transceiver.direction, 'inactive');
|
|
assert_equals(transceiver.currentDirection, 'inactive');
|
|
}, 'Calling removeTrack with currentDirection inactive should not change direction');
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
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.getTransceivers()[0].stop();
|
|
pc.removeTrack(sender);
|
|
assert_equals(sender.track, track);
|
|
}, "Calling removeTrack on a stopped transceiver should be a no-op");
|
|
|
|
promise_test(async t => {
|
|
const pc = new RTCPeerConnection();
|
|
t.add_cleanup(() => pc.close());
|
|
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);
|
|
|
|
await sender.replaceTrack(null);
|
|
pc.removeTrack(sender);
|
|
assert_equals(sender.track, null);
|
|
}, "Calling removeTrack on a null track should have no effect");
|
|
|
|
|
|
/*
|
|
TODO
|
|
5.1. removeTrack
|
|
Stops sending media from sender. The RTCRtpSender will still appear
|
|
in getSenders. Doing so will cause future calls to createOffer to
|
|
mark the media description for the corresponding transceiver as
|
|
recvonly or inactive, as defined in [JSEP] (section 5.2.2.).
|
|
|
|
When the other peer stops sending a track in this manner, an ended
|
|
event is fired at the MediaStreamTrack object.
|
|
|
|
6. If sender is not in senders (which indicates that it was removed
|
|
due to setting an RTCSessionDescription of type "rollback"),
|
|
then abort these steps.
|
|
12. Update the negotiation-needed flag for connection.
|
|
*/
|
|
</script>
|