diff --git a/B侧设计文档.md b/B侧设计文档.md new file mode 100644 index 0000000..c1476dc --- /dev/null +++ b/B侧设计文档.md @@ -0,0 +1,117 @@ +# B 侧设计文档(主控端 + 协议 + 网络) + +> 负责人:成员 B | 主责 crate:`viewer/`、`common/` | 配合:A(agent/) +> 按里程碑推进,每个里程碑独立 feature 分支,合入即打 tag。 + +--- + +## B1 · 联调基线(1-2 天,和 A 合作完成) + +目标:拿到 H.264 版双机实测数据,作为一切优化的对照基线。 + +1. **HUD 扩展**:新增 丢包率(估算)/抖动(jitter)/帧间隔 p95 三项读数 + - 抖动 = 相邻帧 E2E 差值的绝对值 EMA;帧间隔 p95 用环形缓冲取 95 分位; + - 码流异常计数(解码失败次数)也上 HUD,验证关键帧自愈是否生效。 +2. **测试脚本** `tools/bench.md`:固定三个场景各测 60s —— 静止桌面 / 播放视频 / 拖动窗口,记录 HUD 截图。 +3. **产出**:基线数据表填入本文件末尾。 + +## B2 · 码率自适应(3-4 天) + +目标:弱网自动降码率保流畅,好网缓慢升码率提画质。 + +### 协议扩展(common/) + +```rust +/// B2 新增控制消息(流 2) +RateReport { + loss_permille: u16, // 接收端估算丢包率 ‰ + rtt_ms: u32, // 最近 RTT + e2e_ms: u32, // 平滑后端到端延迟 +} +``` + +viewer 每 2s 汇总一次发送(流 2 复用,无新增握手)。 + +### 控制算法(先简后精,AIMD 风格) + +| 网络状态(连续 3 个周期) | 动作 | +|---|---| +| 丢包 > 8% 或 E2E > 400ms | 码率 ×0.6(下限 1Mbps),同时通知编码器降帧率到 20fps | +| 丢包 3-8% | 码率 ×0.9 | +| 丢包 < 1% 且 E2E < 150ms 持续 10s | 码率 +500Kbps 步进上探(上限 15Mbps) | + +### agent 侧配合点(给 A 的接口需求) + +- 需要运行时改码率的入口:优先查 openh264 crate 是否暴露 `SetOption(RC_PARAM)`; +- 若 crate 不支持 → 方案 B:保留 encoder 实例但按新码率重建(重建成本 ~50ms,降码率不频繁,可接受); +- 降级到极限(1Mbps 仍丢包 >15%)→ 主动降分辨率 1080p→720p(A 实现,协议传 `ResChange` 通知,viewer 重配解码器与窗口尺寸)。 + +## B3 · P2P 打洞 + 信令 + 中继(1.5-2 周,核心里程碑) + +目标:跨公网可用。三层结构: + +``` +┌─────────┐ ①注册/交换地址(WebSocket) ┌──────────┐ +│ viewer │ ───────────────────────────→ │ 信令服务器 │ +└────┬────┘ └──────────┘ + │ ②拿到对方候选地址(IP:port×N) ↑ + │ ③a 直连(打洞) ──────────────────── agent 注册 + │ ③b 失败 → 走 TURN 中继 +``` + +### 信令服务器(B 主建,A 参与评审) + +- 最小实现:单二进制 Rust + WebSocket(tokio-tungstenite),无数据库; +- 消息:`Register{agent_id}` / `Connect{agent_id}` / `Answer{candidates, auth_code_hint}`; +- agent 开机注册自身 ID(设备码),viewer 输对方 ID 发起; +- 中立服务器,不碰媒体数据,带宽成本 ≈ 0。 + +### 候选地址与打洞 + +1. agent 侧收集候选:本机局域网地址、路由器公网映射(STUN 探测,自建 coturn 的 stun 或公共 stun); +2. 双方经信令交换候选后**同时**向对方发探测包(UDP 打洞,锥形 NAT 成功率高;对称 NAT 基本打不通); +3. 打通 → 直连(QUIC 直接到该地址);打不通 → TURN 中继兜底。 + +### 中继方案(务实路线) + +QUIC 本身没有标准 TURN 集成,两条路: +- **路线一(推荐先做)**:自建中继服务 `relay` —— viewer 把 QUIC 包封装转发给 relay,relay 转给 agent(本质是应用层 TURN,复用信令服务器的部署); +- **路线二(二期)**:coturn + 手写 ICE-lite,仅在路线一性能不足时投入。 + +### 安全(随 B3 一起做,替代现在的跳过校验) + +- 预共享验证码升级为 **PAKE(SPAKE2)**:验证码不落服务器、不落网络,双方推导会话密钥; +- 信令只传地址候选,媒体端到端加密保持 QUIC TLS 不变。 + +## B4 · 多屏 + 文件传输(各 2-3 天,穿插做) + +**多屏**: +- 协议:`ScreenList{screens: Vec<{id, w, h}>}`(AuthOk 里带)、`SelectScreen{id}`; +- agent 枚举 DXGI outputs(A 实现),viewer 加屏幕切换下拉; +- 第二期再做"多屏同显"。 + +**文件传输**: +- 首选走已有 QUIC 连接开独立双向流(不引入 aria2 依赖,部署简单); +- 协议:`FileOffer{name, size}` → `FileAccept` → 原始分块流(1MB 块 + 校验); +- viewer 做拖拽发送 + 保存对话框。 + +--- + +## 里程碑与分支规划 + +| 里程碑 | 分支 | 预估 | 验收标准 | +|---|---|---|---| +| B1 联调基线 | feature/hud-baseline | 1-2 天 | 三场景基线数据入库 | +| B2 码率自适应 | feature/rate-adapt | 3-4 天 | 模拟弱网(clumsy/丢包工具)下自动降码率,画面不断流 | +| B3 打洞+中继 | feature/p2p-relay | 1.5-2 周 | 两台跨公网机器通过 ID 互联;打洞失败自动走中继 | +| B4 多屏/文件 | feature/screens-files | 3-5 天 | 多屏切换、拖拽传文件可用 | + +顺序建议:B1 → B2 → B3 → B4(B3 是价值最大项,B4 可穿插)。 + +## 基线数据表(B1 完成后填写) + +| 场景 | FPS | RTT | E2E | 码率 | 丢包 | 抖动 | +|---|---|---|---|---|---|---| +| 静止桌面 | | | | | | | +| 视频播放 | | | | | | | +| 拖动窗口 | | | | | | |