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博客
相关文章:
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">…...
web 基础之 HTTP 请求
web 基础 网上冲浪 就是在互联网(internet)上获取各种信息,进行工作,或者娱乐,他的英文表示surfing the Internet,因 “surfing”d的意思是冲浪,即成为网上冲浪,这是一种形象说法, 也是一个非…...
嵌入式 - GPIO编程简介
An Introduction to GPIO Programming By Jeff Tranter Wednesday, June 12, 2019 编者按:本 2019 年博客系列是 ICS 最受欢迎的系列之一,现已更新(2022 年 12 月),以确保内容仍然准确、相关和有用。 本博客是 Integr…...
8种区块链开发者必须知道的顶级编程语言!
我来问你一个问题:请说出一种技术,它以去中心化、不可篡改和透明性等核心特征席卷了全球。 这个问题的答案是,当然是区块链,它在近些年进入大家的视野并颠覆了工商业,没有任何其他技术能够做到这一点。 预计从2020年…...
十三、Redis哨兵模式--Sentinel
上一篇介绍了Redis中的主从复制。我们知道Redis主从中一般只有主节点对外提供写操作,如果主节点发生故障,为了保证Redis的可用性,这时就要在可用的slave节点中,挑选一个作为主节点。这种切换操作如果是人为的操作,那么…...
[力扣题解]1005. K 次取反后最大化的数组和
题目:1005. K 次取反后最大化的数组和 思路 贪心法; 用绝对值大小排序,自己写一个比较函数, static bool compare(int a, int b) {return abs(a) > abs(b); }注意这样写出来是降序排列; 代码 class Solution {…...
Web UI自动化测试--PO模式
没有PO实现的测试用例的问题: 重用性低:登录功能重复可维护性差:数据和代码混合可读性差:元素定位方法杂乱(id、xpath、css混杂)可读性差:不易识别操作的含义(特别是css和xpath语法)可维护性差:如果某个元素的属性改了,你要更改多次PO(Page Object Model)页面对象模型…...
Python进阶之-反射机制详解
✨前言: 什么是反射? Python中的“反射”是一个编程术语,它指的是程序在运行时能够检查和操作其自身状态的能力,特别是通过名称(通常是字符串)来访问对象的属性、方法和其他组成部分。这种机制允许代码动态…...
day05-面向对象内存原理和数组
day05 面向对象内存原理和数组 我们在之前已经学习过创建对象了,那么在底层中他是如何运行的。 1.对象内存图 1.1 Java 内存分配 Java 程序在运行时,需要在内存中分配空间。为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域…...
从头理解transformer,注意力机制(下)
交叉注意力 交叉注意力里面q和KV生成的数据不一样 自注意力机制就是闷头自学 解码器里面的每一层都会拿着编码器结果进行参考,然后比较相互之间的差异。每做一次注意力计算都需要校准一次 编码器和解码器是可以并行进行训练的 训练过程 好久不见输入到编码器&…...
ORA-609频繁出现在alert.log,如何解决?
ORA-609就alertlog中比较常见的一个报错,虽然并没有太大的影响,但是频繁的出现在alert log也是很让人厌烦的事情,本文介绍如何排查解决ORA-609问题。 1.ORA-609官方定义 could not attach to incoming connection Cause Oracle process cou…...
JVM 类加载机制
JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的 java.lang.class 对…...
亲测-wordpress文章实时同步发布修改删除多个站点的WP2WP插件
一款将wordpress文章同步到其他WordPress网站的插件,通过这款插件,可以保持不同博客之间文章发布、修改、删除的同步。 安装步骤: 主站和分站都要上传这个插件 1.把插件上传到wp-content\plugins解压出来wp2wp文件夹,然后启用插…...
npm无法安装node-sass 的问题
安装 node-sass 的问题呈现:4.9.0版本无法下载 Downloading binary from https://github.com/sass/node-sass/releases/download/v4.9.0/win32-x64-72_binding.node Cannot download "https://github.com/sass/node-sass/releases/download/v4.9.0/win32-x64-…...
springboot 引入第三方bean
如何进行第三方bean的定义 参数进行自动装配...
安装Nginx
如果没有gcc环境,需要安装gcc yum install gcc-c安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel工作目录 mkdir /opt/nginx && cd /opt/nginx下载安装包 wget http://nginx.org/download/nginx-1.32.1.tar.gz解压安装包…...
爬虫工具you-get
you-get是一个简单易上手的爬虫小工具,可以从网络中爬取多媒体信息,包括图片、音频和视频。you-get的github项目地址为:https://github.com/soimort/you-get 一、安装 以下为相关依赖,需要分别安装: Python 3 (必须…...
hal_stm32_RTC函数
1设置当前时间: 调用 HAL_RTC_SetTime 来设置小时、分钟、秒和亚秒。 调用 HAL_RTC_SetDate 来设置年、月、日和星期。 HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format); HAL_StatusTypeDef HAL_RTC_SetDat…...
【大数据·Hadoop】从词频统计由浅入深介绍MapReduce分布式计算的设计思想和原理
一、引入:词频统计问题 假如我们有一亿份文档,需要统计这一亿份文档的词频。我们会怎么做,有以下思路 使用单台PC执行:能不能存的下不说,串行计算,一份一份文档读,然后进行词频统计࿰…...
win10建立共享文件夹和ipad共享文件
win10端设置 查看自己的局域网IP 在任意地方新建一个文件夹 打开文件夹的属性,点到共享的地方 点击高级共享 然后点击应用,确认 再回到之前哪个地方,点击共享 把Everyone的权限改为读取/写入 最后点击共享就欧克了 失败的可能原因 ipad端设置 然后回出现一个要输入用户名和…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
