| <!doctype html> |
| <html> |
| <head> |
| <meta charset=utf-8> |
| <meta name="timeout" content="long"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/common/gc.js"></script> |
| <script src="RTCPeerConnection-helper.js"></script> |
| </head> |
| <body> |
| <script> |
| 'use strict'; |
| |
| // Check that RTCPeerConnection is not collected by GC while displaying video. |
| |
| promise_test(async t => { |
| const canvas = document.createElement('canvas'); |
| canvas.width = canvas.height = 160; |
| const ctx = canvas.getContext("2d"); |
| ctx.fillStyle = "blue"; |
| const drawCanvas = () => { |
| ctx.fillRect(0, 0, canvas.width, canvas.height); |
| t.step_timeout(drawCanvas, 50); |
| }; |
| |
| drawCanvas(); |
| |
| let pc1 = new RTCPeerConnection(); |
| let pc2 = new RTCPeerConnection(); |
| |
| // Attach video to pc1. |
| const [inputTrack] = canvas.captureStream().getTracks(); |
| pc1.addTrack(inputTrack); |
| |
| const destVideo = document.createElement('video'); |
| destVideo.autoplay = true; |
| destVideo.muted = true; |
| const onVideoChange = async () => { |
| const start = performance.now(); |
| const width = destVideo.videoWidth; |
| const height = destVideo.videoHeight; |
| while (destVideo.videoWidth == width && destVideo.videoHeight == height) { |
| if (performance.now() - start > 5000) { |
| throw new Error("Timeout waiting for video size change"); |
| } |
| await new Promise(r => t.step_timeout(r, 50)); |
| } |
| }; |
| |
| // Setup cleanup. We cannot keep references to pc1 or pc2 so do a best-effort with GC. |
| t.add_cleanup(async () => { |
| inputTrack.stop(); |
| destVideo.srcObject = null; |
| await garbageCollect(); |
| }); |
| |
| // Setup pc1->pc2. |
| let haveTrackEvent = new Promise(r => pc2.ontrack = r); |
| exchangeIceCandidates(pc1, pc2); |
| await pc1.setLocalDescription(); |
| await pc2.setRemoteDescription(pc1.localDescription); |
| await pc2.setLocalDescription(); |
| await pc1.setRemoteDescription(pc2.localDescription); |
| |
| // Display pc2 received track in video element. |
| const loadedMetadata = new Promise(r => destVideo.onloadedmetadata = r); |
| destVideo.srcObject = new MediaStream([(await haveTrackEvent).track]); |
| |
| // Wait for video on the other side. |
| await destVideo.play(); |
| const color = getVideoSignal(destVideo); |
| assert_not_equals(color, 0); |
| |
| // Remove RTCPeerConnection references and garbage collect. |
| pc1 = null; |
| pc2 = null; |
| haveTrackEvent = null; |
| await garbageCollect(); |
| |
| // Check that a change to video input is reflected in the output, i.e., the |
| // peer connections were not garbage collected. |
| canvas.width = canvas.height = 240; |
| ctx.fillStyle = "red"; |
| await onVideoChange(); |
| assert_not_equals(color, getVideoSignal(destVideo)); |
| }, "GC does not collect a peer connection pipe rendering to a video element"); |
| |
| promise_test(async t => { |
| const pc1 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc1.close()); |
| const pc2 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc2.close()); |
| |
| const [track, stream] = await createTrackAndStreamWithCleanup(t, "video"); |
| pc1.addTrack(track, stream); |
| exchangeIceCandidates(pc1, pc2); |
| |
| const metadataToBeLoaded = []; |
| pc2.ontrack = (e) => { |
| const stream = e.streams[0]; |
| const v = document.createElement('video'); |
| v.autoplay = true; |
| v.srcObject = stream; |
| v.id = stream.id |
| metadataToBeLoaded.push(new Promise((resolve) => { |
| v.addEventListener('loadedmetadata', () => { |
| resolve(); |
| }); |
| })); |
| }; |
| await exchangeOfferAnswer(pc1, pc2); |
| |
| garbageCollect(); |
| |
| await Promise.all(metadataToBeLoaded); |
| }, "GC does not collect an HTMLMediaElement playing a video track"); |
| </script> |
| </body> |
| </html> |
| |