| <!doctype html> |
| <html> |
| <head> |
| <script src="../resources/testharness.js"></script> |
| <script src="../resources/testharnessreport.js"></script> |
| </head> |
| <body> |
| <div> |
| <video id="low" playsinline autoplay width="320"></video> |
| <video id="mid" playsinline autoplay width="320"></video> |
| <video id="high" playsinline autoplay width="320"></video> |
| </div> |
| <script> |
| if (window.testRunner) |
| testRunner.setWebRTCUnifiedPlanEnabled(false); |
| var state; |
| var finished = false; |
| // This test is largely inspired from Chrome/Firefox/Simulcast playground tests. |
| promise_test(async (test) => { |
| if (window.testRunner && testRunner.timeout) { |
| setTimeout(() => { |
| if (!finished) |
| throw "test stucked in state: " + state; |
| }, testRunner.timeout * 0.9); |
| } |
| |
| state = "start"; |
| |
| const pc1 = new RTCPeerConnection(); |
| const pc2 = new RTCPeerConnection(); |
| pc1.onicecandidate = (e) => pc2.addIceCandidate(e.candidate); |
| pc2.onicecandidate = (e) => pc1.addIceCandidate(e.candidate); |
| |
| let counter = 0; |
| pc2.ontrack = (e) => { |
| if (counter == 0) |
| low.srcObject = new MediaStream([e.track]); |
| else if (counter == 1) |
| mid.srcObject = new MediaStream([e.track]); |
| else |
| high.srcObject = new MediaStream([e.track]); |
| ++counter; |
| } |
| |
| const localStream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 } }); |
| pc1.addTrack(localStream.getVideoTracks()[0], localStream); |
| const offer = await pc1.createOffer(); |
| |
| state = "got offer"; |
| |
| match = offer.sdp.match(/a=ssrc:(\d+) cname:(.*)\r\n/); |
| msid = offer.sdp.match(/a=ssrc:(\d+) msid:(.*)\r\n/); |
| var lines = offer.sdp.trim().split('\r\n'); |
| var removed = lines.splice(lines.length - 4, 4); |
| var videoSSRC1 = parseInt(match[1]); |
| rtxSSRC1 = offer.sdp.split('\r\n').filter((line) => { return line.startsWith('a=ssrc-group:FID ')})[0].split(' ')[2]; |
| var videoSSRC2 = videoSSRC1 + 1; |
| var rtxSSRC2 = videoSSRC1 + 2; |
| var videoSSRC3 = videoSSRC1 + 3; |
| var rtxSSRC3 = videoSSRC1 + 4; |
| lines.push(removed[0]); |
| lines.push(removed[1]); |
| lines.push('a=ssrc:' + videoSSRC2 + ' cname:' + match[2]); |
| lines.push('a=ssrc:' + videoSSRC2 + ' msid:' + msid[2]); |
| lines.push('a=ssrc:' + rtxSSRC2 + ' cname:' + match[2]); |
| lines.push('a=ssrc:' + rtxSSRC2 + ' msid:' + msid[2]); |
| |
| lines.push('a=ssrc:' + videoSSRC3 + ' cname:' + match[2]); |
| lines.push('a=ssrc:' + videoSSRC3 + ' msid:' + msid[2]); |
| lines.push('a=ssrc:' + rtxSSRC3 + ' cname:' + match[2]); |
| lines.push('a=ssrc:' + rtxSSRC3 + ' msid:' + msid[2]); |
| |
| lines.push('a=ssrc-group:FID ' + videoSSRC2 + ' ' + rtxSSRC2); |
| lines.push('a=ssrc-group:FID ' + videoSSRC3 + ' ' + rtxSSRC3); |
| lines.push('a=ssrc-group:SIM ' + videoSSRC1 + ' ' + videoSSRC2 + ' ' + videoSSRC3); |
| offer.sdp = lines.join('\r\n') + '\r\n'; |
| |
| var offer2 = { |
| type: 'offer', |
| sdp: offer.sdp, |
| }; |
| offer2.sdp = offer2.sdp.replace('a=ssrc-group:SIM ' + videoSSRC1 + ' ' + videoSSRC2 + ' ' + videoSSRC3 + '\r\n', ''); |
| |
| offer2.sdp = offer2.sdp.replace('a=ssrc:' + videoSSRC1 + ' msid:' + msid[2], 'a=ssrc:' + videoSSRC1 + ' msid:low low'); |
| offer2.sdp = offer2.sdp.replace('a=ssrc:' + rtxSSRC1 + ' msid:' + msid[2], 'a=ssrc:' + rtxSSRC1 + ' msid:low low'); |
| |
| offer2.sdp = offer2.sdp.replace('a=ssrc:' + videoSSRC2 + ' msid:' + msid[2], 'a=ssrc:' + videoSSRC2 + ' msid:mid mid'); |
| offer2.sdp = offer2.sdp.replace('a=ssrc:' + rtxSSRC2 + ' msid:' + msid[2], 'a=ssrc:' + rtxSSRC2 + ' msid:mid mid'); |
| |
| offer2.sdp = offer2.sdp.replace('a=ssrc:' + videoSSRC3 + ' msid:' + msid[2], 'a=ssrc:' + videoSSRC3 + ' msid:hi hi'); |
| offer2.sdp = offer2.sdp.replace('a=ssrc:' + rtxSSRC3 + ' msid:' + msid[2], 'a=ssrc:' + rtxSSRC3 + ' msid:hi hi'); |
| await Promise.all([ |
| pc1.setLocalDescription(offer), |
| pc2.setRemoteDescription(offer2), |
| ]); |
| |
| state = "set description"; |
| |
| const answer = await pc2.createAnswer(); |
| |
| state = "got answer"; |
| |
| await Promise.all([ |
| pc2.setLocalDescription(answer), |
| pc1.setRemoteDescription(answer), |
| ]); |
| |
| state = "set description 2"; |
| |
| await low.play(); |
| state = "video low plays"; |
| |
| assert_equals(low.srcObject.getVideoTracks()[0].getSettings().height, 240); |
| assert_equals(low.srcObject.getVideoTracks()[0].getSettings().width, 320); |
| |
| await mid.play(); |
| state = "video mid plays"; |
| |
| assert_equals(mid.srcObject.getVideoTracks()[0].getSettings().height, 480); |
| assert_equals(mid.srcObject.getVideoTracks()[0].getSettings().width, 640); |
| |
| finished = true; |
| }, "Testing simulcast"); |
| </script> |
| </body> |
| </html> |