webrtc 实时视频 .net websocket信令服务器
这篇文章主要参考了 Webrtc WebSocket实现音视频通讯,非常感谢提供代码
前端部分完全是从这篇文章复制过来的,只是修改了webscket的url,还有加入了webrtc-adapterjs ,至于做什么,可以点击链接进行了解
前端代码部分(主要来自开头提及的博文)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> Welcome<br/><input id="text" type="text" /> <button onclick="send()">发送消息</button> <hr/> <button onclick="closeWebSocket()">关闭WebSocket连接</button> <hr/> <div id="message"></div> <video id="vid1" width="320" height="240" autoplay></video> <video id="vid2" width="320" height="240" autoplay></video><br> <a id="create" href="webrtc.html#true" onclick="init()">点击此链接新建聊天室</a><br> <p id="tips" style="background-color:red">请在其他浏览器中打开:http://此电脑 加入此视频聊天</p> <script type="text/javascript" src="http://cdn.staticfile.org/webrtc-adapter/zv4.1.1/adapter.min.js"></script> <script type="text/javascript"> navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; var isCaller = window.location.href.split('#')[1]; var websocket = null; //判断当前浏览器是否支持WebSocket if('WebSocket' in window) { websocket = new WebSocket("ws://192.168.31.175:8181"); } else { alert('当前浏览器 Not support websocket') } //连接发生错误的回调方法 websocket.onerror = function() { setMessageInnerHTML("WebSocket连接发生错误"); }; //连接成功建立的回调方法 websocket.onopen = function() { setMessageInnerHTML("WebSocket连接成功"); } // 创建PeerConnection实例 (参数为null则没有iceserver,即使没有stunserver和turnserver,仍可在局域网下通讯) var pc = new window.RTCPeerConnection(null); // 发送ICE候选到其他客户端 pc.onicecandidate = function(event) { setMessageInnerHTML("我看看 1"); if(event.candidate !== null) { setMessageInnerHTML("我看看 2"); websocket.send(JSON.stringify({ "event": "_ice_candidate", "data": { "candidate": event.candidate } })); } }; //接收到消息的回调方法 websocket.onmessage = function(event) { setMessageInnerHTML("接收到的信息"); setMessageInnerHTML(event.data); if(event.data == "new user") { console.log("new user"); location.reload(); } else { var json = JSON.parse(event.data); console.log('onmessage: ', json); //如果是一个ICE的候选,则将其加入到PeerConnection中,否则设定对方的session描述为传递过来的描述 if(json.event === "_ice_candidate") { pc.addIceCandidate(new RTCIceCandidate(json.data.candidate)); } else { pc.setRemoteDescription(new RTCSessionDescription(json.data.sdp)); // 如果是一个offer,那么需要回复一个answer if(json.event === "_offer") { pc.createAnswer(sendAnswerFn, function(error) { console.log('Failure callback: ' + error); }); } } } } //连接关闭的回调方法 websocket.onclose = function() { setMessageInnerHTML("WebSocket连接关闭"); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function() { closeWebSocket(); } //将消息显示在网页上 function setMessageInnerHTML(innerHTML) { document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //关闭WebSocket连接 function closeWebSocket() { websocket.close(); } //发送消息 function send() { var message = document.getElementById('text').value; websocket.send(message); } // stun和turn服务器 var iceServer = { "iceServers": [{ "url": "stun:stunserver.org" }, { "url": "turn:numb.viagenie.ca", "username": "webrtc@live.com", "credential": "muazkh" }] }; // 如果检测到媒体流连接到本地,将其绑定到一个video标签上输出 pc.onaddstream = function(event) { document.getElementById('vid2').src = URL.createObjectURL(event.stream); }; // 发送offer和answer的函数,发送本地session描述 var sendOfferFn = function(desc) { pc.setLocalDescription(desc); websocket.send(JSON.stringify({ "event": "_offer", "data": { "sdp": desc } })); }, sendAnswerFn = function(desc) { pc.setLocalDescription(desc); websocket.send(JSON.stringify({ "event": "_answer", "data": { "sdp": desc } })); }; // 获取本地音频和视频流 navigator.getUserMedia({ "audio": true, "video": true }, function(stream) { //绑定本地媒体流到video标签用于输出 document.getElementById('vid1').src = URL.createObjectURL(stream); document.getElementById('vid1').muted = true; //向PeerConnection中加入需要发送的流 pc.addStream(stream); //如果是发起方则发送一个offer信令 if(isCaller) { pc.createOffer(sendOfferFn, function(error) { console.log('Failure callback: ' + error); }); } }, function(error) { //处理媒体流创建失败错误 console.log('getUserMedia error: ' + error); }); window.onload = function() { if(isCaller == null || isCaller == undefined) { var tips = document.getElementById("tips"); tips.remove(); } else { var create = document.getElementById("create"); create.remove(); } }; function init() { location.reload(); } </script> </body> </html>
提及博文的java部分,我用了.net 控制台程序简单实现了一个,相当于一个极简的信令服务器吧(Fleck 可以在vs的管理nuget程序包中获得)
static void Main(string[] args) { //客户端url以及其对应的Socket对象字典 IDictionary<string, IWebSocketConnection> dic_Sockets = new Dictionary<string, IWebSocketConnection>(); var server = new WebSocketServer("ws://192.168.31.175:8181"); //出错后重启 server.RestartAfterListenError = true; server.Start(socket => { socket.OnOpen = () => { //获取客户端网页的url string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort; dic_Sockets.Add(clientUrl, socket); Console.WriteLine(DateTime.Now.ToString() + "|服务器:和客户端网页:" + clientUrl + " 建立WebSock连接!"); }; socket.OnClose = () => //连接关闭事件 { string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort; //如果存在这个客户端,那么对这个socket进行移除 if (dic_Sockets.ContainsKey(clientUrl)) { //注:Fleck中有释放 //关闭对象连接 //if (dic_Sockets[clientUrl] != null) //{ //dic_Sockets[clientUrl].Close(); //} dic_Sockets.Remove(clientUrl); } Console.WriteLine(DateTime.Now.ToString() + "|服务器:和客户端网页:" + clientUrl + " 断开WebSock连接!"); }; socket.OnMessage = message => //接受客户端网页消息事件 { string clientUrl = socket.ConnectionInfo.ClientIpAddress + ":" + socket.ConnectionInfo.ClientPort; foreach (var item in dic_Sockets.Where(a=>a.Key!=clientUrl)) { item.Value.Send(message); } //Console.WriteLine(DateTime.Now.ToString() + "|服务器:【收到】来客户端网页:" + clientUrl + "的信息:\n" + message); }; socket.OnBinary = b => { }; }); Console.ReadKey(); }
记录完毕,这个东西仅仅只是个demo,可以拿来玩一下