2 <meta http-equiv=
"content-type" content=
"text/html; charset=utf-8" />
4 <title>Canvas2D test: CaptureStream() with throttled rAF
</title>
6 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
7 <script src=
"captureStream_common.js"></script>
8 <link rel=
"stylesheet" href=
"/tests/SimpleTest/test.css">
11 SimpleTest
.waitForExplicitFinish();
12 SimpleTest
.requestFlakyTimeout("Ensuring nothing happens until timing out with good margin");
14 // CaptureStreamTestHelper holding utility test functions.
15 const h
= new CaptureStreamTestHelper2D();
17 async
function measureRequestAnimationFrameRate() {
18 const frameRate
= await
new Promise(resolve
=> {
21 const tick
= time
=> {
28 if (time
- start
> 1000) {
29 // One second has passed, break.
30 resolve(count
/ ((time
- start
) / 1000));
33 window
.requestAnimationFrame(tick
);
35 window
.requestAnimationFrame(tick
);
40 async
function measureSetTimeoutRate() {
41 const start
= performance
.now();
43 for(let i
= 0; i
< COUNT
; ++i
) {
44 await
new Promise(resolve
=> setTimeout(resolve
, 0));
46 return COUNT
/ ((performance
.now() - start
) / 1000);
49 async
function measureCanvasCaptureFrameRate(captureRate
) {
50 // Canvas element captured by streams.
51 const c
= h
.createAndAppendElement('canvas', 'c');
53 // Since we are in a background tab, the video element won't get composited,
54 // so we cannot look for a frame count there. Instead we use RTCPeerConnection
55 // and RTCOutboundRtpStreamStats.framesEncoded.
56 const pc1
= new RTCPeerConnection();
57 const pc2
= new RTCPeerConnection();
59 // Add the canvas.captureStream track.
60 const ctx
= c
.getContext('2d');
61 const [track
] = c
.captureStream(captureRate
).getTracks();
62 const sender
= pc1
.addTrack(track
);
64 // Ice candidates signaling
65 for (const [local
, remote
] of [[pc1
, pc2
], [pc2
, pc1
]]) {
66 local
.addEventListener("icecandidate", ({candidate
}) => {
67 if (!candidate
|| remote
.signalingState
== "closed") {
70 remote
.addIceCandidate(candidate
);
74 // Offer/Answer exchange
75 await pc1
.setLocalDescription(await pc1
.createOffer());
76 await pc2
.setRemoteDescription(pc1
.localDescription
);
77 await pc2
.setLocalDescription(await pc2
.createAnswer());
78 await pc1
.setRemoteDescription(pc2
.localDescription
);
80 // Wait for connection
81 while ([pc1
, pc2
].some(pc
=> pc
.iceConnectionState
== "new" ||
82 pc
.iceConnectionState
== "checking")) {
84 [pc1
, pc2
].map(p
=> new Promise(r
=> p
.oniceconnectionstatechange
= r
)));
86 for (const [pc
, name
] of [[pc1
, "pc1"], [pc2
, "pc2"]]) {
87 ok(["connected", "completed"].includes(pc
.iceConnectionState
),
88 `${name} connection established (${pc.iceConnectionState})`);
92 const intervalMillis
= 1000 / 60;
93 const getFrameCount
= async () => {
94 const stats
= await sender
.getStats();
96 [...stats
.values()].find(({type
}) => type
== "outbound-rtp");
97 return outbound
?.framesEncoded
?? 0;
99 // Wait for frame count change to ensure sender is working.
100 while (await
getFrameCount() == 0) {
101 h
.drawColor(c
, h
.green
);
102 await
new Promise(resolve
=> setTimeout(resolve
, intervalMillis
));
104 const startFrameCount
= await
getFrameCount();
105 const start
= performance
.now();
107 while(end
- start
<= 1000) {
108 h
.drawColor(c
, h
.green
);
109 await
new Promise(resolve
=> setTimeout(resolve
, intervalMillis
));
110 end
= performance
.now();
112 const framerate
= (await
getFrameCount() - startFrameCount
) / ((end
- start
) / 1000);
119 // Disable background timer throttling so we can use setTimeout to draw to the
120 // canvas while the refresh driver is throttled.
121 await SpecialPowers
.pushPrefEnv({
123 ["dom.timeout.enable_budget_timer_throttling", false],
124 ["dom.min_background_timeout_value", 0],
125 ["dom.min_background_timeout_value_without_budget_throttling", 0],
126 ["browser.link.open_newwindow", 3], // window.open in new tab
129 // Throttle the canvas' refresh driver by opening a new foreground tab
130 const foregroundTab
= window
.open("about:blank");
132 // Let the throttling take effect
133 await
new Promise(r
=> setTimeout(r
, 500));
135 const rAFRate
= await
measureRequestAnimationFrameRate();
136 ok(rAFRate
< 5, `rAF framerate is at ${rAFRate} fps`);
138 const setTimeoutRate
= await
measureSetTimeoutRate();
139 ok(setTimeoutRate
> 30, `setTimeout rate is at ${setTimeoutRate} tps`);
141 const autoRate
= await
measureCanvasCaptureFrameRate();
142 ok(autoRate
> 5, `captureStream() framerate is at ${autoRate} fps`);
144 const cappedRate
= await
measureCanvasCaptureFrameRate(10);
145 ok(cappedRate
> 5, `captureStream(10) framerate is at ${cappedRate} fps`);
147 foregroundTab
.close();