html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>webrtc</title> <style> #yours{ width:300px; position:absolute; top:200px; left:100px; } #theirs{ width:300px; position:absolute; top:200px; left:400px; } </style> </head> <body> <button onclick="createOffer()">建立连接</button> <video id="yours" autoplay></video> <video id="theirs" autoplay></video> </body> <script src="./lib/jquery.min.js"></script> <script src="./lib/webrtc.js"></script> </html>
webrtc.js
1 var websocket; 2 3 function randomNum(minNum,maxNum){ 4 switch(arguments.length){ 5 case 1: 6 return parseInt(Math.random()*minNum+1,10); 7 break; 8 case 2: 9 return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10); 10 break; 11 default: 12 return 0; 13 break; 14 } 15 } 16 const userid = 'user' + randomNum(0,100000); 17 18 function hasUserMedia() { 19 navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; 20 return !!navigator.getUserMedia; 21 } 22 function hasRTCPeerConnection() { 23 window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection; 24 return !!window.RTCPeerConnection; 25 } 26 27 var yourVideo = document.getElementById("yours"); 28 var theirVideo = document.getElementById("theirs"); 29 var Connection; 30 31 32 function startPeerConnection() { 33 //return; 34 var config = { 35 'iceServers': [ 36 //{ 'urls': 'stun:stun.xten.com:3478' }, 37 //{ 'urls': 'stun:stun.voxgratia.org:3478' }, 38 { 'url': 'stun:stun.l.google.com:19302' } 39 ] 40 }; 41 config = { 42 iceServers: [ 43 { urls: 'stun:stun.l.google.com:19302' }, 44 { urls: 'stun:global.stun.twilio.com:3478?transport=udp' } 45 ], 46 //sdpSemantics: 'unified-plan' 47 }; 48 // { 49 // "iceServers": [{ 50 // "url": "stun:stun.1.google.com:19302" 51 // }] 52 // }; 53 Connection = new RTCPeerConnection(config); 54 Connection.onicecandidate = function(e) { 55 console.log('onicecandidate'); 56 if (e.candidate) { 57 websocket.send(JSON.stringify({ 58 "userid":userid, 59 "event": "_ice_candidate", 60 "data": { 61 "candidate": e.candidate 62 } 63 })); 64 } 65 } 66 Connection.onaddstream = function(e) { 67 console.log('onaddstream'); 68 69 //theirVideo.src = window.URL.createObjectURL(e.stream); 70 theirVideo.srcObject = e.stream; 71 } 72 } 73 74 75 createSocket(); 76 startPeerConnection(); 77 78 if (hasUserMedia()) { 79 navigator.getUserMedia({ video: true, audio: false }, 80 stream => { 81 yourVideo.srcObject = stream; 82 window.stream = stream; 83 Connection.addStream(stream) 84 }, 85 err => { 86 console.log(err); 87 }) 88 } 89 90 91 function createOffer(){ 92 //发送offer和answer的函数,发送本地session描述 93 Connection.createOffer().then(offer => { 94 Connection.setLocalDescription(offer); 95 websocket.send(JSON.stringify({ 96 "userid":userid, 97 "event": "offer", 98 "data": { 99 "sdp": offer 100 } 101 })); 102 }); 103 } 104 105 106 107 function createSocket(){ 108 //websocket = null; 109 websocket = new WebSocket('wss://www.ecoblog.online/wss'); 110 eventBind(); 111 }; 112 function eventBind() { 113 //连接成功 114 websocket.onopen = function(e) { 115 console.log('连接成功') 116 }; 117 //server端请求关闭 118 websocket.onclose = function(e) { 119 console.log('close') 120 }; 121 //error 122 websocket.onerror = function(e) { 123 124 }; 125 //收到消息 126 websocket.onmessage = (event)=> { 127 if(event.data == "new user") { 128 location.reload(); 129 } else { 130 var json = JSON.parse(event.data); 131 console.log('onmessage: ', json); 132 if(json.userid !=userid){ 133 //如果是一个ICE的候选,则将其加入到PeerConnection中,否则设定对方的session描述为传递过来的描述 134 if(json.event === "_ice_candidate"&&json.data.candidate) { 135 Connection.addIceCandidate(new RTCIceCandidate(json.data.candidate)); 136 }else if(json.event ==='offer'){ 137 Connection.setRemoteDescription(json.data.sdp); 138 Connection.createAnswer().then(answer => { 139 Connection.setLocalDescription(answer); 140 console.log(window.stream) 141 websocket.send(JSON.stringify({ 142 "userid":userid, 143 "event": "answer", 144 "data": { 145 "sdp": answer 146 } 147 })); 148 }) 149 }else if(json.event ==='answer'){ 150 Connection.setRemoteDescription(json.data.sdp); 151 console.log(window.stream) 152 153 } 154 } 155 } 156 }; 157 }
建立连接的过程:
1⃣️两个浏览器都打开该页面,连接到同一个socket('wss://www.ecoblog.online/wss');
注意:webrtc只能在localhost或者https下使用,所以线上环境的话,我们的socket服务以及html页面都必须是要有https证书的;
对于wss,利用代理,在nginx的站点配置下如下配置/wss:
正如你所看到的那样,socket服务开在12345端口,所以还要去阿里云网站开一个这个端口的出入站规则;
另外centos的防火墙对该端口开放,或者直接关闭防火墙(自行百度)
socket服务写得比较简陋,但已够用,功能就是把收到的信息发给当前连接的所有c端
2⃣️两个c端已经和socket建立连接,然后任意其中一端点击“建立连接”
此时点击建立连接的端就是offer(携带信号源信息),发给另外一个端,另外一个端收到offer之后,发出响应answer(携带信号源信息),offer端收到answer端信息进行存储;
这样每个端都有了自己的信息和对方的信息,
3⃣️candidata信息的发送
其实这块,网上有的说法是offer发出answer发出后设置了localDescription和remoteDescription后就会触发onicecandidate,但是我测试的时候貌似没有,所以
我这里是在获取摄像头信息后通过
Connection.addStream(stream)
来触发Connection.onicecandidate,在这个事件监听的回调里,发出自身端的candidata给对方,如此一来,双方都有了对方的localDescription、remoteDescription和candidata;
三者齐全之后,就会触发Connection.onaddstream,这样,直接通过:
theirVideo.srcObject = e.stream;
把流写到video里面去,这样就能展示对方的视频信息了:
但是这样,只能在局域网内使用,如果要在公网使用的话,还要一个穿透服务器,网上找的一些免费的好像都不能用了?还是说我写得有问题?
具体的可百度,webrtc搭建stun服务器