vue项目基于WebRTC实现一对一音视频通话

效果

前端代码

<template><div class="flex items-center flex-col text-center p-12 h-screen"><div class="relative h-full mb-4 fBox"><video id="localVideo"></video><video id="remoteVideo"></video><div v-if="caller && calling"><p class="mb-4 text-white">等待对方接听...</p><img style="width: 60px;" @click="hangUp" src="@/assets/guaDuang.png" alt=""></div><div v-if="called && calling"><p>收到视频邀请...</p><div class="flex"><img style="width: 60px" @click="hangUp" src="@/assets/guaDuang.png" alt=""><img style="width: 60px" @click="acceptCall" src="@/assets/jieTing.png" alt=""></div></div></div><div><button @click="callRemote" style="margin-right: 10px">发起视频</button><button @click="hangUp" style="margin-left: 10px">挂断视频</button></div></div>
</template>
<script>
import { io, Socket } from "socket.io-client";
let roomId = '001';
export default {name: 'HelloWorld',props: {msg: String},data(){return{wsSocket:null,//实例called:false,// 是否是接收方caller:false,// 是否是发起方calling:false,// 呼叫中communicating:false,// 视频通话中localVideo:null,// video标签实例,播放本人的视频remoteVideo:null,// video标签实例,播放对方的视频peer:null,localStream:null,}},methods:{// 发起方发起视频请求async callRemote(){let that = this;console.log('发起视频');that.caller = true;that.calling = true;// await getLocalStream()// 向信令服务器发送发起请求的事件await that.getLocalStream();that.wsSocket.emit('callRemote', roomId)},// 接收方同意视频请求acceptCall(){console.log('同意视频邀请');this.wsSocket.emit('acceptCall', roomId)},// 挂断视频hangUp(){this.wsSocket.emit('hangUp', roomId)},reset(){this.called = false;this.caller = false;this.calling = false;this.communicating = false;this.peer = null;this.localVideo.srcObject = null;this.remoteVideo.srcObject = null;this.localStream = undefined;console.log('挂断结束视频-------')},// 获取本地音视频流async getLocalStream(){let that = this;let obj = { audio: true, video: true };const stream = await navigator.mediaDevices.getUserMedia(obj); // 获取音视频流that.localVideo.srcObject = stream;that.localVideo.play();that.localStream = stream;return stream;}},mounted() {let that = this;that.$nextTick(()=>{that.localVideo = document.getElementById('localVideo');that.remoteVideo = document.getElementById('remoteVideo');})let sock = io('localhost:3000'); // 对应服务的端口// 连接成功sock.on('connectionSuccess', (sock) => {console.log('连接成功:');});sock.emit('joinRoom', roomId) // 前端发送加入房间事件sock.on('callRemote', (sock) => {// 如果是发送方自己收到这个事件就不用管if (!that.caller){ // 不是发送方(用户A)that.called = true; // 接听方that.calling = true; // 视频通话中}});sock.on('acceptCall',async ()=>{if (that.caller){// 用户A收到用户B同意视频的请求that.peer = new RTCPeerConnection();// 添加本地音视频流that.peer.addStream && that.peer.addStream(that.localStream);// 通过监听onicecandidate事件获取candidate信息that.peer.onicecandidate = (event) => {if (event.candidate) {console.log('用户A获取candidate信息', event.candidate);// 通过信令服务器发送candidate信息给用户Bsock.emit('sendCandidate', {roomId, candidate: event.candidate})}}// 接下来用户A和用户B就可以进行P2P通信流// 监听onaddstream来获取对方的音视频流that.peer.onaddstream = (event) => {console.log('用户A收到用户B的stream',event.stream);that.calling = false;that.communicating = true;that.remoteVideo.srcObject = event.stream;that.remoteVideo.play();}// 生成offerlet offer = await that.peer.createOffer({offerToReceiveAudio: 1,offerToReceiveVideo: 1})console.log('offer', offer);// 设置本地描述的offerawait that.peer.setLocalDescription(offer);// 通过信令服务器将offer发送给用户Bsock.emit('sendOffer', { offer, roomId })}})// 收到offersock.on('sendOffer',async (offer) => {if (that.called){ // 接收方 - 用户Bconsole.log('收到offer',offer);// 创建自己的RTCPeerConnectionthat.peer = new RTCPeerConnection();// 添加本地音视频流const stream = await that.getLocalStream();that.peer.addStream && that.peer.addStream(stream);// 通过监听onicecandidate事件获取candidate信息that.peer.onicecandidate = (event) => {if (event.candidate) {console.log('用户B获取candidate信息', event.candidate);// 通过信令服务器发送candidate信息给用户Asock.emit('sendCandidate', {roomId, candidate: event.candidate})}}// 接下来用户A和用户B就可以进行P2P通信流// 监听onaddstream来获取对方的音视频流that.peer.onaddstream = (event) => {console.log('用户B收到用户A的stream',event.stream);that.calling = false;that.communicating = true;that.remoteVideo.srcObject = event.stream;that.remoteVideo.play();}// 设置远端描述信息await that.peer.setRemoteDescription(offer);let answer = await that.peer.createAnswer();console.log('用户B生成answer',answer);await that.peer.setLocalDescription(answer);// 发送answer给信令服务器sock.emit('sendAnswer', { answer, roomId })}})// 用户A收到answersock.on('sendAnswer',async (answer) => {if (that.caller){ // 接收方 - 用户A   判断是否是发送方// console.log('用户A收到answer',answer);await that.peer.setRemoteDescription(answer);}})// 收到candidate信息sock.on('sendCandidate',async (candidate) => {console.log('收到candidate信息',candidate);// await that.peer.addIceCandidate(candidate) // 用户A和用户B分别收到candidate后,都添加到自己的peer对象上// await that.peer.addCandidate(candidate)await that.peer.addIceCandidate(candidate)})// 挂断sock.on('hangUp',()=>{that.reset()})that.wsSocket = sock;}
}
</script>

服务端代码

const socket = require('socket.io');
const http = require('http');const server = http.createServer()const io = socket(server, {cors: {origin: '*' // 配置跨域}
});io.on('connection', sock => {console.log('连接成功...')// 向客户端发送连接成功的消息sock.emit('connectionSuccess');sock.on('joinRoom',(roomId)=>{sock.join(roomId);console.log('joinRoom-房间ID:'+roomId);})// 广播有人加入到房间sock.on('callRemote',(roomId)=>{io.to(roomId).emit('callRemote')})// 广播同意接听视频sock.on('acceptCall',(roomId)=>{io.to(roomId).emit('acceptCall')})// 接收offersock.on('sendOffer',({offer,roomId})=>{io.to(roomId).emit('sendOffer',offer)})// 接收answersock.on('sendAnswer',({answer,roomId})=>{io.to(roomId).emit('sendAnswer',answer)})// 收到candidatesock.on('sendCandidate',({candidate,roomId})=>{io.to(roomId).emit('sendCandidate',candidate)})// 挂断结束视频sock.on('hangUp',(roomId)=>{io.to(roomId).emit('hangUp')})
})server.listen(3000, () => {console.log('服务器启动成功');
});

完整代码gitee地址: https://gitee.com/wade-nian/wdn-webrtc.git

参考文章:基于WebRTC实现音视频通话_npm create vite@latest webrtc-client -- --template-CSDN博客

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

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

相关文章

C++STL初阶(1):string的使用及初阶原理

此文作为学习stl的笔记&#xff0c;许多普及、概念性的知识点将不再罗列&#xff08;如stl的发展、背景等&#xff09; 便于读者作为复习等方法了解。 0.STL简介&#xff08;笔记向&#xff09; STL不是祖师爷本贾尼实现的&#xff0c;是在惠普实验室中实现的。其作为一个数据结…

【强训笔记】day20

NO.1 思路&#xff1a;先判断能对砍几个回合&#xff0c;取最小值&#xff0c;因为回合数是整数&#xff0c;所以可能存在都大于0的情况&#xff0c;再判断一下如果都存活就再对砍一次&#xff0c;直到一家存活或者都死亡。 代码实现&#xff1a; #include<iostream>u…

51输出周期为40ms的方波(C+汇编)

题目 已知Fosc12MHz&#xff0c;T1工作于方式1&#xff0c; ①&#xff1a;实现20ms延时&#xff0c;求定时器初值TH0&#xff1f;TL0&#xff1f;写出具体的计算过程。 ②&#xff1a;利用汇编或C语言编程实现输出周期为40ms的方波。 周期为40ms的方波&#xff0c;半周期就…

落雪音乐 超好用的桌面端音乐播放器

之前一直都是充某Q音乐的会员&#xff0c;突然不想氪金了&#xff0c;终于找到一个开源的音乐播放器&#xff0c;在此先给落雪无痕大佬跪了 太爱了 简直白嫖怪的福音 话不多说&#xff0c;直接上操作&#xff1a;解压密码&#xff1a;www.1234f.com下载地址&#xff1a;极速云…

XYCTF - web

目录 warm up ezMake ezhttp ezmd5 牢牢记住&#xff0c;逝者为大 ezPOP 我是一个复读机 ezSerialize 第一关 第二关 第三关 第一种方法&#xff1a; 第二种方法&#xff1a; ez?Make 方法一&#xff1a;利用反弹shell 方法二&#xff1a;通过进制编码绕过 ε…

【SRC实战】无法支付的修改金额支付漏洞

挖个洞先 https://mp.weixin.qq.com/s/F4f8R4uKN0Q9BnTmjDMleg “ 以下漏洞均为实验靶场&#xff0c;如有雷同&#xff0c;纯属巧合 ” 01 — 漏洞证明 一、企业用户&#xff0c;标准商品 “ 支付订单需要公对公银行卡转账&#xff0c;如何绕过&#xff1f;” 1、点击任意…

电商购物系统首页的商品分类

如上图对商品的一个分类实际上和省市区的分类十分类似 , 都是通过自关联的方法来实现 , 但是这里不同的是 , 涉及到外键来获取数据 首先让我们来看一下最后通过后端返回数据的形式是什么样子的 """{1:{channels:[{id:1 , name:手机 , url:},{}{}],sub_cats:[{…

iphone进入恢复模式怎么退出?分享2种退出办法!

iPhone手机莫名其妙的进入到了恢复模式&#xff0c;或者是某些原因需要手机进入恢复模式&#xff0c;但是之后我们不知道如何退出恢复模式怎么办&#xff1f; 通常iPhone进入恢复模式的常见原因主要是软件问题、系统升级失败、误操作问题等导致。那iphone进入恢复模式怎么退出&…

Hadoop-未授权访问-内置配合命令执行RCE

一、Hadoop常见端口及配置文件 Hadoop中有多种端口&#xff0c;这些端口用于不同的服务和通信。以下是Hadoop中常见的端口以及它们的用途&#xff1a; NameNode Web界面端口 (默认: 9870)NameNode 对客户端服务端口 (默认: 8020)Secondary NameNode Web界面端口 (默认: 9868)…

Boss让你设计架构图,你懵逼了,解救你的参考图来啦。

架构图是指用于描述系统或软件的结构和组成部分之间关系的图形表示。 它是一种高层次的图示&#xff0c;用于展示系统的组件、模块、接口和数据流等&#xff0c;以及它们之间的相互作用和依赖关系。架构图通常被用于可视化系统的整体设计和组织结构&#xff0c;帮助人们理解系…

macos使用yarn创建vite时出现Usage Error: The nearest package directory问题

步骤是macos上使用了yarn create vite在window上是直接可以使用了yarn但是在macos上就出现报错 我们仔细看&#xff0c;它说的If /Users/chentianyu isnt intended to be a project, remove any yarn.lock and/or package.json file there.说是要我们清除yarn.lock和package.js…

【Linux】Linux线程

一、Linux线程的概念 1.什么是线程 1.一个进程的一个执行线路叫做线程&#xff0c;线程的一个进程内部的控制序列。 2.一个进程至少有一个执行线程 3.线程在进程内部&#xff0c;本质是在进程地址空间内运行 4.操作系统将进程虚拟地址空间的资源分配给每个执行流&#xff0…

JWK和JWT 学习

JWK和JWT 介绍 JWK (JSON Web Key) 和 JWT (JSON Web Token) 是现代Web应用程序中用于安全通信的两个重要概念。它们都是基于JSON的&#xff0c;并且是OAuth 2.0和OpenID Connect等协议的核心组成部分。 官方文档 JWT官方网站 JWK和JWK Set的RFC文档 JWT的RFC文档 JWK (JS…

MySQL数据库基础(数据库操作,常用数据类型,表的操作)

MySQL数据库基础&#xff08;数据库操作&#xff0c;常用数据类型&#xff0c;表的操作&#xff09; 前言 数据库的操作1.显示当前数据库2.创建数据库3.使用数据库4.删除数据库 常用数据类型1.数值类型2.字符串类型3.日期类型 表的操作1.查看表结构2.创建表3.删除表 总结 前言 …

Linux文件管理指令-001

一、文件目录 1ls ls 查看文件和目录 - a 显示指定目录下所有子目录与文件&#xff0c;包括隐藏文件 - t 显示时按修改时间(很近优先)而不是按名字排序。若文件修改时间相同&#xff0c;则 按字典顺序 - R 递归式地显示指定目录的各个子目录中的文件 - r 按字母逆序或很早…

完整性验证器:迈向 Starknet 超高可扩展性的一大步

原文&#xff1a;https://www.starknet.io/en/content/the-integrity-verifier-a-leap-toward-starknet-hyperscaling&#xff1b;https://www.starknet.io/en/ecosystem/grant 编译&#xff1a;TinTinLand 核心观点 由 Herodotus 开发的完整性验证器&#xff0c;使开发者能够…

算法-排序详解

目录 前言 比较排序 选择排序 插入排序 冒泡排序 归并排序 快速排序 非比较类排序 计数排序 桶排序 基数排序 排序的稳定性 排序算法的题目 前言 计算机的工作之一就是对数据的处理&#xff0c;处理数据有一个常见的操作就是对数据排序&#xff0c;比如新闻系统总…

JWT深入浅出

文章目录 JWT深入浅出1.JWT是什么2.为什么选JWT2.1 传统Session认证2.2 JWT认证 3.JWT怎么用4. jwt绝对安全吗&#xff1f; JWT深入浅出 1.JWT是什么 JWT&#xff08;JSON Web Token&#xff09;是一种用于在网络应用间传递信息的开放标准&#xff0c;通常用于身份认证和非敏…

Unity VR在编辑器下开启Quest3透视(PassThrough)功能

现在有个需求是PC端串流在某些特定时候需要开启透视。我研究了两天发现一些坑,记录一下方便查阅,也给没踩坑的朋友一些思路方案。 先说结论,如果要打PC端或者在Unity编辑器中开启,那么OpenXR当前是不行的可能还需要一个长期的过程,必须需要切换到Oculus。当然Unity官方指…

机器人系统可以支持对接人工系统吗?

​ 随着科技的飞速发展&#xff0c;机器人系统在各行各业都扮演着越来越重要的角色。它们可以高效地处理大量数据&#xff0c;执行繁琐的任务&#xff0c;甚至在某些领域超越了人类的能力。然而&#xff0c;机器人系统也有其局限性&#xff0c;特别是在处理复杂的人际交往…