Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!doctype html>
<meta charset=utf-8>
<title>RTCPeerConnection Simulcast Tests - setParameters/maxFramerate</title>
<meta name="timeout" content="long">
<script src="../third_party/sdp/sdp.js"></script>
<script src="simulcast.js"></script>
<script src="../RTCPeerConnection-helper.js"></script>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="../../mediacapture-streams/permission-helper.js"></script>
<script>
async function queryReceiverStats(pc) {
const inboundStats = [];
await Promise.all(pc.getReceivers().map(async receiver => {
const receiverStats = await receiver.getStats();
receiverStats.forEach(stat => {
if (stat.type === 'inbound-rtp') {
inboundStats.push(stat);
}
});
}));
return inboundStats;
}
async function statsDelta(pc, t) {
const initialStats = await queryReceiverStats(pc);
await new Promise(resolve => t.step_timeout(resolve, 1000)); // Wait more.
const subsequentStats = await queryReceiverStats(pc);
return {initialStats, subsequentStats};
}
function calculateFramerate(newStats, oldStats) {
const deltaF = newStats.framesDecoded - oldStats.framesDecoded;
const deltaT = newStats.timestamp - oldStats.timestamp;
return deltaF / deltaT * 1000;
}
function calculateConservativeFramerate(newStats, oldStats) {
// The timestamp represents the exact point in time that the integer
// framesDecoded was sampled. For both samples, one time unit (say a
// nanosecond, i.e. virtually 0) could sway the integer framesDecoded value
// up to 1 frame. For checking that we don't exceed maxFramerate, in the worst
// case, the old timestamp is then at the end of a frame period (right before
// framesDecoded gets incremented) while the new timestamp is at the beginning
// of a frame period. Thus for avoiding intermittents we need to allow up to
// two frames more for the given sampling period, regardless of length.
const deltaF = newStats.framesDecoded - oldStats.framesDecoded - 2;
const deltaT = newStats.timestamp - oldStats.timestamp;
return deltaF / deltaT * 1000;
}
promise_test(async t => {
const expectedFramerates = [15, 10, 5];
const rids = [0, 1, 2];
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
await negotiateSimulcastAndWaitForVideo(
t,
await getCameraStream(t),
rids,
pc1,
pc2
);
// Wait for framerate to stabilize.
await new Promise(resolve => t.step_timeout(resolve, 1000));
// Assert that our framerate is bigger than 20, otherwise this test does not
// assert anything useful.
const defaultStats = await statsDelta(pc2, t);
defaultStats.subsequentStats.forEach((_, idx) => {
const actualFramerate = calculateFramerate(
defaultStats.subsequentStats[idx],
defaultStats.initialStats[idx]
);
assert_greater_than(actualFramerate, 20);
});
// Change the framerate on all layers.
const parameters = pc1.getSenders()[0].getParameters();
parameters.encodings.forEach((e, idx) => {
e.maxFramerate = expectedFramerates[idx];
});
await pc1.getSenders()[0].setParameters(parameters);
// Wait for the change to propagate to the receiver.
await new Promise(resolve => t.step_timeout(resolve, 100));
// Assert the approximate framerate
const newStats = await statsDelta(pc2, t);
const framerates = [];
newStats.subsequentStats.forEach((_, idx) => {
const expectedFramerate = expectedFramerates[idx];
const actualFramerate = calculateConservativeFramerate(
newStats.subsequentStats[idx],
newStats.initialStats[idx]
);
// Assert that the framerate is at most the expected framerate.
assert_less_than(actualFramerate, expectedFramerate);
framerates.push(actualFramerate);
});
// Assert that the framerates are ordered as configured.
assert_equals(framerates, framerates.sort());
}, 'Simulcast setParameters maxFramerate reduces the framerate');
</script>