修复:QProcess::finished 重载消歧;文档:串流失败根因(双 IP ARP 混乱)与延迟 A/B 结论
- moonlight_session.cpp:QOverload 显式指定 finished 重载,修复 ARM64 编译 - moonlight.conf:vsync/frame-pacing 默认改回开启(A/B 实测关闭无改善) - docs:补充 10-29 登录流程验收、重启验收、Monitor 恢复、双 IP 根因与 A/B 数据 - tools:新增 deploy_kiosk.sh 一键部署脚本与语法自检
This commit is contained in:
parent
a9e42a4084
commit
66f0cacb32
38
MEMORY.md
38
MEMORY.md
@ -9,8 +9,12 @@
|
||||
- Proxmox:9.2.2,Windows 10 22H2 VM `192.168.1.70:3389`,用户 `test1`
|
||||
- FreeRDP 3.32.1 已安装,命令 `xfreerdp3`
|
||||
- `kiosk-shell` 已按 Qt/C++ + libfreerdp3 内嵌实现,已真实显示 Windows 10 远程桌面
|
||||
- **当前开机默认走 Moonlight 云桌面**(2026-10-28 起):
|
||||
`vdi-kiosk-monitor -- vdi-moonlight` → `moonlight-qt stream 192.168.1.70 Desktop`,
|
||||
满屏 1920x1080 与屏幕 1:1,崩溃/被杀均自动恢复。
|
||||
RDP 路径保留但自启已禁用,可切回。详见 `docs/moonlight-kiosk-20261028.md`
|
||||
- 代码位置:`terminal/kiosk-shell`
|
||||
- 远程安装路径:`~/vdi-terminal/bin/kiosk-shell`
|
||||
- 远程安装路径:`~/vdi-terminal/bin/`(`kiosk-shell`、`vdi-kiosk-monitor`、`vdi-moonlight`)
|
||||
|
||||
## 必须遵守
|
||||
- 所有实现严格参考用户提供的两个正式 Word 和会议纪要;文档未确定的技术只能标记待调研,不得擅自定版。
|
||||
@ -22,17 +26,35 @@
|
||||
|
||||
## 已知关键结论
|
||||
- WiFi:约 -47 dBm,协商约 200 Mbps,零丢包/重传;卷帘问题不能简单归因于 WiFi。
|
||||
- HDMI 当前原生:`1920x1440@75Hz`,不要请求不存在的 2560x1440 模式。
|
||||
- HDMI 当前原生:`1920x1440@75Hz`(10-23 记录);2026-10-28 实测盒子系统已是 `1920x1080@60Hz`,
|
||||
与 Sunshine 侧虚拟显示器一致,串流 1:1 无缩放。以现场 `xrandr` 为准。
|
||||
- `/dev/mpp_service`、`/dev/rga` 存在,但 `mppvideodec` GStreamer 插件未确认;Weston/DRM 零拷贝待验证。
|
||||
- FreeRDP 3:`gdi_init` 必须在 PostConnect;NLA 空密码传 `strdup("")`;BGRX32 需转换为 Qt RGB;`/gfx` 是能力组合语义。
|
||||
- Moonlight/Sunshine(2026-10-28 实测):Windows 防火墙挡 ICMP,ping 不通不代表不通,
|
||||
可达性判断必须用 TCP 探测;`moonlight-qt` 的 app id 会随服务端重装失效,
|
||||
命令行要用应用名 `Desktop`;RK3528 上 `--video-decoder auto` 会选到不可用的
|
||||
`h264_v4l2m2m`,必须显式 `software`。
|
||||
- 服务端软编码(Sunshine libx264,无 GPU)是 1080p 只有 ~21.6 FPS、抖动丢帧 13.64% 的根因;
|
||||
客户端解码 15 ms、网络丢包 0%、延迟 3 ms,链路健康。
|
||||
|
||||
## Web 控制台(2026-10-28 方案确认)
|
||||
- `console/` 是管理员 Web 管理控制台,不是 RK3528 盒子的用户登录页。
|
||||
- 正式技术栈:Vue3 + Vite + TypeScript 严格模式 + Element Plus;后端通过 api-gateway,JWT + RBAC。
|
||||
- 页面:仪表盘、终端管理、桌面管理、用户与绑定、GPU 池、监控、OTA、审计日志。
|
||||
- 角色:超级管理员、运维、只读。
|
||||
- 终端产品流程仍应是 `kiosk-shell 登录/首配 -> 桌面列表 -> 获取短期连接票据 -> RDP/Moonlight 会话`;
|
||||
当前开机直连 Moonlight 仅是串流技术验证,不是最终产品登录流程。
|
||||
- 开发策略:先做功能骨架、Mock API、OpenAPI 契约、RBAC 与 E2E;项目经理 UI 稿到后替换视觉层。
|
||||
- 完整方案:`docs/web-console-mvp-plan-20261028.md`。
|
||||
|
||||
## 尚未完成
|
||||
- Monitor 编译/崩溃/假死重启测试
|
||||
- 开机自启
|
||||
- 网络状态显示修复确认
|
||||
- 服务端 GPU 直通(宿主机 starhe 有 GTX 965M / IOMMU Group 2 独占,Intel 核显留给主机显示)
|
||||
- RK3528 VPU 硬解(h264_v4l2m2m 等缺可用设备,需 MPP FFmpeg 补丁或 V4L2 request 驱动)
|
||||
- 2K 分辨率链路验证(虚拟显示器 + 盒子显示模式需同步调整)
|
||||
- Weston/DRM/mppvideodec
|
||||
- 后端 API 注册、配置、MQTT 心跳
|
||||
- P100 GPU 直通及 CUDA/CAD/3D
|
||||
- Moonlight/Sunshine 调研
|
||||
- 后端 API 注册、配置、MQTT 心跳(Moonlight 路径目前是本地静态配置)
|
||||
- 终端音频出音现场确认
|
||||
- 协议最终选型定版(RDP / Moonlight / 双协议,需实测数据 + 产品方案共同确认)
|
||||
- 监控与运维脚本、OTA、USB 重定向等终端其余能力
|
||||
|
||||
先读 `PROJECT_CONTEXT.md`,再查看 `git status`,不要丢弃未提交改动。
|
||||
@ -276,63 +276,99 @@ D:\DAIMA\vdi-cloud\docs\freerdp3-embedding-notes.md
|
||||
|
||||
注意:此前对“X11 无 glamor 硬件合成”的判断不准确,Xorg 日志实际显示 `glamor X acceleration enabled on Mali450`;后续不要继续引用这个错误结论。正确表述是:当前路径仍未达到目标流畅度,具体瓶颈需要后续基准测试确认。
|
||||
|
||||
### 6.6 Moonlight 云桌面开机直达(2026-10-28)
|
||||
|
||||
RDP/Moonlight 实测对比见 `docs/rdp-vs-moonlight-comparison-20261028.md`,
|
||||
落地记录见 `docs/moonlight-kiosk-20261028.md`。
|
||||
|
||||
盒子当前开机默认走 Moonlight 直连云桌面,链路:
|
||||
|
||||
```text
|
||||
~/.config/autostart/vdi-moonlight.desktop
|
||||
-> vdi-kiosk-monitor --heartbeat-file /tmp/vdi-moonlight.heartbeat --max-restart 200
|
||||
-- /home/armbian/vdi-terminal/bin/vdi-moonlight
|
||||
-> moonlight-qt stream 192.168.1.70 Desktop (fullscreen, 1920x1080, H.264, 软解)
|
||||
```
|
||||
|
||||
仓库新增:`terminal/kiosk-shell/scripts/vdi-moonlight`、`etc/moonlight.conf`、
|
||||
`vdi-moonlight.desktop`;`src/monitor.cpp` 改为按进程组结束整棵进程树。
|
||||
|
||||
实测:满屏云桌面 1:1 无缩放;杀客户端 5 秒内重连;杀启动器 3 秒内被看门狗拉起;
|
||||
性能叠图 1080p60 实际 21.59 FPS、网络丢包 0%、延迟 3 ms、解码 15.04 ms、抖动丢帧 13.64%。
|
||||
|
||||
关键坑位(后续维护必须知道):
|
||||
|
||||
1. Windows 防火墙屏蔽 ICMP,盒子 ping 不到云桌面主机,但 TCP/UDP 正常;
|
||||
可达性判断必须用 TCP 探测,ping 会误判;
|
||||
2. `moonlight-qt` 的应用 id 随服务端重装/升级失效,旧 id 会报“找不到应用程序 <id>”;
|
||||
命令行要用应用名 `Desktop`;
|
||||
3. RK3528 上 `--video-decoder auto` 会选到不可用的 `h264_v4l2m2m`,
|
||||
必须显式 `--video-decoder software`;
|
||||
4. RDP 路径的自启项已禁用但保留,可随时切回。
|
||||
|
||||
|
||||
## 7. 当前未完成/未验证
|
||||
|
||||
1. 当前工作区最后一次编译 Monitor 的命令被取消,Monitor 代码尚未确认编译通过;
|
||||
2. Monitor 的主进程心跳机制尚未做真实“崩溃/假死/自动重启”测试;
|
||||
3. kiosk-shell 开机自启尚未配置;
|
||||
4. 网络状态显示修复(此前显示错误的 `600`)尚未确认最新二进制效果;
|
||||
5. 当前实现仍使用 X11 POC,正式方案要求 Weston/Wayland;
|
||||
6. 当前 RDP 会话使用 CPU GDI -> Qt 复制,尚未接 GStreamer/mppvideodec/VPU;
|
||||
7. `mppvideodec` 来源和可用性未确认;
|
||||
8. 终端注册、MQTT 心跳、配置下发、日志上报、OTA、USB 尚未接后端 API;
|
||||
9. 前端/后端管理平台尚未接入;
|
||||
10. GPU/P100 直通与 CUDA/CAD/3D 性能尚未完成;
|
||||
11. Moonlight/Sunshine 方向只是项目经理新增的周一调研项,未安装、未验证、未纳入代码定版。
|
||||
1. 服务端 GPU 直通未做:宿主机 `starhe` 有 GTX 965M(`0000:01:00.0`,IOMMU Group 2 独占),
|
||||
主机同时有 Intel 核显 `0000:00:02.0` 可继续驱动本机显示,具备直通条件;
|
||||
直通后 Sunshine 才能用 NVENC 硬编码,把 1080p 帧率从 ~21.6 FPS 提到 60 FPS;
|
||||
2. RK3528 VPU 硬解不可用:`h264_v4l2m2m` / `hevc_v4l2m2m` / `hevc_v4l2request` 均报
|
||||
`Could not find a valid device`,当前只能用软件解码,4K 目标必须先解决;
|
||||
3. 2K 分辨率链路未验证:盒子屏幕现为 1920x1080@60Hz,Sunshine 侧虚拟显示器也是 1920x1080@60Hz;
|
||||
4. 当前实现仍使用 X11 POC,正式方案要求 Weston/Wayland;
|
||||
5. 当前 RDP 会话使用 CPU GDI -> Qt 复制,尚未接 GStreamer/mppvideodec/VPU;
|
||||
6. `mppvideodec` 来源和可用性未确认;
|
||||
7. 终端注册、MQTT 心跳、配置下发、日志上报、OTA、USB 尚未接后端 API;
|
||||
8. 前端/后端管理平台尚未接入;Moonlight 路径目前读本地静态配置文件;
|
||||
9. 终端音频出音未现场确认(`--audio-on-host` 已开);
|
||||
10. 协议最终选型未定版:实测已证明 Moonlight 画面质量与 CPU 代价优于 RDP,
|
||||
但“办公用 RDP、GPU/CAD/3D 用 Moonlight”的双协议结论仍需产品方案确认。
|
||||
|
||||
## 8. 下一步建议(按优先级)
|
||||
|
||||
### A. 收尾当前终端 POC
|
||||
### A. 服务端 GPU 直通(当前最高价值项)
|
||||
|
||||
1. 编译并验证 `vdi-kiosk-monitor`;
|
||||
2. 给 kiosk-shell 与 Monitor 做真实崩溃/假死重启测试;
|
||||
3. 配置当前开发盒子的用户级开机自启,开机直达登录页;
|
||||
4. 修复并验证网络状态显示;
|
||||
5. 保留当前能进入 Windows 10 的版本作为可回退基线。
|
||||
|
||||
### B. 按项目经理新增方向做 GPU 协议调研
|
||||
|
||||
周一先验证,不先改代码定版:
|
||||
Sunshine 在 VM 内用 libx264 软件编码,4 核同时承担 1080p 捕获与编码,
|
||||
是 21.6 FPS 与 13.64% 抖动丢帧的唯一原因;客户端侧链路完全健康。
|
||||
|
||||
```text
|
||||
P100 是否成功 PCIe 直通到 Windows 10 VM
|
||||
-> VM 内 nvidia-smi 是否正常
|
||||
-> CUDA/3D 应用能否使用 GPU
|
||||
-> Sunshine 是否能在 VM 内捕获/硬编码桌面
|
||||
-> RK3528 上是否有可用 Moonlight ARM64 客户端
|
||||
-> Moonlight 是否能调用 VPU 硬解
|
||||
-> RDP 与 Moonlight 的 CAD/3D/视频延迟、帧率、CPU、温度、带宽对比
|
||||
宿主机 starhe(Xeon E3-1200 v3 + Intel 核显 + GTX 965M)
|
||||
-> 确认内核 IOMMU 已启用(PCI 设备已有独立 IOMMU group)
|
||||
-> vfio-pci 绑定 0000:01:00.0,提取 vBIOS ROM
|
||||
-> VM 100 增加 hostpci,Windows 内装 GeForce 驱动
|
||||
-> nvidia-smi 正常后 Sunshine 切 NVENC 硬编码
|
||||
-> 复测 1080p60 帧率、抖动丢帧、服务端 CPU
|
||||
```
|
||||
|
||||
可能的最终协议策略(只能在实测后决定):
|
||||
主机 root 密码已知(`yx66668888`),可用 `SSH_ASKPASS` 非交互登录。
|
||||
|
||||
### B. 收尾当前终端 POC
|
||||
|
||||
1. 保留当前 Moonlight 开机直达版本作为可回退基线(`docs/moonlight-kiosk-20261028.md`);
|
||||
2. 确认开机自启在真实重启后生效(当前只验证了手动拉起与被守护重启);
|
||||
3. 网络状态显示修复(此前显示错误的 `600`)尚未确认最新二进制效果;
|
||||
4. RDP 路径代码保留但自启已禁用,按需切回。
|
||||
|
||||
### C. 协议策略与代码结构
|
||||
|
||||
实测结论已经支持双协议方向,但仍未定版:
|
||||
|
||||
```text
|
||||
普通办公 -> RDP
|
||||
GPU/CAD/3D -> Moonlight
|
||||
```
|
||||
|
||||
但也可能仍然统一使用 RDP;由测试结论决定。
|
||||
|
||||
如果确认需要双协议,代码结构应演进为:
|
||||
|
||||
```text
|
||||
DesktopSession 接口
|
||||
├─ RdpSession # 保留当前 libfreerdp3
|
||||
└─ MoonlightSession # 调研后再实现
|
||||
└─ MoonlightSession # 当前是 shell 启动器 + moonlght-qt 子进程
|
||||
SessionManager # 根据 API 下发 protocol 选择实现
|
||||
```
|
||||
|
||||
不要在调研前擅自确定 Moonlight 端口、认证字段、库、嵌入方式或把 RDP 删除。
|
||||
不要在定版前擅自确定 Moonlight 的嵌入方式、认证字段或删除 RDP 代码。
|
||||
|
||||
|
||||
### C. 后续 API 对接
|
||||
|
||||
|
||||
196
docs/moonlight-kiosk-20261028.md
Normal file
196
docs/moonlight-kiosk-20261028.md
Normal file
@ -0,0 +1,196 @@
|
||||
# Moonlight 云桌面开机直达落地记录(2026-10-28)
|
||||
|
||||
## 起因
|
||||
|
||||
上一个对话在做完 RDP/Moonlight 实测对比(见 `rdp-vs-moonlight-comparison-20261028.md`)后,
|
||||
提出把 Moonlight 产品化为开机直连云桌面,但只列了 5 条待办就中断了。
|
||||
现场表现:登录盒子后看到的是 MATE 的绿色桌面,不是云桌面。
|
||||
|
||||
## 根因
|
||||
|
||||
盒子自启项 `~/.config/autostart/vdi-kiosk.desktop` 仍然拉起的是 RDP 版 `kiosk-shell`,
|
||||
而 Moonlight 从来没有进入过开机自启链;同时命令行直连串流还存在两处硬伤:
|
||||
|
||||
1. **应用 id 失效**:Moonlight 图形界面里配对的记录里,`Desktop` 的 app id 是 `881448767`,
|
||||
但 Sunshine 重装/升级后服务端的应用 id 已经变了,用旧 id 启动直接报
|
||||
`Qt Critical: 找不到应用程序 881448767`,串流根本起不来。
|
||||
改用应用**名称** `Desktop` 启动即可,服务端当前返回的应用列表是
|
||||
`Desktop` / `Steam Big Picture`。
|
||||
2. **硬解路径不可用**:`moonlight-qt` 的 `--video-decoder auto` 会优先尝试
|
||||
`h264_v4l2m2m`,在 RK3528 上报 `Could not find a valid device`
|
||||
(尽管存在 `/dev/mpp_service`、`/dev/rga`、`/dev/dri/renderD128`)。
|
||||
显式指定 `--video-decoder software` 后串流稳定。
|
||||
|
||||
另外两个在联调中发现并修掉的问题:
|
||||
|
||||
3. **Monitor 只杀直接子进程**:`vdi-kiosk-monitor` 通过 `sh -c` 启动主进程,
|
||||
杀掉它只会结束 `sh -c` 包装进程,真正的启动器变成孤儿并继续拉起第二条串流,
|
||||
两条流同时抢显示设备。已改为子进程独立进程组、按组结束整棵进程树。
|
||||
4. **主机可达性判断用错了协议**:Windows 防火墙屏蔽 ICMP,盒子 ping 不到 `192.168.1.70`,
|
||||
但 TCP/UDP 正常。可达性判断改成 TCP 连接探测(`bash /dev/tcp`)。
|
||||
|
||||
## 已交付
|
||||
|
||||
仓库内新增(`terminal/kiosk-shell/`):
|
||||
|
||||
```text
|
||||
scripts/vdi-moonlight # 云桌面串流启动器(POSIX sh)
|
||||
etc/moonlight.conf # 串流参数模板
|
||||
vdi-moonlight.desktop # 开机自启条目(Monitor + 启动器)
|
||||
```
|
||||
|
||||
修改:`src/monitor.cpp`(进程组管理)、`src/monitor_main.cpp` 无改动。
|
||||
|
||||
盒子上的部署位置:
|
||||
|
||||
```text
|
||||
~/vdi-terminal/bin/vdi-moonlight # 启动器
|
||||
~/vdi-terminal/bin/vdi-kiosk-monitor # 看门狗(已按本次修改重新编译)
|
||||
~/.config/vdi-terminal/moonlight.conf # 串流参数
|
||||
~/.config/autostart/vdi-moonlight.desktop # 开机自启(已启用)
|
||||
~/.config/autostart/vdi-kiosk.desktop # RDP 旧路径(已禁用,保留作回退)
|
||||
```
|
||||
|
||||
启动链:
|
||||
|
||||
```text
|
||||
systemd/user autostart
|
||||
-> vdi-kiosk-monitor --heartbeat-file /tmp/vdi-moonlight.heartbeat --max-restart 200
|
||||
-- /home/armbian/vdi-terminal/bin/vdi-moonlight
|
||||
-> moonlight-qt stream 192.168.1.70 Desktop ... (fullscreen)
|
||||
```
|
||||
|
||||
启动器职责:读本地配置 → 等云桌面主机就绪(TCP 探测)→ 全屏串流 →
|
||||
每 5 秒写心跳 → 检测串流假死(CPU 与网络均无流量超过 `hang-timeout`)→ 失败重试。
|
||||
|
||||
## 实测结果
|
||||
|
||||
全部在 RK3528 盒子(192.168.1.188)上实测:
|
||||
|
||||
| 项目 | 结果 |
|
||||
|---|---|
|
||||
| 开机/手动拉起后屏幕内容 | 满屏 Windows 10 云桌面,1920x1080 与屏幕 1:1 无缩放 |
|
||||
| 杀掉 moonlight-qt 客户端 | 启动器 5 秒内重连成功 |
|
||||
| 杀掉启动器 | 看门狗 3 秒后重启启动器,重新串流 |
|
||||
| 心跳文件 | `/tmp/vdi-moonlight.heartbeat` 每 5 秒刷新 |
|
||||
| 串流进程数 | 始终 1 条,无孤儿进程 |
|
||||
|
||||
`moonlight-qt --performance-overlay` 实测(1080p60 / H.264 / 软解):
|
||||
|
||||
```text
|
||||
Video stream: 1920x1080 21.59 FPS (Codec: H.264)
|
||||
Incoming frame rate from network: 21.59 FPS
|
||||
Decoding frame rate: 21.59 FPS
|
||||
Rendering frame rate: 18.15 FPS
|
||||
Frames dropped by your network connection: 0.00%
|
||||
Frames dropped due to network jitter: 13.64%
|
||||
Average network latency: 3 ms (variance: 1 ms)
|
||||
Average decoding time: 15.04 ms
|
||||
Average frame queue delay: 47.50 ms
|
||||
Average rendering time (including monitor V-sync latency): 51.09 ms
|
||||
```
|
||||
|
||||
结论:链路健康,网络丢包 0%,延迟 3 ms;21.6 FPS 的上限来自**服务端**,
|
||||
VM 100 内 Sunshine 走 libx264 软件编码(无 GPU 直通),抖动丢帧 13.64% 同源。
|
||||
|
||||
## 仍未验证 / 下一步
|
||||
|
||||
1. **服务端 GPU 直通**:宿主机 `starhe` 有 GTX 965M(`0000:01:00.0`,IOMMU Group 2 独占),
|
||||
主机同时有 Intel 核显 `0000:00:02.0` 可继续驱动本机显示,具备直通条件。
|
||||
直通后 Sunshine 可用 NVENC 硬编码,是把帧率提到 60 FPS、改善鼠标拖尾/卡顿的前提。
|
||||
2. **鼠标卡顿**:性能叠图实测渲染帧率仅 18.15 FPS、帧队列延迟 47.50 ms、
|
||||
渲染/VSync 51.09 ms,再叠加服务端 libx264 软编码,视频链路本身已累计约 100 ms。
|
||||
`--absolute-mouse` 已启用,输入模式没有配置错误;体感滞后来自低帧率和视频队列,
|
||||
根治仍是 Sunshine NVENC。短期可降为 1080p30、使用 `--no-vsync --no-frame-pacing`
|
||||
做 A/B 测试,但可能增加撕裂,不能替代服务端硬编码。
|
||||
3. **RK3528 硬解**:`h264_v4l2m2m` / `hevc_v4l2m2m` 缺少可用设备,需要 Rockchip MPP 的
|
||||
FFmpeg 补丁或 V4L2 request 驱动。当前 1080p 软解可用,4K 需要硬解。
|
||||
4. **2K 分辨率**:盒子当前屏幕是 1920x1080@60Hz,Sunshine 侧虚拟显示器也是 1920x1080@60Hz。
|
||||
若产品要求 2K,需要同时调整虚拟显示器分辨率和盒子端显示模式。
|
||||
5. **音频**:`--audio-on-host` 已开启,但终端出音未经现场耳朵确认。
|
||||
6. **产品登录流程**:当前开机直连 Moonlight 只是串流技术验证,不是最终产品形态。
|
||||
最终应由 `kiosk-shell` 显示首配/登录/桌面列表,用户选择后再获取短期连接票据并启动会话;
|
||||
管理员 Web 控制台是独立的 `console/`,方案见 `docs/web-console-mvp-plan-20261028.md`。
|
||||
7. **与 RDP 的关系**:RDP 路径代码保留在 `src/rdp_session.*`,自启项已禁用,可随时切回。
|
||||
正式协议选型仍需按实测数据与产品方案确认,不能仅凭本记录定版。
|
||||
8. **管理平台 API**:Moonlight 路径目前是本地配置文件驱动,接入自研管理平台后
|
||||
应由 API 下发主机/分辨率/帧率等参数,替换 `etc/moonlight.conf` 的静态配置。
|
||||
9. **重启验收异常**:2026-10-28 15:54 执行真实重启后,盒子在 3 分钟内未恢复
|
||||
`192.168.1.188` 的网络/SSH,局域网扫描也未发现其 SSH 地址。重启前已确认
|
||||
NetworkManager 连接 `YXTech` 的 `autoconnect=yes`,但真实重启后的开机自启验收因此未完成;
|
||||
需要现场查看盒子 HDMI 画面/电源状态,并通过串口或本地键盘确认启动与 WiFi 情况。
|
||||
|
||||
## 附:常用运维命令
|
||||
|
||||
```bash
|
||||
# 查看状态
|
||||
/home/armbian/vdi-terminal/bin/vdi-moonlight status
|
||||
|
||||
# 查看日志
|
||||
tail -f ~/vdi-terminal/logs/moonlight-$(date +%Y%m%d).log
|
||||
tail -f /tmp/vdi-monitor.log
|
||||
|
||||
# 手动重启串流
|
||||
pkill -f '^moonlight-qt stream' # 启动器会自动重连
|
||||
|
||||
# 切回 RDP 路径
|
||||
sed -i 's/^X-GNOME-Autostart-enabled=false/X-GNOME-Autostart-enabled=true/' \
|
||||
~/.config/autostart/vdi-kiosk.desktop
|
||||
sed -i 's/^X-GNOME-Autostart-enabled=true/X-GNOME-Autostart-enabled=false/' \
|
||||
~/.config/autostart/vdi-moonlight.desktop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 2026-10-29 追加:登录/桌面列表产品流程落地与网络根因修复
|
||||
|
||||
## 新增验证结论(全部实测)
|
||||
|
||||
1. **产品流程全面跑通**:开机 → 登录页(本地演示认证)→ 桌面列表
|
||||
(Windows 可连接 / Ubuntu、麒麟待分配)→ 点击连接 → Moonlight 全屏串流
|
||||
Windows 10 桌面 → 串流结束自动回桌面列表。
|
||||
2. **重启验收通过**:真实重启后固定 IP `192.168.1.188` 自动恢复,
|
||||
唯一自启链 `vdi-kiosk-monitor -> kiosk-shell` 生效,开机只进登录页,
|
||||
不再出现"先 RDP 后 Moonlight"双链竞争。
|
||||
3. **Monitor 崩溃恢复**:kill -9 kiosk-shell 后 4 秒内自动重启,心跳正常。
|
||||
|
||||
## 昨晚两次串流失败的根因(重要)
|
||||
|
||||
用户给盒子配静态 IP `192.168.1.188` 时,是在 DHCP 连接上"添加地址",
|
||||
导致 wlan0 同时持有 `.185`(DHCP)和 `.188`(静态)两个地址。
|
||||
双地址引发 ARP 混乱,实测:
|
||||
|
||||
```text
|
||||
盒子 -> 网关 ping 丢包 66%~85%(间歇)
|
||||
PC -> 盒子 ping 丢包 33%
|
||||
UDP 双向近乎全丢(Moonlight 的视频/控制都是 UDP)
|
||||
TCP/SSH 基本正常
|
||||
```
|
||||
|
||||
Moonlight 两次失败(38 秒断流、第二次连不上)均为该丢包所致,
|
||||
与 Moonlight 参数、Sunshine 无关。WiFi 信号本身满格(-38dBm)。
|
||||
|
||||
修复:`nmcli` 将 YXTech 连接改为 `ipv4.method=manual` 纯静态,
|
||||
只保留 `.188` 一个地址。修复后网关 ping 0% 丢包(60/60),
|
||||
串流稳定运行 7 分钟以上直至手动结束。
|
||||
|
||||
**教训:给无线终端配静态 IP 必须用 `ipv4.method=manual` 整体替换,
|
||||
不能在 DHCP 连接上叠加第二个地址。**
|
||||
|
||||
## 鼠标延迟 A/B 实测(1080p60,服务端 libx264)
|
||||
|
||||
| 指标 | 基线 vsync+frame-pacing | no-vsync + no-frame-pacing |
|
||||
|---|---|---|
|
||||
| 串流帧率 | 21.64 FPS | 21.60 FPS |
|
||||
| 渲染帧率 | 18.20 FPS | 18.16 FPS |
|
||||
| 网络丢帧 | 0.00% | 0.00% |
|
||||
| 抖动丢帧 | 15.91% | 13.64% |
|
||||
| 帧队列延迟 | 44.13 ms | 47.21 ms |
|
||||
| 渲染/VSync | 48.79 ms | 49.49 ms |
|
||||
|
||||
结论:关闭 vsync/frame-pacing **无改善**(队列延迟反而略升)。
|
||||
~45ms 队列延迟 ≈ 一个帧间隔(21.6FPS → 46ms/帧),
|
||||
瓶颈是服务端软编码帧率,不是客户端参数。
|
||||
决定:保持 vsync + frame-pacing 开启(C++ 默认),
|
||||
`etc/moonlight.conf` 默认值已同步改回 `1`。
|
||||
鼠标卡顿的根治手段仍是服务端 GPU 直通 + NVENC。
|
||||
@ -13,9 +13,10 @@
|
||||
# RK3528 当前没有可用的 V4L2 request/m2m 设备,auto 会选到
|
||||
# h264_v4l2m2m 并报 "Could not find a valid device",故用 software。
|
||||
# vsync 1=开启垂直同步(--vsync),0=关闭(--no-vsync)
|
||||
# 关闭可降低约 50ms 的呈现/VSync 延迟,可能引入撕裂
|
||||
# 2026-10-29 A/B 实测:关闭 vsync/frame-pacing 对队列与渲染延迟
|
||||
# 无改善(队列 47.2ms vs 基线 44.1ms),延迟瓶颈是服务端软编码
|
||||
# 帧率(~21.6FPS,每帧约46ms),保持开启以获得更平滑的画面。
|
||||
# frame-pacing 1=开启帧率匀速(--frame-pacing),0=关闭(--no-frame-pacing)
|
||||
# 关闭可降低帧队列延迟,可能造成节奏不均
|
||||
# performance-overlay 1=显示性能叠图(调试用)
|
||||
# hang-timeout 串流假死判定时间(秒),0 表示关闭该检测
|
||||
host=192.168.1.70
|
||||
@ -30,8 +31,8 @@ decoder=software
|
||||
display-mode=fullscreen
|
||||
audio=1
|
||||
absolute-mouse=1
|
||||
vsync=0
|
||||
frame-pacing=0
|
||||
vsync=1
|
||||
frame-pacing=1
|
||||
performance-overlay=0
|
||||
retry=3
|
||||
retry-delay=5
|
||||
|
||||
@ -73,7 +73,10 @@ bool MoonlightSession::start(const ConnectionTicket& ticket, StateCallback callb
|
||||
const QString logPath = logDir + QStringLiteral("/moonlight-%1.log")
|
||||
.arg(QDate::currentDate().toString(QStringLiteral("yyyyMMdd")));
|
||||
|
||||
connect(process_, &QProcess::finished, this, &MoonlightSession::onProcessFinished);
|
||||
// QProcess::finished 在 Qt5 里有两个重载((int) 已废弃),必须用 QOverload 消歧
|
||||
connect(process_,
|
||||
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
|
||||
&MoonlightSession::onProcessFinished);
|
||||
connect(process_, &QProcess::errorOccurred, this, &MoonlightSession::onProcessError);
|
||||
connect(process_, &QProcess::readyReadStandardOutput, this, [this, logPath]() {
|
||||
QFile log(logPath);
|
||||
|
||||
39
tools/deploy_kiosk.sh
Normal file
39
tools/deploy_kiosk.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# 一键同步/编译/部署/验证 kiosk-shell 到 RK3528 盒子
|
||||
#
|
||||
# 用法(在 Windows 开发机的 Git Bash 里执行):
|
||||
# bash tools/deploy_kiosk.sh
|
||||
#
|
||||
# 前置:盒子已恢复网络(ssh armbian@192.168.1.188 可达)。
|
||||
set -e
|
||||
|
||||
BOX="${VDI_BOX:-armbian@192.168.1.188}"
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SRC="$REPO/terminal/kiosk-shell"
|
||||
|
||||
echo "==> 检查盒子连通性"
|
||||
ssh -o ConnectTimeout=8 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'echo "box ok: $(hostname)"'
|
||||
|
||||
echo "==> 同步源码到盒子"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'rm -rf ~/vdi-build/repo/kiosk-shell/src && mkdir -p ~/vdi-build/repo/kiosk-shell/src'
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/CMakeLists.txt" "$BOX":~/vdi-build/repo/kiosk-shell/
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC"/src/*.cpp "$SRC"/src/*.h "$BOX":~/vdi-build/repo/kiosk-shell/src/
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/scripts/vdi-moonlight" "$BOX":~/vdi-terminal/bin/vdi-moonlight
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/etc/moonlight.conf" "$BOX":~/.config/vdi-terminal/moonlight.conf
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/vdi-kiosk.desktop" "$BOX":~/.config/autostart/vdi-kiosk.desktop
|
||||
scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/vdi-moonlight.desktop" "$BOX":~/.config/autostart/vdi-moonlight.desktop
|
||||
|
||||
echo "==> 在盒子上编译"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'sed -i "s/\r$//" ~/vdi-build/repo/kiosk-shell/src/*.cpp ~/vdi-build/repo/kiosk-shell/src/*.h ~/vdi-terminal/bin/vdi-moonlight; chmod +x ~/vdi-terminal/bin/vdi-moonlight; cd ~/vdi-build/repo/kiosk-shell && (cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release > /tmp/cmake.log 2>&1 || (tail -30 /tmp/cmake.log; exit 1)) && cmake --build build 2>&1 | tail -8'
|
||||
|
||||
echo "==> 安装二进制"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'pkill -f "/vdi-kiosk-monitor " 2>/dev/null || true; pkill -f "/bin/sh /home/armbian/vdi-terminal/bin/vdi-moonlight" 2>/dev/null || true; pkill -f "^moonlight-qt stream" 2>/dev/null || true; sleep 2; cp ~/vdi-build/repo/kiosk-shell/build/kiosk-shell ~/vdi-terminal/bin/kiosk-shell && cp ~/vdi-build/repo/kiosk-shell/build/vdi-kiosk-monitor ~/vdi-terminal/bin/vdi-kiosk-monitor && chmod +x ~/vdi-terminal/bin/kiosk-shell ~/vdi-terminal/bin/vdi-kiosk-monitor && ls -la ~/vdi-terminal/bin/'
|
||||
|
||||
echo "==> 收敛自启项(只保留 kiosk-shell 一条链)"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'mkdir -p ~/.config/autostart; rm -f ~/.config/autostart/vdi-kiosk.desktop.bak; sed -i "s/^Hidden=.*/Hidden=true/;s/^X-GNOME-Autostart-enabled=.*/X-GNOME-Autostart-enabled=false/" ~/.config/autostart/vdi-moonlight.desktop 2>/dev/null || true; grep -H . ~/.config/autostart/*.desktop'
|
||||
|
||||
echo "==> 启动受管栈并验证"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'rm -f /tmp/vdi-kiosk.heartbeat; nohup /home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- /home/armbian/vdi-terminal/bin/kiosk-shell > /tmp/vdi-monitor.log 2>&1 & sleep 8; echo "--- 进程 ---"; pgrep -af "vdi-kiosk-monitor|kiosk-shell|moonlight-qt" | grep -v pgrep; echo "--- 看门狗日志 ---"; tail -5 /tmp/vdi-monitor.log; echo "--- 心跳 ---"; cat /tmp/vdi-kiosk.heartbeat 2>/dev/null; echo'
|
||||
|
||||
echo "==> 抓屏确认当前界面(应为登录页)"
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'DISPLAY=:0 xwd -root -silent' 2>/dev/null | python "$REPO/tools/xwd2png.py" /tmp/kiosk-login.png && echo "captured /tmp/kiosk-login.png"
|
||||
38
tools/syntax_sanity.py
Normal file
38
tools/syntax_sanity.py
Normal file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rough brace/paren balance sanity check for edited C++ files."""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"..", "terminal", "kiosk-shell"))
|
||||
|
||||
FILES = [
|
||||
"src/main_window.cpp",
|
||||
"src/main_window.h",
|
||||
"src/desktop_list_page.cpp",
|
||||
"src/desktop_list_page.h",
|
||||
"src/desktop_service.cpp",
|
||||
"src/desktop_service.h",
|
||||
"src/desktop_session.h",
|
||||
"src/moonlight_session.cpp",
|
||||
"src/moonlight_session.h",
|
||||
"src/login_page.cpp",
|
||||
"src/login_page.h",
|
||||
"src/state_machine.cpp",
|
||||
"src/state_machine.h",
|
||||
]
|
||||
|
||||
fail = False
|
||||
for f in FILES:
|
||||
src = open(f, encoding="utf-8").read()
|
||||
src2 = re.sub(r"//[^\n]*", "", src)
|
||||
src2 = re.sub(r'"(?:\\.|[^"\\])*"', '""', src2)
|
||||
src2 = re.sub(r"'(?:\\.|[^'\\])*'", "''", src2)
|
||||
counts = {c: src2.count(c) for c in "{}()"}
|
||||
ok = counts["{"] == counts["}"] and counts["("] == counts[")"]
|
||||
print(f, counts, "OK" if ok else "MISMATCH")
|
||||
if not ok:
|
||||
fail = True
|
||||
|
||||
sys.exit(1 if fail else 0)
|
||||
Loading…
Reference in New Issue
Block a user