live-sfu-demo/README.md

108 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 直播 SFU 分流 Demo(Cloudflare Realtime · SRS)
参考 GOSpeak 技术栈的直播 SFU 分流演示。控制面用 **protobuf / gRPC** 定义,媒体面走
**Cloudflare Realtime**(主 SFU)与 **SRS**(本地对照)的 WebRTC 扇出。
- 一路推流 → SFU 扇出 → 多路拉流。
- 同一路发布可同时落到 **Cloudflare Realtime** 与 **SRS** 两条分发链路(分流)。
- 浏览器经服务端反向代理直连 SFU,凭证不出服务端。
- **默认 DB 使用 Turso 嵌入式 (libSQL file)**:房间分发拓扑持久化到本地文件。
## 目录结构
```
app/live-sfu-demo/
api/live_sfu.proto # protobuf 控制面契约
gen/ # buf generate 产出(Go)
internal/
config/ # 运行参数(对齐 GOSpeak 的 env 布局)
db/ # Turso 嵌入式 (libSQL file) 持久化
sfu/cloudflare/ # Cloudflare Realtime REST 客户端 + Provider
sfu/srs/ # SRS Provider
server/ # gRPC 服务 + JSON 网关 + SRS/CF 媒体反代 + 房间扇出状态
static/ # 浏览器 UI(发布 / 观看 / 分发面板)
cmd/server/main.go
deploy/ # docker-compose.yml + srs.conf
```
## 运行(SRS 链路,开箱即跑)
```bash
# 1) 起本地 SRS(WHIP/WHEP/HLS 三协议后端,见 docs/streaming-pipeline.md)
cd app/live-sfu-demo/deploy && SRS_CANDIDATE=127.0.0.1 docker compose up -d srs
# 2) 起 Demo 控制面(默认 Turso 嵌入式,无需额外配置)
cd app/live-sfu-demo
cp .env.example .env
go run ./cmd/server
# 3) 打开浏览器
# 发布: http://localhost:8088/publish?room=demo
# 观看: http://localhost:8088/watch?room=demo
```
两个标签页用同一房间名即可配对。发布页勾选的分发后端会在「分发状态」中实时显示。
## 默认 DB:Turso 嵌入式
房间的“分发目标”拓扑(`stream_targets`)默认持久化到 **Turso 嵌入式**(`github.com/tursodatabase/go-libsql` 的 SQLite 兼容 file 模式),而非纯内存。
- **DSN**:`TURSO_DATABASE_URL=file:./data/live-sfu.db?cache=shared&_journal_mode=WAL`(默认)
- 兼容 `DATABASE_URL` 覆盖
- 仅支持嵌入 file: / :memory:,拒绝 libsql:// 远程(嵌入适配专注单机持久化)
- 自动建表 `(stream_targets, rooms)`,`SetMaxOpenConns(1)` 适配 SQLite 单写模型
- 重启后自动 `LoadAll` 恢复房间
```bash
# 默认即嵌入文件
TURSO_DATABASE_URL=file:./data/live-sfu.db?cache=shared&_journal_mode=WAL
# 内存(测试)
TURSO_DATABASE_URL=file::memory:?cache=shared
# 验证持久化:发布后重启,/api/rooms 仍在
curl http://localhost:8088/api/rooms | jq
sqlite3 data/live-sfu.db "select room, backend, stream from stream_targets;"
```
## 运行(Cloudflare Realtime 链路,主 SFU)
在 `.env` 填入 Cloudflare Realtime 凭证后,`go run ./cmd/server` 即启用主 SFU:
```
CF_APP_ID=xxxxxxxxxxxx
CF_APP_SECRET=xxxxxxxxxxxx
```
- 发布:服务端用 `CF_APP_SECRET` 创建 Cloudflare session,浏览器只交换 SDP(`/api/cf/...` 反代注入 Bearer)。
- 观看:服务端为观众创建 viewer session,并订阅发布者 session 的轨道(`location=remote`)。
Cloudflare 未配置时,UI 会标注「未配置」,SRS 链路不受影响。
## protobuf / gRPC
```bash
# 修改 api/live_sfu.proto 后重新生成
export PATH="$HOME/go/bin:$PATH"
buf generate api
```
契约(`LiveSFU` 服务):`GetConfig` / `ListRooms` / `Publish` / `Subscribe` / `StopStream` /
`WatchRoom`(服务端流式推送房间分发拓扑)。gRPC 监听 `GRPC_PORT`(默认 9090),浏览器走同端口
的 JSON 网关(`protojson`)。
## 文档
* 推流链路详解(PC → WHIP → SRS → HLS/WHEP/FLV):[`docs/streaming-pipeline.md`](docs/streaming-pipeline.md)
## 分流拓扑
```
publisher ──WHIP/tracks.new──▶ Cloudflare Realtime SFU ──▶ viewer(s)
└────WHIP────────────▶ SRS SFU ─┬─▶ WHEP viewer(s) (低延时 0.2-0.5s)
├─▶ HLS viewer(s) (PC→WHIP→SRS→HLS 延时 5-10s,全端兼容)
└─▶ FLV viewer(s)
一路 WHIP 推流,SRS 自动 remux 三协议同出;房间分发目标由 WatchRoom 实时广播,观众任选后端拉流
```
> 详见 [`docs/streaming-pipeline.md`](docs/streaming-pipeline.md) — 默认已开启 HLS,`Go` 网关反代 `/live/*.m3u8`,前端 iOS 原生 / hls.js 双兼容。