test_peerConnection_threeUnbundledConnections.html (5059B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <script type="application/javascript" src="pc.js"></script> 5 </head> 6 <body> 7 <pre id="test"> 8 <script type="application/javascript"> 9 createHTML({ 10 bug: "1342579", 11 title: "Unbundled PC connects to two different PCs", 12 visible: true 13 }); 14 15 const fakeFingerPrint = "a=fingerprint:sha-256 11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11"; 16 17 const pc1 = new RTCPeerConnection(); 18 const pc2 = new RTCPeerConnection(); 19 const pc3 = new RTCPeerConnection(); 20 21 const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed); 22 pc1.onicecandidate = e => { 23 if (e.candidate) { 24 if (e.candidate.sdpMid === "1") { 25 add(pc2, e.candidate, generateErrorCallback()) 26 } else { 27 add(pc3, e.candidate, generateErrorCallback()) 28 } 29 } 30 }; 31 pc2.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback()); 32 pc3.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback()); 33 34 let ice1Finished, ice2Finished, ice3Finished; 35 const ice1Done = new Promise(r => ice1Finished = r); 36 const ice2Done = new Promise(r => ice2Finished = r); 37 const ice3Done = new Promise(r => ice3Finished = r); 38 39 const icsc = (pc, str, resolve) => { 40 const state = pc.iceConnectionState; 41 info(str + " ICE connection state is: " + state); 42 if (state == "connected") { 43 ok(true, str + " ICE connected"); 44 resolve(); 45 } else if (state == "failed") { 46 ok(false, str + " ICE failed") 47 resolve(); 48 } 49 }; 50 51 pc1.oniceconnectionstatechange = e => icsc(pc1, "PC1", ice1Finished); 52 pc2.oniceconnectionstatechange = e => icsc(pc2, "PC2", ice2Finished); 53 pc3.oniceconnectionstatechange = e => icsc(pc3, "PC3", ice3Finished); 54 55 56 function combineAnswer(origAnswer, answer) { 57 const sdplines = origAnswer.sdp.split('\r\n'); 58 const fpIndex = sdplines.findIndex(l => l.match('^a=fingerprint')); 59 const FP = sdplines[fpIndex]; 60 const audioIndex = sdplines.findIndex(l => l.match(/^m=audio [1-9]/)); 61 const videoIndex = sdplines.findIndex(l => l.match(/^m=video [1-9]/)); 62 if (audioIndex > -1) { 63 var ss = sdplines.slice(0, audioIndex); 64 ss.splice(fpIndex, 1); 65 answer.sessionSection = ss; 66 const rejectedVideoIndex = sdplines.findIndex(l => l.match('m=video 0')); 67 var ams = sdplines.slice(audioIndex, rejectedVideoIndex); 68 ams.push(FP); 69 ams.push(fakeFingerPrint); 70 answer.audioMsection = ams; 71 } 72 if (videoIndex > -1) { 73 var vms = sdplines.slice(videoIndex, sdplines.length -1); 74 vms.push(fakeFingerPrint); 75 vms.push(FP); 76 answer.videoMsection = vms; 77 } 78 return answer; 79 } 80 81 runNetworkTest(async () => { 82 const v1 = createMediaElement('video', 'v1'); 83 const v2 = createMediaElement('video', 'v2'); 84 const v3 = createMediaElement('video', 'v3'); 85 86 const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); 87 (v1.srcObject = stream).getTracks().forEach(t => pc1.addTrack(t, stream)); 88 89 const stream2 = await navigator.mediaDevices.getUserMedia({ video: true }); 90 (v2.srcObject = stream2).getTracks().forEach(t => pc2.addTrack(t, stream2)); 91 92 const stream3 = await navigator.mediaDevices.getUserMedia({ audio: true }); 93 (v3.srcObject = stream3).getTracks().forEach(t => pc3.addTrack(t, stream3)); 94 95 const offer = await pc1.createOffer(); 96 await pc1.setLocalDescription(offer); 97 98 //info("Original OFFER: " + JSON.stringify(offer)); 99 offer.sdp = sdputils.removeBundle(offer.sdp); 100 //info("OFFER w/o BUNDLE: " + JSON.stringify(offer)); 101 const offerAudio = JSON.parse(JSON.stringify(offer)); 102 offerAudio.sdp = offerAudio.sdp.replace('m=video 9', 'm=video 0'); 103 //info("offerAudio: " + JSON.stringify(offerAudio)); 104 const offerVideo = JSON.parse(JSON.stringify(offer)); 105 offerVideo.sdp = offerVideo.sdp.replace('m=audio 9', 'm=audio 0'); 106 //info("offerVideo: " + JSON.stringify(offerVideo)); 107 108 // We need to do these in parallel, otherwise pc1 will start firing 109 // icecandidate events before pc3 is ready. 110 await Promise.all([pc2.setRemoteDescription(offerVideo), pc3.setRemoteDescription(offerAudio)]); 111 112 const answerVideo = await pc2.createAnswer(); 113 const answerAudio = await pc3.createAnswer(); 114 115 const answer = combineAnswer(answerAudio, combineAnswer(answerVideo, {})); 116 const fakeAnswer = answer.sessionSection.concat(answer.audioMsection, answer.videoMsection).join('\r\n'); 117 info("ANSWER: " + fakeAnswer); 118 119 // We want to do these in parallel, because if we do them seqentially, by the 120 // time pc3.sLD completes pc2 could have fired icecandidate events, when we 121 // haven't called pc1.sRD yet. 122 await Promise.all( 123 [pc2.setLocalDescription(answerVideo), 124 pc3.setLocalDescription(answerAudio), 125 pc1.setRemoteDescription({type: 'answer', sdp: fakeAnswer})]); 126 127 await Promise.all([ice1Done, ice2Done, ice3Done]); 128 129 ok(true, "Connected."); 130 }); 131 </script> 132 </pre> 133 </body> 134 </html>