- 构造时以设备 SN 走演示认证,kConfig->kAuthenticating->kDesktopList 同步完成,开机不再显示登录页 - 列表页移除"退出登录"按钮(无登录概念无去向);管理菜单"退出终端"保留 - 登录页与认证代码保留,接平台正式认证后由 DesktopService 驱动恢复登录流程 - 已部署盒子并抓屏验证;文档与 MEMORY 同步
14 KiB
Moonlight 云桌面开机直达落地记录(2026-10-28)
起因
上一个对话在做完 RDP/Moonlight 实测对比(见 rdp-vs-moonlight-comparison-20261028.md)后,
提出把 Moonlight 产品化为开机直连云桌面,但只列了 5 条待办就中断了。
现场表现:登录盒子后看到的是 MATE 的绿色桌面,不是云桌面。
根因
盒子自启项 ~/.config/autostart/vdi-kiosk.desktop 仍然拉起的是 RDP 版 kiosk-shell,
而 Moonlight 从来没有进入过开机自启链;同时命令行直连串流还存在两处硬伤:
- 应用 id 失效:Moonlight 图形界面里配对的记录里,
Desktop的 app id 是881448767, 但 Sunshine 重装/升级后服务端的应用 id 已经变了,用旧 id 启动直接报Qt Critical: 找不到应用程序 881448767,串流根本起不来。 改用应用名称Desktop启动即可,服务端当前返回的应用列表是Desktop/Steam Big Picture。 - 硬解路径不可用:
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后串流稳定。
另外两个在联调中发现并修掉的问题:
- Monitor 只杀直接子进程:
vdi-kiosk-monitor通过sh -c启动主进程, 杀掉它只会结束sh -c包装进程,真正的启动器变成孤儿并继续拉起第二条串流, 两条流同时抢显示设备。已改为子进程独立进程组、按组结束整棵进程树。 - 主机可达性判断用错了协议:Windows 防火墙屏蔽 ICMP,盒子 ping 不到
192.168.1.70, 但 TCP/UDP 正常。可达性判断改成 TCP 连接探测(bash /dev/tcp)。
已交付
仓库内新增(terminal/kiosk-shell/):
scripts/vdi-moonlight # 云桌面串流启动器(POSIX sh)
etc/moonlight.conf # 串流参数模板
vdi-moonlight.desktop # 开机自启条目(Monitor + 启动器)
修改:src/monitor.cpp(进程组管理)、src/monitor_main.cpp 无改动。
盒子上的部署位置:
~/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 旧路径(已禁用,保留作回退)
启动链:
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 / 软解):
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% 同源。
仍未验证 / 下一步
- 服务端 GPU 直通:宿主机
starhe有 GTX 965M(0000:01:00.0,IOMMU Group 2 独占), 主机同时有 Intel 核显0000:00:02.0可继续驱动本机显示,具备直通条件。 直通后 Sunshine 可用 NVENC 硬编码,是把帧率提到 60 FPS、改善鼠标拖尾/卡顿的前提。 - 鼠标卡顿:性能叠图实测渲染帧率仅 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 测试,但可能增加撕裂,不能替代服务端硬编码。 - RK3528 硬解:
h264_v4l2m2m/hevc_v4l2m2m缺少可用设备,需要 Rockchip MPP 的 FFmpeg 补丁或 V4L2 request 驱动。当前 1080p 软解可用,4K 需要硬解。 - 2K 分辨率:盒子当前屏幕是 1920x1080@60Hz,Sunshine 侧虚拟显示器也是 1920x1080@60Hz。 若产品要求 2K,需要同时调整虚拟显示器分辨率和盒子端显示模式。
- 音频:
--audio-on-host已开启,但终端出音未经现场耳朵确认。 - 产品登录流程:当前开机直连 Moonlight 只是串流技术验证,不是最终产品形态。
最终应由
kiosk-shell显示首配/登录/桌面列表,用户选择后再获取短期连接票据并启动会话; 管理员 Web 控制台是独立的console/,方案见docs/web-console-mvp-plan-20261028.md。 - 与 RDP 的关系:RDP 路径代码保留在
src/rdp_session.*,自启项已禁用,可随时切回。 正式协议选型仍需按实测数据与产品方案确认,不能仅凭本记录定版。 - 管理平台 API:Moonlight 路径目前是本地配置文件驱动,接入自研管理平台后
应由 API 下发主机/分辨率/帧率等参数,替换
etc/moonlight.conf的静态配置。 - 重启验收异常:2026-10-28 15:54 执行真实重启后,盒子在 3 分钟内未恢复
192.168.1.188的网络/SSH,局域网扫描也未发现其 SSH 地址。重启前已确认 NetworkManager 连接YXTech的autoconnect=yes,但真实重启后的开机自启验收因此未完成; 需要现场查看盒子 HDMI 画面/电源状态,并通过串口或本地键盘确认启动与 WiFi 情况。
附:常用运维命令
# 查看状态
/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 追加:登录/桌面列表产品流程落地与网络根因修复
新增验证结论(全部实测)
- 产品流程全面跑通:开机 → 登录页(本地演示认证)→ 桌面列表 (Windows 可连接 / Ubuntu、麒麟待分配)→ 点击连接 → Moonlight 全屏串流 Windows 10 桌面 → 串流结束自动回桌面列表。
- 重启验收通过:真实重启后固定 IP
192.168.1.188自动恢复, 唯一自启链vdi-kiosk-monitor -> kiosk-shell生效,开机只进登录页, 不再出现"先 RDP 后 Moonlight"双链竞争。 - Monitor 崩溃恢复:kill -9 kiosk-shell 后 4 秒内自动重启,心跳正常。
昨晚两次串流失败的根因(重要)
用户给盒子配静态 IP 192.168.1.188 时,是在 DHCP 连接上"添加地址",
导致 wlan0 同时持有 .185(DHCP)和 .188(静态)两个地址。
双地址引发 ARP 混乱,实测:
盒子 -> 网关 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。
2026-10-29 下午追加:四件事落地(删 RDP / 退出设计 / 过场层 / ultrafast 实测)
已交付(全部编译部署到盒子并实测)
- 删 RDP:
rdp_session.*、FreeRDP 渲染管线、CMake 构建依赖全部移除, 协议概念(Protocol枚举)从代码删除,Moonlight 是唯一会话通道; RDP 代码只存在于 git 历史(网关备用)。部署后ldd kiosk-shell无任何 freerdp 依赖。 - 退出设计三层:
- 会话内
Ctrl+Alt+Shift+Q退出串流回桌面列表——moonlight-qt 自带快捷键为主路径, kiosk 侧保留同一快捷键兜底(串流未建立/焦点仍在 kiosk 时生效); - 桌面列表右上角"退出登录"→ 登录页(已有);
- 登录页删除"退出"按钮(退出终端是维护功能,只留在管理菜单 Ctrl+Alt+Shift+M)。
- 会话内
- 加载过场层:新增
src/transition_overlay.*,点连接后全屏置顶黑层 ("正在进入云桌面…"动态省略号 + 旋轮),盖住 moonlight-qt 启动期左上角小窗闪烁; 首帧检测用 moonlight 进程 CPU 时间近似(软解 CPU 远高于握手期,阈值 120 tick / 15s 兜底), 检测到后 1 秒淡出。活动期间吞键盘鼠标、周期 raise 保持置顶。 - Sunshine x264 预设 ultrafast:VM 100 的
C:\Program Files\Sunshine\config\sunshine.conf写入sw_preset = ultrafast(该版本支持sw_preset/sw_tune键),服务已重启生效。
关键实测结论:21.6 FPS 帽子不在 x264,旧根因判定被推翻
ultrafast 前后对照(1080p60 / H.264 / 盒子软解,同参数同网络):
| 指标 | medium(基线 10-28) | ultrafast(本次) |
|---|---|---|
| Video stream FPS | 21.59~21.64 | 21.57~21.61 |
| 抖动丢帧 | 13.64%~15.91% | 13.64%(分毫不差) |
| 客户端解码 | 15.04 ms | 10.51~11.14 ms |
决定性证据:
- 串流进行中 VM 内
Processor(_Total)CPU 仅 0.2%~0.5%, sunshine 进程自服务重启起累计 CPU 时间仅 14 秒(含三次共约 3 分钟会话); - 用 xdotool 持续晃鼠标制造画面运动,帧率与 CPU 都不变;
- libx264 只有
MMX2 SSE2Fast SSSE3 SSE4.2——VM CPU 型号是QEMU Virtual CPU version 2.5+(kvm64 类),宿主指令集(含 AVX2)没有透传。
结论:21.6 FPS 的上限来自虚拟显示/捕获节奏(IddSampleDriver/MttVDD 呈现与 Desktop Duplication 的脏帧节奏,叠加 QEMU 显示栈),不是编码器算力—— 编码器近乎空转,预设怎么调都不会变。ultrafast 保留(无害,客户端解码也略降)。 根治路径按优先级:GPU 直通(NVENC + 真实显示栈)> 换 VM 显示方案 (VirtIO-GPU / cpu=host 先排除 QEMU 显示栈因素)> 都不是调 Sunshine 参数能解决的。
运维新装 Linux 云桌面 VM(2026-10-29 接入信息)
| VM | Proxmox | 地址 | 账号 | 实测 |
|---|---|---|---|---|
| kylin-test | 101 | 192.168.1.71 | test1 / yx66668888 | SSH 可登,银河麒麟 V10 SP1 桌面版,UKUI + Xorg,图形会话运行中 |
| ubuntu-test | 102 | 192.168.1.72 | test2 / yx66668888 | SSH 可登,Ubuntu 24.04.4 LTS,GNOME + Xorg,图形会话运行中(4 核 / 5.8GiB) |
两台均未装 Sunshine(47989 等端口关闭),还不能被 Moonlight 连接。 接入桌面列表的步骤:装 Sunshine(Linux 版)→ 配对 → 在盒子"桌面连接设置"里 把 Ubuntu/麒麟卡片的地址填上即可(kiosk 代码已支持,无需改代码)。
2026-10-30 追加:开机直进桌面列表
按产品决定,盒子开机不再显示 kiosk 登录页,直接进入"选择云桌面"列表页:
- 构造时以设备身份(SN)自动走 DesktopService 演示认证(SN 即当前用户展示),
状态链
kConfig -> kAuthenticating -> kDesktopList同步完成,无登录页闪烁; - 列表页右上角"退出登录"按钮移除(无登录概念后无去向),管理菜单"退出终端"保留;
- 登录页与 DesktopService 认证代码全部保留:接入管理平台正式认证 (/api/v1/terminal/session/login)后,由 DesktopService 决定是否回到登录流程, 页面代码无需重写;git 历史含完整"登录页 -> 桌面列表"产品流程版本。
- 已编译部署盒子并抓屏验证:开机落在桌面列表,Windows 可连接、Ubuntu/麒麟待分配。