基于surging 如何利用peerjs进行语音视频通话

news/2024/11/17 18:26:27/文章来源:https://www.cnblogs.com/westworldss/p/18388704

一 、 概述

PeerJS 是一个基于浏览器WebRTC功能实现的js功能包,简化了WebrRTC的开发过程,对底层的细节做了封装,直接调用API即可,再配合surging 协议组件化从而做到稳定,高效可扩展的微服务,再利用RtmpToWebrtc 引擎组件可以做到不仅可以利用httpflv 观看rtmp推流直播,还可以采用基于 Webrtc的peerjs 进行观看,那么今天要讲的是如何结合开发语音视频通话功能。放到手机和电脑上都可以实现语音视频通话。

一键运行打包成品下载:https://pan.baidu.com/s/1MVsjKtVYpUonauAz9ZXtPg?pwd=1q2g

测试用户:fanly

测试密码:123456

为了让大家节约时间,能尽快运行产品看到效果,上面有 一键运行打包成品可以进行下载测试运行。

二、如何测试运行

以下是目录结构,

IDE:consul 注册中心

kayak.client: 网关

kayak.server:微服务

apache-skywalking-apm:skywalking链路跟踪

以上是目录结构, 不需要进入管理界面配置网络组件,启动后自带端口96的ws协议主机,只要打开video文件夹,里面有两个语音通话的html测试文件,在同一一个局域网只要输入对方的name就可以进行语音通话

打开界面如图

三、基于surging如何开发

以上是没有开发环境的进行下载进行下载测试,那么正在使用surging 的如何开发此功能呢?

1. 创建服务接口,继承于IServiceKey

 [ServiceBundle("Device/{Service}")]public  interface IChatService : IServiceKey{}

2. 创建中间服务,继承于WSBehavior, IChatService

  internal class ChatService : WSBehavior, IChatService{private static readonly ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();private static readonly ConcurrentDictionary<string, string> _clients = new ConcurrentDictionary<string, string>();protected override void OnOpen(){var _name = Context.QueryString["name"]; if (!string.IsNullOrEmpty(_name)){_clients[ID] = _name;_users[_name] = ID;}}protected override void OnError( WebSocketCore.ErrorEventArgs e){var msg = e.Message;}protected override void OnMessage(MessageEventArgs e){if (_clients.ContainsKey(ID)){var message = JsonConvert.DeserializeObjectstring, object>>(e.Data);//消息类型message.TryGetValue("type",out object @type);message.TryGetValue("toUser", out object toUser);message.TryGetValue("fromUser", out object fromUser);message.TryGetValue("msg", out object msg);message.TryGetValue("sdp", out object sdp);message.TryGetValue("iceCandidate", out object iceCandidate);Dictionary result = new Dictionary();result.Add("type", @type);//呼叫的用户不在线if (!_users.ContainsKey(toUser?.ToString())){result["type"]= "call_back";result.Add("fromUser", "系统消息");result.Add("msg", "Sorry,呼叫的用户不在线!");this.Client().SendTo(JsonConvert.SerializeObject(result), ID);return;}//对方挂断if ("hangup".Equals(@type)){result.Add("fromUser", fromUser);result.Add("msg", "对方挂断!");}//视频通话请求if ("call_start".Equals(@type)){result.Add("fromUser", fromUser);result.Add("msg", "1");}//视频通话请求回应if ("call_back".Equals(type)){result.Add("fromUser", toUser);result.Add("msg", msg);}//offerif ("offer".Equals(type)){result.Add("fromUser", toUser); result.Add("sdp", sdp);}//answerif ("answer".Equals(type)){result.Add("fromUser", toUser);result.Add("sdp", sdp);}//iceif ("_ice".Equals(type)){result.Add("fromUser", toUser);result.Add("iceCandidate", iceCandidate);}this.Client().SendTo(JsonConvert.SerializeObject(result), _users.GetValueOrDefault(toUser?.ToString()));}}protected override void OnClose(CloseEventArgs e){if( _clients.TryRemove(ID, out string name))_users.TryRemove (name, out string value);}}

3.设置surgingSettings的WSPort端口配置,默认96

以上就是利用websocket协议中转消息,下面是页面如何编号,代码如下:

DOCTYPE><html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>WebRTC + WebSockettitle><meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"><style>html,body{margin: 0;padding: 0;}#main{position: absolute;width: 370px;height: 550px;}#localVideo{position: absolute;background: #757474;top: 10px;right: 10px;width: 100px;height: 150px;z-index: 2;}#remoteVideo{position: absolute;top: 0px;left: 0px;width: 100%;height: 100%;background: #222;}#buttons{z-index: 3;bottom: 20px;left: 90px;position: absolute;}#toUser{border: 1px solid #ccc;padding: 7px 0px;border-radius: 5px;padding-left: 5px;margin-bottom: 5px;}#toUser:focus{border-color: #66afe9;outline: 0;-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}#call{width: 70px;height: 35px;background-color: #00BB00;border: none;margin-right: 25px;color: white;border-radius: 5px;}#hangup{width:70px;height:35px;background-color:#FF5151;border:none;color:white;border-radius: 5px;}style>
head>
<body><div id="main"><video id="remoteVideo" playsinline autoplay>video><video id="localVideo" playsinline autoplay muted>video><div id="buttons"><input id="toUser" placeholder="输入在线好友账号"/><br/><button id="call">视频通话button><button id="hangup">挂断button>div>div>
body><script type="text/javascript" th:inline="javascript">let username = "fanly";let localVideo = document.getElementById('localVideo');let remoteVideo = document.getElementById('remoteVideo');let websocket = null;let peer = null;WebSocketInit();ButtonFunInit();/* WebSocket */function WebSocketInit(){//判断当前浏览器是否支持WebSocketif ('WebSocket' in window) {websocket = new WebSocket("ws://127.0.0.1:961/device/chat?name="+username);} else {alert("当前浏览器不支持WebSocket!");}//连接发生错误的回调方法websocket.onerror = function (e) {alert("WebSocket连接发生错误!");};//连接关闭的回调方法websocket.onclose = function () {console.error("WebSocket连接关闭");};//连接成功建立的回调方法websocket.onopen = function () {console.log("WebSocket连接成功");};//接收到消息的回调方法websocket.onmessage = async function (event) {let { type, fromUser, msg, sdp, iceCandidate } = JSON.parse(event.data.replace(/\n/g,"\\n").replace(/\r/g,"\\r"));console.log(type);if (type === 'hangup') {console.log(msg);document.getElementById('hangup').click();return;}if (type === 'call_start') {let msg = "0"if(confirm(fromUser + "发起视频通话,确定接听吗")==true){document.getElementById('toUser').value = fromUser;WebRTCInit();msg = "1"}websocket.send(JSON.stringify({type:"call_back",toUser:fromUser,fromUser:username,msg:msg}));return;}if (type === 'call_back') {if(msg === "1"){console.log(document.getElementById('toUser').value + "同意视频通话");//创建本地视频并发送offerlet stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })localVideo.srcObject = stream;stream.getTracks().forEach(track => {peer.addTrack(track, stream);});let offer = await peer.createOffer();await peer.setLocalDescription(offer); let newOffer = offer;newOffer["fromUser"] = username;newOffer["toUser"] = document.getElementById('toUser').value;websocket.send(JSON.stringify(newOffer));}else if(msg === "0"){alert(document.getElementById('toUser').value + "拒绝视频通话");document.getElementById('hangup').click();}else{alert(msg);document.getElementById('hangup').click();}return;}if (type === 'offer') {let stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });localVideo.srcObject = stream;stream.getTracks().forEach(track => {peer.addTrack(track, stream);});await peer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));let answer = await peer.createAnswer();let newAnswer = answer;newAnswer["fromUser"] = username;newAnswer["toUser"] = document.getElementById('toUser').value;websocket.send(JSON.stringify(newAnswer));await peer.setLocalDescription(answer);return;}if (type === 'answer') {peer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));return;}if (type === '_ice') {peer.addIceCandidate(iceCandidate);return;}}}/* WebRTC */function WebRTCInit(){peer = new RTCPeerConnection();//icepeer.onicecandidate = function (e) {if (e.candidate) {websocket.send(JSON.stringify({type: '_ice',toUser:document.getElementById('toUser').value,fromUser:username,iceCandidate: e.candidate}));}};//trackpeer.ontrack = function (e) {if (e && e.streams) {remoteVideo.srcObject = e.streams[0];}};}/* 按钮事件 */function ButtonFunInit(){//视频通话document.getElementById('call').onclick = function (e){document.getElementById('toUser').style.visibility = 'hidden';let toUser = document.getElementById('toUser').value;if(!toUser){alert("请先指定好友账号,再发起视频通话!");return;}if(peer == null){WebRTCInit();}websocket.send(JSON.stringify({type:"call_start",fromUser:username,toUser:toUser,}));}//挂断document.getElementById('hangup').onclick = function (e){document.getElementById('toUser').style.visibility = 'unset';if(localVideo.srcObject){const videoTracks = localVideo.srcObject.getVideoTracks();videoTracks.forEach(videoTrack => {videoTrack.stop();localVideo.srcObject.removeTrack(videoTrack);});}if(remoteVideo.srcObject){const videoTracks = remoteVideo.srcObject.getVideoTracks();videoTracks.forEach(videoTrack => {videoTrack.stop();remoteVideo.srcObject.removeTrack(videoTrack);});//挂断同时,通知对方websocket.send(JSON.stringify({type:"hangup",fromUser:username,toUser:document.getElementById('toUser').value,}));}if(peer){peer.ontrack = null;peer.onremovetrack = null;peer.onremovestream = null;peer.onicecandidate = null;peer.oniceconnectionstatechange = null;peer.onsignalingstatechange = null;peer.onicegatheringstatechange = null;peer.onnegotiationneeded = null;peer.close();peer = null;}localVideo.srcObject = null;remoteVideo.srcObject = null;}}
script>
html>

以上是页面的代码,如需要添加其它账号测试只要更改username ,或者ws地址也可以更改标记红色的区域。

三、总结

本人正在开发平台,如有疑问可以联系作者,QQ群:744677125

本博客参考蓝猫机场。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/789604.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gitee误删项目,重新上传

删除项目更目录.git 解除绑定

pinpoint-php-aop 内部原理

pinpoint-php-aop 是一个支持pinpoint-php agent 的库自动注入PHP内置函数,比如redis,pdo,mysqli 自动注入用户类,比如 guzzlehttp, predis怎样处理内置函数内置函数解释:PHP comes standard with many functions and constructs. There are also functions that require…

从代码到产品,我的IT职业成长之路

每个人的职业生涯都是一段充满转折和挑战的旅程,当然每一次职业转型都是一次重新定义自己的机会,从2015年开始,当时我刚踏入IT行业,成为一名Java开发者,后来随着时间的推移,我的职业方向逐渐转向了前端开发者,埋头于代码的世界。最终在2018年找到了属于自己的职业定位—…

1-0.AI工具

1-0.AI工具 一. 我知道或使用过的AI大模型平台 1. OpenAI 平台: OpenAI GPT 特点: 提供先进的自然语言处理能力,支持对话生成、文本总结、翻译等。包括GPT-3、GPT-4等版本。 2. Google AI 平台: Google Cloud AI 特点: 提供全面的AI和机器学习服务,包括AutoML、自然语言处理、…

折腾 Quickwit,Rust 编写的分布式搜索引擎 - 可观测性之分布式追踪

概述 分布式追踪是一种跟踪应用程序请求流经不同服务(如前端、后端、数据库等)的过程。它是一个强大的工具,可以帮助您了解应用程序的工作原理并调试性能问题。 Quickwit 是一个用于索引和搜索非结构化数据的云原生引擎,这使其非常适合用作追踪数据的后端。 此外,Quickwit…

POA:已开源,蚂蚁集团提出同时预训练多种尺寸网络的自监督范式 | ECCV 2024

论文提出一种新颖的POA自监督学习范式,通过弹性分支设计允许同时对多种尺寸的模型进行预训练。POA可以直接从预训练teacher生成不同尺寸的模型,并且这些模型可以直接用于下游任务而无需额外的预训练。这个优势显著提高了部署灵活性,并有助于预训练的模型在各种视觉任务中取得…

【信息收集】旁站和C段

一、 站长之家二、 google hacking2.1 网络空间搜索引擎2.2 在线c段 webscan.cc2.3 Nmap,Msscan扫描等2.4 常见端口表旁站往往存在业务功能站点,建议先收集已有IP的旁站,再探测C段,确认C段目标后,再在C段的基础上再收集一次旁站。 旁站是和已知目标站点在同一服务器但不同端…

茂名工厂智能视频监控系统

茂名工厂智能视频监控系统除开监控出入工作人员外,还必须监控车子,以追踪出入时长。除开组装超清精彩短视频监控监控摄像头外,茂名工厂智能视频监控系统还必须组装车辆识别系统和智能安全通道。办公室一般是一个主要的信息内容,在安装视频监控时,也需要考虑到防盗系统系统…

【信息收集】查找真实ip

一、 多地ping确认是否使用CDN二、查询历史DNS解析记录2.1 DNSDB2.2 微步在线2.3 Ipip.net2.4 viewdns三、phpinfo四、绕过CDN如果目标网站使用了CDN,使用了cdn真实的ip会被隐藏,如果要查找真实的服务器就必须获取真实的ip,根据这个ip继续查询旁站。 注意:很多时候,主站虽…

【信息收集】收集子域名

一、子域名作用二、常用方式三、域名的类型3.1 A (Address) 记录3.2 别名(CNAME)记录3.3 如何检测CNAME记录?3.4 MX(Mail Exchanger)记录3.5 什么是TXT记录?3.6 什么是NS记录?3.7 子域名在线查询13.8 子域名在线查询23.9 dns侦测3.10 IP138查询子域名3…

船新的神仙语录

重新开坑!开坑原因:最近看到的乐子有点多,现在没有只有周末能更新的问题了,所以重新开坑!欢迎投稿! begin:2024.8.30 2024秋QwQwQ

生物信息学家、计算机科学家和遗传学家,谁主导开发的生物信息学工具更好?

新西兰奥塔哥大学高级讲师(Senior Lecturer ) Paul P. Gardner 在 bioRxiv 上发表了一篇名为《A Bioinformatician, Computer Scientist, and Geneticist lead bioinformatic tool development — which one is better?》的预印版文章,旨在探讨生物信息学软件工具开发中,作…