Skip to content

14 · 媒体与实时语音

Buzz 把持久媒体和实时语音分成两条完全不同的链:媒体经 Blossom/NIP-98 鉴权、严格内容验证后进入 S3-compatible storage;Huddle 音频经 WebSocket/二进制帧进入 room/mesh,强调低延迟、背压和租约围栏。两者只在客户端体验上相邻,可靠性模型不同。

1. 媒体上传链

text
client file
   │ Blossom auth / NIP-98

Relay HTTP handler
   ├─ size/MIME/magic validation
   ├─ format-specific structural checks
   ├─ metadata/privacy rejection
   ├─ optional thumbnail + blurhash
   └─ S3 put
        ├─ immutable/hashed blob key
        └─ community-scoped metadata sidecar

buzz-media 是验证与存储库,Axum handler 留在 Relay。这保持 library 不依赖具体 HTTP 框架,并让 tenant/auth 信息由 Relay 注入。

2. 为什么不能相信 MIME

客户端声明 image/jpeg 只是提示。验证器交叉检查:

  • 文件 magic bytes 与声明 MIME。
  • JPEG marker、PNG chunks、WebP RIFF、GIF 结构。
  • 图像尺寸/像素预算,防解压炸弹。
  • MP4 box 长度、嵌套深度、codec 与 moov/mdat 次序。
  • 通用文件扩展与危险类型限制。

格式 parser 的目标不是完整解码,而是以小成本拒绝明显伪造、溢出与资源攻击。

3. 隐私元数据

图片可能含 EXIF/GPS/XMP,泄露位置、设备和编辑历史。Buzz 选择在服务端验证/拒绝敏感 metadata,而不完全依赖客户端“上传前清理”。

物理 blob key 与社区 sidecar metadata 分开:同 hash blob 可按存储策略复用,但 tenant/uploader/network attribution 必须在社区语境内查询,不能靠对象名暴露。

4. 派生资源

图片可生成 thumbnail/blurhash;这些是原 blob 的派生物:

  • 原 blob 成功持久化是主事实。
  • 派生失败可重试,不应伪造原上传成功。
  • 派生 key 必须绑定原 hash/variant,避免旧缩略图覆盖新内容。
  • 输出再次受尺寸与编码预算限制。

5. Huddle 组件

实时语音位于 Relay audio/ 与 Desktop huddle/

text
Desktop capture / preprocessing
       │ encoded audio frames

Relay huddle handler
  ├─ join/auth/channel membership
  ├─ room manager (community, channel)
  ├─ control queue (reliable/bounded)
  ├─ audio queue (drop on full)
  └─ optional mesh owner/tunnel


Desktop jitter/playout + optional STT/TTS

6. 协议版本协商

Room 的第一个 peer 固定 wire protocol version。后续 peer 若不兼容被拒绝。若 room 已满与版本不匹配同时成立,优先返回 full,避免外部探测者通过错误类型推断房间内部版本/成员状态。

soft cap 约 25 peers,peer index 用 u8。具体 cap 同时受带宽、编码和 UI 设计限制,不应仅因为 index 可表示 255 就放宽。

7. 控制与音频背压分离

控制帧(join/leave/mute/renew)影响状态,需要有界可靠处理;音频帧追求低延迟,队列满时丢旧/新帧比积压数秒更合理。

text
control full → slow/close, preserve state correctness
audio full   → drop frame, preserve real-time latency

如果共用一个队列,音频洪峰会让 leave/lease renew 无法处理,导致幽灵成员或错误 takeover。

8. Room owner 与 generation fence

多 Relay pod 下,一个 Huddle session 由某 node/runtime 持有 lease。Redis directory 记录 owner 与 generation:

  • renew 只接受当前 owner/generation。
  • takeover 产生更高 generation。
  • 旧 owner 的晚到帧/释放请求被 fence 拒绝。
  • Mesh tunnel 每次发送可检查 lease 是否仍有效。

这是典型 fencing token:TTL 只说明租约可能过期,generation 才阻止旧持有者“复活”。

9. Desktop 音频栈

Desktop native backend 处理设备、jitter、playout、预处理、重连、PTT、STT/TTS 与 Agent TTS routing。前端只接收状态和用户控制,不承担实时 DSP。

本地 TTS 模型/voice registry 还有升级与选择测试;防睡眠确保会话/Agent 讲话时 OS 不进入不合适状态。

10. 生命周期事件

Huddle start/join/leave/end/archive 使用事件对协作层可见;高频音频帧不进入事件数据库。最后一个 peer 离开触发 teardown/end,异常断连靠 lease/timeout 收敛。

这是“控制面持久可观察、数据面实时短命”的分离。

11. 源码入口

独立源码研究笔记 · 非 Buzz 官方文档