9.3 KiB
推流链路文档 · PC → WHIP → SRS → HLS / WHEP / FLV
默认推流链路已升级为 一路 WHIP 推流,SRS 三协议同出。本文档沉淀该链路的拓扑、配置与验证方法,便于后续维护与排查。
1. 拓扑总览
┌─→ WHEP (WebRTC 0.2-0.5s) → <video> RTCPeerConnection (watch.html: SRS WebRTC)
PC (getUserMedia) ─WHIP─→ SRS ─┼─→ HLS (5-10s 切片) → <video> hls.js / Safari原生 (watch.html: SRS HLS)
└─→ FLV (http-flv) → flv.js / 直接下载 (publish.html 展示链接)
│
└─→ 同时经 Go 网关反代,无需浏览器直连 8080
└──── 同一房间经 WatchRoom (SSE / gRPC stream) 广播分发目标 ────┘
另一条分流链路(可选,未配置不影响):
PC ─WHIP/tracks.new─→ Cloudflare Realtime SFU ─→ viewer (tracks.new location=remote)
核心结论:浏览器推流始终只走一次 WHIP(/rtc/v1/whip/?app=live&stream=<room>),SRS 在 vhost __defaultVhost__ 内自动 remux 为 HLS / FLV / WHEP,无需二次推流或转码。
| 协议 | 播放地址(经 Go 网关 8088) | SRS 源地址(8080) | 延时 | 兼容性 |
|---|---|---|---|---|
| WHEP | POST /rtc/v1/whep/?app=live&stream=xxx |
同上(经网关) | 0.2–0.5s | 需 WebRTC |
| HLS | GET /live/xxx.m3u8 + .ts |
http://srs:8080/live/xxx.m3u8 经 GET/HEAD /live/ 反代 |
5–10s(3×10s 切片) | 全端,iOS 原生 |
| FLV | GET /live/xxx.flv |
同上 | 1–2s | 需 flv.js |
当前发布页(
/publish)与观看页(/watch)已同时支持三种观看方式,发布页推流后自动显示 HLS/FLV 链接(见internal/server/static/app.js:hlsUrl)。
2. 配置清单
2.1 SRS 服务端 · deploy/srs.conf
http_server:8080 产出静态切片,http_api:1985 负责 WHIP/WHEP 信令,rtc_server:8000 负责媒体。
listen 1935;
max_connections 1000;
daemon off;
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html; # HLS 切片落盘目录,Go 网关反代至此
}
http_api {
enabled on;
listen 1985;
crossdomain on;
}
rtc_server {
enabled on;
listen 8000;
tcp { enabled on; listen 8000; }
protocol all;
candidate $CANDIDATE; # 由 SRS_CANDIDATE 注入,公网部署填公网 IP
}
vhost __defaultVhost__ {
rtc { enabled on; nack on; twcc on; }
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
hls {
enabled on;
hls_path ./objs/nginx/html; # 与 http_server.dir 一致
hls_fragment 10; # 单切片 10s,首屏约 10–20s
hls_window 60; # 窗口 60s,保留 6 片
}
}
调整建议:
- 降低
hls_fragment 5可将延时压至 5–7s,但会增加切片数与 I/O。 - 若需更低 HLS 延时,可启用 LL-HLS(SRS 6 支持
hls_ll),但前端需 ll-hls 客户端,当前未启用以保持兼容。
2.2 Docker · deploy/docker-compose.yml
services:
srs:
image: ossrs/srs:6
ports:
- "1935:1935"
- "1985:1985"
- "8080:8080"
- "8000:8000/udp"
- "8000:8000/tcp"
environment:
CANDIDATE: "${SRS_CANDIDATE:-127.0.0.1}"
volumes:
- ./srs.conf:/usr/local/srs/conf/srs.conf:ro
command: ./objs/srs -c conf/srs.conf
8080 仅需宿主机验证时直连;生产经 Go 网关反代后可不暴露公网 8080,仅保留 1985/8000 供信令与媒体。
2.3 Go 控制面 · internal/config/config.go + .env.example
| 变量 | 默认 | 说明 |
|---|---|---|
SRS_API_BASE |
http://localhost:1985 |
WHIP/WHEP 信令反代目标(srsProxyHandler) |
SRS_HTTP_BASE |
http://localhost:8080 |
HLS/FLV 静态资源反代目标(srsHlsProxyHandler,本次新增) |
SRS_APP |
live |
推流 app,决定 URL 路径 /live/<stream> |
SRS_CANDIDATE |
127.0.0.1 |
ICE candidate,容器部署填宿主机/公网 IP |
SFU_PROVIDER |
cloudflare,srs |
后端顺序,(GetConfig 返回) |
TURSO_DATABASE_URL |
file:./data/live-sfu.db?cache=shared&_journal_mode=WAL |
房间拓扑持久化(本次未改) |
2.4 反向代理 · internal/server/proxy.go + internal/server/server.go
srsProxyHandler:透传 /rtc/v1/* 至 SRS_API_BASE,可选校验 ?token=(SFU_TOKEN_REQUIRED=1 时)。
srsHlsProxyHandler(新增):
target, _ := url.Parse(cfg.SRSHttpURL) // 默认 :8080
rp := httputil.NewSingleHostReverseProxy(target)
mux.Handle("GET /live/", s.srsHlsProxy)
mux.Handle("HEAD /live/", s.srsHlsProxy)
- 统一经
http://localhost:8088/live/*.m3u8/.ts/.flv访问,避免前端直连 8080 的跨域与端口暴露。 - 自动补
Access-Control-Allow-Origin: *,iOS/桌面端<video>可直接播放。
拉流 HLS 无需
Subscribe创建 WHEP PeerConnection;观看页srs-hls模式仅用stream拼出 hlsUrl 并交由watchHLS()播放(见下)。
3. 前端链路 · internal/server/static/app.js
3.1 推流(WHIP)
POST /rtc/v1/whip/?app=live&stream=live-demo&token=<publishToken>
Content-Type: application/sdp
Body: offer.sdp → 200 answer.sdp → pc.setRemoteDescription(answer)
publishSRS():创建RTCPeerConnection→createOffer→fetch WHIP→setRemoteDescription。- 成功后
hlsLink立即显示hlsUrl(stream) = /live/<stream>.m3u8与flvUrl,日志提示“三协议同出”。
3.2 观看
- WHEP(
watchSRS):POST /rtc/v1/whep/?app=live&stream=xxx同 WHIP 流程,pc.ontrack挂<video>。 - HLS(
watchHLS,新增):hlsUrl = `/live/${stream}.m3u8` if (video.canPlayType('application/vnd.apple.mpegurl')) video.src = hlsUrl; // iOS 原生 else if (Hls.isSupported()) { hls.loadSource(hlsUrl); hls.attachMedia(video); } // hls.js 1.5.7 - 切换:观看页提供三档单选
cloudflare / srs(whep) / srs-hls,srs-hls复用BACKEND_KIND_SRS枚举,仅前端分流。
3.3 UI
publish.html:新增#hlsLink,推流后展示 HLS/FLV 超链接;引入hls.jsCDN。watch.html:新增srs-hls选项 +#hlsInfo+controls;首屏文案强调“三协议同出”。
4. 控制面与房间拓扑
Publish(room, BACKEND_KIND_SRS)→ 分配stream = live-<room>+ JWTpublishToken→hub.setTarget(room, "srs", {stream, publishToken, url})→ 广播 SSE。Subscribe(room, BACKEND_KIND_SRS)→ 取hub中stream返回,WHEP 与 HLS 共用同一stream。WatchRoom / GET /api/room/{room}/events(SSE)推送RoomEvent{targets},观看页据此自动subscribe()。
房间拓扑持久化至 Turso 嵌入式(internal/db/db.go),重启后恢复,不影响 HLS 切片(切片为临时文件,SRS 重启清空)。
5. 快速验证
# 1) 起 SRS(含 HLS)
cd deploy && SRS_CANDIDATE=127.0.0.1 docker compose up -d srs && docker logs -f live-sfu-srs
# 2) 起控制面
cd .. && go run ./cmd/server
# 日志:live-sfu demo ready: http://localhost:8088
# 3) 浏览器
# 发布: http://localhost:8088/publish?room=demo → 勾选 SRS → 开始推流
# 观看 WHEP: http://localhost:8088/watch?room=demo → 选 SRS WebRTC → 开始观看(<1s)
# 观看 HLS : 同页切 SRS HLS → 停止后重新开始观看(约 5–10s 后出画面)
# 裸 HLS: http://localhost:8088/live/live-demo.m3u8 (VLC / ffplay / curl 均可)
curl -i http://localhost:8088/live/live-demo.m3u8
curl -I http://localhost:8088/live/live-demo.flv
ffplay http://localhost:8088/live/live-demo.m3u8
排障:
m3u8 404:推流后等待 10–20s 再试;检查docker exec live-sfu-srs ls /usr/local/srs/objs/nginx/html/live/是否有切片。candidate不通:容器内ip与浏览器不在同一网段时,SRS_CANDIDATE改为宿主机公网/局域网 IP 后docker compose up -d --force-recreate。- HLS 跨域:已由
srsHlsProxyHandler统一加 CORS,若直连 8080 需自行在srs.conf加crossdomain(http_server 无此指令,建议走网关)。
6. 延时与选型建议
| 场景 | 推荐 | 理由 |
|---|---|---|
| 连麦/互动直播 | WHEP / Cloudflare Realtime | 200–500ms,可双向 |
| 单向大并发/回看友好 | HLS | 全端兼容,CDN 友好,成本最低 |
| 演示/对比 | WHEP + HLS 双出(默认) | 一路推流兼顾低延时与兼容性 |
本 Demos 默认即为双出:无需额外推流或配置,开箱获得两条观看链路。
7. 文件索引
- 服务端配置:
deploy/srs.conf、deploy/docker-compose.yml、internal/config/config.go、.env.example - 网关:
internal/server/proxy.go(srsProxyHandler/srsHlsProxyHandler)、internal/server/server.go(Handler路由)、internal/server/gateway.go、internal/server/service.go - 前端:
internal/server/static/app.js(publishSRS/watchSRS/watchHLS)、internal/server/static/publish.html、internal/server/static/watch.html - 协议:
api/live_sfu.proto(BackendKind/StreamTarget)
更新:2026-08 — 默认链路 PC→WHIP→SRS→HLS/WHEP/FLV 已启用并经网关反代,文档与代码同源。