Files
opendance-web/contracts/websocket-keypoints.md
T
zijunchiang 6913e181b5 docs: 其余易混淆字符改 ASCII(× − ≥ ≤ ≈ ✓ 等),消除 Gitea 告警
实测确认 Gitea 的「模棱两可 Unicode」检测覆盖:— – … § → × − ≥ ≤ ≈ ✓。
三库 .md/.txt 全部替换为 x - >= <= ~ OK;contracts 三库同源保持一致。
重新打包 v0.2.0。
2026-09-15 21:35:10 +08:00

4.1 KiB
Raw Blame History

contracts/websocket-keypoints.md - 关键点传输契约 v0.1.0

Android 辅助 App(发送端)-> Windows 客户端(接收端)。 服务端不参与实时链路,但 T-WEB-006 预处理产出的序列必须与此结构同构。

1. 端点

ws://<windows-host>:8765/pose
  • Windows 客户端监听,Android App 主动连接。
  • 子协议:opendance.pose.v1(Sec-WebSocket-Protocol)
  • 二进制帧传输关键点,文本帧传输控制消息(JSON)。
  • 默认 30 FPS;发送端允许丢帧,接收端必须按 frame_id 检测丢包并插值补齐。

2. 二进制帧:239 字节(冻结)

小端序(little-endian)。

偏移  长度  字段          类型      说明
0     2     magic         u16 LE    固定 0x4F44(ASCII "OD")
2     1     version       u8        固定 0x01
3     1     flags         u8        bit0 = 镜像画面(mirrored)
                                    bit1 = 本帧含低置信度点(<0.5)
                                    bit2 = 上半身模式(仅 0..22 有效)
                                    bit3-7 保留,必须为 0
4     4     frame_id      u32 LE    单调递增,从 0 开始
8     231   keypoints     33 x 7B   见下
--
合计  239

单个关键点(7 字节,i 为 0..32)

偏移  长度  字段   类型     换算
+0    2     x      i16 LE   x_norm = x / 32767.0
+2    2     y      i16 LE   y_norm = y / 32767.0
+4    2     z      i16 LE   z_norm = z / 32767.0
+6    1     conf   u8       visibility = conf / 255.0
  • 点索引与坐标语义见 skeleton-topology.md。
  • 编码:x = clamp(round(x_norm * 32767), -32768, 32767),y、z 同理。
  • 低置信度点(conf < 128)坐标无意义,接收端必须插值,不得直接使用。
  • i16 为有符号数:0x8000 表示 -32768。

参考实现(GDScript 风格伪码)

func decode(buf: PackedByteArray) -> Dictionary:
    assert(buf.size() == 239)
    var magic := buf.decode_u16(0)
    assert(magic == 0x4F44 and buf.decode_u8(2) == 1)
    var flags := buf.decode_u8(3)
    var frame_id := buf.decode_u32(4)
    var pts := []
    for i in 33:
        var o := 8 + i * 7
        pts.append({
            "x": float(buf.decode_s16(o)) / 32767.0,
            "y": float(buf.decode_s16(o + 2)) / 32767.0,
            "z": float(buf.decode_s16(o + 4)) / 32767.0,
            "conf": float(buf.decode_u8(o + 6)) / 255.0,
        })
    return {"flags": flags, "frame_id": frame_id, "points": pts}

3. 文本控制帧(JSON,UTF-8)

3.1 hello - Android -> Windows,连接后立即发送

{
  "type": "hello",
  "protocol": "opendance.pose.v1",
  "device": "Pixel 8",
  "appVersion": "0.1.0",
  "cameraFacing": "front",
  "targetFps": 30,
  "resolution": "640x480"
}

3.2 welcome - Windows -> Android

{ "type": "welcome", "protocol": "opendance.pose.v1", "sessionId": "uuid", "expectedFps": 30 }

3.3 config - Windows -> Android,运行中调整

{ "type": "config", "mirror": true, "targetFps": 30, "upperBodyOnly": false, "resolution": "640x480" }

3.4 stats - Android -> Windows,每秒一次

{ "type": "stats", "fps": 29.6, "dropped": 3, "latencyMs": 42, "confidenceAvg": 0.93 }

3.5 bye - 双向

{ "type": "bye", "reason": "user_stop" }

4. JSON 调试帧(可选,仅开发期)

{
  "type": "pose",
  "protocol": "opendance.pose.v1",
  "frameId": 1024,
  "timestampMs": 1730000000000,
  "flags": { "mirrored": false, "upperBodyOnly": false },
  "points": [[0.51, 0.22, -0.03, 0.99], "...共 33 项,顺序固定 [x, y, z, conf]"]
}

生产链路必须使用二进制帧;JSON 帧仅用于联调,接收端可选择性支持。

5. 兼容性规则

  • version 不同的大版本接收端必须拒绝并在 bye.reason 中说明。
  • flags 新增位不得改变既有位语义;旧接收端必须忽略未知位。
  • 33 点顺序一旦冻结不得插入或重排;新增点只能追加新 version。
  • 服务端 motions.keypoint_format 字段取值 mediapipe33@0.1.0 对应本契约。