一小时教你用SpringBoot+WebSocket+WebRTC实现视频通话

news/2024/11/2 17:24:35/

一小时教你用SpringBoot+WebSocket+WebRTC实现视频通话

        • 1. 运行结果
        • 2. 实现
          • 2.1 后端实现
          • 2.2 前端页面实现
        • 3. 总结

1. 运行结果

SpringBoot+WebSocket+WebRTC实现视频通话

上述运行结果中是有声音(比较小而已)及动的画面的(画面不是静止的)。

网上关于webrtc的文档(文章)和视频也挺多的,但是用SpringBoot结合WebRTC的却屈指可数,前一段时间小编我学习了一下WebRTC的相关知识,于是用SpringBoot+WebRTC实现了一个多人的线上自习室(有画面,但是没有声音的那种,开启声音也挺简单,在js代码里设置一下即可[运行结果在最后的总结里])。最近CSDN有活动,正好把前一段时间学习的知识运用起来(下述代码只是实现了,但是其中的逻辑是存在一定问题的,所以如果读者用下述代码,切记需要改动改动哈!)。既然是WebRTC,为什么又和WebSocket扯上关系了呢?因为利用WebSocket技术来发送消息具有实时性,你看我在这端发送一个消息出去,只要另一端处于连接状态,那么就可以接收到这个消息。而如果使用的是http、https等的话,这一端你发送一个消息,另外一段需要刷新一下页面才能看到消息(当然可以搞个定时器)。结合WebSocket技术,能很快速地实现一个视频通话功能。

2. 实现

导入相关jar包的依赖,如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.10</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

上述jar包可能有一些不需要的喔!

2.1 后端实现

websocket 配置类
GetHttpSessionConfig.class

package com.example.demo.websocket2;import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {HttpSession httpSession = (HttpSession) request.getHttpSession();// 获取httpsession对象sec.getUserProperties().put(HttpSession.class.getName(), httpSession);}
}

ServerEndpointExporter Bean的定义 Config.class

package com.example.demo.websocket2;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class Config {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

*websocket服务器类 WebSocketServer *

package com.example.demo.websocket2;import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;@Component
@ServerEndpoint(value = "/video",configurator = GetHttpSessionConfig.class)
public class WebSocketServer {//存储客户端的连接对象,每个客户端连接都会产生一个连接对象private static ConcurrentHashMap<String,WebSocketServer> map = new ConcurrentHashMap();//每个连接都会有自己的会话private Session session;private String account;@OnOpenpublic void open(Session session,EndpointConfig config){HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());String account = String.valueOf(httpSession.getAttribute("account"));map.put(account,this);this.session = session;this.account = account;}@OnClosepublic void close(){map.remove(account);}@OnErrorpublic void error(Throwable error){error.printStackTrace();}@OnMessagepublic void getMessage(String message) throws IOException {Set<Map.Entry<String, WebSocketServer>> entries = map.entrySet();for (Map.Entry<String, WebSocketServer> entry : entries) {if(!entry.getKey().equals(account)){//将消息转发到其他非自身客户端entry.getValue().send(message);}}}public void send(String message) throws IOException {if(session.isOpen()){session.getBasicRemote().sendText(message);}}public int  getConnetNum(){return map.size();}
}
2.2 前端页面实现

登录界面的代码就不在这儿粘贴了,下面主要展示视频通话界面的代码(包括css样式和js代码都在的)

<html><head><title>main</title><link rel = "stylesheet" href = "https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap-theme.min.css"/></head><style>body {background: #eee;padding: 5% 0;}video {background: black;border: 1px solid gray;}.call-page {position: relative;display: block;margin: 0 auto;width: 500px;height: 500px;}#localVideo {width: 150px;height: 150px;position: absolute;top: 15px;right: 15px;}#remoteVideo {width: 500px;height: 500px;}</style><body><div id = "callPage" class = "call-page"><video id = "localVideo" autoplay></video><video id = "remoteVideo" autoplay></video><div class = "row text-center"><div class = "col-md-12"><input id = "callToUsernameInput" type = "text"placeholder = "username to call" /><button id = "callBtn" class = "btn-success btn">Call</button><button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button></div></div></div><script type="text/javascript">//our usernamevar connectedUser;//connecting to our signaling servervar conn = new WebSocket("ws://localhost:9999/video");conn.onopen = function () {console.log("Connected to the signaling server");};//when we got a message from a signaling serverconn.onmessage = function (msg) {console.log("Got message", msg.data);var data = JSON.parse(msg.data);switch(data.type) {case "login":handleLogin(data.success);break;//when somebody wants to call uscase "offer":handleOffer(data.offer, data.name);break;case "answer":handleAnswer(data.answer);break;//when a remote peer sends an ice candidate to uscase "candidate":handleCandidate(data.candidate);break;case "leave":handleLeave();break;default:break;}};conn.onerror = function (err) {console.log("Got error", err);};//alias for sending JSON encoded messagesfunction send(message) {//attach the other peer username to our messagesif (connectedUser) {message.name = connectedUser;}conn.send(JSON.stringify(message));}//******//UI selectors block//******var callPage = document.querySelector("#callPage");var callToUsernameInput = document.querySelector("#callToUsernameInput");var callBtn = document.querySelector("#callBtn");var hangUpBtn = document.querySelector("#hangUpBtn");var localVideo = document.querySelector("#localVideo");var remoteVideo = document.querySelector("#remoteVideo");var yourConn;var stream;// callPage.style.display = "none";var PeerConnection = (window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCPeerConnection || undefined);var RTCSessionDescription = (window.webkitRTCSessionDescription || window.mozRTCSessionDescription || window.RTCSessionDescription || undefined);navigator.getUserMedia = (navigator.getUserMedia ||navigator.webkitGetUserMedia ||navigator.mozGetUserMedia ||navigator.msGetUserMedia);//**********************//Starting a peer connection//**********************//getting local video streamnavigator.getUserMedia({ video: true, audio: true }, function (myStream) {stream = myStream;//displaying local video stream on the pagelocalVideo.srcObject = stream;//using Google public stun servervar configuration = {"iceServers": []};yourConn = new PeerConnection(configuration);// setup stream listeningyourConn.addStream(stream);//when a remote user adds stream to the peer connection, we display ityourConn.onaddstream = function (e) {remoteVideo.srcObject = e.stream;};// Setup ice handlingyourConn.onicecandidate = function (event) {if (event.candidate) {send({type: "candidate",candidate: event.candidate});}};}, function (error) {console.log(error);});//initiating a callcallBtn.addEventListener("click", function () {var callToUsername = callToUsernameInput.value;if (callToUsername.length > 0) {connectedUser = callToUsername;// create an offeryourConn.createOffer(function (offer) {send({type: "offer",offer: offer});yourConn.setLocalDescription(offer);}, function (error) {alert("Error when creating an offer");});}});//when somebody sends us an offerfunction handleOffer(offer, name) {connectedUser = name;yourConn.setRemoteDescription(new RTCSessionDescription(offer));//create an answer to an offeryourConn.createAnswer(function (answer) {yourConn.setLocalDescription(answer);send({type: "answer",answer: answer});}, function (error) {alert("Error when creating an answer");});}//when we got an answer from a remote userfunction handleAnswer(answer) {yourConn.setRemoteDescription(new RTCSessionDescription(answer));}//when we got an ice candidate from a remote userfunction handleCandidate(candidate) {yourConn.addIceCandidate(new RTCIceCandidate(candidate));}//hang uphangUpBtn.addEventListener("click", function () {send({type: "leave"});handleLeave();});function handleLeave() {connectedUser = null;remoteVideo.src = null;yourConn.close();yourConn.onicecandidate = null;yourConn.onaddstream = null;}
</script></body></html>

3. 总结

上述前端代码参考来自这里:webrtc视频演示,上述代码中如果有不懂的读者可以去仔细看看这个链接里的知识,里面关于webrtc有详细的介绍及实现,不过,没有讲多人的,它只讲了一对一的,不过,前一段时间小编在参考一些大佬的实现思路及自己思考下,也实现了一个多人的,运行结果如下:

基于SpringBoot,WebSocket,WebRTC实现多人自习室功能


http://www.ppmy.cn/news/529782.html

相关文章

【安全点】

在OopMap的协助下&#xff0c;HotSpot可以快速准确地完成GC Roots枚举&#xff0c;但一个很现实的问题随之而来&#xff1a;可能导致引用关 系变化&#xff0c;或者说导致OopMap内容变化的指令非常多&#xff0c;如果为每一条指令都生成对应的OopMap&#xff0c;那将会需要大量…

腾讯安全管家

QQ电脑管家 http://dlied6.qq.com/invc/xfspeed/qqpcmgr/versetup/portal/portal/PCMgr_Setup_13_1_19961_225.exe http://dlied6.qq.com/invc/xfspeed/qqpcmgr/versetup/portal/portal/PCMgr_Setup_13_0_19803_222.exe http://dlied6.qq.com/invc/xfspeed/qqpcmgr/versetup…

网站被百度网址安全中心 警告 该怎么取消拦截提示

今天早晨一上班&#xff0c;习惯性的打开我们公司的网站&#xff0c;发现公司网站竟然跳转到了赌博、彩票网站上去了&#xff0c;我还奇了怪了&#xff0c;于是去百度搜索我们公司网站&#xff0c;发现网站在百度搜索出现&#xff1a;“百度网址安全中心提醒您&#xff1a;该站…

qq登录测试点

一、基本功能测试&#xff1a; 输入正确的用户名和密码登录成功输入错误的用户名密码登录失败用户名正确&#xff0c;密码错误&#xff0c;是否提示输入密码错误&#xff1f;用户名错误&#xff0c;密码正常&#xff0c;是否提示输入用户名错误&#xff1f;用户名和密码都错误…

危险的QQ

最近在使用QQ时&#xff0c;发现他增加了一个很好的功能&#xff0c;能找回可能和你有关系的朋友&#xff0c;同学。这些人可能你已经丢失了联系方式。 但是&#xff01;&#xff01;&#xff01; 我发现他找出来的人&#xff0c;大部分都是实名&#xff01;我想绝大多数人上…

安全管理中心

系统管理 应对系统管理员进行身份鉴别&#xff0c;只允许其通过特定的命令或操作界面进行系统管理操作&#xff0c;并对这些操作进行审计 应通过系统管理员对系统的资源和运行进行配置&#xff0c;控制和管理&#xff0c;包括用户身份&#xff0c;系统资源配置&#xff0c;系统…

android手机qq账号管理在哪里,qq安全中心手机版之功能详解

qq安全中心是由腾讯推出的易于保护账号安全性的产品&#xff0c;同时现在推出了手机版本&#xff0c;支持安卓系统和IOS系统。下面学习啦小编以安卓系统为例&#xff0c;具体的说下安全中心的功能。 qq安全中心手机版之功能详解 打开手机桌面的”QQ安全中心“应用&#xff0c;进…

QQ 安全相关问题

文章目录 1.QQ 开通了设备锁&#xff0c;被盗后坏人能否登录&#xff1f;2.目前QQ和QQ群的用户量有多少&#xff1f; 1.QQ 开通了设备锁&#xff0c;被盗后坏人能否登录&#xff1f; QQ 开通设备锁后&#xff0c; 在不常用设备&#xff08;如电脑&#xff0c;手机&#xff0c;…