WebRTC实践视频聊天室

tech2024-02-23  59

技术前言

通过前几次教程已经可以轻松的实现两个浏览器之间的文本信令交互和视频对讲功能。本次课程要融合以前所有技术点,做一次综合性实例,采用nodejs实现一个完整的聊天室功能。其界面样式仿照微信的界面搭建。

接口方法

在此我根据实例代码用到的接口做如下讲解,为了方便本实例我们采用nodejs开发,其中用到了socket.io技术。如果对socket.io(https://socket.io/)不明白的话可以先学习一下socket.io再回来看本教程。 1. io.sockets.on(‘connection’, function (socket) {}); 消息连接方法,其中回调函数的socket参数为一个client与服务器的连接标识,不同的client会有不同的连接标识。 2. socket.emit(‘message’, “this is a test”); 向所有已连接服务器的终端也包括自己在内广播消息"this is a test"。 3. socket.broadcast.emit(‘message’, “this is a test”); 向除了自己所有已连接服务器的socket终端广播消息"this is a test" 4. socket.broadcast.to(‘game’).emit(‘message’, ‘nice game’); 向game房间中除了自己的所有终端广播消息"this is a test" 5. io.sockets.in(‘game’).emit(‘message’, ‘cool game’); 向game房间中包括自己在内的所有终端广播消息"this is a test" sending to individual socketid 6. io.sockets.socket(socketid).emit(‘message’, ‘for your eyes only’); 仅给连接编号为socketid的终端发送消息 7. io.sockets.on(‘xx’,function); 以‘xx‘为协义订阅服务端的消息通知事件。

实践代码

前端界面 index.jade

extends layout title=abc block header link(rel='stylesheet', href='/stylesheets/reset.min.css') script(src = "/javascripts/trace.js") script(src = "/javascripts/config.js") script(src = "/javascripts/socket.io.js") script(src="/javascripts/jquery-1.11.1.min.js") script(src="/javascripts/Tdrag.js") script(src = "/javascripts/jquerysocketio.js") script(src="../javascripts/index/index.js") block content h1 SocketIo Server Web1 p Welcome to SocketIo Server Web

index.html

<style type="text/css"> #localVideo { background-color: #999999; border: 1px #999999 solid; width: 100%; height: 100%; } #remoteVideo { position: relative; float: left; top: -395px; left: 5px; background-color: black; width: 150px; height: 150px; text-align: center; z-index: 1; } .vbtn{ position: relative; top: -90px; } .vbtn img{ width: 64px; height: 64px; } .video { position: absolute; display: none; top:150px; left: 250px; border: 1px black solid; width: 450px; height: 400px; } .talk a:link {color: white;} .span { position: relative; padding: 5px; } .tip { display: block; background: #f00; border-radius: 50%; width: 8px; height: 8px; top: 0px; right: 0px; position: absolute; } </style> <div class="wrapper"> <div class="container"> <div class="left"> <div class="top"> <img src="../images/chat/thomas.jpg" id="headerImg" class="search" alt="" /> <span class="name" id="headerName"></span> </div> <ul class="people"> <li class="person" data-chat="person1"> <img src="../images/chat/thomas.jpg" alt="" /> <span class="name">张三三</span> <span class="preview">I was wondering...</span> </li> <li class="person" data-chat="person2"> <img src="../images/chat/dog.png" alt="" /> <span class="name">李四四</span> <span class="preview">I've forgotten how it felt before</span> </li> <li class="person" data-chat="person3"> <img src="../images/chat/louis-ck.jpeg" alt="" /> <span class="name">王五五</span> <span class="preview">But we’re probably gonna need a new carpet.</span> </li> <li class="person" data-chat="person4"> <img src="../images/chat/bo-jackson.jpg" alt="" /> <span class="name">赵七七</span> <span class="preview">It’s not that bad...</span> </li> <li class="person" data-chat="person5"> <img src="../images/chat/michael-jordan.jpg" alt="" /> <span class="name">周八皮</span> <span class="preview">Wasup for the third time like is you blind bitch</span> </li> <li class="person" data-chat="person6"> <img src="../images/chat/drake.jpg" alt="" /> <span class="name">李寻欢</span> <span class="preview">howdoyoudoaspace</span> </li> </ul> </div> <div class="right"> <div class="top"><span>To: <span class="name" id="friendName">暂无好友</span></span> </div> <div class="chat" data-chat="person1"></div> <div class="chat" data-chat="person2"></div> <div class="chat" data-chat="person3"></div> <div class="chat" data-chat="person4"></div> <div class="chat" data-chat="person5"></div> <div class="chat" data-chat="person6"></div> <div class="write"> <a href="javascript:;" class="write-link attach"></a> <input type="text" id="txtContent" /> <a href="javascript:;" class="write-link smiley" id="videoBtn"></a> <a href="javascript:;" class="write-link send" id="sendBtn"></a> </div> </div> </div> </div> <div id="video" class="video"> <video id="localVideo" autoplay playsinline></video> <video id="remoteVideo" autoplay playsinline></video> <div class="vbtn"> <img src="../images/call_0.png" id="CloseBtn" /> </div> </div>

服务端

var app = express(); app.listen(6000); var server = require('http').Server(app); var io = require('socket.io')(server); server.listen(6100); var persons = []; var getPersonSeq = function() { var tempSeq = Math.floor(Math.random() * 5) + 1; persons.forEach(p => { if (p.seq == tempSeq) { tempSeq = getPersonSeq(); } }); return tempSeq; } io.sockets.on('connection', function(socket) { if (persons.indexOf(socket.id) == -1) { persons.push({ id: socket.id, seq: getPersonSeq() }); } function trace() { console.log(arguments); } console.log("用户 '" + socket.id + "' 连接成功!"); socket.emit('ready', socket.id, persons); socket.broadcast.emit('change', persons); socket.on('disconnect', function() { trace('终端(' + socket.id + ')已断开。 '); var tempPersons = []; persons.forEach(e => { if (e.id != socket.id) { tempPersons.push(e); } }); persons = tempPersons; //rooms.del(socket.id); socket.broadcast.emit('change', persons); }); socket.on('message', function(body) { var d = new Date(); body.from = socket.id; body.time = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds(); socket.to(body.to).emit('message', body); trace('终端(' + socket.id + "):message>", body); }); socket.on('meeting', function(body) { ///向除自己外所有meeting终端广播消息 socket.broadcast.emit('meeting', body); //sockets.in(room).emit('meeting', body); trace('终端(' + socket.id + "):meeting>", body); }); socket.on('create', function(room, callbackfunc) { var clientsInRoom = io.sockets.adapter.rooms[room]; var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; var temproom = { id: room, count: numClients }; socket.join(room); //rooms.save(temproom); if (typeof(callbackfunc) == 'function') { callbackfunc(temproom); } }); socket.on('join', function(room, callbackfunc) { var clientsInRoom = io.sockets.adapter.rooms[room]; var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; var robj = { id: room, count: numClients } switch (numClients) { case 0: trace('终端(' + socket.id + ')进入的房间 ”' + room + '" 不存在!'); socket.emit('empty', room); break; case 1: ///向房间room中发送消息 io.sockets.in(room).emit('join', room); socket.join(room); trace('房间 ' + room + '中现在有' + numClients + '个终端!'); break; case 2: socket.emit('full', room); trace('房间 ”' + room + '" 已满。'); break; } if (typeof(callbackfunc) == 'function') { callbackfunc(robj); } }); socket.on('ipaddr', function() { var ifaces = os.networkInterfaces(); for (var dev in ifaces) { ifaces[dev].forEach(function(details) { if (details.family === 'IPv4' && details.address !== '127.0.0.1') { socket.emit('ipaddr', details.address); } }); } }); });

部署发布

本实例代码用nodejs直接发布到服务上,经测试socket.io会发生跨域访问,我们可以通过Nginx实现一个反向代理,由于webrtc视频通讯需要采用https协议访问才能调用视频流,Nginx需要设置ssl配置。 Nginx Https Server 配置如下:

# HTTPS server server { listen 443 ssl; server_name localhost; ssl_certificate ca/cert/cre.pem; ssl_certificate_key ca/cert/cre.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Nginx-Proxy true; proxy_set_header Connection ""; proxy_pass http://127.0.0.1:3000; proxy_redirect default; } }

运行结果

不同浏览器之间普通文本内容通讯,左侧选择好友,右侧发送消息。 不同浏览器之间视频实时通讯,左侧选择好友,右侧发送视频邀请。在测试的时候我是用的同一台电脑,所以本地和远程都是相同的图像。

实践历程

1. WebRTC实践简介 2. WebRTC实践获取视频流 3. WebRTC实践传输视频流 4. WebRTC实践信令服务 5. WebRTC实践点对点通信 6. WebRTC实践视频聊天室 7.WebRTC实践总结

最新回复(0)