ZLMediaKit WebRTC 使用指南:SFU(WHIP/WHEP) 与 P2P 双信令架构、HTTP API 与 [rtc] 配置全解

原创2026-10-01 14:59:121,646 阅读
文章标签:音视频直播后端

ZLMediaKit WebRTC 使用指南:SFU(WHIP/WHEP) 与 P2P 双信令架构、HTTP API 与 [rtc] 配置全解

ZLMediaKit 内置完整的 WebRTC 服务端框架,既支持基于 HTTP 标准信令的 WHIP/WHEP(SFU 中继)模式,也支持基于 WebSocket 自定义信令的 P2P 房间直连模式。本文以 webrtc/USAGE.md 为骨架,结合 server/WebApi.cpp、server/main.cpp、webrtc/WebRtcTransport.cpp、conf/config.ini 等源码,完整讲解两种模式的架构、信令 URL 格式、房间管理 API、流代理 API 以及 [rtc] 配置段,读完即可独立完成 WebRTC 拉流代理、房间管理和相关服务端调优。

一、WebRTC 双模式架构总览

ZLMediaKit 的 WebRTC 模块同时支持两种截然不同的信令与媒体转发模型,二者由 URL 参数 signaling_protocols 区分(0 或 1):

  • SFU 模式(WHIP/WHEP):媒体流全部经服务器中继,天然支持多路复用与转码,适合广播、多人会议等场景;
  • P2P 模式(WebSocket 信令):信令仍经过服务器(WebSocket + STUN + TURN 辅助),但媒体面由两端直接建立连接,降低服务器带宽压力,适合低延迟通话与私人通信。

在服务端实现上,两类连接由不同的 Session 承载:信令由 WebRtcWebcosktSignalingSession(WebSocket 信令服务器)处理,媒体面统一由 WebRtcSession(UDP/TCP)承载,STUN/TURN 由 IceSession 提供。对应启动逻辑见 server/main.cpp:

// webrtc udp服务器
if (rtcPort) { rtcSrv_udp->start<WebRtcSession>(rtcPort, listen_ip); }
if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort, listen_ip); }
// webrtc 信令服务器
if (signalingPort) { signaleSrv->start<WebRtcWebcosktSignalingSession>(signalingPort); }
if (signalSslPort) { signalsSrv->start<WebRtcWebcosktSignalSslSession>(signalSslPort); }
// STUN/TURN服务
if (icePort) { iceSrv->start<IceSession>(icePort); }
if (iceTcpPort) { iceTcpSrv->start<IceSession>(iceTcpPort); }

注意:以上所有服务均遵循“端口为 0 则不开机”的约定(详见 server/main.cpp 对 signalingPort、icePort 等配置的读取),因此默认 conf/config.ini 中这些端口为 0 时 WebRTC 相关服务不会启动。

1. SFU 模式(WHIP/WHEP)架构

WHIP(WebRTC-HTTP Ingestion Protocol)与 WHEP(WebRTC-HTTP Egress Protocol)是 IETF 标准化的 HTTP 信令协议,一个用于推流(ingest),一个用于拉流(playback)。ZLMediaKit 将其作为 SFU 模式的信令实现:

                    WebRTC SFU 模式 (WHIP/WHEP)

     推流端 (WHIP)                                          拉流端 (WHEP)
   +----------------+                                 +-----------------+
   |   Encoder      |                                 |   Player        |
   |  (Browser/ZLM) |                                 |  (Browser/ZLM)  |
   +----------------+                                 +-----------------+
          |                                               |
          | WHIP Protocol                                 | WHEP Protocol
          | (WebRTC ingest)                               | (WebRTC playback)
          |                                               |
          v                                               v
    +-------------------------------------------------------------------+
    |                          ZLMediaKit Server                        |
    +-------------------------------------------------------------------+
    - WHIP: WebRTC-HTTP Ingestion Protocol (推流)
    - WHEP: WebRTC-HTTP Egress Protocol (拉流)

媒体流统一汇聚到 ZLMediaKit 的流媒体管道(MultiMediaSourceMuxer 多协议复用),因此 WHIP 推入的流可以同时被 RTSP、RTMP、HLS 等其他协议拉取,这是 SFU 模式下“多路复用”的直接体现。

2. P2P 模式(WebSocket 信令)架构

P2P 模式基于 ZLMediaKit 自定义的 WebSocket 信令协议。客户端 A/B 与服务器之间通过 WebSocket 交换 SDP Offer/Answer 与 ICE Candidate,最终媒体直接点对点传输:

                    WebRTC P2P 模式

    客户端 A                                      客户端 B
   +------------+                                +-------------+
   | Browser/ZLM|                                | Browser/ZLM |
   +------------+                                +-------------+
        |                                               |
        | 1. 信令交换 (SDP Offer/Answer)                 |
        | 2. ICE Candidate 交换                         |
        +----------------  -----+-----------------------+
        |                       |                       |
        |           +-----------------------+           |
        |           | ZLMediaKit Server     |           |
        |           | 信令服务器 (WebSocket) |           |
        |           | STUN 服务器            |           |
        |           | TURN 服务器            |           |
        |           +-----------------------+           |
        |                                               |
        +-----------------------------------------------+
                            直接P2P连接

在 P2P 模式下,服务器承担三份职责:WebSocket 信令转发、STUN(打洞探测公网地址)、TURN(Relay 兜底转发)。当两端无法建立直连(对称 NAT 等)时,ICE 会回落到 TURN Relay,此时 portRange 端口池(conf/config.ini 中为 port_range=49152-65535)开始生效。

从源码看,P2P 模式的房间归属由 WebRtcSignalingPeer 管理:客户端通过 checkIn(peer_room_id, ...) 登记到目标房间并触发呼叫请求(sendCallRequest),离开时调用 checkOut(peer_room_id) 发送 Bye 通知,相关逻辑见 webrtc/WebRtcSignalingPeer.cpp 与 webrtc/WebRtcClient.cpp。

二、WebRTC URL 格式与信令协议参数

无论是拉流代理还是推流代理,WebRTC 源/目标 URL 都遵循统一格式,并通过 query 参数选择信令模式。

1. WHIP/WHEP 模式(SFU)——signaling_protocols=0

标准 HTTP 信令协议,为默认模式(WebRtcClient.h 中 _signaling_protocols 默认值即 WHEP_WHIP):

# HTTP
webrtc://server_host:server_port/app/stream_id?signaling_protocols=0

# HTTPS(暂未实现)
webrtcs://server_host:server_port/app/stream_id?signaling_protocols=0

2. WebSocket P2P 模式——signaling_protocols=1

基于 WebSocket 的自定义信令协议,需额外指定目标房间:

# WebSocket
webrtc://signaling_server_host:signaling_server_port/app/stream_id?signaling_protocols=1&peer_room_id=target_room_id

# WebSocket Secure(暂未实现)
webrtcs://signaling_server_host:signaling_server_port/app/stream_id?signaling_protocols=1&peer_room_id=target_room_id

注意:在 curl -d 表单提交中,& 必须做 URL 编码(%26),否则会被 shell 或表单解析器误认为多个参数,详见下文请求示例。

3. 参数对照表

参数 取值 含义 说明
signaling_protocols 0 WHIP/WHEP 模式(默认) 基于 HTTP 的标准 WebRTC 信令;SFU 场景,适合广播与多人会议
signaling_protocols 1 WebSocket P2P 模式 基于 WebSocket 的自定义信令;点对点直连,适合低延迟通话与私人通信
peer_room_id 任意字符串 P2P 目标房间 ID 仅 P2P 模式需要,对应 WebRtcSignalingPeer::checkIn 的 peer_room_id 入参

从源码角度印证:URL 解析位于 webrtc/WebRtcClient.cpp,客户端读取 peer_room_id 与 signaling_protocols 后,在 webrtc/WebRtcClient.cpp 中按协议类型分发到 WEBSOCKET 或 WHEP_WHIP 分支,未识别的协议值直接抛出 not support signaling_protocols 异常。

三、WebRTC 房间管理 HTTP API

P2P 模式依赖“房间保持器(Room Keeper)”把本地房间注册到远端信令服务器。相关 API 统一挂在 ZLMediaKit 的 HTTP API 体系下(实现在 server/WebApi.cpp,均要求 secret 鉴权,即 CHECK_SECRET())。

1. 添加房间保持器

POST /index/api/addWebrtcRoomKeeper

在指定信令服务器上维持一个房间连接,注册后信令服务器会对 room_id 做唯一性检查。请求参数:

参数 说明
secret 接口访问密钥
server_host 信令服务器主机地址
server_port 信令服务器端口
room_id 房间 ID,信令服务器对该 ID 进行唯一性检查

调用链:addWebrtcRoomKeeper(server_host, server_port, room_id, ssl) → 通过 WebRtcSignalingPeer 建立 WebSocket 连接并注册房间。成功返回 data.room_key(房间保持器唯一标识),该值在删除接口中要用到。

2. 删除房间保持器

POST /index/api/delWebrtcRoomKeeper

删除指定信令服务器上的房间保持器。请求参数:

参数 说明
secret 接口访问密钥
room_key 房间保持器的唯一标识符(由添加接口返回)

3. 列出所有房间保持器

POST /index/api/listWebrtcRoomKeepers

请求参数仅 secret。返回每个房间保持器对应 WebRtcSignalingPeer 的 JSON 序列化信息(ToJson(p)),并附加 room_key 字段。

4. 列出活跃的 WebRTC 房间会话

POST /index/api/listWebrtcRooms

请求参数仅 secret。枚举当前所有活跃的 WebRtcSignalingSession Peer 会话,返回项附加 room_id 字段。会话对象维护于 WebRtcSignalingSession 的全局注册表中(见 webrtc/WebRtcSignalingSession.cpp)。

5. 查询 WebRTC 代理播放器信息

POST /index/api/getWebrtcProxyPlayerInfo

获取 WebRTC 代理播放器的连接信息和状态。请求参数:

参数 说明
secret 接口访问密钥
key 代理播放器标识符

实现要点(server/WebApi.cpp):通过 key 找到 WebRtcProxyPlayerImp 并取其 WebRtcTransport,异步调用 getTransportInfo 返回 ICE 候选、连接状态等传输信息;若 key 不存在返回 NotFound,若代理不是 WebRTC 类型返回 OtherFailed。

四、WebRTC 拉流与推流代理 API

ZLMediaKit 复用标准的流代理接口(addStreamProxy / addStreamPusherProxy)创建 WebRTC 拉流与推流,两种信令模式均可通过 URL 参数切换。

1. 创建 WebRTC 拉流代理

POST /index/api/addStreamProxy
参数 说明
secret 接口访问密钥
vhost 虚拟主机名,默认为 __defaultVhost__
app 应用名
stream 流 ID
url WebRTC 源 URL(支持上述两种格式)

WHIP/WHEP 模式拉流示例:

curl -X POST "http://127.0.0.1/index/api/addStreamProxy" \
  -d "secret=your_secret" \
  -d "vhost=__defaultVhost__" \
  -d "app=live" \
  -d "stream=test" \
  -d "url=webrtc://source.server.com:80/live/source_stream?signaling_protocols=0"

P2P 模式拉流示例(注意 & 编码为 %26):

curl -X POST "http://127.0.0.1/index/api/addStreamProxy" \
  -d "secret=your_secret" \
  -d "vhost=__defaultVhost__" \
  -d "app=live" \
  -d "stream=test" \
  -d "url=webrtc://signaling.server.com:3000/live/source_stream??signaling_protocols=1%26peer_room_id=target_room_id"

拉流代理在服务端对应 WebRtcProxyPlayer(继承 PlayerProxy 体系),通过 WebRtcPlayer 内部创建 WebRtcTransport 并完成信令握手,最终把 WebRTC 流接入本地媒体管道。

2. 创建 WebRTC 推流代理(暂未实现)

POST /index/api/addStreamPusherProxy

注意:该接口当前尚未实现(USAGE.md 明确标注“暂未实现”,也列入文末未实现功能清单),此处仅给出约定中的接口契约与参数,供后续版本参考:

参数 说明
secret 接口访问密钥
schema 源流协议(如 rtmp、rtsp、hls 等)
vhost 虚拟主机名
app 应用名
stream 源流 ID
dst_url WebRTC 目标推流 URL

约定中的 WebRTC 推流 URL 格式与拉流一致:

  1. WHIP 模式(SFU)——推流到支持 WHIP 的服务器:

    # HTTP
    webrtc://target_server:port/app/stream_id?signaling_protocols=0
    
    # HTTPS(暂未实现)
    webrtcs://target_server:port/app/stream_id?signaling_protocols=0
    
  2. WebSocket P2P 模式——推流到 P2P 房间:

    # WebSocket
    webrtc://signaling_server:port/app/stream_id?signaling_protocols=1&peer_room_id=target_room
    # WebSocket Secure
    webrtcs://signaling_server:port/app/stream_id?signaling_protocols=1&peer_room_id=target_room
    

约定的请求示例:

# 将RTSP流推送到WHIP服务器
curl -X POST "http://127.0.0.1/index/api/addStreamPusherProxy" \
  -d "secret=your_secret" \
  -d "schema=rtsp" \
  -d "vhost=__defaultVhost__" \
  -d "app=live" \
  -d "stream=test" \
  -d "dst_url=webrtc://target.server.com:80/live/target_stream?signaling_protocols=0"

# 将RTSP流推送到P2P房间
curl -X POST "http://127.0.0.1/index/api/addStreamPusherProxy" \
  -d "secret=your_secret" \
  -d "schema=rtsp" \
  -d "vhost=__defaultVhost__" \
  -d "app=live" \
  -d "stream=test" \
  -d "dst_url=webrtc://signaling.server.com:3000/live/room_stream?signaling_protocols=1%26peer_room_id=target_room_id"

五、[rtc] 配置段详解

WebRTC 相关配置全部位于 config.ini 的 <a href="https://link.gitcode.com/i/5ef5a7d8e7adf76a572c9ef0b606411c" target="_blank">rtc] 段。以下配置来自 [conf/config.ini,并交叉核对 webrtc/WebRtcTransport.cpp 中 Rtc 命名空间的默认值与字段定义:

[rtc]
#webrtc 信令服务器端口
signalingPort=3000
#STUN/TURN服务器端口
icePort=3478
#STUN/TURN端口是否使能TURN服务
enableTurn=1

#TURN服务分配端口池
portRange=50000-65000

#ICE传输策略:0=不限制(默认),1=仅支持Relay转发,2=仅支持P2P直连
iceTransportPolicy=0

#UDP ICE会话空闲超时时间,单位秒,0为关闭
iceSessionTimeoutSec=60

#STUN/TURN 服务Ice密码
iceUfrag=ZLMediaKit
icePwd=ZLMediaKit

各配置项说明与源码对应

配置项 说明 源码默认值
signalingPort WebRTC 信令服务器(WebSocket)监听端口 0(源码默认关闭,见 WebRtcTransport.cpp)
icePort STUN/TURN 服务器 UDP 端口 0(默认关闭)
enableTurn STUN/TURN 端口是否使能 TURN 服务;1 开启 1
portRange TURN 服务分配端口池 仓库 config.ini 中为 port_range=49152-65535
iceTransportPolicy ICE 传输策略:0 不限制(默认)、1 仅 Relay 转发、2 仅 P2P 直连 0
iceSessionTimeoutSec UDP ICE 会话空闲超时(秒),0 关闭 60
iceUfrag / icePwd STUN/TURN 服务 ICE 凭证 ZLMediaKit / ZLMediaKit

源码层面的深层解读

  • 配置注册与默认值:Rtc 命名空间在 webrtc/WebRtcTransport.cpp 通过 onceToken 统一注册上述所有字段的默认值,例如 kSignalingPort = 0、kIcePort = 0、kEnableTurn = 1、kIceTransportPolicy = 0、kIceSessionTimeoutSec = 60、kIceUfrag = kIcePwd = "ZLMediaKit"。这意味着即使 config.ini 未显式配置,进程内也有一套可用的兜底值。
  • 端口 0 即关闭:服务端读取 signalingPort、icePort 后仅在非 0 时启动对应服务(server/main.cpp)。因此生产环境必须显式把端口配为非 0,否则 WebRTC 信令与 STUN/TURN 服务不会监听。
  • ICE 凭证的下发:WebRtcSignalingSession 在生成 ice_servers(STUN/TURN 服务器列表)时读取 icePort、iceUfrag、icePwd,并拼接 turn:// 或 stun:// URL 下发给客户端(webrtc/WebRtcSignalingSession.cpp);IceSession 则用同一份 ufrag/pwd 校验入站 STUN 请求(webrtc/IceSession.cpp)。修改凭证时务必保持两端一致。
  • 其它相关配置:[rtc] 段还有 port(WebRTC 单端口 UDP 服务器,默认 8000)、tcpPort、signalingSslPort、iceTcpPort、timeoutSec(RTP/RTCP 接收超时,默认 15 秒)、externIP(服务器外网 IP,用于生成正确的 ICE 候选)等,均可在 conf/config.ini 与 webrtc/WebRtcTransport.cpp 中查阅。

六、防火墙与部署注意事项

WebRTC 依赖大量 UDP 端口,部署时务必在防火墙/安全组中放行:

  1. 信令端口:3000(默认,对应 signalingPort),供 WebSocket 信令连接;
  2. STUN/TURN 端口:3478(默认,对应 icePort),供 STUN 打洞探测与 TURN 分配请求;
  3. TURN Alloc 端口范围:50000-65000(默认,对应 portRange),TURN Relay 实际转发媒体所用的动态端口池;仓库 conf/config.ini 中该项名为 port_range=49152-65535,以当前仓库实际配置为准。

此外还应注意:

  • 媒体端口:WebRTC 媒体默认由 [rtc] port(UDP,默认 8000)与 tcpPort 承载,需一并放行;
  • 外网部署:服务器在 NAT 后时需配置 externIP,否则 ICE 候选中的地址是内网地址,客户端无法连通;
  • P2P 打洞失败兜底:若双方 NAT 类型无法打洞成功,媒体会经 TURN Relay 转发,此时 TURN 端口池的容量直接决定并发上限。

七、参考示例与未实现功能清单

官方参考示例

USAGE.md 提供了一个基于 libwebrtc 实现的 P2P 代理拉流简单示例(zlm_peerconnection),可用于快速验证 P2P 模式的信令流程与房间机制。仓库内的 webrtc/ 目录还包含 WebRtcPlayer、WebRtcPusher、WebRtcProxyPlayer、WebRtcProxyPusher、WebRtcTalk、WebRtcEchoTest 等完整客户端/服务端实现,www/webrtc/ZLMRTCClient.js 提供了浏览器端 JavaScript 客户端,可作为二次开发的起点。

暂未实现的功能

当前版本(以仓库实际代码为准)以下功能尚不支持,请勿在文档中当作可用能力:

  • WebRTC 信令服务的安全校验(WebSocket 信令缺少鉴权);
  • 自定义外部 STUN/TURN 服务器的配置(当前 STUN/TURN 由 ZLMediaKit 内置提供);
  • WebRTC 代理推流(addStreamPusherProxy 接口尚未实现);
  • HTTPS/WSS 信令(URL 中的 webrtcs:// 标记为“暂未实现”)。

八、总结

ZLMediaKit 的 WebRTC 能力围绕“双信令模式”展开:signaling_protocols=0 走标准 WHIP/WHEP HTTP 信令实现 SFU 中继,signaling_protocols=1 走 WebSocket 自定义信令实现 P2P 房间直连;通过 addStreamProxy、房间管理系列 API 与 [rtc] 配置段,可以组合出广播、会议、点对点通话等多种拓扑。部署时牢记三点:信令端口、STUN/TURN 端口与 TURN 端口池必须放行;端口为 0 的服务不会启动;外网环境务必配置 externIP。如需深入源码,建议从 server/main.cpp(服务启动)、webrtc/WebRtcSignalingPeer.cpp(房间信令)、webrtc/WebRtcClient.cpp(URL 解析与协议分发)三个文件入手。

登录后查看全文
ZLMediaKit