WebRTC 初探
前言
项目中有局域网投屏与文件传输的需求,所以研究了一下 webRTC,这里记录一下学习过程。
WebRTC 基本流程以及概念
下面以 1 对 1 音视频实时通话案例介绍 WebRTC 的基本流程以及概念
WebRTC 中的角色
-
WebRTC 终端,负责音视频采集、编解码、NAT 穿越、音视频数据传输
-
Signal 服务器,负责信令处理,如加入房间、离开房间、媒体协商消息的传递等。
-
STUN/TURN 服务器,负责获取 WebRTC 终端在公网的 IP 地址,以及 NAT 穿越失败后的数据中转。
媒体协商
SDP(Session Description Protocal): 用文本描述的各端(PC 端、Mac 端、Android 端、iOS 端等)的能力。
这里的能力指的是各端所支持的音频编解码器是什么,这些编解码器设定的参数是什么,使用的传输协议是什么,以及包括的音视频媒体是什么
媒体协商流程图
-
呼叫方创建 Offer 类型的 SDP 消息。创建完成后,调用 setLocalDescriptoin 方法将该 Offer 保存到本地 Local 域,然后通过信令将 Offer 发送给被呼叫方。
-
被呼叫方收到 Offer 类型的 SDP 消息后,调用 setRemoteDescription 方法将 Offer 保存到它的 Remote 域。作为应答,被呼叫方要创建 Answer 类型的 SDP 消息,
-
Answer 消息创建成功后,再调用 setLocalDescription 方法将 Answer 类型的 SDP 消息保存到本地的 Local 域。最后,被呼叫方将 Answer 消息通过信令发送给呼叫方。
-
呼叫方收到 Answer 类型的消息后,调用 RTCPeerConnecton 对象的 setRemoteDescription 方法,将 Answer 保存到它的 Remote 域
具体的媒体协商的过程是 WebRTC 内部自己去实现的, 作为开发者只需要记住本地的 SDP 和远端的 SDP 都设置好后,协商就算成功了.
紧接着在 WebRTC 底层会收集 Candidate,并进行连通性检测,最终在通话双方之间建立起一条链路来。
WebRTC 的连接 与 ICE Candidate(重点)
WebRTC 之间建立连接的过程是非常复杂的, 主要的原因在于它内部的实现既要考虑传输的高效性,又要保证端与端之间的连通率。
当同时存在多个有效连接时,它首先选择传输质量最好的线路,如能用内网连通就不用公网。另外,如果尝试了很多线路都连通不了,那么它还会使用服务端中继的方式让双方连通
WebRTC 是如何做到的? 答案是采用多个 Candicate 排序并执行连通性测试的方式
ICE Candidate
它表示 WebRTC 与远端通信时使用的协议、IP 地址和端口, 一般有以下字段
其中:
-
host 表示 本机候选者
-
srflx 表示内网主机映射的外网的地址和端口(通过 TURN 服务器, 需要在 PeerConnectionConfig 里面去配)
-
relay 表示中继候选者 (通过 TURN 服务器, 需要在 PeerConnectionConfig 里面去配),
假设 A 和 B 都有多个 candidate, 那么 WebRTC 会按照host->srflx→relay的方式进行联通性测试, 选择最合适的方案
作为开发者, 只需要做两件事情:
1.部署相应的 TURN 服务器和 STUN 服务器(有现成的), 初始化 peerConnection 对象的时候配置 iceServers
- 监听 oniceccandidate 方法, 每接收到一个 candidate, 就通过信令服务器发送给另外一方即可
当我们没有设置 TURN 服务器或者 STUN 服务器的时候, 两个端建立起了连接, 就可以判断两个端位于同一个局域网内
STUN 服务器与 NAT 穿透
如果两台主机不在同一个内网, WebRTC 将尝试 NAT 打洞,即 P2P 穿越。WebRTC 将 NAT 分类为 4 种类型
-
完全锥型
-
NAT IP 限制型
-
NAT 端口限制型
-
NAT 对称型 NAT
具体的穿透逻辑也是对开发者屏蔽的, 但基本的思路是: 在公网上架设一台服务器,并向这台服务器发个请求, 该服务器往响应中塞入公网 IP , 这样客户端就可以知道自己的公网 IP
WebRTC 兼容性
从 can-i-use 中可以看到浏览器的支持情况还是相对乐观的
但是直接使用 webrtc 原生的 API 还是比较麻烦的, 虽然浏览器支持了 WebRTC, 但是各大浏览器内部的实现方式还是有差异, 需要对应的 pollyfill 方案
目前只有 adaptor.js 可以选择, 可以官方推荐的方案
基于原生 webrtc 的调用流程图
从上面的描述可以看到, 基于原生 WebRTC 去实现投屏功能的话还是比较麻烦的,
特别是 SDP 交换(createOffer 及 createAnswer)、网络候选信息收集(ICE candidate)
Peerjs
peerjs 简化了 webrtc 的开发过程,把 SDP 交换、ICE candidate 这些偏底层的细节都做了封装,开发人员只需要关注应用本身就行了。
peerjs 的核心对象 Peer,它有几个常用方法:
- peer.connect 创建点对点的连接
- peer.call 向另 1 个 peer 端发起音视频实时通信
- peer.on 对各种事件的监控回调
- peer.disconnect 断开连接
- peer.reconnect 重新连接
- peer.destroy 销毁对象
另外还有二个重要对象 DataConnection、MediaConnection,其中:
- DataConnection 用于收发数据(对应于 webrtc 中的 DataChannel),它的所有方法中有一个重要的 send 方法,用于向另一个 peer 端发送数据;
- MediaConnection 用于处理媒体流,它有一个重要的 stream 属性,表示关联的媒体流。
peerjs 内置了一套信令服务器, peer-server, 开发者可以自己部署, 不指定的情况下会使用 peerjs 官方托管的信令服务器
总结:
- 相比原生的方案, api 简化, 开发者关注底层的细节少了
- 提供了 peer-server 可供部署, 官方也提供免费托管的 peerServer
- 封装程度比较高, 后续改造会比较麻烦
Simple-peer
与 peerjs 类似, 把 SDP 交换、ICE candidate 这些偏底层的细节都做了封装, 但是封装程度没有 peerjs 那么高, 提供了一定的灵活性:
peer.signal(data): 发送信令
peer.send(data): 发送 data
peer.addStream(stream): 添加音视频流
peer.removeStream(stream): 移除音视频流
peer.addTrack(track, stream)
peer.removeTrack(track, stream)
peer.destroy([err]): 销毁实例
peer.on(): 监听各种事件
相比 peerjs, simple-peerjs 自己并不提供 peerServer 作为信令服务器, 而是提供了一个 signal 事件
peer.on("signal", (data) => {// when peer1 has signaling data, give it to peer2 somehow// 在这里使用IM发送信令
});
总结:
- 一定程度的封装, 相比原生不需要写很多冗余的样板代码,但也意味着一个高级功能的实现需要自己来实现(peerjs 有文件传输相关的实现)
- 相比 peerjs, 不依赖 peerServer, 可以接入我们的 IM
- 总代码不到一千行, 且不依赖 server 代码, 改造起来比较容易
一些注意事项
https 限制
由于浏览器的安全限制, 本地开发的时候需要使用 localhost 访问, 或者配置 https 证书, 不然无法拿到 navigator.mediaDevices 对象
参考
-
webrtc-samples
-
google webrtc
-
Peerjs
-
Simple-peer
-
从 0 打造音视频直播系统
本文首发于个人博客前端开发笔记,由于笔者能力有限,文章难免有疏漏之处,欢迎指正
相关文章:

WebRTC 初探
前言 项目中有局域网投屏与文件传输的需求,所以研究了一下 webRTC,这里记录一下学习过程。 WebRTC 基本流程以及概念 下面以 1 对 1 音视频实时通话案例介绍 WebRTC 的基本流程以及概念 WebRTC 中的角色 WebRTC 终端,负责音视频采集、编解码、NAT 穿…...
Python:read,readline和readlines的区别
在Python中,read(), readline(), 和 readlines() 是文件操作中常用的三个方法,它们都用于从文件中读取数据,但各自的使用方式和适用场景有所不同。 read() 方法: read(size-1) 方法用于从文件中读取指定数量的字符。如果指定了si…...
重生之我学编程
编程小白如何成为大神?大学新生的最佳入门攻略 编程已成为当代大学生的必备技能,但面对众多编程语言和学习资源,新生们常常感到迷茫。如何选择适合自己的编程语言?如何制定有效的学习计划?如何避免常见的学习陷阱&…...

如何将PostgreSQL的数据实时迁移到SelectDB?
PostgreSQL 作为一个开源且功能强大的关系型数据库管理系统,在 OLTP 系统中得到了广泛应用。很多企业利用其卓越的性能和灵活的架构,应对高并发事务、快速响应等需求。 然而对于 OLAP 场景,PostgreSQL 可能并不是最佳选择。 为了实现庞大规…...

关于c语言的const 指针
const * type A 指向的数据是常量 如上所示,运行结果如下,通过解引用的方式,改变了data的值 const type * A 位置是常量,不能修改 运行结果如下 type const * A 指针是个常量,指向的值可以改变 如上所示,…...

万能门店小程序开发平台功能源码系统 带完整的安装代码包以及安装搭建教程
互联网技术的迅猛发展和用户对于便捷性需求的不断提高,小程序以其轻量、快捷、无需安装的特点,成为了众多商家和开发者关注的焦点。为满足广大商家对于门店线上化、智能化管理的需求,小编给大家分享一款“万能门店小程序开发平台功能源码系统…...

C#初级——字典Dictionary
字典 字典是C#中的一种集合,它存储键值对,并且每个键与一个值相关联。 创建字典 Dictionary<键的类型, 值的类型> 字典名字 new Dictionary<键的类型, 值的类型>(); Dictionary<int, string> dicStudent new Dictionary<int, str…...

git版本控制的底层实现
目录 前言 核心概念串讲 底层存储形式探测 本地仓库的详细解析 提交与分支的深入解析 几个问题的深入探讨 前言 Git的重要性 Git是一个开源的版本控制工具,广泛用于编程开发领域。它极大地提高了研发团队的开发协作效率。对于开发者来说,Git是一个…...
深入解析数据处理的技术与实践
欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: 工💗重💗hao💗:野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、应用领域等内容。 ⭐…...
python-调用c#代码
环境: win10,net framework 4,python3.9 镜像: C#-使用IronPython调用python代码_ironpython wpf-CSDN博客 https://blog.csdn.net/pxy7896/article/details/119929434 目录 hello word不接收参数接收参数 其他例子 hello word 不…...

构建铁路安全防线:EasyCVR视频+AI智能分析赋能铁路上道作业高效监管
一、方案背景 随着我国铁路特别是高速铁路的快速发展,铁路运营里程不断增加,铁路沿线的安全环境对保障铁路运输的安全畅通及人民群众的生命财产安全具有至关重要的作用。铁路沿线安全环境复杂多变,涉及多种风险因素,如人员入侵、…...

openai command not found (mac)
题意:mac 系统上无法识别 openai 的命令 问题背景: Im trying to follow the fine tuning guide for Openai here. 我正在尝试遵循 OpenAI 的微调指南 I ran: 我运行以下命令 pip install --upgrade openaiWhich install without any errors.…...

鸿蒙(API 12 Beta2版)NDK开发【LLDB高性能调试器】调试和性能分析
概述 LLDB(Low Level Debugger)是新一代高性能调试器。 当前HarmonyOS中的LLDB工具是在[llvm15.0.4]基础上适配演进出来的工具,是HUAWEI DevEco Studio工具中默认的调试器,支持调试C和C应用。 工具获取 可通过HUAWEI DevEco S…...

HAL库源码移植与使用之DMA
内存到内存不支持传输计数器自动重装 结构: 与DMA具有连线的外设都可以完成搬运 DMA触发源 DMA优先级分配 由仲裁器来决定 寄存器作用: DMA.C #include "./BSP/DMA/dma.h" #include "./SYSTEM/delay/delay.h"DMA_HandleTypeDef…...

Scrapy爬虫框架介绍、创建Scrapy项目
Scrapy官网:https://scrapy.org/ 什么是Scrapy Scrapy 是一个基于 Python 的快速的高级网页抓取和网页爬取框架,用于抓取网站并从其页面中提取结构化数据。它可用于多种用途,从数据挖掘到监控和自动化测试。 Scrapy核心组件 1. Scrapy Engin…...
如何监测某个进程是否退出(C++)?
使用WaitForSingleObject函数,可以判断进程是否退出。 WaitForSingleObject函数的作用是:等待直到指定的对象处于信号状态(通知状态)或到达指定的等待时间(超时时间)。 函数声明如下: 1 DWOR…...
Python:Neo 库读取 ABF 文件,数据格式详解
Neo 库读取 ABF 文件后的数据格式 neo 是一个用于处理电生理数据的 Python 库,支持多种数据格式,包括 ABF 文件。了解 neo 读入 ABF 文件后的数据结构非常重要,以下给大家介绍一下使用 neo 读取 ABF 文件,及其对象格式。 1. ABF…...

【Linux】网络基础_3
文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础 5. socket编程 socket 常见API // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服…...

C++之从C过渡(上)
C之从C过渡 前言 暂时告别C语言,我们走进C。对于有C语言基础,初学C的我们来说,在正式学习C的主体内容之前,我们需要先有一个过渡,本文中会总结过渡需要了解的零散知识,主要是语法。 正文 C的第一个程序 …...
MongoDB 100问
基础问题 1. 什么是MongoDB? MongoDB是一种面向文档的NoSQL数据库,使用BSON(二进制JSON)格式存储数据。它支持动态模式设计,具有高性能、高可用性和易扩展性。 2. MongoDB和传统关系型数据库的区别是什么?…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...