当前位置: 首页 > news >正文

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)上获取各种信息&#xff0c;进行工作&#xff0c;或者娱乐&#xff0c;他的英文表示surfing the Internet&#xff0c;因 “surfing”d的意思是冲浪&#xff0c;即成为网上冲浪&#xff0c;这是一种形象说法&#xff0c; 也是一个非…...

嵌入式 - GPIO编程简介

An Introduction to GPIO Programming By Jeff Tranter Wednesday, June 12, 2019 编者按&#xff1a;本 2019 年博客系列是 ICS 最受欢迎的系列之一&#xff0c;现已更新&#xff08;2022 年 12 月&#xff09;&#xff0c;以确保内容仍然准确、相关和有用。 本博客是 Integr…...

8种区块链开发者必须知道的顶级编程语言!

我来问你一个问题&#xff1a;请说出一种技术&#xff0c;它以去中心化、不可篡改和透明性等核心特征席卷了全球。 这个问题的答案是&#xff0c;当然是区块链&#xff0c;它在近些年进入大家的视野并颠覆了工商业&#xff0c;没有任何其他技术能够做到这一点。 预计从2020年…...

十三、Redis哨兵模式--Sentinel

上一篇介绍了Redis中的主从复制。我们知道Redis主从中一般只有主节点对外提供写操作&#xff0c;如果主节点发生故障&#xff0c;为了保证Redis的可用性&#xff0c;这时就要在可用的slave节点中&#xff0c;挑选一个作为主节点。这种切换操作如果是人为的操作&#xff0c;那么…...

[力扣题解]1005. K 次取反后最大化的数组和

题目&#xff1a;1005. K 次取反后最大化的数组和 思路 贪心法&#xff1b; 用绝对值大小排序&#xff0c;自己写一个比较函数&#xff0c; static bool compare(int a, int b) {return abs(a) > abs(b); }注意这样写出来是降序排列&#xff1b; 代码 class Solution {…...

Web UI自动化测试--PO模式

没有PO实现的测试用例的问题: 重用性低:登录功能重复可维护性差:数据和代码混合可读性差:元素定位方法杂乱(id、xpath、css混杂)可读性差:不易识别操作的含义(特别是css和xpath语法)可维护性差:如果某个元素的属性改了,你要更改多次PO(Page Object Model)页面对象模型…...

Python进阶之-反射机制详解

✨前言&#xff1a; 什么是反射&#xff1f; Python中的“反射”是一个编程术语&#xff0c;它指的是程序在运行时能够检查和操作其自身状态的能力&#xff0c;特别是通过名称&#xff08;通常是字符串&#xff09;来访问对象的属性、方法和其他组成部分。这种机制允许代码动态…...

day05-面向对象内存原理和数组

day05 面向对象内存原理和数组 我们在之前已经学习过创建对象了,那么在底层中他是如何运行的。 1.对象内存图 1.1 Java 内存分配 Java 程序在运行时&#xff0c;需要在内存中分配空间。为了提高运算效率&#xff0c;就对空间进行了不同区域的划分&#xff0c;因为每一片区域…...

从头理解transformer,注意力机制(下)

交叉注意力 交叉注意力里面q和KV生成的数据不一样 自注意力机制就是闷头自学 解码器里面的每一层都会拿着编码器结果进行参考&#xff0c;然后比较相互之间的差异。每做一次注意力计算都需要校准一次 编码器和解码器是可以并行进行训练的 训练过程 好久不见输入到编码器&…...

ORA-609频繁出现在alert.log,如何解决?

ORA-609就alertlog中比较常见的一个报错&#xff0c;虽然并没有太大的影响&#xff0c;但是频繁的出现在alert log也是很让人厌烦的事情&#xff0c;本文介绍如何排查解决ORA-609问题。 1.ORA-609官方定义 could not attach to incoming connection Cause Oracle process cou…...

JVM 类加载机制

JVM 类加载机制分为五个部分&#xff1a;加载&#xff0c;验证&#xff0c;准备&#xff0c;解析&#xff0c;初始化&#xff0c;下面我们就分别来看一下这五个过程。 加载 加载是类加载过程中的一个阶段&#xff0c;这个阶段会在内存中生成一个代表这个类的 java.lang.class 对…...

亲测-wordpress文章实时同步发布修改删除多个站点的WP2WP插件

一款将wordpress文章同步到其他WordPress网站的插件&#xff0c;通过这款插件&#xff0c;可以保持不同博客之间文章发布、修改、删除的同步。 安装步骤&#xff1a; 主站和分站都要上传这个插件 1.把插件上传到wp-content\plugins解压出来wp2wp文件夹&#xff0c;然后启用插…...

npm无法安装node-sass 的问题

安装 node-sass 的问题呈现&#xff1a;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环境&#xff0c;需要安装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是一个简单易上手的爬虫小工具&#xff0c;可以从网络中爬取多媒体信息&#xff0c;包括图片、音频和视频。you-get的github项目地址为&#xff1a;https://github.com/soimort/you-get 一、安装 以下为相关依赖&#xff0c;需要分别安装&#xff1a; Python 3 (必须…...

hal_stm32_RTC函数

1设置当前时间&#xff1a; 调用 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分布式计算的设计思想和原理

一、引入&#xff1a;词频统计问题 假如我们有一亿份文档&#xff0c;需要统计这一亿份文档的词频。我们会怎么做&#xff0c;有以下思路 使用单台PC执行&#xff1a;能不能存的下不说&#xff0c;串行计算&#xff0c;一份一份文档读&#xff0c;然后进行词频统计&#xff0…...

win10建立共享文件夹和ipad共享文件

win10端设置 查看自己的局域网IP 在任意地方新建一个文件夹 打开文件夹的属性,点到共享的地方 点击高级共享 然后点击应用,确认 再回到之前哪个地方,点击共享 把Everyone的权限改为读取/写入 最后点击共享就欧克了 失败的可能原因 ipad端设置 然后回出现一个要输入用户名和…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...