Files
synctv-weihang/docs/first-version-plan.md
2026-06-15 22:46:12 +08:00

19 KiB
Raw Permalink Blame History

SyncTV 第一版方案

1. 产品定位

SyncTV 第一版是一个无注册、无用户体系的同步观影客户端,面向 App 和 Windows 应用。

核心目标:

  • 输入房间 Code 即可进入房间。
  • 不做账号、昵称、好友、聊天、历史用户等用户体系。
  • 不支持本地视频。
  • 只支持网络播放源普通链接、OSS、WebDAV、直播流。
  • 客户端负责解码播放,避免网页播放器格式支持弱的问题。
  • 后端负责房间状态、同步信令、播放源解析、临时凭据保存,以及必要时的流式转发。
  • 支持断线重连,并恢复到房间当前播放进度。

第一版优先把三件事做好:

  • 能播。
  • 同步准。
  • 断线能回来。

2. 第一版功能范围

2.1 创建和进入房间

用户打开应用后可以创建房间,或输入已有房间 Code 加入房间。

房间可以先没有播放源。第一个加入房间成功的设备自动成为房主。

房主可以配置:

  • 播放源类型。
  • 播放源地址或配置。
  • 是否为直播流。
  • 连接方式,默认客户端直连,可手动切换为服务端代理。

创建新房间时服务端生成房间 Code例如

A7K29Q

2.2 加入房间

客户端只需要输入房间 Code。

加入后客户端自动获取:

  • 播放源信息。
  • 当前播放状态。
  • 当前播放进度。
  • 是否直播。
  • 当前房主设备信息。

不需要用户登录,也不需要设置昵称。

只有房主可以配置或更换播放源。所有房间成员都可以执行播放、暂停、跳转进度、回到直播等播放控制。

播放源连接方式由房主在配置时选择。默认是客户端直连源站,代理模式必须由房主主动打开。

2.3 播放控制同步

点播场景支持:

  • 播放。
  • 暂停。
  • 跳转进度。
  • 当前进度同步。
  • 缓冲后恢复。

直播场景支持:

  • 播放。
  • 暂停。
  • 回到直播。
  • 直播延迟同步。

2.4 断线重连

客户端 WebSocket 断开后自动重连。

重连时携带:

  • 房间 Code。
  • 本地设备 ID。

服务端返回最新房间快照,客户端重新计算应播放位置并恢复。

房间无人在线后保留一段时间,建议第一版保留 30 分钟。超过后销毁房间和临时播放源凭据。

3. 非目标

第一版不做:

  • 注册。
  • 登录。
  • 昵称。
  • 用户资料。
  • 好友。
  • 聊天。
  • 弹幕。
  • 房间列表。
  • 历史记录。
  • 本地视频同步。
  • 内置影视内容。
  • 完整资源站。

4. 技术选型建议

4.1 客户端

客户端改为分端开发。

Windows 客户端建议:

  • Python。
  • PySide6。
  • python-vlc。
  • PyInstaller 打包 Windows exe。
  • 系统播放器逻辑和同步逻辑分离。

移动 App 建议:

  • HBuilder/uni-app。
  • UI 和房间同步逻辑使用 Vue/uni-app。
  • 播放器通过 App 原生插件接 VLC。
  • Android 原生插件封装 libVLC。
  • iOS 原生插件封装 MobileVLCKit / VLCKit。

分端开发的原因:

  • 避免重型桌面工具链问题。
  • Windows 端优先把强播放能力跑通。
  • App 端通过 HBuilder 快速开发,同时保留原生 VLC 播放能力。
  • 客户端共享后端协议,不共享 UI 代码。

选择播放器时需要重点验证:

  • mp4。
  • mkv。
  • flv。
  • m3u8。
  • dash。
  • 常见编码格式。
  • 直播流。
  • HTTP Header 自定义。
  • Cookie / Authorization Header。
  • WebDAV URL 播放。
  • 字幕和音轨扩展能力。

4.2 后端

第一版服务端原则:

  • 尽量轻。
  • 尽量少模块。
  • 尽量少状态。
  • 默认不接触视频流。
  • 只有房主手动开启代理时才做流式转发。
  • 不做用户系统。
  • 不做数据库表设计。
  • 不做文件存储。

推荐:

  • Go。
  • 标准库 net/http 或 Gin/Fiber/Echo。
  • WebSocket。
  • Redis。
  • Docker + Nginx。

第一版不建议上 PostgreSQL。房间状态、连接状态、临时播放源和过期时间都放 Redis开发更快清理也简单。

Go 更适合服务端保持轻量:单二进制部署,内存占用低,并发连接和流式转发能力好。第一版可以优先使用标准库 net/http减少框架依赖。

4.3 服务模块

后端建议只拆最少模块:

  • RoomService房间创建、加入、房主判定、房间快照、过期清理。
  • SyncGatewayWebSocket 连接、播放控制广播、心跳、重连。
  • SourceService播放源保存、直连信息下发、代理 URL 生成。
  • StreamProxy房主手动开启代理时对 WebDAV/OSS/普通私有链接做流式转发,不落盘存储。

第一版可以先不单独拆 CredentialService、SourceResolver 等细模块,避免服务端过早变重。等代理来源和鉴权方式变多后再拆。

5. 播放源设计

第一版支持四类来源。

类型 示例 第一版处理方式
普通链接 mp4、m3u8、flv、dash 默认客户端直连,可手动开启代理
OSS 阿里云 OSS、S3 兼容对象存储 默认客户端直连,可手动开启代理
WebDAV WebDAV 文件 URL 默认客户端直连,可手动开启代理
直播流 m3u8、flv、其他播放器支持协议 默认客户端直连,可手动开启代理

房主配置播放源时需要有一个明确的连接方式控件:

  • 默认:客户端直连。
  • 可选:服务端代理。

这个控件可以做成分段按钮或开关,不做自动切换。代理模式涉及服务器带宽成本和隐私边界,必须让房主显式选择。

5.1 普通链接

普通链接是最简单的来源。

后端只保存 URL 和基础元信息,客户端直接播放。

需要支持:

  • 普通 HTTP/HTTPS URL。
  • m3u8。
  • flv。
  • dash。
  • 带必要 Header 的 URL。

5.2 OSS

OSS 有两种使用方式。

方式一:用户直接输入公开 URL 或签名 URL。

  • 实现简单。
  • 客户端直接播放。
  • 后端只保存 URL。
  • 适合第一版快速落地。

方式二:房主输入 OSS 配置,服务端保存临时凭据,并返回代理播放 URL。

  • 更安全。
  • 不把 Secret 直接广播给所有客户端。
  • 服务端只做流式转发,不下载、不落盘。
  • 房间销毁后清理配置。
  • 适合稍微完整的第一版。

建议第一版支持方式一和方式二。方式一是默认模式;方式二需要房主手动开启,会增加服务器带宽压力,但不会增加硬盘存储压力。

5.3 WebDAV

WebDAV 是第一版需要重点设计的来源,因为它通常需要账号密码。

常见 WebDAV 鉴权方式:

  • Basic Auth。
  • Digest Auth。
  • Cookie。
  • Token。

播放器播放 WebDAV 文件时,客户端通常必须具备以下能力之一:

  • URL 中包含凭据。
  • 播放请求附带 Authorization Header。
  • 访问服务端流式代理地址。

所以“WebDAV 的 key 不给客户端还能不能用”的答案是:

如果客户端要直接连接 WebDAV凭据或等价的临时授权信息必须给客户端不给就不能直连播放。

如果使用服务端流式代理,客户端不需要拿到原始 WebDAV 凭据;服务端拿到凭据后直接向 WebDAV 拉流,并边读边转发给客户端,不下载、不落盘。

5.3.1 极简方案:凭据下发客户端

创建房间时,房主输入:

  • WebDAV 文件 URL。
  • 用户名。
  • 密码或 Token。

服务端保存房间播放源,并在成员加入房间时下发给客户端。

客户端用这些凭据直接播放。

优点:

  • 实现最简单。
  • 后端不承担视频流量。
  • 播放性能最好。
  • 成本最低。

缺点:

  • 所有加入房间的客户端都能拿到 WebDAV 凭据。
  • 如果房间 Code 泄露,凭据也可能泄露。
  • 对安全和隐私不友好。

适用场景:

  • 早期内测。
  • 用户之间互相信任。
  • WebDAV 凭据本身是临时的。
  • 产品明确提示风险。

5.3.2 代理模式:服务端保存凭据并流式转发

创建房间时,房主提交 WebDAV 配置给后端。

后端:

  • 加密保存 WebDAV 凭据。
  • 验证文件是否可访问。
  • 为房间成员生成代理播放 URL。
  • 使用 WebDAV 凭据向源站发起请求。
  • 将源站响应以流式方式转发给客户端。
  • 不把视频文件下载到服务器硬盘。
  • 不做长期缓存。
  • 房间过期后删除凭据。

客户端播放:

https://api.example.com/rooms/A7K29Q/source/stream?token=short-lived-token

服务端收到请求后使用 WebDAV 凭据去拉取源文件,再把内容边读边转发给客户端。

优点:

  • 客户端拿不到原始 WebDAV 密码。
  • 可以做房间权限、过期、限速、防盗链。
  • 房间销毁后授权自然失效。
  • 不承担硬盘存储压力。

缺点:

  • 后端要承担视频流量,带宽成本高。
  • 需要支持 Range 请求,否则拖动进度会很差。
  • 大文件和高并发时压力明显。
  • 直播和大码率视频对服务端要求更高。
  • 需要处理连接中断、源站限速、响应头透传等转发细节。

这个方案安全性更好,不占用硬盘,但基础设施带宽成本更高。

5.3.3 第一版建议

第一版建议做两个模式,但默认始终是直连模式:

  1. 直连模式:客户端直连 WebDAV。

    • 房主输入 WebDAV URL 和凭据。
    • 后端临时保存并下发给房间客户端。
    • 明确这是共享房间凭据,适合可信房间。
    • 这是默认模式。
  2. 代理模式:服务端流式转发 WebDAV。

    • 房主输入 WebDAV URL 和凭据。
    • 房主必须手动打开代理模式。
    • 后端保存凭据。
    • 客户端只拿代理播放 URL。
    • 后端支持 Range 请求。
    • 后端不下载、不落盘,只做流式转发。
    • 后端承担带宽和连接压力。

普通 URL 和 OSS 也可以使用同样的代理模式。只要服务端能拿到访问源站所需的 URL、Header、Cookie 或 Secret就可以向源站发起请求并流式转发给客户端。

6. 房间状态模型

Redis 中建议保存房间快照。

点播房间示例:

{
  "roomCode": "A7K29Q",
  "sourceType": "webdav",
  "sourceId": "src_123",
  "isLive": false,
  "playbackState": "playing",
  "positionMs": 128400,
  "serverUpdatedAt": 1780000000000,
  "ownerDeviceId": "dev_abc",
  "onlineCount": 2,
  "expireAt": 1780001800000
}

直播房间示例:

{
  "roomCode": "A7K29Q",
  "sourceType": "url",
  "sourceId": "src_456",
  "isLive": true,
  "playbackState": "playing",
  "liveSyncMode": "latency",
  "targetLatencyMs": 3000,
  "serverUpdatedAt": 1780000000000,
  "ownerDeviceId": "dev_abc",
  "onlineCount": 2,
  "expireAt": 1780001800000
}

7. 同步协议

7.1 WebSocket 事件

客户端到服务端:

  • createRoom
  • joinRoom
  • leaveRoom
  • play
  • pause
  • seek
  • syncProgress
  • syncToLive
  • heartbeat
  • reconnectRoom

服务端到客户端:

  • roomCreated
  • roomJoined
  • roomSnapshot
  • playbackChanged
  • progressSynced
  • liveSynced
  • controllerChanged
  • roomExpired
  • error

7.2 点播同步

控制端执行播放控制时发送事件。

播放事件示例:

{
  "type": "play",
  "roomCode": "A7K29Q",
  "positionMs": 128400,
  "clientSentAt": 1780000000000
}

服务端写入:

{
  "playbackState": "playing",
  "positionMs": 128400,
  "serverUpdatedAt": 1780000000120
}

其他客户端收到后计算目标进度:

targetPositionMs = positionMs + (clientNowServerTime - serverUpdatedAt)

误差处理建议:

  • 小于 500ms不处理。
  • 500ms 到 2000ms短时间微调倍速。
  • 大于 2000ms直接 seek。

控制端每 3 秒上报一次当前状态,用于校准。

7.3 直播同步

直播不以 positionMs 为核心,而以直播延迟为核心。

建议同步字段:

{
  "isLive": true,
  "targetLatencyMs": 3000,
  "playbackState": "playing"
}

客户端尽量保持距离直播边缘 3 秒左右。

直播控制:

  • play开始拉流。
  • pause暂停本地播放。
  • syncToLive回到直播边缘或目标延迟。

直播流由于源站、CDN、客户端缓冲策略不同很难做到毫秒级同步。第一版目标应是让多个客户端处于接近的直播延迟。

8. 断线重连恢复

8.1 客户端本地保存

客户端本地保存:

{
  "deviceId": "dev_abc",
  "lastRoomCode": "A7K29Q",
  "lastConnectedAt": 1780000000000
}

deviceId 是本地随机生成的设备标识。

它不是账号,也不是用户 ID只用于

  • 断线重连。
  • 控制权恢复。
  • 同一设备重复连接识别。

8.2 重连流程

  1. WebSocket 断开。
  2. 客户端进入重连状态。
  3. 客户端带 roomCode 和 deviceId 重连。
  4. 服务端检查房间是否还存在。
  5. 服务端返回 roomSnapshot。
  6. 客户端重新解析播放源。
  7. 点播房间根据 positionMs 和 serverUpdatedAt 恢复。
  8. 直播房间恢复到目标直播延迟。

8.3 房主和控制权处理

建议第一版规则:

  • 第一个加入房间成功的设备成为房主。
  • 只有房主可以配置或更换播放源。
  • 所有房间成员都可以播放、暂停、跳转进度、回到直播。
  • 房主短暂断线 30 秒内,房主身份保留。
  • 房主超过 30 秒未恢复,允许当前房间内最早在线的设备成为新房主。
  • 房间无人后进入保留期,保留期间房主身份和房间状态仍然存在。

这个规则避免了用户体系,同时保留了“谁能换片”的边界。

9. 客户端架构

客户端建议拆成:

  • RoomController创建房间、加入房间、房间状态。
  • SocketClientWebSocket 连接、重连、事件收发。
  • PlayerController播放、暂停、seek、倍速、直播控制。
  • SyncEngine进度计算、误差校正、直播延迟同步。
  • SourceManager播放源初始化、凭据处理、URL 刷新。
  • LocalDeviceStore本地 deviceId 和 lastRoomCode。

10. 后端架构

后端第一版建议保持单体服务,不拆微服务。

最小结构:

  • HTTP API创建房间、加入房间、配置播放源、获取房间快照。
  • WebSocket Gateway播放、暂停、seek、直播同步、心跳、断线重连。
  • Redis Store房间状态、连接状态、播放源配置、过期时间。
  • Stream Proxy可选代理入口只有房主开启代理模式时使用。

代码模块:

  • RoomService房间创建、房间加入、房主判定、快照维护。
  • PlaybackService播放状态写入和广播。
  • SourceService播放源保存、直连信息下发、代理 URL 生成。
  • StreamProxyServiceWebDAV/OSS/私有链接流式转发,不落盘存储。

暂时不做:

  • 用户数据库。
  • PostgreSQL。
  • 微服务拆分。
  • 消息队列。
  • 文件存储。
  • 复杂权限系统。

Redis Key 示例:

room:A7K29Q
room:A7K29Q:connections
source:src_123
device:dev_abc:last

11. WebDAV 凭据结论

WebDAV 凭据问题可以概括为一句话:

客户端直连 WebDAV 时,客户端必须拿到凭据或等价授权;代理模式下客户端只拿我们服务端的播放 URL原始凭据由服务端持有。

两种可选做法:

方案 客户端拿到什么 后端流量成本 安全性 实现难度
直连模式 原始凭据或 Basic Auth Header
代理模式 服务端代理 URL 中高

代理模式的关键点:

  • 服务端拿到用户的 key、Cookie、Header 或账号密码。
  • 服务端向 WebDAV/OSS/私有源站发起请求。
  • 服务端把响应流转发给客户端。
  • 服务端不下载完整文件。
  • 服务端不做硬盘存储。
  • 服务端必须正确支持 Range、Content-Type、Content-Length、Accept-Ranges 等响应头。
  • 服务端需要承担带宽、并发连接和转发稳定性压力。

第一版建议:

  • 同时支持直连模式和代理模式。
  • 房主配置播放源时通过按钮或开关选择连接方式。
  • 默认连接方式是客户端直连。
  • 代理模式必须由房主主动开启。
  • 直连模式适合可信房间和低成本场景。
  • 代理模式适合不想把 key 暴露给房间成员的场景。

12. 第一版里程碑

Milestone 1最小可跑通

  • Windows 客户端。
  • 轻量 Node.js 服务端。
  • Redis 房间状态。
  • 房间创建和加入。
  • 普通 URL 播放。
  • 播放、暂停、seek 同步。
  • WebSocket 断线重连。

Milestone 2播放源扩展

  • m3u8 直播流。
  • OSS 签名 URL。
  • WebDAV 直连模式。
  • HTTP Header 支持。

Milestone 3同步体验打磨

  • 周期性进度校准。
  • 小误差倍速追赶。
  • 大误差 seek。
  • 直播回到实时。

Milestone 4移动端

  • App 客户端。
  • 复用房间和同步协议。
  • 验证播放器内核能力。

Milestone 5代理能力增强

  • WebDAV/OSS 最小流式代理。
  • 凭据加密存储。
  • 代理 URL 短期 Token。
  • Range 请求和拖动进度优化。
  • 源站 Header 透传和错误处理。
  • 房间过期自动清理。

13. 关键风险

13.1 播放器能力风险

不同平台播放器能力不一致,尤其是:

  • iOS 对部分格式限制更多。
  • Android 机型和系统版本差异大。
  • Windows mpv/libVLC 能力较强,但集成和打包需要验证。

第一阶段应优先做播放内核 PoC。

13.2 WebDAV 兼容风险

不同 WebDAV 服务差异很大:

  • 鉴权方式不同。
  • 是否支持 Range 请求不同。
  • 是否支持文件直链不同。
  • 是否限制 User-Agent 或 Header。

第一版应先支持最常见的 Basic Auth + Range。

13.3 服务端流式代理成本风险

如果服务端代理 WebDAV 或 OSS 视频流,所有视频流量都会走后端。

这会带来:

  • 高带宽成本。
  • 高并发压力。
  • 大文件拖动复杂度。
  • 直播流转发压力。
  • 但不会带来硬盘存储压力,因为代理模式只做流式转发,不下载、不落盘。

因此代理模式适合安全要求更高的房间但需要配套限流、超时、Range 支持和错误恢复。

13.4 直播同步风险

直播流本身缓冲和 CDN 延迟不稳定。

第一版不要承诺毫秒级同步直播,目标是保持相近的直播延迟。

14. 推荐第一版决策

建议第一版采用:

  • Windows 和 App 分端开发。
  • Windows 优先跑通。
  • Windows 客户端使用 Python + PySide6 + python-vlc并用 PyInstaller 打包。
  • App 使用 HBuilder/uni-app + 原生 VLC 插件。
  • 后端 Go + WebSocket + Redis。
  • 服务端保持单体和轻量,不上 PostgreSQL不拆微服务。
  • 普通 URL、OSS、WebDAV、m3u8 直播优先。
  • 播放源支持直连和服务端流式代理。
  • 默认客户端直连,代理模式由房主手动开启。
  • 第一个加入房间的设备为房主。
  • 只有房主能配置或更换播放源。
  • 所有人都可以播放、暂停、跳转进度、回到直播。
  • 房间无人后 30 分钟销毁。

最终第一版交付目标:

用户创建或加入房间,第一个加入者成为房主并配置网络播放源;默认由客户端直接获取源站内容,房主也可以手动开启服务端代理;其他设备输入 Code 加入后自动播放同一来源并同步播放、暂停、跳转断线后自动回到房间当前进度支持普通链接、OSS、WebDAV 和直播流;代理模式下服务端拿到 key 后流式转发给客户端,不下载、不落盘。