152 lines
5.8 KiB
Markdown
152 lines
5.8 KiB
Markdown
# SyncLive — 同步直播 · SFU 分流服务(对标 SyncTV)
|
||
|
||
> **对标 SyncTV**:SyncTV 是 *Sync + TV*(一起看),**SyncLive 是 *Sync + Live*(一起播·一起看直播)**。本项目是 SyncTV 理念在直播场景的姐妹实现:一路推流,多端同步扇出。
|
||
|
||
基于 **SRS 主干 + 多路分发** 的同步直播服务。控制面以 **protobuf / gRPC** 定义,媒体面 SRS 为唯一推流入口。
|
||
|
||
- 推流仅进入 SRS 主干(WHIP),由服务端按配置扇出到多路分发。
|
||
- 分发方式:**SRS HLS 直推**(remux 原地出 HLS/FLV)+ **Cloudflare Realtime SFU**(低延时)+ **第三方直播 CDN**(RTMP 转推)。
|
||
- 浏览器经服务端反向代理直连 SFU,凭证不出服务端。
|
||
- 默认 DB 使用嵌入式 libSQL (file) 持久化房间分发拓扑,零外部依赖。
|
||
- 前端为 **SolidJS + TanStack Router/Query + Ark UI + Tailwind v4** 单页应用,构建产物嵌入 Go 二进制。
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
sync-live/
|
||
api/sync_live.proto # protobuf 控制面契约
|
||
gen/ # buf generate 产出(Go)
|
||
internal/
|
||
config/ # 运行参数 + 正式环境密钥校验
|
||
db/ # 嵌入式 libSQL 持久化
|
||
sfu/cloudflare/ # Cloudflare Realtime 分发
|
||
sfu/srs/ # SRS 主干
|
||
sfu/cdn/ # 第三方 CDN 分发
|
||
sfu/distributor.go # 分发抽象接口
|
||
server/ # gRPC 服务 + JSON 网关 + 媒体反代 + 房间拓扑 + 分发编排
|
||
static/ # 前端构建产物(由 web/ 构建生成,Go embed)
|
||
static/ # 前端构建产物(由 web/ 构建生成,Go embed)
|
||
auth/ # Casbin RBAC + JWT + 用户存储
|
||
cmd/server/main.go
|
||
deploy/ # docker-compose.yml + srs.conf
|
||
web/ # 前端工程(Solid + TanStack + Ark UI + Tailwind v4)
|
||
src/
|
||
vite.config.ts # 构建输出到 ../internal/server/static
|
||
```
|
||
|
||
## 运行
|
||
|
||
### 1. 配置环境变量
|
||
|
||
复制示例并填写鉴权密钥(正式环境必填):
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# 必填:JWT_SECRET(与可选 SFU_TOKEN_SECRET)
|
||
# 可选:BOOTSTRAP_ADMIN_USER / BOOTSTRAP_ADMIN_PASS 冷启动首个管理员
|
||
```
|
||
|
||
未配置 `JWT_SECRET` 时服务将拒绝启动,避免以不安全默认密钥上线。
|
||
|
||
### 2. 起本地 SRS(主干,必须)
|
||
|
||
```bash
|
||
cd deploy && SRS_CANDIDATE=127.0.0.1 docker compose up -d srs
|
||
```
|
||
|
||
SRS 为唯一推流入口,未启动时推流将失败。
|
||
|
||
### 3. 起控制面
|
||
|
||
```bash
|
||
go run ./cmd/server
|
||
# 首页: http://localhost:8088/
|
||
# 发布: http://localhost:8088/publish?room=<房间名>
|
||
# 观看: http://localhost:8088/watch?room=<房间名>
|
||
# 登录: http://localhost:8088/login
|
||
```
|
||
|
||
发布/订阅需先登录;房间名通过 URL `?room=` 指定,不依赖默认占位。
|
||
|
||
## 鉴权(Casbin RBAC + JWT)
|
||
|
||
控制面启用登录与 RBAC:
|
||
|
||
- 发布 / 订阅 / 房间管理需登录;`guest` 仅可读取配置与 SRS 切片流(HLS/FLV)。
|
||
- 用户存储于 `data/users.json`(首次启动为空,需通过 `BOOTSTRAP_ADMIN_*` 或注册创建首个管理员)。
|
||
- 登录后服务端下发 HttpOnly Cookie,前端同源请求自动携带。
|
||
|
||
角色:`admin` / `publisher` / `viewer` / `guest`,策略见 `internal/auth/policy.csv`。
|
||
|
||
## 持久化(嵌入式 libSQL)
|
||
|
||
房间「分发目标」拓扑(`stream_targets`)默认持久化到嵌入式 libSQL file,而非纯内存:
|
||
|
||
- DSN:`file:./data/sync-live.db?cache=shared&_journal_mode=WAL`(默认)
|
||
- 兼容 `DATABASE_URL` 覆盖;仅支持 file: / :memory:,拒绝远程 libsql://
|
||
- 自动建表,重启后自动恢复房间拓扑
|
||
|
||
```bash
|
||
curl -b cookie.txt http://localhost:8088/api/rooms | jq
|
||
```
|
||
|
||
## 运行(Cloudflare Realtime,主 SFU)
|
||
|
||
在 `.env` 填入 Cloudflare Realtime 凭证后启用主 SFU:
|
||
|
||
```
|
||
CF_APP_ID=xxxxxxxxxxxx
|
||
CF_APP_SECRET=xxxxxxxxxxxx
|
||
```
|
||
|
||
- 发布:服务端用 `CF_APP_SECRET` 创建 Cloudflare session,浏览器只交换 SDP(反代注入 Bearer)。
|
||
- 观看:服务端为观众创建 viewer session,并订阅发布者 session 的轨道。
|
||
|
||
Cloudflare 未配置时 UI 标注「未配置」,SRS 链路不受影响。
|
||
|
||
## protobuf / gRPC
|
||
|
||
```bash
|
||
export PATH="$HOME/go/bin:$PATH"
|
||
buf generate api
|
||
```
|
||
|
||
契约(`SyncLive` 服务):`GetConfig` / `ListRooms` / `Publish` / `Subscribe` / `StopStream` /
|
||
`WatchRoom`(服务端流式推送房间分发拓扑)以及 `Login` / `Register` / `GetMe` / `ListUsers` / `UpdateUserRole`。
|
||
gRPC 监听 `GRPC_PORT`(默认 9090),浏览器走同端口的 JSON 网关(`protojson`)。
|
||
|
||
## 前端开发(web/)
|
||
|
||
```bash
|
||
cd web
|
||
pnpm install
|
||
pnpm dev # http://localhost:5173 热更新
|
||
pnpm build # 构建进 ../internal/server/static(被 Go embed)
|
||
```
|
||
|
||
## 分流拓扑
|
||
|
||
```
|
||
publisher ──WHIP/tracks.new──▶ Cloudflare Realtime SFU ──▶ viewer(s)
|
||
└────WHIP────────────▶ SRS SFU ─┬─▶ WHEP viewer(s) (低延时 0.2-0.5s)
|
||
├─▶ HLS viewer(s) (全端兼容,/live/*.m3u8)
|
||
└─▶ FLV viewer(s)
|
||
```
|
||
|
||
> SRS 推流链路详解见 [`docs/streaming-pipeline.md`](docs/streaming-pipeline.md)。
|
||
|
||
## 主播外部推送 API(弹幕广播)
|
||
|
||
除观众可发送的 `POST /api/room/{room}/chat` 外,额外提供**仅主播可用**的独立推送端点:
|
||
|
||
```
|
||
POST /api/room/{room}/broadcast
|
||
Authorization: Bearer <publisher-or-admin jwt>
|
||
Content-Type: application/json
|
||
|
||
{ "message": "欢迎来到直播间", "color": "#4ea8ff" }
|
||
```
|
||
|
||
- 权限:需 `room:publish`(仅 `publisher` / `admin`),未登录返回 `401`,越权返回 `403`。
|
||
- 推送的弹幕自动标记 `host: true`,经同一 `chatHub` 广播;同样受滑动窗口限流保护。
|