webrtc学习----前端推流拉流,局域网socket版,一对多
提示:局域网socket版,一对多
文章目录
-
- @[TOC](文章目录)
- 前言
- 一、教程
- 二、webrtc工作流程
- 三、推流端
- 四、拉流
- 五、socket服务
- 六、效果
- 七、备注
- 总结
前言
WebRTC(Web Real-Time Communication)是一种实时通讯技术,允许网络应用或站点在不借助中间媒介的情况下,建立浏览器之间的点对点(Peer-to-Peer)连接,实现视频流、音频流或其他任意数据的传输。WebRTC的核心功能包括音视频的采集、编解码、网络传输和显示等
WebRTC的技术特点
1、实时通信:WebRTC专注于实时通信,包括音频、视频和其他数据传输。
2、点对点通信:WebRTC支持点对点通信,即两个浏览器之间直接建立连接,无需通过中间服务器。
3、多媒体引擎:WebRTC包含一个多媒体引擎,处理音频和视频流,并提供丰富的API和协议。
4、NAT穿越:WebRTC提供机制,使得在NAT(Network Address Translation)和防火墙等网络设备背后进行通信更为容易。
5、TURN服务器:当P2P连接无法建立时,WebRTC会利用TURN服务器进行数据中转,确保通信的稳定性
一、教程
webrtc文档
二、webrtc工作流程
// 推流拉流过程
/*** 推流端获取视频stream* 推流端生成offer * 推流端通过offer设置推流LocalDescription* 推流端发送offer给(拉)流端* (拉)流端接收offer* (拉)流端通过offer设置(拉)流端RemoteDescription* (拉)流端生成answer* (拉)流端通过answer设置(拉)流端LocalDescription* (拉)流端发送answer给推流端* 推流端接收answer设置推流端RemoteDescription* 推流端发送candidate(video,audio各一次)* (拉)流端接收candidate* (拉)流端发送candidate(video,audio各一次)* 推流端接收candidate* **/
三、推流端
一个拉流RTCPeerConnection,对应一个推流RTCPeerConnection
X 个拉流RTCPeerConnection,对应X 个推流RTCPeerConnection
push.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>推流</title>
</head>
<body><video id="webrtcVideo" autoplay></video><script>const video = document.getElementById('webrtcVideo');// webscoketconst ws = new WebSocket('ws://127.0.0.1:1990'); // 可换成局域网ip地址let videoStream;// 一个拉流RTCPeerConnection对应一个推流RTCPeerConnection,xx个拉流RTCPeerConnection,对应xx个推流RTCPeerConnectionconst pushPool = {};// rtc connectionlet pushRtcCon;// 打开摄像头,video标签播放视频流const getStream = async () => {if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia)console.log('不支持:getUserMedia');const stream = await navigator.mediaDevices.getUserMedia({video:true});video.srcObject = stream;videoStream = stream;}getStream();// 开始推流const startPush = (pullId) => {if(!pushPool[pullId])pushPool[pullId] = pushRtcCon = new RTCPeerConnection();// rtc connection 添加trackvideoStream.getVideoTracks().forEach(track => {pushRtcCon.addTrack(track,videoStream);});// 监听icecandidatepushRtcCon.onicecandidate = (event)=>{if(event.candidate)ws.send(JSON.stringify({type:'candidate',candidate:event.candidate,id:pullId}))}// 创建offerpushRtcCon.createOffer().then(offer=>{console.log(offer)// 设置推流LocalDescriptionpushRtcCon.setLocalDescription(offer).then(()=>{ console.log('推流设置LocalDescription成功');});// offer信息发送给拉流ws.send(JSON.stringify({type:'offer',id:pullId,offer}))});}// 开启websocket服务ws.addEventListener('open',()=>{// 初始化推流通道ws.send(JSON.stringify({type:'push_init'}))console.log('websocket连接成功')});// 接收wenbscoket信息ws.addEventListener('message', (event) => {let data = JSON.parse(event.data);console.log(data)// 接收到拉流传来的answer 设置推流RemoteDescriptionif(data.type == 'answer')pushRtcCon.setRemoteDescription(data.answer).then(()=>{ console.log('推流设置RemoteDescription成功');});// 接收拉流candidate 推流rtc connection 添加IceCandidateif(data.type == 'candidate'&&data.candidate)pushRtcCon.addIceCandidate(data.candidate).then(()=>{ console.log('推流添加candidate成功');});// 接收拉流开启消息 开始推流if(data.type == 'pull_start')startPush(data.id);})</script>
</body>
</html>
四、拉流
pull.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><video id="pullVideo" autoplay preload muted></video><div id="pullBtn">拉流</div><script>const pullBtn = document.getElementById('pullBtn');// 开始拉流const startPll = () =>{let ws = new WebSocket('ws://127.0.0.1:1990'); // 可换成局域网ip地址const pullVideo = document.getElementById('pullVideo');let pullStrem;// 拉流rtc connectionconst pullRtcCon = new RTCPeerConnection();const pullID = new Date().getTime()+'io'+Math.round(Math.random()*10000);// 拉流监听icecandidatepullRtcCon.onicecandidate = (event)=>{// 接收到icecandidate 发送candidate给推流端if(event.candidate)ws.send(JSON.stringify({type:'candidate',candidate:event.candidate,num:1,id:pullID}))}// 监听trackpullRtcCon.addEventListener('track' ,(event) => {pullStrem = event.streams[0];pullVideo.srcObject = event.streams[0];})// 打开webscoketws.addEventListener('open',async ()=>{await ws.send(JSON.stringify({type:'pull_init',id:pullID}));// 通知推流端,开始推流ws.send(JSON.stringify({type:'pull_start',id:pullID}));console.log('websocket连接成功')});// 监听webscoket消息ws.addEventListener('message',(event)=>{let data = JSON.parse(event.data);// 接收到推流端offerconsole.log(data,'????')if(data.type == 'offer'){// 设置拉流端 RemoteDescriptionpullRtcCon.setRemoteDescription(data.offer).then(()=>{console.log('拉流设置RemoteDescription成功')// 创建answerpullRtcCon.createAnswer(data.offer).then((answer)=>{// 设置拉流的LocalDescriptionpullRtcCon.setLocalDescription(answer).then(()=>{console.log('拉流设置LocalDescription成功')});// 发送answer到推流端ws.send(JSON.stringify({type:'answer',answer,id:pullID}))});});}// 接收推流端candidate 拉流端添加IceCandidateif(data.type == 'candidate')pullRtcCon.addIceCandidate(data.candidate).then(()=>{ console.log('拉流添加candidate成功');});})}// 拉流按钮点击事件pullBtn.addEventListener('click',startPll)</script>
</body>
</html>
五、socket服务
安装依赖
npm init
npm install nodejs-websocket -S
index.js
const ws = require('nodejs-websocket');
const port = '1990';// 推流通道 拉流通道
let wsPush,wsPull,pullPool={};
const server = ws.createServer((connection)=>{// websocket 连接接收数据connection.on('text',(msg)=>{let data = JSON.parse(msg);// 初始化推流websocketif(data.type == 'push_init')wsPush = connection;// 初始化拉流websocketif(data.type == 'pull_init')if(!pullPool[data.id]) pullPool[data.id] = connection;// 接收推流消息 发送给拉流if(connection == wsPush&&pullPool[data.id])pullPool[data.id].send(msg);// 接收拉流消息 发送给推流for(let key in pullPool){if(connection == pullPool[key]&&wsPush)wsPush.send(msg);}})// websocket 关闭connection.on('close',()=>{wsPush = null;wsPull = null;console.log('通道关闭')})// websocket 报错connection.on('err',(err)=>{wsPush = null;wsPull = null;console.log('通道报错:'+err)})
})
server.listen(port,console.log('ws启动成功,127.0.0.1:'+port));
六、效果
推流端
拉流端(点击拉流按钮)
七、备注
1、socket地址可换成局域网IP地址访问
2、pull来流请求地址可换成局域网IP地址访问
总结
踩坑路漫漫长@~@
相关文章:

webrtc学习----前端推流拉流,局域网socket版,一对多
提示:局域网socket版,一对多 文章目录 [TOC](文章目录) 前言一、教程二、webrtc工作流程三、推流端四、拉流五、socket服务六、效果七、备注总结 前言 WebRTC(Web Real-Time Communication)是一种实时通讯技术,允许网…...

美国加州房价数据分析01
1.项目简介 本数据分析项目目的是分析美国加州房价数据,预测房价中值。 环境要求: ancondajupyter notebookpython3.10.10 虚拟环境: pandas 2.1.1 numpy 1.26.1 matplotlib 3.8.0 scikit-learn1.3.1 2. 导入并探索数据集 通用的数据分析…...

用Python开启人工智能之旅(四)深度学习的框架和使用方法
第四部分:深度学习的框架和使用方法 用Python开启人工智能之旅(一)Python简介与安装 用Python开启人工智能之旅(二)Python基础 用Python开启人工智能之旅(三)常用的机器学习算法与实现 用Pyt…...

两分钟解决:vscode卡在设置SSH主机,VS Code-正在本地初始化VSCode服务器
问题原因 remote-ssh还是有一些bug的,在跟新之后可能会一直加载初始化SSH主机解决方案 1.打开终端2.登录链接vscode的账号,到家目录下3.找到 .vscode-server文件,删掉这个文件4.重启 vscode 就没问题了...
信号仿真高级工程师面试题
信号仿真高级工程师面试题可能涵盖多个方面,旨在全面评估应聘者的专业知识、技能水平、实践经验和问题解决能力。以下是一些可能的面试题及其简要解析: 一、专业知识与技能 描述你对信号仿真的理解 考察点:对信号仿真基本概念、原理及应用的掌握程度。参考答案:信号仿真是…...
循环和迭代
从更高层次的思维角度来看迭代和循环的区别: 哲学层面: 迭代体现了"螺旋上升"的发展理念,每次迭代都在前一次的基础上有所提升和改进 循环体现了"周而复始"的概念,强调重复相同的过程 思维方式:…...
一个简单封装的的nodejs缓存对象
我们在日常编码中,经常会用到缓存,而一个有效的缓存管理,也是大家必不可少的工具。而nodejs没有内置专用的缓存对象,并且由于js的作用域链的原因,很多变量使用起来容易出错,如果用一个通用的缓存管理起来&a…...
【Rust自学】5.3. struct的方法(Method)
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 5.3.1. 什么是方法(Method) 方法和函数类似,也是用fn关键字进行声明,方法也有名称,也有参数ÿ…...
ChatGPT之父:奥尔特曼
奥尔特曼 阿尔特曼一般指萨姆奥尔特曼,他是OpenAI的联合创始人兼首席执行官,被称为“ChatGPT之父”.以下是其具体介绍: 个人经历 1985年4月22日出生于美国芝加哥,8岁学会编程,9岁拥有电脑,对信息技术和互联网产生兴趣.高中就读于约翰巴勒斯中学,后进入斯坦福大学主修计…...

如何在谷歌浏览器中设置桌面快捷方式
在日常使用电脑时,反复在浏览器中输入经常访问的网址不仅耗时,而且降低了工作效率。为了解决这一问题,我们可以通过在主屏幕上创建谷歌浏览器的快捷方式来简化操作。本文将详细介绍如何在Windows和Mac系统中实现这一功能。 一、步骤概述 1. …...
systemverilog中的priority if
1 基本概念 在 SystemVerilog 中,priority - if是一种条件判断结构。它和普通的if - else语句类似,但在条件评估和错误检查方面有自己的特点,主要用于按顺序评估多个条件,并且对不符合预期的情况进行报错。报错如下两点 当所有条件…...

图像处理-Ch2-空间域的图像增强
Ch2 空间域的图像增强 文章目录 Ch2 空间域的图像增强Background灰度变换函数(Gray-level Transformation)对数变换(Logarithmic)幂律变换(Power-Law)分段线性变换函数(Piecewise-Linear)对比度拉伸(Contrast-Stretching)灰度级分层(Gray-level Slicing) 直方图处理(Histogram …...
css 编写注意-1-命名约定
编写按照可维护性、性能和可读性规则: 1.代码组织与结构 层次清晰:使用模块化的结构,将样式分块组织。命名规范:采用统一的命名规则(如 BEM、SMACSS)以增强可读性。 /* BEM …...
虚幻引擎反射机制
在虚幻引擎中,反射系统是一种强大的机制,它允许开发者和引擎本身在运行时获取并操作类、对象、属性和方法的元信息。这个系统是基于UObject(Unreal Engine中所有支持反射的对象的基类)构建的,为游戏开发提供了极大的灵…...
Knife4j Swagger
1. 依赖 <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency>2. 配置 第二步配置完成就可以访问:http://localhost…...

Xcode 16 编译弹窗问题、编译通过无法,编译通过打包等问题汇总
问题1:打包的过程中不断提示 :codesign 想要访问你的钥匙串中的密钥“develop 或者distribution 证书” 解决:打开钥匙串,点击证书---显示简介---信任----改为始终信任 (记住 :不能只修改钥匙的显示简介的…...
卷积神经网络入门指南:从原理到实践
目录 1 CNN的发展历史 2 CNN的基本原理 3 CNN核心组件 3.1 卷积操作基础 3.2 卷积层详解 3.3 高级卷积操作 3.3.1 分组卷积(Group Convolution) 3.3.2 深度可分离卷积(Depthwise Separable Convolution): 3.3 池…...

eNSP安装教程(内含安装包)
通过网盘分享的文件:eNSP模拟器.zip 链接: https://pan.baidu.com/s/1wPmAr4MV8YBq3U5i3hbhzQ 提取码: tefj --来自百度网盘超级会员v1的分享 !!!!解压后有四个文件,先安装Box,第二个安装cap&a…...

VBA技术资料MF244:利用VBA在图表工作表中创建堆积条形图
我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套,分为初级、中级、高级三大部分,教程是对VBA的系统讲解&#…...

【计算机网络安全】网络攻击
实验二 网络攻击 实验人员:第五组全体成员 一、实验目的: 1:掌握ARP欺骗的原理,实践ARP欺骗的过程。 2:掌握TCP劫持的原理,实践TCP劫持的过程。 3:掌握DNS欺骗的原理,实践DN…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...