socket实现视频通话-WebRTC
最近喜欢研究视频流,所以思考了双向通信socket,接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~
客户端获取视频流
首先思考如何获取视频流呢?
其实跟录音的功能差不多,都是查询电脑上是否有媒体设备,如果有录音和录像的设备,首先就需要授权,然后将视频流通过socket传输给服务端。
获取媒体设备
const stream = await navigator.mediaDevices.getUserMedia({audio: true,video: true
})
因为是打视频的功能,那A客户端本身也希望看到A的摄像头,所以我们直接将其赋值给一个video标签,就能看到图像了.
<p>这是A页面</p><div class="local-stream-page"><video autoplay controls muted id="elA"></video><button onclick="onStart()">打视频给B页面</button>
</div><script>try {const stream = await navigator.mediaDevices.getUserMedia({audio: true,video: true})if (videoElA) {videoElA.srcObject = stream // 在 video 标签上播放媒体流}peerInit(stream) // 初始化连接} catch (error) {console.log('error:', error)}</script>
然后就是重要部分了,我们需要用到WebRTC的API
RTCPeerConnection
RTCPeerConnection是WebRTC API中的一个对象,用于建立和管理两个或多个用户之间的实时通信。它允许通过互联网进行音频和视频通话,以及共享数据流。
RTCPeerConnection对象提供了一系列的方法和事件,用于配置、管理和控制媒体流的传输。它支持使用不同的技术,如ICE(Interactive Connectivity Establishment)和STUN(Session Traversal Utilities for NAT)来解决网络地址转换(NAT)问题,以便在防火墙后面的不同设备之间建立连接。
使用RTCPeerConnection对象,您可以创建媒体流并将其发送到其他设备,也可以接收来自其他设备的媒体流。它还支持使用SDP(Session Description Protocol)描述媒体会话的配置,以及通过ICE和STUN协议协商和转发媒体数据包的路由。
const peerInit = stream => {// 1. 创建连接实例peerA = new RTCPeerConnection()// 2. 添加视频流轨道stream.getTracks().forEach(track => {peerA.addTrack(track, stream)})// peerA 端peerA.onicecandidate = event => {if (event.candidate) {socketA.send(JSON.stringify({ type: 'candid', data: event.candidate })) // socketA发送数据}}// 检测连接状态peerA.onconnectionstatechange = event => {if (peerA.connectionState === 'connected') {console.log('对等连接成功!')}}// 互换sdp认证transSDP()
}
到这里我们发送数据部分就是这样子啦,但是还不行,因为两者通视频,还需要SDP认证,什么是SDP认证呢?
SDP(Session Description Protocol)认证是指通过在SDP协议中添加特定的信息来验证身份或其他属性的方法。SDP协议是一种用于描述多媒体会话的信息协议,它包含了音频、视频等媒体的编码格式、分辨率、网络地址等信息,用于在通话双方之间建立和维护媒体连接。
在SDP认证中,通过在SDP协议中添加特定的信息,如用户名、会议ID等,双方可以互相验证身份。此外,还可以通过在SDP协议中包含数字签名或加密信息等技术来增强认证的安全性。
SDP认证通常用于多媒体通信、视频会议等应用场景中,以确保通信的安全性和可信度。在SDP认证中,需要使用相应的协议或算法来验证SDP信息的来源和完整性,以确认身份或其他属性的合法性。
互换SDP认证
// peerA 端
const transSDP = async () => {let offer = await peerA.createOffer()// 向 peerB 传输 offersocketA.send(JSON.stringify({ type: 'offer', data: offer }))// 接收 peerB 传来的 answersocketA.onmessage = async evt => {let reader = new FileReader()reader.readAsText(evt.data, 'utf-8')reader.onload = async function() {let { type, data } = JSON.parse(reader.result)console.log(JSON.parse(reader.result), 111)if (type == 'answer') {await peerA.setLocalDescription(offer)await peerA.setRemoteDescription(data)}}}
}
这就是A客户端的全部代码啦~
放心,全部代码文章末尾会给到.
node服务端socket传输
接下来我们来看看服务端是如何处理的.对了,这里必须说一下,两个socket之间的通信,必须要靠服务端管理,所以这就是为什么一定要学node的原因😂
const WebSocket = require('ws');// 创建一个 WebSocket 服务器,监听 8080 端口
const wss = new WebSocket.Server({ port: 8000 });// 当有客户端连接时,创建一个 WebSocket 并将其添加到客户端列表中
wss.on('connection', function connection(ws) {console.log('Client connected');// 当客户端发送消息时,将消息发送给所有客户端ws.on('message', function incoming(message) {console.log('Received message:', message.toString('utf8')); // 接受的对象,客户端发送的是字符串,Buffer// 将消息发送给所有客户端wss.clients.forEach(function each(client) {if (client !== ws && client.readyState === WebSocket.OPEN) {client.send(message); // 客户端接受的是blob格式数据}});});// 当客户端断开连接时,将其从客户端列表中删除ws.on('close', function close() {console.log('Client disconnected');});
});
服务端用到了ws依赖, 如何区分两个不同的socket客户端, 特别是在同一个服务器下,同一个端口,不同的页面下,我发现必须要给两个socket一个唯一的标识才能做到,所以这期就先出功能,后面再继续补一下ws的源码学习.
不过这里要区分清楚,这是将当前的client客户端发送给处理自己以外的,其他所以socket客户端,发送消息这里,就是一对多的关系哦.
客户端接受视频流
服务端处理完了,就进行下一个客户端如何接受视频流
刚刚的sdp认证,肯定不止止A页面的事情,都说了是认证,那肯定通信双方需要知晓.
这里有一个顺序问题
1.首先是A页面创建offer—createOffer
2.然后是B页面设置远程描述—setRemoteDescription
3.B页面生成发送到A页面的answer—createAnswer
4.B页面设置本地描述—setLocalDescription
5.A页面设置本地描述—setLocalDescription(传参是A页面的offer)
6.A页面设备远程描述–setRemoteDescription(传参是B页面的answer)
只要这上面6步都正常执行,B页面才能接收到A页面的视频流和音频流
const transSDP = async () => {// 1. 创建 offerlet offer = await peerA.createOffer()await peerB.setRemoteDescription(offer)// 2. 创建 answerlet answer = await peerB.createAnswer()await peerB.setLocalDescription(answer)// 3. 发送端设置 SDPawait peerA.setLocalDescription(offer)await peerA.setRemoteDescription(answer)
}
加上socket之后就是这样
不过既然是socket了,所以数据上要做转换处理,接收到的是blob数据

// B接收A的消息
// peerB 端,接收 peerA 传来的 offer
socketB.onmessage = evt => {// console.log(evt.data)handleBlobToText(evt.data)
}const handleBlobToText = (blob) => {let reader = new FileReader()reader.readAsText(blob, 'utf-8') // 接收到的是blob数据,先转成文本reader.onload = async function() {console.log(reader.result)let { type, data } = JSON.parse(reader.result) // 文本转对象console.log(JSON.parse(reader.result))if (type == 'offer') {await peerB.setRemoteDescription(data)console.log('2.然后是B页面设置远程描述', new Date().getTime())let answer = await peerB.createAnswer()console.log('3.B页面生成发送到A页面的answer', new Date().getTime())await peerB.setLocalDescription(answer)console.log('4.B页面设置本地描述', new Date().getTime())// 向 peerA 传输 answersocketB.send(JSON.stringify({ type: 'answer', data: answer }))}if (type == 'candid') {peerB.addIceCandidate(data)}}
}socketB.onerror = function() {console.log('WebSocket error. Ready state:', socketB.readyState);
};

根据时间戳,就能发现这六步的顺序.
将接收到的视频流渲染到B页面的video标签中,这就能接受的A页面的视频流了.
const socketB = new WebSocket('ws://localhost:8000');const peerB = new RTCPeerConnection()
const videoElB = document.getElementById('elB')// 监听数据传来
peerB.ontrack = async event => {const [remoteStream] = event.streamsvideoElB.srcObject = remoteStream
}
效果
这就是两个页面视频通讯的结果如下:

全部源码已经上传在GitHub上啦~
https://github.com/0522skylar/webRTC-video
相关文章:
socket实现视频通话-WebRTC
最近喜欢研究视频流,所以思考了双向通信socket,接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~ 客户端获取视频流 首先思考如何获取视频流呢? 其实跟录音的功能差不多,都是查询电脑上是否有媒体设备,如果…...
simulink代码生成(九)—— 串口显示数据(纸飞机联合调试)
纸飞机里面的协议是固定的,必须按照协议配置; (1)使用EasyHEX协议,测试int16数据类型 测试串口发出的数据是否符合? 串口接收数据为: 打开纸飞机绘图侧: (1)…...
Mysql数据库(中)——增删改查的学习(全面,详细)
上一篇主要对查询操作进行了详细的总结,本篇主要对增删改操作以及一些常用的函数进行总结,包括流程控制等;以下的代码可以直接复制到数据库可视化软件中,便于理解和练习; 常用的操作: #函数: S…...
test dbtest-03-对比 Liquibase、flyway、dbDeploy、dbsetup
详细对比 Liquibase、flyway、dbDeploy、dbsetup,给出对比表格 下面是一个简要的对比表格,涵盖了 Liquibase、Flyway、dbDeploy 和 DbSetup 这四个数据库变更管理工具的一些主要特点。 特点/工具LiquibaseFlywaydbDeployDbSetup开发语言Java࿰…...
力导向图与矩阵排序
Graph-layout force directed(力导向图布局)是一种用于可视化网络图的布局算法。它基于物理模型,模拟了图中节点之间的相互排斥和连接弹性,以生成具有良好可读性和美观性的图形布局。 在力导向图布局中,每个节点被视为…...
word 常用功能记录
word手册 多行文字对齐标题调整文字间距打钩方框插入三线表插入参考文献自动生成目录 多行文字对齐 标题调整文字间距 打钩方框 插入三线表 插入一个最基本的表格把整个表格设置为无框线设置上框线【实线1.5磅】设置下框线【实线1.5磅】选中第一行,设置下框线【实线…...
C#线程基础(线程启动和停止)
目录 一、关于线程 二、示例 三、生成效果 一、关于线程 在使用多线程前要先引用命名空间System.Threading,引用命名空间后就可以在需要的地方方便地创建并使用线程。 创建线程对象的构造方法中使用了ThreadStart()委托,当线程开始执行时,…...
如何利用ChatGPT来提高编程效率
如何利用ChatGPT来提高编程效率 在当今这个信息爆炸和技术快速发展的时代,程序员们面临着巨大的压力,既要保证代码的质量,又要提高工作效率。幸运的是,人工智能(AI)正在改变我们编写和维护代码的方式,而OpenAI的ChatGPT是其中的佼佼者。本文将讨论如何利用ChatGPT以及结合…...
java智慧工地源码,互联网+建筑工地,实现对工程项目内人员、车辆、安全、设备、材料等的智能化管理
智慧工地全套源码,微服务JavaSpring Cloud UniApp MySql;支持多端展示(大屏端、PC端、手机端、平板端)演示自主版权。 智慧工地概念: 智慧工地就是互联网建筑工地,是将互联网的理念和技术引入建筑工地&…...
创建并使用自己的C++模块(Windows10+MSVC)
module是C20种新引入的特性,关于module的介绍和好处,网上已有大量的文章,此处也不再赘述,本文仅记录在个人的环境上创建一个简单的module并使用这个module。 环境同上一篇文章( windows10,MSVC C工具链&am…...
Spring Boot 2.7.11 集成 GraphQL
GraphQL介绍 GraphQL(Graph Query Language)是一种用于API的查询语言和运行时环境,由Facebook于2012年创建并在2015年公开发布。与传统的RESTful API相比,GraphQL提供了更灵活、高效和强大的数据查询和操作方式。 以下是GraphQL…...
软件工程期末总结
软件工程期末总结 软件危机出现的原因软件生命周期软件生命周期的概念生命周期的各个阶段 软件开发模型极限编程 可行性研究与项目开发计划需求分析结构化分析的方法结构化分析的图形工具软件设计的原则用户界面设计结构化软件设计面向对象面向对象建模 软件危机出现的原因 忽视…...
MidTool图文创作-GPT-4与DALL·E 3的结合
GPT-4与DALLE 3的结合 GPT-4是由OpenAI开发的最新一代语言预测模型,它在前代模型的基础上进行了大幅度的改进,不仅在文本生成的连贯性、准确性上有了显著提升,还在理解复杂语境和执行多步骤指令方面表现出了更高的能力。而DALLE 3则是一个创…...
Python将两个或多个列表合并为一个列表,并根据每个输入列表中的元素的位置将其组合在一起
将两个或多个列表合并为一个列表,并根据每个输入列表中的元素的位置将其组合在一起。 这个需求在实际开发过程中应该说非常常见,当然python也给我们内置了相关方法! zip(*iterables, strictFalse) 在多个迭代器上并行迭代,从每…...
数模混合SoC芯片中LEF2Milkyway的golden flow
在数模混合芯片中的项目中,特别是数字模块很少甚至只有一个简单的数字控制逻辑时,我们要做数字模块的后端实现时,通常模拟那边会问我们实现需要他们提供哪些数据。 通常来说,我们可以让模拟设计提供数字模块的GDS或LEF文件即可。…...
Five tips to make your essay flow
This post was written by Sydney Nicholson, a second-year master’s student in the English Department. Dear writer, Have you ever wondered what it takes to make an essay “flow”? In my time as a writing center tutor, I’ve noticed that this is one of th…...
linux驱动(二):led补
本文主要探讨s5pv210的led驱动相关知识,包括驱动主次设备注册和取消,udev(mdev)机制,静态和动态映射操作寄存器。 字符设备驱动注册 老接口(register_chrdev) static inline int register_chrdev(unsigned int major, const char *n…...
性能测试-jmeter:安装 / 基础使用
一、理解jmeter 官网-Apache JMeter-Apache JMeter™ JMeter是一款开源的性能测试工具,主要用于模拟大量用户并发访问目标服务器,以评估服务器的性能和稳定性。 JMeter可以执行以下任务序号用途描述1性能测试通过模拟多个用户在同一时间对服务器进行请…...
数据仓库-数仓优化小厂实践
一、背景 由于公司规模较小,大数据相关没有实现平台化,相关的架构都是原生的Apache组件,所以集群的维护和优化都需要人工的参与。根据自己的实践整理一些数仓相关的优化。 二、优化 1、简易架构图 2、ODS层优化 2.1 分段式解析 随着业务增长…...
uniapp中uview组件丰富的Code 验证码输入框的使用方法
目录 基本使用 #自定义提示语 #保持倒计时 API #Props #Methods #Event 基本使用 通过ref获取组件对象,再执行后面的操作,见下方示例。 通过seconds设置需要倒计的秒数(默认60)通过ref调用组件内部的start方法,开始倒计时通过监听cha…...
GraphAgent:大语言模型与图数据融合的智能体框架解析与实践
1. 项目概述:当大语言模型遇上图数据最近在折腾一些涉及复杂关系数据的项目,比如学术文献网络、社交关系分析,甚至是企业内部的知识库梳理。这些场景里,数据不只是孤立的文本或数字,它们之间充满了各种显式的连接&…...
手把手图解:用‘阻挫’和‘复本’理解自旋玻璃、自旋冰与量子自旋液体
手把手图解:用‘阻挫’和‘复本’理解自旋玻璃、自旋冰与量子自旋液体 凝聚态物理中那些看似晦涩的概念,往往只需要一个恰到好处的比喻就能豁然开朗。想象你正在参加一场磁铁小人的派对,它们的箭头方向就像固执的舞伴,既想跟随音乐…...
【C++】继承详解——基类/派生类、作用域、默认函数、菱形继承(超详细)
文章目录一、继承开篇二、继承的概念及定义1. 继承是什么2. 继承定义格式3. 继承后成员访问权限变化(超级重要)三、基类和派生类的赋值转换(切片/切割)四、继承中的作用域(隐藏 / 重定义)1. 成员变量隐藏2.…...
魔兽争霸3游戏优化终极指南:3步解决帧率限制与界面显示问题
魔兽争霸3游戏优化终极指南:3步解决帧率限制与界面显示问题 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸3的卡顿画面和界…...
LinkedIn Liger Kernel:移动设备内核定制与性能优化实战
1. 项目概述:一个面向移动设备的开源内核探索如果你在移动设备开发、嵌入式系统或者内核研究的圈子里待过一段时间,大概率听说过或者接触过“Liger Kernel”这个名字。它不是一个商业产品,而是一个在GitHub上由LinkedIn开源并维护的Android内…...
激光带宽对半导体光刻OPC模型精度的影响与优化
1. 激光带宽对OPC模型精度的影响机制解析在半导体光刻技术领域,随着制程节点不断向32nm及以下推进,光学邻近效应校正(OPC)模型的精度要求日益严苛。激光光源的带宽特性作为影响成像质量的关键因素之一,其作用机制主要体现在三个方面ÿ…...
ARM中断控制器架构演进与Redistributor关键设计
1. ARM中断控制器架构演进与Redistributor定位现代多核处理器系统中,中断控制器作为连接外设与CPU的核心枢纽,其设计直接影响系统实时性和吞吐量。ARM架构从GICv2到GICv4的演进过程中,最显著的变革之一是引入了Redistributor模块。这个位于CP…...
OpenCorpo开源企业情报工具:从数据抓取到关系图谱构建实战
1. 项目概述:当开源情报遇上企业数据最近在开源情报(OSINT)的圈子里,一个名为 OpenCorpo 的项目引起了我的注意。它不是一个传统意义上的商业数据库,而是一个由社区驱动的、旨在聚合和解析全球企业公开信息的工具集。简…...
终极指南:如何快速掌握Clean Code PHP编码规范提升团队协作效率
终极指南:如何快速掌握Clean Code PHP编码规范提升团队协作效率 【免费下载链接】clean-code-php :bathtub: Clean Code concepts adapted for PHP 项目地址: https://gitcode.com/gh_mirrors/cl/clean-code-php 在PHP开发中,编写清晰、可维护的代…...
从AceForge看一体化AI平台:如何实现模型部署与运维的平民化
1. 项目概述:从“AceForge”看开源AI工具链的平民化革命最近在GitHub上闲逛,发现一个叫“AceForge”的项目,作者是sudokrang。点进去一看,简介写得挺有意思,大意是说这是一个“一站式、开箱即用的AI应用开发与部署平台…...
