live-sfu-demo/docs/streaming-pipeline.md

9.3 KiB
Raw Blame History

SyncLive 推流链路文档 · 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/sync-live.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.js CDN。
  • watch.html:新增 srs-hls 选项 + #hlsInfo + controls;首屏文案强调“三协议同出”。

4. 控制面与房间拓扑

  • Publish(room, BACKEND_KIND_SRS) → 分配 stream = live-<room> + JWT publishToken → 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 sync-live-srs

# 2) 起控制面
cd .. && go run ./cmd/server
# 日志:sync-live ready: http://localhost:8088

# 3) 浏览器
# 发布: http://localhost:8088/publish?room=<房间名>  → 勾选 SRS → 开始推流
# 观看 WHEP: http://localhost:8088/watch?room=<房间名> → 选 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 sync-live-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 双出(默认) 一路推流兼顾低延时与兼容性

默认即为双出:无需额外推流或配置,开箱获得两条观看链路。


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/sync_live.proto(BackendKind / StreamTarget)

更新:2026-08 — 默认链路 PC→WHIP→SRS→HLS/WHEP/FLV 已启用并经网关反代,文档与代码同源。