split.https.html (3839B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>RTCPeerConnection BUNDLE</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="../RTCPeerConnection-helper.js"></script> 7 <script src="/webrtc/third_party/sdp/sdp.js"></script> 8 <script> 9 'use strict'; 10 promise_test(async t => { 11 const caller = new RTCPeerConnection(); 12 t.add_cleanup(() => caller.close()); 13 const calleeAudio = new RTCPeerConnection(); 14 t.add_cleanup(() => calleeAudio.close()); 15 const calleeVideo = new RTCPeerConnection(); 16 t.add_cleanup(() => calleeVideo.close()); 17 18 const stream = await getNoiseStream({audio: true, video: true}); 19 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 20 stream.getTracks().forEach(track => caller.addTrack(track, stream)); 21 22 let metadataToBeLoaded; 23 calleeVideo.ontrack = (e) => { 24 const stream = e.streams[0]; 25 const v = document.createElement('video'); 26 v.autoplay = true; 27 v.srcObject = stream; 28 v.id = stream.id 29 metadataToBeLoaded = new Promise((resolve) => { 30 v.addEventListener('loadedmetadata', () => { 31 resolve(); 32 }); 33 }); 34 }; 35 36 caller.addEventListener('icecandidate', (e) => { 37 // route depending on sdpMlineIndex 38 if (e.candidate) { 39 const target = e.candidate.sdpMLineIndex === 0 ? calleeAudio : calleeVideo; 40 target.addIceCandidate({sdpMid: e.candidate.sdpMid, candidate: e.candidate.candidate}); 41 } else { 42 calleeAudio.addIceCandidate(); 43 calleeVideo.addIceCandidate(); 44 } 45 }); 46 calleeAudio.addEventListener('icecandidate', (e) => { 47 if (e.candidate) { 48 caller.addIceCandidate({sdpMid: e.candidate.sdpMid, candidate: e.candidate.candidate}); 49 } 50 // Note: caller.addIceCandidate is only called for video to avoid calling it twice. 51 }); 52 calleeVideo.addEventListener('icecandidate', (e) => { 53 if (e.candidate) { 54 caller.addIceCandidate({sdpMid: e.candidate.sdpMid, candidate: e.candidate.candidate}); 55 } else { 56 caller.addIceCandidate(); 57 } 58 }); 59 60 const offer = await caller.createOffer(); 61 const sections = SDPUtils.splitSections(offer.sdp); 62 // Remove the a=group:BUNDLE from the SDP when signaling. 63 const bundle = SDPUtils.matchPrefix(sections[0], 'a=group:BUNDLE')[0]; 64 sections[0] = sections[0].replace(bundle + '\r\n', ''); 65 66 const audioSdp = sections[0] + sections[1]; 67 const videoSdp = sections[0] + sections[2]; 68 69 await calleeAudio.setRemoteDescription({type: 'offer', sdp: audioSdp}); 70 await calleeVideo.setRemoteDescription({type: 'offer', sdp: videoSdp}); 71 await caller.setLocalDescription(offer); 72 73 const answerAudio = await calleeAudio.createAnswer(); 74 const answerVideo = await calleeVideo.createAnswer(); 75 const audioSections = SDPUtils.splitSections(answerAudio.sdp); 76 const videoSections = SDPUtils.splitSections(answerVideo.sdp); 77 78 // Remove the fingerprint from the session part of the SDP if present 79 // and move it to the media section. 80 SDPUtils.matchPrefix(audioSections[0], 'a=fingerprint:').forEach(line => { 81 audioSections[0] = audioSections[0].replace(line + '\r\n', ''); 82 audioSections[1] += line + '\r\n'; 83 }); 84 SDPUtils.matchPrefix(videoSections[0], 'a=fingerprint:').forEach(line => { 85 videoSections[0] = videoSections[0].replace(line + '\r\n', ''); 86 videoSections[1] += line + '\r\n'; 87 }); 88 89 const sdp = audioSections[0] + audioSections[1] + videoSections[1]; 90 await caller.setRemoteDescription({type: 'answer', sdp}); 91 await calleeAudio.setLocalDescription(answerAudio); 92 await calleeVideo.setLocalDescription(answerVideo); 93 94 await metadataToBeLoaded; 95 assert_equals(calleeAudio.connectionState, 'connected'); 96 assert_equals(calleeVideo.connectionState, 'connected'); 97 }, 'Connect audio and video to two independent PeerConnections'); 98 </script>