一、WebRTC的本质:实时通信的“网络协议栈”类比
将WebRTC类比为Linux网络协议栈极具洞察力,二者在架构设计和功能定位上高度相似:
分层协议栈架构
getUserMedia
、RTCPeerConnection
)暴露功能,类似Linux通过Socket接口供应用调用。原生性与系统整合
二、浏览器中的WebRTC:API接口的技术全景
WebRTC在浏览器中的实现,本质是将协议栈能力通过标准化API开放给开发者,其核心接口体系包括:
getUserMedia
:直接调用设备传感器,获取音视频流,支持参数配置(如分辨率、帧率、摄像头方向)。navigator.mediaDevices.getUserMedia({ video: { facingMode: "user" }, audio: true })
MediaRecorder
:基于WebRTC流实现客户端录制(如录制会议内容)。RTCPeerConnection
:核心接口,负责创建连接、交换SDP信令、管理ICE候选(网络地址),并通过addTrack
/ontrack
处理媒体流传输。SFU模式(Selective Forwarding Unit)深度解析:架构、原理与应用场景
一、SFU模式的核心定义与定位
SFU(选择性转发单元)是流媒体服务器的一种转发模式,其核心功能是接收多个客户端的媒体流,根据订阅关系选择性地将流转发给其他客户端,而非对媒体流进行解码、混流或重新编码。
二、SFU的技术架构与工作流程
1. 核心组件与网络模型
(典型SFU架构示意图:客户端A/B/C发布流至SFU,SFU根据订阅关系将流转发给订阅者)
关键角色:
数据流向:
2. 与MCU模式的本质区别
维度 | SFU模式 | MCU模式(Multipoint Control Unit) |
---|---|---|
媒体处理 | 不解码、不混流,仅转发原始数据包 | 解码所有流,混合成单一流(如将多路视频合成画中画) |
延迟 | 极低(仅网络传输延迟+转发处理时间) | 高(需解码、混流、重新编码,引入处理延迟) |
带宽消耗 | 发布者推1路流,订阅者接收N路流(总带宽≈发布带宽×N) | 发布者推N路流,服务器混流后推1路流(总带宽≈发布带宽×N + 混流带宽) |
计算资源 | 几乎无媒体处理消耗,适合高并发 | 需大量CPU/GPU资源,并发量受限 |
典型场景 | 实时互动直播、多人视频会议(如Zoom) | 低并发、需要画面合成的场景(如传统视频会议) |
三、SFU模式的技术优势与适用场景
1. 核心优势
低延迟特性:
高扩展性与低资源消耗:
灵活性与兼容性:
2. 典型应用场景
实时互动直播:
视频会议系统:
物联网与实时监控:
/rtc/v1/publish/
接口逻辑,解析流的元数据(如流 ID、编码格式、分辨率等) 。/rtc/v1/play
或 WebRTC 协议发起订阅请求时,SRS 依据请求中的流 ID,查询“流路由表”,确定对应的发布者流 。candidate
(候选地址),解决客户端处于 NAT 环境下的互联互通问题,让流顺利收发 。 srs 1985 控制台
(对应 URL 可查看),可视化展示 SRS 服务状态、流信息、客户端连接情况,方便运维人员监控流转发状态、排查问题 。signal1989
)配合,通过/sig/v1/rtc
等接口,处理房间创建、加入、流订阅/取消等信令逻辑,协同完成“发布 - 订阅”全流程 。比如信令通知 SRS 有新订阅者加入,SRS 即刻启动对应流的转发 。srs_player.html
播放)使用;也能将 RTMP 流转为 WebRTC 流输出,打通不同协议终端的实时通信 。信令的本质:
信令服务器核心职责是 处理设备 / 客户端之间的 “协商逻辑”,比如 WebRTC 里的 SDP 交换(协商编解码、媒体能力)、房间管理(加入 / 退出、流订阅关系)、ICE 候选交换(网络地址协商)等,让终端知道 “和谁连、怎么连、发什么流” 。
SRS 对信令的支持方式:
背景设定
join
/publish
/subscribe
等信令接口。publish
(推流)和 play
(拉流)的媒体接口。流程拆解(Step by Step)
1. CWR 加入房间(join
信令)
CWR 操作:
调用信令服务器的 join
接口,参数:roomId=1001
,userId=CWR
。
// CWR 发送给信令服务器的 join 请求
{
"action": "join",
"roomId": "1001",
"userId": "CWR"
}
信令服务器逻辑:
1001
是否存在,若不存在则创建。1001
的参与者,维护“房间 - 参与者列表”:{"1001": ["CWR"]}
。// 信令服务器返回给 CWR 的响应
{
"code": 0,
"roomId": "1001",
"participants": ["CWR"], // 目前只有自己
"streams": [] // 暂时没有流发布
}
2. CWR 发布流(publish
信令 + SRS 推流)
CWR 操作:
publish
接口,告知“要发布流”:{
"action": "publish",
"roomId": "1001",
"userId": "CWR",
"streamId": "CWR_stream" // 自定义流标识
}
publish
地址(如 webrtc://srs.server/1001/CWR_stream
)。信令服务器逻辑:
CWR_stream
”,更新房间流列表:{"1001": {"streams": ["CWR_stream"]}}
。{
"action": "stream_published",
"roomId": "1001",
"userId": "CWR",
"streamId": "CWR_stream",
"streamUrl": "webrtc://srs.server/1001/CWR_stream" // SRS 流地址
}
SRS(SFU)逻辑:
CWR_stream
)- 发布者(CWR)”的映射,等待订阅者。3. CZ 加入房间(join
信令)
CZ 操作:
调用信令服务器的 join
接口,参数:roomId=1001
,userId=CZ
。
{
"action": "join",
"roomId": "1001",
"userId": "CZ"
}
信令服务器逻辑:
1001
已存在,将 CZ 加入参与者列表:{"1001": ["CWR", "CZ"]}
。{
"code": 0,
"roomId": "1001",
"participants": ["CWR", "CZ"],
"streams": [
{
"userId": "CWR",
"streamId": "CWR_stream",
"streamUrl": "webrtc://srs.server/1001/CWR_stream"
}
]
}
{
"action": "participant_joined",
"roomId": "1001",
"userId": "CZ"
}
4. CZ 订阅 CWR 的流(subscribe
信令 + SRS 拉流)
CZ 操作:
从信令服务器返回的 streams
中,拿到 CWR 的流地址 webrtc://srs.server/1001/CWR_stream
,调用信令服务器的 subscribe
接口(或直接通过 WebRTC 发起 play
请求 ):
{
"action": "subscribe",
"roomId": "1001",
"userId": "CZ",
"streamUrl": "webrtc://srs.server/1001/CWR_stream"
}
信令服务器逻辑:
记录“CZ 订阅了 CWR 的流”,并通知 SRS 处理转发。
SRS(SFU)逻辑:
play
请求(对应 webrtc://srs.server/1001/CWR_stream
)。5. CZ 发布自己的流(publish
信令 + SRS 推流)
CZ 操作:
publish
接口,告知“要发布流”:{
"action": "publish",
"roomId": "1001",
"userId": "CZ",
"streamId": "CZ_stream"
}
publish
地址(如 webrtc://srs.server/1001/CZ_stream
)。信令服务器逻辑:
CZ_stream
”,更新房间流列表。{
"action": "stream_published",
"roomId": "1001",
"userId": "CZ",
"streamId": "CZ_stream",
"streamUrl": "webrtc://srs.server/1001/CZ_stream"
}
SRS(SFU)逻辑:
接收 CZ 推来的流,建立“流标识(CZ_stream
)- 发布者(CZ)”的映射,等待订阅者(如 CWR 会触发订阅 )。
6. CWR 订阅 CZ 的流(自动触发或信令驱动)
CWR 操作:
收到信令服务器推送的“CZ 发布流”事件后,自动调用 subscribe
接口(或直接 play
),订阅 webrtc://srs.server/1001/CZ_stream
。
SRS(SFU)逻辑:
将 CZ 的流转发给 CWR,此时 CWR 和 CZ 实现双向音视频互通(CWR 看/听 CZ,CZ 看/听 CWR )。
关键协同总结
角色 | 核心动作 | 数据/信令流向 | 作用 |
---|---|---|---|
CWR(发布者) | join → publish → 推流到 SRS |
信令服务器 ←→ CWR;SRS ← CWR(媒体流) | 初始化房间、发布本地流 |
CZ(订阅者) | join → 订阅 CWR 流 → publish → 推流 |
信令服务器 ←→ CZ;SRS ←→ CZ(媒体流) | 加入房间、订阅他人流、发布自己流 |
信令服务器 | 管理房间、同步状态、转发事件 | 信令服务器 ←→ CWR/CZ | 指挥“谁连谁、什么时候连”,不碰媒体数据 |
SRS(SFU) | 接收流、转发流(publish /play 接口) |
SRS ← 发布者(媒体流);SRS → 订阅者 | 只负责“搬流媒体数据”,依赖信令调度 |
运行命令:./objs/srs -c ./conf/rtc.conf
# WebRTC streaming config for SRS.
# @see full.conf for detail config.
listen 1935;
max_connections 1000;
daemon off;
srs_log_tank console;
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
http_api {
enabled on;
listen 1985;
}
stats {
network 0;
}
rtc_server {
enabled on;
listen 8000; # UDP port
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
candidate $CANDIDATE;
}
vhost __defaultVhost__ {
rtc {
enabled on;
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
rtmp_to_rtc off;
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
rtc_to_rtmp off;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
}
整体功能概述
该配置文件实现了一个基础的WebRTC流媒体服务,主要包含以下核心功能:
核心配置项解析
listen 1935;
max_connections 1000;
daemon off;
srs_log_tank console;
listen 1935
:启用RTMP协议监听,用于接收传统RTMP推流max_connections 1000
:限制最大客户端连接数为1000daemon off
:不以守护进程方式运行,便于调试srs_log_tank console
:日志输出到控制台http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
./objs/nginx/html
,可用于存放播放器页面等静态资源http_api {
enabled on;
listen 1985;
}
RESTful 不是 “强制标准”,而是一套让 API 更简洁、规范的设计风格,核心通过 “资源 URI + HTTP 方法” 定义接口,让系统交互清晰、易维护。像你之前提到的 SRS 的 HTTP API,若遵循 RESTful 设计,就能用GET /api/streams查列表、POST /api/streams创建流,比自定义复杂参数的接口好理解得多~
HTTPAPI:
HTTP API 虽然不直接参与媒体流的传输(由 SFU 模块负责),但它为 WebRTC 服务提供了 控制平面 的能力:
动态配置 WebRTC 参数:通过 HTTP API 修改 rtc_server 的配置(如端口、候选地址),无需重启服务。
监控 WebRTC 连接质量:实时查看 WebRTC 客户端的连接数、丢包率、带宽使用情况。
管理 WebRTC 房间 / 会话:结合业务逻辑,通过 HTTP API 创建 / 销毁 WebRTC 会话(如多人会议房间)
rtc_server {
enabled on;
listen 8000; # UDP port
candidate $CANDIDATE;
}
enabled on
:启用WebRTC服务listen 8000
:使用UDP端口8000进行WebRTC数据传输(UDP更适合低延迟实时通信)candidate $CANDIDATE
:配置ICE候选地址,$CANDIDATE
通常会在启动时被替换为服务器的公网IP或域名,用于NAT穿透vhost __defaultVhost__ {
rtc {
enabled on;
rtmp_to_rtc off;
rtc_to_rtmp off;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
}
WebRTC相关配置:
rtc.enabled on
:在默认虚拟主机上启用WebRTC功能rtmp_to_rtc off
:关闭RTMP到WebRTC的转换(不将RTMP流转换为WebRTC流)rtc_to_rtmp off
:关闭WebRTC到RTMP的转换(不将WebRTC流转换为RTMP流)HTTP-FLV配置:
http_remux.enabled on
:启用HTTP-FLV流输出mount [vhost]/[app]/[stream].flv
:定义HTTP-FLV流的访问路径格式,例如:http://server:8080/live/stream.flv
SRS 中第三方库的信令服务器是一个用 Go 语言开发的组件,位于 SRS 的3rdparty/signaling目录下,默认监听 1989 端口。它在 SRS 的 WebRTC 功能中扮演着重要角色,主要负责以下任务:
cd ~/git/srs/trunk/3rdparty/signaling && make && ./objs/signaling
#运行命令
cd ~/git/srs/trunk/3rdparty/httpx-static && make &&
./objs/httpx-static -http 80 -https 443 -ssk server.key -ssc server.crt \
-proxy http://127.0.0.1:1989/sig -proxy http://127.0.0.1:1985/rtc \
-proxy http://127.0.0.1:8080/
httpx-static 是一个基于 C++ 开发的高性能 HTTP/HTTPS 反向代理服务器,主要用于将客户端的 HTTP/HTTPS 请求转发到后端的多个目标服务(如 API 接口、其他服务器等)
一、按请求类型拆分:HTTP vs WebSocket
httpx-static
同时支持 HTTP 请求 和 WebSocket 请求 的转发,对应不同的业务场景:
1. HTTP 请求转发(常规接口、静态文件)
请求示例:
/rtc/v1/publish/
→ 转发到 srs 1985 http api
/console/ng_index.html
→ 转发到 srs 1985 控制台
/players/srs_player.html
→ 转发到 srs 8080 播放器
作用:
srs_player.html
这类播放器页面,通过 httpx-static
直接代理到 SRS 的静态文件服务器(8080 端口),客户端访问 http://your-domain/players/srs_player.html
就能加载页面。/rtc/v1/publish
是 SRS 的 HTTP API(1985 端口),用于触发流发布逻辑(如推流到 SRS)。/console/ng_index.html
是 SRS 的 Web 管理界面,通过代理统一入口,方便运维人员访问。2. WebSocket 请求转发(信令交互)
请求示例:
/sig/v1/rtc
→ 转发到 signal1989 信令服务器
(WebSocket 协议)作用:
WebRTC 信令交互(如房间创建、SDP 交换、ICE 候选协商)依赖 WebSocket 长连接,httpx-static
作为代理,将客户端的 WebSocket 请求(ws://your-domain/sig/v1/rtc
)转发到信令服务器(1989 端口),确保实时信令能稳定传输。
二、路由逻辑:“流量分发规则”
httpx-static
通过 路径匹配 决定请求转发到哪个后端:
请求路径 | 协议 | 转发目标 | 作用 |
---|---|---|---|
/rtc/v1/publish/ |
HTTP | srs 1985 http api |
调用 SRS 的流发布 API |
/console/ng_index.html |
HTTP | srs 1985 控制台 |
访问 SRS 管理界面 |
/players/srs_player.html |
HTTP | srs 8080 播放器 |
加载 SRS 播放器静态页面 |
/sig/v1/rtc |
WebSocket | signal1989 信令服务器 |
WebRTC 信令交互(房间、流协商) |
三、业务价值:为什么需要这个“中枢”?
1. 统一入口,简化客户端配置
httpx-static
的地址(如 http://your-domain
),无需关心后端多个服务的具体端口(1985、8080、1989 等)。http://your-domain/players/srs_player.html
,无需拼接 :8080
端口。ws://your-domain/sig/v1/rtc
,无需关心信令服务器的 1989 端口。2. 协议转换与兼容
httpx-static
可以将 外部 HTTPS 请求 转换为内部 HTTP 请求,反之亦然。https://your-domain
访问(更安全),httpx-static
内部转发到 SRS 的 HTTP 服务(1985、8080 等),无需后端服务单独配置 HTTPS。3. 隐藏后端细节,提升安全性
httpx-static
通信。httpx-static
代理)。4. 支持复杂业务流程
/rtc/v1/publish
告诉 SRS “我要推流”。/sig/v1/rtc
与信令服务器协商推流参数。httpx-static
统一路由。四、类比理解:把 httpx-static
当“小区门卫”
httpx-static
= 小区门卫,负责登记居民需求,然后帮居民把请求转发到对应的设施:
srs 8080 播放器
)。srs 1985 控制台
)。signal1989 信令服务器
)。这样,居民(客户端)无需记住每个设施的具体位置(端口),只需找门卫(httpx-static
)即可~
以下结合 httpx-static
代理、SRS(SFU)、信令服务器 与这张前端交互流程图,完整拆解 “用户 → 前端 → 后端服务” 的交互逻辑,理解从“进入网页”到“音视频互通”的全流程:
一、核心角色回顾
前端侧:
one2one.html
:前端页面(用户入口)。srs.sig.js
:信令交互工具库(处理 WebSocket 连接、收发信令)。srs.sdk.js
:WebRTC 工具库(处理音视频采集、发布、订阅)。后端侧:
httpx-static
:反向代理(统一入口,转发 HTTP/WebSocket 请求)。二、流程拆解(从“用户进入网页”到“音视频互通”)
1. 初始化阶段:前端加载与配置
one2one.html
页面(进入网页)。one2one.html
加载后,调用 srs.sig.js
的 SrsRtcSignalingParse
方法,解析配置:
wsHost
(WebSocket 信令地址,如 ws://your-domain/sig/v1/rtc
,实际由 httpx-static
代理)。room
(房间 ID,如 1234
)、display
(用户标识,如 user1
)等参数。2. 信令连接阶段:建立 WebSocket 通道
srs.sig.js
的 SrsRtcSignalingAsync
方法,基于 wsHost
创建 WebSocket 连接:
sig
对象(封装了 WebSocket 收发信令的能力)。sig.connect()
发起 WebSocket 连接,请求到达 httpx-static
。httpx-static
转发:
/sig/v1/rtc
,将 WebSocket 请求转发到 信令服务器(如 signal1989
)。3. 加入房间阶段:信令交互(join
)
sig.send({ action: 'join', room: '1234', display: 'user1' })
发送 join
信令。1234
是否存在,若不存在则创建。4. 发布流阶段:采集并推送音视频(publish
)
srs.sdk.js
的 new SrsRtcPublisherAsync()
方法,初始化发布器:
Publisher
对象(封装了发布流的能力)。Publisher.publish()
发起流发布:
sig.send({ action: 'publish', sdp: '...' })
发送给信令服务器。publish
信令,解析 SDP 内容。httpx-static
代理的 /rtc/v1/publish
接口)。Publisher
。5. 订阅流阶段:接收对方音视频(用户 2 同理)
1234
并发布自己的流。webrtc://your-domain/1234/user2
)。srs.sdk.js
收到事件后,调用 new SrsRtcSubscriberAsync()
初始化订阅器。6. 音视频互通阶段:媒体流传输
srs.sdk.js
渲染对方的音视频流,实现“一对一通话”。四、核心协同点
httpx-static
的“桥梁作用”:
/rtc/v1/publish
)和 WebSocket(如 /sig/v1/rtc
)请求,让前端只需访问一个域名,简化配置。信令服务器的“指挥作用”:
SRS(SFU)的“管道作用”:
前端 SDK 的“工具作用”:
srs.sig.js
封装信令交互,srs.sdk.js
封装 WebRTC 能力,让前端开发者无需关注复杂的协议细节。五、类比理解:把整个流程当“线上电话亭”
one2one.html
= 电话亭的“操作面板”(用户输入要拨打的号码)。srs.sig.js
= 电话亭的“信号线路”(负责传递“拨号、接听”等信令)。srs.sdk.js
= 电话亭的“话筒+听筒”(负责采集、播放声音/画面)。httpx-static
= 电话局的“总机”(统一转接所有通话请求)。