RTCPeerConnection-GC.https.html (3666B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <meta name="timeout" content="long"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/common/gc.js"></script> 9 <script src="RTCPeerConnection-helper.js"></script> 10 </head> 11 <body> 12 <script> 13 'use strict'; 14 15 // Check that RTCPeerConnection is not collected by GC while displaying video. 16 17 promise_test(async t => { 18 const canvas = document.createElement('canvas'); 19 canvas.width = canvas.height = 160; 20 const ctx = canvas.getContext("2d"); 21 ctx.fillStyle = "blue"; 22 const drawCanvas = () => { 23 ctx.fillRect(0, 0, canvas.width, canvas.height); 24 t.step_timeout(drawCanvas, 50); 25 }; 26 27 drawCanvas(); 28 29 let pc1 = new RTCPeerConnection(); 30 let pc2 = new RTCPeerConnection(); 31 32 // Attach video to pc1. 33 const [inputTrack] = canvas.captureStream().getTracks(); 34 pc1.addTrack(inputTrack); 35 36 const destVideo = document.createElement('video'); 37 destVideo.autoplay = true; 38 destVideo.muted = true; 39 const onVideoChange = async () => { 40 const start = performance.now(); 41 const width = destVideo.videoWidth; 42 const height = destVideo.videoHeight; 43 while (destVideo.videoWidth == width && destVideo.videoHeight == height) { 44 if (performance.now() - start > 5000) { 45 throw new Error("Timeout waiting for video size change"); 46 } 47 await new Promise(r => t.step_timeout(r, 50)); 48 } 49 }; 50 51 // Setup cleanup. We cannot keep references to pc1 or pc2 so do a best-effort with GC. 52 t.add_cleanup(async () => { 53 inputTrack.stop(); 54 destVideo.srcObject = null; 55 await garbageCollect(); 56 }); 57 58 // Setup pc1->pc2. 59 let haveTrackEvent = new Promise(r => pc2.ontrack = r); 60 exchangeIceCandidates(pc1, pc2); 61 await pc1.setLocalDescription(); 62 await pc2.setRemoteDescription(pc1.localDescription); 63 await pc2.setLocalDescription(); 64 await pc1.setRemoteDescription(pc2.localDescription); 65 66 // Display pc2 received track in video element. 67 const loadedMetadata = new Promise(r => destVideo.onloadedmetadata = r); 68 destVideo.srcObject = new MediaStream([(await haveTrackEvent).track]); 69 70 // Wait for video on the other side. 71 await destVideo.play(); 72 const color = getVideoSignal(destVideo); 73 assert_not_equals(color, 0); 74 75 // Remove RTCPeerConnection references and garbage collect. 76 pc1 = null; 77 pc2 = null; 78 haveTrackEvent = null; 79 await garbageCollect(); 80 81 // Check that a change to video input is reflected in the output, i.e., the 82 // peer connections were not garbage collected. 83 canvas.width = canvas.height = 240; 84 ctx.fillStyle = "red"; 85 await onVideoChange(); 86 assert_not_equals(color, getVideoSignal(destVideo)); 87 }, "GC does not collect a peer connection pipe rendering to a video element"); 88 89 promise_test(async t => { 90 const pc1 = new RTCPeerConnection(); 91 t.add_cleanup(() => pc1.close()); 92 const pc2 = new RTCPeerConnection(); 93 t.add_cleanup(() => pc2.close()); 94 95 const [track, stream] = await createTrackAndStreamWithCleanup(t, "video"); 96 pc1.addTrack(track, stream); 97 exchangeIceCandidates(pc1, pc2); 98 99 const metadataToBeLoaded = []; 100 pc2.ontrack = (e) => { 101 const stream = e.streams[0]; 102 const v = document.createElement('video'); 103 v.autoplay = true; 104 v.srcObject = stream; 105 v.id = stream.id 106 metadataToBeLoaded.push(new Promise((resolve) => { 107 v.addEventListener('loadedmetadata', () => { 108 resolve(); 109 }); 110 })); 111 }; 112 await exchangeOfferAnswer(pc1, pc2); 113 114 garbageCollect(); 115 116 await Promise.all(metadataToBeLoaded); 117 }, "GC does not collect an HTMLMediaElement playing a video track"); 118 </script> 119 </body> 120 </html>