Skip to content

协议速查表

面向修改协议与排查抓包的紧凑参考。定义基于 scrcpy 4.1;协议没有独立协商版本,client/server 必须锁步。

一、连接建立顺序

启用哪些通道,就严格按以下顺序建立:

text
video? → audio? → control?

Android 端实现见 DesktopConnection.open(),C 端配对见 sc_server_connect_to()。连接没有通道标签。

连接前导

数据所在 socket条件长度
dummy byte第一条启用 socketadb forward1
device name第一条启用 socketsend_device_meta64 bytes

设备名是固定字段,不是长度前缀字符串;客户端强制最后一字节为 \0

二、媒体流

2.1 Stream header

text
offset  size  field
0       4     codec_id (u32 big-endian)
IDASCII含义
0x68323634h264H.264
0x68323635h265H.265 / HEVC
0x00617631\0av1AV1
0x00767038\0vp8VP8
0x00767039\0vp9VP9
0x6f707573opusOpus
0x00616163\0aacAAC
0x666c6163flacFLAC
0x00726177\0rawPCM S16LE
0stream 主动禁用
1stream 配置错误

映射源见 demuxer.c

2.2 Session header(仅视频)

text
offset  size  field
0       4     flags (u32 BE): bit31=session, bit0=client_resized
4       4     width (u32 BE)
8       4     height (u32 BE)

最高 bit 为 1 时,整个 12 字节块就是 session,无 payload。

2.3 Media packet

text
offset  size  field
0       8     pts_and_flags (u64 BE)
                  bit62 config
                  bit61 key frame
                  low 61 bits PTS (microseconds)
8       4     payload_size (u32 BE)
12      N     payload

编码端见 Streamer.writeFrameMeta(),解码端见 sc_demuxer_recv_packet()

三、ControlMessage:desktop → device

所有消息第 0 字节是 type。

type名称type 后 payload
0INJECT_KEYCODEaction:u8, keycode:u32, repeat:u32, meta:u32
1INJECT_TEXTlen:u32, utf8
2INJECT_TOUCH_EVENTaction:u8, pointer:u64, position, pressure:u16, action_button:u32, buttons:u32
3INJECT_SCROLL_EVENTposition, hscroll:i16, vscroll:i16, buttons:u32
4BACK_OR_SCREEN_ONaction:u8
5EXPAND_NOTIFICATION_PANEL
6EXPAND_SETTINGS_PANEL
7COLLAPSE_PANELS
8GET_CLIPBOARDcopy_key:u8
9SET_CLIPBOARDsequence:u64, paste:u8, len:u32, utf8
10SET_DISPLAY_POWERon:u8
11ROTATE_DEVICE
12UHID_CREATEid:u16, vendor:u16, product:u16, name_len:u8, name, data_len:u16, report
13UHID_INPUTid:u16, data_len:u16, report
14UHID_DESTROYid:u16
15OPEN_HARD_KEYBOARD_SETTINGS
16START_APPlen:u8, utf8
17RESET_VIDEO
18CAMERA_SET_TORCHon:u8
19CAMERA_ZOOM_IN
20CAMERA_ZOOM_OUT
21RESIZE_DISPLAYwidth:u16, height:u16
22SCAN_FILElen:u32, utf8

position 固定为 x:i32, y:i32, screen_width:u16, screen_height:u16。pressure 是 [0,1] 的 u16 定点数;scroll 是映射到 [-16,16] 的 i16 定点数。

完整 C enum/union 见 control_msg.h,Java parser 见 ControlMessageReader

四、DeviceMessage:device → desktop

type名称type 后 payload
0CLIPBOARDlen:u32, utf8
1ACK_CLIPBOARDsequence:u64
2UHID_OUTPUTid:u16, len:u16, bytes

写端见 DeviceMessageWriter,读端见 device_msg.c

五、长度与特殊值

约束
control message 最大值256 KiB
device message 最大值256 KiB
inject text 最大 UTF-8 字节300
clipboard 最大 UTF-8 字节256 KiB - header
scan file path256 bytes
mouse pointer idUINT64_MAX
generic finger idUINT64_MAX - 1
virtual pinch fingerUINT64_MAX - 2
invalid clipboard sequence0

UTF-8 截断必须发生在 code point 边界。C 消息销毁时,inject text、clipboard、app name 和 scan path 需要释放。

六、改协议时的最小验证

  1. 双端 type 数值一致;
  2. 字段顺序、宽度、signedness 与字节序一致;
  3. 长度上限发送端和接收端一致;
  4. C ownership/destroy 分支已加入;
  5. droppable 语义不会破坏状态机;
  6. camera/display 模式分发允许新消息;
  7. C serialize test 与 Java reader test 都有 golden bytes;
  8. 反向消息还要覆盖 TCP partial read。

基于 scrcpy 4.1 源码的独立学习笔记