test_peerConnection_simulcastOddResolution.html (8090B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <script type="application/javascript" src="pc.js"></script> 5 <script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script> 6 <script type="application/javascript" src="helpers_from_wpt/sdp.js"></script> 7 <script type="application/javascript" src="simulcast.js"></script> 8 <script type="application/javascript" src="stats.js"></script> 9 </head> 10 <body> 11 <pre id="test"> 12 <script type="application/javascript"> 13 createHTML({ 14 bug: "1432793", 15 title: "Simulcast with odd resolution", 16 visible: true 17 }); 18 19 runNetworkTest(async () => { 20 const helper = new VideoStreamHelper(); 21 const emitter = new VideoFrameEmitter(helper.green, helper.red, 705, 528); 22 23 async function checkVideoElement(senderElement, receiverElement, encoding) { 24 info(`Waiting for receiver video element ${encoding.rid} to start playing`); 25 await helper.checkVideoPlaying(receiverElement); 26 const srcWidth = senderElement.videoWidth; 27 const srcHeight = senderElement.videoHeight; 28 info(`Source resolution is ${srcWidth}x${srcHeight}`); 29 30 const scaleDownBy = encoding.scaleResolutionDownBy; 31 const expectedWidth = srcWidth / scaleDownBy; 32 const expectedHeight = srcHeight / scaleDownBy; 33 const margin = srcWidth * 0.1; 34 const width = receiverElement.videoWidth; 35 const height = receiverElement.videoHeight; 36 const rid = encoding.rid; 37 ok(width >= expectedWidth - margin && width <= expectedWidth + margin, 38 `Width ${width} should be within 10% of ${expectedWidth} for rid '${rid}'`); 39 ok(height >= expectedHeight - margin && height <= expectedHeight + margin, 40 `Height ${height} should be within 10% of ${expectedHeight} for rid '${rid}'`); 41 } 42 43 async function checkVideoElements(senderElement, receiverElements, encodings) { 44 is(receiverElements.length, encodings.length, 'Number of video elements should match number of encodings'); 45 info('Waiting for sender video element to start playing'); 46 await helper.checkVideoPlaying(senderElement); 47 for (let i = 0; i < encodings.length; i++) { 48 await checkVideoElement(senderElement, receiverElements[i], encodings[i]); 49 } 50 } 51 52 const sendEncodings = [{ rid: "0", maxBitrate: 40000, scaleResolutionDownBy: 1.9 }, 53 { rid: "1", maxBitrate: 40000, scaleResolutionDownBy: 3.5 }, 54 { rid: "2", maxBitrate: 40000, scaleResolutionDownBy: 6.8 }]; 55 56 async function checkSenderStats(sender) { 57 const senderStats = await sender.getStats(); 58 checkSenderStats(senderStats, sendEncodings.length); 59 checkExpectedFields(senderStats); 60 pedanticChecks(senderStats); 61 } 62 63 async function waitForResizeEvents(elements) { 64 return Promise.all(elements.map(elem => haveEvent(elem, 'resize'))); 65 } 66 67 await pushPrefs( 68 // 180Kbps was determined empirically, set well-higher than 69 // the 80Kbps+overhead needed for the two simulcast streams. 70 // 100Kbps was apparently too low. 71 ['media.peerconnection.video.min_bitrate_estimate', 180*1000]); 72 73 // [TODO] re-enable HW decoder after bug 1526207 is fixed. 74 if (navigator.userAgent.includes("Android")) { 75 await pushPrefs(["media.navigator.mediadatadecoder_vpx_enabled", false]); 76 } 77 78 79 const offerer = new RTCPeerConnection(); 80 const answerer = new RTCPeerConnection(); 81 82 const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed); 83 offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback()); 84 answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback()); 85 86 const metadataToBeLoaded = []; 87 answerer.ontrack = (e) => { 88 metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track)); 89 }; 90 91 // One send transceiver, that will be used to send both simulcast streams 92 const videoStream = emitter.stream(); 93 offerer.addTransceiver(videoStream.getVideoTracks()[0], {sendEncodings}); 94 const senderElement = document.createElement('video'); 95 senderElement.autoplay = true; 96 senderElement.srcObject = videoStream; 97 senderElement.id = videoStream.id 98 99 const sender = offerer.getSenders()[0]; 100 let parameters = sender.getParameters(); 101 is(parameters.encodings[0].maxBitrate, sendEncodings[0].maxBitrate); 102 isfuzzy(parameters.encodings[0].scaleResolutionDownBy, 103 sendEncodings[0].scaleResolutionDownBy, 0.01); 104 is(parameters.encodings[1].maxBitrate, sendEncodings[1].maxBitrate); 105 isfuzzy(parameters.encodings[1].scaleResolutionDownBy, 106 sendEncodings[1].scaleResolutionDownBy, 0.01); 107 is(parameters.encodings[2].maxBitrate, sendEncodings[2].maxBitrate); 108 isfuzzy(parameters.encodings[2].scaleResolutionDownBy, 109 sendEncodings[2].scaleResolutionDownBy, 0.01); 110 111 const offer = await offerer.createOffer(); 112 113 const mungedOffer = ridToMid(offer); 114 info(`Transformed send simulcast offer to multiple m-sections: ${offer.sdp} to ${mungedOffer}`); 115 116 await answerer.setRemoteDescription({type: 'offer', sdp: mungedOffer}); 117 await offerer.setLocalDescription(offer); 118 119 const rids = answerer.getTransceivers().map(t => t.mid); 120 is(rids.length, 3, 'Should have 3 mids in offer'); 121 ok(rids[0], 'First mid should be non-empty'); 122 ok(rids[1], 'Second mid should be non-empty'); 123 ok(rids[2], 'Third mid should be non-empty'); 124 info(`rids: ${JSON.stringify(rids)}`); 125 126 const answer = await answerer.createAnswer(); 127 128 const mungedAnswer = midToRid(answer); 129 info(`Transformed recv answer to simulcast: ${answer.sdp} to ${mungedAnswer}`); 130 await offerer.setRemoteDescription({type: 'answer', sdp: mungedAnswer}); 131 await answerer.setLocalDescription(answer); 132 133 is(metadataToBeLoaded.length, 3, 'Offerer should have gotten 3 ontrack events'); 134 emitter.start(); 135 info('Waiting for 3 loadedmetadata events'); 136 const videoElems = await Promise.all(metadataToBeLoaded); 137 await checkVideoElements(senderElement, videoElems, parameters.encodings); 138 emitter.stop(); 139 140 await Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]); 141 142 info(`Changing source resolution to 1280x720`); 143 emitter.size(1280, 720); 144 emitter.start(); 145 await waitForResizeEvents([senderElement, ...videoElems]); 146 await checkVideoElements(senderElement, videoElems, parameters.encodings); 147 await checkSenderStats(sender); 148 149 parameters = sender.getParameters(); 150 parameters.encodings[0].scaleResolutionDownBy = 1; 151 parameters.encodings[1].scaleResolutionDownBy = 2; 152 parameters.encodings[2].scaleResolutionDownBy = 3; 153 info(`Changing encodings to ${JSON.stringify(parameters.encodings)}`); 154 await sender.setParameters(parameters); 155 await waitForResizeEvents(videoElems); 156 await checkVideoElements(senderElement, videoElems, parameters.encodings); 157 await checkSenderStats(sender); 158 159 parameters = sender.getParameters(); 160 parameters.encodings[0].scaleResolutionDownBy = 6; 161 parameters.encodings[1].scaleResolutionDownBy = 5; 162 parameters.encodings[2].scaleResolutionDownBy = 4; 163 info(`Changing encodings to ${JSON.stringify(parameters.encodings)}`); 164 await sender.setParameters(parameters); 165 await waitForResizeEvents(videoElems); 166 await checkVideoElements(senderElement, videoElems, parameters.encodings); 167 await checkSenderStats(sender); 168 169 parameters = sender.getParameters(); 170 parameters.encodings[0].scaleResolutionDownBy = 4; 171 parameters.encodings[1].scaleResolutionDownBy = 1; 172 parameters.encodings[2].scaleResolutionDownBy = 2; 173 info(`Changing encodings to ${JSON.stringify(parameters.encodings)}`); 174 await sender.setParameters(parameters); 175 await waitForResizeEvents(videoElems); 176 await checkVideoElements(senderElement, videoElems, parameters.encodings); 177 await checkSenderStats(sender); 178 179 emitter.stop(); 180 videoStream.getVideoTracks()[0].stop(); 181 offerer.close(); 182 answerer.close(); 183 }); 184 185 </script> 186 </pre> 187 </body> 188 </html>