Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script src="third_party/sdp/sdp.js"></script>
<script src="simulcast/simulcast.js"></script>
<script>
'use strict';
async function hasStats(pc, type) {
const report = await pc.getStats();
for (const stats of report.values()) {
if (stats.type == type) {
return true;
}
}
return false;
}
async function getInboundRtpPollUntilItExists(pc, kTimeoutMs = 10000) {
const t0 = performance.now();
while (performance.now() - t0 < kTimeoutMs) {
const report = await pc.getStats();
for (const stats of report.values()) {
if (stats.type == 'inbound-rtp') {
return stats;
}
}
}
return null;
}
promise_test(async t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
pc.addTransceiver('video');
assert_false(await hasStats(pc, 'outbound-rtp'),
'outbound-rtp does not exist after addTransceiver');
await pc.setLocalDescription();
assert_false(await hasStats(pc, 'outbound-rtp'),
'outbound-rtp does not exist in have-local-offer');
}, `RTCOutboundRtpStreamStats does not exist as early as have-local-offer`);
// This test does not exchange ICE candidates, meaning no packets are sent.
// We should still see outbound-rtp stats because they are created by the O/A.
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
// Offer to send. See previous test for assertions that the outbound-rtp is
// not created this early, which this test does not care about.
pc1.addTransceiver('video');
await pc1.setLocalDescription();
// Answer to send.
await pc2.setRemoteDescription(pc1.localDescription);
const [transceiver] = pc2.getTransceivers();
transceiver.direction = 'sendrecv';
assert_false(await hasStats(pc2, 'outbound-rtp'),
'outbound-rtp does not exist in has-remote-offer');
await pc2.setLocalDescription();
assert_true(await hasStats(pc2, 'outbound-rtp'),
'outbound-rtp exists after answerer returns to stable');
// Complete offerer negotiation.
await pc1.setRemoteDescription(pc2.localDescription);
assert_true(await hasStats(pc1, 'outbound-rtp'),
'outbound-rtp exists after offerer returns to stable');
}, `RTCOutboundRtpStreamStats exists after returning to stable`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
pc1.addTransceiver('video', {sendEncodings: [{rid: 'foo'}, {rid: 'bar'}]});
// O/A with tweaks to accept simulcast.
await doOfferToSendSimulcastAndAnswer(pc1, pc2, ['foo', 'bar']);
// Despite not sending anything (ICE not connected) both outbound-rtp stats
// objects should appear immediately.
const report = await pc1.getStats();
const outboundRtpFoo =
report.values().find(s => s.type == 'outbound-rtp' && s.rid == 'foo');
const outboundRtpBar =
report.values().find(s => s.type == 'outbound-rtp' && s.rid == 'bar');
assert_not_equals(outboundRtpFoo, undefined);
assert_not_equals(outboundRtpFoo.ssrc, undefined);
assert_not_equals(outboundRtpBar, undefined);
assert_not_equals(outboundRtpBar.ssrc, undefined);
}, `RTCOutboundRtpStreamStats exists per simulcast encoding`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
// Negotaite to send, but don't send anything yet (track is null).
const {sender} = pc1.addTransceiver('video');
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
await pc1.setRemoteDescription(pc2.localDescription);
assert_false(await hasStats(pc2, 'inbound-rtp'),
'inbound-rtp does not exist before packets are received');
// Start sending. This results in inbound-rtp being created.
const stream = await getNoiseStream({video:true});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
await sender.replaceTrack(track);
const inboundRtp = await getInboundRtpPollUntilItExists(pc2);
assert_not_equals(
inboundRtp, null,
'inbound-rtp should be created in response to the sender having a track');
assert_greater_than(
inboundRtp.packetsReceived, 0,
'inbound-rtp must only exist after packets have been received');
}, `RTCInboundRtpStreamStats are created by packet reception`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
// Negotiate a dummy m-section for DTLS to be established.
pc1.addTransceiver('video', {direction:'sendonly'});
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription();
await pc1.setRemoteDescription(pc2.localDescription);
// Offer to receive.
pc1.addTransceiver('video', {direction:'recvonly'});
await pc1.setLocalDescription();
await pc2.setRemoteDescription(pc1.localDescription);
assert_false(await hasStats(pc1, 'inbound-rtp'),
'inbound-rtp does not exist before packets are received');
// Answer to send.
const transceivers = pc2.getTransceivers();
assert_equals(transceivers.length, 2);
const transceiver = transceivers[1];
const stream = await getNoiseStream({video:true});
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
await transceiver.sender.replaceTrack(track);
transceiver.direction = 'sendonly';
await pc2.setLocalDescription();
// We never set the remote answer.
assert_equals(pc1.signalingState, 'have-local-offer');
// But because of early media, we're still able to receive packets.
// - Whether or not we unmute the track in response to this is outside the
// scope of this test.
const inboundRtp = await getInboundRtpPollUntilItExists(pc1);
assert_not_equals(
inboundRtp, null,
'inbound-rtp should be created in the early media use case');
assert_greater_than(
inboundRtp.packetsReceived, 0,
'inbound-rtp must only exist after packets have been received');
}, `RTCInboundRtpStreamStats exists for early media`);
</script>