vnrit 0.2.28

Lightweight X11 desktop WebRTC streaming server (pure Rust, no GStreamer)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# vnrit — 纯 Rust X11 WebRTC 流媒体服务器

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

**vnrit** 是一款使用纯 Rust 编写的 X11 桌面流媒体服务器,通过 **WebRTC** 将 X11 屏幕以低延迟、高画质的方式传输至浏览器,并支持键盘鼠标反向控制。  
项目**不依赖 GStreamer、FFmpeg 或任何系统编解码器**,所有核心组件均为 Rust 实现或安全封装,编译后二进制仅约 **6.6 MB**,资源占用极低。

---

##  功能特性一览

###  视频流
- **MIT‑SHM 零拷贝捕获** – 优先使用 X11 共享内存扩展,像素数据直接写入内存,免除 Socket 传输。
- **SIMD 色彩转换** – 使用 Google libyuv 的 ARM NEON / x86 SSE 加速 BGRA → I420 转换。
- **智能缩放** – 支持服务器端缩放(`--height`),在 YUV 域使用高质量 Catmull‑Rom 双三次插值,节省带宽。
- **实时图像增强** – 内置边缘保持的 Unsharp Mask,融合运动自适应、纹理/亮度/色度感知,所有计算均为整数 SIMD 指令,无浮点开销。
- **H.264 硬件级编码** – 采用 Cisco OpenH264 编码器,专为屏幕内容优化的实时模式(Screen Content RealTime),支持运行时动态码率调整。
- **自适应码率(ABR)** – 基于 WebRTC 的 TWCC 带宽估计和丢包率反馈,自动调节编码码率,适应网络波动。

### 音频流
- **PulseAudio 系统音频捕获** – 自动探测默认音频输出设备的监听源(Monitor),捕获桌面声音(如媒体播放、系统提示音)。
- **Opus 编码** – 48 kHz 立体声,20 ms 帧长,低延迟高压缩比。
- **内存池复用** – PCM 缓冲区循环利用,零分配。

###  输入转发
- **XTest 扩展注入** – 支持键盘(KeySym)、鼠标(绝对/相对移动、滚轮、按键)事件。
- **前端物理键码映射** – 浏览器发送 `KeyboardEvent.code`(如 `KeyA``Digit2`),后端通过预构建的 keysym→keycode 哈希表快速转换,布局无关。
- **触控手势** – 单指移动(鼠标拖动)、双指缩放、长按右键、双指滚动(滚轮模拟)。
- **虚拟键盘** – 内置多层(主键 / F 键 / 数字键)虚拟键盘,支持修饰键“点按暂态”、“长按锁定”、“双击锁定”,适合触屏设备。

### 前端界面
- **纯 HTML + CSS + JS** – 无需任何第三方框架,体积小巧。
- **WebRTC 播放器** – 基于 `RTCPeerConnection` 接收 H.264 + Opus 流,支持自动播放与音频延迟解锁。
- **CSS 光标叠加层** – 远程光标位置通过 WebSocket 独立同步,不编码进视频流,保持清晰且无编码延迟。
- **缩放/平移** – 前端 CSS 变换实现平滑缩放(Alt 键或按钮切换),缩放时远程光标自动保持在视野中央。
- **自适应布局** – 使用 ResizeObserver 动态适应容器大小,完美适配窗口/全屏变化。
- **连接状态监控** – 显示实时 RTT 延迟、连接状态、自动重连(指数退避)。
- **压缩信令** – WebSocket 消息支持 deflate 压缩(原生 DecompressionStream + pako 回退),节省流量。

###  运维与可靠性
- **令牌认证** – 可选 `--token`,支持 URL 参数或 Cookie 传递,防止未授权访问。
- **信号处理** – 捕获 SIGINT/SIGTERM/SIGQUIT/SIGHUP,自动释放所有 XTest 按住键,避免 X 服务器“卡键”。
- **资源隔离** – 每个客户端独立 PeerConnection,连接断开时彻底清理所有后台任务,释放内存(调用 `mi_collect` 回收 mimalloc 缓存)。
- **超时保护** – X11 Socket 设置 5 秒接收超时,WebRTC 协商 15 秒超时,避免死锁。

---

##  系统架构

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                            vnrit 服务器 (Rust)                             │
│                                                                             │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐  │
│  │  捕获阶段    │──▶│  转换阶段    │──▶│  编码阶段    │──▶│  发送阶段    │  │
│  │  (阻塞线程)  │   │  (阻塞线程)  │   │  (阻塞线程)  │   │  (异步任务)  │  │
│  │  MIT-SHM    │   │  libyuv     │   │  openh264   │   │  WebRTC     │  │
│  │  / get_image│   │  BGRA→I420 │   │  H.264      │   │  track      │  │
│  │  + 缩放     │   │  + 增强     │   │  码率控制   │   │  write_sample│  │
│  └─────────────┘   └─────────────┘   └─────────────┘   └─────────────┘  │
│         │                  │                  │                  │         │
│         └──────────────────┴──────────────────┴──────────────────┘         │
│                               有界通道 (容量4)                              │
│                                                                             │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐                      │
│  │  音频捕获    │──▶│  音频编码    │──▶│  音频发送    │                      │
│  │  (阻塞线程)  │   │  (阻塞线程)  │   │  (异步任务)  │                      │
│  │  PulseAudio │   │  Opus       │   │  WebRTC     │                      │
│  │  事件驱动    │   │  48kHz/stereo│  │  track      │                      │
│  └─────────────┘   └─────────────┘   └─────────────┘                      │
│                                                                             │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐                      │
│  │  信令服务    │   │  输入注入    │   │  光标追踪    │                      │
│  │  (axum)     │   │  XTest      │   │  XI2事件    │                      │
│  │  WebSocket  │   │  键盘/鼠标   │   │  异步fd     │                      │
│  └─────────────┘   └─────────────┘   └─────────────┘                      │
└─────────────────────────────────────────────────────────────────────────────┘
                              ┌───────────────┐
                              │  浏览器前端    │
                              │  (HTML+JS)    │
                              │  WebRTC / WS  │
                              └───────────────┘
```

- **并行管道** – 捕获、转换、编码、发送四个阶段分别运行在不同线程,通过 `SyncChannel`(容量 4)连接,互不阻塞。
- **双 X11 连接** – 捕获与输入使用独立连接,避免互斥锁竞争。
- **事件驱动光标更新** – 通过 XI2 扩展订阅鼠标移动事件,配合 `AsyncFd` 在 Tokio 事件循环中高效读取,无需轮询。

---

##  视频管道详解

### 1. 捕获(Capture)
- **优先 MIT‑SHM** – 通过 `shm::create_segment` 获取共享内存 FD,`mmap` 后 X 服务器直接写入原始 BGRA 像素。  
  对于 1920×1080@24fps,节省约 200 MB/s 的 X11 Socket 流量。
- **降级方案** – 若 SHM 不可用,自动回退至 `xproto::get_image`,依然保证功能完整。
- **缩放融合** – 若 `--height` 指定缩放,则在捕获阶段同时进行缩放和色彩转换,避免在 I420 域额外遍历。

### 2. 转换与增强(Convert & Enhance)
- **色彩空间转换** – 使用 `vnrit_libyuv`(libyuv 绑定)的 `ARGBToI420`,单次 SIMD 调用完成 BGRA → YUV420。
- **缩放(如需)** – 采用 Catmull‑Rom(Mitchell‑Netravali)双三次插值,预计算权重表(Q12 定点),水平+垂直分步执行,并利用块缓存(tile)优化内存访问。
- **图像增强(Unsharp Mask)** – 在 Y 平面应用边缘保持的 USM,具体亮点:
  - **边缘保持**:使用梯度指导的混合权重,避免传统 USM 的光晕(halo)。
  - **运动自适应**:基于帧间 MAD(平均绝对差)查表动态调整强度,抑制运动闪烁。
  - **纹理自适应**:根据局部对比度(相邻像素差异)调节增益,避免过度锐化。
  - **全局亮度/色度自适应**:参考平均亮度和色度活跃度,自动调节整体锐度。
  - **全部整数 + SIMD**:核心循环使用 `i16x8` 批量处理,无浮点,性能极致。
- **Chroma 饱和度增强**:当亮度锐化后,自动提升 U/V 饱和度以补偿视觉弱化。

### 3. 编码(Encode)
- **OpenH264** 编码器配置:
  - 配置文件:`Constrained Baseline`(浏览器兼容性最佳)
  - 使用模式:`ScreenContentRealTime`(针对屏幕内容优化)
  - 复杂度:`Low`(平衡 CPU 与画质)
  - GOP:`10 × framerate`(例如 240 帧)
  - 码率控制:`Bitrate` 模式,支持动态调整。
- **动态码率调整**:通过 `set_option(ENCODER_OPTION_BITRATE)` 运行时修改,不重建编码器。
- **关键帧强制**:当码率大幅下降(>30%)或每 5 秒定期插入 IDR,确保随机访问和错误恢复。
- **错误恢复**:编码失败时重置编码器,并进入指数退避重试,防止崩溃。

### 4. 发送(Send)
- 使用 `webrtc-rs``TrackLocalStaticSample`,异步调用 `write_sample` 将 H.264 NAL 单元打包为 RTP 包发送。
- 帧持续时间固定(`1/framerate`),确保码流平稳。
- 零拷贝:编码器输出直接转为 `Bytes` 传送,无需额外复制。

---

##  音频管道详解

- **PulseAudio 捕获**  - 启动时自动调用 `find_default_monitor` 检测默认输出的监听源(`.monitor`)。
  - 若检测失败,退回到默认录音源(通常为麦克风)。
  - 使用事件驱动的主循环(`pa_mainloop_iterate(true)`)阻塞等待数据,而非轮询,CPU 占用极低。
  - PCM 数据为 48 kHz 立体声 S16LE,每帧 3840 字节(20 ms)。
- **PCM 内存池**:预分配 8 个缓冲区,循环使用,零分配。
- **Opus 编码**:使用 `audiopus` crate,编码器配置为 48 kHz 立体声、音频应用(适合音乐/语音),输出帧长 20 ms,码率自适应。
- **WebRTC 发送**:与视频相同,通过音频 `TrackLocalStaticSample` 发送 RTP 包。

---

##  输入处理链路

### 前端 → 后端协议
所有输入命令通过 **WebSocket** 或 **DataChannel**(优先)以 CSV 形式发送,格式稳定:

| 命令 | 格式 | 说明 |
|------|------|------|
| 鼠标相对移动 | `mr,dx,dy` | 增量移动(像素) |
| 鼠标绝对移动 | `ma,x,y` | 绝对坐标(视频坐标系) |
| 鼠标按下 | `md,button` | button=1/2/3(左/中/右) |
| 鼠标释放 | `mu,button` | 同上 |
| 滚轮 | `ms,delta` | delta>0 下滚,<0 上滚(单位:步进) |
| 键盘按下 | `kd,code` | `KeyboardEvent.code` 字符串 |
| 键盘释放 | `ku,code` | 同上 |

### 后端处理
- **鼠标**:收到 `ma`/`mr` 后调用 `xtest::fake_input` 发送 `X11_MOTION_NOTIFY`,并更新本地缓存位置。
- **键盘**:通过预构建的 `keysym→keycode` 映射表(首次连接时从 `GetKeyboardMapping` 构建),将 `code` 转换为 X11 键码,再通过 XTest 发送 `KeyPress`/`KeyRelease`- **修饰键状态跟踪**:记录所有按下的键码,连接断开时自动释放,防止 X 服务器卡键。

### 光标同步优化
- 服务器通过 XI2 事件实时追踪光标绝对位置,并通过 WebSocket 发送 `{type:"cursor", x, y}` 给前端。
- 前端收到后使用 **CSS transform** 将自定义光标叠加层移动到对应坐标,无需依赖视频帧。
- 前端本地输入时立即更新叠加层,并发送绝对/相对移动命令,形成闭环。

---

## 🔌 信令与 WebRTC 连接

### WebSocket 信令流程
1. 浏览器连接 `/ws` 并发送 `{"type":"ready"}`2. 服务器创建 `PeerConnection`,生成 Offer SDP,发送 `{"type":"offer","sdp":"..."}`3. 浏览器创建 Answer,发送 `{"type":"answer","sdp":"..."}`4. 双方交换 ICE 候选(`{"type":"ice","candidate":"...","sdp_mline_index":0}`)。
5. 连接建立后,视频/音频轨道自动开始传输。

### 数据通道(DataChannel)
- 服务器主动创建名为 `"input"` 的 DataChannel,用于承载输入命令(低延迟、可靠传输)。
- 浏览器在 `ondatachannel` 事件中接收该通道,之后所有输入优先走 DataChannel,降级到 WebSocket。
- DataChannel 关闭时自动回退,保持可用性。

### ICE 配置
- 支持 STUN 服务器(默认 `stun:stun.cloudflare.com:3478`),可禁用(`--stun ""`)。
- 支持 TCP-only 模式(`--tcp-only`),适用于 UDP 被限制的网络。
- ICE 超时时间放宽至 15s(断连)和 60s(失败),适应移动网络波动。

---

##  前端界面详解

### 视频渲染
- 使用 `<video>` 元素,`object-fit: contain` 保持比例。
- 通过 `RTCPeerConnection``ontrack` 将媒体流挂载到 video/audio 元素。
- 音频默认静音(`muted`),首次用户交互时取消静音并调用 `play()`,符合浏览器自动播放策略。

### 缩放与平移(前端 CSS 变换)
- `zoomLevel``zoomPanX``zoomPanY` 控制 `#zoom-layer``transform: translate(X,Y) scale(Z)`- 触发方式:
  - 鼠标滚轮(Alt 键按住时缩放,否则滚动)。
  - 双指捏合(触控板/触摸屏)。
  - 工具栏 `+` 按钮(开启/关闭缩放模式,Alt 键替代)。
- 平移边界自动限制,防止超出视频区域。
- 缩放时智能跟踪远程光标:若光标移出视口,自动平移使其回中,提升浏览体验。

### 光标叠加层
- 独立 `div#cursor-overlay`,样式为圆形光晕,通过 `transform: translate(x,y)` 定位。
- 位置更新使用 **requestAnimationFrame** 批量处理,与视频帧同步。
- 当服务器光标位置与本地预测偏差较大时,启动 40ms 缓动动画(ease-out),平滑过渡。

### 自定义虚拟键盘
- **三层布局**:主键(QWERTY)、功能键(F1-F12 等)、数字小键盘。
- **修饰键行为**  - **点击**:按下并保持 300ms 内释放 → 视为暂态(用于组合键,如 Shift+A)。
  - **长按 300ms**:进入“锁定”状态(适合连续输入,如 Ctrl+C/V)。
  - **双击**:永久锁定(直到再次点击解锁),适合需要长时间按住修饰键的场景。
- **层切换**:底部按钮 `Fn``123` 切换层,再次点击返回主层。
- **关闭**:点击视频区域或底部 `` 按钮关闭键盘,同时释放所有修饰键。

### 网络状态与重连
- 连接状态指示灯(红/绿),RTT 延迟显示。
- 断开后自动重连,退避间隔从 1s 开始,每次翻倍,最大 30s。
- 会话计数器(`currentSession`)防止旧连接的回调干扰新连接。

### 消息压缩
- 信令消息(offer/answer/ice)以纯文本发送,确保兼容性。
- 普通文本消息(如光标位置)超过 512 字节时,使用 deflate 压缩并加上前缀字节 `0x01` 发送为 Binary。
- 前端首先尝试 `DecompressionStream`(Chrome/Firefox/Safari 原生支持),失败后回退到 `pako`,最后尝试直接 UTF-8 解码。

---

##  性能优化一览

| 优化技术 | 具体实现 |
|----------|----------|
| **零拷贝内存池** | 所有环节使用预分配 Vec,通过 `Bytes` 和自定义 `PooledBuf` 自动回收到池,稳态零分配。 |
| **SHM 共享内存** | 捕获阶段直接映射 X 服务器写入的内存,无数据复制。 |
| **SIMD 批处理** | 色彩转换(libyuv)、缩放权重计算、增强循环均使用 SIMD(i16x8 / i32x4)指令。 |
| **I420 域缩放** | 缩放仅在 YUV 平面进行(1.5 字节/像素),避免在 BGRA(4 字节/像素)上操作,节省内存带宽。 |
| **双 X11 连接** | 捕获和输入分离,避免锁竞争。 |
| **异步非阻塞 I/O** | 所有网络操作(WebSocket、WebRTC)均为异步,充分利用 Tokio 运行时。 |
| **有界通道** | 管道间使用 `SyncChannel`(容量 4),防止生产者过快占用内存,同时保证流水线并行。 |
| **`with_resize_uninit`** | 使用 `set_len` 跳过缓冲区零初始化(写入前全部覆盖),减少内存清零开销。 |
| **原子操作优化** | 使用 `Ordering::Relaxed` 适当放宽内存屏障,提升 ARM 性能。 |
| **强制 IDR 间隔** | 每 5 秒强制关键帧,保证随机访问,同时避免过多 I 帧导致码率尖峰。 |
| **动态码率死区** | ABR 调整阈值为 50 kbps,防止频繁抖动。 |
| **PCM 内存池** | 音频捕获与编码共用一个环形池,无分配。 |
| **事件驱动 PulseAudio** | 使用 `pa_mainloop_iterate(true)` 阻塞等待,而非轮询,降低 CPU。 |
| **XI2 事件驱动光标** | 利用 `AsyncFd` 监听 X11 事件,无轮询开销。 |
| **WebSocket 压缩** | 非信令消息采用 deflate 压缩,减少带宽。 |
| **前端 RAF 合并** | 所有 DOM 更新在单次 `requestAnimationFrame` 中完成,避免布局抖动。 |
| **LayoutCache 缓存** | 缓存 `getBoundingClientRect` 结果,减少强制回流。 |

---

## 🛠️ 构建与运行

### 依赖项
- **Rust 工具链**:1.82 或更高(推荐使用 `rustup`)。
- **CMake**:3.20+(用于编译 libyuv)。
- **X11 开发库**(Linux):`libx11-dev``libxtst-dev``libxext-dev`(构建时链接)。
- **PulseAudio 开发库**(可选,但编译需要):`libpulse-dev`- **编译目标**:支持 x86_64 和 ARM(包括 Android Termux)。

### 构建步骤

```bash
# 克隆仓库
git clone https://github.com/yourname/vnrit.git
cd vnrit

# 或手动构建
export CMAKE=$(which cmake)
cargo build --release
```
### 运行服务器

```bash
# 最基本用法(捕获 :1 显示器,端口 8080)
./target/release/vnrit --display :1

# 推荐远程访问配置(720p,500 kbps)
./target/release/vnrit --display :1 --height 720 --bitrate 500

# 启用自适应码率
./target/release/vnrit --adaptive-bitrate

# 开启调试日志
RUST_LOG=debug ./target/release/vnrit
```

浏览器访问 `http://<服务器IP>:8080` 即可。

---

##  命令行参数完整参考

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `--display` | String | `:1` | X11 显示编号(如 `:0``:1`|
| `-p, --port` | u16 | `8080` | HTTP/WebSocket 监听端口 |
| `--framerate` | i32 | `24` | 捕获帧率(fps) |
| `--bitrate` | i32 | `1000` | 目标视频码率(kbps) |
| `--height` | i32 | `0` | 输出高度(像素),0 表示保持原始分辨率,宽度按比例缩放 |
| `--stun` | String | `stun:stun.cloudflare.com:3478` | STUN 服务器 URL,设为空字符串可禁用 |
| `--token` | Option\<String\> | `None` | 认证令牌,设置后所有连接需提供 |
| `--log-level` | String | `info` | 日志级别:`off`, `error`, `warn`, `info`, `debug`, `trace` |
| `--tcp-only` | bool | `false` | 仅使用 TCP ICE 候选(禁用 UDP) |
| `--adaptive-bitrate` | bool | `false` | 开启自适应码率(基于 TWCC + 丢包) |
| `--enhance` | Option\<f32\> | `None` | Y 平面 USM 强度(0.0~2.0),当 `--height` 有效时默认 0.8,否则 0.0 |

---

## 🧪 使用示例

### 1. 本地局域网(无 STUN)
```bash
vnrit --stun "" --display :0
```

### 2. 高画质远程桌面(2 Mbps)
```bash
vnrit --bitrate 2000 --height 1080
```

### 3. 低带宽移动访问(300 kbps,240p)
```bash
vnrit --height 240 --bitrate 300 --framerate 15
```

### 4. 带认证与自适应码率
```bash
vnrit --token "supersecret" --adaptive-bitrate
```
浏览器访问需携带 `?token=supersecret` 或设置 Cookie `token=supersecret`。

### 5. 调试模式(日志输出到终端)
```bash
RUST_LOG=debug vnrit --log-level debug
```

---

## 🔒 安全与认证

- **令牌认证**:通过 `--token` 启用。浏览器需在 URL 查询参数或 Cookie 中提供 `token` 字段。首次通过查询参数认证后,服务器会设置 `HttpOnly` Cookie,后续 WebSocket 升级自动携带,无需重复传递。
- **WebSocket 安全**:若部署在生产环境,建议将 vnrit 置于反向代理(如 Nginx)后,启用 HTTPS/WSS 加密传输。
- **X11 权限**:vnrit 需要访问 X11 显示,通常通过 `DISPLAY` 环境变量或 `--display` 参数指定。确保 X 服务器允许连接(例如 `xhost +` 或使用 Cookie 认证)。
- **输入注入**:XTest 扩展允许模拟键盘鼠标,因此建议只在可信网络中使用,或使用防火墙限制访问。

---

## 🚧 故障排查指南

### 连接失败
- **检查 X11 服务器**`echo $DISPLAY`,确保 vnrit 使用的显示编号正确。
- **确认 XTest 扩展**:运行 `xdpyinfo | grep XTest`,若无输出则需要加载 XTest 模块(通常默认安装)。
- **防火墙**:确保端口(默认 8080)可被浏览器访问。
- **STUN 不可达**:若无法访问默认 STUN 服务器,可禁用(`--stun ""`)或更换为内网可用的 STUN/TURN。

### 视频卡顿/黑屏
- **编码器错误**:查看日志是否有 `[encoder] error`,可能是资源不足,尝试降低分辨率/码率。
- **网络带宽**:检查上行带宽是否满足当前码率,可启用 `--adaptive-bitrate` 自动调节。
- **帧率过高**:降低 `--framerate` 至 15 或 10。

### 键盘输入无效
- **键码映射**:确保 X11 键盘布局与前端物理键盘匹配(通常无需干预)。
- **修饰键卡住**:断开连接后服务器会自动释放,若仍卡住可手动执行 `xdotool key X` 恢复。

### 音频无声
- **确认 PulseAudio 运行**`pactl info`- **检查默认输出监听源**`pactl list sinks short`,若不存在 monitor,可能需要设置默认源为 `alsa_output` 等。
- **浏览器自动播放策略**:需用户点击页面才能启用音频,点击任意位置即可。

### 构建失败
- **CMake 找不到**:安装 cmake,并设置 `CMAKE` 环境变量。
- **libclang 缺失**`apt install libclang-dev`(Debian)或 `pkg install libclang`(Termux)。
- **libyuv 下载超时**:配置 Git 镜像(见构建章节)。

---

##  开发与贡献

### 代码结构
- `src/main.rs` – 所有后端逻辑(X11 捕获、编码、WebRTC、输入处理)。
- `src/index.html` – 前端完整页面(包含 CSS 与 JavaScript)。
- `build.sh` – 构建脚本,自动设置环境变量。
- `Cargo.toml` – 依赖管理,使用 `[patch.crates-io]` 对部分 crate 进行了补丁(如 `audiopus_sys`)以适配 Termux。

### 贡献指南
1. 提交 Issue 描述问题或建议。
2. Fork 仓库,创建功能分支。
3. 确保代码通过 `cargo fmt``cargo clippy`4. 编写测试(若适用)。
5. 发起 Pull Request。

### 依赖管理
所有依赖均为纯 Rust 或安全封装,已尽量减小第三方库的版本锁定。部分核心库(如 `webrtc-rs`)为 Git 依赖,确保使用最新修复。

---

## 📄 许可证

本项目采用 **MIT 许可证**,可自由使用、修改、分发。详见 [LICENSE](LICENSE) 文件。

---

## 🌟 致谢

- [webrtc-rs]https://github.com/webrtc-rs/webrtc – WebRTC 实现基石。
- [x11rb]https://github.com/psychon/x11rb – X11 协议绑定。
- [openh264]https://github.com/cisco/openh264 – 视频编码引擎。
- [libyuv]https://chromium.googlesource.com/libyuv/libyuv – 高性能色彩转换。
- [libpulse-binding]https://github.com/jnqnfe/pulse-binding-rust – PulseAudio 绑定。
- [mimalloc]https://github.com/microsoft/mimalloc – 内存分配器。

---

**vnrit** – 让您的 X11 桌面触手可及,无论身处何地。